Fwd: libraw update for libkdcraw from tde

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



update against v14.0.x branch

---------- Forwarded message ---------
От: Андрей Рандрианасулу <randrik@xxxxxxx>
Date: пн, 7 нояб. 2022 г., 06:09
Subject: libraw update for libkdcraw from tde
To: randrianasulu <randrianasulu@xxxxxxxxx>


 
seems to work
 
Megapatch is for ray-V slackbuilds
 
 
--
Андрей  Рандрианасулу

Attachment: libraw_update.png
Description: PNG image

diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/CMakeLists.txt libkdcraw/CMakeLists.txt
--- libkdcraw-wrk/CMakeLists.txt	2022-11-07 08:15:53.602821808 +0300
+++ libkdcraw/CMakeLists.txt	2022-11-07 07:46:31.654795007 +0300
@@ -71,7 +71,7 @@
 set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TQT_CXX_FLAGS}" )
 set( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined" )
 set( CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--no-undefined" )
-
+set( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -llcms -ljasper")
 
 ##### directories
 
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/dcraw/dcraw.c libkdcraw/libkdcraw/dcraw/dcraw.c
--- libkdcraw-wrk/libkdcraw/dcraw/dcraw.c	2022-11-07 08:15:53.606821808 +0300
+++ libkdcraw/libkdcraw/dcraw/dcraw.c	2022-11-07 07:46:31.726795008 +0300
@@ -1,6 +1,6 @@
 /*
    dcraw.c -- Dave Coffin's raw photo decoder
-   Copyright 1997-2008 by Dave Coffin, dcoffin a cybercom o net
+   Copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net
 
    This is a command-line ANSI C program to convert raw photos from
    any digital camera on any computer running any operating system.
@@ -19,15 +19,15 @@
    *If you have not modified dcraw.c in any way, a link to my
    homepage qualifies as "full source code".
 
-   $Revision: 1.399 $
-   $Date: 2008/03/05 01:29:34 $
+   $Revision: 1.478 $
+   $Date: 2018/06/01 20:36:25 $
  */
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
 
-#define DCRAW_VERSION "8.83"
+#define DCRAW_VERSION "9.28"
 
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
 #define _USE_MATH_DEFINES
 #include <ctype.h>
 #include <errno.h>
@@ -41,26 +41,8 @@
 #include <string.h>
 #include <time.h>
 #include <sys/types.h>
-#include <locale.h>
-
-/*
-   NO_JPEG disables decoding of compressed Kodak DC120 files.
-   NO_LCMS disables the "-p" option.
- */
 
-#ifdef HAVE_JPEG
-#include <jpeglib.h>
-#endif
-#ifndef NO_LCMS
-#include LCMS_HEADER
-#endif
-#ifdef LOCALEDIR
-#include <libintl.h>
-#define _(String) gettext(String)
-#else
-#define _(String) (String)
-#endif
-#ifdef DJGPP
+#if defined(DJGPP) || defined(__MINGW32__)
 #define fseeko fseek
 #define ftello ftell
 #else
@@ -86,51 +68,66 @@
 typedef unsigned long long UINT64;
 #endif
 
-#ifdef LJPEG_DECODE
-#error Please compile dcraw.c by itself.
-#error Do not link it with ljpeg_decode.
+#ifdef NODEPS
+#define NO_JASPER
+#define NO_JPEG
+#define NO_LCMS
 #endif
-
-#ifndef LONG_BIT
-#define LONG_BIT (8 * sizeof (long))
+#ifndef NO_JASPER
+#include <jasper/jasper.h>	/* Decode Red camera movies */
+#endif
+#ifndef NO_JPEG
+#include <jpeglib.h>		/* Decode compressed Kodak DC120 photos */
+#endif				/* and Adobe Lossy DNGs */
+#ifndef NO_LCMS
+#include <lcms2.h>		/* Support color profiles */
+#endif
+#ifdef LOCALEDIR
+#include <libintl.h>
+#define _(String) gettext(String)
+#else
+#define _(String) (String)
 #endif
 
-#define ushort UshORt
-typedef unsigned char uchar;
-typedef unsigned short ushort;
+#if !defined(uchar)
+#define uchar unsigned char
+#endif
+#if !defined(ushort)
+#define ushort unsigned short
+#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.
+   access them are prefixed with "CLASS".  For thread-safety, all
+   non-const static local variables except cbrt[] must be declared
+   "thread_local".
  */
-
-void swab(const void *restrict from, void *restrict to, ssize_t n);
-void *memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen);
-
-FILE *ifp;
+FILE *ifp, *ofp;
 short order;
-char *ifname, *meta_data;
+const char *ifname;
+char *meta_data, xtrans[6][6], xtrans_abs[6][6];
 char cdesc[5], desc[512], make[64], model[64], model2[64], artist[64];
 float flash_used, canon_ev, iso_speed, shutter, aperture, focal_len;
 time_t timestamp;
-unsigned shot_order, kodak_cbpp, filters, exif_cfa, unique_id;
-off_t    strip_offset, data_offset;
-off_t    thumb_offset, meta_offset, profile_offset;
+off_t strip_offset, data_offset;
+off_t thumb_offset, meta_offset, profile_offset;
+unsigned shot_order, kodak_cbpp, exif_cfa, unique_id;
 unsigned thumb_length, meta_length, profile_length;
 unsigned thumb_misc, *oprof, fuji_layout, shot_select=0, multi_out=0;
 unsigned tiff_nifds, tiff_samples, tiff_bps, tiff_compress;
-unsigned black, maximum, mix_green, raw_color, use_gamma, zero_is_bad;
+unsigned black, maximum, mix_green, raw_color, zero_is_bad;
 unsigned zero_after_ff, is_raw, dng_version, is_foveon, data_error;
-unsigned tile_width, tile_length, gpsdata[32];
+unsigned tile_width, tile_length, gpsdata[32], load_flags;
+unsigned flip, tiff_flip, filters, colors;
 ushort raw_height, raw_width, height, width, top_margin, left_margin;
 ushort shrink, iheight, iwidth, fuji_width, thumb_width, thumb_height;
-int flip, tiff_flip, colors;
-double pixel_aspect, aber[4]={1,1,1,1};
-ushort (*image)[4], white[8][8], curve[0x4001], cr2_slice[3], sraw_mul[4];
+ushort *raw_image, (*image)[4], cblack[4102];
+ushort white[8][8], curve[0x10000], cr2_slice[3], sraw_mul[4];
+double pixel_aspect, aber[4]={1,1,1,1}, gamm[6]={ 0.45,4.5,0,0,0,0 };
 float bright=1, user_mul[4]={0,0,0,0}, threshold=0;
+int mask[8][4];
 int half_size=0, four_color_rgb=0, document_mode=0, highlight=0;
-int verbose=0, use_auto_wb=0, use_camera_wb=0, use_camera_matrix=-1;
+int verbose=0, use_auto_wb=0, use_camera_wb=0, use_camera_matrix=1;
 int output_color=1, output_bps=8, output_tiff=0, med_passes=0;
 int no_auto_bright=0;
 unsigned greybox[4] = { 0, 0, UINT_MAX, UINT_MAX };
@@ -141,7 +138,7 @@
   { 0.019334, 0.119193, 0.950227 } };
 const float d65_white[3] = { 0.950456, 1, 1.088754 };
 int histogram[4][0x2000];
-void (*write_thumb)(FILE *), (*write_fun)(FILE *);
+void (*write_thumb)(), (*write_fun)();
 void (*load_raw)(), (*thumb_load_raw)();
 jmp_buf failure;
 
@@ -150,12 +147,15 @@
   int leaf;
 } first_decode[2048], *second_decode, *free_decode;
 
-struct {
+struct tiff_ifd {
   int width, height, bps, comp, phint, offset, flip, samples, bytes;
+  int tile_width, tile_length;
+  float shutter;
 } tiff_ifd[10];
 
-struct {
-  int format, key_off, black, black_off, split_col, tag_21a;
+struct ph1 {
+  int format, key_off, tag_21a;
+  int black, split_col, black_col, split_row, black_row;
   float tag_210;
 } ph1;
 
@@ -172,8 +172,8 @@
 #define MAX(a,b) ((a) > (b) ? (a) : (b))
 #define LIM(x,min,max) MAX(min,MIN(x,max))
 #define ULIM(x,y,z) ((y) < (z) ? LIM(x,y,z) : LIM(x,z,y))
-#define CLIP(x) LIM(x,0,65535)
-#define SWAP(a,b) { a ^= b; a ^= (b ^= a); }
+#define CLIP(x) LIM((int)(x),0,65535)
+#define SWAP(a,b) { a=a+b; b=a-b; a=a-b; }
 
 /*
    In order to inline this calculation, I make the risky
@@ -214,6 +214,9 @@
 	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
  */
 
+#define RAW(row,col) \
+	raw_image[(row)*raw_width+(col)]
+
 #define FC(row,col) \
 	(filters >> ((((row) << 1 & 14) + ((col) & 1)) << 1) & 3)
 
@@ -221,9 +224,9 @@
 	image[((row) >> shrink)*iwidth + ((col) >> shrink)][FC(row,col)]
 
 #define BAYER2(row,col) \
-	image[((row) >> shrink)*iwidth + ((col) >> shrink)][fc(row,col)]
+	image[((row) >> shrink)*iwidth + ((col) >> shrink)][fcol(row,col)]
 
-int CLASS fc (int row, int col)
+int CLASS fcol (int row, int col)
 {
   static const char filter[16][16] =
   { { 2,1,1,3,2,3,2,0,3,2,3,0,1,2,1,0 },
@@ -243,8 +246,9 @@
     { 2,1,3,2,3,1,2,1,0,3,0,2,0,2,0,2 },
     { 0,3,1,0,0,2,0,3,2,1,3,1,1,3,1,3 } };
 
-  if (filters != 1) return FC(row,col);
-  return filter[(row+top_margin) & 15][(col+left_margin) & 15];
+  if (filters == 1) return filter[(row+top_margin)&15][(col+left_margin)&15];
+  if (filters == 9) return xtrans[(row+6) % 6][(col+6) % 6];
+  return FC(row,col);
 }
 
 #ifndef __GLIBC__
@@ -258,9 +262,18 @@
   return 0;
 }
 #define memmem my_memmem
+char *my_strcasestr (char *haystack, const char *needle)
+{
+  char *c;
+  for (c = haystack; *c; c++)
+    if (!strncasecmp(c, needle, strlen(needle)))
+      return c;
+  return 0;
+}
+#define strcasestr my_strcasestr
 #endif
 
-void CLASS merror (void *ptr, char *where)
+void CLASS merror (void *ptr, const char *where)
 {
   if (ptr) return;
   fprintf (stderr,_("%s: Out of memory in %s\n"), ifname, where);
@@ -276,7 +289,7 @@
     else
       fprintf (stderr,_("Corrupt data near 0x%llx\n"), (INT64) ftello(ifp));
   }
-  data_error = 1;
+  data_error++;
 }
 
 ushort CLASS sget2 (uchar *s)
@@ -353,6 +366,61 @@
     swab (pixel, pixel, count*2);
 }
 
+void CLASS cubic_spline (const int *x_, const int *y_, const int len)
+{
+  float **A, *b, *c, *d, *x, *y;
+  int i, j;
+
+  A = (float **) calloc (((2*len + 4)*sizeof **A + sizeof *A), 2*len);
+  if (!A) return;
+  A[0] = (float *) (A + 2*len);
+  for (i = 1; i < 2*len; i++)
+    A[i] = A[0] + 2*len*i;
+  y = len + (x = i + (d = i + (c = i + (b = A[0] + i*i))));
+  for (i = 0; i < len; i++) {
+    x[i] = x_[i] / 65535.0;
+    y[i] = y_[i] / 65535.0;
+  }
+  for (i = len-1; i > 0; i--) {
+    b[i] = (y[i] - y[i-1]) / (x[i] - x[i-1]);
+    d[i-1] = x[i] - x[i-1];
+  }
+  for (i = 1; i < len-1; i++) {
+    A[i][i] = 2 * (d[i-1] + d[i]);
+    if (i > 1) {
+      A[i][i-1] = d[i-1];
+      A[i-1][i] = d[i-1];
+    }
+    A[i][len-1] = 6 * (b[i+1] - b[i]);
+  }
+  for(i = 1; i < len-2; i++) {
+    float v = A[i+1][i] / A[i][i];
+    for(j = 1; j <= len-1; j++)
+      A[i+1][j] -= v * A[i][j];
+  }
+  for(i = len-2; i > 0; i--) {
+    float acc = 0;
+    for(j = i; j <= len-2; j++)
+      acc += A[i][j]*c[j];
+    c[i] = (A[i][len-1] - acc) / A[i][i];
+  }
+  for (i = 0; i < 0x10000; i++) {
+    float x_out = (float)(i / 65535.0);
+    float y_out = 0;
+    for (j = 0; j < len-1; j++) {
+      if (x[j] <= x_out && x_out <= x[j+1]) {
+	float v = x_out - x[j];
+	y_out = y[j] +
+	  ((y[j+1] - y[j]) / d[j] - (2 * d[j] * c[j] + c[j+1] * d[j])/6) * v
+	   + (c[j] * 0.5) * v*v + ((c[j+1] - c[j]) / (6 * d[j])) * v*v*v;
+      }
+    }
+    curve[i] = y_out < 0.0 ? 0 : (y_out >= 1.0 ? 65535 :
+		(ushort)(y_out * 65535.0 + 0.5));
+  }
+  free (A);
+}
+
 void CLASS canon_600_fixed_wb (int temp)
 {
   static const short mul[4][5] = {
@@ -472,14 +540,13 @@
 void CLASS canon_600_load_raw()
 {
   uchar  data[1120], *dp;
-  ushort pixel[896], *pix;
-  int irow, row, col, val;
-  static const short mul[4][2] =
-  { { 1141,1145 }, { 1128,1109 }, { 1178,1149 }, { 1128,1109 } };
+  ushort *pix;
+  int irow, row;
 
   for (irow=row=0; irow < height; irow++) {
-    if (fread (data, 1, raw_width*5/4, ifp) < raw_width*5/4) derror();
-    for (dp=data, pix=pixel; dp < data+1120; dp+=10, pix+=8) {
+    if (fread (data, 1, 1120, ifp) < 1120) derror();
+    pix = raw_image + row*raw_width;
+    for (dp=data; 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);
@@ -489,14 +556,16 @@
       pix[6] = (dp[7] << 2) + (dp[9] >> 4 & 3);
       pix[7] = (dp[8] << 2) + (dp[9] >> 6    );
     }
-    for (col=0; col < width; col++)
-      BAYER(row,col) = pixel[col];
-    for (col=width; col < raw_width; col++)
-      black += pixel[col];
     if ((row+=2) > height) row = 1;
   }
-  if (raw_width > width)
-    black = black / ((raw_width - width) * height) - 4;
+}
+
+void CLASS canon_600_correct()
+{
+  int row, col, val;
+  static const short mul[4][2] =
+  { { 1141,1145 }, { 1128,1109 }, { 1178,1149 }, { 1128,1109 } };
+
   for (row=0; row < height; row++)
     for (col=0; col < width; col++) {
       if ((val = BAYER(row,col) - black) < 0) val = 0;
@@ -510,23 +579,6 @@
   black = 0;
 }
 
-void CLASS remove_zeroes()
-{
-  unsigned row, col, tot, n, r, c;
-
-  for (row=0; row < height; row++)
-    for (col=0; col < width; col++)
-      if (BAYER(row,col) == 0) {
-	tot = n = 0;
-	for (r = row-2; r <= row+2; r++)
-	  for (c = col-2; c <= col+2; c++)
-	    if (r < height && c < width &&
-		FC(r,c) == FC(row,col) && BAYER(r,c))
-	      tot += (n++,BAYER(r,c));
-	if (n) BAYER(row,col) = tot/n;
-      }
-}
-
 int CLASS canon_s2is()
 {
   unsigned row;
@@ -538,57 +590,33 @@
   return 0;
 }
 
-void CLASS canon_a5_load_raw()
-{
-  ushort data[2565], *dp, pixel;
-  int vbits=0, buf=0, row, col, bc=0;
-
-  order = 0x4949;
-  for (row=-top_margin; row < raw_height-top_margin; row++) {
-    read_shorts (dp=data, raw_width * 10 / 16);
-    for (col=-left_margin; col < raw_width-left_margin; col++) {
-      if (vbits < 10)
-	buf = (vbits += 16, (buf << 16) + *dp++);
-      pixel = buf >> (vbits -= 10) & 0x3ff;
-      if ((unsigned) row < height && (unsigned) col < width)
-	BAYER(row,col) = pixel;
-      else if (col > 1-left_margin && col != width)
-	black += (bc++,pixel);
-    }
-  }
-  if (bc) black /= bc;
-  maximum = 0x3ff;
-  if (raw_width > 1600) remove_zeroes();
-}
-
-/*
-   getbits(-1) initializes the buffer
-   getbits(n) where 0 <= n <= 25 returns an n-bit integer
- */
-unsigned CLASS getbits (int nbits)
+unsigned CLASS getbithuff (int nbits, ushort *huff)
 {
   static unsigned bitbuf=0;
   static int vbits=0, reset=0;
   unsigned c;
 
-  if (nbits == -1)
+  if (nbits > 25) return 0;
+  if (nbits < 0)
     return bitbuf = vbits = reset = 0;
-  if (nbits == 0 || reset) return 0;
-  while (vbits < nbits) {
-    if ((c = fgetc(ifp)) == EOF) derror();
-    if ((reset = zero_after_ff && c == 0xff && fgetc(ifp))) return 0;
+  if (nbits == 0 || vbits < 0) return 0;
+  while (!reset && vbits < nbits && (c = fgetc(ifp)) != EOF &&
+    !(reset = zero_after_ff && c == 0xff && fgetc(ifp))) {
     bitbuf = (bitbuf << 8) + (uchar) c;
     vbits += 8;
   }
-  vbits -= nbits;
-  return bitbuf << (32-nbits-vbits) >> (32-nbits);
+  c = bitbuf << (32-vbits) >> (32-nbits);
+  if (huff) {
+    vbits -= huff[c] >> 8;
+    c = (uchar) huff[c];
+  } else
+    vbits -= nbits;
+  if (vbits < 0) derror();
+  return c;
 }
 
-void CLASS init_decoder()
-{
-  memset (first_decode, 0, sizeof first_decode);
-  free_decode = first_decode;
-}
+#define getbits(n) getbithuff(n,0)
+#define gethuff(h) getbithuff(*h,h+1)
 
 /*
    Construct a decode tree according the specification in *source.
@@ -616,33 +644,31 @@
 	1111110		0x0b
 	1111111		0xff
  */
-uchar * CLASS make_decoder (const uchar *source, int level)
+ushort * CLASS make_decoder_ref (const uchar **source)
 {
-  struct decode *cur;
-  static int leaf;
-  int i, next;
+  int max, len, h, i, j;
+  const uchar *count;
+  ushort *huff;
 
-  if (level==0) leaf=0;
-  cur = free_decode++;
-  if (free_decode > first_decode+2048) {
-    fprintf (stderr,_("%s: decoder table overflow\n"), ifname);
-    longjmp (failure, 2);
-  }
-  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 (uchar *) source + 16 + leaf;
+  count = (*source += 16) - 17;
+  for (max=16; max && !count[max]; max--);
+  huff = (ushort *) calloc (1 + (1 << max), sizeof *huff);
+  merror (huff, "make_decoder()");
+  huff[0] = max;
+  for (h=len=1; len <= max; len++)
+    for (i=0; i < count[len]; i++, ++*source)
+      for (j=0; j < 1 << (max-len); j++)
+	if (h <= 1 << max)
+	  huff[h++] = len << 8 | **source;
+  return huff;
+}
+
+ushort * CLASS make_decoder (const uchar *source)
+{
+  return make_decoder_ref (&source);
 }
 
-void CLASS crw_init_tables (unsigned table)
+void CLASS crw_init_tables (unsigned table, ushort *huff[2])
 {
   static const uchar first_tree[3][29] = {
     { 0,1,4,2,3,1,2,0,0,0,0,0,0,0,0,0,
@@ -700,10 +726,8 @@
       0xe2,0x82,0xf1,0xa3,0xc2,0xa1,0xc1,0xe3,0xa2,0xe1,0xff,0xff  }
   };
   if (table > 2) table = 2;
-  init_decoder();
-  make_decoder ( first_tree[table], 0);
-  second_decode = free_decode;
-  make_decoder (second_tree[table], 0);
+  huff[0] = make_decoder ( first_tree[table]);
+  huff[1] = make_decoder (second_tree[table]);
 }
 
 /*
@@ -727,33 +751,25 @@
   return ret;
 }
 
-void CLASS canon_compressed_load_raw()
+void CLASS canon_load_raw()
 {
-  ushort *pixel, *prow;
-  int nblocks, lowbits, i, row, r, col, save, val;
-  unsigned irow, icol;
-  struct decode *decode, *dindex;
+  ushort *pixel, *prow, *huff[2];
+  int nblocks, lowbits, i, c, row, r, save, val;
   int block, diffbuf[64], leaf, len, diff, carry=0, pnum=0, base[2];
-  uchar c;
 
-  crw_init_tables (tiff_compress);
-  pixel = (ushort *) calloc (raw_width*8, sizeof *pixel);
-  merror (pixel, "canon_compressed_load_raw()");
+  crw_init_tables (tiff_compress, huff);
   lowbits = canon_has_lowbits();
   if (!lowbits) maximum = 0x3ff;
   fseek (ifp, 540 + lowbits*raw_height*raw_width/4, SEEK_SET);
   zero_after_ff = 1;
   getbits(-1);
   for (row=0; row < raw_height; row+=8) {
+    pixel = raw_image + row*raw_width;
     nblocks = MIN (8, raw_height-row) * raw_width >> 6;
     for (block=0; block < nblocks; 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(1)];
-	leaf = dindex->leaf;
-	decode = second_decode;
+	leaf = gethuff(huff[i > 0]);
 	if (leaf == 0 && i) break;
 	if (leaf == 0xff) continue;
 	i  += leaf >> 4;
@@ -786,91 +802,84 @@
       }
       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;
+  FORC(2) free (huff[c]);
 }
 
-/*
-   Not a full implementation of Lossless JPEG, just
-   enough to decode Canon, Kodak and Adobe DNG images.
- */
 struct jhead {
-  int bits, high, wide, clrs, psv, restart, vpred[4];
-  struct CLASS decode *huff[4];
-  ushort *row;
+  int algo, bits, high, wide, clrs, sraw, psv, restart, vpred[6];
+  ushort quant[64], idct[64], *huff[20], *free[20], *row;
 };
 
 int CLASS ljpeg_start (struct jhead *jh, int info_only)
 {
-  int i, tag, len;
-  uchar data[0x10000], *dp;
+  ushort c, tag, len;
+  uchar data[0x10000];
+  const uchar *dp;
 
-  init_decoder();
   memset (jh, 0, sizeof *jh);
-  for (i=0; i < 4; i++)
-    jh->huff[i] = free_decode;
   jh->restart = INT_MAX;
-  fread (data, 2, 1, ifp);
-  if (data[1] != 0xd8) return 0;
+  if ((fgetc(ifp),fgetc(ifp)) != 0xd8) return 0;
   do {
-    fread (data, 2, 2, ifp);
+    if (!fread (data, 2, 2, ifp)) return 0;
     tag =  data[0] << 8 | data[1];
     len = (data[2] << 8 | data[3]) - 2;
     if (tag <= 0xff00) return 0;
     fread (data, 1, len, ifp);
     switch (tag) {
-      case 0xffc0: data[7] = 0;
       case 0xffc3:
+	jh->sraw = ((data[7] >> 4) * (data[7] & 15) - 1) & 3;
+      case 0xffc1:
+      case 0xffc0:
+	jh->algo = tag & 0xff;
 	jh->bits = data[0];
 	jh->high = data[1] << 8 | data[2];
 	jh->wide = data[3] << 8 | data[4];
-	jh->clrs = data[5] + (data[7] == 0x21);
+	jh->clrs = data[5] + jh->sraw;
 	if (len == 9 && !dng_version) getc(ifp);
 	break;
       case 0xffc4:
 	if (info_only) break;
-	for (dp = data; dp < data+len && *dp < 4; ) {
-	  jh->huff[*dp] = free_decode;
-	  dp = make_decoder (++dp, 0);
-	}
+	for (dp = data; dp < data+len && !((c = *dp++) & -20); )
+	  jh->free[c] = jh->huff[c] = make_decoder_ref (&dp);
 	break;
       case 0xffda:
 	jh->psv = data[1+data[0]*2];
+	jh->bits -= data[3+data[0]*2] & 15;
+	break;
+      case 0xffdb:
+	FORC(64) jh->quant[c] = data[c*2+1] << 8 | data[c*2+2];
 	break;
       case 0xffdd:
 	jh->restart = data[0] << 8 | data[1];
     }
   } while (tag != 0xffda);
+  if (jh->bits > 16 || jh->clrs > 6 ||
+     !jh->bits || !jh->high || !jh->wide || !jh->clrs) return 0;
   if (info_only) return 1;
-  if (jh->clrs == 4) {
-    jh->huff[3] = jh->huff[2] = jh->huff[1];
-    jh->huff[1] = jh->huff[0];
+  if (!jh->huff[0]) return 0;
+  FORC(19) if (!jh->huff[c+1]) jh->huff[c+1] = jh->huff[c];
+  if (jh->sraw) {
+    FORC(4)        jh->huff[2+c] = jh->huff[1];
+    FORC(jh->sraw) jh->huff[1+c] = jh->huff[0];
   }
   jh->row = (ushort *) calloc (jh->wide*jh->clrs, 4);
   merror (jh->row, "ljpeg_start()");
   return zero_after_ff = 1;
 }
 
-int CLASS ljpeg_diff (struct decode *dindex)
+void CLASS ljpeg_end (struct jhead *jh)
+{
+  int c;
+  FORC4 if (jh->free[c]) free (jh->free[c]);
+  free (jh->row);
+}
+
+int CLASS ljpeg_diff (ushort *huff)
 {
   int len, diff;
 
-  while (dindex->branch[0])
-    dindex = dindex->branch[getbits(1)];
-  len = dindex->leaf;
+  len = gethuff(huff);
   if (len == 16 && (!dng_version || dng_version >= 0x1010000))
     return -32768;
   diff = getbits(len);
@@ -881,22 +890,24 @@
 
 ushort * CLASS ljpeg_row (int jrow, struct jhead *jh)
 {
-  int col, c, diff, pred;
+  int col, c, diff, pred, spred=0;
   ushort mark=0, *row[3];
 
   if (jrow * jh->wide % jh->restart == 0) {
-    FORC4 jh->vpred[c] = 1 << (jh->bits-1);
-    if (jrow)
+    FORC(6) jh->vpred[c] = 1 << (jh->bits-1);
+    if (jrow) {
+      fseek (ifp, -2, SEEK_CUR);
       do mark = (mark << 8) + (c = fgetc(ifp));
       while (c != EOF && mark >> 4 != 0xffd);
+    }
     getbits(-1);
   }
   FORC3 row[c] = jh->row + jh->wide*jh->clrs*((jrow+c) & 1);
   for (col=0; col < jh->wide; col++)
     FORC(jh->clrs) {
       diff = ljpeg_diff (jh->huff[c]);
-      if (jh->clrs == 4 && c < 2 && (col | c))
-		    pred = row[0][(c << 1)-3];
+      if (jh->sraw && c <= jh->sraw && (col | c))
+		    pred = spred;
       else if (col) pred = row[0][-jh->clrs];
       else	    pred = (jh->vpred[c] += diff) - diff;
       if (jrow && col) switch (jh->psv) {
@@ -910,6 +921,7 @@
 	default: pred = 0;
       }
       if ((**row = pred + diff) >> jh->bits) derror();
+      if (c <= jh->sraw) spred = **row;
       row[0]++; row[1]++;
     }
   return row[2];
@@ -919,7 +931,6 @@
 {
   int jwide, jrow, jcol, val, jidx, i, j, row=0, col=0;
   struct jhead jh;
-  int min=INT_MAX;
   ushort *rp;
 
   if (!ljpeg_start (&jh, 0)) return;
@@ -927,109 +938,158 @@
 
   for (jrow=0; jrow < jh.high; jrow++) {
     rp = ljpeg_row (jrow, &jh);
+    if (load_flags & 1)
+      row = jrow & 1 ? height-1-jrow/2 : jrow/2;
     for (jcol=0; jcol < jwide; jcol++) {
-      val = *rp++;
-      if (jh.bits <= 12)
-	val = curve[val];
+      val = curve[*rp++];
       if (cr2_slice[0]) {
 	jidx = jrow*jwide + jcol;
-	i = jidx / (cr2_slice[1]*jh.high);
+	i = jidx / (cr2_slice[1]*raw_height);
 	if ((j = i >= cr2_slice[0]))
 		 i  = cr2_slice[0];
-	jidx -= i * (cr2_slice[1]*jh.high);
+	jidx -= i * (cr2_slice[1]*raw_height);
 	row = jidx / cr2_slice[1+j];
 	col = jidx % cr2_slice[1+j] + i*cr2_slice[1];
       }
       if (raw_width == 3984 && (col -= 2) < 0)
 	col += (row--,raw_width);
-      if ((unsigned) (row-top_margin) < height) {
-	if ((unsigned) (col-left_margin) < width) {
-	  BAYER(row-top_margin,col-left_margin) = val;
-	  if (min > val) min = val;
-	} else black += val;
-      }
+      if ((unsigned) row < raw_height) RAW(row,col) = val;
       if (++col >= raw_width)
 	col = (row++,0);
     }
   }
-  free (jh.row);
-  if (raw_width > width)
-    black /= (raw_width - width) * height;
-  if (!strcasecmp(make,"KODAK"))
-    black = min;
+  ljpeg_end (&jh);
 }
 
 void CLASS canon_sraw_load_raw()
 {
   struct jhead jh;
-  short *rp=0, *ip;
+  short *rp=0, (*ip)[4];
   int jwide, slice, scol, ecol, row, col, jrow=0, jcol=0, pix[3], c;
+  int v[3]={0,0,0}, ver, hue;
+  char *cp;
 
-  if (!ljpeg_start (&jh, 0)) return;
-  jwide = (jh.wide >>= 1) * 4;
+  if (!ljpeg_start (&jh, 0) || jh.clrs < 4) return;
+  jwide = (jh.wide >>= 1) * jh.clrs;
 
   for (ecol=slice=0; slice <= cr2_slice[0]; slice++) {
     scol = ecol;
-    ecol += cr2_slice[1] >> 1;
-    if (!cr2_slice[0] || ecol > width-1) ecol = width & -2;
-    for (row=0; row < height; row++) {
-      ip = (short *) image[row*width+scol];
-      for (col=scol; col < ecol; col+=2, jcol+=4, ip+=8) {
+    ecol += cr2_slice[1] * 2 / jh.clrs;
+    if (!cr2_slice[0] || ecol > raw_width-1) ecol = raw_width & -2;
+    for (row=0; row < height; row += (jh.clrs >> 1) - 1) {
+      ip = (short (*)[4]) image + row*width;
+      for (col=scol; col < ecol; col+=2, jcol+=jh.clrs) {
 	if ((jcol %= jwide) == 0)
 	  rp = (short *) ljpeg_row (jrow++, &jh);
-	ip[0] = rp[jcol];
-	ip[4] = rp[jcol+1];
-	ip[1] = (short) (rp[jcol+2] << 2) >> 2;
-	ip[2] = (short) (rp[jcol+3] << 2) >> 2;
+	if (col >= width) continue;
+	FORC (jh.clrs-2)
+	  ip[col + (c >> 1)*width + (c & 1)][0] = rp[jcol+c];
+	ip[col][1] = rp[jcol+jh.clrs-2] - 16384;
+	ip[col][2] = rp[jcol+jh.clrs-1] - 16384;
       }
     }
   }
-  for (row=0; row < height; row++) {
-    ip = (short *) image[row*width+1];
-    for (col=1; col < width-1; col+=2, ip+=8) {
-      ip[1] = (ip[-3] + ip[5] + 1) >> 1;
-      ip[2] = (ip[-2] + ip[6] + 1) >> 1;
-    }
-    if (col < width) { ip[1] = ip[-3]; ip[2] = ip[-2]; }
-    ip = (short *) image[row*width];
-    for (col=0; col < width; col++, ip+=4) {
-      pix[0] = ip[2] + ip[0];
-      pix[2] = ip[1] + ip[0];
-      pix[1] = ((ip[0] << 12) - ip[1]*778 - (ip[2] << 11)) >> 12;
-      FORC3 ip[c] = CLIP((pix[c] - 512) * sraw_mul[c] >> 10);
+  for (cp=model2; *cp && !isdigit(*cp); cp++);
+  sscanf (cp, "%d.%d.%d", v, v+1, v+2);
+  ver = (v[0]*1000 + v[1])*1000 + v[2];
+  hue = (jh.sraw+1) << 2;
+  if (unique_id >= 0x80000281 || (unique_id == 0x80000218 && ver > 1000006))
+    hue = jh.sraw << 1;
+  ip = (short (*)[4]) image;
+  rp = ip[0];
+  for (row=0; row < height; row++, ip+=width) {
+    if (row & (jh.sraw >> 1))
+      for (col=0; col < width; col+=2)
+	for (c=1; c < 3; c++)
+	  if (row == height-1)
+	       ip[col][c] =  ip[col-width][c];
+	  else ip[col][c] = (ip[col-width][c] + ip[col+width][c] + 1) >> 1;
+    for (col=1; col < width; col+=2)
+      for (c=1; c < 3; c++)
+	if (col == width-1)
+	     ip[col][c] =  ip[col-1][c];
+	else ip[col][c] = (ip[col-1][c] + ip[col+1][c] + 1) >> 1;
+  }
+  for ( ; rp < ip[0]; rp+=4) {
+    if (unique_id == 0x80000218 ||
+	unique_id == 0x80000250 ||
+	unique_id == 0x80000261 ||
+	unique_id == 0x80000281 ||
+	unique_id == 0x80000287) {
+      rp[1] = (rp[1] << 2) + hue;
+      rp[2] = (rp[2] << 2) + hue;
+      pix[0] = rp[0] + ((   50*rp[1] + 22929*rp[2]) >> 14);
+      pix[1] = rp[0] + ((-5640*rp[1] - 11751*rp[2]) >> 14);
+      pix[2] = rp[0] + ((29040*rp[1] -   101*rp[2]) >> 14);
+    } else {
+      if (unique_id < 0x80000218) rp[0] -= 512;
+      pix[0] = rp[0] + rp[2];
+      pix[2] = rp[0] + rp[1];
+      pix[1] = rp[0] + ((-778*rp[1] - (rp[2] << 11)) >> 12);
     }
+    FORC3 rp[c] = CLIP(pix[c] * sraw_mul[c] >> 10);
   }
-  free (jh.row);
+  ljpeg_end (&jh);
   maximum = 0x3fff;
 }
 
-void CLASS adobe_copy_pixel (int row, int col, ushort **rp)
+void CLASS adobe_copy_pixel (unsigned row, unsigned col, ushort **rp)
 {
-  unsigned r, c;
+  int c;
 
-  r = row -= top_margin;
-  c = col -= left_margin;
-  if (is_raw == 2 && shot_select) (*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 += is_raw;
+  if (tiff_samples == 2 && shot_select) (*rp)++;
+  if (raw_image) {
+    if (row < raw_height && col < raw_width)
+      RAW(row,col) = curve[**rp];
+    *rp += tiff_samples;
   } else {
-    if (r < height && c < width)
+    if (row < height && col < width)
       FORC(tiff_samples)
-	image[row*width+col][c] = (*rp)[c] < 0x1000 ? curve[(*rp)[c]]:(*rp)[c];
+	image[row*width+col][c] = curve[(*rp)[c]];
     *rp += tiff_samples;
   }
-  if (is_raw == 2 && shot_select) (*rp)--;
+  if (tiff_samples == 2 && shot_select) (*rp)--;
+}
+
+void CLASS ljpeg_idct (struct jhead *jh)
+{
+  int c, i, j, len, skip, coef;
+  float work[3][8][8];
+  static float cs[106] = { 0 };
+  static const uchar zigzag[80] =
+  {  0, 1, 8,16, 9, 2, 3,10,17,24,32,25,18,11, 4, 5,12,19,26,33,
+    40,48,41,34,27,20,13, 6, 7,14,21,28,35,42,49,56,57,50,43,36,
+    29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,
+    47,55,62,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63 };
+
+  if (!cs[0])
+    FORC(106) cs[c] = cos((c & 31)*M_PI/16)/2;
+  memset (work, 0, sizeof work);
+  work[0][0][0] = jh->vpred[0] += ljpeg_diff (jh->huff[0]) * jh->quant[0];
+  for (i=1; i < 64; i++ ) {
+    len = gethuff (jh->huff[16]);
+    i += skip = len >> 4;
+    if (!(len &= 15) && skip < 15) break;
+    coef = getbits(len);
+    if ((coef & (1 << (len-1))) == 0)
+      coef -= (1 << len) - 1;
+    ((float *)work)[zigzag[i]] = coef * jh->quant[i];
+  }
+  FORC(8) work[0][0][c] *= M_SQRT1_2;
+  FORC(8) work[0][c][0] *= M_SQRT1_2;
+  for (i=0; i < 8; i++)
+    for (j=0; j < 8; j++)
+      FORC(8) work[1][i][j] += work[0][i][c] * cs[(j*2+1)*c];
+  for (i=0; i < 8; i++)
+    for (j=0; j < 8; j++)
+      FORC(8) work[2][i][j] += work[1][c][j] * cs[(i*2+1)*c];
+
+  FORC(64) jh->idct[c] = CLIP(((float *)work[2])[c]+0.5);
 }
 
-void CLASS adobe_dng_load_raw_lj()
+void CLASS lossless_dng_load_raw()
 {
-  unsigned save, trow=0, tcol=0, jwide, jrow, jcol, row, col;
+  unsigned save, trow=0, tcol=0, jwide, jrow, jcol, row, col, i, j;
   struct jhead jh;
   ushort *rp;
 
@@ -1040,29 +1100,47 @@
     if (!ljpeg_start (&jh, 0)) break;
     jwide = jh.wide;
     if (filters) jwide *= jh.clrs;
-    jwide /= is_raw;
-    for (row=col=jrow=0; jrow < jh.high; jrow++) {
-      rp = ljpeg_row (jrow, &jh);
-      for (jcol=0; jcol < jwide; jcol++) {
-	adobe_copy_pixel (trow+row, tcol+col, &rp);
-	if (++col >= tile_width || col >= raw_width)
-	  row += 1 + (col = 0);
-      }
+    jwide /= MIN (is_raw, tiff_samples);
+    switch (jh.algo) {
+      case 0xc1:
+	jh.vpred[0] = 16384;
+	getbits(-1);
+	for (jrow=0; jrow+7 < jh.high; jrow += 8) {
+	  for (jcol=0; jcol+7 < jh.wide; jcol += 8) {
+	    ljpeg_idct (&jh);
+	    rp = jh.idct;
+	    row = trow + jcol/tile_width + jrow*2;
+	    col = tcol + jcol%tile_width;
+	    for (i=0; i < 16; i+=2)
+	      for (j=0; j < 8; j++)
+		adobe_copy_pixel (row+i, col+j, &rp);
+	  }
+	}
+	break;
+      case 0xc3:
+	for (row=col=jrow=0; jrow < jh.high; jrow++) {
+	  rp = ljpeg_row (jrow, &jh);
+	  for (jcol=0; jcol < jwide; jcol++) {
+	    adobe_copy_pixel (trow+row, tcol+col, &rp);
+	    if (++col >= tile_width || col >= raw_width)
+	      row += 1 + (col = 0);
+	  }
+	}
     }
     fseek (ifp, save+4, SEEK_SET);
     if ((tcol += tile_width) >= raw_width)
       trow += tile_length + (tcol = 0);
-    free (jh.row);
+    ljpeg_end (&jh);
   }
 }
 
-void CLASS adobe_dng_load_raw_nc()
+void CLASS packed_dng_load_raw()
 {
   ushort *pixel, *rp;
   int row, col;
 
-  pixel = (ushort *) calloc (raw_width * tiff_samples, sizeof *pixel);
-  merror (pixel, "adobe_dng_load_raw_nc()");
+  pixel = (ushort *) calloc (raw_width, tiff_samples*sizeof *pixel);
+  merror (pixel, "packed_dng_load_raw()");
   for (row=0; row < raw_height; row++) {
     if (tiff_bps == 16)
       read_shorts (pixel, raw_width * tiff_samples);
@@ -1077,29 +1155,34 @@
   free (pixel);
 }
 
-void CLASS pentax_k10_load_raw()
+void CLASS pentax_load_raw()
 {
-  static const uchar pentax_tree[] =
-  { 0,2,3,1,1,1,1,1,1,2,0,0,0,0,0,0,
-    3,4,2,5,1,6,0,7,8,9,10,11,12 };
-  int row, col, diff;
+  ushort bit[2][15], huff[4097];
+  int dep, row, col, diff, c, i;
   ushort vpred[2][2] = {{0,0},{0,0}}, hpred[2];
 
-  init_decoder();
-  make_decoder (pentax_tree, 0);
+  fseek (ifp, meta_offset, SEEK_SET);
+  dep = (get2() + 12) & 15;
+  fseek (ifp, 12, SEEK_CUR);
+  FORC(dep) bit[0][c] = get2();
+  FORC(dep) bit[1][c] = fgetc(ifp);
+  FORC(dep)
+    for (i=bit[0][c]; i <= ((bit[0][c]+(4096 >> bit[1][c])-1) & 4095); )
+      huff[++i] = bit[1][c] << 8 | c;
+  huff[0] = 12;
+  fseek (ifp, data_offset, SEEK_SET);
   getbits(-1);
-  for (row=0; row < height; row++)
+  for (row=0; row < raw_height; row++)
     for (col=0; col < raw_width; col++) {
-      diff = ljpeg_diff (first_decode);
+      diff = ljpeg_diff (huff);
       if (col < 2) hpred[col] = vpred[row & 1][col] += diff;
       else	   hpred[col & 1] += diff;
-      if (col < width)
-	BAYER(row,col) = hpred[col & 1];
-      if (hpred[col & 1] >> 12) derror();
+      RAW(row,col) = hpred[col & 1];
+      if (hpred[col & 1] >> tiff_bps) derror();
     }
 }
 
-void CLASS nikon_compressed_load_raw()
+void CLASS nikon_load_raw()
 {
   static const uchar nikon_tree[][32] = {
     { 0,1,5,1,1,1,1,1,1,2,0,0,0,0,0,0,	/* 12-bit lossy */
@@ -1114,17 +1197,16 @@
       8,0x5c,0x4b,0x3a,0x29,7,6,5,4,3,2,1,0,13,14 },
     { 0,1,4,2,2,3,1,2,0,0,0,0,0,0,0,0,	/* 14-bit lossless */
       7,6,8,5,9,4,10,3,11,12,2,0,1,13,14 } };
-  struct decode *dindex;
-  ushort ver0, ver1, vpred[2][2], hpred[2], csize;
-  int i, max, step=0, huff=0, split=0, row, col, len, shl, diff;
+  ushort *huff, ver0, ver1, vpred[2][2], hpred[2], csize;
+  int i, min, max, step=0, tree=0, split=0, row, col, len, shl, diff;
 
   fseek (ifp, meta_offset, SEEK_SET);
   ver0 = fgetc(ifp);
   ver1 = fgetc(ifp);
   if (ver0 == 0x49 || ver1 == 0x58)
     fseek (ifp, 2110, SEEK_CUR);
-  if (ver0 == 0x46) huff = 2;
-  if (tiff_bps == 14) huff += 3;
+  if (ver0 == 0x46) tree = 2;
+  if (tiff_bps == 14) tree += 3;
   read_shorts (vpred[0], 4);
   max = 1 << tiff_bps & 0x7fff;
   if ((csize = get2()) > 1)
@@ -1139,72 +1221,49 @@
     split = get2();
   } else if (ver0 != 0x46 && csize <= 0x4001)
     read_shorts (curve, max=csize);
-  init_decoder();
-  make_decoder (nikon_tree[huff], 0);
+  while (curve[max-2] == curve[max-1]) max--;
+  huff = make_decoder (nikon_tree[tree]);
   fseek (ifp, data_offset, SEEK_SET);
   getbits(-1);
-  for (row=0; row < height; row++) {
+  for (min=row=0; row < height; row++) {
     if (split && row == split) {
-      init_decoder();
-      make_decoder (nikon_tree[huff+1], 0);
+      free (huff);
+      huff = make_decoder (nikon_tree[tree+1]);
+      max += (min = 16) << 1;
     }
     for (col=0; col < raw_width; col++) {
-      for (dindex=first_decode; dindex->branch[0]; )
-	dindex = dindex->branch[getbits(1)];
-      len = dindex->leaf & 15;
-      shl = dindex->leaf >> 4;
+      i = gethuff(huff);
+      len = i & 15;
+      shl = i >> 4;
       diff = ((getbits(len-shl) << 1) + 1) << shl >> 1;
       if ((diff & (1 << (len-1))) == 0)
 	diff -= (1 << len) - !shl;
       if (col < 2) hpred[col] = vpred[row & 1][col] += diff;
       else	   hpred[col & 1] += diff;
-      if (hpred[col & 1] >= max) derror();
-      if ((unsigned) (col-left_margin) < width)
-	BAYER(row,col-left_margin) = curve[hpred[col & 1] & 0x3fff];
+      if ((ushort)(hpred[col & 1] + min) >= max) derror();
+      RAW(row,col) = curve[LIM((short)hpred[col & 1],0,0x3fff)];
     }
   }
+  free (huff);
 }
 
-void CLASS nikon_load_raw()
+void CLASS nikon_yuv_load_raw()
 {
-  int irow, row, col, i;
+  int row, col, yuv[4], rgb[3], b, c;
+  UINT64 bitbuf=0;
 
-  getbits(-1);
-  for (irow=0; irow < height; irow++) {
-    row = irow;
-    if (make[0] == 'O' || model[0] == 'E') {
-      row = irow * 2 % height + irow / (height/2);
-      if (row == 1 && data_offset == 0) {
-	fseek (ifp, 0, SEEK_END);
-	fseek (ifp, ftell(ifp)/2, SEEK_SET);
-	getbits(-1);
-      }
-    }
+  for (row=0; row < raw_height; row++)
     for (col=0; col < raw_width; col++) {
-      i = getbits(12);
-      if ((unsigned) (col-left_margin) < width)
-	BAYER(row,col-left_margin) = i;
-      if (tiff_compress > 32768 && (col % 10) == 9)
-	if (getbits(8)) derror();
+      if (!(b = col & 1)) {
+	bitbuf = 0;
+	FORC(6) bitbuf |= (UINT64) fgetc(ifp) << c*8;
+	FORC(4) yuv[c] = (bitbuf >> c*12 & 0xfff) - (c >> 1 << 11);
+      }
+      rgb[0] = yuv[b] + 1.370705*yuv[3];
+      rgb[1] = yuv[b] - 0.337633*yuv[2] - 0.698001*yuv[3];
+      rgb[2] = yuv[b] + 1.732446*yuv[2];
+      FORC3 image[row*width+col][c] = curve[LIM(rgb[c],0,0xfff)] / cam_mul[c];
     }
-  }
-}
-
-/*
-   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 CLASS nikon_is_compressed()
-{
-  uchar test[256];
-  int i;
-
-  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;
 }
 
 /*
@@ -1251,10 +1310,10 @@
     int bits;
     char make[12], model[15];
   } table[] = {
-    { 0x00, "PENTAX",  "Optio 33WR" },
-    { 0x03, "NIKON",   "E3200" },
-    { 0x32, "NIKON",   "E3700" },
-    { 0x33, "OLYMPUS", "C740UZ" } };
+    { 0x00, "Pentax",  "Optio 33WR" },
+    { 0x03, "Nikon",   "E3200" },
+    { 0x32, "Nikon",   "E3700" },
+    { 0x33, "Olympus", "C740UZ" } };
 
   fseek (ifp, 3072, SEEK_SET);
   fread (dp, 1, 24, ifp);
@@ -1271,105 +1330,46 @@
  */
 int CLASS minolta_z2()
 {
-  int i;
+  int i, nz;
   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;
-}
-
-/* Here raw_width is in bytes, not pixels. */
-void CLASS nikon_e900_load_raw()
-{
-  int offset=0, irow, row, col;
-
-  for (irow=0; irow < height; irow++) {
-    row = irow * 2 % height;
-    if (row == 1)
-      offset = - (-offset & -4096);
-    fseek (ifp, offset, SEEK_SET);
-    offset += raw_width;
-    getbits(-1);
-    for (col=0; col < width; col++)
-      BAYER(row,col) = getbits(10);
-  }
-}
-
-void CLASS nikon_e2100_load_raw()
-{
-  uchar   data[4608], *dp;
-  ushort pixel[3072], *pix;
-  int row, col;
-
-  for (row=0; row <= height; row+=2) {
-    if (row == height) {
-      fseek (ifp, 0, SEEK_END);
-      fseek (ifp, ftell(ifp)/2, 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);
-  }
+  for (nz=i=0; i < sizeof tail; i++)
+    if (tail[i]) nz++;
+  return nz > 20;
 }
 
-/*
-   The Fuji Super CCD is just a Bayer grid rotated 45 degrees.
- */
-void CLASS fuji_load_raw()
-{
-  ushort *pixel;
-  int wide, row, col, r, c;
-
-  fseek (ifp, (top_margin*raw_width + left_margin) * 2, SEEK_CUR);
-  wide = fuji_width << !fuji_layout;
-  pixel = (ushort *) calloc (wide, sizeof *pixel);
-  merror (pixel, "fuji_load_raw()");
-  for (row=0; row < raw_height; row++) {
-    read_shorts (pixel, wide);
-    fseek (ifp, 2*(raw_width - wide), SEEK_CUR);
-    for (col=0; col < wide; col++) {
-      if (fuji_layout) {
-	r = fuji_width - 1 - col + (row >> 1);
-	c = col + ((row+1) >> 1);
-      } else {
-	r = fuji_width - 1 + row - (col >> 1);
-	c = row + ((col+1) >> 1);
-      }
-      BAYER(r,c) = pixel[col];
-    }
-  }
-  free (pixel);
-}
-
-void CLASS jpeg_thumb (FILE *tfp);
+void CLASS jpeg_thumb();
 
-void CLASS ppm_thumb (FILE *tfp)
+void CLASS ppm_thumb()
 {
   char *thumb;
   thumb_length = thumb_width*thumb_height*3;
   thumb = (char *) malloc (thumb_length);
   merror (thumb, "ppm_thumb()");
-  fprintf (tfp, "P6\n%d %d\n255\n", thumb_width, thumb_height);
+  fprintf (ofp, "P6\n%d %d\n255\n", thumb_width, thumb_height);
   fread  (thumb, 1, thumb_length, ifp);
-  fwrite (thumb, 1, thumb_length, tfp);
+  fwrite (thumb, 1, thumb_length, ofp);
+  free (thumb);
+}
+
+void CLASS ppm16_thumb()
+{
+  int i;
+  char *thumb;
+  thumb_length = thumb_width*thumb_height*3;
+  thumb = (char *) calloc (thumb_length, 2);
+  merror (thumb, "ppm16_thumb()");
+  read_shorts ((ushort *) thumb, thumb_length);
+  for (i=0; i < thumb_length; i++)
+    thumb[i] = ((ushort *) thumb)[i] >> 8;
+  fprintf (ofp, "P6\n%d %d\n255\n", thumb_width, thumb_height);
+  fwrite (thumb, 1, thumb_length, ofp);
   free (thumb);
 }
 
-void CLASS layer_thumb (FILE *tfp)
+void CLASS layer_thumb()
 {
   int i, c;
   char *thumb, map[][4] = { "012","102" };
@@ -1378,15 +1378,15 @@
   thumb_length = thumb_width*thumb_height;
   thumb = (char *) calloc (colors, thumb_length);
   merror (thumb, "layer_thumb()");
-  fprintf (tfp, "P%d\n%d %d\n255\n",
+  fprintf (ofp, "P%d\n%d %d\n255\n",
 	5 + (colors >> 1), thumb_width, thumb_height);
   fread (thumb, thumb_length, colors, ifp);
   for (i=0; i < thumb_length; i++)
-    FORCC putc (thumb[i+thumb_length*(map[thumb_misc >> 8][c]-'0')], tfp);
+    FORCC putc (thumb[i+thumb_length*(map[thumb_misc >> 8][c]-'0')], ofp);
   free (thumb);
 }
 
-void CLASS rollei_thumb (FILE *tfp)
+void CLASS rollei_thumb()
 {
   unsigned i;
   ushort *thumb;
@@ -1394,12 +1394,12 @@
   thumb_length = thumb_width * thumb_height;
   thumb = (ushort *) calloc (thumb_length, 2);
   merror (thumb, "rollei_thumb()");
-  fprintf (tfp, "P6\n%d %d\n255\n", thumb_width, thumb_height);
+  fprintf (ofp, "P6\n%d %d\n255\n", thumb_width, thumb_height);
   read_shorts (thumb, thumb_length);
   for (i=0; i < thumb_length; i++) {
-    putc (thumb[i] << 3, tfp);
-    putc (thumb[i] >> 5  << 2, tfp);
-    putc (thumb[i] >> 11 << 3, tfp);
+    putc (thumb[i] << 3, ofp);
+    putc (thumb[i] >> 5  << 2, ofp);
+    putc (thumb[i] >> 11 << 3, ofp);
   }
   free (thumb);
 }
@@ -1407,7 +1407,7 @@
 void CLASS rollei_load_raw()
 {
   uchar pixel[10];
-  unsigned iten=0, isix, i, buffer=0, row, col, todo[16];
+  unsigned iten=0, isix, i, buffer=0, todo[16];
 
   isix = raw_width * raw_height * 5 / 8;
   while (fread (pixel, 1, 10, ifp) == 10) {
@@ -1420,32 +1420,30 @@
       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);
-    }
+    for (i=0; i < 16; i+=2)
+      raw_image[todo[i]] = (todo[i+1] & 0x3ff);
   }
   maximum = 0x3ff;
 }
 
-int CLASS bayer (unsigned row, unsigned col)
+int CLASS raw (unsigned row, unsigned col)
 {
-  return (row < height && col < width) ? BAYER(row,col) : 0;
+  return (row < raw_height && col < raw_width) ? RAW(row,col) : 0;
 }
 
 void CLASS phase_one_flat_field (int is_float, int nc)
 {
   ushort head[8];
-  unsigned wide, y, x, c, rend, cend, row, col;
+  unsigned wide, high, y, x, c, rend, cend, row, col;
   float *mrow, num, mult[4];
 
   read_shorts (head, 8);
-  wide = head[2] / head[4];
+  if (head[2] * head[3] * head[4] * head[5] == 0) return;
+  wide = head[2] / head[4] + (head[2] % head[4] != 0);
+  high = head[3] / head[5] + (head[3] % head[5] != 0);
   mrow = (float *) calloc (nc*wide, sizeof *mrow);
   merror (mrow, "phase_one_flat_field()");
-  for (y=0; y < head[3] / head[5]; y++) {
+  for (y=0; y < high; y++) {
     for (x=0; x < wide; x++)
       for (c=0; c < nc; c+=2) {
 	num = is_float ? getreal(11) : get2()/32768.0;
@@ -1453,19 +1451,23 @@
 	else mrow[(c+1)*wide+x] = (num - mrow[c*wide+x]) / head[5];
       }
     if (y==0) continue;
-    rend = head[1]-top_margin + y*head[5];
-    for (row = rend-head[5]; row < height && row < rend; row++) {
+    rend = head[1] + y*head[5];
+    for (row = rend-head[5];
+	 row < raw_height && row < rend &&
+	 row < head[1]+head[3]-head[5]; row++) {
       for (x=1; x < wide; x++) {
 	for (c=0; c < nc; c+=2) {
 	  mult[c] = mrow[c*wide+x-1];
 	  mult[c+1] = (mrow[c*wide+x] - mult[c]) / head[4];
 	}
-	cend = head[0]-left_margin + x*head[4];
-	for (col = cend-head[4]; col < width && col < cend; col++) {
-	  c = nc > 2 ? FC(row,col) : 0;
+	cend = head[0] + x*head[4];
+	for (col = cend-head[4];
+	     col < raw_width &&
+	     col < cend && col < head[0]+head[2]-head[4]; col++) {
+	  c = nc > 2 ? FC(row-top_margin,col-left_margin) : 0;
 	  if (!(c & 1)) {
-	    c = BAYER(row,col) * mult[c];
-	    BAYER(row,col) = LIM(c,0,65535);
+	    c = RAW(row,col) * mult[c];
+	    RAW(row,col) = LIM(c,0,65535);
 	  }
 	  for (c=0; c < nc; c+=2)
 	    mult[c] += mult[c+1];
@@ -1488,7 +1490,8 @@
     { {-1,-1}, {-1,1}, {1,-1}, {1,1}, {-2,0}, {0,-2}, {0,2}, {2,0},
       {-2,-2}, {-2,2}, {2,-2}, {2,2} };
   float poly[8], num, cfrac, frac, mult[2], *yval[2];
-  ushort curve[0x10000], *xval[2];
+  ushort *xval[2];
+  int qmult_applied = 0, qlin_applied = 0;
 
   if (half_size || !meta_length) return;
   if (verbose) fprintf (stderr,_("Phase One correction...\n"));
@@ -1519,37 +1522,37 @@
 	  num = num * i + poly[j];
 	curve[i] = LIM(num+i,0,65535);
       } apply:					/* apply to whole image */
-      for (row=0; row < height; row++)
-	for (col = (tag & 1)*ph1.split_col; col < width; col++)
-	  BAYER(row,col) = curve[BAYER(row,col)];
+      for (row=0; row < raw_height; row++)
+	for (col = (tag & 1)*ph1.split_col; col < raw_width; col++)
+	  RAW(row,col) = curve[RAW(row,col)];
     } else if (tag == 0x400) {			/* Sensor defects */
       while ((len -= 8) >= 0) {
-	col  = get2() - left_margin;
-	row  = get2() - top_margin;
+	col  = get2();
+	row  = get2();
 	type = get2(); get2();
-	if (col >= width) continue;
-	if (type == 131)			/* Bad column */
-	  for (row=0; row < height; row++)
-	    if (FC(row,col) == 1) {
+	if (col >= raw_width) continue;
+	if (type == 131 || type == 137)		/* Bad column */
+	  for (row=0; row < raw_height; row++)
+	    if (FC(row-top_margin,col-left_margin) == 1) {
 	      for (sum=i=0; i < 4; i++)
-		sum += val[i] = bayer (row+dir[i][0], col+dir[i][1]);
+		sum += val[i] = raw (row+dir[i][0], col+dir[i][1]);
 	      for (max=i=0; i < 4; i++) {
 		dev[i] = abs((val[i] << 2) - sum);
 		if (dev[max] < dev[i]) max = i;
 	      }
-	      BAYER(row,col) = (sum - val[max])/3.0 + 0.5;
+	      RAW(row,col) = (sum - val[max])/3.0 + 0.5;
 	    } else {
 	      for (sum=0, i=8; i < 12; i++)
-		sum += bayer (row+dir[i][0], col+dir[i][1]);
-	      BAYER(row,col) = 0.5 + sum * 0.0732233 +
-		(bayer(row,col-2) + bayer(row,col+2)) * 0.3535534;
+		sum += raw (row+dir[i][0], col+dir[i][1]);
+	      RAW(row,col) = 0.5 + sum * 0.0732233 +
+		(raw(row,col-2) + raw(row,col+2)) * 0.3535534;
 	    }
 	else if (type == 129) {			/* Bad pixel */
-	  if (row >= height) continue;
-	  j = (FC(row,col) != 1) * 4;
+	  if (row >= raw_height) continue;
+	  j = (FC(row-top_margin,col-left_margin) != 1) * 4;
 	  for (sum=0, i=j; i < j+8; i++)
-	    sum += bayer (row+dir[i][0], col+dir[i][1]);
-	  BAYER(row,col) = (sum + 4) >> 3;
+	    sum += raw (row+dir[i][0], col+dir[i][1]);
+	  RAW(row,col) = (sum + 4) >> 3;
 	}
       }
     } else if (tag == 0x401) {			/* All-color flat fields */
@@ -1565,6 +1568,83 @@
 	mindiff = diff;
 	off_412 = ftell(ifp) - 38;
       }
+    } else if (tag == 0x41f && !qlin_applied) { /* Quadrant linearization */
+      ushort lc[2][2][16], ref[16];
+      int qr, qc;
+      for (qr = 0; qr < 2; qr++)
+	for (qc = 0; qc < 2; qc++)
+	  for (i = 0; i < 16; i++)
+	    lc[qr][qc][i] = get4();
+      for (i = 0; i < 16; i++) {
+	int v = 0;
+	for (qr = 0; qr < 2; qr++)
+	  for (qc = 0; qc < 2; qc++)
+	    v += lc[qr][qc][i];
+	ref[i] = (v + 2) >> 2;
+      }
+      for (qr = 0; qr < 2; qr++) {
+	for (qc = 0; qc < 2; qc++) {
+	  int cx[19], cf[19];
+	  for (i = 0; i < 16; i++) {
+	    cx[1+i] = lc[qr][qc][i];
+	    cf[1+i] = ref[i];
+	  }
+	  cx[0] = cf[0] = 0;
+	  cx[17] = cf[17] = ((unsigned) ref[15] * 65535) / lc[qr][qc][15];
+	  cx[18] = cf[18] = 65535;
+	  cubic_spline(cx, cf, 19);
+	  for (row = (qr ? ph1.split_row : 0);
+	       row < (qr ? raw_height : ph1.split_row); row++)
+	    for (col = (qc ? ph1.split_col : 0);
+		 col < (qc ? raw_width : ph1.split_col); col++)
+	      RAW(row,col) = curve[RAW(row,col)];
+	}
+      }
+      qlin_applied = 1;
+    } else if (tag == 0x41e && !qmult_applied) { /* Quadrant multipliers */
+      float qmult[2][2] = { { 1, 1 }, { 1, 1 } };
+      get4(); get4(); get4(); get4();
+      qmult[0][0] = 1.0 + getreal(11);
+      get4(); get4(); get4(); get4(); get4();
+      qmult[0][1] = 1.0 + getreal(11);
+      get4(); get4(); get4();
+      qmult[1][0] = 1.0 + getreal(11);
+      get4(); get4(); get4();
+      qmult[1][1] = 1.0 + getreal(11);
+      for (row=0; row < raw_height; row++)
+	for (col=0; col < raw_width; col++) {
+	  i = qmult[row >= ph1.split_row][col >= ph1.split_col] * RAW(row,col);
+	  RAW(row,col) = LIM(i,0,65535);
+	}
+      qmult_applied = 1;
+    } else if (tag == 0x431 && !qmult_applied) { /* Quadrant combined */
+      ushort lc[2][2][7], ref[7];
+      int qr, qc;
+      for (i = 0; i < 7; i++)
+	ref[i] = get4();
+      for (qr = 0; qr < 2; qr++)
+	for (qc = 0; qc < 2; qc++)
+	  for (i = 0; i < 7; i++)
+	    lc[qr][qc][i] = get4();
+      for (qr = 0; qr < 2; qr++) {
+	for (qc = 0; qc < 2; qc++) {
+	  int cx[9], cf[9];
+	  for (i = 0; i < 7; i++) {
+	    cx[1+i] = ref[i];
+	    cf[1+i] = ((unsigned) ref[i] * lc[qr][qc][i]) / 10000;
+	  }
+	  cx[0] = cf[0] = 0;
+	  cx[8] = cf[8] = 65535;
+	  cubic_spline(cx, cf, 9);
+	  for (row = (qr ? ph1.split_row : 0);
+	       row < (qr ? raw_height : ph1.split_row); row++)
+	    for (col = (qc ? ph1.split_col : 0);
+		 col < (qc ? raw_width : ph1.split_col); col++)
+	      RAW(row,col) = curve[RAW(row,col)];
+	}
+      }
+      qmult_applied = 1;
+      qlin_applied = 1;
     }
     fseek (ifp, save, SEEK_SET);
   }
@@ -1583,11 +1663,11 @@
     for (i=0; i < 2; i++)
       for (j=0; j < head[i+1]*head[i+3]; j++)
 	xval[i][j] = get2();
-    for (row=0; row < height; row++)
-      for (col=0; col < width; col++) {
+    for (row=0; row < raw_height; row++)
+      for (col=0; col < raw_width; col++) {
 	cfrac = (float) col * head[3] / raw_width;
 	cfrac -= cip = cfrac;
-	num = BAYER(row,col) * 0.5;
+	num = RAW(row,col) * 0.5;
 	for (i=cip; i < cip+2; i++) {
 	  for (k=j=0; j < head[1]; j++)
 	    if (num < xval[0][k = head[1]*i+j]) break;
@@ -1595,9 +1675,8 @@
 		(xval[0][k] - num) / (xval[0][k] - xval[0][k-1]);
 	  mult[i-cip] = yval[0][k-1] * frac + yval[0][k] * (1-frac);
 	}
-	i = ((mult[0] * (1-cfrac) + mult[1] * cfrac)
-		* (row + top_margin) + num) * 2;
-	BAYER(row,col) = LIM(i,0,65535);
+	i = ((mult[0] * (1-cfrac) + mult[1] * cfrac) * row + num) * 2;
+	RAW(row,col) = LIM(i,0,65535);
       }
     free (yval[0]);
   }
@@ -1605,35 +1684,29 @@
 
 void CLASS phase_one_load_raw()
 {
-  int row, col, a, b;
-  ushort *pixel, akey, bkey, mask;
+  int a, b, i;
+  ushort akey, bkey, mask;
 
   fseek (ifp, ph1.key_off, SEEK_SET);
   akey = get2();
   bkey = get2();
   mask = ph1.format == 1 ? 0x5555:0x1354;
-  fseek (ifp, data_offset + top_margin*raw_width*2, SEEK_SET);
-  pixel = (ushort *) calloc (raw_width, sizeof *pixel);
-  merror (pixel, "phase_one_load_raw()");
-  for (row=0; row < height; row++) {
-    read_shorts (pixel, raw_width);
-    for (col=0; col < raw_width; col+=2) {
-      a = pixel[col+0] ^ akey;
-      b = pixel[col+1] ^ bkey;
-      pixel[col+0] = (a & mask) | (b & ~mask);
-      pixel[col+1] = (b & mask) | (a & ~mask);
+  fseek (ifp, data_offset, SEEK_SET);
+  read_shorts (raw_image, raw_width*raw_height);
+  if (ph1.format)
+    for (i=0; i < raw_width*raw_height; i+=2) {
+      a = raw_image[i+0] ^ akey;
+      b = raw_image[i+1] ^ bkey;
+      raw_image[i+0] = (a & mask) | (b & ~mask);
+      raw_image[i+1] = (b & mask) | (a & ~mask);
     }
-    for (col=0; col < width; col++)
-      BAYER(row,col) = pixel[col+left_margin];
-  }
-  free (pixel);
-  phase_one_correct();
 }
 
-unsigned CLASS ph1_bits (int nbits)
+unsigned CLASS ph1_bithuff (int nbits, ushort *huff)
 {
   static UINT64 bitbuf=0;
   static int vbits=0;
+  unsigned c;
 
   if (nbits == -1)
     return bitbuf = vbits = 0;
@@ -1642,27 +1715,38 @@
     bitbuf = bitbuf << 32 | get4();
     vbits += 32;
   }
+  c = bitbuf << (64-vbits) >> (64-nbits);
+  if (huff) {
+    vbits -= huff[c] >> 8;
+    return (uchar) huff[c];
+  }
   vbits -= nbits;
-  return bitbuf << (64-nbits-vbits) >> (64-nbits);
+  return c;
 }
+#define ph1_bits(n) ph1_bithuff(n,0)
+#define ph1_huff(h) ph1_bithuff(*h,h+1)
 
 void CLASS phase_one_load_raw_c()
 {
   static const int length[] = { 8,7,6,9,11,10,5,12,14,13 };
   int *offset, len[2], pred[2], row, col, i, j;
   ushort *pixel;
-  short (*black)[2];
+  short (*cblack)[2], (*rblack)[2];
 
-  pixel = (ushort *) calloc (raw_width + raw_height*4, 2);
+  pixel = (ushort *) calloc (raw_width*3 + raw_height*4, 2);
   merror (pixel, "phase_one_load_raw_c()");
   offset = (int *) (pixel + raw_width);
   fseek (ifp, strip_offset, SEEK_SET);
   for (row=0; row < raw_height; row++)
     offset[row] = get4();
-  black = (short (*)[2]) offset + raw_height;
-  fseek (ifp, ph1.black_off, SEEK_SET);
-  if (ph1.black_off)
-    read_shorts ((ushort *) black[0], raw_height*2);
+  cblack = (short (*)[2]) (offset + raw_height);
+  fseek (ifp, ph1.black_col, SEEK_SET);
+  if (ph1.black_col)
+    read_shorts ((ushort *) cblack[0], raw_height*2);
+  rblack = cblack + raw_height;
+  fseek (ifp, ph1.black_row, SEEK_SET);
+  if (ph1.black_row)
+    read_shorts ((ushort *) rblack[0], raw_width*2);
   for (i=0; i < 256; i++)
     curve[i] = i*i / 3.969 + 0.5;
   for (row=0; row < raw_height; row++) {
@@ -1685,94 +1769,127 @@
       if (ph1.format == 5 && pixel[col] < 256)
 	pixel[col] = curve[pixel[col]];
     }
-    if ((unsigned) (row-top_margin) < height)
-      for (col=0; col < width; col++) {
-	i = (pixel[col+left_margin] << 2)
-		- ph1.black + black[row][col >= ph1.split_col];
-	if (i > 0) BAYER(row-top_margin,col) = i;
-      }
+    for (col=0; col < raw_width; col++) {
+      i = (pixel[col] << 2*(ph1.format != 8)) - ph1.black
+	+ cblack[row][col >= ph1.split_col]
+	+ rblack[col][row >= ph1.split_row];
+      if (i > 0) RAW(row,col) = i;
+    }
   }
   free (pixel);
-  phase_one_correct();
   maximum = 0xfffc - ph1.black;
 }
 
 void CLASS hasselblad_load_raw()
 {
   struct jhead jh;
-  struct decode *dindex;
-  int row, col, pred[2], len[2], diff, i;
+  int shot, row, col, *back[5], len[2], diff[12], pred, sh, f, s, c;
+  unsigned upix, urow, ucol;
+  ushort *ip;
 
   if (!ljpeg_start (&jh, 0)) return;
-  free (jh.row);
+  order = 0x4949;
   ph1_bits(-1);
-  for (row=-top_margin; row < height; row++) {
-    pred[0] = pred[1] = 0x8000;
-    for (col=-left_margin; col < raw_width-left_margin; col+=2) {
-      for (i=0; i < 2; i++) {
-	for (dindex=jh.huff[0]; dindex->branch[0]; )
-	  dindex = dindex->branch[ph1_bits(1)];
-	len[i] = dindex->leaf;
-      }
-      for (i=0; i < 2; i++) {
-	diff = ph1_bits(len[i]);
-	if ((diff & (1 << (len[i]-1))) == 0)
-	  diff -= (1 << len[i]) - 1;
-	pred[i] += diff;
-	if (row >= 0 && (unsigned)(col+i) < width)
-	  BAYER(row,col+i) = pred[i];
+  back[4] = (int *) calloc (raw_width, 3*sizeof **back);
+  merror (back[4], "hasselblad_load_raw()");
+  FORC3 back[c] = back[4] + c*raw_width;
+  cblack[6] >>= sh = tiff_samples > 1;
+  shot = LIM(shot_select, 1, tiff_samples) - 1;
+  for (row=0; row < raw_height; row++) {
+    FORC4 back[(c+3) & 3] = back[c];
+    for (col=0; col < raw_width; col+=2) {
+      for (s=0; s < tiff_samples*2; s+=2) {
+	FORC(2) len[c] = ph1_huff(jh.huff[0]);
+	FORC(2) {
+	  diff[s+c] = ph1_bits(len[c]);
+	  if ((diff[s+c] & (1 << (len[c]-1))) == 0)
+	    diff[s+c] -= (1 << len[c]) - 1;
+	  if (diff[s+c] == 65535) diff[s+c] = -32768;
+	}
+      }
+      for (s=col; s < col+2; s++) {
+	pred = 0x8000 + load_flags;
+	if (col) pred = back[2][s-2];
+	if (col && row > 1) switch (jh.psv) {
+	  case 11: pred += back[0][s]/2 - back[0][s-2]/2;  break;
+	}
+	f = (row & 1)*3 ^ ((col+s) & 1);
+	FORC (tiff_samples) {
+	  pred += diff[(s & 1)*tiff_samples+c];
+	  upix = pred >> sh & 0xffff;
+	  if (raw_image && c == shot)
+	    RAW(row,s) = upix;
+	  if (image) {
+	    urow = row-top_margin  + (c & 1);
+	    ucol = col-left_margin - ((c >> 1) & 1);
+	    ip = &image[urow*width+ucol][f];
+	    if (urow < height && ucol < width)
+	      *ip = c < 4 ? upix : (*ip + upix) >> 1;
+	  }
+	}
+	back[2][s] = pred;
       }
     }
   }
-  maximum = 0xffff;
+  free (back[4]);
+  ljpeg_end (&jh);
+  if (image) mix_green = 1;
 }
 
 void CLASS leaf_hdr_load_raw()
 {
-  ushort *pixel;
+  ushort *pixel=0;
   unsigned tile=0, r, c, row, col;
 
-  pixel = (ushort *) calloc (raw_width, sizeof *pixel);
-  merror (pixel, "leaf_hdr_load_raw()");
+  if (!filters) {
+    pixel = (ushort *) calloc (raw_width, sizeof *pixel);
+    merror (pixel, "leaf_hdr_load_raw()");
+  }
   FORC(tiff_samples)
     for (r=0; r < raw_height; r++) {
       if (r % tile_length == 0) {
 	fseek (ifp, data_offset + 4*tile++, SEEK_SET);
-	fseek (ifp, get4() + 2*left_margin, SEEK_SET);
+	fseek (ifp, get4(), SEEK_SET);
       }
       if (filters && c != shot_select) continue;
+      if (filters) pixel = raw_image + r*raw_width;
       read_shorts (pixel, raw_width);
-      if ((row = r - top_margin) >= height) continue;
-      for (col=0; col < width; col++)
-	if (filters)  BAYER(row,col) = pixel[col];
-	else image[row*width+col][c] = pixel[col];
+      if (!filters && (row = r - top_margin) < height)
+	for (col=0; col < width; col++)
+	  image[row*width+col][c] = pixel[col+left_margin];
     }
-  free (pixel);
   if (!filters) {
     maximum = 0xffff;
     raw_color = 1;
+    free (pixel);
   }
 }
 
-void CLASS unpacked_load_raw();
+void CLASS unpacked_load_raw()
+{
+  int row, col, bits=0;
+
+  while (1 << ++bits < maximum);
+  read_shorts (raw_image, raw_width*raw_height);
+  for (row=0; row < raw_height; row++)
+    for (col=0; col < raw_width; col++)
+      if ((RAW(row,col) >>= load_flags) >> bits
+	&& (unsigned) (row-top_margin) < height
+	&& (unsigned) (col-left_margin) < width) derror();
+}
 
 void CLASS sinar_4shot_load_raw()
 {
   ushort *pixel;
   unsigned shot, row, col, r, c;
 
-  if ((shot = shot_select) || half_size) {
-    if (shot) shot--;
-    if (shot > 3) shot = 3;
+  if (raw_image) {
+    shot = LIM (shot_select, 1, 4) - 1;
     fseek (ifp, data_offset + shot*4, SEEK_SET);
     fseek (ifp, get4(), SEEK_SET);
     unpacked_load_raw();
     return;
   }
-  free (image);
-  image = (ushort (*)[4])
-	calloc ((iheight=height)*(iwidth=width), sizeof *image);
-  merror (image, "sinar_4shot_load_raw()");
   pixel = (ushort *) calloc (raw_width, sizeof *pixel);
   merror (pixel, "sinar_4shot_load_raw()");
   for (shot=0; shot < 4; shot++) {
@@ -1783,199 +1900,197 @@
       if ((r = row-top_margin - (shot >> 1 & 1)) >= height) continue;
       for (col=0; col < raw_width; col++) {
 	if ((c = col-left_margin - (shot & 1)) >= width) continue;
-        image[r*width+c][FC(row,col)] = pixel[col];
+	image[r*width+c][(row & 1)*3 ^ (~col & 1)] = pixel[col];
       }
     }
   }
   free (pixel);
-  shrink = filters = 0;
+  mix_green = 1;
 }
 
 void CLASS imacon_full_load_raw()
 {
   int row, col;
 
+  if (!image) return;
   for (row=0; row < height; row++)
     for (col=0; col < width; col++)
       read_shorts (image[row*width+col], 3);
 }
 
-void CLASS packed_12_load_raw()
+void CLASS packed_load_raw()
 {
-  int row, col;
-
-  if (raw_width * 2 < width * 3)
-    raw_width = raw_width * 3 / 2;	/* Convert raw_width to bytes */
-  getbits(-1);
-  for (row=0; row < height; row++) {
-    for (col=0; col < left_margin; col++)
-      getbits(12);
-    for (col=0; col < width; col++)
-      BAYER(row,col) = getbits(12);
-    for (col = (width+left_margin)*3/2; col < raw_width; col++)
-      if (getbits(8) && raw_width-col < 35 && width != 3896) derror();
-  }
-}
+  int vbits=0, bwide, rbits, bite, half, irow, row, col, val, i;
+  UINT64 bitbuf=0;
 
-void CLASS unpacked_load_raw()
-{
-  ushort *pixel;
-  int row, col, bits=0;
-
-  while (1 << ++bits < maximum);
-  fseek (ifp, (top_margin*raw_width + left_margin) * 2, SEEK_CUR);
-  pixel = (ushort *) calloc (width, sizeof *pixel);
-  merror (pixel, "unpacked_load_raw()");
-  for (row=0; row < height; row++) {
-    read_shorts (pixel, width);
-    fseek (ifp, 2*(raw_width - width), SEEK_CUR);
-    for (col=0; col < width; col++)
-      if ((BAYER2(row,col) = pixel[col]) >> bits) derror();
+  bwide = raw_width * tiff_bps / 8;
+  bwide += bwide & load_flags >> 9;
+  rbits = bwide * 8 - raw_width * tiff_bps;
+  if (load_flags & 1) bwide = bwide * 16 / 15;
+  bite = 8 + (load_flags & 56);
+  half = (raw_height+1) >> 1;
+  for (irow=0; irow < raw_height; irow++) {
+    row = irow;
+    if (load_flags & 2 &&
+	(row = irow % half * 2 + irow / half) == 1 &&
+	load_flags & 4) {
+      if (vbits=0, tiff_compress)
+	fseek (ifp, data_offset - (-half*bwide & -2048), SEEK_SET);
+      else {
+	fseek (ifp, 0, SEEK_END);
+	fseek (ifp, ftell(ifp) >> 3 << 2, SEEK_SET);
+      }
+    }
+    for (col=0; col < raw_width; col++) {
+      for (vbits -= tiff_bps; vbits < 0; vbits += bite) {
+	bitbuf <<= bite;
+	for (i=0; i < bite; i+=8)
+	  bitbuf |= ((UINT64) fgetc(ifp) << i);
+      }
+      val = bitbuf << (64-tiff_bps-vbits) >> (64-tiff_bps);
+      RAW(row,col ^ (load_flags >> 6 & 3)) = val;
+      if (load_flags & 1 && (col % 10) == 9 && fgetc(ifp) &&
+	row < height+top_margin && col < width+left_margin) derror();
+    }
+    vbits -= rbits;
   }
-  free (pixel);
 }
 
 void CLASS nokia_load_raw()
 {
   uchar  *data,  *dp;
-  ushort *pixel, *pix;
-  int dwide, row, c;
+  int rev, dwide, row, col, c;
+  double sum[]={0,0};
 
-  dwide = raw_width * 5 / 4;
-  data = (uchar *) malloc (dwide + raw_width*2);
+  rev = 3 * (order == 0x4949);
+  dwide = (raw_width * 5 + 1) / 4;
+  data = (uchar *) malloc (dwide*2);
   merror (data, "nokia_load_raw()");
-  pixel = (ushort *) (data + dwide);
   for (row=0; row < raw_height; row++) {
-    if (fread (data, 1, dwide, ifp) < dwide) derror();
-    for (dp=data, pix=pixel; pix < pixel+raw_width; dp+=5, pix+=4)
-      FORC4 pix[c] = (dp[c] << 2) | (dp[4] >> (c << 1) & 3);
-    if (row < top_margin)
-      FORC(width) black += pixel[c];
-    else
-      FORC(width) BAYER(row-top_margin,c) = pixel[c];
+    if (fread (data+dwide, 1, dwide, ifp) < dwide) derror();
+    FORC(dwide) data[c] = data[dwide+(c ^ rev)];
+    for (dp=data, col=0; col < raw_width; dp+=5, col+=4)
+      FORC4 RAW(row,col+c) = (dp[c] << 2) | (dp[4] >> (c << 1) & 3);
   }
   free (data);
-  if (top_margin) black /= top_margin * width;
   maximum = 0x3ff;
+  if (strcmp(make,"OmniVision")) return;
+  row = raw_height/2;
+  FORC(width-1) {
+    sum[ c & 1] += SQR(RAW(row,c)-RAW(row+1,c+1));
+    sum[~c & 1] += SQR(RAW(row+1,c)-RAW(row,c+1));
+  }
+  if (sum[1] > sum[0]) filters = 0x4b4b4b4b;
 }
 
-unsigned CLASS pana_bits (int nbits)
+void CLASS canon_rmf_load_raw()
 {
-  static uchar buf[16], vbits=0;
+  int row, col, bits, orow, ocol, c;
+
+  for (row=0; row < raw_height; row++)
+    for (col=0; col < raw_width-2; col+=3) {
+      bits = get4();
+      FORC3 {
+	orow = row;
+	if ((ocol = col+c-4) < 0) {
+	  ocol += raw_width;
+	  if ((orow -= 2) < 0)
+	    orow += raw_height;
+	}
+	RAW(orow,ocol) = curve[bits >> (10*c+2) & 0x3ff];
+      }
+    }
+  maximum = curve[0x3ff];
+}
 
-  if (!vbits && fread (buf, 1, 16, ifp) < 16) derror();
-  vbits = (vbits - nbits) & 127;
-  return (buf[(vbits >> 3)+1] << 8 | buf[vbits >> 3])
-	>> (vbits & 7) & ~(-1 << nbits);
+unsigned CLASS pana_bits (int nbits)
+{
+  static uchar buf[0x4000];
+  static int vbits;
+  int byte;
+
+  if (!nbits) return vbits=0;
+  if (!vbits) {
+    fread (buf+load_flags, 1, 0x4000-load_flags, ifp);
+    fread (buf, 1, load_flags, ifp);
+  }
+  vbits = (vbits - nbits) & 0x1ffff;
+  byte = vbits >> 3 ^ 0x3ff0;
+  return (buf[byte] | buf[byte+1] << 8) >> (vbits & 7) & ~(-1 << nbits);
 }
 
 void CLASS panasonic_load_raw()
 {
   int row, col, i, j, sh=0, pred[2], nonz[2];
 
-  raw_width = (raw_width+13)/14*14;
+  pana_bits(0);
   for (row=0; row < height; row++)
     for (col=0; col < raw_width; col++) {
-      if ((i = col % 14) < 2)
-	nonz[i] = pred[i] = pana_bits(12);
-      else {
-	if (i % 3 == 2) sh = 4 >> (3 - pana_bits(2));
+      if ((i = col % 14) == 0)
+	pred[0] = pred[1] = nonz[0] = nonz[1] = 0;
+      if (i % 3 == 2) sh = 4 >> (3 - pana_bits(2));
+      if (nonz[i & 1]) {
 	if ((j = pana_bits(8))) {
 	  if ((pred[i & 1] -= 0x80 << sh) < 0 || sh == 4)
 	       pred[i & 1] &= ~(-1 << sh);
-	  pred[i & 1] += nonz[i & 1] ? j << sh : j;
-	  nonz[i & 1] = 1;
+	  pred[i & 1] += j << sh;
 	}
-      }
-      if (col < width)
-	if ((BAYER(row,col) = pred[col & 1]) >> 12) derror();
-    }
-  maximum = 0xf96;
-  black = 15;
-}
-
-void CLASS olympus_e300_load_raw()
-{
-  uchar  *data,  *dp;
-  ushort *pixel, *pix;
-  int dwide, row, col;
-
-  dwide = raw_width * 16 / 10;
-  fseek (ifp, dwide*top_margin, SEEK_CUR);
-  data = (uchar *) malloc (dwide + raw_width*2);
-  merror (data, "olympus_e300_load_raw()");
-  pixel = (ushort *) (data + dwide);
-  for (row=0; row < height; row++) {
-    if (fread (data, 1, dwide, ifp) < dwide) derror();
-    for (dp=data, pix=pixel; pix < pixel+raw_width; dp+=3, pix+=2) {
-      if (((dp-data) & 15) == 15)
-	if (*dp++ && pix < pixel+width+left_margin) derror();
-      pix[0] = dp[1] << 8 | dp[0];
-      pix[1] = dp[2] << 4 | dp[1] >> 4;
+      } else if ((nonz[i & 1] = pana_bits(8)) || i > 11)
+	pred[i & 1] = nonz[i & 1] << 4 | pana_bits(4);
+      if ((RAW(row,col) = pred[col & 1]) > 4098 && col < width) derror();
     }
-    for (col=0; col < width; col++)
-      BAYER(row,col) = (pixel[col+left_margin] & 0xfff);
-  }
-  free (data);
-  maximum >>= 4;
-  black >>= 4;
 }
 
-void CLASS olympus_e410_load_raw()
+void CLASS olympus_load_raw()
 {
-  int row, col, nbits, sign, low, high, i, w, n, nw;
+  ushort huff[4096];
+  int row, col, nbits, sign, low, high, i, c, w, n, nw;
   int acarry[2][3], *carry, pred, diff;
 
+  huff[n=0] = 0xc0c;
+  for (i=12; i--; )
+    FORC(2048 >> i) huff[++n] = (i+1) << 8 | i;
   fseek (ifp, 7, SEEK_CUR);
   getbits(-1);
   for (row=0; row < height; row++) {
     memset (acarry, 0, sizeof acarry);
-    for (col=0; col < width; col++) {
+    for (col=0; col < raw_width; col++) {
       carry = acarry[col & 1];
       i = 2 * (carry[2] < 3);
       for (nbits=2+i; (ushort) carry[0] >> (nbits+i); nbits++);
-      sign = getbits(1) * -1;
-      low  = getbits(2);
-      for (high=0; high < 12; high++)
-	if (getbits(1)) break;
-      if (high == 12)
+      low = (sign = getbits(3)) & 3;
+      sign = sign << 29 >> 31;
+      if ((high = getbithuff(12,huff)) == 12)
 	high = getbits(16-nbits) >> 1;
       carry[0] = (high << nbits) | getbits(nbits);
       diff = (carry[0] ^ sign) + carry[1];
       carry[1] = (diff*3 + carry[1]) >> 5;
       carry[2] = carry[0] > 16 ? 0 : carry[2]+1;
+      if (col >= width) continue;
       if (row < 2 && col < 2) pred = 0;
-      else if (row < 2) pred = BAYER(row,col-2);
-      else if (col < 2) pred = BAYER(row-2,col);
+      else if (row < 2) pred = RAW(row,col-2);
+      else if (col < 2) pred = RAW(row-2,col);
       else {
-	w  = BAYER(row,col-2);
-	n  = BAYER(row-2,col);
-	nw = BAYER(row-2,col-2);
+	w  = RAW(row,col-2);
+	n  = RAW(row-2,col);
+	nw = RAW(row-2,col-2);
 	if ((w < nw && nw < n) || (n < nw && nw < w)) {
 	  if (ABS(w-nw) > 32 || ABS(n-nw) > 32)
 	    pred = w + n - nw;
 	  else pred = (w + n) >> 1;
 	} else pred = ABS(w-nw) > ABS(n-nw) ? w : n;
       }
-      if ((BAYER(row,col) = pred + ((diff << 2) | low)) >> 12) derror();
+      if ((RAW(row,col) = pred + ((diff << 2) | low)) >> 12) derror();
     }
   }
 }
 
-void CLASS olympus_cseries_load_raw()
+void CLASS canon_crx_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(-1);
-    }
-    for (col=0; col < width; col++)
-      BAYER(row,col) = getbits(12);
-  }
-  black >>= 4;
+void CLASS fuji_xtrans_load_raw()
+{
 }
 
 void CLASS minolta_rd175_load_raw()
@@ -1995,37 +2110,17 @@
     }
     if ((box < 12) && (box & 1)) {
       for (col=0; col < 1533; col++, row ^= 1)
-	if (col != 1) BAYER(row,col) = (col+1) & 2 ?
+	if (col != 1) RAW(row,col) = (col+1) & 2 ?
 		   pixel[col/2-1] + pixel[col/2+1] : pixel[col/2] << 1;
-      BAYER(row,1)    = pixel[1]   << 1;
-      BAYER(row,1533) = pixel[765] << 1;
+      RAW(row,1)    = pixel[1]   << 1;
+      RAW(row,1533) = pixel[765] << 1;
     } else
       for (col=row & 1; col < 1534; col+=2)
-	BAYER(row,col) = pixel[col/2] << 1;
+	RAW(row,col) = pixel[col/2] << 1;
   }
   maximum = 0xff << 1;
 }
 
-void CLASS casio_qv5700_load_raw()
-{
-  uchar  data[3232],  *dp;
-  ushort 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 CLASS quicktake_100_load_raw()
 {
   uchar pixel[484][644];
@@ -2089,32 +2184,20 @@
     }
   for (row=0; row < height; row++)
     for (col=0; col < width; col++)
-      BAYER(row,col) = curve[pixel[row+2][col+2]];
+      RAW(row,col) = curve[pixel[row+2][col+2]];
   maximum = 0x3ff;
 }
 
-const int * CLASS make_decoder_int (const int *source, int level)
-{
-  struct decode *cur;
+#define radc_token(tree) ((signed char) getbithuff(8,huff[tree]))
 
-  cur = free_decode++;
-  if (level < source[0]) {
-    cur->branch[0] = free_decode;
-    source = make_decoder_int (source, level+1);
-    cur->branch[1] = free_decode;
-    source = make_decoder_int (source, level+1);
-  } else {
-    cur->leaf = source[1];
-    source += 2;
-  }
-  return source;
-}
+#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)
 
-int CLASS radc_token (int tree)
+void CLASS kodak_radc_load_raw()
 {
-  int t;
-  static struct decode *dstart[18], *dindex;
-  static const int *s, source[] = {
+  static const char src[] = {
     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,
@@ -2134,37 +2217,24 @@
     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 (kodak_cbpp == 243)
-      return (getbits(6) << 2) + 2;	/* most DC50 photos */
-    else
-      return (getbits(5) << 3) + 4;	/* DC40, Fotoman Pixtura */
-  }
-  for (dindex = dstart[tree]; dindex->branch[0]; )
-    dindex = dindex->branch[getbits(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 CLASS kodak_radc_load_raw()
-{
+  ushort huff[19][256];
   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];
+  static const ushort pt[] =
+    { 0,0, 1280,1344, 2320,3616, 3328,8000, 4095,16383, 65535,16383 };
 
-  init_decoder();
+  for (i=2; i < 12; i+=2)
+    for (c=pt[i-2]; c <= pt[i]; c++)
+      curve[c] = (float)
+	(c-pt[i-2]) / (pt[i]-pt[i-2]) * (pt[i+1]-pt[i-1]) + pt[i-1] + 0.5;
+  for (s=i=0; i < sizeof src; i+=2)
+    FORC(256 >> src[i])
+      ((ushort *)huff)[s++] = src[i] << 8 | (uchar) src[i+1];
+  s = kodak_cbpp == 243 ? 2 : 3;
+  FORC(256) huff[18][c] = (8-s) << 8 | c >> s << s | 1 << (s-1);
   getbits(-1);
   for (i=0; i < sizeof(buf)/sizeof(short); i++)
-    buf[0][0][i] = 2048;
+    ((short *)buf)[i] = 2048;
   for (row=0; row < height; row+=4) {
     FORC3 mul[c] = getbits(6);
     FORC3 {
@@ -2173,7 +2243,7 @@
       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;
+	((short *)buf[c])[i] = (((short *)buf[c])[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;
@@ -2181,7 +2251,7 @@
 	  if ((tree = radc_token(tree))) {
 	    col -= 2;
 	    if (tree == 8)
-	      FORYX buf[c][y][x] = radc_token(tree+10) * mul[c];
+	      FORYX buf[c][y][x] = (uchar) radc_token(18) * mul[c];
 	    else
 	      FORYX buf[c][y][x] = radc_token(tree+10) * 16 + PREDICTOR;
 	  } else
@@ -2201,8 +2271,8 @@
 	  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;
+	    if (c) RAW(row+y*2+c-1,x*2+2-c) = val;
+	    else   RAW(row+r*2+y,x*2+y) = val;
 	  }
 	memcpy (buf[c][0]+!c, buf[c][2], sizeof buf[c][0]-2*!c);
       }
@@ -2212,20 +2282,22 @@
 	if ((x+y) & 1) {
 	  r = x ? x-1 : x+1;
 	  s = x+1 < width ? x+1 : x-1;
-	  val = (BAYER(y,x)-2048)*2 + (BAYER(y,r)+BAYER(y,s))/2;
+	  val = (RAW(y,x)-2048)*2 + (RAW(y,r)+RAW(y,s))/2;
 	  if (val < 0) val = 0;
-	  BAYER(y,x) = val;
+	  RAW(y,x) = val;
 	}
   }
-  maximum = 0xfff;
-  use_gamma = 0;
+  for (i=0; i < height*width; i++)
+    raw_image[i] = curve[raw_image[i]];
+  maximum = 0x3fff;
 }
 
 #undef FORYX
 #undef PREDICTOR
 
-#ifdef HAVE_JPEG
+#ifdef NO_JPEG
 void CLASS kodak_jpeg_load_raw() {}
+void CLASS lossy_dng_load_raw() {}
 #else
 
 METHODDEF(boolean)
@@ -2270,16 +2342,81 @@
     jpeg_read_scanlines (&cinfo, buf, 1);
     pixel = (JSAMPLE (*)[3]) 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];
+      RAW(row+0,col+0) = pixel[col+0][1] << 1;
+      RAW(row+1,col+1) = pixel[col+1][1] << 1;
+      RAW(row+0,col+1) = pixel[col][0] + pixel[col+1][0];
+      RAW(row+1,col+0) = pixel[col][2] + pixel[col+1][2];
     }
   }
   jpeg_finish_decompress (&cinfo);
   jpeg_destroy_decompress (&cinfo);
   maximum = 0xff << 1;
 }
+
+void CLASS gamma_curve (double pwr, double ts, int mode, int imax);
+
+void CLASS lossy_dng_load_raw()
+{
+  struct jpeg_decompress_struct cinfo;
+  struct jpeg_error_mgr jerr;
+  JSAMPARRAY buf;
+  JSAMPLE (*pixel)[3];
+  unsigned sorder=order, ntags, opcode, deg, i, j, c;
+  unsigned save=data_offset-4, trow=0, tcol=0, row, col;
+  ushort cur[3][256];
+  double coeff[9], tot;
+
+  if (meta_offset) {
+    fseek (ifp, meta_offset, SEEK_SET);
+    order = 0x4d4d;
+    ntags = get4();
+    while (ntags--) {
+      opcode = get4(); get4(); get4();
+      if (opcode != 8)
+      { fseek (ifp, get4(), SEEK_CUR); continue; }
+      fseek (ifp, 20, SEEK_CUR);
+      if ((c = get4()) > 2) break;
+      fseek (ifp, 12, SEEK_CUR);
+      if ((deg = get4()) > 8) break;
+      for (i=0; i <= deg && i < 9; i++)
+	coeff[i] = getreal(12);
+      for (i=0; i < 256; i++) {
+	for (tot=j=0; j <= deg; j++)
+	  tot += coeff[j] * pow(i/255.0, j);
+	cur[c][i] = tot*0xffff;
+      }
+    }
+    order = sorder;
+  } else {
+    gamma_curve (1/2.4, 12.92, 1, 255);
+    FORC3 memcpy (cur[c], curve, sizeof cur[0]);
+  }
+  cinfo.err = jpeg_std_error (&jerr);
+  jpeg_create_decompress (&cinfo);
+  while (trow < raw_height) {
+    fseek (ifp, save+=4, SEEK_SET);
+    if (tile_length < INT_MAX)
+      fseek (ifp, get4(), SEEK_SET);
+    jpeg_stdio_src (&cinfo, ifp);
+    jpeg_read_header (&cinfo, TRUE);
+    jpeg_start_decompress (&cinfo);
+    buf = (*cinfo.mem->alloc_sarray)
+	((j_common_ptr) &cinfo, JPOOL_IMAGE, cinfo.output_width*3, 1);
+    while (cinfo.output_scanline < cinfo.output_height &&
+	(row = trow + cinfo.output_scanline) < height) {
+      jpeg_read_scanlines (&cinfo, buf, 1);
+      pixel = (JSAMPLE (*)[3]) buf[0];
+      for (col=0; col < cinfo.output_width && tcol+col < width; col++) {
+	FORC3 image[row*width+tcol+col][c] = cur[c][pixel[col][c]];
+      }
+    }
+    jpeg_abort_decompress (&cinfo);
+    if ((tcol += tile_width) >= raw_width)
+      trow += tile_length + (tcol = 0);
+  }
+  jpeg_destroy_decompress (&cinfo);
+  maximum = 0xffff;
+}
 #endif
 
 void CLASS kodak_dc120_load_raw()
@@ -2293,7 +2430,7 @@
     if (fread (pixel, 1, 848, ifp) < 848) derror();
     shift = row * mul[row & 3] + add[row & 3];
     for (col=0; col < width; col++)
-      BAYER(row,col) = (ushort) pixel[(col + shift) % 848];
+      RAW(row,col) = (ushort) pixel[(col + shift) % 848];
   }
   maximum = 0xff;
 }
@@ -2301,25 +2438,65 @@
 void CLASS eight_bit_load_raw()
 {
   uchar *pixel;
-  unsigned row, col, val, lblack=0;
+  unsigned row, col;
 
   pixel = (uchar *) calloc (raw_width, sizeof *pixel);
   merror (pixel, "eight_bit_load_raw()");
-  fseek (ifp, top_margin*raw_width, SEEK_CUR);
-  for (row=0; row < height; row++) {
+  for (row=0; row < raw_height; row++) {
     if (fread (pixel, 1, raw_width, ifp) < raw_width) derror();
-    for (col=0; col < raw_width; col++) {
-      val = curve[pixel[col]];
-      if ((unsigned) (col-left_margin) < width)
-        BAYER(row,col-left_margin) = val;
-      else lblack += val;
+    for (col=0; col < raw_width; col++)
+      RAW(row,col) = curve[pixel[col]];
+  }
+  free (pixel);
+  maximum = curve[0xff];
+}
+
+void CLASS kodak_c330_load_raw()
+{
+  uchar *pixel;
+  int row, col, y, cb, cr, rgb[3], c;
+
+  pixel = (uchar *) calloc (raw_width, 2*sizeof *pixel);
+  merror (pixel, "kodak_c330_load_raw()");
+  for (row=0; row < height; row++) {
+    if (fread (pixel, raw_width, 2, ifp) < 2) derror();
+    if (load_flags && (row & 31) == 31)
+      fseek (ifp, raw_width*32, SEEK_CUR);
+    for (col=0; col < width; col++) {
+      y  = pixel[col*2];
+      cb = pixel[(col*2 & -4) | 1] - 128;
+      cr = pixel[(col*2 & -4) | 3] - 128;
+      rgb[1] = y - ((cb + cr + 2) >> 2);
+      rgb[2] = rgb[1] + cb;
+      rgb[0] = rgb[1] + cr;
+      FORC3 image[row*width+col][c] = curve[LIM(rgb[c],0,255)];
+    }
+  }
+  free (pixel);
+  maximum = curve[0xff];
+}
+
+void CLASS kodak_c603_load_raw()
+{
+  uchar *pixel;
+  int row, col, y, cb, cr, rgb[3], c;
+
+  pixel = (uchar *) calloc (raw_width, 3*sizeof *pixel);
+  merror (pixel, "kodak_c603_load_raw()");
+  for (row=0; row < height; row++) {
+    if (~row & 1)
+      if (fread (pixel, raw_width, 3, ifp) < 3) derror();
+    for (col=0; col < width; col++) {
+      y  = pixel[width*2*(row & 1) + col];
+      cb = pixel[width + (col & -2)]   - 128;
+      cr = pixel[width + (col & -2)+1] - 128;
+      rgb[1] = y - ((cb + cr + 2) >> 2);
+      rgb[2] = rgb[1] + cb;
+      rgb[0] = rgb[1] + cr;
+      FORC3 image[row*width+col][c] = curve[LIM(rgb[c],0,255)];
     }
   }
   free (pixel);
-  if (raw_width > width+1)
-    black = lblack / ((raw_width - width) * height);
-  if (!strncmp(model,"DC2",3))
-    black = 0;
   maximum = curve[0xff];
 }
 
@@ -2328,22 +2505,17 @@
   static const uchar kodak_tree[2][26] =
   { { 0,1,5,1,1,2,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9 },
     { 0,3,1,1,1,1,1,2,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9 } };
-  struct decode *decode[2];
+  ushort *huff[2];
   uchar *pixel;
-  int *strip, ns, i, row, col, chess, pi=0, pi1, pi2, pred, val;
+  int *strip, ns, c, row, col, chess, pi=0, pi1, pi2, pred, val;
 
-  init_decoder();
-  for (i=0; i < 2; i++) {
-    decode[i] = free_decode;
-    make_decoder (kodak_tree[i], 0);
-  }
+  FORC(2) huff[c] = make_decoder (kodak_tree[c]);
   ns = (raw_height+63) >> 5;
   pixel = (uchar *) malloc (raw_width*32 + ns*4);
   merror (pixel, "kodak_262_load_raw()");
   strip = (int *) (pixel + raw_width*32);
   order = 0x4d4d;
-  for (i=0; i < ns; i++)
-    strip[i] = get4();
+  FORC(ns) strip[c] = get4();
   for (row=0; row < raw_height; row++) {
     if ((row & 31) == 0) {
       fseek (ifp, strip[row >> 5], SEEK_SET);
@@ -2359,17 +2531,14 @@
       if (pi2 < 0) pi2 = pi1;
       if (pi1 < 0 && col > 1) pi1 = pi2 = pi-2;
       pred = (pi1 < 0) ? 0 : (pixel[pi1] + pixel[pi2]) >> 1;
-      pixel[pi] = val = pred + ljpeg_diff (decode[chess]);
+      pixel[pi] = val = pred + ljpeg_diff (huff[chess]);
       if (val >> 8) derror();
       val = curve[pixel[pi++]];
-      if ((unsigned) (col-left_margin) < width)
-	BAYER(row,col-left_margin) = val;
-      else black += val;
+      RAW(row,col) = val;
     }
   }
   free (pixel);
-  if (raw_width > width)
-    black /= (raw_width - width) * height;
+  FORC(2) free (huff[c]);
 }
 
 int CLASS kodak_65000_decode (short *out, int bsize)
@@ -2429,7 +2598,7 @@
       len = MIN (256, width-col);
       ret = kodak_65000_decode (buf, len);
       for (i=0; i < len; i++)
-	if ((BAYER(row,col+i) =	curve[ret ? buf[i] :
+	if ((RAW(row,col+i) =	curve[ret ? buf[i] :
 		(pred[i & 1] += buf[i])]) >> 12) derror();
     }
 }
@@ -2440,6 +2609,7 @@
   int row, col, len, c, i, j, k, y[2][2], cb, cr, rgb[3];
   ushort *ip;
 
+  if (!image) return;
   for (row=0; row < height; row+=2)
     for (col=0; col < width; col+=128) {
       len = MIN (128, width-col);
@@ -2500,8 +2670,8 @@
     for (p=0; p < 127; p++)
       pad[p] = htonl(pad[p]);
   }
-  while (len--)
-    *data++ ^= pad[p++ & 127] = pad[(p+1) & 127] ^ pad[(p+65) & 127];
+  while (len-- && p++)
+    *data++ ^= pad[(p-1) & 127] = pad[p & 127] ^ pad[(p+64) & 127];
 }
 
 void CLASS sony_load_raw()
@@ -2516,44 +2686,37 @@
   key = get4();
   fseek (ifp, 164600, SEEK_SET);
   fread (head, 1, 40, ifp);
-  sony_decrypt ((unsigned int *) head, 10, 1, key);
+  sony_decrypt ((unsigned *) head, 10, 1, key);
   for (i=26; i-- > 22; )
     key = key << 8 | head[i];
   fseek (ifp, data_offset, SEEK_SET);
-  pixel = (ushort *) calloc (raw_width, sizeof *pixel);
-  merror (pixel, "sony_load_raw()");
-  for (row=0; row < height; row++) {
+  for (row=0; row < raw_height; row++) {
+    pixel = raw_image + row*raw_width;
     if (fread (pixel, 2, raw_width, ifp) < raw_width) derror();
-    sony_decrypt ((unsigned int *) pixel, raw_width/2, !row, key);
-    for (col=9; col < left_margin; col++)
-      black += ntohs(pixel[col]);
-    for (col=0; col < width; col++)
-      if ((BAYER(row,col) = ntohs(pixel[col+left_margin])) >> 14)
-	derror();
+    sony_decrypt ((unsigned *) pixel, raw_width/2, !row, key);
+    for (col=0; col < raw_width; col++)
+      if ((pixel[col] = ntohs(pixel[col])) >> 14) derror();
   }
-  free (pixel);
-  if (left_margin > 9)
-    black /= (left_margin-9) * height;
   maximum = 0x3ff0;
 }
 
 void CLASS sony_arw_load_raw()
 {
-  int col, row, len, diff, sum=0;
-
+  ushort huff[32770];
+  static const ushort tab[18] =
+  { 0xf11,0xf10,0xe0f,0xd0e,0xc0d,0xb0c,0xa0b,0x90a,0x809,
+    0x708,0x607,0x506,0x405,0x304,0x303,0x300,0x202,0x201 };
+  int i, c, n, col, row, sum=0;
+
+  huff[0] = 15;
+  for (n=i=0; i < 18; i++)
+    FORC(32768 >> (tab[i] >> 8)) huff[++n] = tab[i];
   getbits(-1);
   for (col = raw_width; col--; )
     for (row=0; row < raw_height+1; row+=2) {
       if (row == raw_height) row = 1;
-      len = 4 - getbits(2);
-      if (len == 3 && getbits(1)) len = 0;
-      if (len == 4)
-	while (len < 17 && !getbits(1)) len++;
-      diff = getbits(len);
-      if ((diff & (1 << (len-1))) == 0)
-	diff -= (1 << len) - 1;
-      if ((sum += diff) >> 12) derror();
-      if (row < height) BAYER(row,col) = sum;
+      if ((sum += ljpeg_diff(huff)) >> 12) derror();
+      if (row < height) RAW(row,col) = sum;
     }
 }
 
@@ -2563,37 +2726,132 @@
   ushort pix[16];
   int row, col, val, max, min, imax, imin, sh, bit, i;
 
-  data = (uchar *) malloc (raw_width*tiff_bps >> 3);
+  data = (uchar *) malloc (raw_width+1);
   merror (data, "sony_arw2_load_raw()");
   for (row=0; row < height; row++) {
-    fread (data, 1, raw_width*tiff_bps >> 3, ifp);
-    if (tiff_bps == 8) {
-      for (dp=data, col=0; col < width-30; dp+=16) {
-	max = 0x7ff & (val = sget4(dp));
-	min = 0x7ff & val >> 11;
-	imax = 0x0f & val >> 22;
-	imin = 0x0f & val >> 26;
-	for (sh=0; sh < 4 && 0x80 << sh <= max-min; sh++);
-	for (bit=30, i=0; i < 16; i++)
-	  if      (i == imax) pix[i] = max;
-	  else if (i == imin) pix[i] = min;
-	  else {
-	    pix[i] = ((sget2(dp+(bit >> 3)) >> (bit & 7) & 0x7f) << sh) + min;
-	    if (pix[i] > 0x7ff) pix[i] = 0x7ff;
-	    bit += 7;
-	  }
-	for (i=0; i < 16; i++, col+=2)
-	  BAYER(row,col) = curve[pix[i] << 1] >> 1;
-	col -= col & 1 ? 1:31;
-      }
-    } else if (tiff_bps == 12)
-      for (dp=data, col=0; col < width; dp+=3, col+=2) {
-	BAYER(row,col)   = ((dp[1] << 8 | dp[0]) & 0xfff) << 1;
-	BAYER(row,col+1) =  (dp[2] << 4 | dp[1] >> 4) << 1;
-      }
+    fread (data, 1, raw_width, ifp);
+    for (dp=data, col=0; col < raw_width-30; dp+=16) {
+      max = 0x7ff & (val = sget4(dp));
+      min = 0x7ff & val >> 11;
+      imax = 0x0f & val >> 22;
+      imin = 0x0f & val >> 26;
+      for (sh=0; sh < 4 && 0x80 << sh <= max-min; sh++);
+      for (bit=30, i=0; i < 16; i++)
+	if      (i == imax) pix[i] = max;
+	else if (i == imin) pix[i] = min;
+	else {
+	  pix[i] = ((sget2(dp+(bit >> 3)) >> (bit & 7) & 0x7f) << sh) + min;
+	  if (pix[i] > 0x7ff) pix[i] = 0x7ff;
+	  bit += 7;
+	}
+      for (i=0; i < 16; i++, col+=2)
+	RAW(row,col) = curve[pix[i] << 1] >> 2;
+      col -= col & 1 ? 1:31;
+    }
   }
   free (data);
-  maximum = 0x1fff;
+}
+
+void CLASS samsung_load_raw()
+{
+  int row, col, c, i, dir, op[4], len[4];
+
+  order = 0x4949;
+  for (row=0; row < raw_height; row++) {
+    fseek (ifp, strip_offset+row*4, SEEK_SET);
+    fseek (ifp, data_offset+get4(), SEEK_SET);
+    ph1_bits(-1);
+    FORC4 len[c] = row < 2 ? 7:4;
+    for (col=0; col < raw_width; col+=16) {
+      dir = ph1_bits(1);
+      FORC4 op[c] = ph1_bits(2);
+      FORC4 switch (op[c]) {
+	case 3: len[c] = ph1_bits(4);	break;
+	case 2: len[c]--;		break;
+	case 1: len[c]++;
+      }
+      for (c=0; c < 16; c+=2) {
+	i = len[((c & 1) << 1) | (c >> 3)];
+	RAW(row,col+c) = ((signed) ph1_bits(i) << (32-i) >> (32-i)) +
+	  (dir ? RAW(row+(~c | -2),col+c) : col ? RAW(row,col+(c | -2)) : 128);
+	if (c == 14) c = -1;
+      }
+    }
+  }
+  for (row=0; row < raw_height-1; row+=2)
+    for (col=0; col < raw_width-1; col+=2)
+      SWAP (RAW(row,col+1), RAW(row+1,col));
+}
+
+void CLASS samsung2_load_raw()
+{
+  static const ushort tab[14] =
+  { 0x304,0x307,0x206,0x205,0x403,0x600,0x709,
+    0x80a,0x90b,0xa0c,0xa0d,0x501,0x408,0x402 };
+  ushort huff[1026], vpred[2][2] = {{0,0},{0,0}}, hpred[2];
+  int i, c, n, row, col, diff;
+
+  huff[0] = 10;
+  for (n=i=0; i < 14; i++)
+    FORC(1024 >> (tab[i] >> 8)) huff[++n] = tab[i];
+  getbits(-1);
+  for (row=0; row < raw_height; row++)
+    for (col=0; col < raw_width; col++) {
+      diff = ljpeg_diff (huff);
+      if (col < 2) hpred[col] = vpred[row & 1][col] += diff;
+      else	   hpred[col & 1] += diff;
+      RAW(row,col) = hpred[col & 1];
+      if (hpred[col & 1] >> tiff_bps) derror();
+    }
+}
+
+void CLASS samsung3_load_raw()
+{
+  int opt, init, mag, pmode, row, tab, col, pred, diff, i, c;
+  ushort lent[3][2], len[4], *prow[2];
+
+  order = 0x4949;
+  fseek (ifp, 9, SEEK_CUR);
+  opt = fgetc(ifp);
+  init = (get2(),get2());
+  for (row=0; row < raw_height; row++) {
+    fseek (ifp, (data_offset-ftell(ifp)) & 15, SEEK_CUR);
+    ph1_bits(-1);
+    mag = 0; pmode = 7;
+    FORC(6) ((ushort *)lent)[c] = row < 2 ? 7:4;
+    prow[ row & 1] = &RAW(row-1,1-((row & 1) << 1));	// green
+    prow[~row & 1] = &RAW(row-2,0);			// red and blue
+    for (tab=0; tab+15 < raw_width; tab+=16) {
+      if (~opt & 4 && !(tab & 63)) {
+	i = ph1_bits(2);
+	mag = i < 3 ? mag-'2'+"204"[i] : ph1_bits(12);
+      }
+      if (opt & 2)
+	pmode = 7 - 4*ph1_bits(1);
+      else if (!ph1_bits(1))
+	pmode = ph1_bits(3);
+      if (opt & 1 || !ph1_bits(1)) {
+	FORC4 len[c] = ph1_bits(2);
+	FORC4 {
+	  i = ((row & 1) << 1 | (c & 1)) % 3;
+	  len[c] = len[c] < 3 ? lent[i][0]-'1'+"120"[len[c]] : ph1_bits(4);
+	  lent[i][0] = lent[i][1];
+	  lent[i][1] = len[c];
+	}
+      }
+      FORC(16) {
+	col = tab + (((c & 7) << 1)^(c >> 3)^(row & 1));
+	pred = (pmode == 7 || row < 2)
+	     ? (tab ? RAW(row,tab-2+(col & 1)) : init)
+	     : (prow[col & 1][col-'4'+"0224468"[pmode]] +
+		prow[col & 1][col-'4'+"0244668"[pmode]] + 1) >> 1;
+	diff = ph1_bits (i = len[c >> 2]);
+	if (diff >> (i-1)) diff -= 1 << i;
+	diff = diff * (mag*2+1) + mag;
+	RAW(row,col) = pred + diff;
+      }
+    }
+  }
 }
 
 #define HOLE(row) ((holes >> (((row) - raw_height) & 7)) & 1)
@@ -2606,13 +2864,14 @@
     { 7, 7, 0, 0, 63, 55, 47, 39, 31, 23, 15, 7, 0 },
     { 3, 3, 0, 0, 63,     47,     31,     15,    0 } };
   int low, high=0xff, carry=0, nbits=8;
-  int s, count, bin, next, i, sym[3];
+  int pix, s, count, bin, next, i, sym[3];
   uchar diff, pred[]={0,0};
   ushort data=0, range=0;
-  unsigned pix, row, col;
 
   fseek (ifp, seg[0][1]+1, SEEK_SET);
   getbits(-1);
+  if (seg[1][0] > raw_width*raw_height)
+      seg[1][0] = raw_width*raw_height;
   for (pix=seg[0][0]; pix < seg[1][0]; pix++) {
     for (s=0; s < 3; s++) {
       data = data << nbits | getbits(nbits);
@@ -2655,12 +2914,8 @@
       diff = diff ? -diff : 0x80;
     if (ftell(ifp) + 12 >= seg[1][1])
       diff = 0;
-    pred[pix & 1] += diff;
-    row = pix / raw_width - top_margin;
-    col = pix % raw_width - left_margin;
-    if (row < height && col < width)
-      BAYER(row,col) = pred[pix & 1];
-    if (!(pix & 1) && HOLE(row)) pix += 2;
+    raw_image[pix] = pred[pix & 1] += diff;
+    if (!(pix & 1) && HOLE(pix / raw_width)) pix += 2;
   }
   maximum = 0xff;
 }
@@ -2675,7 +2930,6 @@
   seg[1][0] = raw_width * raw_height;
   seg[1][1] = INT_MAX;
   smal_decode_segment (seg, 0);
-  use_gamma = 0;
 }
 
 int CLASS median4 (int *p)
@@ -2698,21 +2952,21 @@
   for (row=2; row < height-2; row++) {
     if (!HOLE(row)) continue;
     for (col=1; col < width-1; col+=4) {
-      val[0] = BAYER(row-1,col-1);
-      val[1] = BAYER(row-1,col+1);
-      val[2] = BAYER(row+1,col-1);
-      val[3] = BAYER(row+1,col+1);
-      BAYER(row,col) = median4(val);
+      val[0] = RAW(row-1,col-1);
+      val[1] = RAW(row-1,col+1);
+      val[2] = RAW(row+1,col-1);
+      val[3] = RAW(row+1,col+1);
+      RAW(row,col) = median4(val);
     }
     for (col=2; col < width-2; col+=4)
       if (HOLE(row-2) || HOLE(row+2))
-	BAYER(row,col) = (BAYER(row,col-2) + BAYER(row,col+2)) >> 1;
+	RAW(row,col) = (RAW(row,col-2) + RAW(row,col+2)) >> 1;
       else {
-	val[0] = BAYER(row,col-2);
-	val[1] = BAYER(row,col+2);
-	val[2] = BAYER(row-2,col);
-	val[3] = BAYER(row+2,col);
-	BAYER(row,col) = median4(val);
+	val[0] = RAW(row,col-2);
+	val[1] = RAW(row,col+2);
+	val[2] = RAW(row-2,col);
+	val[3] = RAW(row+2,col);
+	RAW(row,col) = median4(val);
       }
   }
 }
@@ -2723,10 +2977,10 @@
 
   fseek (ifp, 67, SEEK_SET);
   offset = get4();
-  nseg = fgetc(ifp);
+  nseg = (uchar) fgetc(ifp);
   fseek (ifp, offset, SEEK_SET);
   for (i=0; i < nseg*2; i++)
-    seg[0][i] = get4() + data_offset*(i & 1);
+    ((unsigned *)seg)[i] = get4() + data_offset*(i & 1);
   fseek (ifp, 78, SEEK_SET);
   holes = fgetc(ifp);
   fseek (ifp, 88, SEEK_SET);
@@ -2737,6 +2991,58 @@
   if (holes) fill_holes (holes);
 }
 
+void CLASS redcine_load_raw()
+{
+#ifndef NO_JASPER
+  int c, row, col;
+  jas_stream_t *in;
+  jas_image_t *jimg;
+  jas_matrix_t *jmat;
+  jas_seqent_t *data;
+  ushort *img, *pix;
+
+  jas_init();
+  in = jas_stream_fopen (ifname, "rb");
+  jas_stream_seek (in, data_offset+20, SEEK_SET);
+  jimg = jas_image_decode (in, -1, 0);
+  if (!jimg) longjmp (failure, 3);
+  jmat = jas_matrix_create (height/2, width/2);
+  merror (jmat, "redcine_load_raw()");
+  img = (ushort *) calloc ((height+2), (width+2)*2);
+  merror (img, "redcine_load_raw()");
+  FORC4 {
+    jas_image_readcmpt (jimg, c, 0, 0, width/2, height/2, jmat);
+    data = jas_matrix_getref (jmat, 0, 0);
+    for (row = c >> 1; row < height; row+=2)
+      for (col = c & 1; col < width; col+=2)
+	img[(row+1)*(width+2)+col+1] = data[(row/2)*(width/2)+col/2];
+  }
+  for (col=1; col <= width; col++) {
+    img[col] = img[2*(width+2)+col];
+    img[(height+1)*(width+2)+col] = img[(height-1)*(width+2)+col];
+  }
+  for (row=0; row < height+2; row++) {
+    img[row*(width+2)] = img[row*(width+2)+2];
+    img[(row+1)*(width+2)-1] = img[(row+1)*(width+2)-3];
+  }
+  for (row=1; row <= height; row++) {
+    pix = img + row*(width+2) + (col = 1 + (FC(row,1) & 1));
+    for (   ; col <= width; col+=2, pix+=2) {
+      c = (((pix[0] - 0x800) << 3) +
+	pix[-(width+2)] + pix[width+2] + pix[-1] + pix[1]) >> 2;
+      pix[0] = LIM(c,0,4095);
+    }
+  }
+  for (row=0; row < height; row++)
+    for (col=0; col < width; col++)
+      RAW(row,col) = curve[img[(row+1)*(width+2)+col+1]];
+  free (img);
+  jas_matrix_destroy (jmat);
+  jas_image_destroy (jimg);
+  jas_stream_close (in);
+#endif
+}
+
 /* RESTRICTED code starts here */
 
 void CLASS foveon_decoder (unsigned size, unsigned code)
@@ -2748,7 +3054,8 @@
   if (!code) {
     for (i=0; i < size; i++)
       huff[i] = get4();
-    init_decoder();
+    memset (first_decode, 0, sizeof first_decode);
+    free_decode = first_decode;
   }
   cur = free_decode++;
   if (free_decode > first_decode+2048) {
@@ -2770,7 +3077,7 @@
   foveon_decoder (size, code+1);
 }
 
-void CLASS foveon_thumb (FILE *tfp)
+void CLASS foveon_thumb()
 {
   unsigned bwide, row, col, bitbuf=0, bit=1, c, i;
   char *buf;
@@ -2778,14 +3085,14 @@
   short pred[3];
 
   bwide = get4();
-  fprintf (tfp, "P6\n%d %d\n255\n", thumb_width, thumb_height);
+  fprintf (ofp, "P6\n%d %d\n255\n", thumb_width, thumb_height);
   if (bwide > 0) {
     if (bwide < thumb_width*3) return;
     buf = (char *) malloc (bwide);
     merror (buf, "foveon_thumb()");
     for (row=0; row < thumb_height; row++) {
       fread  (buf, 1, bwide, ifp);
-      fwrite (buf, 3, thumb_width, tfp);
+      fwrite (buf, 3, thumb_width, ofp);
     }
     free (buf);
     return;
@@ -2804,41 +3111,26 @@
 	  dindex = dindex->branch[bitbuf >> bit & 1];
 	}
 	pred[c] += dindex->leaf;
-	fputc (pred[c], tfp);
+	fputc (pred[c], ofp);
       }
   }
 }
 
-void CLASS foveon_load_camf()
-{
-  unsigned key, i, val;
-
-  fseek (ifp, meta_offset, SEEK_SET);
-  key = get4();
-  fread (meta_data, 1, meta_length, ifp);
-  for (i=0; i < meta_length; i++) {
-    key = (key * 1597 + 51749) % 244944;
-    val = key * (INT64) 301593171 >> 24;
-    meta_data[i] ^= ((((key << 8) - val) >> 1) + val) >> 17;
-  }
-}
-
-void CLASS foveon_load_raw()
+void CLASS foveon_sd_load_raw()
 {
   struct decode *dindex;
   short diff[1024];
   unsigned bitbuf=0;
-  int pred[3], fixed, row, col, bit=-1, c, i;
+  int pred[3], row, col, bit=-1, c, i;
 
-  fixed = get4();
   read_shorts ((ushort *) diff, 1024);
-  if (!fixed) foveon_decoder (1024, 0);
+  if (!load_flags) foveon_decoder (1024, 0);
 
   for (row=0; row < height; row++) {
     memset (pred, 0, sizeof pred);
-    if (!bit && !fixed && atoi(model+2) < 14) get4();
+    if (!bit && !load_flags && atoi(model+2) < 14) get4();
     for (col=bit=0; col < width; col++) {
-      if (fixed) {
+      if (load_flags) {
 	bitbuf = get4();
 	FORC3 pred[2-c] += diff[bitbuf >> c*10 & 0x3ff];
       }
@@ -2855,10 +3147,83 @@
       FORC3 image[row*width+col][c] = pred[c];
     }
   }
-  if (document_mode)
-    for (i=0; i < height*width*4; i++)
-      if ((short) image[0][i] < 0) image[0][i] = 0;
-  foveon_load_camf();
+}
+
+void CLASS foveon_huff (ushort *huff)
+{
+  int i, j, clen, code;
+
+  huff[0] = 8;
+  for (i=0; i < 13; i++) {
+    clen = getc(ifp);
+    code = getc(ifp);
+    for (j=0; j < 256 >> clen; )
+      huff[code+ ++j] = clen << 8 | i;
+  }
+  get2();
+}
+
+void CLASS foveon_dp_load_raw()
+{
+  unsigned c, roff[4], row, col, diff;
+  ushort huff[512], vpred[2][2], hpred[2];
+
+  fseek (ifp, 8, SEEK_CUR);
+  foveon_huff (huff);
+  roff[0] = 48;
+  FORC3 roff[c+1] = -(-(roff[c] + get4()) & -16);
+  FORC3 {
+    fseek (ifp, data_offset+roff[c], SEEK_SET);
+    getbits(-1);
+    vpred[0][0] = vpred[0][1] = vpred[1][0] = vpred[1][1] = 512;
+    for (row=0; row < height; row++) {
+      for (col=0; col < width; col++) {
+	diff = ljpeg_diff(huff);
+	if (col < 2) hpred[col] = vpred[row & 1][col] += diff;
+	else hpred[col & 1] += diff;
+	image[row*width+col][c] = hpred[col & 1];
+      }
+    }
+  }
+}
+
+void CLASS foveon_load_camf()
+{
+  unsigned type, wide, high, i, j, row, col, diff;
+  ushort huff[258], vpred[2][2] = {{512,512},{512,512}}, hpred[2];
+
+  fseek (ifp, meta_offset, SEEK_SET);
+  type = get4();  get4();  get4();
+  wide = get4();
+  high = get4();
+  if (type == 2) {
+    fread (meta_data, 1, meta_length, ifp);
+    for (i=0; i < meta_length; i++) {
+      high = (high * 1597 + 51749) % 244944;
+      wide = high * (INT64) 301593171 >> 24;
+      meta_data[i] ^= ((((high << 8) - wide) >> 1) + wide) >> 17;
+    }
+  } else if (type == 4) {
+    free (meta_data);
+    meta_data = (char *) malloc (meta_length = wide*high*3/2);
+    merror (meta_data, "foveon_load_camf()");
+    foveon_huff (huff);
+    get4();
+    getbits(-1);
+    for (j=row=0; row < high; row++) {
+      for (col=0; col < wide; col++) {
+	diff = ljpeg_diff(huff);
+	if (col < 2) hpred[col] = vpred[row & 1][col] += diff;
+	else         hpred[col & 1] += diff;
+	if (col & 1) {
+	  meta_data[j++] = hpred[0] >> 4;
+	  meta_data[j++] = hpred[0] << 4 | hpred[1] >> 8;
+	  meta_data[j++] = hpred[1];
+	}
+      }
+    }
+  } else
+    fprintf (stderr,_("%s has unknown CAMF type %d.\n"), ifname, type);
 }
 
 const char * CLASS foveon_camf_param (const char *block, const char *param)
@@ -2922,6 +3287,7 @@
   void *dp;
   unsigned dim[3];
 
+  if (!name) return 0;
   dp = foveon_camf_matrix (dim, name);
   if (!dp) return 0;
   memcpy (ptr, dp, size*4);
@@ -3002,6 +3368,7 @@
   if (verbose)
     fprintf (stderr,_("Foveon interpolation...\n"));
 
+  foveon_load_camf();
   foveon_fixed (dscr, 4, "DarkShieldColRange");
   foveon_fixed (ppm[0][0], 27, "PostPolyMatrix");
   foveon_fixed (satlev, 3, "SaturationLevel");
@@ -3010,9 +3377,9 @@
   foveon_fixed (chroma_dq, 3, "ChromaDQ");
   foveon_fixed (color_dq, 3,
 	foveon_camf_param ("IncludeBlocks", "ColorDQ") ?
-		"ColorDQ" : "ColorDTQCamRGB");
+		"ColorDQ" : "ColorDQCamRGB");
   if (foveon_camf_param ("IncludeBlocks", "ColumnFilter"))
-  		 foveon_fixed (&cfilt, 1, "ColumnFilter");
+		 foveon_fixed (&cfilt, 1, "ColumnFilter");
 
   memset (ddft, 0, sizeof ddft);
   if (!foveon_camf_param ("IncludeBlocks", "DarkDrift")
@@ -3076,10 +3443,10 @@
   black = (float (*)[3]) 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]);
+      ((float *)ddft[0])[i] = ((float *)ddft[1])[i] +
+	row / (height-1.0) * (((float *)ddft[2])[i] - ((float *)ddft[1])[i]);
     FORC3 black[row][c] =
- 	( foveon_avg (image[row*width]+c, dscr[0], cfilt) +
+	( 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];
   }
@@ -3122,8 +3489,8 @@
 
   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]);
+      ((float *)ddft[0])[i] = ((float *)ddft[1])[i] +
+	row / (height-1.0) * (((float *)ddft[2])[i] - ((float *)ddft[1])[i]);
     pix = image[row*width];
     memcpy (prev, pix, sizeof prev);
     frow = row / (height-1.0) * (dim[2]-1);
@@ -3162,7 +3529,7 @@
   free (sgrow);
   free (sgain);
 
-  if ((badpix = (unsigned int *) foveon_camf_matrix (dim, "BadPixels"))) {
+  if ((badpix = (unsigned *) 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];
@@ -3304,7 +3671,7 @@
   }
 
   /* Smooth the image bottom-to-top and save at 1/4 scale */
-  shrink = (short (*)[3]) calloc ((width/4) * (height/4), sizeof *shrink);
+  shrink = (short (*)[3]) calloc ((height/4), (width/4)*sizeof *shrink);
   merror (shrink, "foveon_interpolate()");
   for (row = height/4; row--; )
     for (col=0; col < width/4; col++) {
@@ -3380,19 +3747,106 @@
 
 /* RESTRICTED code ends here */
 
+void CLASS crop_masked_pixels()
+{
+  int row, col;
+  unsigned r, c, m, mblack[8], zero, val;
+
+  if (load_raw == &CLASS phase_one_load_raw ||
+      load_raw == &CLASS phase_one_load_raw_c)
+    phase_one_correct();
+  if (fuji_width) {
+    for (row=0; row < raw_height-top_margin*2; row++) {
+      for (col=0; col < fuji_width << !fuji_layout; col++) {
+	if (fuji_layout) {
+	  r = fuji_width - 1 - col + (row >> 1);
+	  c = col + ((row+1) >> 1);
+	} else {
+	  r = fuji_width - 1 + row - (col >> 1);
+	  c = row + ((col+1) >> 1);
+	}
+	if (r < height && c < width)
+	  BAYER(r,c) = RAW(row+top_margin,col+left_margin);
+      }
+    }
+  } else {
+    for (row=0; row < height; row++)
+      for (col=0; col < width; col++)
+	BAYER2(row,col) = RAW(row+top_margin,col+left_margin);
+  }
+  if (mask[0][3] > 0) goto mask_set;
+  if (load_raw == &CLASS canon_load_raw ||
+      load_raw == &CLASS lossless_jpeg_load_raw) {
+    mask[0][1] = mask[1][1] += 2;
+    mask[0][3] -= 2;
+    goto sides;
+  }
+  if (load_raw == &CLASS canon_600_load_raw ||
+      load_raw == &CLASS sony_load_raw ||
+     (load_raw == &CLASS eight_bit_load_raw && strncmp(model,"DC2",3)) ||
+      load_raw == &CLASS kodak_262_load_raw ||
+     (load_raw == &CLASS packed_load_raw && (load_flags & 256))) {
+sides:
+    mask[0][0] = mask[1][0] = top_margin;
+    mask[0][2] = mask[1][2] = top_margin+height;
+    mask[0][3] += left_margin;
+    mask[1][1] += left_margin+width;
+    mask[1][3] += raw_width;
+  }
+  if (load_raw == &CLASS nokia_load_raw) {
+    mask[0][2] = top_margin;
+    mask[0][3] = width;
+  }
+mask_set:
+  memset (mblack, 0, sizeof mblack);
+  for (zero=m=0; m < 8; m++)
+    for (row=MAX(mask[m][0],0); row < MIN(mask[m][2],raw_height); row++)
+      for (col=MAX(mask[m][1],0); col < MIN(mask[m][3],raw_width); col++) {
+	c = FC(row-top_margin,col-left_margin);
+	mblack[c] += val = RAW(row,col);
+	mblack[4+c]++;
+	zero += !val;
+      }
+  if (load_raw == &CLASS canon_600_load_raw && width < raw_width) {
+    black = (mblack[0]+mblack[1]+mblack[2]+mblack[3]) /
+	    (mblack[4]+mblack[5]+mblack[6]+mblack[7]) - 4;
+    canon_600_correct();
+  } else if (zero < mblack[4] && mblack[5] && mblack[6] && mblack[7]) {
+    FORC4 cblack[c] = mblack[c] / mblack[4+c];
+    cblack[4] = cblack[5] = cblack[6] = 0;
+  }
+}
+
+void CLASS remove_zeroes()
+{
+  unsigned row, col, tot, n, r, c;
+
+  for (row=0; row < height; row++)
+    for (col=0; col < width; col++)
+      if (BAYER(row,col) == 0) {
+	tot = n = 0;
+	for (r = row-2; r <= row+2; r++)
+	  for (c = col-2; c <= col+2; c++)
+	    if (r < height && c < width &&
+		FC(r,c) == FC(row,col) && BAYER(r,c))
+	      tot += (n++,BAYER(r,c));
+	if (n) BAYER(row,col) = tot/n;
+      }
+}
+
 /*
    Seach from the current directory up to the root looking for
    a ".badpixels" file, and fix those pixels now.
  */
-void CLASS bad_pixels (char *fname)
+void CLASS bad_pixels (const char *cfname)
 {
   FILE *fp=0;
-  char *cp, line[128];
+  char *fname, *cp, line[128];
   int len, time, row, col, r, c, rad, tot, n, fixed=0;
 
   if (!filters) return;
-  if (fname)
-    fp = fopen (fname, "r");
+  if (cfname)
+    fp = fopen (cfname, "r");
   else {
     for (len=32 ; ; len *= 2) {
       fname = (char *) malloc (len);
@@ -3428,7 +3882,7 @@
       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)) {
+		(r != row || c != col) && fcol(r,c) == fcol(row,col)) {
 	    tot += BAYER2(r,c);
 	    n++;
 	  }
@@ -3443,7 +3897,7 @@
   fclose (fp);
 }
 
-void CLASS subtract (char *fname)
+void CLASS subtract (const char *fname)
 {
   FILE *fp;
   int dim[3]={0,0,0}, comment=0, number=0, error=0, nd=0, c, row, col;
@@ -3480,17 +3934,54 @@
       BAYER(row,col) = MAX (BAYER(row,col) - ntohs(pixel[col]), 0);
   }
   free (pixel);
+  fclose (fp);
+  memset (cblack, 0, sizeof cblack);
   black = 0;
 }
 
-void CLASS pseudoinverse (double (*in)[3], double (*out)[3], int size)
+void CLASS gamma_curve (double pwr, double ts, int mode, int imax)
 {
-  double work[3][6], num;
-  int i, j, k;
+  int i;
+  double g[6], bnd[2]={0,0}, r;
 
-  for (i=0; i < 3; i++) {
-    for (j=0; j < 6; j++)
-      work[i][j] = j == i+3;
+  g[0] = pwr;
+  g[1] = ts;
+  g[2] = g[3] = g[4] = 0;
+  bnd[g[1] >= 1] = 1;
+  if (g[1] && (g[1]-1)*(g[0]-1) <= 0) {
+    for (i=0; i < 48; i++) {
+      g[2] = (bnd[0] + bnd[1])/2;
+      if (g[0]) bnd[(pow(g[2]/g[1],-g[0]) - 1)/g[0] - 1/g[2] > -1] = g[2];
+      else	bnd[g[2]/exp(1-1/g[2]) < g[1]] = g[2];
+    }
+    g[3] = g[2] / g[1];
+    if (g[0]) g[4] = g[2] * (1/g[0] - 1);
+  }
+  if (g[0]) g[5] = 1 / (g[1]*SQR(g[3])/2 - g[4]*(1 - g[3]) +
+		(1 - pow(g[3],1+g[0]))*(1 + g[4])/(1 + g[0])) - 1;
+  else      g[5] = 1 / (g[1]*SQR(g[3])/2 + 1
+		- g[2] - g[3] -	g[2]*g[3]*(log(g[3]) - 1)) - 1;
+  if (!mode--) {
+    memcpy (gamm, g, sizeof gamm);
+    return;
+  }
+  for (i=0; i < 0x10000; i++) {
+    curve[i] = 0xffff;
+    if ((r = (double) i / imax) < 1)
+      curve[i] = 0x10000 * ( mode
+	? (r < g[3] ? r*g[1] : (g[0] ? pow( r,g[0])*(1+g[4])-g[4]    : log(r)*g[2]+1))
+	: (r < g[2] ? r/g[1] : (g[0] ? pow((r+g[4])/(1+g[4]),1/g[0]) : exp((r-1)/g[2]))));
+  }
+}
+
+void CLASS pseudoinverse (double (*in)[3], double (*out)[3], int size)
+{
+  double work[3][6], num;
+  int i, j, k;
+
+  for (i=0; i < 3; i++) {
+    for (j=0; j < 6; j++)
+      work[i][j] = j == i+3;
     for (j=0; j < 3; j++)
       for (k=0; k < size; k++)
 	work[i][j] += in[k][i] * in[k][j];
@@ -3512,7 +4003,7 @@
 	out[i][j] += work[j][k+3] * in[i][k];
 }
 
-void CLASS cam_xyz_coeff (double cam_xyz[4][3])
+void CLASS cam_xyz_coeff (float rgb_cam[3][4], double cam_xyz[4][3])
 {
   double cam_rgb[4][3], inverse[4][3], num;
   int i, j, k;
@@ -3530,7 +4021,7 @@
     pre_mul[i] = 1 / num;
   }
   pseudoinverse (cam_rgb, inverse, colors);
-  for (raw_color = i=0; i < 3; i++)
+  for (i=0; i < 3; i++)
     for (j=0; j < colors; j++)
       rgb_cam[i][j] = inverse[j][i];
 }
@@ -3541,31 +4032,7 @@
 #define NSQ 24
 // Coordinates of the GretagMacbeth ColorChecker squares
 // width, height, 1st_column, 1st_row
-  static const int cut[NSQ][4] = {
-    { 241, 231, 234, 274 },
-    { 251, 235, 534, 274 },
-    { 255, 239, 838, 272 },
-    { 255, 240, 1146, 274 },
-    { 251, 237, 1452, 278 },
-    { 243, 238, 1758, 288 },
-    { 253, 253, 218, 558 },
-    { 255, 249, 524, 562 },
-    { 261, 253, 830, 562 },
-    { 260, 255, 1144, 564 },
-    { 261, 255, 1450, 566 },
-    { 247, 247, 1764, 576 },
-    { 255, 251, 212, 862 },
-    { 259, 259, 518, 862 },
-    { 263, 261, 826, 864 },
-    { 265, 263, 1138, 866 },
-    { 265, 257, 1450, 872 },
-    { 257, 255, 1762, 874 },
-    { 257, 253, 212, 1164 },
-    { 262, 251, 516, 1172 },
-    { 263, 257, 826, 1172 },
-    { 263, 255, 1136, 1176 },
-    { 255, 252, 1452, 1182 },
-    { 257, 253, 1760, 1180 } };
+  int cut[NSQ][4];			// you must set these
 // ColorChecker Chart under 6500-kelvin illumination
   static const double gmb_xyY[NSQ][3] = {
     { 0.400, 0.350, 10.1 },		// Dark Skin
@@ -3593,8 +4060,8 @@
     { 0.310, 0.316, 9.0 },		// Neutral 3.5
     { 0.310, 0.316, 3.1 } };		// Black
   double gmb_cam[NSQ][4], gmb_xyz[NSQ][3];
-  double inverse[NSQ][3], cam_xyz[4][3], num;
-  int c, i, j, k, sq, row, col, count[4];
+  double inverse[NSQ][3], cam_xyz[4][3], balance[4], num;
+  int c, i, j, k, sq, row, col, pass, count[4];
 
   memset (gmb_cam, 0, sizeof gmb_cam);
   for (sq=0; sq < NSQ; sq++) {
@@ -3603,7 +4070,8 @@
       for (col=cut[sq][2]; col < cut[sq][2]+cut[sq][0]; col++) {
 	c = FC(row,col);
 	if (c >= colors) c -= 2;
-	gmb_cam[sq][c] += BAYER(row,col);
+	gmb_cam[sq][c] += BAYER2(row,col);
+	BAYER2(row,col) = black + (BAYER2(row,col)-black)/2;
 	count[c]++;
       }
     FORCC gmb_cam[sq][c] = gmb_cam[sq][c]/count[c] - black;
@@ -3613,11 +4081,16 @@
 		(1 - gmb_xyY[sq][0] - gmb_xyY[sq][1]) / gmb_xyY[sq][1];
   }
   pseudoinverse (gmb_xyz, inverse, NSQ);
-  for (i=0; i < colors; i++)
-    for (j=0; j < 3; j++)
-      for (cam_xyz[i][j] = k=0; k < NSQ; k++)
-	cam_xyz[i][j] += gmb_cam[k][i] * inverse[k][j];
-  cam_xyz_coeff (cam_xyz);
+  for (pass=0; pass < 2; pass++) {
+    for (raw_color = i=0; i < colors; i++)
+      for (j=0; j < 3; j++)
+	for (cam_xyz[i][j] = k=0; k < NSQ; k++)
+	  cam_xyz[i][j] += gmb_cam[k][i] * inverse[k][j];
+    cam_xyz_coeff (rgb_cam, cam_xyz);
+    FORCC balance[c] = pre_mul[c] * gmb_cam[20][c];
+    for (sq=0; sq < NSQ; sq++)
+      FORCC gmb_cam[sq][c] *= balance[c];
+  }
   if (verbose) {
     printf ("    { \"%s %s\", %d,\n\t{", make, model, black);
     num = 10000 / (cam_xyz[1][0] + cam_xyz[1][1] + cam_xyz[1][2]);
@@ -3643,7 +4116,7 @@
 void CLASS wavelet_denoise()
 {
   float *fimg=0, *temp, thold, mul[2], avg, diff;
-  int scale=1, size, lev, hpass, lpass, row, col, nc, c, i, wlast;
+  int scale=1, size, lev, hpass, lpass, row, col, nc, c, i, wlast, blk[2];
   ushort *window[4];
   static const float noise[] =
   { 0.8002,0.2735,0.1202,0.0585,0.0291,0.0152,0.0080,0.0044 };
@@ -3653,6 +4126,7 @@
   while (maximum << scale < 0x10000) scale++;
   maximum <<= --scale;
   black <<= scale;
+  FORC4 cblack[c] <<= scale;
   if ((size = iheight*iwidth) < 0x15550000)
     fimg = (float *) malloc ((size*3 + iheight + iwidth) * sizeof *fimg);
   merror (fimg, "wavelet_denoise()");
@@ -3660,12 +4134,12 @@
   if ((nc = colors) == 3 && filters) nc++;
   FORC(nc) {			/* denoise R,G1,B,G3 individually */
     for (i=0; i < size; i++)
-      fimg[i] = sqrt((unsigned) (image[i][c] << (scale+16)));
+      fimg[i] = 256 * sqrt(image[i][c] << scale);
     for (hpass=lev=0; lev < 5; lev++) {
       lpass = size*((lev & 1)+1);
       for (row=0; row < iheight; row++) {
 	hat_transform (temp, fimg+hpass+row*iwidth, 1, iwidth, 1 << lev);
-        for (col=0; col < iwidth; col++)
+	for (col=0; col < iwidth; col++)
 	  fimg[lpass + row*iwidth + col] = temp[col] * 0.25;
       }
       for (col=0; col < iwidth; col++) {
@@ -3687,8 +4161,10 @@
       image[i][c] = CLIP(SQR(fimg[i]+fimg[lpass+i])/0x10000);
   }
   if (filters && colors == 3) {  /* pull G1 and G3 closer together */
-    for (row=0; row < 2; row++)
+    for (row=0; row < 2; row++) {
       mul[row] = 0.125 * pre_mul[FC(row+1,0) | 1] / pre_mul[FC(row,0) | 1];
+      blk[row] = cblack[FC(row,0) | 1];
+    }
     for (i=0; i < 4; i++)
       window[i] = (ushort *) fimg + width*i;
     for (wlast=-1, row=1; row < height-1; row++) {
@@ -3701,8 +4177,8 @@
       thold = threshold/512;
       for (col = (FC(row,0) & 1)+1; col < width-1; col+=2) {
 	avg = ( window[0][col-1] + window[0][col+1] +
-		window[2][col-1] + window[2][col+1] - black*4 )
-	      * mul[row & 1] + (window[1][col] - black) * 0.5 + black;
+		window[2][col-1] + window[2][col+1] - blk[~row & 1]*4 )
+	      * mul[row & 1] + (window[1][col] + blk[row & 1]) * 0.5;
 	avg = avg < 0 ? 0 : sqrt(avg);
 	diff = sqrt(BAYER(row,col)) - avg;
 	if      (diff < -thold) diff += thold;
@@ -3736,12 +4212,12 @@
 	  for (x=col; x < col+8 && x < right; x++)
 	    FORC4 {
 	      if (filters) {
-		c = FC(y,x);
-		val = BAYER(y,x);
+		c = fcol(y,x);
+		val = BAYER2(y,x);
 	      } else
 		val = image[y*width+x][c];
 	      if (val > maximum-25) goto skip_block;
-	      if ((val -= black) < 0) val = 0;
+	      if ((val -= cblack[c]) < 0) val = 0;
 	      sum[c] += val;
 	      sum[c+4]++;
 	      if (filters) break;
@@ -3756,7 +4232,7 @@
     for (row=0; row < 8; row++)
       for (col=0; col < 8; col++) {
 	c = FC(row,col);
-	if ((val = white[row][col] - black) > 0)
+	if ((val = white[row][col] - cblack[c]) > 0)
 	  sum[c] += val;
 	sum[c+4]++;
       }
@@ -3767,6 +4243,7 @@
     else
       fprintf (stderr,_("%s: Cannot use camera white balance.\n"), ifname);
   }
+  if (pre_mul[1] == 0) pre_mul[1] = 1;
   if (pre_mul[3] == 0) pre_mul[3] = colors < 4 ? pre_mul[1] : 1;
   dark = black;
   sat = maximum;
@@ -3786,13 +4263,20 @@
     FORC4 fprintf (stderr, " %f", pre_mul[c]);
     fputc ('\n', stderr);
   }
+  if (filters > 1000 && (cblack[4]+1)/2 == 1 && (cblack[5]+1)/2 == 1) {
+    FORC4 cblack[FC(c/2,c%2)] +=
+	cblack[6 + c/2 % cblack[4] * cblack[5] + c%2 % cblack[5]];
+    cblack[4] = cblack[5] = 0;
+  }
   size = iheight*iwidth;
   for (i=0; i < size*4; i++) {
-    val = image[0][i];
-    if (!val) continue;
-    val -= black;
+    if (!(val = ((ushort *)image)[i])) continue;
+    if (cblack[4] && cblack[5])
+      val -= cblack[6 + i/4 / iwidth % cblack[4] * cblack[5] +
+			i/4 % iwidth % cblack[5]];
+    val -= cblack[i & 3];
     val *= scale_mul[i & 3];
-    image[0][i] = CLIP(val);
+    ((ushort *)image)[i] = CLIP(val);
   }
   if ((aber[0] != 1 || aber[2] != 1) && colors == 3) {
     if (verbose)
@@ -3831,12 +4315,24 @@
     if (half_size) {
       height = iheight;
       width  = iwidth;
+      if (filters == 9) {
+	for (row=0; row < 3; row++)
+	  for (col=1; col < 4; col++)
+	    if (!(image[row*width+col][0] | image[row*width+col][2]))
+	      goto break2;  break2:
+	for ( ; row < height; row+=3)
+	  for (col=(col-1)%3+1; col < width-1; col+=3) {
+	    img = image + row*width+col;
+	    for (c=0; c < 3; c+=2)
+	      img[0][c] = (img[-1][c] + img[1][c]) >> 1;
+	  }
+      }
     } else {
-      img = (ushort (*)[4]) calloc (height*width, sizeof *img);
-      merror (img, "unshrink()");
+      img = (ushort (*)[4]) calloc (height, width*sizeof *img);
+      merror (img, "pre_interpolate()");
       for (row=0; row < height; row++)
 	for (col=0; col < width; col++) {
-	  c = fc(row,col);
+	  c = fcol(row,col);
 	  img[row*width+col][c] = image[(row >> 1)*iwidth+(col >> 1)][c];
 	}
       free (image);
@@ -3844,8 +4340,9 @@
       shrink = 0;
     }
   }
-  if (filters && colors == 3) {
-    if ((mix_green = four_color_rgb)) colors++;
+  if (filters > 1000 && colors == 3) {
+    mix_green = four_color_rgb ^ half_size;
+    if (four_color_rgb | half_size) colors++;
     else {
       for (row = FC(1,0) >> 1; row < height; row+=2)
 	for (col = FC(row,1) & 1; col < width; col+=2)
@@ -3868,11 +4365,11 @@
       for (y=row-1; y != row+2; y++)
 	for (x=col-1; x != col+2; x++)
 	  if (y < height && x < width) {
-	    f = fc(y,x);
+	    f = fcol(y,x);
 	    sum[f] += image[y*width+x][f];
 	    sum[f+4]++;
 	  }
-      f = fc(row,col);
+      f = fcol(row,col);
       FORCC if (c != f && sum[c+4])
 	image[row*width+col][c] = sum[c] / sum[c+4];
     }
@@ -3880,29 +4377,31 @@
 
 void CLASS lin_interpolate()
 {
-  int code[16][16][32], *ip, sum[4];
-  int c, i, x, y, row, col, shift, color;
+  int code[16][16][32], size=16, *ip, sum[4];
+  int f, c, i, x, y, row, col, shift, color;
   ushort *pix;
 
   if (verbose) fprintf (stderr,_("Bilinear interpolation...\n"));
-
+  if (filters == 9) size = 6;
   border_interpolate(1);
-  for (row=0; row < 16; row++)
-    for (col=0; col < 16; col++) {
-      ip = code[row][col];
+  for (row=0; row < size; row++)
+    for (col=0; col < size; col++) {
+      ip = code[row][col]+1;
+      f = fcol(row,col);
       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);
+	  color = fcol(row+y,col+x);
+	  if (color == f) continue;
 	  *ip++ = (width*y + x)*4 + color;
 	  *ip++ = shift;
 	  *ip++ = color;
 	  sum[color] += 1 << shift;
 	}
+      code[row][col][0] = (ip - code[row][col]) / 3;
       FORCC
-	if (c != fc(row,col)) {
+	if (c != f) {
 	  *ip++ = c;
 	  *ip++ = 256 / sum[c];
 	}
@@ -3910,9 +4409,9 @@
   for (row=1; row < height-1; row++)
     for (col=1; col < width-1; col++) {
       pix = image[row*width+col];
-      ip = code[row & 15][col & 15];
+      ip = code[row % size][col % size];
       memset (sum, 0, sizeof sum);
-      for (i=8; i--; ip+=3)
+      for (i=*ip++; i--; ip+=3)
 	sum[ip[2]] += pix[ip[0]] << ip[1];
       for (i=colors; --i; ip+=2)
 	pix[ip[0]] = sum[ip[0]] * ip[1] >> 8;
@@ -3924,66 +4423,60 @@
 
    "Interpolation using a Threshold-based variable number of gradients"
 
-   described in http://scien.stanford.edu/class/psych221/projects/99/tingchen/algodep/vargra.html
+   described in http://scien.stanford.edu/pages/labsite/1999/psych221/projects/99/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.
  */
 void CLASS vng_interpolate()
 {
-  struct interpolate_terms {
-    signed char y1, x1, y2, x2, weight;
-    unsigned char grads;
-  };
-  static const struct interpolate_terms terms[] = {
-    {-2,-2,+0,-1,0,0x01}, {-2,-2,+0,+0,1,0x01}, {-2,-1,-1,+0,0,0x01},
-    {-2,-1,+0,-1,0,0x02}, {-2,-1,+0,+0,0,0x03}, {-2,-1,+0,+1,1,0x01},
-    {-2,+0,+0,-1,0,0x06}, {-2,+0,+0,+0,1,0x02}, {-2,+0,+0,+1,0,0x03},
-    {-2,+1,-1,+0,0,0x04}, {-2,+1,+0,-1,1,0x04}, {-2,+1,+0,+0,0,0x06},
-    {-2,+1,+0,+1,0,0x02}, {-2,+2,+0,+0,1,0x04}, {-2,+2,+0,+1,0,0x04},
-    {-1,-2,-1,+0,0,0x80}, {-1,-2,+0,-1,0,0x01}, {-1,-2,+1,-1,0,0x01},
-    {-1,-2,+1,+0,1,0x01}, {-1,-1,-1,+1,0,0x88}, {-1,-1,+1,-2,0,0x40},
-    {-1,-1,+1,-1,0,0x22}, {-1,-1,+1,+0,0,0x33}, {-1,-1,+1,+1,1,0x11},
-    {-1,+0,-1,+2,0,0x08}, {-1,+0,+0,-1,0,0x44}, {-1,+0,+0,+1,0,0x11},
-    {-1,+0,+1,-2,1,0x40}, {-1,+0,+1,-1,0,0x66}, {-1,+0,+1,+0,1,0x22},
-    {-1,+0,+1,+1,0,0x33}, {-1,+0,+1,+2,1,0x10}, {-1,+1,+1,-1,1,0x44},
-    {-1,+1,+1,+0,0,0x66}, {-1,+1,+1,+1,0,0x22}, {-1,+1,+1,+2,0,0x10},
-    {-1,+2,+0,+1,0,0x04}, {-1,+2,+1,+0,1,0x04}, {-1,+2,+1,+1,0,0x04},
-    {+0,-2,+0,+0,1,0x80}, {+0,-1,+0,+1,1,0x88}, {+0,-1,+1,-2,0,0x40},
-    {+0,-1,+1,+0,0,0x11}, {+0,-1,+2,-2,0,0x40}, {+0,-1,+2,-1,0,0x20},
-    {+0,-1,+2,+0,0,0x30}, {+0,-1,+2,+1,1,0x10}, {+0,+0,+0,+2,1,0x08},
-    {+0,+0,+2,-2,1,0x40}, {+0,+0,+2,-1,0,0x60}, {+0,+0,+2,+0,1,0x20},
-    {+0,+0,+2,+1,0,0x30}, {+0,+0,+2,+2,1,0x10}, {+0,+1,+1,+0,0,0x44},
-    {+0,+1,+1,+2,0,0x10}, {+0,+1,+2,-1,1,0x40}, {+0,+1,+2,+0,0,0x60},
-    {+0,+1,+2,+1,0,0x20}, {+0,+1,+2,+2,0,0x10}, {+1,-2,+1,+0,0,0x80},
-    {+1,-1,+1,+1,0,0x88}, {+1,+0,+1,+2,0,0x08}, {+1,+0,+2,-1,0,0x40},
-    {+1,+0,+2,+1,0,0x10}
-  };
-  const struct interpolate_terms *cpt;
-  signed char *cp;
-  signed char chood[] = { -1,-1, -1,0, -1,+1, 0,+1, +1,+1, +1,0, +1,-1, 0,-1 };
+  static const signed char *cp, terms[] = {
+    -2,-2,+0,-1,0,0x01, -2,-2,+0,+0,1,0x01, -2,-1,-1,+0,0,0x01,
+    -2,-1,+0,-1,0,0x02, -2,-1,+0,+0,0,0x03, -2,-1,+0,+1,1,0x01,
+    -2,+0,+0,-1,0,0x06, -2,+0,+0,+0,1,0x02, -2,+0,+0,+1,0,0x03,
+    -2,+1,-1,+0,0,0x04, -2,+1,+0,-1,1,0x04, -2,+1,+0,+0,0,0x06,
+    -2,+1,+0,+1,0,0x02, -2,+2,+0,+0,1,0x04, -2,+2,+0,+1,0,0x04,
+    -1,-2,-1,+0,0,0x80, -1,-2,+0,-1,0,0x01, -1,-2,+1,-1,0,0x01,
+    -1,-2,+1,+0,1,0x01, -1,-1,-1,+1,0,0x88, -1,-1,+1,-2,0,0x40,
+    -1,-1,+1,-1,0,0x22, -1,-1,+1,+0,0,0x33, -1,-1,+1,+1,1,0x11,
+    -1,+0,-1,+2,0,0x08, -1,+0,+0,-1,0,0x44, -1,+0,+0,+1,0,0x11,
+    -1,+0,+1,-2,1,0x40, -1,+0,+1,-1,0,0x66, -1,+0,+1,+0,1,0x22,
+    -1,+0,+1,+1,0,0x33, -1,+0,+1,+2,1,0x10, -1,+1,+1,-1,1,0x44,
+    -1,+1,+1,+0,0,0x66, -1,+1,+1,+1,0,0x22, -1,+1,+1,+2,0,0x10,
+    -1,+2,+0,+1,0,0x04, -1,+2,+1,+0,1,0x04, -1,+2,+1,+1,0,0x04,
+    +0,-2,+0,+0,1,0x80, +0,-1,+0,+1,1,0x88, +0,-1,+1,-2,0,0x40,
+    +0,-1,+1,+0,0,0x11, +0,-1,+2,-2,0,0x40, +0,-1,+2,-1,0,0x20,
+    +0,-1,+2,+0,0,0x30, +0,-1,+2,+1,1,0x10, +0,+0,+0,+2,1,0x08,
+    +0,+0,+2,-2,1,0x40, +0,+0,+2,-1,0,0x60, +0,+0,+2,+0,1,0x20,
+    +0,+0,+2,+1,0,0x30, +0,+0,+2,+2,1,0x10, +0,+1,+1,+0,0,0x44,
+    +0,+1,+1,+2,0,0x10, +0,+1,+2,-1,1,0x40, +0,+1,+2,+0,0,0x60,
+    +0,+1,+2,+1,0,0x20, +0,+1,+2,+2,0,0x10, +1,-2,+1,+0,0,0x80,
+    +1,-1,+1,+1,0,0x88, +1,+0,+1,+2,0,0x08, +1,+0,+2,-1,0,0x40,
+    +1,+0,+2,+1,0,0x10
+  }, chood[] = { -1,-1, -1,0, -1,+1, 0,+1, +1,+1, +1,0, +1,-1, 0,-1 };
   ushort (*brow[5])[4], *pix;
-  int prow=7, pcol=1, *ip, *code[16][16], gval[8], gmin, gmax, sum[4];
+  int prow=8, pcol=2, *ip, *code[16][16], gval[8], gmin, gmax, sum[4];
   int row, col, x, y, x1, x2, y1, y2, t, weight, grads, color, diag;
   int g, diff, thold, num, c;
 
   lin_interpolate();
   if (verbose) fprintf (stderr,_("VNG interpolation...\n"));
 
-  if (filters == 1) prow = pcol = 15;
-  ip = (int *) calloc ((prow+1)*(pcol+1), 1280);
+  if (filters == 1) prow = pcol = 16;
+  if (filters == 9) prow = pcol =  6;
+  ip = (int *) calloc (prow*pcol, 1280);
   merror (ip, "vng_interpolate()");
-  for (row=0; row <= prow; row++)		/* Precalculate for VNG */
-    for (col=0; col <= pcol; col++) {
+  for (row=0; row < prow; row++)		/* Precalculate for VNG */
+    for (col=0; col < pcol; col++) {
       code[row][col] = ip;
-      for (cpt=&terms[0], t=0; t < 64, cpt = &terms[t]; t++) {
-	y1 = cpt->y1;  x1 = cpt->x1;
-	y2 = cpt->y2;  x2 = cpt->x2;
-	weight = cpt->weight;
-	grads = cpt->grads;
-	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;
+      for (cp=terms, t=0; t < 64; t++) {
+	y1 = *cp++;  x1 = *cp++;
+	y2 = *cp++;  x2 = *cp++;
+	weight = *cp++;
+	grads = *cp++;
+	color = fcol(row+y1,col+x1);
+	if (fcol(row+y2,col+x2) != color) continue;
+	diag = (fcol(row,col+1) == color && fcol(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;
@@ -3996,8 +4489,8 @@
       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)
+	color = fcol(row,col);
+	if (fcol(row+y,col+x) != color && fcol(row+y*2,col+x*2) == color)
 	  *ip++ = (y*width + x) * 8 + color;
 	else
 	  *ip++ = 0;
@@ -4010,7 +4503,7 @@
   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 & prow][col & pcol];
+      ip = code[row % prow][col % pcol];
       memset (gval, 0, sizeof gval);
       while ((g = ip[0]) != INT_MAX) {		/* Calculate gradients */
 	diff = ABS(pix[g] - pix[ip[1]]) << ip[2];
@@ -4033,7 +4526,7 @@
       }
       thold = gmin + (gmax >> 1);
       memset (sum, 0, sizeof sum);
-      color = fc(row,col);
+      color = fcol(row,col);
       for (num=g=0; g < 8; g++,ip+=2) {		/* Average the neighbors */
 	if (gval[g] <= thold) {
 	  FORCC
@@ -4067,9 +4560,8 @@
 */
 void CLASS ppg_interpolate()
 {
-  int gr[4], dir[5] = { 1, width, -1, -width, 1 };
-  int row, col, avg, diff[2], guess[2], c, d, i;
-  static const short sort[] = { 0,2,1,3,0,1,2,3 };
+  int dir[5] = { 1, width, -1, -width, 1 };
+  int row, col, diff[2], guess[2], c, d, i;
   ushort (*pix)[4];
 
   border_interpolate(3);
@@ -4079,20 +4571,6 @@
   for (row=3; row < height-3; row++)
     for (col=3+(FC(row,3) & 1), c=FC(row,col); col < width-3; col+=2) {
       pix = image + row*width+col;
-      for (avg=i=0; i < 4; i++)
-	avg += gr[i] = pix[dir[i]][1] << 2;
-      avg >>= 2;
-      for (i=0; i < 8; i+=2)
-	if (gr[sort[i]] > gr[sort[i+1]])
-	  SWAP(gr[sort[i]],gr[sort[i+1]])
-      for (d=0; d < 4; d++) {
-	for (i=-2; i < 2; i++)
-	  if (pix[i*dir[d] + (i+1)*dir[d+1]][1] <= avg) break;
-	if (i == 2) {
-	  pix[0][1] = (gr[1]+gr[2]) >> 3;
-	  goto next_pixel;
-	}
-      }
       for (i=0; (d=dir[i]) > 0; i++) {
 	guess[i] = (pix[-d][1] + pix[0][c] + pix[d][1]) * 2
 		      - pix[-2*d][c] - pix[2*d][c];
@@ -4104,7 +4582,6 @@
       }
       d = dir[i = diff[0] > diff[1]];
       pix[0][1] = ULIM(guess[i] >> 2, pix[d][1], pix[-d][1]);
-next_pixel: ;
     }
 /*  Calculate red and blue for each green pixel:		*/
   for (row=1; row < height-1; row++)
@@ -4132,36 +4609,288 @@
     }
 }
 
+void CLASS cielab (ushort rgb[3], short lab[3])
+{
+  int c, i, j, k;
+  float r, xyz[3];
+  static float cbrt[0x10000], xyz_cam[3][4];
+
+  if (!rgb) {
+    for (i=0; i < 0x10000; i++) {
+      r = i / 65535.0;
+      cbrt[i] = r > 0.008856 ? pow(r,1/3.0) : 7.787*r + 16/116.0;
+    }
+    for (i=0; i < 3; i++)
+      for (j=0; j < colors; j++)
+	for (xyz_cam[i][j] = k=0; k < 3; k++)
+	  xyz_cam[i][j] += xyz_rgb[i][k] * rgb_cam[k][j] / d65_white[i];
+    return;
+  }
+  xyz[0] = xyz[1] = xyz[2] = 0.5;
+  FORCC {
+    xyz[0] += xyz_cam[0][c] * rgb[c];
+    xyz[1] += xyz_cam[1][c] * rgb[c];
+    xyz[2] += xyz_cam[2][c] * rgb[c];
+  }
+  xyz[0] = cbrt[CLIP((int) xyz[0])];
+  xyz[1] = cbrt[CLIP((int) xyz[1])];
+  xyz[2] = cbrt[CLIP((int) xyz[2])];
+  lab[0] = 64 * (116 * xyz[1] - 16);
+  lab[1] = 64 * 500 * (xyz[0] - xyz[1]);
+  lab[2] = 64 * 200 * (xyz[1] - xyz[2]);
+}
+
+#define TS 512		/* Tile Size */
+#define fcol(row,col) xtrans[(row+6) % 6][(col+6) % 6]
+
+/*
+   Frank Markesteijn's algorithm for Fuji X-Trans sensors
+ */
+void CLASS xtrans_interpolate (int passes)
+{
+  int c, d, f, g, h, i, v, ng, row, col, top, left, mrow, mcol;
+  int val, ndir, pass, hm[8], avg[4], color[3][8];
+  static const short orth[12] = { 1,0,0,1,-1,0,0,-1,1,0,0,1 },
+	patt[2][16] = { { 0,1,0,-1,2,0,-1,0,1,1,1,-1,0,0,0,0 },
+			{ 0,1,0,-2,1,0,-2,0,1,1,-2,-2,1,-1,-1,1 } },
+	dir[4] = { 1,TS,TS+1,TS-1 };
+  short allhex[3][3][2][8], *hex;
+  ushort min, max, sgrow, sgcol;
+  ushort (*rgb)[TS][TS][3], (*rix)[3], (*pix)[4];
+   short (*lab)    [TS][3], (*lix)[3];
+   float (*drv)[TS][TS], diff[6], tr;
+   char (*homo)[TS][TS], *buffer;
+
+  if (verbose)
+    fprintf (stderr,_("%d-pass X-Trans interpolation...\n"), passes);
+
+  cielab (0,0);
+  ndir = 4 << (passes > 1);
+  buffer = (char *) malloc (TS*TS*(ndir*11+6));
+  merror (buffer, "xtrans_interpolate()");
+  rgb  = (ushort(*)[TS][TS][3]) buffer;
+  lab  = (short (*)    [TS][3])(buffer + TS*TS*(ndir*6));
+  drv  = (float (*)[TS][TS])   (buffer + TS*TS*(ndir*6+6));
+  homo = (char  (*)[TS][TS])   (buffer + TS*TS*(ndir*10+6));
+
+/* Map a green hexagon around each non-green pixel and vice versa:	*/
+  for (row=0; row < 3; row++)
+    for (col=0; col < 3; col++)
+      for (ng=d=0; d < 10; d+=2) {
+	g = fcol(row,col) == 1;
+	if (fcol(row+orth[d],col+orth[d+2]) == 1) ng=0; else ng++;
+	if (ng == 4) { sgrow = row; sgcol = col; }
+	if (ng == g+1) FORC(8) {
+	  v = orth[d  ]*patt[g][c*2] + orth[d+1]*patt[g][c*2+1];
+	  h = orth[d+2]*patt[g][c*2] + orth[d+3]*patt[g][c*2+1];
+	  allhex[row][col][0][c^(g*2 & d)] = h + v*width;
+	  allhex[row][col][1][c^(g*2 & d)] = h + v*TS;
+	}
+      }
+
+/* Set green1 and green3 to the minimum and maximum allowed values:	*/
+  for (row=2; row < height-2; row++)
+    for (min=~(max=0), col=2; col < width-2; col++) {
+      if (fcol(row,col) == 1 && (min=~(max=0))) continue;
+      pix = image + row*width + col;
+      hex = allhex[row % 3][col % 3][0];
+      if (!max) FORC(6) {
+	val = pix[hex[c]][1];
+	if (min > val) min = val;
+	if (max < val) max = val;
+      }
+      pix[0][1] = min;
+      pix[0][3] = max;
+      switch ((row-sgrow) % 3) {
+	case 1: if (row < height-3) { row++; col--; } break;
+	case 2: if ((min=~(max=0)) && (col+=2) < width-3 && row > 2) row--;
+      }
+    }
+
+  for (top=3; top < height-19; top += TS-16)
+    for (left=3; left < width-19; left += TS-16) {
+      mrow = MIN (top+TS, height-3);
+      mcol = MIN (left+TS, width-3);
+      for (row=top; row < mrow; row++)
+	for (col=left; col < mcol; col++)
+	  memcpy (rgb[0][row-top][col-left], image[row*width+col], 6);
+      FORC3 memcpy (rgb[c+1], rgb[0], sizeof *rgb);
+
+/* Interpolate green horizontally, vertically, and along both diagonals: */
+      for (row=top; row < mrow; row++)
+	for (col=left; col < mcol; col++) {
+	  if ((f = fcol(row,col)) == 1) continue;
+	  pix = image + row*width + col;
+	  hex = allhex[row % 3][col % 3][0];
+	  color[1][0] = 174 * (pix[  hex[1]][1] + pix[  hex[0]][1]) -
+			 46 * (pix[2*hex[1]][1] + pix[2*hex[0]][1]);
+	  color[1][1] = 223 *  pix[  hex[3]][1] + pix[  hex[2]][1] * 33 +
+			 92 * (pix[      0 ][f] - pix[ -hex[2]][f]);
+	  FORC(2) color[1][2+c] =
+		164 * pix[hex[4+c]][1] + 92 * pix[-2*hex[4+c]][1] + 33 *
+		(2*pix[0][f] - pix[3*hex[4+c]][f] - pix[-3*hex[4+c]][f]);
+	  FORC4 rgb[c^!((row-sgrow) % 3)][row-top][col-left][1] =
+		LIM(color[1][c] >> 8,pix[0][1],pix[0][3]);
+	}
+
+      for (pass=0; pass < passes; pass++) {
+	if (pass == 1)
+	  memcpy (rgb+=4, buffer, 4*sizeof *rgb);
+
+/* Recalculate green from interpolated values of closer pixels:	*/
+	if (pass) {
+	  for (row=top+2; row < mrow-2; row++)
+	    for (col=left+2; col < mcol-2; col++) {
+	      if ((f = fcol(row,col)) == 1) continue;
+	      pix = image + row*width + col;
+	      hex = allhex[row % 3][col % 3][1];
+	      for (d=3; d < 6; d++) {
+		rix = &rgb[(d-2)^!((row-sgrow) % 3)][row-top][col-left];
+		val = rix[-2*hex[d]][1] + 2*rix[hex[d]][1]
+		    - rix[-2*hex[d]][f] - 2*rix[hex[d]][f] + 3*rix[0][f];
+		rix[0][1] = LIM(val/3,pix[0][1],pix[0][3]);
+	      }
+	    }
+	}
+
+/* Interpolate red and blue values for solitary green pixels:	*/
+	for (row=(top-sgrow+4)/3*3+sgrow; row < mrow-2; row+=3)
+	  for (col=(left-sgcol+4)/3*3+sgcol; col < mcol-2; col+=3) {
+	    rix = &rgb[0][row-top][col-left];
+	    h = fcol(row,col+1);
+	    memset (diff, 0, sizeof diff);
+	    for (i=1, d=0; d < 6; d++, i^=TS^1, h^=2) {
+	      for (c=0; c < 2; c++, h^=2) {
+		g = 2*rix[0][1] - rix[i<<c][1] - rix[-i<<c][1];
+		color[h][d] = g + rix[i<<c][h] + rix[-i<<c][h];
+		if (d > 1)
+		  diff[d] += SQR (rix[i<<c][1] - rix[-i<<c][1]
+				- rix[i<<c][h] + rix[-i<<c][h]) + SQR(g);
+	      }
+	      if (d > 1 && (d & 1))
+		if (diff[d-1] < diff[d])
+		  FORC(2) color[c*2][d] = color[c*2][d-1];
+	      if (d < 2 || (d & 1)) {
+		FORC(2) rix[0][c*2] = CLIP(color[c*2][d]/2);
+		rix += TS*TS;
+	      }
+	    }
+	  }
+
+/* Interpolate red for blue pixels and vice versa:		*/
+	for (row=top+3; row < mrow-3; row++)
+	  for (col=left+3; col < mcol-3; col++) {
+	    if ((f = 2-fcol(row,col)) == 1) continue;
+	    rix = &rgb[0][row-top][col-left];
+	    c = (row-sgrow) % 3 ? TS:1;
+	    h = 3 * (c ^ TS ^ 1);
+	    for (d=0; d < 4; d++, rix += TS*TS) {
+	      i = d > 1 || ((d ^ c) & 1) ||
+		 ((ABS(rix[0][1]-rix[c][1])+ABS(rix[0][1]-rix[-c][1])) <
+		2*(ABS(rix[0][1]-rix[h][1])+ABS(rix[0][1]-rix[-h][1]))) ? c:h;
+	      rix[0][f] = CLIP((rix[i][f] + rix[-i][f] +
+		  2*rix[0][1] - rix[i][1] - rix[-i][1])/2);
+	    }
+	  }
+
+/* Fill in red and blue for 2x2 blocks of green:		*/
+	for (row=top+2; row < mrow-2; row++) if ((row-sgrow) % 3)
+	  for (col=left+2; col < mcol-2; col++) if ((col-sgcol) % 3) {
+	    rix = &rgb[0][row-top][col-left];
+	    hex = allhex[row % 3][col % 3][1];
+	    for (d=0; d < ndir; d+=2, rix += TS*TS)
+	      if (hex[d] + hex[d+1]) {
+		g = 3*rix[0][1] - 2*rix[hex[d]][1] - rix[hex[d+1]][1];
+		for (c=0; c < 4; c+=2) rix[0][c] =
+			CLIP((g + 2*rix[hex[d]][c] + rix[hex[d+1]][c])/3);
+	      } else {
+		g = 2*rix[0][1] - rix[hex[d]][1] - rix[hex[d+1]][1];
+		for (c=0; c < 4; c+=2) rix[0][c] =
+			CLIP((g + rix[hex[d]][c] + rix[hex[d+1]][c])/2);
+	      }
+	  }
+      }
+      rgb = (ushort(*)[TS][TS][3]) buffer;
+      mrow -= top;
+      mcol -= left;
+
+/* Convert to CIELab and differentiate in all directions:	*/
+      for (d=0; d < ndir; d++) {
+	for (row=2; row < mrow-2; row++)
+	  for (col=2; col < mcol-2; col++)
+	    cielab (rgb[d][row][col], lab[row][col]);
+	for (f=dir[d & 3],row=3; row < mrow-3; row++)
+	  for (col=3; col < mcol-3; col++) {
+	    lix = &lab[row][col];
+	    g = 2*lix[0][0] - lix[f][0] - lix[-f][0];
+	    drv[d][row][col] = SQR(g)
+	      + SQR((2*lix[0][1] - lix[f][1] - lix[-f][1] + g*500/232))
+	      + SQR((2*lix[0][2] - lix[f][2] - lix[-f][2] - g*500/580));
+	  }
+      }
+
+/* Build homogeneity maps from the derivatives:			*/
+      memset(homo, 0, ndir*TS*TS);
+      for (row=4; row < mrow-4; row++)
+	for (col=4; col < mcol-4; col++) {
+	  for (tr=FLT_MAX, d=0; d < ndir; d++)
+	    if (tr > drv[d][row][col])
+		tr = drv[d][row][col];
+	  tr *= 8;
+	  for (d=0; d < ndir; d++)
+	    for (v=-1; v <= 1; v++)
+	      for (h=-1; h <= 1; h++)
+		if (drv[d][row+v][col+h] <= tr)
+		  homo[d][row][col]++;
+	}
+
+/* Average the most homogenous pixels for the final result:	*/
+      if (height-top < TS+4) mrow = height-top+2;
+      if (width-left < TS+4) mcol = width-left+2;
+      for (row = MIN(top,8); row < mrow-8; row++)
+	for (col = MIN(left,8); col < mcol-8; col++) {
+	  for (d=0; d < ndir; d++)
+	    for (hm[d]=0, v=-2; v <= 2; v++)
+	      for (h=-2; h <= 2; h++)
+		hm[d] += homo[d][row+v][col+h];
+	  for (d=0; d < ndir-4; d++)
+	    if (hm[d] < hm[d+4]) hm[d  ] = 0; else
+	    if (hm[d] > hm[d+4]) hm[d+4] = 0;
+	  for (max=hm[0],d=1; d < ndir; d++)
+	    if (max < hm[d]) max = hm[d];
+	  max -= max >> 3;
+	  memset (avg, 0, sizeof avg);
+	  for (d=0; d < ndir; d++)
+	    if (hm[d] >= max) {
+	      FORC3 avg[c] += rgb[d][row][col][c];
+	      avg[3]++;
+	    }
+	  FORC3 image[(row+top)*width+col+left][c] = avg[c]/avg[3];
+	}
+    }
+  free(buffer);
+  border_interpolate(8);
+}
+#undef fcol
+
 /*
    Adaptive Homogeneity-Directed interpolation is based on
    the work of Keigo Hirakawa, Thomas Parks, and Paul Lee.
  */
-#define TS 256		/* Tile Size */
-
 void CLASS ahd_interpolate()
 {
-  int i, j, k, top, left, row, col, tr, tc, c, d, val, hm[2];
-  ushort (*pix)[4], (*rix)[3];
+  int i, j, top, left, row, col, tr, tc, c, d, val, hm[2];
   static const int dir[4] = { -1, 1, -TS, TS };
   unsigned ldiff[2][4], abdiff[2][4], leps, abeps;
-  float r, cbrt[0x10000], xyz[3], xyz_cam[3][4];
-  ushort (*rgb)[TS][TS][3];
+  ushort (*rgb)[TS][TS][3], (*rix)[3], (*pix)[4];
    short (*lab)[TS][TS][3], (*lix)[3];
    char (*homo)[TS][TS], *buffer;
 
   if (verbose) fprintf (stderr,_("AHD interpolation...\n"));
 
-  for (i=0; i < 0x10000; i++) {
-    r = i / 65535.0;
-    cbrt[i] = r > 0.008856 ? pow(r,1/3.0) : 7.787*r + 16/116.0;
-  }
-  for (i=0; i < 3; i++)
-    for (j=0; j < colors; j++)
-      for (xyz_cam[i][j] = k=0; k < 3; k++)
-	xyz_cam[i][j] += xyz_rgb[i][k] * rgb_cam[k][j] / d65_white[i];
-
+  cielab (0,0);
   border_interpolate(5);
-  buffer = (char *) malloc (26*TS*TS);		/* 1664 kB */
+  buffer = (char *) malloc (26*TS*TS);
   merror (buffer, "ahd_interpolate()");
   rgb  = (ushort(*)[TS][TS][3]) buffer;
   lab  = (short (*)[TS][TS][3])(buffer + 12*TS*TS);
@@ -4171,7 +4900,7 @@
     for (left=2; left < width-5; left += TS-6) {
 
 /*  Interpolate green horizontally and vertically:		*/
-      for (row = top; row < top+TS && row < height-2; row++) {
+      for (row=top; row < top+TS && row < height-2; row++) {
 	col = left + (FC(row,left) & 1);
 	for (c = FC(row,col); col < left+TS && col < width-2; col+=2) {
 	  pix = image + row*width+col;
@@ -4205,18 +4934,7 @@
 	    rix[0][c] = CLIP(val);
 	    c = FC(row,col);
 	    rix[0][c] = pix[0][c];
-	    xyz[0] = xyz[1] = xyz[2] = 0.5;
-	    FORCC {
-	      xyz[0] += xyz_cam[0][c] * rix[0][c];
-	      xyz[1] += xyz_cam[1][c] * rix[0][c];
-	      xyz[2] += xyz_cam[2][c] * rix[0][c];
-	    }
-	    xyz[0] = cbrt[CLIP((int) xyz[0])];
-	    xyz[1] = cbrt[CLIP((int) xyz[1])];
-	    xyz[2] = cbrt[CLIP((int) xyz[2])];
-	    lix[0][0] = 64 * (116 * xyz[1] - 16);
-	    lix[0][1] = 64 * 500 * (xyz[0] - xyz[1]);
-	    lix[0][2] = 64 * 200 * (xyz[1] - xyz[2]);
+	    cielab (rix[0],lix[0]);
 	  }
 /*  Build homogeneity maps from the CIELab images:		*/
       memset (homo, 0, 2*TS*TS);
@@ -4263,7 +4981,7 @@
 }
 #undef TS
 
-void CLASS median_filter ()
+void CLASS median_filter()
 {
   ushort (*pix)[4];
   int pass, c, i, j, k, med[9];
@@ -4346,7 +5064,7 @@
     if (pre_mul[kc] < pre_mul[c]) kc = c;
   high = height / SCALE;
   wide =  width / SCALE;
-  map = (float *) calloc (high*wide, sizeof *map);
+  map = (float *) calloc (high, wide*sizeof *map);
   merror (map, "recover_highlights()");
   FORCC if (c != kc) {
     memset (map, 0, high*wide*sizeof *map);
@@ -4413,7 +5131,7 @@
   *type = get2();
   *len  = get4();
   *save = ftell(ifp) + 4;
-  if (*len * ("11124811248488"[*type < 14 ? *type:0]-'0') > 4)
+  if (*len * ("11124811248484"[*type < 14 ? *type:0]-'0') > 4)
     fseek (ifp, get4()+base, SEEK_SET);
 }
 
@@ -4470,12 +5188,13 @@
   unsigned offset=0, entries, tag, type, len, save, c;
   unsigned ver97=0, serial=0, i, wbi=0, wb[4]={0,0,0,0};
   uchar buf97[324], ci, cj, ck;
-  short sorder=order;
+  short morder, sorder=order;
   char buf[10];
 /*
    The MakerNote might have its own TIFF header (possibly with
    its own byte-order!), or it might just be a table.
  */
+  if (!strcmp(make,"Nokia")) return;
   fread (buf, 1, 10, ifp);
   if (!strncmp (buf,"KDK" ,3) ||	/* these aren't TIFF tables */
       !strncmp (buf,"VER" ,3) ||
@@ -4499,14 +5218,18 @@
     if (get2() != 42) goto quit;
     offset = get4();
     fseek (ifp, offset-8, SEEK_CUR);
-  } else if (!strcmp (buf,"OLYMPUS")) {
+  } else if (!strcmp (buf,"OLYMPUS") ||
+	     !strcmp (buf,"PENTAX ")) {
     base = ftell(ifp)-10;
     fseek (ifp, -2, SEEK_CUR);
-    order = get2();  get2();
-  } else if (!strncmp (buf,"FUJIFILM",8) ||
-	     !strncmp (buf,"SONY",4) ||
+    order = get2();
+    if (buf[0] == 'O') get2();
+  } else if (!strncmp (buf,"SONY",4) ||
 	     !strcmp  (buf,"Panasonic")) {
-    order = 0x4949;
+    goto nf;
+  } else if (!strncmp (buf,"FUJIFILM",8)) {
+    base = ftell(ifp)-10;
+nf: order = 0x4949;
     fseek (ifp,  2, SEEK_CUR);
   } else if (!strcmp (buf,"OLYMP") ||
 	     !strcmp (buf,"LEICA") ||
@@ -4516,31 +5239,52 @@
   else if (!strcmp (buf,"AOC") ||
 	   !strcmp (buf,"QVC"))
     fseek (ifp, -4, SEEK_CUR);
-  else fseek (ifp, -10, SEEK_CUR);
-
+  else {
+    fseek (ifp, -10, SEEK_CUR);
+    if (!strncmp(make,"SAMSUNG",7))
+      base = ftell(ifp);
+  }
   entries = get2();
   if (entries > 1000) return;
+  morder = order;
   while (entries--) {
+    order = morder;
     tiff_get (base, &tag, &type, &len, &save);
     tag |= uptag << 16;
-    if (tag == 2 && strstr(make,"NIKON"))
+    if (tag == 2 && strstr(make,"NIKON") && !iso_speed)
       iso_speed = (get2(),get2());
     if (tag == 4 && len > 26 && len < 35) {
-      iso_speed = 50 * pow (2, (get4(),get2())/32.0 - 4);
-      if ((i=(get2(),get2())) != 0x7fff)
+      if ((i=(get4(),get2())) != 0x7fff && !iso_speed)
+	iso_speed = 50 * pow (2, i/32.0 - 4);
+      if ((i=(get2(),get2())) != 0x7fff && !aperture)
 	aperture = pow (2, i/64.0);
-      if ((i=get2()) != 0xffff)
+      if ((i=get2()) != 0xffff && !shutter)
 	shutter = pow (2, (short) i/-32.0);
       wbi = (get2(),get2());
       shot_order = (get2(),get2());
     }
+    if ((tag == 4 || tag == 0x114) && !strncmp(make,"KONICA",6)) {
+      fseek (ifp, tag == 4 ? 140:160, SEEK_CUR);
+      switch (get2()) {
+	case 72:  flip = 0;  break;
+	case 76:  flip = 6;  break;
+	case 82:  flip = 5;  break;
+      }
+    }
+    if (tag == 7 && type == 2 && len > 20)
+      fgets (model2, 64, ifp);
     if (tag == 8 && type == 4)
       shot_order = get4();
     if (tag == 9 && !strcmp(make,"Canon"))
       fread (artist, 64, 1, ifp);
-    if (tag == 0xc && len == 4) {
-      cam_mul[0] = getreal(type);
-      cam_mul[2] = getreal(type);
+    if (tag == 0xc && len == 4)
+      FORC3 cam_mul[(c << 1 | c >> 1) & 3] = getreal(type);
+    if (tag == 0xd && type == 7 && get2() == 0xaaaa) {
+      for (c=i=2; (ushort) c != 0xbbbb && i < len; i++)
+	c = c << 8 | fgetc(ifp);
+      while ((i+=4) < len-5)
+	if (get4() == 257 && (i=len) && (c = (get4(),fgetc(ifp))) < 3)
+	  flip = "065"[c]-'0';
     }
     if (tag == 0x10 && type == 4)
       unique_id = get4();
@@ -4548,9 +5292,18 @@
       fseek (ifp, get4()+base, SEEK_SET);
       parse_tiff_ifd (base);
     }
-    if (tag == 0x14 && len == 2560 && type == 7) {
-      fseek (ifp, 1248, SEEK_CUR);
-      goto get2_256;
+    if (tag == 0x14 && type == 7) {
+      if (len == 2560) {
+	fseek (ifp, 1248, SEEK_CUR);
+	goto get2_256;
+      }
+      fread (buf, 1, 10, ifp);
+      if (!strncmp(buf,"NRW ",4)) {
+	fseek (ifp, strcmp(buf+4,"0100") ? 46:1546, SEEK_CUR);
+	cam_mul[0] = get4() << 2;
+	cam_mul[1] = get4() + get4();
+	cam_mul[2] = get4() << 2;
+      }
     }
     if (tag == 0x15 && type == 2 && is_raw)
       fread (model, 64, 1, ifp);
@@ -4561,6 +5314,13 @@
     if (tag == 0x1d)
       while ((c = fgetc(ifp)) && c != EOF)
 	serial = serial*10 + (isdigit(c) ? c - '0' : c % 10);
+    if (tag == 0x29 && type == 1) {
+      c = wbi < 18 ? "012347800000005896"[wbi]-'0' : 0;
+      fseek (ifp, 8 + c*32, SEEK_CUR);
+      FORC4 cam_mul[c ^ (c >> 1) ^ 1] = get4();
+    }
+    if (tag == 0x3d && type == 3 && len == 4)
+      FORC4 cblack[c ^ c >> 1] = get2() >> (14-tiff_bps);
     if (tag == 0x81 && type == 4) {
       data_offset = get4();
       fseek (ifp, data_offset + 41, SEEK_SET);
@@ -4568,11 +5328,6 @@
       raw_width  = get2();
       filters = 0x61616161;
     }
-    if (tag == 0x29 && type == 1) {
-      c = wbi < 18 ? "012347800000005896"[wbi]-'0' : 0;
-      fseek (ifp, 8 + c*32, SEEK_CUR);
-      FORC4 cam_mul[c ^ (c >> 1) ^ 1] = get4();
-    }
     if ((tag == 0x81  && type == 7) ||
 	(tag == 0x100 && type == 7) ||
 	(tag == 0x280 && type == 1)) {
@@ -4587,51 +5342,55 @@
       meta_offset = ftell(ifp);
     if (tag == 0x97) {
       for (i=0; i < 4; i++)
-	ver97 = (ver97 << 4) + fgetc(ifp)-'0';
+	ver97 = ver97 * 10 + fgetc(ifp)-'0';
       switch (ver97) {
-	case 0x100:
+	case 100:
 	  fseek (ifp, 68, SEEK_CUR);
 	  FORC4 cam_mul[(c >> 1) | ((c & 1) << 1)] = get2();
 	  break;
-	case 0x102:
+	case 102:
 	  fseek (ifp, 6, SEEK_CUR);
-	  goto get2_rggb;
-	case 0x103:
+	  FORC4 cam_mul[c ^ (c >> 1)] = get2();
+	  break;
+	case 103:
 	  fseek (ifp, 16, SEEK_CUR);
 	  FORC4 cam_mul[c] = get2();
       }
-      if (ver97 >> 8 == 2) {
-	if (ver97 != 0x205) fseek (ifp, 280, SEEK_CUR);
+      if (ver97 >= 200) {
+	if (ver97 != 205) fseek (ifp, 280, SEEK_CUR);
 	fread (buf97, 324, 1, ifp);
       }
     }
+    if (tag == 0xa1 && type == 7) {
+      order = 0x4949;
+      fseek (ifp, 140, SEEK_CUR);
+      FORC3 cam_mul[c] = get4();
+    }
     if (tag == 0xa4 && type == 3) {
       fseek (ifp, wbi*48, SEEK_CUR);
       FORC3 cam_mul[c] = get2();
     }
-    if (tag == 0xa7 && ver97 >> 8 == 2) {
+    if (tag == 0xa7 && (unsigned) (ver97-200) < 17) {
       ci = xlat[0][serial & 0xff];
       cj = xlat[1][fgetc(ifp)^fgetc(ifp)^fgetc(ifp)^fgetc(ifp)];
       ck = 0x60;
       for (i=0; i < 324; i++)
 	buf97[i] ^= (cj += ci * ck++);
-      FORC4 cam_mul[c ^ (c >> 1)] =
-	sget2 (buf97 + (ver97 == 0x205 ? 14:6) + c*2);
-      if (ver97 == 0x209)
-	FORC4 cam_mul[c ^ (c >> 1) ^ 1] =
-	  sget2 (buf97 + 10 + c*2);
+      i = "66666>666;6A;:;55"[ver97-200] - '0';
+      FORC4 cam_mul[c ^ (c >> 1) ^ (i & 1)] =
+	sget2 (buf97 + (i & -2) + c*2);
     }
     if (tag == 0x200 && len == 3)
       shot_order = (get4(),get4());
     if (tag == 0x200 && len == 4)
-      black = (get2()+get2()+get2()+get2())/4;
+      FORC4 cblack[c ^ c >> 1] = get2();
     if (tag == 0x201 && len == 4)
-      goto get2_rggb;
-    if (tag == 0x401 && len == 4) {
-      black = (get4()+get4()+get4()+get4())/4;
-    }
+      FORC4 cam_mul[c ^ (c >> 1)] = get2();
+    if (tag == 0x220 && type == 7)
+      meta_offset = ftell(ifp);
+    if (tag == 0x401 && type == 4 && len == 4)
+      FORC4 cblack[c ^ c >> 1] = get4();
     if (tag == 0xe01) {		/* Nikon Capture Note */
-      type = order;
       order = 0x4949;
       fseek (ifp, 22, SEEK_CUR);
       for (offset=22; offset+22 < len; offset += 22+i) {
@@ -4641,7 +5400,6 @@
 	if (tag == 0x76a43207) flip = get2();
 	else fseek (ifp, i, SEEK_CUR);
       }
-      order = type;
     }
     if (tag == 0xe80 && len == 256 && type == 7) {
       fseek (ifp, 48, SEEK_CUR);
@@ -4660,8 +5418,7 @@
       for (i=0; i < 3; i++)
 	FORC3 cmatrix[i][c] = ((short) get2()) / 256.0;
     if ((tag == 0x1012 || tag == 0x20400600) && len == 4)
-      for (black = i=0; i < 4; i++)
-	black += get2() << 2;
+      FORC4 cblack[c ^ c >> 1] = get2();
     if (tag == 0x1017 || tag == 0x20400100)
       cam_mul[0] = get2() / 256.0;
     if (tag == 0x1018 || tag == 0x20400100)
@@ -4672,22 +5429,34 @@
       cam_mul[0] = get2() / 256.0;
       cam_mul[2] = get2() / 256.0;
     }
-    if (tag == 0x2020)
+    if ((tag | 0x70) == 0x2070 && (type == 4 || type == 13))
+      fseek (ifp, get4()+base, SEEK_SET);
+    if (tag == 0x2020 && !strncmp(buf,"OLYMP",5))
       parse_thumb_note (base, 257, 258);
     if (tag == 0x2040)
       parse_makernote (base, 0x2040);
     if (tag == 0xb028) {
-      fseek (ifp, get4(), SEEK_SET);
+      fseek (ifp, get4()+base, SEEK_SET);
       parse_thumb_note (base, 136, 137);
     }
-    if (tag == 0x4001 && type == 3) {
-      i = len == 582 ? 50 : len == 653 ? 68 : 126;
+    if (tag == 0x4001 && len > 500) {
+      i = len == 582 ? 50 : len == 653 ? 68 : len == 5120 ? 142 : 126;
       fseek (ifp, i, SEEK_CUR);
-get2_rggb:
       FORC4 cam_mul[c ^ (c >> 1)] = get2();
-      fseek (ifp, 22, SEEK_CUR);
-      FORC4 sraw_mul[c ^ (c >> 1)] = get2();
+      for (i+=18; i <= len; i+=10) {
+	get2();
+	FORC4 sraw_mul[c ^ (c >> 1)] = get2();
+	if (sraw_mul[1] == 1170) break;
+      }
     }
+    if (tag == 0x4021 && get4() && get4())
+      FORC4 cam_mul[c] = 1024;
+    if (tag == 0xa021)
+      FORC4 cam_mul[c ^ (c >> 1)] = get4();
+    if (tag == 0xa028)
+      FORC4 cam_mul[c ^ (c >> 1)] -= get4();
+    if (tag == 0xb001)
+      unique_id = get2();
 next:
     fseek (ifp, save, SEEK_SET);
   }
@@ -4716,6 +5485,7 @@
     return;
   t.tm_year -= 1900;
   t.tm_mon -= 1;
+  t.tm_isdst = -1;
   if (mktime(&t) > 0)
     timestamp = mktime(&t);
 }
@@ -4725,17 +5495,19 @@
   unsigned kodak, entries, tag, type, len, save, c;
   double expo;
 
-  kodak = !strncmp(make,"EASTMAN",7);
+  kodak = !strncmp(make,"EASTMAN",7) && tiff_nifds < 3;
   entries = get2();
   while (entries--) {
     tiff_get (base, &tag, &type, &len, &save);
     switch (tag) {
-      case 33434:  shutter = getreal(type);		break;
+      case 33434:  tiff_ifd[tiff_nifds-1].shutter =
+		   shutter = getreal(type);		break;
       case 33437:  aperture = getreal(type);		break;
       case 34855:  iso_speed = get2();			break;
       case 36867:
       case 36868:  get_timestamp(0);			break;
       case 37377:  if ((expo = -getreal(type)) < 128)
+		     tiff_ifd[tiff_nifds-1].shutter =
 		     shutter = pow (2, expo);		break;
       case 37378:  aperture = pow (2, getreal(type)/2);	break;
       case 37386:  focal_len = getreal(type);		break;
@@ -4793,7 +5565,9 @@
   static const char *mod[] =
   { "","DCB2","Volare","Cantare","CMost","Valeo 6","Valeo 11","Valeo 22",
     "Valeo 11p","Valeo 17","","Aptus 17","Aptus 22","Aptus 75","Aptus 65",
-    "Aptus 54S","Aptus 65S","Aptus 75S" };
+    "Aptus 54S","Aptus 65S","Aptus 75S","AFi 5","AFi 6","AFi 7",
+    "AFi-II 7","Aptus-II 7","","Aptus-II 6","","","Aptus-II 10","Aptus-II 5",
+    "","","","","Aptus-II 10R","Aptus-II 8","","Aptus-II 12","","AFi-II 12" };
   float romm_cam[3][3];
 
   fseek (ifp, offset, SEEK_SET);
@@ -4818,12 +5592,12 @@
     }
     if (!strcmp(data,"icc_camera_to_tone_matrix")) {
       for (i=0; i < 9; i++)
-	romm_cam[0][i] = int_to_float(get4());
+	((float *)romm_cam)[i] = int_to_float(get4());
       romm_coeff (romm_cam);
     }
     if (!strcmp(data,"CaptProf_color_matrix")) {
       for (i=0; i < 9; i++)
-	fscanf (ifp, "%f", &romm_cam[0][i]);
+	fscanf (ifp, "%f", (float *)romm_cam + i);
       romm_coeff (romm_cam);
     }
     if (!strcmp(data,"CaptProf_number_of_planes"))
@@ -4843,6 +5617,8 @@
       FORC4 fscanf (ifp, "%d", neut+c);
       FORC3 cam_mul[c] = (float) neut[0] / neut[c+1];
     }
+    if (!strcmp(data,"Rows_data"))
+      load_flags = get4();
     parse_mos (from);
     fseek (ifp, skip+from, SEEK_SET);
   }
@@ -4865,7 +5641,8 @@
 {
   unsigned entries, tag, type, len, save;
   int i, c, wbi=-2, wbtemp=6500;
-  float mul[3], num;
+  float mul[3]={1,1,1}, num;
+  static const int wbtag[] = { 64037,64040,64039,64041,-1,-1,64042 };
 
   entries = get2();
   if (entries > 1024) return;
@@ -4878,6 +5655,8 @@
       wbi = -2;
     }
     if (tag == 2118) wbtemp = getint(type);
+    if (tag == 2120 + wbi && wbi >= 0)
+      FORC3 cam_mul[c] = 2048.0 / getreal(type);
     if (tag == 2130 + wbi)
       FORC3 mul[c] = getreal(type);
     if (tag == 2140 + wbi && wbi >= 0)
@@ -4888,11 +5667,17 @@
       }
     if (tag == 2317) linear_table (len);
     if (tag == 6020) iso_speed = getint(type);
+    if (tag == 64013) wbi = fgetc(ifp);
+    if ((unsigned) wbi < 7 && tag == wbtag[wbi])
+      FORC3 cam_mul[c] = get4();
+    if (tag == 64019) width = getint(type);
+    if (tag == 64020) height = (getint(type)+1) & -2;
     fseek (ifp, save, SEEK_SET);
   }
 }
 
 void CLASS parse_minolta (int base);
+int CLASS parse_tiff (int base);
 
 int CLASS parse_tiff_ifd (int base)
 {
@@ -4900,7 +5685,7 @@
   int ifd, use_cm=0, cfa, i, j, c, ima_len=0;
   char software[64], *cbuf, *cp;
   uchar cfa_pat[16], cfa_pc[] = { 0,1,2,3 }, tab[256];
-  double dblack, cc[4][4], cm[4][3], cam_xyz[4][3], num;
+  double cc[4][4], cm[4][3], cam_xyz[4][3], num;
   double ab[]={ 1,1,1,1 }, asn[] = { 0,0,0,0 }, xyz[] = { 1,1,1 };
   unsigned sony_curve[] = { 0,0,0,0,0,4095 };
   unsigned *buf, sony_offset=0, sony_length=0, sony_key=0;
@@ -4918,6 +5703,10 @@
   while (entries--) {
     tiff_get (base, &tag, &type, &len, &save);
     switch (tag) {
+      case 5:   width  = get2();  break;
+      case 6:   height = get2();  break;
+      case 7:   width += get2();  break;
+      case 9:   if ((i = get2())) filters = i;  break;
       case 17: case 18:
 	if (type == 3 && len == 1)
 	  cam_mul[(tag-17)*2] = get2() / 256.0;
@@ -4925,8 +5714,12 @@
       case 23:
 	if (type == 3) iso_speed = get2();
 	break;
+      case 28: case 29: case 30:
+	cblack[tag-28] = get2();
+	cblack[3] = cblack[1];
+	break;
       case 36: case 37: case 38:
-	cam_mul[tag-0x24] = get2();
+	cam_mul[tag-36] = get2();
 	break;
       case 39:
 	if (len < 50 || cam_mul[0]) break;
@@ -4938,18 +5731,30 @@
 	thumb_offset = ftell(ifp) - 2;
 	thumb_length = len;
 	break;
-      case 2: case 256:			/* ImageWidth */
+      case 61440:			/* Fuji HS10 table */
+	fseek (ifp, get4()+base, SEEK_SET);
+	parse_tiff_ifd (base);
+	break;
+      case 2: case 256: case 61441:	/* ImageWidth */
 	tiff_ifd[ifd].width = getint(type);
 	break;
-      case 3: case 257:			/* ImageHeight */
+      case 3: case 257: case 61442:	/* ImageHeight */
 	tiff_ifd[ifd].height = getint(type);
 	break;
       case 258:				/* BitsPerSample */
+      case 61443:
 	tiff_ifd[ifd].samples = len & 7;
-	tiff_ifd[ifd].bps = get2();
+	if ((tiff_ifd[ifd].bps = getint(type)) > 32)
+	     tiff_ifd[ifd].bps = 8;
+	if (tiff_bps < tiff_ifd[ifd].bps)
+	    tiff_bps = tiff_ifd[ifd].bps;
+	break;
+      case 61446:
+	raw_height = 0;
+	load_flags = get4() ? 24:80;
 	break;
       case 259:				/* Compression */
-	tiff_ifd[ifd].comp = get2();
+	tiff_ifd[ifd].comp = getint(type);
 	break;
       case 262:				/* PhotometricInterpretation */
 	tiff_ifd[ifd].phint = get2();
@@ -4963,21 +5768,35 @@
       case 272:				/* Model */
 	fgets (model, 64, ifp);
 	break;
+      case 280:				/* Panasonic RW2 offset */
+	if (type != 4) break;
+	load_raw = &CLASS panasonic_load_raw;
+	load_flags = 0x2008;
       case 273:				/* StripOffset */
-      case 513:
+      case 513:				/* JpegIFOffset */
+      case 61447:
 	tiff_ifd[ifd].offset = get4()+base;
-	if (!tiff_ifd[ifd].bps) {
+	if (!tiff_ifd[ifd].bps && tiff_ifd[ifd].offset > 0) {
 	  fseek (ifp, tiff_ifd[ifd].offset, SEEK_SET);
 	  if (ljpeg_start (&jh, 1)) {
 	    tiff_ifd[ifd].comp    = 6;
-	    tiff_ifd[ifd].width   = jh.wide << (jh.clrs == 2);
+	    tiff_ifd[ifd].width   = jh.wide;
 	    tiff_ifd[ifd].height  = jh.high;
 	    tiff_ifd[ifd].bps     = jh.bits;
 	    tiff_ifd[ifd].samples = jh.clrs;
+	    if (!(jh.sraw || (jh.clrs & 1)))
+	      tiff_ifd[ifd].width *= jh.clrs;
+	    if ((tiff_ifd[ifd].width > 4*tiff_ifd[ifd].height) & ~jh.clrs) {
+	      tiff_ifd[ifd].width  /= 2;
+	      tiff_ifd[ifd].height *= 2;
+	    }
+	    i = order;
+	    parse_tiff (tiff_ifd[ifd].offset + 12);
+	    order = i;
 	  }
 	}
 	break;
-      case 274:				/* Qt::Orientation */
+      case 274:				/* Orientation */
 	tiff_ifd[ifd].flip = "50132467"[get2() & 7]-'0';
 	break;
       case 277:				/* SamplesPerPixel */
@@ -4985,12 +5804,17 @@
 	break;
       case 279:				/* StripByteCounts */
       case 514:
+      case 61448:
 	tiff_ifd[ifd].bytes = get4();
 	break;
-      case 305:				/* Software */
+      case 61454:
+	FORC3 cam_mul[(4-c) % 3] = getint(type);
+	break;
+      case 305:  case 11:		/* Software */
 	fgets (software, 64, ifp);
 	if (!strncmp(software,"Adobe",5) ||
 	    !strncmp(software,"dcraw",5) ||
+	    !strncmp(software,"UFRaw",5) ||
 	    !strncmp(software,"Bibble",6) ||
 	    !strncmp(software,"Nikon Scan",10) ||
 	    !strcmp (software,"Digital Photo Professional"))
@@ -5003,13 +5827,15 @@
 	fread (artist, 64, 1, ifp);
 	break;
       case 322:				/* TileWidth */
-	tile_width = getint(type);
+	tiff_ifd[ifd].tile_width = getint(type);
 	break;
       case 323:				/* TileLength */
-	tile_length = getint(type);
+	tiff_ifd[ifd].tile_length = getint(type);
 	break;
       case 324:				/* TileOffsets */
 	tiff_ifd[ifd].offset = len > 1 ? ftell(ifp) : get4();
+	if (len == 1)
+	  tiff_ifd[ifd].tile_width = tiff_ifd[ifd].tile_length = 0;
 	if (len == 4) {
 	  load_raw = &CLASS sinar_4shot_load_raw;
 	  is_raw = 5;
@@ -5017,6 +5843,7 @@
 	break;
       case 330:				/* SubIFDs */
 	if (!strcmp(model,"DSLR-A100") && tiff_ifd[ifd].width == 3872) {
+	  load_raw = &CLASS sony_arw_load_raw;
 	  data_offset = get4()+base;
 	  ifd++;  break;
 	}
@@ -5047,14 +5874,27 @@
       case 29443:
 	FORC4 cam_mul[c ^ (c < 2)] = get2();
 	break;
+      case 29459:
+	FORC4 cam_mul[c] = get2();
+	i = (cam_mul[1] == 1024 && cam_mul[2] == 1024) << 1;
+	SWAP (cam_mul[i],cam_mul[i+1])
+	break;
       case 33405:			/* Model2 */
 	fgets (model2, 64, ifp);
 	break;
+      case 33421:			/* CFARepeatPatternDim */
+	if (get2() == 6 && get2() == 6)
+	  filters = 9;
+	break;
       case 33422:			/* CFAPattern */
+	if (filters == 9) {
+	  FORC(36) ((char *)xtrans)[c] = fgetc(ifp) & 3;
+	  break;
+	}
       case 64777:			/* Kodak P-series */
 	if ((plen=len) > 16) plen = 16;
 	fread (cfa_pat, 1, plen, ifp);
-	for (colors=cfa=i=0; i < plen; i++) {
+	for (colors=cfa=i=0; i < plen && colors < 4; i++) {
 	  colors += !(cfa & (1 << cfa_pat[i]));
 	  cfa |= 1 << cfa_pat[i];
 	}
@@ -5062,11 +5902,12 @@
 	if (cfa == 072) memcpy (cfa_pc,"\005\003\004\001",4);	/* GMCY */
 	goto guess_cfa_pc;
       case 33424:
+      case 65024:
 	fseek (ifp, get4()+base, SEEK_SET);
 	parse_kodak_ifd (base);
 	break;
       case 33434:			/* ExposureTime */
-	shutter = getreal(type);
+	tiff_ifd[ifd].shutter = shutter = getreal(type);
 	break;
       case 33437:			/* FNumber */
 	aperture = getreal(type);
@@ -5119,13 +5960,24 @@
 	  FORC3 rgb_cam[i][c] = getreal(type);
 	}
 	break;
+      case 40976:
+	strip_offset = get4();
+	switch (tiff_ifd[ifd].comp) {
+	  case 32770: load_raw = &CLASS samsung_load_raw;   break;
+	  case 32772: load_raw = &CLASS samsung2_load_raw;  break;
+	  case 32773: load_raw = &CLASS samsung3_load_raw;  break;
+	}
+	break;
       case 46275:			/* Imacon tags */
 	strcpy (make, "Imacon");
 	data_offset = ftell(ifp);
 	ima_len = len;
 	break;
       case 46279:
-	fseek (ifp, 78, SEEK_CUR);
+	if (!ima_len) break;
+	fseek (ifp, 38, SEEK_CUR);
+      case 46274:
+	fseek (ifp, 40, SEEK_CUR);
 	raw_width  = get4();
 	raw_height = get4();
 	left_margin = get4() & 7;
@@ -5143,7 +5995,9 @@
 	flip = (get2() >> 7) * 90;
 	if (width * height * 6 == ima_len) {
 	  if (flip % 180 == 90) SWAP(width,height);
-	  filters = flip = 0;
+	  raw_width = width;
+	  raw_height = height;
+	  left_margin = top_margin = filters = flip = 0;
 	}
 	sprintf (model, "Ixpress %d-Mp", height*width/1000000);
 	load_raw = &CLASS imacon_full_load_raw;
@@ -5162,6 +6016,9 @@
 	    sscanf (cp+8, "%f %f %f", cam_mul, cam_mul+1, cam_mul+2);
 	free (cbuf);
 	break;
+      case 50458:
+	if (!make[0]) strcpy (make, "Hasselblad");
+	break;
       case 50459:			/* Hasselblad tag */
 	i = order;
 	j = ftell(ifp);
@@ -5175,8 +6032,19 @@
 	break;
       case 50706:			/* DNGVersion */
 	FORC4 dng_version = (dng_version << 8) + fgetc(ifp);
+	if (!make[0]) strcpy (make, "DNG");
+	is_raw = 1;
+	break;
+      case 50708:			/* UniqueCameraModel */
+	if (model[0]) break;
+	fgets (make, 64, ifp);
+	if ((cp = strchr(make,' '))) {
+	  strcpy(model,cp+1);
+	  *cp = 0;
+	}
 	break;
       case 50710:			/* CFAPlaneColor */
+	if (filters == 9) break;
 	if (len > 4) len = 4;
 	colors = len;
 	fread (cfa_pc, 1, colors, ifp);
@@ -5185,23 +6053,35 @@
 	cdesc[c] = 0;
 	for (i=16; i--; )
 	  filters = filters << 2 | tab[cfa_pat[i % plen]];
+	filters -= !filters;
 	break;
       case 50711:			/* CFALayout */
-	if (get2() == 2) {
-	  fuji_width = 1;
-	  filters = 0x49494949;
-	}
+	if (get2() == 2) fuji_width = 1;
 	break;
       case 291:
       case 50712:			/* LinearizationTable */
 	linear_table (len);
 	break;
+      case 50713:			/* BlackLevelRepeatDim */
+	cblack[4] = get2();
+	cblack[5] = get2();
+	if (cblack[4] * cblack[5] > sizeof cblack / sizeof *cblack - 6)
+	    cblack[4] = cblack[5] = 1;
+	break;
+      case 61450:
+	cblack[4] = cblack[5] = MIN(sqrt(len),64);
       case 50714:			/* BlackLevel */
+	if (!(cblack[4] * cblack[5]))
+	  cblack[4] = cblack[5] = 1;
+	FORC (cblack[4] * cblack[5])
+	  cblack[6+c] = getreal(type);
+	black = 0;
+	break;
       case 50715:			/* BlackLevelDeltaH */
       case 50716:			/* BlackLevelDeltaV */
-	for (dblack=i=0; i < len; i++)
-	  dblack += getreal(type);
-	black += dblack/len + 0.5;
+	for (num=i=0; i < (len & 0xffff); i++)
+	  num += getreal(type);
+	black += num/len + 0.5;
 	break;
       case 50717:			/* WhiteLevel */
 	maximum = getint(type);
@@ -5220,6 +6100,7 @@
       case 50724:			/* CameraCalibration2 */
 	for (i=0; i < colors; i++)
 	  FORCC cc[i][c] = getreal(type);
+	break;
       case 50727:			/* AnalogBalance */
 	FORCC ab[c] = getreal(type);
 	break;
@@ -5247,12 +6128,24 @@
 	height = getint(type) - top_margin;
 	width = getint(type) - left_margin;
 	break;
+      case 50830:			/* MaskedAreas */
+	for (i=0; i < len && i < 32; i++)
+	  ((int *)mask)[i] = getint(type);
+	black = 0;
+	break;
+      case 51009:			/* OpcodeList2 */
+	meta_offset = ftell(ifp);
+	break;
       case 64772:			/* Kodak P-series */
+	if (len < 13) break;
 	fseek (ifp, 16, SEEK_CUR);
 	data_offset = get4();
 	fseek (ifp, 28, SEEK_CUR);
 	data_offset += get4();
-	load_raw = &CLASS packed_12_load_raw;
+	load_raw = &CLASS packed_load_raw;
+	break;
+      case 65026:
+	if (type == 2) fgets (model2, 64, ifp);
     }
     fseek (ifp, save, SEEK_SET);
   }
@@ -5276,7 +6169,7 @@
     FORCC for (i=0; i < 3; i++)
       for (cam_xyz[c][i]=j=0; j < colors; j++)
 	cam_xyz[c][i] += cc[c][j] * cm[j][i] * xyz[i];
-    cam_xyz_coeff (cam_xyz);
+    cam_xyz_coeff (cmatrix, cam_xyz);
   }
   if (asn[0]) {
     cam_mul[3] = 0;
@@ -5287,21 +6180,26 @@
   return 0;
 }
 
-void CLASS parse_tiff (int base)
+int CLASS parse_tiff (int base)
 {
-  int doff, max_samp=0, raw=-1, thm=-1, i;
-  struct jhead jh;
+  int doff;
 
   fseek (ifp, base, SEEK_SET);
   order = get2();
-  if (order != 0x4949 && order != 0x4d4d) return;
+  if (order != 0x4949 && order != 0x4d4d) return 0;
   get2();
-  memset (tiff_ifd, 0, sizeof tiff_ifd);
-  tiff_nifds = 0;
   while ((doff = get4())) {
     fseek (ifp, doff+base, SEEK_SET);
     if (parse_tiff_ifd (base)) break;
   }
+  return 1;
+}
+
+void CLASS apply_tiff()
+{
+  int max_samp=0, ties=0, os, ns, raw=-1, thm=-1, i;
+  struct jhead jh;
+
   thumb_misc = 16;
   if (thumb_offset) {
     fseek (ifp, thumb_offset, SEEK_SET);
@@ -5311,12 +6209,25 @@
       thumb_height = jh.high;
     }
   }
+  for (i=tiff_nifds; i--; ) {
+    if (tiff_ifd[i].shutter)
+      shutter = tiff_ifd[i].shutter;
+    tiff_ifd[i].shutter = shutter;
+  }
   for (i=0; i < tiff_nifds; i++) {
     if (max_samp < tiff_ifd[i].samples)
 	max_samp = tiff_ifd[i].samples;
     if (max_samp > 3) max_samp = 3;
+    os = raw_width*raw_height;
+    ns = tiff_ifd[i].width*tiff_ifd[i].height;
+    if (tiff_bps) {
+      os *= tiff_bps;
+      ns *= tiff_ifd[i].bps;
+    }
     if ((tiff_ifd[i].comp != 6 || tiff_ifd[i].samples != 3) &&
-	tiff_ifd[i].width*tiff_ifd[i].height > raw_width*raw_height) {
+	(tiff_ifd[i].width | tiff_ifd[i].height) < 0x10000 &&
+	 ns && ((ns > os && (ties = 1)) ||
+		(ns == os && shot_select == ties++))) {
       raw_width     = tiff_ifd[i].width;
       raw_height    = tiff_ifd[i].height;
       tiff_bps      = tiff_ifd[i].bps;
@@ -5324,60 +6235,105 @@
       data_offset   = tiff_ifd[i].offset;
       tiff_flip     = tiff_ifd[i].flip;
       tiff_samples  = tiff_ifd[i].samples;
+      tile_width    = tiff_ifd[i].tile_width;
+      tile_length   = tiff_ifd[i].tile_length;
+      shutter       = tiff_ifd[i].shutter;
       raw = i;
     }
   }
-  fuji_width *= (raw_width+1)/2;
-  if (tiff_ifd[0].flip) tiff_flip = tiff_ifd[0].flip;
+  if (is_raw == 1 && ties) is_raw = ties;
+  if (!tile_width ) tile_width  = INT_MAX;
+  if (!tile_length) tile_length = INT_MAX;
+  for (i=tiff_nifds; i--; )
+    if (tiff_ifd[i].flip) tiff_flip = tiff_ifd[i].flip;
   if (raw >= 0 && !load_raw)
     switch (tiff_compress) {
+      case 32767:
+	if (tiff_ifd[raw].bytes == raw_width*raw_height) {
+	  tiff_bps = 12;
+	  maximum = 4095;
+	  load_raw = &CLASS sony_arw2_load_raw;			break;
+	}
+	if (tiff_ifd[raw].bytes*8 != raw_width*raw_height*tiff_bps) {
+	  raw_height += 8;
+	  load_raw = &CLASS sony_arw_load_raw;			break;
+	}
+	load_flags = 79;
+      case 32769:
+	load_flags++;
+      case 32770:
+      case 32773: goto slr;
       case 0:  case 1:
+	if (!strncmp(make,"OLYMPUS",7) &&
+		tiff_ifd[raw].bytes*2 == raw_width*raw_height*3)
+	  load_flags = 24;
+	if (!strcmp(make,"SONY") && tiff_bps < 14 &&
+		tiff_ifd[raw].bytes == raw_width*raw_height*2)
+	    tiff_bps = 14;
+	if (tiff_ifd[raw].bytes*5 == raw_width*raw_height*8) {
+	  load_flags = 81;
+	  tiff_bps = 12;
+	} slr:
 	switch (tiff_bps) {
 	  case  8: load_raw = &CLASS eight_bit_load_raw;	break;
-	  case 12: load_raw = &CLASS packed_12_load_raw;
-		   if (!strncmp(make,"NIKON",5))
-		     load_raw = &CLASS nikon_load_raw;
-		   if (strncmp(make,"PENTAX",6)) break;
-	  case 14:
-	  case 16: load_raw = &CLASS unpacked_load_raw;		break;
-	}
-	if (tiff_ifd[raw].bytes * 5 == raw_width * raw_height * 8)
-	  load_raw = &CLASS olympus_e300_load_raw;
-	if (tiff_bps == 12 && tiff_ifd[raw].phint == 2)
-	  load_raw = &CLASS olympus_cseries_load_raw;
+	  case 12: if (tiff_ifd[raw].phint == 2)
+		     load_flags = 6;
+		   load_raw = &CLASS packed_load_raw;		break;
+	  case 14: load_raw = &CLASS packed_load_raw;
+		   if (tiff_ifd[raw].bytes*4 == raw_width*raw_height*7) break;
+		   load_flags = 0;
+	  case 16: load_raw = &CLASS unpacked_load_raw;
+		   if (!strncmp(make,"OLYMPUS",7) &&
+			tiff_ifd[raw].bytes*7 > raw_width*raw_height)
+		     load_raw = &CLASS olympus_load_raw;
+	}
+	if (filters == 9 && tiff_ifd[raw].bytes*8 < raw_width*raw_height*tiff_bps)
+	  load_raw = &CLASS fuji_xtrans_load_raw;
 	break;
       case 6:  case 7:  case 99:
 	load_raw = &CLASS lossless_jpeg_load_raw;		break;
       case 262:
 	load_raw = &CLASS kodak_262_load_raw;			break;
-      case 32767:
-	load_raw = &CLASS sony_arw2_load_raw;			break;
-      case 32769:
-	load_raw = &CLASS nikon_load_raw;			break;
-      case 32773:
-	load_raw = &CLASS packed_12_load_raw;			break;
       case 34713:
-	load_raw = &CLASS nikon_compressed_load_raw;		break;
+	if ((raw_width+9)/10*16*raw_height == tiff_ifd[raw].bytes) {
+	  load_raw = &CLASS packed_load_raw;
+	  load_flags = 1;
+	} else if (raw_width*raw_height*3 == tiff_ifd[raw].bytes*2) {
+	  load_raw = &CLASS packed_load_raw;
+	  if (model[0] == 'N') load_flags = 80;
+	} else if (raw_width*raw_height*3 == tiff_ifd[raw].bytes) {
+	  load_raw = &CLASS nikon_yuv_load_raw;
+	  gamma_curve (1/2.4, 12.92, 1, 4095);
+	  memset (cblack, 0, sizeof cblack);
+	  filters = 0;
+	} else if (raw_width*raw_height*2 == tiff_ifd[raw].bytes) {
+	  load_raw = &CLASS unpacked_load_raw;
+	  load_flags = 4;
+	  order = 0x4d4d;
+	} else
+	  load_raw = &CLASS nikon_load_raw;			break;
       case 65535:
-	load_raw = &CLASS pentax_k10_load_raw;			break;
+	load_raw = &CLASS pentax_load_raw;			break;
       case 65000:
 	switch (tiff_ifd[raw].phint) {
 	  case 2: load_raw = &CLASS kodak_rgb_load_raw;   filters = 0;  break;
 	  case 6: load_raw = &CLASS kodak_ycbcr_load_raw; filters = 0;  break;
 	  case 32803: load_raw = &CLASS kodak_65000_load_raw;
 	}
-      case 32867: break;
+      case 32867: case 34892: break;
       default: is_raw = 0;
     }
-  if (!dng_version && tiff_samples == 3)
-    if (tiff_ifd[raw].bytes && tiff_bps != 14 && tiff_bps != 2048)
+  if (!dng_version)
+    if ( (tiff_samples == 3 && tiff_ifd[raw].bytes && tiff_bps != 14 &&
+	  (tiff_compress & -16) != 32768)
+      || (tiff_bps == 8 && strncmp(make,"Phase",5) &&
+	  !strcasestr(make,"Kodak") && !strstr(model2,"DEBUG RAW")))
       is_raw = 0;
-  if (!dng_version && tiff_bps == 8 && tiff_compress == 1 &&
-	tiff_ifd[raw].phint == 1) is_raw = 0;
   for (i=0; i < tiff_nifds; i++)
     if (i != raw && tiff_ifd[i].samples == max_samp &&
-	tiff_ifd[i].width * tiff_ifd[i].height / SQR(tiff_ifd[i].bps+1) >
-	      thumb_width *       thumb_height / SQR(thumb_misc+1)) {
+	tiff_ifd[i].width * tiff_ifd[i].height / (SQR(tiff_ifd[i].bps)+1) >
+	      thumb_width *       thumb_height / (SQR(thumb_misc)+1)
+	&& tiff_ifd[i].comp != 34892) {
       thumb_width  = tiff_ifd[i].width;
       thumb_height = tiff_ifd[i].height;
       thumb_offset = tiff_ifd[i].offset;
@@ -5392,10 +6348,12 @@
 	write_thumb = &CLASS layer_thumb;
 	break;
       case 1:
-	if (tiff_ifd[thm].bps > 8)
-	  thumb_load_raw = &CLASS kodak_thumb_load_raw;
-	else
+	if (tiff_ifd[thm].bps <= 8)
 	  write_thumb = &CLASS ppm_thumb;
+	else if (!strcmp(make,"Imacon"))
+	  write_thumb = &CLASS ppm16_thumb;
+	else
+	  thumb_load_raw = &CLASS kodak_thumb_load_raw;
 	break;
       case 65000:
 	thumb_load_raw = tiff_ifd[thm].phint == 6 ?
@@ -5425,7 +6383,7 @@
 	break;
       case 0x574247:				/* WBG */
 	get4();
-	i = strstr(model,"A200") ? 3:0;
+	i = strcmp(model,"DiMAGE A200") ? 0:3;
 	FORC4 cam_mul[c ^ (c >> 1) ^ i] = get2();
 	break;
       case 0x545457:				/* TTW */
@@ -5446,7 +6404,8 @@
  */
 void CLASS parse_external_jpeg()
 {
-  char *file, *ext, *jname, *jfile, *jext;
+  const char *file, *ext;
+  char *jname, *jfile, *jext;
   FILE *save=ifp;
 
   ext  = strrchr (ifname, '.');
@@ -5456,18 +6415,20 @@
   file++;
   if (!ext || strlen(ext) != 4 || ext-file != 8) return;
   jname = (char *) malloc (strlen(ifname) + 1);
-  merror (jname, "parse_external()");
+  merror (jname, "parse_external_jpeg()");
   strcpy (jname, ifname);
   jfile = file - ifname + jname;
   jext  = ext  - ifname + jname;
   if (strcasecmp (ext, ".jpg")) {
     strcpy (jext, isupper(ext[1]) ? ".JPG":".jpg");
-    memcpy (jfile, file+4, 4);
-    memcpy (jfile+4, file, 4);
+    if (isdigit(*file)) {
+      memcpy (jfile, file+4, 4);
+      memcpy (jfile+4, file, 4);
+    }
   } else
     while (isdigit(*--jext)) {
       if (*jext != '9') {
-        (*jext)++;
+	(*jext)++;
 	break;
       }
       *jext = '0';
@@ -5507,16 +6468,14 @@
 	bitbuf = bitbuf << 16 | (get2() ^ key[i++ & 1]);
 	vbits += 16;
       }
-      white[row][col] =
-	bitbuf << (LONG_BIT - vbits) >> (LONG_BIT - bpp);
-      vbits -= bpp;
+      white[row][col] = bitbuf >> (vbits -= bpp) & ~(-1 << bpp);
     }
 }
 
 /*
    Parse a CIFF file, better known as Canon CRW format.
  */
-void CLASS parse_ciff (int offset, int length)
+void CLASS parse_ciff (int offset, int length, int depth)
 {
   int tboff, nrecs, c, type, len, save, wbi=-1;
   ushort key[] = { 0x410, 0x45f3 };
@@ -5525,15 +6484,14 @@
   tboff = get4() + offset;
   fseek (ifp, tboff, SEEK_SET);
   nrecs = get2();
-  if (nrecs > 100) return;
+  if ((nrecs | depth) > 127) return;
   while (nrecs--) {
     type = get2();
     len  = get4();
     save = ftell(ifp) + 4;
     fseek (ifp, offset+get4(), SEEK_SET);
     if ((((type >> 8) + 8) | 8) == 0x38)
-      parse_ciff (ftell(ifp), len);	/* Parse a sub-table */
-
+      parse_ciff (ftell(ifp), len, depth+1); /* Parse a sub-table */
     if (type == 0x0810)
       fread (artist, 64, 1, ifp);
     if (type == 0x080a) {
@@ -5542,7 +6500,9 @@
       fread (model, 64, 1, ifp);
     }
     if (type == 0x1810) {
-      fseek (ifp, 12, SEEK_CUR);
+      width = get4();
+      height = get4();
+      pixel_aspect = int_to_float(get4());
       flip = get4();
     }
     if (type == 0x1835)			/* Get the decoder table */
@@ -5700,7 +6660,7 @@
   fseek (ifp, base, SEEK_SET);
   order = get4() & 0xffff;
   if (get4() >> 8 != 0x526177) return;		/* "Raw" */
-  fseek (ifp, base+get4(), SEEK_SET);
+  fseek (ifp, get4()+base, SEEK_SET);
   entries = get4();
   get4();
   while (entries--) {
@@ -5714,7 +6674,7 @@
       case 0x100:  flip = "0653"[data & 3]-'0';  break;
       case 0x106:
 	for (i=0; i < 9; i++)
-	  romm_cam[0][i] = getreal(11);
+	  ((float *)romm_cam)[i] = getreal(11);
 	romm_coeff (romm_cam);
 	break;
       case 0x107:
@@ -5735,8 +6695,10 @@
       case 0x21a:  ph1.tag_21a   = data;		break;
       case 0x21c:  strip_offset  = data+base;		break;
       case 0x21d:  ph1.black     = data;		break;
-      case 0x222:  ph1.split_col = data - left_margin;	break;
-      case 0x223:  ph1.black_off = data+base;		break;
+      case 0x222:  ph1.split_col = data;		break;
+      case 0x223:  ph1.black_col = data+base;		break;
+      case 0x224:  ph1.split_row = data;		break;
+      case 0x225:  ph1.black_row = data+base;		break;
       case 0x301:
 	model[63] = 0;
 	fread (model, 1, 63, ifp);
@@ -5774,10 +6736,22 @@
     } else if (tag == 0x121) {
       height = get2();
       if ((width = get2()) == 4284) width += 3;
-    } else if (tag == 0x130)
+    } else if (tag == 0x130) {
       fuji_layout = fgetc(ifp) >> 7;
-    if (tag == 0x2ff0)
+      fuji_width = !(fgetc(ifp) & 8);
+    } else if (tag == 0x131) {
+      filters = 9;
+      FORC(36) xtrans_abs[0][35-c] = fgetc(ifp) & 3;
+    } else if (tag == 0x2ff0) {
       FORC4 cam_mul[c ^ 1] = get2();
+    } else if (tag == 0xc000 && len > 20000) {
+      c = order;
+      order = 0x4949;
+      while ((tag = get4()) > raw_width);
+      width = tag;
+      height = get4();
+      order = c;
+    }
     fseek (ifp, save+len, SEEK_SET);
   }
   height <<= fuji_layout;
@@ -5795,7 +6769,7 @@
     order = 0x4d4d;
     len   = get2() - 2;
     save  = ftell(ifp);
-    if (mark == 0xc0 || mark == 0xc3) {
+    if (mark == 0xc0 || mark == 0xc3 || mark == 0xc9) {
       fgetc(ifp);
       raw_height = get2();
       raw_width  = get2();
@@ -5803,8 +6777,8 @@
     order = get2();
     hlen  = get4();
     if (get4() == 0x48454150)		/* "HEAP" */
-      parse_ciff (save+hlen, len-hlen);
-    parse_tiff (save+6);
+      parse_ciff (save+hlen, len-hlen, 0);
+    if (parse_tiff (save+6)) apply_tiff();
     fseek (ifp, save+len, SEEK_SET);
   }
   return 1;
@@ -5821,18 +6795,26 @@
   order = 0x4949;
   fread (tag, 4, 1, ifp);
   size = get4();
+  end = ftell(ifp) + size;
   if (!memcmp(tag,"RIFF",4) || !memcmp(tag,"LIST",4)) {
-    end = ftell(ifp) + size;
     get4();
-    while (ftell(ifp) < end)
+    while (ftell(ifp)+7 < end && !feof(ifp))
       parse_riff();
+  } else if (!memcmp(tag,"nctg",4)) {
+    while (ftell(ifp)+7 < end) {
+      i = get2();
+      size = get2();
+      if ((i+1) >> 1 == 10 && size == 20)
+	get_timestamp(0);
+      else fseek (ifp, size, SEEK_CUR);
+    }
   } else if (!memcmp(tag,"IDIT",4) && size < 64) {
     fread (date, 64, 1, ifp);
     date[size] = 0;
     memset (&t, 0, sizeof t);
     if (sscanf (date, "%*s %s %d %d:%d:%d %d", month, &t.tm_mday,
 	&t.tm_hour, &t.tm_min, &t.tm_sec, &t.tm_year) == 6) {
-      for (i=0; i < 12 && strcmp(mon[i],month); i++);
+      for (i=0; i < 12 && strcasecmp(mon[i],month); i++);
       t.tm_mon = i;
       t.tm_year -= 1900;
       if (mktime(&t) > 0)
@@ -5842,6 +6824,92 @@
     fseek (ifp, size, SEEK_CUR);
 }
 
+void CLASS parse_crx (int end)
+{
+  unsigned i, save, size, tag, base;
+  static int index=0, wide, high, off, len;
+
+  order = 0x4d4d;
+  while (ftell(ifp)+7 < end) {
+    save = ftell(ifp);
+    if ((size = get4()) < 8) break;
+    switch (tag = get4()) {
+      case 0x6d6f6f76:				/* moov */
+      case 0x7472616b:				/* trak */
+      case 0x6d646961:				/* mdia */
+      case 0x6d696e66:				/* minf */
+      case 0x7374626c:				/* stbl */
+	parse_crx (save+size);
+	break;
+      case 0x75756964:				/* uuid */
+	switch (i=get4()) {
+	  case 0xeaf42b5e: fseek (ifp,  8, SEEK_CUR);
+	  case 0x85c0b687: fseek (ifp, 12, SEEK_CUR);
+	    parse_crx (save+size);
+	}
+	break;
+      case 0x434d5431:				/* CMT1 */
+      case 0x434d5432:				/* CMT2 */
+	base = ftell(ifp);
+	order = get2();
+	fseek (ifp, 6, SEEK_CUR);
+	tag & 1 ? parse_tiff_ifd (base) : parse_exif (base);
+	order = 0x4d4d;
+	break;
+      case 0x746b6864:				/* tkhd */
+	fseek (ifp, 12, SEEK_CUR);
+	index = get4();
+	fseek (ifp, 58, SEEK_CUR);
+	wide = get4();
+	high = get4();
+	break;
+      case 0x7374737a:				/* stsz */
+	len = (get4(),get4());
+	break;
+      case 0x636f3634:				/* co64 */
+	fseek (ifp, 12, SEEK_CUR);
+	off = get4();
+	switch (index) {
+	  case 1:			/* 1 = full size, 2 = 27% size */
+	    thumb_width  = wide;
+	    thumb_height = high;
+	    thumb_length = len;
+	    thumb_offset = off;
+	    break;
+	  case 3:
+	    raw_width  = wide;
+	    raw_height = high;
+	    data_offset = off;
+	    load_raw = &CLASS canon_crx_load_raw;
+	}
+	break;
+      case 0x50525657:				/* PRVW */
+	fseek (ifp, 6, SEEK_CUR);
+    }
+    fseek (ifp, save+size, SEEK_SET);
+  }
+}
+
+void CLASS parse_qt (int end)
+{
+  unsigned save, size;
+  char tag[4];
+
+  order = 0x4d4d;
+  while (ftell(ifp)+7 < end) {
+    save = ftell(ifp);
+    if ((size = get4()) < 8) return;
+    fread (tag, 4, 1, ifp);
+    if (!memcmp(tag,"moov",4) ||
+	!memcmp(tag,"udta",4) ||
+	!memcmp(tag,"CNTH",4))
+      parse_qt (save+size);
+    if (!memcmp(tag,"CNDA",4))
+      parse_jpeg (ftell(ifp));
+    fseek (ifp, save+size, SEEK_SET);
+  }
+}
+
 void CLASS parse_smal (int offset, int fsize)
 {
   int ver;
@@ -5910,6 +6978,35 @@
   data_offset += (INT64) get4() << 32;
 }
 
+void CLASS parse_redcine()
+{
+  unsigned i, len, rdvo;
+
+  order = 0x4d4d;
+  is_raw = 0;
+  fseek (ifp, 52, SEEK_SET);
+  width  = get4();
+  height = get4();
+  fseek (ifp, 0, SEEK_END);
+  fseek (ifp, -(i = ftello(ifp) & 511), SEEK_CUR);
+  if (get4() != i || get4() != 0x52454f42) {
+    fprintf (stderr,_("%s: Tail is missing, parsing from head...\n"), ifname);
+    fseek (ifp, 0, SEEK_SET);
+    while ((len = get4()) != EOF) {
+      if (get4() == 0x52454456)
+	if (is_raw++ == shot_select)
+	  data_offset = ftello(ifp) - 8;
+      fseek (ifp, len-8, SEEK_CUR);
+    }
+  } else {
+    rdvo = get4();
+    fseek (ifp, 12, SEEK_CUR);
+    is_raw = get4();
+    fseeko (ifp, rdvo+8 + shot_select*4, SEEK_SET);
+    data_offset = get4();
+  }
+}
+
 char * CLASS foveon_gets (int offset, char *str, int len)
 {
   int i;
@@ -5942,16 +7039,25 @@
     switch (tag) {
       case 0x47414d49:			/* IMAG */
       case 0x32414d49:			/* IMA2 */
-	fseek (ifp, 12, SEEK_CUR);
+	fseek (ifp, 8, SEEK_CUR);
+	pent = get4();
 	wide = get4();
 	high = get4();
 	if (wide > raw_width && high > raw_height) {
+	  switch (pent) {
+	    case  5:  load_flags = 1;
+	    case  6:  load_raw = &CLASS foveon_sd_load_raw;  break;
+	    case 30:  load_raw = &CLASS foveon_dp_load_raw;  break;
+	    default:  load_raw = 0;
+	  }
 	  raw_width  = wide;
 	  raw_height = high;
-	  data_offset = off+24;
+	  data_offset = off+28;
+	  is_foveon = 1;
 	}
 	fseek (ifp, off+28, SEEK_SET);
-	if (fgetc(ifp) == 0xff && fgetc(ifp) == 0xd8) {
+	if (fgetc(ifp) == 0xff && fgetc(ifp) == 0xd8
+		&& thumb_length < len-28) {
 	  thumb_offset = off+28;
 	  thumb_length = len-28;
 	  write_thumb = &CLASS jpeg_thumb;
@@ -5964,10 +7070,8 @@
 	}
 	break;
       case 0x464d4143:			/* CAMF */
-	meta_offset = off+24;
+	meta_offset = off+8;
 	meta_length = len-28;
-	if (meta_length > 0x20000)
-	    meta_length = 0x20000;
 	break;
       case 0x504f5250:			/* PROP */
 	pent = (get4(),get4());
@@ -5975,7 +7079,7 @@
 	off += pent*8 + 24;
 	if ((unsigned) pent > 256) pent=256;
 	for (i=0; i < pent*2; i++)
-	  poff[0][i] = off + get4()*2;
+	  ((int *)poff)[i] = off + get4()*2;
 	for (i=0; i < pent; i++) {
 	  foveon_gets (poff[i][0], name, 64);
 	  foveon_gets (poff[i][1], value, 64);
@@ -6002,384 +7106,1159 @@
     }
     fseek (ifp, save, SEEK_SET);
   }
-  is_foveon = 1;
 }
 
 /*
-   Thanks to Adobe for providing these excellent CAM -> XYZ matrices!
+   All matrices are from Adobe DNG Converter unless otherwise noted.
  */
-void CLASS adobe_coeff (char *make, char *model)
+void CLASS adobe_coeff (const char *make, const char *model)
 {
   static const struct {
     const char *prefix;
-    short black, trans[12];
+    short black, maximum, trans[12];
   } table[] = {
-    { "Apple QuickTake", 0,	/* DJC */
-	{ 17576,-3191,-3318,5210,6733,-1942,9031,1280,-124 } },
-    { "Canon EOS D2000", 0,
+    { "AgfaPhoto DC-833m", 0, 0,	/* DJC */
+	{ 11438,-3762,-1115,-2409,9914,2497,-1227,2295,5300 } },
+    { "Apple QuickTake", 0, 0,		/* DJC */
+	{ 21392,-5653,-3353,2406,8010,-415,7166,1427,2078 } },
+    { "Canon EOS D2000", 0, 0,
 	{ 24542,-10860,-3401,-1490,11370,-297,2858,-605,3225 } },
-    { "Canon EOS D6000", 0,
+    { "Canon EOS D6000", 0, 0,
 	{ 20482,-7172,-3125,-1033,10410,-285,2542,226,3136 } },
-    { "Canon EOS D30", 0,
+    { "Canon EOS D30", 0, 0,
 	{ 9805,-2689,-1312,-5803,13064,3068,-2438,3075,8775 } },
-    { "Canon EOS D60", 0,
+    { "Canon EOS D60", 0, 0xfa0,
 	{ 6188,-1341,-890,-7168,14489,2937,-2640,3228,8483 } },
-    { "Canon EOS 5D", 0,
+    { "Canon EOS 5DS", 0, 0x3c96,
+	{ 6250,-711,-808,-5153,12794,2636,-1249,2198,5610 } },
+    { "Canon EOS 5D Mark IV", 0, 0,
+	{ 6446,-366,-864,-4436,12204,2513,-952,2496,6348 } },
+    { "Canon EOS 5D Mark III", 0, 0x3c80,
+	{ 6722,-635,-963,-4287,12460,2028,-908,2162,5668 } },
+    { "Canon EOS 5D Mark II", 0, 0x3cf0,
+	{ 4716,603,-830,-7798,15474,2480,-1496,1937,6651 } },
+    { "Canon EOS 5D", 0, 0xe6c,
 	{ 6347,-479,-972,-8297,15954,2480,-1968,2131,7649 } },
-    { "Canon EOS 20Da", 0,
+    { "Canon EOS 6D Mark II", 0, 0,
+	{ 6875,-970,-932,-4691,12459,2501,-874,1953,5809 } },
+    { "Canon EOS 6D", 0, 0x3c82,
+	{ 7034,-804,-1014,-4420,12564,2058,-851,1994,5758 } },
+    { "Canon EOS 7D Mark II", 0, 0x3510,
+	{ 7268,-1082,-969,-4186,11839,2663,-825,2029,5839 } },
+    { "Canon EOS 7D", 0, 0x3510,
+	{ 6844,-996,-856,-3876,11761,2396,-593,1772,6198 } },
+    { "Canon EOS 10D", 0, 0xfa0,
+	{ 8197,-2000,-1118,-6714,14335,2592,-2536,3178,8266 } },
+    { "Canon EOS 20Da", 0, 0,
 	{ 14155,-5065,-1382,-6550,14633,2039,-1623,1824,6561 } },
-    { "Canon EOS 20D", 0,
+    { "Canon EOS 20D", 0, 0xfff,
 	{ 6599,-537,-891,-8071,15783,2424,-1983,2234,7462 } },
-    { "Canon EOS 30D", 0,
+    { "Canon EOS 30D", 0, 0,
 	{ 6257,-303,-1000,-7880,15621,2396,-1714,1904,7046 } },
-    { "Canon EOS 40D", 0,
+    { "Canon EOS 40D", 0, 0x3f60,
 	{ 6071,-747,-856,-7653,15365,2441,-2025,2553,7315 } },
-    { "Canon EOS 350D", 0,
+    { "Canon EOS 50D", 0, 0x3d93,
+	{ 4920,616,-593,-6493,13964,2784,-1774,3178,7005 } },
+    { "Canon EOS 60D", 0, 0x2ff7,
+	{ 6719,-994,-925,-4408,12426,2211,-887,2129,6051 } },
+    { "Canon EOS 70D", 0, 0x3bc7,
+	{ 7034,-804,-1014,-4420,12564,2058,-851,1994,5758 } },
+    { "Canon EOS 77D", 0, 0,
+	{ 7377,-742,-998,-4235,11981,2549,-673,1918,5538 } },
+    { "Canon EOS 80D", 0, 0,
+	{ 7457,-671,-937,-4849,12495,2643,-1213,2354,5492 } },
+    { "Canon EOS 100D", 0, 0x350f,
+	{ 6602,-841,-939,-4472,12458,2247,-975,2039,6148 } },
+    { "Canon EOS 200D", 0, 0,
+	{ 7377,-742,-998,-4235,11981,2549,-673,1918,5538 } },
+    { "Canon EOS 300D", 0, 0xfa0,
+	{ 8197,-2000,-1118,-6714,14335,2592,-2536,3178,8266 } },
+    { "Canon EOS 350D", 0, 0xfff,
 	{ 6018,-617,-965,-8645,15881,2975,-1530,1719,7642 } },
-    { "Canon EOS 400D", 0,
+    { "Canon EOS 400D", 0, 0xe8e,
 	{ 7054,-1501,-990,-8156,15544,2812,-1278,1414,7796 } },
-    { "Canon EOS-1Ds Mark III", 0,
+    { "Canon EOS 450D", 0, 0x390d,
+	{ 5784,-262,-821,-7539,15064,2672,-1982,2681,7427 } },
+    { "Canon EOS 500D", 0, 0x3479,
+	{ 4763,712,-646,-6821,14399,2640,-1921,3276,6561 } },
+    { "Canon EOS 550D", 0, 0x3dd7,
+	{ 6941,-1164,-857,-3825,11597,2534,-416,1540,6039 } },
+    { "Canon EOS 600D", 0, 0x3510,
+	{ 6461,-907,-882,-4300,12184,2378,-819,1944,5931 } },
+    { "Canon EOS 650D", 0, 0x354d,
+	{ 6602,-841,-939,-4472,12458,2247,-975,2039,6148 } },
+    { "Canon EOS 700D", 0, 0x3c00,
+	{ 6602,-841,-939,-4472,12458,2247,-975,2039,6148 } },
+    { "Canon EOS 750D", 0, 0x368e,
+	{ 6362,-823,-847,-4426,12109,2616,-743,1857,5635 } },
+    { "Canon EOS 760D", 0, 0x350f,
+	{ 6362,-823,-847,-4426,12109,2616,-743,1857,5635 } },
+    { "Canon EOS 800D", 0, 0,
+	{ 6970,-512,-968,-4425,12161,2553,-739,1982,5601 } },
+    { "Canon EOS 1000D", 0, 0xe43,
+	{ 6771,-1139,-977,-7818,15123,2928,-1244,1437,7533 } },
+    { "Canon EOS 1100D", 0, 0x3510,
+	{ 6444,-904,-893,-4563,12308,2535,-903,2016,6728 } },
+    { "Canon EOS 1200D", 0, 0x37c2,
+	{ 6461,-907,-882,-4300,12184,2378,-819,1944,5931 } },
+    { "Canon EOS 1300D", 0, 0x3510,
+	{ 6939,-1016,-866,-4428,12473,2177,-1175,2178,6162 } },
+    { "Canon EOS 1500D", 0, 0,
+	{ 8532,-701,-1167,-4095,11879,2508,-797,2424,7010 } },
+    { "Canon EOS 3000D", 0, 0,
+	{ 6939,-1016,-866,-4428,12473,2177,-1175,2178,6162 } },
+    { "Canon EOS M6", 0, 0,
+	{ 8532,-701,-1167,-4095,11879,2508,-797,2424,7010 } },
+    { "Canon EOS M5", 0, 0,	/* also M50 */
+	{ 8532,-701,-1167,-4095,11879,2508,-797,2424,7010 } },
+    { "Canon EOS M3", 0, 0,
+	{ 6362,-823,-847,-4426,12109,2616,-743,1857,5635 } },
+    { "Canon EOS M100", 0, 0,
+	{ 8532,-701,-1167,-4095,11879,2508,-797,2424,7010 } },
+    { "Canon EOS M10", 0, 0,
+	{ 6400,-480,-888,-5294,13416,2047,-1296,2203,6137 } },
+    { "Canon EOS M", 0, 0,
+	{ 6602,-841,-939,-4472,12458,2247,-975,2039,6148 } },
+    { "Canon EOS-1Ds Mark III", 0, 0x3bb0,
 	{ 5859,-211,-930,-8255,16017,2353,-1732,1887,7448 } },
-    { "Canon EOS-1Ds Mark II", 0,
+    { "Canon EOS-1Ds Mark II", 0, 0xe80,
 	{ 6517,-602,-867,-8180,15926,2378,-1618,1771,7633 } },
-    { "Canon EOS-1D Mark II N", 0,
-	{ 6240,-466,-822,-8180,15825,2500,-1801,1938,8042 } },
-    { "Canon EOS-1D Mark III", 0,
+    { "Canon EOS-1D Mark IV", 0, 0x3bb0,
+	{ 6014,-220,-795,-4109,12014,2361,-561,1824,5787 } },
+    { "Canon EOS-1D Mark III", 0, 0x3bb0,
 	{ 6291,-540,-976,-8350,16145,2311,-1714,1858,7326 } },
-    { "Canon EOS-1D Mark II", 0,
+    { "Canon EOS-1D Mark II N", 0, 0xe80,
+	{ 6240,-466,-822,-8180,15825,2500,-1801,1938,8042 } },
+    { "Canon EOS-1D Mark II", 0, 0xe80,
 	{ 6264,-582,-724,-8312,15948,2504,-1744,1919,8664 } },
-    { "Canon EOS-1DS", 0,
+    { "Canon EOS-1DS", 0, 0xe20,
 	{ 4374,3631,-1743,-7520,15212,2472,-2892,3632,8161 } },
-    { "Canon EOS-1D", 0,
+    { "Canon EOS-1D C", 0, 0x3c4e,
+	{ 6847,-614,-1014,-4669,12737,2139,-1197,2488,6846 } },
+    { "Canon EOS-1D X Mark II", 0, 0,
+	{ 7596,-978,-967,-4808,12571,2503,-1398,2567,5752 } },
+    { "Canon EOS-1D X", 0, 0x3c4e,
+	{ 6847,-614,-1014,-4669,12737,2139,-1197,2488,6846 } },
+    { "Canon EOS-1D", 0, 0xe20,
 	{ 6806,-179,-1020,-8097,16415,1687,-3267,4236,7690 } },
-    { "Canon EOS", 0,
-	{ 8197,-2000,-1118,-6714,14335,2592,-2536,3178,8266 } },
-    { "Canon PowerShot A50", 0,
+    { "Canon EOS C500", 853, 0,		/* DJC */
+	{ 17851,-10604,922,-7425,16662,763,-3660,3636,22278 } },
+    { "Canon PowerShot A530", 0, 0,
+	{ 0 } },	/* don't want the A5 matrix */
+    { "Canon PowerShot A50", 0, 0,
 	{ -5300,9846,1776,3436,684,3939,-5540,9879,6200,-1404,11175,217 } },
-    { "Canon PowerShot A5", 0,
+    { "Canon PowerShot A5", 0, 0,
 	{ -4801,9475,1952,2926,1611,4094,-5259,10164,5947,-1554,10883,547 } },
-    { "Canon PowerShot G1", 0,
+    { "Canon PowerShot G10", 0, 0,
+	{ 11093,-3906,-1028,-5047,12492,2879,-1003,1750,5561 } },
+    { "Canon PowerShot G11", 0, 0,
+	{ 12177,-4817,-1069,-1612,9864,2049,-98,850,4471 } },
+    { "Canon PowerShot G12", 0, 0,
+	{ 13244,-5501,-1248,-1508,9858,1935,-270,1083,4366 } },
+    { "Canon PowerShot G15", 0, 0,
+	{ 7474,-2301,-567,-4056,11456,2975,-222,716,4181 } },
+    { "Canon PowerShot G16", 0, 0,
+	{ 8020,-2687,-682,-3704,11879,2052,-965,1921,5556 } },
+    { "Canon PowerShot G1 X Mark III", 0, 0,
+	{ 8532,-701,-1167,-4095,11879,2508,-797,2424,7010 } },
+    { "Canon PowerShot G1 X", 0, 0,
+	{ 7378,-1255,-1043,-4088,12251,2048,-876,1946,5805 } },
+    { "Canon PowerShot G1", 0, 0,
 	{ -4778,9467,2172,4743,-1141,4344,-5146,9908,6077,-1566,11051,557 } },
-    { "Canon PowerShot G2", 0,
+    { "Canon PowerShot G2", 0, 0,
 	{ 9087,-2693,-1049,-6715,14382,2537,-2291,2819,7790 } },
-    { "Canon PowerShot G3", 0,
+    { "Canon PowerShot G3 X", 0, 0,
+	{ 9701,-3857,-921,-3149,11537,1817,-786,1817,5147 } },
+    { "Canon PowerShot G3", 0, 0,
 	{ 9212,-2781,-1073,-6573,14189,2605,-2300,2844,7664 } },
-    { "Canon PowerShot G5", 0,
+    { "Canon PowerShot G5 X", 0, 0,
+	{ 9602,-3823,-937,-2984,11495,1675,-407,1415,5049 } },
+    { "Canon PowerShot G5", 0, 0,
 	{ 9757,-2872,-933,-5972,13861,2301,-1622,2328,7212 } },
-    { "Canon PowerShot G6", 0,
+    { "Canon PowerShot G6", 0, 0,
 	{ 9877,-3775,-871,-7613,14807,3072,-1448,1305,7485 } },
-    { "Canon PowerShot G9", 0,
+    { "Canon PowerShot G7 X", 0, 0,
+	{ 9602,-3823,-937,-2984,11495,1675,-407,1415,5049 } },
+    { "Canon PowerShot G9 X Mark II", 0, 0,
+	{ 10056,-4131,-944,-2576,11143,1625,-238,1294,5179 } },
+    { "Canon PowerShot G9 X", 0, 0,
+	{ 9602,-3823,-937,-2984,11495,1675,-407,1415,5049 } },
+    { "Canon PowerShot G9", 0, 0,
 	{ 7368,-2141,-598,-5621,13254,2625,-1418,1696,5743 } },
-    { "Canon PowerShot Pro1", 0,
+    { "Canon PowerShot Pro1", 0, 0,
 	{ 10062,-3522,-999,-7643,15117,2730,-765,817,7323 } },
-    { "Canon PowerShot Pro70", 34,
+    { "Canon PowerShot Pro70", 34, 0,
 	{ -4155,9818,1529,3939,-25,4522,-5521,9870,6610,-2238,10873,1342 } },
-    { "Canon PowerShot Pro90", 0,
+    { "Canon PowerShot Pro90", 0, 0,
 	{ -4963,9896,2235,4642,-987,4294,-5162,10011,5859,-1770,11230,577 } },
-    { "Canon PowerShot S30", 0,
+    { "Canon PowerShot S30", 0, 0,
 	{ 10566,-3652,-1129,-6552,14662,2006,-2197,2581,7670 } },
-    { "Canon PowerShot S40", 0,
+    { "Canon PowerShot S40", 0, 0,
 	{ 8510,-2487,-940,-6869,14231,2900,-2318,2829,9013 } },
-    { "Canon PowerShot S45", 0,
+    { "Canon PowerShot S45", 0, 0,
 	{ 8163,-2333,-955,-6682,14174,2751,-2077,2597,8041 } },
-    { "Canon PowerShot S50", 0,
+    { "Canon PowerShot S50", 0, 0,
 	{ 8882,-2571,-863,-6348,14234,2288,-1516,2172,6569 } },
-    { "Canon PowerShot S60", 0,
+    { "Canon PowerShot S60", 0, 0,
 	{ 8795,-2482,-797,-7804,15403,2573,-1422,1996,7082 } },
-    { "Canon PowerShot S70", 0,
+    { "Canon PowerShot S70", 0, 0,
 	{ 9976,-3810,-832,-7115,14463,2906,-901,989,7889 } },
-    { "Canon PowerShot A610", 0, /* DJC */
+    { "Canon PowerShot S90", 0, 0,
+	{ 12374,-5016,-1049,-1677,9902,2078,-83,852,4683 } },
+    { "Canon PowerShot S95", 0, 0,
+	{ 13440,-5896,-1279,-1236,9598,1931,-180,1001,4651 } },
+    { "Canon PowerShot S100", 0, 0,
+	{ 7968,-2565,-636,-2873,10697,2513,180,667,4211 } },
+    { "Canon PowerShot S110", 0, 0,
+	{ 8039,-2643,-654,-3783,11230,2930,-206,690,4194 } },
+    { "Canon PowerShot S120", 0, 0,
+	{ 6961,-1685,-695,-4625,12945,1836,-1114,2152,5518 } },
+    { "Canon PowerShot SX1 IS", 0, 0,
+	{ 6578,-259,-502,-5974,13030,3309,-308,1058,4970 } },
+    { "Canon PowerShot SX50 HS", 0, 0,
+	{ 12432,-4753,-1247,-2110,10691,1629,-412,1623,4926 } },
+    { "Canon PowerShot SX60 HS", 0, 0,
+	{ 13161,-5451,-1344,-1989,10654,1531,-47,1271,4955 } },
+    { "Canon PowerShot A3300", 0, 0,	/* DJC */
+	{ 10826,-3654,-1023,-3215,11310,1906,0,999,4960 } },
+    { "Canon PowerShot A470", 0, 0,	/* DJC */
+	{ 12513,-4407,-1242,-2680,10276,2405,-878,2215,4734 } },
+    { "Canon PowerShot A610", 0, 0,	/* DJC */
 	{ 15591,-6402,-1592,-5365,13198,2168,-1300,1824,5075 } },
-    { "Canon PowerShot A620", 0, /* DJC */
+    { "Canon PowerShot A620", 0, 0,	/* DJC */
 	{ 15265,-6193,-1558,-4125,12116,2010,-888,1639,5220 } },
-    { "Canon PowerShot A640", 0, /* DJC */
+    { "Canon PowerShot A630", 0, 0,	/* DJC */
+	{ 14201,-5308,-1757,-6087,14472,1617,-2191,3105,5348 } },
+    { "Canon PowerShot A640", 0, 0,	/* DJC */
 	{ 13124,-5329,-1390,-3602,11658,1944,-1612,2863,4885 } },
-    { "Canon PowerShot A650", 0, /* DJC */
+    { "Canon PowerShot A650", 0, 0,	/* DJC */
 	{ 9427,-3036,-959,-2581,10671,1911,-1039,1982,4430 } },
-    { "Canon PowerShot S3 IS", 0, /* DJC */
+    { "Canon PowerShot A720", 0, 0,	/* DJC */
+	{ 14573,-5482,-1546,-1266,9799,1468,-1040,1912,3810 } },
+    { "Canon PowerShot S3 IS", 0, 0,	/* DJC */
 	{ 14062,-5199,-1446,-4712,12470,2243,-1286,2028,4836 } },
-    { "CINE 650", 0,
+    { "Canon PowerShot SX110 IS", 0, 0,	/* DJC */
+	{ 14134,-5576,-1527,-1991,10719,1273,-1158,1929,3581 } },
+    { "Canon PowerShot SX220", 0, 0,	/* DJC */
+	{ 13898,-5076,-1447,-1405,10109,1297,-244,1860,3687 } },
+    { "Canon IXUS 160", 0, 0,		/* DJC */
+	{ 11657,-3781,-1136,-3544,11262,2283,-160,1219,4700 } },
+    { "Casio EX-S20", 0, 0,		/* DJC */
+	{ 11634,-3924,-1128,-4968,12954,2015,-1588,2648,7206 } },
+    { "Casio EX-Z750", 0, 0,		/* DJC */
+	{ 10819,-3873,-1099,-4903,13730,1175,-1755,3751,4632 } },
+    { "Casio EX-Z10", 128, 0xfff,	/* DJC */
+	{ 9790,-3338,-603,-2321,10222,2099,-344,1273,4799 } },
+    { "CINE 650", 0, 0,
 	{ 3390,480,-500,-800,3610,340,-550,2336,1192 } },
-    { "CINE 660", 0,
+    { "CINE 660", 0, 0,
 	{ 3390,480,-500,-800,3610,340,-550,2336,1192 } },
-    { "CINE", 0,
+    { "CINE", 0, 0,
 	{ 20183,-4295,-423,-3940,15330,3985,-280,4870,9800 } },
-    { "Contax N Digital", 0,
+    { "Contax N Digital", 0, 0xf1e,
 	{ 7777,1285,-1053,-9280,16543,2916,-3677,5679,7060 } },
-    { "EPSON R-D1", 0,
+    { "DXO ONE", 0, 0,
+	{ 6596,-2079,-562,-4782,13016,1933,-970,1581,5181 } },
+    { "Epson R-D1", 0, 0,
 	{ 6827,-1878,-732,-8429,16012,2564,-704,592,7145 } },
-    { "FUJIFILM FinePix E550", 0,
+    { "Fujifilm E550", 0, 0,
 	{ 11044,-3888,-1120,-7248,15168,2208,-1531,2277,8069 } },
-    { "FUJIFILM FinePix E900", 0,
+    { "Fujifilm E900", 0, 0,
 	{ 9183,-2526,-1078,-7461,15071,2574,-2022,2440,8639 } },
-    { "FUJIFILM FinePix F8", 0,
-	{ 11044,-3888,-1120,-7248,15168,2208,-1531,2277,8069 } },
-    { "FUJIFILM FinePix F7", 0,
+    { "Fujifilm F5", 0, 0,
+	{ 13690,-5358,-1474,-3369,11600,1998,-132,1554,4395 } },
+    { "Fujifilm F6", 0, 0,
+	{ 13690,-5358,-1474,-3369,11600,1998,-132,1554,4395 } },
+    { "Fujifilm F77", 0, 0xfe9,
+	{ 13690,-5358,-1474,-3369,11600,1998,-132,1554,4395 } },
+    { "Fujifilm F7", 0, 0,
 	{ 10004,-3219,-1201,-7036,15047,2107,-1863,2565,7736 } },
-    { "FUJIFILM FinePix S20Pro", 0,
+    { "Fujifilm F8", 0, 0,
+	{ 13690,-5358,-1474,-3369,11600,1998,-132,1554,4395 } },
+    { "Fujifilm GFX 50S", 0, 0,
+	{ 11756,-4754,-874,-3056,11045,2305,-381,1457,6006 } },
+    { "Fujifilm S100FS", 514, 0,
+	{ 11521,-4355,-1065,-6524,13767,3058,-1466,1984,6045 } },
+    { "Fujifilm S1", 0, 0,
+	{ 12297,-4882,-1202,-2106,10691,1623,-88,1312,4790 } },
+    { "Fujifilm S20Pro", 0, 0,
 	{ 10004,-3219,-1201,-7036,15047,2107,-1863,2565,7736 } },
-    { "FUJIFILM FinePix S2Pro", 128,
+    { "Fujifilm S20", 512, 0x3fff,
+	{ 11401,-4498,-1312,-5088,12751,2613,-838,1568,5941 } },
+    { "Fujifilm S2Pro", 128, 0xf15,
 	{ 12492,-4690,-1402,-7033,15423,1647,-1507,2111,7697 } },
-    { "FUJIFILM FinePix S3Pro", 0,
+    { "Fujifilm S3Pro", 0, 0x3dff,
 	{ 11807,-4612,-1294,-8927,16968,1988,-2120,2741,8006 } },
-    { "FUJIFILM FinePix S5Pro", 0,
+    { "Fujifilm S5Pro", 0, 0,
 	{ 12300,-5110,-1304,-9117,17143,1998,-1947,2448,8100 } },
-    { "FUJIFILM FinePix S5000", 0,
+    { "Fujifilm S5000", 0, 0,
 	{ 8754,-2732,-1019,-7204,15069,2276,-1702,2334,6982 } },
-    { "FUJIFILM FinePix S5100", 0,
+    { "Fujifilm S5100", 0, 0,
 	{ 11940,-4431,-1255,-6766,14428,2542,-993,1165,7421 } },
-    { "FUJIFILM FinePix S5500", 0,
+    { "Fujifilm S5500", 0, 0,
 	{ 11940,-4431,-1255,-6766,14428,2542,-993,1165,7421 } },
-    { "FUJIFILM FinePix S5200", 0,
+    { "Fujifilm S5200", 0, 0,
 	{ 9636,-2804,-988,-7442,15040,2589,-1803,2311,8621 } },
-    { "FUJIFILM FinePix S5600", 0,
+    { "Fujifilm S5600", 0, 0,
 	{ 9636,-2804,-988,-7442,15040,2589,-1803,2311,8621 } },
-    { "FUJIFILM FinePix S6", 0,
+    { "Fujifilm S6", 0, 0,
 	{ 12628,-4887,-1401,-6861,14996,1962,-2198,2782,7091 } },
-    { "FUJIFILM FinePix S7000", 0,
+    { "Fujifilm S7000", 0, 0,
 	{ 10190,-3506,-1312,-7153,15051,2238,-2003,2399,7505 } },
-    { "FUJIFILM FinePix S9000", 0,
+    { "Fujifilm S9000", 0, 0,
 	{ 10491,-3423,-1145,-7385,15027,2538,-1809,2275,8692 } },
-    { "FUJIFILM FinePix S9500", 0,
+    { "Fujifilm S9500", 0, 0,
 	{ 10491,-3423,-1145,-7385,15027,2538,-1809,2275,8692 } },
-    { "FUJIFILM FinePix S9100", 0,
+    { "Fujifilm S9100", 0, 0,
 	{ 12343,-4515,-1285,-7165,14899,2435,-1895,2496,8800 } },
-    { "FUJIFILM FinePix S9600", 0,
+    { "Fujifilm S9600", 0, 0,
 	{ 12343,-4515,-1285,-7165,14899,2435,-1895,2496,8800 } },
-    { "FUJIFILM IS-1", 0,
+    { "Fujifilm SL1000", 0, 0,
+	{ 11705,-4262,-1107,-2282,10791,1709,-555,1713,4945 } },
+    { "Fujifilm IS-1", 0, 0,
 	{ 21461,-10807,-1441,-2332,10599,1999,289,875,7703 } },
-    { "Imacon Ixpress", 0,	/* DJC */
+    { "Fujifilm IS Pro", 0, 0,
+	{ 12300,-5110,-1304,-9117,17143,1998,-1947,2448,8100 } },
+    { "Fujifilm HS10 HS11", 0, 0xf68,
+	{ 12440,-3954,-1183,-1123,9674,1708,-83,1614,4086 } },
+    { "Fujifilm HS2", 0, 0xfef,
+	{ 13690,-5358,-1474,-3369,11600,1998,-132,1554,4395 } },
+    { "Fujifilm HS3", 0, 0,
+	{ 13690,-5358,-1474,-3369,11600,1998,-132,1554,4395 } },
+    { "Fujifilm HS50EXR", 0, 0,
+	{ 12085,-4727,-953,-3257,11489,2002,-511,2046,4592 } },
+    { "Fujifilm F900EXR", 0, 0,
+	{ 12085,-4727,-953,-3257,11489,2002,-511,2046,4592 } },
+    { "Fujifilm X100F", 0, 0,
+	{ 11434,-4948,-1210,-3746,12042,1903,-666,1479,5235 } },
+    { "Fujifilm X100S", 0, 0,
+	{ 10592,-4262,-1008,-3514,11355,2465,-870,2025,6386 } },
+    { "Fujifilm X100T", 0, 0,
+	{ 10592,-4262,-1008,-3514,11355,2465,-870,2025,6386 } },
+    { "Fujifilm X100", 0, 0,
+	{ 12161,-4457,-1069,-5034,12874,2400,-795,1724,6904 } },
+    { "Fujifilm X10", 0, 0,
+	{ 13509,-6199,-1254,-4430,12733,1865,-331,1441,5022 } },
+    { "Fujifilm X20", 0, 0,
+	{ 11768,-4971,-1133,-4904,12927,2183,-480,1723,4605 } },
+    { "Fujifilm X30", 0, 0,
+	{ 12328,-5256,-1144,-4469,12927,1675,-87,1291,4351 } },
+    { "Fujifilm X70", 0, 0,
+	{ 10450,-4329,-878,-3217,11105,2421,-752,1758,6519 } },
+    { "Fujifilm X-Pro1", 0, 0,
+	{ 10413,-3996,-993,-3721,11640,2361,-733,1540,6011 } },
+    { "Fujifilm X-Pro2", 0, 0,
+	{ 11434,-4948,-1210,-3746,12042,1903,-666,1479,5235 } },
+    { "Fujifilm X-A10", 0, 0,
+	{ 11540,-4999,-991,-2949,10963,2278,-382,1049,5605 } },
+    { "Fujifilm X-A20", 0, 0,
+	{ 11540,-4999,-991,-2949,10963,2278,-382,1049,5605 } },
+    { "Fujifilm X-A1", 0, 0,
+	{ 11086,-4555,-839,-3512,11310,2517,-815,1341,5940 } },
+    { "Fujifilm X-A2", 0, 0,
+	{ 10763,-4560,-917,-3346,11311,2322,-475,1135,5843 } },
+    { "Fujifilm X-A3", 0, 0,
+	{ 12407,-5222,-1086,-2971,11116,2120,-294,1029,5284 } },
+    { "Fujifilm X-A5", 0, 0,
+	{ 11673,-4760,-1041,-3988,12058,2166,-771,1417,5569 } },
+    { "Fujifilm X-E1", 0, 0,
+	{ 10413,-3996,-993,-3721,11640,2361,-733,1540,6011 } },
+    { "Fujifilm X-E2S", 0, 0,
+	{ 11562,-5118,-961,-3022,11007,2311,-525,1569,6097 } },
+    { "Fujifilm X-E2", 0, 0,
+	{ 8458,-2451,-855,-4597,12447,2407,-1475,2482,6526 } },
+    { "Fujifilm X-E3", 0, 0,
+	{ 11434,-4948,-1210,-3746,12042,1903,-666,1479,5235 } },
+    { "Fujifilm X-H1", 0, 0,
+	{ 11434,-4948,-1210,-3746,12042,1903,-666,1479,5235 } },
+    { "Fujifilm X-M1", 0, 0,
+	{ 10413,-3996,-993,-3721,11640,2361,-733,1540,6011 } },
+    { "Fujifilm X-S1", 0, 0,
+	{ 13509,-6199,-1254,-4430,12733,1865,-331,1441,5022 } },
+    { "Fujifilm X-T1", 0, 0,	/* also X-T10 */
+	{ 8458,-2451,-855,-4597,12447,2407,-1475,2482,6526 } },
+    { "Fujifilm X-T2", 0, 0,	/* also X-T20 */
+	{ 11434,-4948,-1210,-3746,12042,1903,-666,1479,5235 } },
+    { "Fujifilm XF1", 0, 0,
+	{ 13509,-6199,-1254,-4430,12733,1865,-331,1441,5022 } },
+    { "Fujifilm XQ", 0, 0,	/* XQ1 and XQ2 */
+	{ 9252,-2704,-1064,-5893,14265,1717,-1101,2341,4349 } },
+    { "GoPro HERO5 Black", 0, 0,
+	{ 10344,-4210,-620,-2315,10625,1948,93,1058,5541 } },
+    { "Imacon Ixpress", 0, 0,		/* DJC */
 	{ 7025,-1415,-704,-5188,13765,1424,-1248,2742,6038 } },
-    { "KODAK NC2000", 0,
+    { "Kodak NC2000", 0, 0,
 	{ 13891,-6055,-803,-465,9919,642,2121,82,1291 } },
-    { "Kodak DCS315C", 8,
+    { "Kodak DCS315C", 8, 0,
 	{ 17523,-4827,-2510,756,8546,-137,6113,1649,2250 } },
-    { "Kodak DCS330C", 8,
+    { "Kodak DCS330C", 8, 0,
 	{ 20620,-7572,-2801,-103,10073,-396,3551,-233,2220 } },
-    { "KODAK DCS420", 0,
+    { "Kodak DCS420", 0, 0,
 	{ 10868,-1852,-644,-1537,11083,484,2343,628,2216 } },
-    { "KODAK DCS460", 0,
+    { "Kodak DCS460", 0, 0,
 	{ 10592,-2206,-967,-1944,11685,230,2206,670,1273 } },
-    { "KODAK EOSDCS1", 0,
+    { "Kodak EOSDCS1", 0, 0,
 	{ 10592,-2206,-967,-1944,11685,230,2206,670,1273 } },
-    { "KODAK EOSDCS3B", 0,
+    { "Kodak EOSDCS3B", 0, 0,
 	{ 9898,-2700,-940,-2478,12219,206,1985,634,1031 } },
-    { "Kodak DCS520C", 180,
+    { "Kodak DCS520C", 178, 0,
 	{ 24542,-10860,-3401,-1490,11370,-297,2858,-605,3225 } },
-    { "Kodak DCS560C", 188,
+    { "Kodak DCS560C", 177, 0,
 	{ 20482,-7172,-3125,-1033,10410,-285,2542,226,3136 } },
-    { "Kodak DCS620C", 180,
+    { "Kodak DCS620C", 177, 0,
 	{ 23617,-10175,-3149,-2054,11749,-272,2586,-489,3453 } },
-    { "Kodak DCS620X", 185,
+    { "Kodak DCS620X", 176, 0,
 	{ 13095,-6231,154,12221,-21,-2137,895,4602,2258 } },
-    { "Kodak DCS660C", 214,
+    { "Kodak DCS660C", 173, 0,
 	{ 18244,-6351,-2739,-791,11193,-521,3711,-129,2802 } },
-    { "Kodak DCS720X", 0,
+    { "Kodak DCS720X", 0, 0,
 	{ 11775,-5884,950,9556,1846,-1286,-1019,6221,2728 } },
-    { "Kodak DCS760C", 0,
+    { "Kodak DCS760C", 0, 0,
 	{ 16623,-6309,-1411,-4344,13923,323,2285,274,2926 } },
-    { "Kodak DCS Pro SLR", 0,
+    { "Kodak DCS Pro SLR", 0, 0,
 	{ 5494,2393,-232,-6427,13850,2846,-1876,3997,5445 } },
-    { "Kodak DCS Pro 14nx", 0,
+    { "Kodak DCS Pro 14nx", 0, 0,
 	{ 5494,2393,-232,-6427,13850,2846,-1876,3997,5445 } },
-    { "Kodak DCS Pro 14", 0,
+    { "Kodak DCS Pro 14", 0, 0,
 	{ 7791,3128,-776,-8588,16458,2039,-2455,4006,6198 } },
-    { "Kodak ProBack645", 0,
+    { "Kodak ProBack645", 0, 0,
 	{ 16414,-6060,-1470,-3555,13037,473,2545,122,4948 } },
-    { "Kodak ProBack", 0,
+    { "Kodak ProBack", 0, 0,
 	{ 21179,-8316,-2918,-915,11019,-165,3477,-180,4210 } },
-    { "KODAK P712", 0,
+    { "Kodak P712", 0, 0,
 	{ 9658,-3314,-823,-5163,12695,2768,-1342,1843,6044 } },
-    { "KODAK P850", 0,
+    { "Kodak P850", 0, 0xf7c,
 	{ 10511,-3836,-1102,-6946,14587,2558,-1481,1792,6246 } },
-    { "KODAK P880", 0,
+    { "Kodak P880", 0, 0xfff,
 	{ 12805,-4662,-1376,-7480,15267,2360,-1626,2194,7904 } },
-    { "Leaf CMost", 0,
+    { "Kodak EasyShare Z980", 0, 0,
+	{ 11313,-3559,-1101,-3893,11891,2257,-1214,2398,4908 } },
+    { "Kodak EasyShare Z981", 0, 0,
+	{ 12729,-4717,-1188,-1367,9187,2582,274,860,4411 } },
+    { "Kodak EasyShare Z990", 0, 0xfed,
+	{ 11749,-4048,-1309,-1867,10572,1489,-138,1449,4522 } },
+    { "Kodak EASYSHARE Z1015", 0, 0xef1,
+	{ 11265,-4286,-992,-4694,12343,2647,-1090,1523,5447 } },
+    { "Leaf CMost", 0, 0,
 	{ 3952,2189,449,-6701,14585,2275,-4536,7349,6536 } },
-    { "Leaf Valeo 6", 0,
+    { "Leaf Valeo 6", 0, 0,
 	{ 3952,2189,449,-6701,14585,2275,-4536,7349,6536 } },
-    { "Leaf Aptus 54S", 0,
+    { "Leaf Aptus 54S", 0, 0,
 	{ 8236,1746,-1314,-8251,15953,2428,-3673,5786,5771 } },
-    { "Leaf Aptus 65", 0,
+    { "Leaf Aptus 65", 0, 0,
 	{ 7914,1414,-1190,-8777,16582,2280,-2811,4605,5562 } },
-    { "Leaf Aptus 75", 0,
+    { "Leaf Aptus 75", 0, 0,
 	{ 7914,1414,-1190,-8777,16582,2280,-2811,4605,5562 } },
-    { "Leaf", 0,
+    { "Leaf", 0, 0,
 	{ 8236,1746,-1314,-8251,15953,2428,-3673,5786,5771 } },
-    { "Mamiya ZD", 0,
+    { "Mamiya ZD", 0, 0,
 	{ 7645,2579,-1363,-8689,16717,2015,-3712,5941,5961 } },
-    { "Micron 2010", 110,	/* DJC */
+    { "Micron 2010", 110, 0,		/* DJC */
 	{ 16695,-3761,-2151,155,9682,163,3433,951,4904 } },
-    { "Minolta DiMAGE 5", 0,
+    { "Minolta DiMAGE 5", 0, 0xf7d,
 	{ 8983,-2942,-963,-6556,14476,2237,-2426,2887,8014 } },
-    { "Minolta DiMAGE 7Hi", 0,
+    { "Minolta DiMAGE 7Hi", 0, 0xf7d,
 	{ 11368,-3894,-1242,-6521,14358,2339,-2475,3056,7285 } },
-    { "Minolta DiMAGE 7", 0,
+    { "Minolta DiMAGE 7", 0, 0xf7d,
 	{ 9144,-2777,-998,-6676,14556,2281,-2470,3019,7744 } },
-    { "Minolta DiMAGE A1", 0,
+    { "Minolta DiMAGE A1", 0, 0xf8b,
 	{ 9274,-2547,-1167,-8220,16323,1943,-2273,2720,8340 } },
-    { "MINOLTA DiMAGE A200", 0,
+    { "Minolta DiMAGE A200", 0, 0,
 	{ 8560,-2487,-986,-8112,15535,2771,-1209,1324,7743 } },
-    { "Minolta DiMAGE A2", 0,
+    { "Minolta DiMAGE A2", 0, 0xf8f,
 	{ 9097,-2726,-1053,-8073,15506,2762,-966,981,7763 } },
-    { "Minolta DiMAGE Z2", 0,	/* DJC */
+    { "Minolta DiMAGE Z2", 0, 0,	/* DJC */
 	{ 11280,-3564,-1370,-4655,12374,2282,-1423,2168,5396 } },
-    { "MINOLTA DYNAX 5", 0,
+    { "Minolta DYNAX 5", 0, 0xffb,
 	{ 10284,-3283,-1086,-7957,15762,2316,-829,882,6644 } },
-    { "MINOLTA DYNAX 7", 0,
+    { "Minolta DYNAX 7", 0, 0xffb,
 	{ 10239,-3104,-1099,-8037,15727,2451,-927,925,6871 } },
-    { "NIKON D100", 0,
+    { "Motorola PIXL", 0, 0,		/* DJC */
+	{ 8898,-989,-1033,-3292,11619,1674,-661,3178,5216 } },
+    { "Nikon D100", 0, 0,
 	{ 5902,-933,-782,-8983,16719,2354,-1402,1455,6464 } },
-    { "NIKON D1H", 0,
+    { "Nikon D1H", 0, 0,
 	{ 7577,-2166,-926,-7454,15592,1934,-2377,2808,8606 } },
-    { "NIKON D1X", 0,
+    { "Nikon D1X", 0, 0,
 	{ 7702,-2245,-975,-9114,17242,1875,-2679,3055,8521 } },
-    { "NIKON D1", 0,	/* multiplied by 2.218750, 1.0, 1.148438 */
+    { "Nikon D1", 0, 0, /* multiplied by 2.218750, 1.0, 1.148438 */
 	{ 16772,-4726,-2141,-7611,15713,1972,-2846,3494,9521 } },
-    { "NIKON D2H", 0,
+    { "Nikon D200", 0, 0xfbc,
+	{ 8367,-2248,-763,-8758,16447,2422,-1527,1550,8053 } },
+    { "Nikon D2H", 0, 0,
 	{ 5710,-901,-615,-8594,16617,2024,-2975,4120,6830 } },
-    { "NIKON D2X", 0,
+    { "Nikon D2X", 0, 0,
 	{ 10231,-2769,-1255,-8301,15900,2552,-797,680,7148 } },
-    { "NIKON D40X", 0,
+    { "Nikon D3000", 0, 0,
+	{ 8736,-2458,-935,-9075,16894,2251,-1354,1242,8263 } },
+    { "Nikon D3100", 0, 0,
+	{ 7911,-2167,-813,-5327,13150,2408,-1288,2483,7968 } },
+    { "Nikon D3200", 0, 0xfb9,
+	{ 7013,-1408,-635,-5268,12902,2640,-1470,2801,7379 } },
+    { "Nikon D3300", 0, 0,
+	{ 6988,-1384,-714,-5631,13410,2447,-1485,2204,7318 } },
+    { "Nikon D3400", 0, 0,
+	{ 6988,-1384,-714,-5631,13410,2447,-1485,2204,7318 } },
+    { "Nikon D300", 0, 0,
+	{ 9030,-1992,-715,-8465,16302,2255,-2689,3217,8069 } },
+    { "Nikon D3X", 0, 0,
+	{ 7171,-1986,-648,-8085,15555,2718,-2170,2512,7457 } },
+    { "Nikon D3S", 0, 0,
+	{ 8828,-2406,-694,-4874,12603,2541,-660,1509,7587 } },
+    { "Nikon D3", 0, 0,
+	{ 8139,-2171,-663,-8747,16541,2295,-1925,2008,8093 } },
+    { "Nikon D40X", 0, 0,
 	{ 8819,-2543,-911,-9025,16928,2151,-1329,1213,8449 } },
-    { "NIKON D40", 0,
+    { "Nikon D40", 0, 0,
 	{ 6992,-1668,-806,-8138,15748,2543,-874,850,7897 } },
-    { "NIKON D50", 0,
+    { "Nikon D4S", 0, 0,
+	{ 8598,-2848,-857,-5618,13606,2195,-1002,1773,7137 } },
+    { "Nikon D4", 0, 0,
+	{ 8598,-2848,-857,-5618,13606,2195,-1002,1773,7137 } },
+    { "Nikon Df", 0, 0,
+	{ 8598,-2848,-857,-5618,13606,2195,-1002,1773,7137 } },
+    { "Nikon D5000", 0, 0xf00,
+	{ 7309,-1403,-519,-8474,16008,2622,-2433,2826,8064 } },
+    { "Nikon D5100", 0, 0x3de6,
+	{ 8198,-2239,-724,-4871,12389,2798,-1043,2050,7181 } },
+    { "Nikon D5200", 0, 0,
+	{ 8322,-3112,-1047,-6367,14342,2179,-988,1638,6394 } },
+    { "Nikon D5300", 0, 0,
+	{ 6988,-1384,-714,-5631,13410,2447,-1485,2204,7318 } },
+    { "Nikon D5500", 0, 0,
+	{ 8821,-2938,-785,-4178,12142,2287,-824,1651,6860 } },
+    { "Nikon D5600", 0, 0,
+	{ 8821,-2938,-785,-4178,12142,2287,-824,1651,6860 } },
+    { "Nikon D500", 0, 0,
+	{ 8813,-3210,-1036,-4703,12868,2021,-1054,1940,6129 } },
+    { "Nikon D50", 0, 0,
 	{ 7732,-2422,-789,-8238,15884,2498,-859,783,7330 } },
-    { "NIKON D70", 0,
+    { "Nikon D5", 0, 0,
+	{ 9200,-3522,-992,-5755,13803,2117,-753,1486,6338 } },
+    { "Nikon D600", 0, 0x3e07,
+	{ 8178,-2245,-609,-4857,12394,2776,-1207,2086,7298 } },
+    { "Nikon D610", 0, 0,
+	{ 8178,-2245,-609,-4857,12394,2776,-1207,2086,7298 } },
+    { "Nikon D60", 0, 0,
+	{ 8736,-2458,-935,-9075,16894,2251,-1354,1242,8263 } },
+    { "Nikon D7000", 0, 0,
+	{ 8198,-2239,-724,-4871,12389,2798,-1043,2050,7181 } },
+    { "Nikon D7100", 0, 0,
+	{ 8322,-3112,-1047,-6367,14342,2179,-988,1638,6394 } },
+    { "Nikon D7200", 0, 0,
+	{ 8322,-3112,-1047,-6367,14342,2179,-988,1638,6394 } },
+    { "Nikon D7500", 0, 0,
+	{ 8813,-3210,-1036,-4703,12868,2021,-1054,1940,6129 } },
+    { "Nikon D750", 0, 0,
+	{ 9020,-2890,-715,-4535,12436,2348,-934,1919,7086 } },
+    { "Nikon D700", 0, 0,
+	{ 8139,-2171,-663,-8747,16541,2295,-1925,2008,8093 } },
+    { "Nikon D70", 0, 0,
 	{ 7732,-2422,-789,-8238,15884,2498,-859,783,7330 } },
-    { "NIKON D80", 0,
+    { "Nikon D850", 0, 0,
+	{ 10405,-3755,-1270,-5461,13787,1793,-1040,2015,6785 } },
+    { "Nikon D810", 0, 0,
+	{ 9369,-3195,-791,-4488,12430,2301,-893,1796,6872 } },
+    { "Nikon D800", 0, 0,
+	{ 7866,-2108,-555,-4869,12483,2681,-1176,2069,7501 } },
+    { "Nikon D80", 0, 0,
 	{ 8629,-2410,-883,-9055,16940,2171,-1490,1363,8520 } },
-    { "NIKON D200", 0,
-	{ 8367,-2248,-763,-8758,16447,2422,-1527,1550,8053 } },
-    { "NIKON D300", 0,
-	{ 9030,-1992,-715,-8465,16302,2255,-2689,3217,8069 } },
-    { "NIKON D3", 0,
-	{ 8139,-2171,-663,-8747,16541,2295,-1925,2008,8093 } },
-    { "NIKON E950", 0,		/* DJC */
+    { "Nikon D90", 0, 0xf00,
+	{ 7309,-1403,-519,-8474,16008,2622,-2434,2826,8064 } },
+    { "Nikon E700", 0, 0x3dd,		/* DJC */
+	{ -3746,10611,1665,9621,-1734,2114,-2389,7082,3064,3406,6116,-244 } },
+    { "Nikon E800", 0, 0x3dd,		/* DJC */
 	{ -3746,10611,1665,9621,-1734,2114,-2389,7082,3064,3406,6116,-244 } },
-    { "NIKON E995", 0,	/* copied from E5000 */
+    { "Nikon E950", 0, 0x3dd,		/* DJC */
+	{ -3746,10611,1665,9621,-1734,2114,-2389,7082,3064,3406,6116,-244 } },
+    { "Nikon E995", 0, 0,	/* copied from E5000 */
 	{ -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } },
-    { "NIKON E2100", 0,	/* copied from Z2, new white balance */
+    { "Nikon E2100", 0, 0,	/* copied from Z2, new white balance */
 	{ 13142,-4152,-1596,-4655,12374,2282,-1769,2696,6711} },
-    { "NIKON E2500", 0,
+    { "Nikon E2500", 0, 0,
 	{ -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } },
-    { "NIKON E4300", 0, /* copied from Minolta DiMAGE Z2 */
+    { "Nikon E3200", 0, 0,		/* DJC */
+	{ 9846,-2085,-1019,-3278,11109,2170,-774,2134,5745 } },
+    { "Nikon E4300", 0, 0,	/* copied from Minolta DiMAGE Z2 */
 	{ 11280,-3564,-1370,-4655,12374,2282,-1423,2168,5396 } },
-    { "NIKON E4500", 0,
+    { "Nikon E4500", 0, 0,
 	{ -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } },
-    { "NIKON E5000", 0,
+    { "Nikon E5000", 0, 0,
 	{ -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } },
-    { "NIKON E5400", 0,
+    { "Nikon E5400", 0, 0,
 	{ 9349,-2987,-1001,-7919,15766,2266,-2098,2680,6839 } },
-    { "NIKON E5700", 0,
+    { "Nikon E5700", 0, 0,
 	{ -5368,11478,2368,5537,-113,3148,-4969,10021,5782,778,9028,211 } },
-    { "NIKON E8400", 0,
+    { "Nikon E8400", 0, 0,
 	{ 7842,-2320,-992,-8154,15718,2599,-1098,1342,7560 } },
-    { "NIKON E8700", 0,
+    { "Nikon E8700", 0, 0,
 	{ 8489,-2583,-1036,-8051,15583,2643,-1307,1407,7354 } },
-    { "NIKON E8800", 0,
+    { "Nikon E8800", 0, 0,
 	{ 7971,-2314,-913,-8451,15762,2894,-1442,1520,7610 } },
-    { "OLYMPUS C5050", 0,
+    { "Nikon COOLPIX A", 0, 0,
+	{ 8198,-2239,-724,-4871,12389,2798,-1043,2050,7181 } },
+    { "Nikon COOLPIX B700", 200, 0,
+	{ 14387,-6014,-1299,-1357,9975,1616,467,1047,4744 } },
+    { "Nikon COOLPIX P330", 200, 0,
+	{ 10321,-3920,-931,-2750,11146,1824,-442,1545,5539 } },
+    { "Nikon COOLPIX P340", 200, 0,
+	{ 10321,-3920,-931,-2750,11146,1824,-442,1545,5539 } },
+    { "Nikon COOLPIX P6000", 0, 0,
+	{ 9698,-3367,-914,-4706,12584,2368,-837,968,5801 } },
+    { "Nikon COOLPIX P7000", 0, 0,
+	{ 11432,-3679,-1111,-3169,11239,2202,-791,1380,4455 } },
+    { "Nikon COOLPIX P7100", 0, 0,
+	{ 11053,-4269,-1024,-1976,10182,2088,-526,1263,4469 } },
+    { "Nikon COOLPIX P7700", 200, 0,
+	{ 10321,-3920,-931,-2750,11146,1824,-442,1545,5539 } },
+    { "Nikon COOLPIX P7800", 200, 0,
+	{ 10321,-3920,-931,-2750,11146,1824,-442,1545,5539 } },
+    { "Nikon 1 V3", 0, 0,
+	{ 5958,-1559,-571,-4021,11453,2939,-634,1548,5087 } },
+    { "Nikon 1 J4", 0, 0,
+	{ 5958,-1559,-571,-4021,11453,2939,-634,1548,5087 } },
+    { "Nikon 1 J5", 0, 0,
+	{ 7520,-2518,-645,-3844,12102,1945,-913,2249,6835 } },
+    { "Nikon 1 S2", 200, 0,
+	{ 6612,-1342,-618,-3338,11055,2623,-174,1792,5075 } },
+    { "Nikon 1 V2", 0, 0,
+	{ 6588,-1305,-693,-3277,10987,2634,-355,2016,5106 } },
+    { "Nikon 1 J3", 0, 0,
+	{ 6588,-1305,-693,-3277,10987,2634,-355,2016,5106 } },
+    { "Nikon 1 AW1", 0, 0,
+	{ 6588,-1305,-693,-3277,10987,2634,-355,2016,5106 } },
+    { "Nikon 1 ", 0, 0,		/* J1, J2, S1, V1 */
+	{ 8994,-2667,-865,-4594,12324,2552,-699,1786,6260 } },
+    { "Olympus AIR A01", 0, 0,
+	{ 8992,-3093,-639,-2563,10721,2122,-437,1270,5473 } },
+    { "Olympus C5050", 0, 0,
 	{ 10508,-3124,-1273,-6079,14294,1901,-1653,2306,6237 } },
-    { "OLYMPUS C5060", 0,
+    { "Olympus C5060", 0, 0,
 	{ 10445,-3362,-1307,-7662,15690,2058,-1135,1176,7602 } },
-    { "OLYMPUS C7070", 0,
+    { "Olympus C7070", 0, 0,
 	{ 10252,-3531,-1095,-7114,14850,2436,-1451,1723,6365 } },
-    { "OLYMPUS C70", 0,
+    { "Olympus C70", 0, 0,
 	{ 10793,-3791,-1146,-7498,15177,2488,-1390,1577,7321 } },
-    { "OLYMPUS C80", 0,
+    { "Olympus C80", 0, 0,
 	{ 8606,-2509,-1014,-8238,15714,2703,-942,979,7760 } },
-    { "OLYMPUS E-10", 0,
+    { "Olympus E-10", 0, 0xffc,
 	{ 12745,-4500,-1416,-6062,14542,1580,-1934,2256,6603 } },
-    { "OLYMPUS E-1", 0,
+    { "Olympus E-1", 0, 0,
 	{ 11846,-4767,-945,-7027,15878,1089,-2699,4122,8311 } },
-    { "OLYMPUS E-20", 0,
+    { "Olympus E-20", 0, 0xffc,
 	{ 13173,-4732,-1499,-5807,14036,1895,-2045,2452,7142 } },
-    { "OLYMPUS E-300", 0,
+    { "Olympus E-300", 0, 0,
 	{ 7828,-1761,-348,-5788,14071,1830,-2853,4518,6557 } },
-    { "OLYMPUS E-330", 0,
+    { "Olympus E-330", 0, 0,
 	{ 8961,-2473,-1084,-7979,15990,2067,-2319,3035,8249 } },
-    { "OLYMPUS E-3", 0,
+    { "Olympus E-30", 0, 0xfbc,
+	{ 8144,-1861,-1111,-7763,15894,1929,-1865,2542,7607 } },
+    { "Olympus E-3", 0, 0xf99,
 	{ 9487,-2875,-1115,-7533,15606,2010,-1618,2100,7389 } },
-    { "OLYMPUS E-400", 0,
+    { "Olympus E-400", 0, 0,
 	{ 6169,-1483,-21,-7107,14761,2536,-2904,3580,8568 } },
-    { "OLYMPUS E-410", 0,
+    { "Olympus E-410", 0, 0xf6a,
 	{ 8856,-2582,-1026,-7761,15766,2082,-2009,2575,7469 } },
-    { "OLYMPUS E-500", 0,
+    { "Olympus E-420", 0, 0xfd7,
+	{ 8746,-2425,-1095,-7594,15612,2073,-1780,2309,7416 } },
+    { "Olympus E-450", 0, 0xfd2,
+	{ 8745,-2425,-1095,-7594,15613,2073,-1780,2309,7416 } },
+    { "Olympus E-500", 0, 0,
 	{ 8136,-1968,-299,-5481,13742,1871,-2556,4205,6630 } },
-    { "OLYMPUS E-510", 0,
+    { "Olympus E-510", 0, 0xf6a,
 	{ 8785,-2529,-1033,-7639,15624,2112,-1783,2300,7817 } },
-    { "OLYMPUS SP350", 0,
+    { "Olympus E-520", 0, 0xfd2,
+	{ 8344,-2322,-1020,-7596,15635,2048,-1748,2269,7287 } },
+    { "Olympus E-5", 0, 0xeec,
+	{ 11200,-3783,-1325,-4576,12593,2206,-695,1742,7504 } },
+    { "Olympus E-600", 0, 0xfaf,
+	{ 8453,-2198,-1092,-7609,15681,2008,-1725,2337,7824 } },
+    { "Olympus E-620", 0, 0xfaf,
+	{ 8453,-2198,-1092,-7609,15681,2008,-1725,2337,7824 } },
+    { "Olympus E-P1", 0, 0xffd,
+	{ 8343,-2050,-1021,-7715,15705,2103,-1831,2380,8235 } },
+    { "Olympus E-P2", 0, 0xffd,
+	{ 8343,-2050,-1021,-7715,15705,2103,-1831,2380,8235 } },
+    { "Olympus E-P3", 0, 0,
+	{ 7575,-2159,-571,-3722,11341,2725,-1434,2819,6271 } },
+    { "Olympus E-P5", 0, 0,
+	{ 8380,-2630,-639,-2887,10725,2496,-627,1427,5438 } },
+    { "Olympus E-PL1s", 0, 0,
+	{ 11409,-3872,-1393,-4572,12757,2003,-709,1810,7415 } },
+    { "Olympus E-PL1", 0, 0,
+	{ 11408,-4289,-1215,-4286,12385,2118,-387,1467,7787 } },
+    { "Olympus E-PL2", 0, 0xcf3,
+	{ 15030,-5552,-1806,-3987,12387,1767,-592,1670,7023 } },
+    { "Olympus E-PL3", 0, 0,
+	{ 7575,-2159,-571,-3722,11341,2725,-1434,2819,6271 } },
+    { "Olympus E-PL5", 0, 0xfcb,
+	{ 8380,-2630,-639,-2887,10725,2496,-627,1427,5438 } },
+    { "Olympus E-PL6", 0, 0,
+	{ 8380,-2630,-639,-2887,10725,2496,-627,1427,5438 } },
+    { "Olympus E-PL7", 0, 0,
+	{ 9197,-3190,-659,-2606,10830,2039,-458,1250,5458 } },
+    { "Olympus E-PL8", 0, 0,
+	{ 9197,-3190,-659,-2606,10830,2039,-458,1250,5458 } },
+    { "Olympus E-PL9", 0, 0,
+	{ 8380,-2630,-639,-2887,10725,2496,-627,1427,5438 } },
+    { "Olympus E-PM1", 0, 0,
+	{ 7575,-2159,-571,-3722,11341,2725,-1434,2819,6271 } },
+    { "Olympus E-PM2", 0, 0,
+	{ 8380,-2630,-639,-2887,10725,2496,-627,1427,5438 } },
+    { "Olympus E-M10", 0, 0,	/* also E-M10 Mark II & III */
+	{ 8380,-2630,-639,-2887,10725,2496,-627,1427,5438 } },
+    { "Olympus E-M1Mark II", 0, 0,
+	{ 9383,-3170,-763,-2457,10702,2020,-384,1236,5552 } },
+    { "Olympus E-M1", 0, 0,
+	{ 7687,-1984,-606,-4327,11928,2721,-1381,2339,6452 } },
+    { "Olympus E-M5MarkII", 0, 0,
+	{ 9422,-3258,-711,-2655,10898,2015,-512,1354,5512 } },
+    { "Olympus E-M5", 0, 0xfe1,
+	{ 8380,-2630,-639,-2887,10725,2496,-627,1427,5438 } },
+    { "Olympus PEN-F", 0, 0,
+	{ 9476,-3182,-765,-2613,10958,1893,-449,1315,5268 } },
+    { "Olympus SH-2", 0, 0,
+	{ 10156,-3425,-1077,-2611,11177,1624,-385,1592,5080 } },
+    { "Olympus SP350", 0, 0,
 	{ 12078,-4836,-1069,-6671,14306,2578,-786,939,7418 } },
-    { "OLYMPUS SP3", 0,
+    { "Olympus SP3", 0, 0,
 	{ 11766,-4445,-1067,-6901,14421,2707,-1029,1217,7572 } },
-    { "OLYMPUS SP500UZ", 0,
+    { "Olympus SP500UZ", 0, 0xfff,
 	{ 9493,-3415,-666,-5211,12334,3260,-1548,2262,6482 } },
-    { "OLYMPUS SP510UZ", 0,
+    { "Olympus SP510UZ", 0, 0xffe,
 	{ 10593,-3607,-1010,-5881,13127,3084,-1200,1805,6721 } },
-    { "OLYMPUS SP550UZ", 0,
+    { "Olympus SP550UZ", 0, 0xffe,
 	{ 11597,-4006,-1049,-5432,12799,2957,-1029,1750,6516 } },
-    { "OLYMPUS SP560UZ", 0,
+    { "Olympus SP560UZ", 0, 0xff9,
 	{ 10915,-3677,-982,-5587,12986,2911,-1168,1968,6223 } },
-    { "PENTAX *ist DL2", 0,
+    { "Olympus SP570UZ", 0, 0,
+	{ 11522,-4044,-1146,-4736,12172,2904,-988,1829,6039 } },
+    { "Olympus STYLUS1", 0, 0,
+	{ 8360,-2420,-880,-3928,12353,1739,-1381,2416,5173 } },
+    { "Olympus TG-4", 0, 0,
+	{ 11426,-4159,-1126,-2066,10678,1593,-120,1327,4998 } },
+    { "Olympus TG-5", 0, 0,
+	{ 10899,-3833,-1082,-2112,10736,1575,-267,1452,5269 } },
+    { "Olympus XZ-10", 0, 0,
+	{ 9777,-3483,-925,-2886,11297,1800,-602,1663,5134 } },
+    { "Olympus XZ-1", 0, 0,
+	{ 10901,-4095,-1074,-1141,9208,2293,-62,1417,5158 } },
+    { "Olympus XZ-2", 0, 0,
+	{ 9777,-3483,-925,-2886,11297,1800,-602,1663,5134 } },
+    { "OmniVision", 0, 0,		/* DJC */
+	{ 12782,-4059,-379,-478,9066,1413,1340,1513,5176 } },
+    { "Pentax *ist DL2", 0, 0,
 	{ 10504,-2438,-1189,-8603,16207,2531,-1022,863,12242 } },
-    { "PENTAX *ist DL", 0,
+    { "Pentax *ist DL", 0, 0,
 	{ 10829,-2838,-1115,-8339,15817,2696,-837,680,11939 } },
-    { "PENTAX *ist DS2", 0,
+    { "Pentax *ist DS2", 0, 0,
 	{ 10504,-2438,-1189,-8603,16207,2531,-1022,863,12242 } },
-    { "PENTAX *ist DS", 0,
+    { "Pentax *ist DS", 0, 0,
 	{ 10371,-2333,-1206,-8688,16231,2602,-1230,1116,11282 } },
-    { "PENTAX *ist D", 0,
+    { "Pentax *ist D", 0, 0,
 	{ 9651,-2059,-1189,-8881,16512,2487,-1460,1345,10687 } },
-    { "PENTAX K10D", 0,
+    { "Pentax K10D", 0, 0,
 	{ 9566,-2863,-803,-7170,15172,2112,-818,803,9705 } },
-    { "PENTAX K1", 0,
+    { "Pentax K1", 0, 0,
 	{ 11095,-3157,-1324,-8377,15834,2720,-1108,947,11688 } },
-    { "Panasonic DMC-FZ8", 0,
+    { "Pentax K20D", 0, 0,
+	{ 9427,-2714,-868,-7493,16092,1373,-2199,3264,7180 } },
+    { "Pentax K200D", 0, 0,
+	{ 9186,-2678,-907,-8693,16517,2260,-1129,1094,8524 } },
+    { "Pentax K2000", 0, 0,
+	{ 11057,-3604,-1155,-5152,13046,2329,-282,375,8104 } },
+    { "Pentax K-m", 0, 0,
+	{ 11057,-3604,-1155,-5152,13046,2329,-282,375,8104 } },
+    { "Pentax K-x", 0, 0,
+	{ 8843,-2837,-625,-5025,12644,2668,-411,1234,7410 } },
+    { "Pentax K-r", 0, 0,
+	{ 9895,-3077,-850,-5304,13035,2521,-883,1768,6936 } },
+    { "Pentax K-1", 0, 0,
+	{ 8596,-2981,-639,-4202,12046,2431,-685,1424,6122 } },
+    { "Pentax K-30", 0, 0,
+	{ 8710,-2632,-1167,-3995,12301,1881,-981,1719,6535 } },
+    { "Pentax K-3 II", 0, 0,
+	{ 8626,-2607,-1155,-3995,12301,1881,-1039,1822,6925 } },
+    { "Pentax K-3", 0, 0,
+	{ 7415,-2052,-721,-5186,12788,2682,-1446,2157,6773 } },
+    { "Pentax K-5 II", 0, 0,
+	{ 8170,-2725,-639,-4440,12017,2744,-771,1465,6599 } },
+    { "Pentax K-5", 0, 0,
+	{ 8713,-2833,-743,-4342,11900,2772,-722,1543,6247 } },
+    { "Pentax K-70", 0, 0,
+	{ 8270,-2117,-1299,-4359,12953,1515,-1078,1933,5975 } },
+    { "Pentax K-7", 0, 0,
+	{ 9142,-2947,-678,-8648,16967,1663,-2224,2898,8615 } },
+    { "Pentax K-S1", 0, 0,
+	{ 8512,-3211,-787,-4167,11966,2487,-638,1288,6054 } },
+    { "Pentax K-S2", 0, 0,
+	{ 8662,-3280,-798,-3928,11771,2444,-586,1232,6054 } },
+    { "Pentax KP", 0, 0,
+	{ 8617,-3228,-1034,-4674,12821,2044,-803,1577,5728 } },
+    { "Pentax Q-S1", 0, 0,
+	{ 12995,-5593,-1107,-1879,10139,2027,-64,1233,4919 } },
+    { "Pentax 645D", 0, 0x3e00,
+	{ 10646,-3593,-1158,-3329,11699,1831,-667,2874,6287 } },
+    { "Panasonic DMC-CM1", 15, 0,
+	{ 8770,-3194,-820,-2871,11281,1803,-513,1552,4434 } },
+    { "Panasonic DC-FZ80", 0, 0,
+	{ 8550,-2908,-842,-3195,11529,1881,-338,1603,4631 } },
+    { "Panasonic DMC-FZ8", 0, 0xf7f,
 	{ 8986,-2755,-802,-6341,13575,3077,-1476,2144,6379 } },
-    { "Panasonic DMC-FZ18", 0,
+    { "Panasonic DMC-FZ18", 0, 0,
 	{ 9932,-3060,-935,-5809,13331,2753,-1267,2155,5575 } },
-    { "Panasonic DMC-FZ30", 0,
+    { "Panasonic DMC-FZ28", 15, 0xf96,
+	{ 10109,-3488,-993,-5412,12812,2916,-1305,2140,5543 } },
+    { "Panasonic DMC-FZ2500", 15, 0,
+	{ 7386,-2443,-743,-3437,11864,1757,-608,1660,4766 } },
+    { "Panasonic DMC-FZ330", 15, 0,
+	{ 8378,-2798,-769,-3068,11410,1877,-538,1792,4623 } },
+    { "Panasonic DMC-FZ300", 15, 0,
+	{ 8378,-2798,-769,-3068,11410,1877,-538,1792,4623 } },
+    { "Panasonic DMC-FZ30", 0, 0xf94,
 	{ 10976,-4029,-1141,-7918,15491,2600,-1670,2071,8246 } },
-    { "Panasonic DMC-FZ50", 0,	/* aka "LEICA V-LUX1" */
+    { "Panasonic DMC-FZ3", 15, 0,	/* FZ35, FZ38 */
+	{ 9938,-2780,-890,-4604,12393,2480,-1117,2304,4620 } },
+    { "Panasonic DMC-FZ4", 15, 0,	/* FZ40, FZ45 */
+	{ 13639,-5535,-1371,-1698,9633,2430,316,1152,4108 } },
+    { "Panasonic DMC-FZ50", 0, 0,
+	{ 7906,-2709,-594,-6231,13351,3220,-1922,2631,6537 } },
+    { "Panasonic DMC-FZ7", 15, 0,	/* FZ70, FZ72 */
+	{ 11532,-4324,-1066,-2375,10847,1749,-564,1699,4351 } },
+    { "Leica V-LUX1", 0, 0,
 	{ 7906,-2709,-594,-6231,13351,3220,-1922,2631,6537 } },
-    { "Panasonic DMC-L10", 0,
+    { "Panasonic DMC-L10", 15, 0xf96,
 	{ 8025,-1942,-1050,-7920,15904,2100,-2456,3005,7039 } },
-    { "Panasonic DMC-L1", 0,	/* aka "LEICA DIGILUX 3" */
+    { "Panasonic DMC-L1", 0, 0xf7f,
 	{ 8054,-1885,-1025,-8349,16367,2040,-2805,3542,7629 } },
-    { "Panasonic DMC-LC1", 0,	/* aka "LEICA DIGILUX 2" */
+    { "Leica DIGILUX 3", 0, 0xf7f,
+	{ 8054,-1885,-1025,-8349,16367,2040,-2805,3542,7629 } },
+    { "Panasonic DMC-LC1", 0, 0,
+	{ 11340,-4069,-1275,-7555,15266,2448,-2960,3426,7685 } },
+    { "Leica DIGILUX 2", 0, 0,
 	{ 11340,-4069,-1275,-7555,15266,2448,-2960,3426,7685 } },
-    { "Panasonic DMC-LX1", 0,	/* aka "LEICA D-LUX2" */
+    { "Panasonic DMC-LX100", 15, 0,
+	{ 8844,-3538,-768,-3709,11762,2200,-698,1792,5220 } },
+    { "Leica D-LUX (Typ 109)", 15, 0,
+	{ 8844,-3538,-768,-3709,11762,2200,-698,1792,5220 } },
+    { "Panasonic DMC-LF1", 15, 0,
+	{ 9379,-3267,-816,-3227,11560,1881,-926,1928,5340 } },
+    { "Leica C (Typ 112)", 15, 0,
+	{ 9379,-3267,-816,-3227,11560,1881,-926,1928,5340 } },
+    { "Panasonic DMC-LX1", 0, 0xf7f,
 	{ 10704,-4187,-1230,-8314,15952,2501,-920,945,8927 } },
-    { "Panasonic DMC-LX2", 0,	/* aka "LEICA D-LUX3" */
+    { "Leica D-LUX2", 0, 0xf7f,
+	{ 10704,-4187,-1230,-8314,15952,2501,-920,945,8927 } },
+    { "Panasonic DMC-LX2", 0, 0,
+	{ 8048,-2810,-623,-6450,13519,3272,-1700,2146,7049 } },
+    { "Leica D-LUX3", 0, 0,
 	{ 8048,-2810,-623,-6450,13519,3272,-1700,2146,7049 } },
-    { "Phase One H 20", 0,	/* DJC */
+    { "Panasonic DMC-LX3", 15, 0,
+	{ 8128,-2668,-655,-6134,13307,3161,-1782,2568,6083 } },
+    { "Leica D-LUX 4", 15, 0,
+	{ 8128,-2668,-655,-6134,13307,3161,-1782,2568,6083 } },
+    { "Panasonic DMC-LX5", 15, 0,
+	{ 10909,-4295,-948,-1333,9306,2399,22,1738,4582 } },
+    { "Leica D-LUX 5", 15, 0,
+	{ 10909,-4295,-948,-1333,9306,2399,22,1738,4582 } },
+    { "Panasonic DMC-LX7", 15, 0,
+	{ 10148,-3743,-991,-2837,11366,1659,-701,1893,4899 } },
+    { "Leica D-LUX 6", 15, 0,
+	{ 10148,-3743,-991,-2837,11366,1659,-701,1893,4899 } },
+    { "Panasonic DMC-LX9", 15, 0,
+	{ 7790,-2736,-755,-3452,11870,1769,-628,1647,4898 } },
+    { "Panasonic DMC-FZ1000", 15, 0,
+	{ 7830,-2696,-763,-3325,11667,1866,-641,1712,4824 } },
+    { "Leica V-LUX (Typ 114)", 15, 0,
+	{ 7830,-2696,-763,-3325,11667,1866,-641,1712,4824 } },
+    { "Panasonic DMC-FZ100", 15, 0xfff,
+	{ 16197,-6146,-1761,-2393,10765,1869,366,2238,5248 } },
+    { "Leica V-LUX 2", 15, 0xfff,
+	{ 16197,-6146,-1761,-2393,10765,1869,366,2238,5248 } },
+    { "Panasonic DMC-FZ150", 15, 0xfff,
+	{ 11904,-4541,-1189,-2355,10899,1662,-296,1586,4289 } },
+    { "Leica V-LUX 3", 15, 0xfff,
+	{ 11904,-4541,-1189,-2355,10899,1662,-296,1586,4289 } },
+    { "Panasonic DMC-FZ200", 15, 0xfff,
+	{ 8112,-2563,-740,-3730,11784,2197,-941,2075,4933 } },
+    { "Leica V-LUX 4", 15, 0xfff,
+	{ 8112,-2563,-740,-3730,11784,2197,-941,2075,4933 } },
+    { "Panasonic DMC-FX150", 15, 0xfff,
+	{ 9082,-2907,-925,-6119,13377,3058,-1797,2641,5609 } },
+    { "Panasonic DMC-G10", 0, 0,
+	{ 10113,-3400,-1114,-4765,12683,2317,-377,1437,6710 } },
+    { "Panasonic DMC-G1", 15, 0xf94,
+	{ 8199,-2065,-1056,-8124,16156,2033,-2458,3022,7220 } },
+    { "Panasonic DMC-G2", 15, 0xf3c,
+	{ 10113,-3400,-1114,-4765,12683,2317,-377,1437,6710 } },
+    { "Panasonic DMC-G3", 15, 0xfff,
+	{ 6763,-1919,-863,-3868,11515,2684,-1216,2387,5879 } },
+    { "Panasonic DMC-G5", 15, 0xfff,
+	{ 7798,-2562,-740,-3879,11584,2613,-1055,2248,5434 } },
+    { "Panasonic DMC-G6", 15, 0xfff,
+	{ 8294,-2891,-651,-3869,11590,2595,-1183,2267,5352 } },
+    { "Panasonic DMC-G7", 15, 0xfff,
+	{ 7610,-2780,-576,-4614,12195,2733,-1375,2393,6490 } },
+    { "Panasonic DMC-G8", 15, 0xfff,	/* G8, G80, G81, G85 */
+	{ 7610,-2780,-576,-4614,12195,2733,-1375,2393,6490 } },
+    { "Panasonic DC-G9", 15, 0xfff,
+	{ 7685,-2375,-634,-3687,11700,2249,-748,1546,5111 } },
+    { "Panasonic DMC-GF1", 15, 0xf92,
+	{ 7888,-1902,-1011,-8106,16085,2099,-2353,2866,7330 } },
+    { "Panasonic DMC-GF2", 15, 0xfff,
+	{ 7888,-1902,-1011,-8106,16085,2099,-2353,2866,7330 } },
+    { "Panasonic DMC-GF3", 15, 0xfff,
+	{ 9051,-2468,-1204,-5212,13276,2121,-1197,2510,6890 } },
+    { "Panasonic DMC-GF5", 15, 0xfff,
+	{ 8228,-2945,-660,-3938,11792,2430,-1094,2278,5793 } },
+    { "Panasonic DMC-GF6", 15, 0,
+	{ 8130,-2801,-946,-3520,11289,2552,-1314,2511,5791 } },
+    { "Panasonic DMC-GF7", 15, 0,
+	{ 7610,-2780,-576,-4614,12195,2733,-1375,2393,6490 } },
+    { "Panasonic DMC-GF8", 15, 0,
+	{ 7610,-2780,-576,-4614,12195,2733,-1375,2393,6490 } },
+    { "Panasonic DC-GF9", 15, 0,
+	{ 7610,-2780,-576,-4614,12195,2733,-1375,2393,6490 } },
+    { "Panasonic DMC-GH1", 15, 0xf92,
+	{ 6299,-1466,-532,-6535,13852,2969,-2331,3112,5984 } },
+    { "Panasonic DMC-GH2", 15, 0xf95,
+	{ 7780,-2410,-806,-3913,11724,2484,-1018,2390,5298 } },
+    { "Panasonic DMC-GH3", 15, 0,
+	{ 6559,-1752,-491,-3672,11407,2586,-962,1875,5130 } },
+    { "Panasonic DMC-GH4", 15, 0,
+	{ 7122,-2108,-512,-3155,11201,2231,-541,1423,5045 } },
+    { "Panasonic DC-GH5S", 15, 0,
+	{ 6929,-2355,-708,-4192,12534,1828,-1097,1989,5195 } },
+    { "Panasonic DC-GH5", 15, 0,
+	{ 7641,-2336,-605,-3218,11299,2187,-485,1338,5121 } },
+    { "Panasonic DMC-GM1", 15, 0,
+	{ 6770,-1895,-744,-5232,13145,2303,-1664,2691,5703 } },
+    { "Panasonic DMC-GM5", 15, 0,
+	{ 8238,-3244,-679,-3921,11814,2384,-836,2022,5852 } },
+    { "Panasonic DMC-GX1", 15, 0,
+	{ 6763,-1919,-863,-3868,11515,2684,-1216,2387,5879 } },
+    { "Panasonic DMC-GX7", 15, 0,
+	{ 7610,-2780,-576,-4614,12195,2733,-1375,2393,6490 } },
+    { "Panasonic DMC-GX85", 15, 0,
+	{ 7771,-3020,-629,-4029,11950,2345,-821,1977,6119 } },
+    { "Panasonic DMC-GX8", 15, 0,
+	{ 7564,-2263,-606,-3148,11239,2177,-540,1435,4853 } },
+    { "Panasonic DC-GX9", 15, 0,
+	{ 7564,-2263,-606,-3148,11239,2177,-540,1435,4853 } },
+    { "Panasonic DMC-ZS100", 15, 0,
+	{ 7790,-2736,-755,-3452,11870,1769,-628,1647,4898 } },
+    { "Panasonic DC-ZS200", 15, 0,
+	{ 7790,-2736,-755,-3452,11870,1769,-628,1647,4898 } },
+    { "Panasonic DMC-ZS40", 15, 0,
+	{ 8607,-2822,-808,-3755,11930,2049,-820,2060,5224 } },
+    { "Panasonic DMC-ZS50", 15, 0,
+	{ 8802,-3135,-789,-3151,11468,1904,-550,1745,4810 } },
+    { "Panasonic DMC-TZ82", 15, 0,
+	{ 8550,-2908,-842,-3195,11529,1881,-338,1603,4631 } },
+    { "Panasonic DMC-ZS6", 15, 0,
+	{ 8550,-2908,-842,-3195,11529,1881,-338,1603,4631 } },
+    { "Panasonic DMC-ZS70", 15, 0,
+	{ 9052,-3117,-883,-3045,11346,1927,-205,1520,4730 } },
+    { "Leica S (Typ 007)", 0, 0,
+	{ 6063,-2234,-231,-5210,13787,1500,-1043,2866,6997 } },
+    { "Leica X", 0, 0,		/* X and X-U, both (Typ 113) */
+	{ 7712,-2059,-653,-3882,11494,2726,-710,1332,5958 } },
+    { "Leica Q (Typ 116)", 0, 0,
+	{ 11865,-4523,-1441,-5423,14458,935,-1587,2687,4830 } },
+    { "Leica M (Typ 262)", 0, 0,
+	{ 6653,-1486,-611,-4221,13303,929,-881,2416,7226 } },
+    { "Leica SL (Typ 601)", 0, 0,
+	{ 11865,-4523,-1441,-5423,14458,935,-1587,2687,4830 } },
+    { "Leica TL2", 0, 0,
+	{ 5836,-1626,-647,-5384,13326,2261,-1207,2129,5861 } },
+    { "Leica TL", 0, 0,
+	{ 5463,-988,-364,-4634,12036,2946,-766,1389,6522 } },
+    { "Leica CL", 0, 0,
+	{ 7414,-2393,-840,-5127,13180,2138,-1585,2468,5064 } },
+    { "Leica M10", 0, 0,
+	{ 8249,-2849,-620,-5415,14756,565,-957,3074,6517 } },
+    { "Phase One H 20", 0, 0,		/* DJC */
 	{ 1313,1855,-109,-6715,15908,808,-327,1840,6020 } },
-    { "Phase One P 2", 0,
+    { "Phase One H 25", 0, 0,
 	{ 2905,732,-237,-8134,16626,1476,-3038,4253,7517 } },
-    { "Phase One P 30", 0,
+    { "Phase One P 2", 0, 0,
+	{ 2905,732,-237,-8134,16626,1476,-3038,4253,7517 } },
+    { "Phase One P 30", 0, 0,
 	{ 4516,-245,-37,-7020,14976,2173,-3206,4671,7087 } },
-    { "Phase One P 45", 0,
+    { "Phase One P 45", 0, 0,
 	{ 5053,-24,-117,-5684,14076,1702,-2619,4492,5849 } },
-    { "SAMSUNG GX-1", 0,
+    { "Phase One P40", 0, 0,
+	{ 8035,435,-962,-6001,13872,2320,-1159,3065,5434 } },
+    { "Phase One P65", 0, 0,
+	{ 8035,435,-962,-6001,13872,2320,-1159,3065,5434 } },
+    { "Photron BC2-HD", 0, 0,		/* DJC */
+	{ 14603,-4122,-528,-1810,9794,2017,-297,2763,5936 } },
+    { "Red One", 704, 0xffff,		/* DJC */
+	{ 21014,-7891,-2613,-3056,12201,856,-2203,5125,8042 } },
+    { "Ricoh GR II", 0, 0,
+	{ 4630,-834,-423,-4977,12805,2417,-638,1467,6115 } },
+    { "Ricoh GR", 0, 0,
+	{ 3708,-543,-160,-5381,12254,3556,-1471,1929,8234 } },
+    { "Samsung EX1", 0, 0x3e00,
+	{ 8898,-2498,-994,-3144,11328,2066,-760,1381,4576 } },
+    { "Samsung EX2F", 0, 0x7ff,
+	{ 10648,-3897,-1055,-2022,10573,1668,-492,1611,4742 } },
+    { "Samsung EK-GN120", 0, 0,
+	{ 7557,-2522,-739,-4679,12949,1894,-840,1777,5311 } },
+    { "Samsung NX mini", 0, 0,
+	{ 5222,-1196,-550,-6540,14649,2009,-1666,2819,5657 } },
+    { "Samsung NX3300", 0, 0,
+	{ 8060,-2933,-761,-4504,12890,1762,-630,1489,5227 } },
+    { "Samsung NX3000", 0, 0,
+	{ 8060,-2933,-761,-4504,12890,1762,-630,1489,5227 } },
+    { "Samsung NX30", 0, 0,	/* NX30, NX300, NX300M */
+	{ 7557,-2522,-739,-4679,12949,1894,-840,1777,5311 } },
+    { "Samsung NX2000", 0, 0,
+	{ 7557,-2522,-739,-4679,12949,1894,-840,1777,5311 } },
+    { "Samsung NX2", 0, 0xfff,	/* NX20, NX200, NX210 */
+	{ 6933,-2268,-753,-4921,13387,1647,-803,1641,6096 } },
+    { "Samsung NX1000", 0, 0,
+	{ 6933,-2268,-753,-4921,13387,1647,-803,1641,6096 } },
+    { "Samsung NX1100", 0, 0,
+	{ 6933,-2268,-753,-4921,13387,1647,-803,1641,6096 } },
+    { "Samsung NX11", 0, 0,
+	{ 10332,-3234,-1168,-6111,14639,1520,-1352,2647,8331 } },
+    { "Samsung NX10", 0, 0,	/* also NX100 */
+	{ 10332,-3234,-1168,-6111,14639,1520,-1352,2647,8331 } },
+    { "Samsung NX500", 0, 0,
+	{ 10686,-4042,-1052,-3595,13238,276,-464,1259,5931 } },
+    { "Samsung NX5", 0, 0,
+	{ 10332,-3234,-1168,-6111,14639,1520,-1352,2647,8331 } },
+    { "Samsung NX1", 0, 0,
+	{ 10686,-4042,-1052,-3595,13238,276,-464,1259,5931 } },
+    { "Samsung WB2000", 0, 0xfff,
+	{ 12093,-3557,-1155,-1000,9534,1733,-22,1787,4576 } },
+    { "Samsung GX-1", 0, 0,
 	{ 10504,-2438,-1189,-8603,16207,2531,-1022,863,12242 } },
-    { "Sinar", 0,		/* DJC */
+    { "Samsung GX20", 0, 0,	/* copied from Pentax K20D */
+	{ 9427,-2714,-868,-7493,16092,1373,-2199,3264,7180 } },
+    { "Samsung S85", 0, 0,		/* DJC */
+	{ 11885,-3968,-1473,-4214,12299,1916,-835,1655,5549 } },
+    { "Sinar", 0, 0,			/* DJC */
 	{ 16442,-2956,-2422,-2877,12128,750,-1136,6066,4559 } },
-    { "SONY DSC-F828", 491,
+    { "Sony DSC-F828", 0, 0,
 	{ 7924,-1910,-777,-8226,15459,2998,-1517,2199,6818,-7242,11401,3481 } },
-    { "SONY DSC-R1", 512,
+    { "Sony DSC-R1", 0, 0,
 	{ 8512,-2641,-694,-8042,15670,2526,-1821,2117,7414 } },
-    { "SONY DSC-V3", 0,
+    { "Sony DSC-V3", 0, 0,
 	{ 7511,-2571,-692,-7894,15088,3060,-948,1111,8128 } },
-    { "SONY DSLR-A100", 0,
+    { "Sony DSC-RX100M", 0, 0,		/* M2, M3, M4, and M5 */
+	{ 6596,-2079,-562,-4782,13016,1933,-970,1581,5181 } },
+    { "Sony DSC-RX100", 0, 0,
+	{ 8651,-2754,-1057,-3464,12207,1373,-568,1398,4434 } },
+    { "Sony DSC-RX10M4", 0, 0,
+	{ 7699,-2566,-629,-2967,11270,1928,-378,1286,4807 } },
+    { "Sony DSC-RX10", 0, 0,		/* also RX10M2, RX10M3 */
+	{ 6679,-1825,-745,-5047,13256,1953,-1580,2422,5183 } },
+    { "Sony DSC-RX1RM2", 0, 0,
+	{ 6629,-1900,-483,-4618,12349,2550,-622,1381,6514 } },
+    { "Sony DSC-RX1", 0, 0,
+	{ 6344,-1612,-462,-4863,12477,2681,-865,1786,6899 } },
+    { "Sony DSC-RX0", 200, 0,
+	{ 9396,-3507,-843,-2497,11111,1572,-343,1355,5089 } },
+    { "Sony DSLR-A100", 0, 0xfeb,
 	{ 9437,-2811,-774,-8405,16215,2290,-710,596,7181 } },
-    { "SONY DSLR-A200", 0,
+    { "Sony DSLR-A290", 0, 0,
+	{ 6038,-1484,-579,-9145,16746,2512,-875,746,7218 } },
+    { "Sony DSLR-A2", 0, 0,
 	{ 9847,-3091,-928,-8485,16345,2225,-715,595,7103 } },
-    { "SONY DSLR-A350", 0,	/* copied from above */
+    { "Sony DSLR-A300", 0, 0,
 	{ 9847,-3091,-928,-8485,16345,2225,-715,595,7103 } },
-    { "SONY DSLR-A700", 254,
-	{ 5775,-805,-359,-8574,16295,2391,-1943,2341,7249 } }
+    { "Sony DSLR-A330", 0, 0,
+	{ 9847,-3091,-929,-8485,16346,2225,-714,595,7103 } },
+    { "Sony DSLR-A350", 0, 0xffc,
+	{ 6038,-1484,-578,-9146,16746,2513,-875,746,7217 } },
+    { "Sony DSLR-A380", 0, 0,
+	{ 6038,-1484,-579,-9145,16746,2512,-875,746,7218 } },
+    { "Sony DSLR-A390", 0, 0,
+	{ 6038,-1484,-579,-9145,16746,2512,-875,746,7218 } },
+    { "Sony DSLR-A450", 0, 0xfeb,
+	{ 4950,-580,-103,-5228,12542,3029,-709,1435,7371 } },
+    { "Sony DSLR-A580", 0, 0xfeb,
+	{ 5932,-1492,-411,-4813,12285,2856,-741,1524,6739 } },
+    { "Sony DSLR-A500", 0, 0xfeb,
+	{ 6046,-1127,-278,-5574,13076,2786,-691,1419,7625 } },
+    { "Sony DSLR-A5", 0, 0xfeb,
+	{ 4950,-580,-103,-5228,12542,3029,-709,1435,7371 } },
+    { "Sony DSLR-A700", 0, 0,
+	{ 5775,-805,-359,-8574,16295,2391,-1943,2341,7249 } },
+    { "Sony DSLR-A850", 0, 0,
+	{ 5413,-1162,-365,-5665,13098,2866,-608,1179,8440 } },
+    { "Sony DSLR-A900", 0, 0,
+	{ 5209,-1072,-397,-8845,16120,2919,-1618,1803,8654 } },
+    { "Sony ILCA-68", 0, 0,
+	{ 6435,-1903,-536,-4722,12449,2550,-663,1363,6517 } },
+    { "Sony ILCA-77M2", 0, 0,
+	{ 5991,-1732,-443,-4100,11989,2381,-704,1467,5992 } },
+    { "Sony ILCA-99M2", 0, 0,
+	{ 6660,-1918,-471,-4613,12398,2485,-649,1433,6447 } },
+    { "Sony ILCE-6", 0, 0,		/* 6300, 6500 */
+	{ 5973,-1695,-419,-3826,11797,2293,-639,1398,5789 } },
+    { "Sony ILCE-7M2", 0, 0,
+	{ 5271,-712,-347,-6153,13653,2763,-1601,2366,7242 } },
+    { "Sony ILCE-7M3", 0, 0,
+	{ 7374,-2389,-551,-5435,13162,2519,-1006,1795,6552 } },
+    { "Sony ILCE-7S", 0, 0,	/* also ILCE-7SM2 */
+	{ 5838,-1430,-246,-3497,11477,2297,-748,1885,5778 } },
+    { "Sony ILCE-7RM3", 0, 0,
+	{ 6640,-1847,-503,-5238,13010,2474,-993,1673,6527 } },
+    { "Sony ILCE-7RM2", 0, 0,
+	{ 6629,-1900,-483,-4618,12349,2550,-622,1381,6514 } },
+    { "Sony ILCE-7R", 0, 0,
+	{ 4913,-541,-202,-6130,13513,2906,-1564,2151,7183 } },
+    { "Sony ILCE-7", 0, 0,
+	{ 5271,-712,-347,-6153,13653,2763,-1601,2366,7242 } },
+    { "Sony ILCE-9", 0, 0,
+	{ 6389,-1703,-378,-4562,12265,2587,-670,1489,6550 } },
+    { "Sony ILCE", 0, 0,	/* 3000, 5000, 5100, 6000, and QX1 */
+	{ 5991,-1456,-455,-4764,12135,2980,-707,1425,6701 } },
+    { "Sony NEX-5N", 0, 0,
+	{ 5991,-1456,-455,-4764,12135,2980,-707,1425,6701 } },
+    { "Sony NEX-5R", 0, 0,
+	{ 6129,-1545,-418,-4930,12490,2743,-977,1693,6615 } },
+    { "Sony NEX-5T", 0, 0,
+	{ 6129,-1545,-418,-4930,12490,2743,-977,1693,6615 } },
+    { "Sony NEX-3N", 0, 0,
+	{ 6129,-1545,-418,-4930,12490,2743,-977,1693,6615 } },
+    { "Sony NEX-3", 138, 0,		/* DJC */
+	{ 6907,-1256,-645,-4940,12621,2320,-1710,2581,6230 } },
+    { "Sony NEX-5", 116, 0,		/* DJC */
+	{ 6807,-1350,-342,-4216,11649,2567,-1089,2001,6420 } },
+    { "Sony NEX-3", 0, 0,		/* Adobe */
+	{ 6549,-1550,-436,-4880,12435,2753,-854,1868,6976 } },
+    { "Sony NEX-5", 0, 0,		/* Adobe */
+	{ 6549,-1550,-436,-4880,12435,2753,-854,1868,6976 } },
+    { "Sony NEX-6", 0, 0,
+	{ 6129,-1545,-418,-4930,12490,2743,-977,1693,6615 } },
+    { "Sony NEX-7", 0, 0,
+	{ 5491,-1192,-363,-4951,12342,2948,-911,1722,7192 } },
+    { "Sony NEX", 0, 0,	/* NEX-C3, NEX-F3 */
+	{ 5991,-1456,-455,-4764,12135,2980,-707,1425,6701 } },
+    { "Sony SLT-A33", 0, 0,
+	{ 6069,-1221,-366,-5221,12779,2734,-1024,2066,6834 } },
+    { "Sony SLT-A35", 0, 0,
+	{ 5986,-1618,-415,-4557,11820,3120,-681,1404,6971 } },
+    { "Sony SLT-A37", 0, 0,
+	{ 5991,-1456,-455,-4764,12135,2980,-707,1425,6701 } },
+    { "Sony SLT-A55", 0, 0,
+	{ 5932,-1492,-411,-4813,12285,2856,-741,1524,6739 } },
+    { "Sony SLT-A57", 0, 0,
+	{ 5991,-1456,-455,-4764,12135,2980,-707,1425,6701 } },
+    { "Sony SLT-A58", 0, 0,
+	{ 5991,-1456,-455,-4764,12135,2980,-707,1425,6701 } },
+    { "Sony SLT-A65", 0, 0,
+	{ 5491,-1192,-363,-4951,12342,2948,-911,1722,7192 } },
+    { "Sony SLT-A77", 0, 0,
+	{ 5491,-1192,-363,-4951,12342,2948,-911,1722,7192 } },
+    { "Sony SLT-A99", 0, 0,
+	{ 6344,-1612,-462,-4863,12477,2681,-865,1786,6899 } },
+    { "YI M1", 0, 0,
+	{ 7712,-2059,-653,-3882,11494,2726,-710,1332,5958 } },
   };
   double cam_xyz[4][3];
   char name[130];
@@ -6388,11 +8267,13 @@
   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))) {
-      if (table[i].black)
-	black = table[i].black;
-      for (j=0; j < 12; j++)
-	cam_xyz[0][j] = table[i].trans[j] / 10000.0;
-      cam_xyz_coeff (cam_xyz);
+      if (table[i].black)   black   = (ushort) table[i].black;
+      if (table[i].maximum) maximum = (ushort) table[i].maximum;
+      if (table[i].trans[0]) {
+	for (raw_color = j=0; j < 12; j++)
+	  ((double *)cam_xyz)[j] = table[i].trans[j] / 10000.0;
+	cam_xyz_coeff (rgb_cam, cam_xyz);
+      }
       break;
     }
 }
@@ -6436,100 +8317,336 @@
   return sum[0] < sum[1] ? 0x4d4d : 0x4949;
 }
 
+float CLASS find_green (int bps, int bite, int off0, int off1)
+{
+  UINT64 bitbuf=0;
+  int vbits, col, i, c;
+  ushort img[2][2064];
+  double sum[]={0,0};
+
+  FORC(2) {
+    fseek (ifp, c ? off1:off0, SEEK_SET);
+    for (vbits=col=0; col < width; col++) {
+      for (vbits -= bps; vbits < 0; vbits += bite) {
+	bitbuf <<= bite;
+	for (i=0; i < bite; i+=8)
+	  bitbuf |= (unsigned) (fgetc(ifp) << i);
+      }
+      img[c][col] = bitbuf << (64-bps-vbits) >> (64-bps);
+    }
+  }
+  FORC(width-1) {
+    sum[ c & 1] += ABS(img[0][c]-img[1][c+1]);
+    sum[~c & 1] += ABS(img[1][c]-img[0][c+1]);
+  }
+  return 100 * log(sum[0]/sum[1]);
+}
+
 /*
    Identify which camera created this file, and set global variables
    accordingly.
  */
 void CLASS identify()
 {
-  char head[32], *cp;
-  unsigned hlen, fsize, i, c, is_canon;
-  struct jhead jh;
+  static const short pana[][6] = {
+    { 3130, 1743,  4,  0, -6,  0 },
+    { 3130, 2055,  4,  0, -6,  0 },
+    { 3130, 2319,  4,  0, -6,  0 },
+    { 3170, 2103, 18,  0,-42, 20 },
+    { 3170, 2367, 18, 13,-42,-21 },
+    { 3177, 2367,  0,  0, -1,  0 },
+    { 3304, 2458,  0,  0, -1,  0 },
+    { 3330, 2463,  9,  0, -5,  0 },
+    { 3330, 2479,  9,  0,-17,  4 },
+    { 3370, 1899, 15,  0,-44, 20 },
+    { 3370, 2235, 15,  0,-44, 20 },
+    { 3370, 2511, 15, 10,-44,-21 },
+    { 3690, 2751,  3,  0, -8, -3 },
+    { 3710, 2751,  0,  0, -3,  0 },
+    { 3724, 2450,  0,  0,  0, -2 },
+    { 3770, 2487, 17,  0,-44, 19 },
+    { 3770, 2799, 17, 15,-44,-19 },
+    { 3880, 2170,  6,  0, -6,  0 },
+    { 4060, 3018,  0,  0,  0, -2 },
+    { 4290, 2391,  3,  0, -8, -1 },
+    { 4330, 2439, 17, 15,-44,-19 },
+    { 4508, 2962,  0,  0, -3, -4 },
+    { 4508, 3330,  0,  0, -3, -6 },
+  };
+  static const ushort canon[][11] = {
+    { 1944, 1416,   0,  0, 48,  0 },
+    { 2144, 1560,   4,  8, 52,  2, 0, 0, 0, 25 },
+    { 2224, 1456,  48,  6,  0,  2 },
+    { 2376, 1728,  12,  6, 52,  2 },
+    { 2672, 1968,  12,  6, 44,  2 },
+    { 3152, 2068,  64, 12,  0,  0, 16 },
+    { 3160, 2344,  44, 12,  4,  4 },
+    { 3344, 2484,   4,  6, 52,  6 },
+    { 3516, 2328,  42, 14,  0,  0 },
+    { 3596, 2360,  74, 12,  0,  0 },
+    { 3744, 2784,  52, 12,  8, 12 },
+    { 3944, 2622,  30, 18,  6,  2 },
+    { 3948, 2622,  42, 18,  0,  2 },
+    { 3984, 2622,  76, 20,  0,  2, 14 },
+    { 4104, 3048,  48, 12, 24, 12 },
+    { 4116, 2178,   4,  2,  0,  0 },
+    { 4152, 2772, 192, 12,  0,  0 },
+    { 4160, 3124, 104, 11,  8, 65 },
+    { 4176, 3062,  96, 17,  8,  0, 0, 16, 0, 7, 0x49 },
+    { 4192, 3062,  96, 17, 24,  0, 0, 16, 0, 0, 0x49 },
+    { 4312, 2876,  22, 18,  0,  2 },
+    { 4352, 2874,  62, 18,  0,  0 },
+    { 4476, 2954,  90, 34,  0,  0 },
+    { 4480, 3348,  12, 10, 36, 12, 0, 0, 0, 18, 0x49 },
+    { 4480, 3366,  80, 50,  0,  0 },
+    { 4496, 3366,  80, 50, 12,  0 },
+    { 4768, 3516,  96, 16,  0,  0, 0, 16 },
+    { 4832, 3204,  62, 26,  0,  0 },
+    { 4832, 3228,  62, 51,  0,  0 },
+    { 5108, 3349,  98, 13,  0,  0 },
+    { 5120, 3318, 142, 45, 62,  0 },
+    { 5280, 3528,  72, 52,  0,  0 },
+    { 5344, 3516, 142, 51,  0,  0 },
+    { 5344, 3584, 126,100,  0,  2 },
+    { 5360, 3516, 158, 51,  0,  0 },
+    { 5568, 3708,  72, 38,  0,  0 },
+    { 5632, 3710,  96, 17,  0,  0, 0, 16, 0, 0, 0x49 },
+    { 5712, 3774,  62, 20, 10,  2 },
+    { 5792, 3804, 158, 51,  0,  0 },
+    { 5920, 3950, 122, 80,  2,  0 },
+    { 6096, 4051,  76, 35,  0,  0 },
+    { 6096, 4056,  72, 34,  0,  0 },
+    { 6288, 4056, 264, 36,  0,  0 },
+    { 6384, 4224, 120, 44,  0,  0 },
+    { 6880, 4544, 136, 42,  0,  0 },
+    { 8896, 5920, 160, 64,  0,  0 },
+  };
+  static const struct {
+    ushort id;
+    char model[20];
+  } unique[] = {
+    { 0x168, "EOS 10D" },    { 0x001, "EOS-1D" },
+    { 0x175, "EOS 20D" },    { 0x174, "EOS-1D Mark II" },
+    { 0x234, "EOS 30D" },    { 0x232, "EOS-1D Mark II N" },
+    { 0x190, "EOS 40D" },    { 0x169, "EOS-1D Mark III" },
+    { 0x261, "EOS 50D" },    { 0x281, "EOS-1D Mark IV" },
+    { 0x287, "EOS 60D" },    { 0x167, "EOS-1DS" },
+    { 0x325, "EOS 70D" },
+    { 0x408, "EOS 77D" },    { 0x331, "EOS M" },
+    { 0x350, "EOS 80D" },    { 0x328, "EOS-1D X Mark II" },
+    { 0x346, "EOS 100D" },
+    { 0x417, "EOS 200D" },
+    { 0x170, "EOS 300D" },   { 0x188, "EOS-1Ds Mark II" },
+    { 0x176, "EOS 450D" },   { 0x215, "EOS-1Ds Mark III" },
+    { 0x189, "EOS 350D" },   { 0x324, "EOS-1D C" },
+    { 0x236, "EOS 400D" },   { 0x269, "EOS-1D X" },
+    { 0x252, "EOS 500D" },   { 0x213, "EOS 5D" },
+    { 0x270, "EOS 550D" },   { 0x218, "EOS 5D Mark II" },
+    { 0x286, "EOS 600D" },   { 0x285, "EOS 5D Mark III" },
+    { 0x301, "EOS 650D" },   { 0x302, "EOS 6D" },
+    { 0x326, "EOS 700D" },   { 0x250, "EOS 7D" },
+    { 0x393, "EOS 750D" },   { 0x289, "EOS 7D Mark II" },
+    { 0x347, "EOS 760D" },   { 0x406, "EOS 6D Mark II" },
+    { 0x405, "EOS 800D" },   { 0x349, "EOS 5D Mark IV" },
+    { 0x254, "EOS 1000D" },
+    { 0x288, "EOS 1100D" },
+    { 0x327, "EOS 1200D" },  { 0x382, "EOS 5DS" },
+    { 0x404, "EOS 1300D" },  { 0x401, "EOS 5DS R" },
+    { 0x422, "EOS 1500D" },
+    { 0x432, "EOS 3000D" },
+  }, sonique[] = {
+    { 0x002, "DSC-R1" },     { 0x100, "DSLR-A100" },
+    { 0x101, "DSLR-A900" },  { 0x102, "DSLR-A700" },
+    { 0x103, "DSLR-A200" },  { 0x104, "DSLR-A350" },
+    { 0x105, "DSLR-A300" },  { 0x108, "DSLR-A330" },
+    { 0x109, "DSLR-A230" },  { 0x10a, "DSLR-A290" },
+    { 0x10d, "DSLR-A850" },  { 0x111, "DSLR-A550" },
+    { 0x112, "DSLR-A500" },  { 0x113, "DSLR-A450" },
+    { 0x116, "NEX-5" },      { 0x117, "NEX-3" },
+    { 0x118, "SLT-A33" },    { 0x119, "SLT-A55V" },
+    { 0x11a, "DSLR-A560" },  { 0x11b, "DSLR-A580" },
+    { 0x11c, "NEX-C3" },     { 0x11d, "SLT-A35" },
+    { 0x11e, "SLT-A65V" },   { 0x11f, "SLT-A77V" },
+    { 0x120, "NEX-5N" },     { 0x121, "NEX-7" },
+    { 0x123, "SLT-A37" },    { 0x124, "SLT-A57" },
+    { 0x125, "NEX-F3" },     { 0x126, "SLT-A99V" },
+    { 0x127, "NEX-6" },      { 0x128, "NEX-5R" },
+    { 0x129, "DSC-RX100" },  { 0x12a, "DSC-RX1" },
+    { 0x12e, "ILCE-3000" },  { 0x12f, "SLT-A58" },
+    { 0x131, "NEX-3N" },     { 0x132, "ILCE-7" },
+    { 0x133, "NEX-5T" },     { 0x134, "DSC-RX100M2" },
+    { 0x135, "DSC-RX10" },   { 0x136, "DSC-RX1R" },
+    { 0x137, "ILCE-7R" },    { 0x138, "ILCE-6000" },
+    { 0x139, "ILCE-5000" },  { 0x13d, "DSC-RX100M3" },
+    { 0x13e, "ILCE-7S" },    { 0x13f, "ILCA-77M2" },
+    { 0x153, "ILCE-5100" },  { 0x154, "ILCE-7M2" },
+    { 0x155, "DSC-RX100M4" },{ 0x156, "DSC-RX10M2" },
+    { 0x158, "DSC-RX1RM2" }, { 0x15a, "ILCE-QX1" },
+    { 0x15b, "ILCE-7RM2" },  { 0x15e, "ILCE-7SM2" },
+    { 0x161, "ILCA-68" },    { 0x162, "ILCA-99M2" },
+    { 0x163, "DSC-RX10M3" }, { 0x164, "DSC-RX100M5" },
+    { 0x165, "ILCE-6300" },  { 0x166, "ILCE-9" },
+    { 0x168, "ILCE-6500" },  { 0x16a, "ILCE-7RM3" },
+    { 0x16b, "ILCE-7M3" },   { 0x16c, "DSC-RX0" },
+    { 0x16d, "DSC-RX10M4" },
+  };
+  static const char *orig, panalias[][12] = {
+    "@DC-FZ80", "DC-FZ82", "DC-FZ85",
+    "@DC-FZ81", "DC-FZ83",
+    "@DC-GF9", "DC-GX800", "DC-GX850",
+    "@DC-GF10", "DC-GF90",
+    "@DC-GX9", "DC-GX7MK3",
+    "@DC-ZS70", "DC-TZ90", "DC-TZ91", "DC-TZ92", "DC-TZ93",
+    "@DMC-FZ40", "DMC-FZ45",
+    "@DMC-FZ2500", "DMC-FZ2000", "DMC-FZH1",
+    "@DMC-G8", "DMC-G80", "DMC-G81", "DMC-G85",
+    "@DMC-GX85", "DMC-GX80", "DMC-GX7MK2",
+    "@DMC-LX9", "DMC-LX10", "DMC-LX15",
+    "@DMC-ZS40", "DMC-TZ60", "DMC-TZ61",
+    "@DMC-ZS50", "DMC-TZ70", "DMC-TZ71",
+    "@DMC-ZS60", "DMC-TZ80", "DMC-TZ81", "DMC-TZ85",
+    "@DMC-ZS100", "DMC-ZS110", "DMC-TZ100", "DMC-TZ101", "DMC-TZ110", "DMC-TX1",
+    "@DC-ZS200", "DC-TX2", "DC-TZ200", "DC-TZ202", "DC-TZ220", "DC-ZS220",
+  };
   static const struct {
-    int fsize;
-    char make[12], model[19], withjpeg;
+    unsigned fsize;
+    ushort rw, rh;
+    uchar lm, tm, rm, bm, lf, cf, max, flags;
+    char make[10], model[20];
+    ushort offset;
   } table[] = {
-    {    62464, "Kodak",    "DC20"       ,0 },
-    {   124928, "Kodak",    "DC20"       ,0 },
-    {  1652736, "Kodak",    "DCS200"     ,0 },
-    {  4159302, "Kodak",    "C330"       ,0 },
-    {  4162462, "Kodak",    "C330"       ,0 },
-    {   311696, "ST Micro", "STV680 VGA" ,0 },  /* SPYz */
-    {   614400, "Kodak",    "KAI-0340"   ,0 },
-    {   787456, "Creative", "PC-CAM 600" ,0 },
-    {  1138688, "Minolta",  "RD175"      ,0 },
-    {  3840000, "Foculus",  "531C"       ,0 },
-    {   786432, "AVT",      "F-080C"     ,0 },
-    {  1447680, "AVT",      "F-145C"     ,0 },
-    {  1920000, "AVT",      "F-201C"     ,0 },
-    {  5067304, "AVT",      "F-510C"     ,0 },
-    { 10134608, "AVT",      "F-510C"     ,0 },
-    { 16157136, "AVT",      "F-810C"     ,0 },
-    {  1409024, "Sony",     "XCD-SX910CR",0 },
-    {  2818048, "Sony",     "XCD-SX910CR",0 },
-    {  3884928, "Micron",   "2010"       ,0 },
-    {  6624000, "Pixelink", "A782"       ,0 },
-    { 13248000, "Pixelink", "A782"       ,0 },
-    {  6291456, "RoverShot","3320AF"     ,0 },
-    {  6553440, "Canon",    "PowerShot A460",0 },
-    {  6653280, "Canon",    "PowerShot A530",0 },
-    {  6573120, "Canon",    "PowerShot A610",0 },
-    {  9219600, "Canon",    "PowerShot A620",0 },
-    { 10341600, "Canon",    "PowerShot A720",0 },
-    { 10383120, "Canon",    "PowerShot A630",0 },
-    { 12945240, "Canon",    "PowerShot A640",0 },
-    { 15636240, "Canon",    "PowerShot A650",0 },
-    {  7710960, "Canon",    "PowerShot S3 IS",0 },
-    {  5939200, "OLYMPUS",  "C770UZ"     ,0 },
-    {  1581060, "NIKON",    "E900"       ,1 },  /* or E900s,E910 */
-    {  2465792, "NIKON",    "E950"       ,1 },  /* or E800,E700 */
-    {  2940928, "NIKON",    "E2100"      ,1 },  /* or E2500 */
-    {  4771840, "NIKON",    "E990"       ,1 },  /* or E995, Oly C3030Z */
-    {  4775936, "NIKON",    "E3700"      ,1 },  /* or Optio 33WR */
-    {  5869568, "NIKON",    "E4300"      ,1 },  /* or DiMAGE Z2 */
-    {  5865472, "NIKON",    "E4500"      ,1 },
-    {  7438336, "NIKON",    "E5000"      ,1 },  /* or E5700 */
-    {  8998912, "NIKON",    "COOLPIX S6" ,1 },
-    {  1976352, "CASIO",    "QV-2000UX"  ,1 },
-    {  3217760, "CASIO",    "QV-3*00EX"  ,1 },
-    {  6218368, "CASIO",    "QV-5700"    ,1 },
-    {  6054400, "CASIO",    "QV-R41"     ,1 },
-    {  7530816, "CASIO",    "QV-R51"     ,1 },
-    {  7684000, "CASIO",    "QV-4000"    ,1 },
-    {  4948608, "CASIO",    "EX-S100"    ,1 },
-    {  7542528, "CASIO",    "EX-Z50"     ,1 },
-    {  7753344, "CASIO",    "EX-Z55"     ,1 },
-    {  7426656, "CASIO",    "EX-P505"    ,1 },
-    {  9313536, "CASIO",    "EX-P600"    ,1 },
-    { 10979200, "CASIO",    "EX-P700"    ,1 },
-    {  3178560, "PENTAX",   "Optio S"    ,1 },
-    {  4841984, "PENTAX",   "Optio S"    ,1 },
-    {  6114240, "PENTAX",   "Optio S4"   ,1 },  /* or S4i, CASIO EX-Z4 */
-    { 10702848, "PENTAX",   "Optio 750Z" ,1 },
-    { 12582980, "Sinar",    ""           ,0 },
-    { 33292868, "Sinar",    ""           ,0 },
-    { 44390468, "Sinar",    ""           ,0 } };
+    {   786432,1024, 768, 0, 0, 0, 0, 0,0x94,0,0,"AVT","F-080C" },
+    {  1447680,1392,1040, 0, 0, 0, 0, 0,0x94,0,0,"AVT","F-145C" },
+    {  1920000,1600,1200, 0, 0, 0, 0, 0,0x94,0,0,"AVT","F-201C" },
+    {  5067304,2588,1958, 0, 0, 0, 0, 0,0x94,0,0,"AVT","F-510C" },
+    {  5067316,2588,1958, 0, 0, 0, 0, 0,0x94,0,0,"AVT","F-510C",12 },
+    { 10134608,2588,1958, 0, 0, 0, 0, 9,0x94,0,0,"AVT","F-510C" },
+    { 10134620,2588,1958, 0, 0, 0, 0, 9,0x94,0,0,"AVT","F-510C",12 },
+    { 16157136,3272,2469, 0, 0, 0, 0, 9,0x94,0,0,"AVT","F-810C" },
+    { 15980544,3264,2448, 0, 0, 0, 0, 8,0x61,0,1,"AgfaPhoto","DC-833m" },
+    {  9631728,2532,1902, 0, 0, 0, 0,96,0x61,0,0,"Alcatel","5035D" },
+    {  2868726,1384,1036, 0, 0, 0, 0,64,0x49,0,8,"Baumer","TXG14",1078 },
+    {  5298000,2400,1766,12,12,44, 2, 8,0x94,0,2,"Canon","PowerShot SD300" },
+    {  6553440,2664,1968, 4, 4,44, 4, 8,0x94,0,2,"Canon","PowerShot A460" },
+    {  6573120,2672,1968,12, 8,44, 0, 8,0x94,0,2,"Canon","PowerShot A610" },
+    {  6653280,2672,1992,10, 6,42, 2, 8,0x94,0,2,"Canon","PowerShot A530" },
+    {  7710960,2888,2136,44, 8, 4, 0, 8,0x94,0,2,"Canon","PowerShot S3 IS" },
+    {  9219600,3152,2340,36,12, 4, 0, 8,0x94,0,2,"Canon","PowerShot A620" },
+    {  9243240,3152,2346,12, 7,44,13, 8,0x49,0,2,"Canon","PowerShot A470" },
+    { 10341600,3336,2480, 6, 5,32, 3, 8,0x94,0,2,"Canon","PowerShot A720 IS" },
+    { 10383120,3344,2484,12, 6,44, 6, 8,0x94,0,2,"Canon","PowerShot A630" },
+    { 12945240,3736,2772,12, 6,52, 6, 8,0x94,0,2,"Canon","PowerShot A640" },
+    { 15636240,4104,3048,48,12,24,12, 8,0x94,0,2,"Canon","PowerShot A650" },
+    { 15467760,3720,2772, 6,12,30, 0, 8,0x94,0,2,"Canon","PowerShot SX110 IS" },
+    { 15534576,3728,2778,12, 9,44, 9, 8,0x94,0,2,"Canon","PowerShot SX120 IS" },
+    { 18653760,4080,3048,24,12,24,12, 8,0x94,0,2,"Canon","PowerShot SX20 IS" },
+    { 19131120,4168,3060,92,16, 4, 1, 8,0x94,0,2,"Canon","PowerShot SX220 HS" },
+    { 21936096,4464,3276,25,10,73,12, 8,0x16,0,2,"Canon","PowerShot SX30 IS" },
+    { 24724224,4704,3504, 8,16,56, 8, 8,0x94,0,2,"Canon","PowerShot A3300 IS" },
+    { 30858240,5248,3920, 8,16,56,16, 8,0x94,0,2,"Canon","IXUS 160" },
+    {  1976352,1632,1211, 0, 2, 0, 1, 0,0x94,0,1,"Casio","QV-2000UX" },
+    {  3217760,2080,1547, 0, 0,10, 1, 0,0x94,0,1,"Casio","QV-3*00EX" },
+    {  6218368,2585,1924, 0, 0, 9, 0, 0,0x94,0,1,"Casio","QV-5700" },
+    {  7816704,2867,2181, 0, 0,34,36, 0,0x16,0,1,"Casio","EX-Z60" },
+    {  2937856,1621,1208, 0, 0, 1, 0, 0,0x94,7,13,"Casio","EX-S20" },
+    {  4948608,2090,1578, 0, 0,32,34, 0,0x94,7,1,"Casio","EX-S100" },
+    {  6054400,2346,1720, 2, 0,32, 0, 0,0x94,7,1,"Casio","QV-R41" },
+    {  7426656,2568,1928, 0, 0, 0, 0, 0,0x94,0,1,"Casio","EX-P505" },
+    {  7530816,2602,1929, 0, 0,22, 0, 0,0x94,7,1,"Casio","QV-R51" },
+    {  7542528,2602,1932, 0, 0,32, 0, 0,0x94,7,1,"Casio","EX-Z50" },
+    {  7562048,2602,1937, 0, 0,25, 0, 0,0x16,7,1,"Casio","EX-Z500" },
+    {  7753344,2602,1986, 0, 0,32,26, 0,0x94,7,1,"Casio","EX-Z55" },
+    {  9313536,2858,2172, 0, 0,14,30, 0,0x94,7,1,"Casio","EX-P600" },
+    { 10834368,3114,2319, 0, 0,27, 0, 0,0x94,0,1,"Casio","EX-Z750" },
+    { 10843712,3114,2321, 0, 0,25, 0, 0,0x94,0,1,"Casio","EX-Z75" },
+    { 10979200,3114,2350, 0, 0,32,32, 0,0x94,7,1,"Casio","EX-P700" },
+    { 12310144,3285,2498, 0, 0, 6,30, 0,0x94,0,1,"Casio","EX-Z850" },
+    { 12489984,3328,2502, 0, 0,47,35, 0,0x94,0,1,"Casio","EX-Z8" },
+    { 15499264,3754,2752, 0, 0,82, 0, 0,0x94,0,1,"Casio","EX-Z1050" },
+    { 18702336,4096,3044, 0, 0,24, 0,80,0x94,7,1,"Casio","EX-ZR100" },
+    {  7684000,2260,1700, 0, 0, 0, 0,13,0x94,0,1,"Casio","QV-4000" },
+    {   787456,1024, 769, 0, 1, 0, 0, 0,0x49,0,0,"Creative","PC-CAM 600" },
+    { 28829184,4384,3288, 0, 0, 0, 0,36,0x61,0,0,"DJI" },
+    { 15151104,4608,3288, 0, 0, 0, 0, 0,0x94,0,0,"Matrix" },
+    {  3840000,1600,1200, 0, 0, 0, 0,65,0x49,0,0,"Foculus","531C" },
+    {   307200, 640, 480, 0, 0, 0, 0, 0,0x94,0,0,"Generic" },
+    {    62464, 256, 244, 1, 1, 6, 1, 0,0x8d,0,0,"Kodak","DC20" },
+    {   124928, 512, 244, 1, 1,10, 1, 0,0x8d,0,0,"Kodak","DC20" },
+    {  1652736,1536,1076, 0,52, 0, 0, 0,0x61,0,0,"Kodak","DCS200" },
+    {  4159302,2338,1779, 1,33, 1, 2, 0,0x94,0,0,"Kodak","C330" },
+    {  4162462,2338,1779, 1,33, 1, 2, 0,0x94,0,0,"Kodak","C330",3160 },
+    {  2247168,1232, 912, 0, 0,16, 0, 0,0x00,0,0,"Kodak","C330" },
+    {  3370752,1232, 912, 0, 0,16, 0, 0,0x00,0,0,"Kodak","C330" },
+    {  6163328,2864,2152, 0, 0, 0, 0, 0,0x94,0,0,"Kodak","C603" },
+    {  6166488,2864,2152, 0, 0, 0, 0, 0,0x94,0,0,"Kodak","C603",3160 },
+    {   460800, 640, 480, 0, 0, 0, 0, 0,0x00,0,0,"Kodak","C603" },
+    {  9116448,2848,2134, 0, 0, 0, 0, 0,0x00,0,0,"Kodak","C603" },
+    { 12241200,4040,3030, 2, 0, 0,13, 0,0x49,0,0,"Kodak","12MP" },
+    { 12272756,4040,3030, 2, 0, 0,13, 0,0x49,0,0,"Kodak","12MP",31556 },
+    { 18000000,4000,3000, 0, 0, 0, 0, 0,0x00,0,0,"Kodak","12MP" },
+    {   614400, 640, 480, 0, 3, 0, 0,64,0x94,0,0,"Kodak","KAI-0340" },
+    { 15360000,3200,2400, 0, 0, 0, 0,96,0x16,0,0,"Lenovo","A820" },
+    {  3884928,1608,1207, 0, 0, 0, 0,96,0x16,0,0,"Micron","2010",3212 },
+    {  1138688,1534, 986, 0, 0, 0, 0, 0,0x61,0,0,"Minolta","RD175",513 },
+    {  1581060,1305, 969, 0, 0,18, 6, 6,0x1e,4,1,"Nikon","E900" },
+    {  2465792,1638,1204, 0, 0,22, 1, 6,0x4b,5,1,"Nikon","E950" },
+    {  2940928,1616,1213, 0, 0, 0, 7,30,0x94,0,1,"Nikon","E2100" },
+    {  4771840,2064,1541, 0, 0, 0, 1, 6,0xe1,0,1,"Nikon","E990" },
+    {  4775936,2064,1542, 0, 0, 0, 0,30,0x94,0,1,"Nikon","E3700" },
+    {  5865472,2288,1709, 0, 0, 0, 1, 6,0xb4,0,1,"Nikon","E4500" },
+    {  5869568,2288,1710, 0, 0, 0, 0, 6,0x16,0,1,"Nikon","E4300" },
+    {  7438336,2576,1925, 0, 0, 0, 1, 6,0xb4,0,1,"Nikon","E5000" },
+    {  8998912,2832,2118, 0, 0, 0, 0,30,0x94,7,1,"Nikon","COOLPIX S6" },
+    {  5939200,2304,1718, 0, 0, 0, 0,30,0x16,0,0,"Olympus","C770UZ" },
+    {  3178560,2064,1540, 0, 0, 0, 0, 0,0x94,0,1,"Pentax","Optio S" },
+    {  4841984,2090,1544, 0, 0,22, 0, 0,0x94,7,1,"Pentax","Optio S" },
+    {  6114240,2346,1737, 0, 0,22, 0, 0,0x94,7,1,"Pentax","Optio S4" },
+    { 10702848,3072,2322, 0, 0, 0,21,30,0x94,0,1,"Pentax","Optio 750Z" },
+    {  4147200,1920,1080, 0, 0, 0, 0, 0,0x49,0,0,"Photron","BC2-HD" },
+    {  4151666,1920,1080, 0, 0, 0, 0, 0,0x49,0,0,"Photron","BC2-HD",8 },
+    { 13248000,2208,3000, 0, 0, 0, 0,13,0x61,0,0,"Pixelink","A782" },
+    {  6291456,2048,1536, 0, 0, 0, 0,96,0x61,0,0,"RoverShot","3320AF" },
+    {   311696, 644, 484, 0, 0, 0, 0, 0,0x16,0,8,"ST Micro","STV680 VGA" },
+    { 16098048,3288,2448, 0, 0,24, 0, 9,0x94,0,1,"Samsung","S85" },
+    { 16215552,3312,2448, 0, 0,48, 0, 9,0x94,0,1,"Samsung","S85" },
+    { 20487168,3648,2808, 0, 0, 0, 0,13,0x94,5,1,"Samsung","WB550" },
+    { 24000000,4000,3000, 0, 0, 0, 0,13,0x94,5,1,"Samsung","WB550" },
+    { 12582980,3072,2048, 0, 0, 0, 0,33,0x61,0,0,"Sinar","",68 },
+    { 33292868,4080,4080, 0, 0, 0, 0,33,0x61,0,0,"Sinar","",68 },
+    { 44390468,4080,5440, 0, 0, 0, 0,33,0x61,0,0,"Sinar","",68 },
+    {  1409024,1376,1024, 0, 0, 1, 0, 0,0x49,0,0,"Sony","XCD-SX910CR" },
+    {  2818048,1376,1024, 0, 0, 1, 0,97,0x49,0,0,"Sony","XCD-SX910CR" },
+    { 17496000,4320,3240, 0, 0, 0,0,224,0x94,0,0,"Xiro","Xplorer V" },
+  };
   static const char *corp[] =
-    { "Canon", "NIKON", "EPSON", "KODAK", "Kodak", "OLYMPUS", "PENTAX",
-      "MINOLTA", "Minolta", "Konica", "CASIO", "Sinar", "Phase One",
-      "SAMSUNG", "Mamiya" };
+    { "AgfaPhoto", "Canon", "Casio", "Epson", "Fujifilm",
+      "Mamiya", "Minolta", "Motorola", "Kodak", "Konica", "Leica",
+      "Nikon", "Nokia", "Olympus", "Ricoh", "Pentax", "Phase One",
+      "Samsung", "Sigma", "Sinar", "Sony", "YI" };
+  char head[32], *cp;
+  int hlen, flen, fsize, zero_fsize=1, i, c;
+  struct jhead jh;
 
-  tiff_flip = flip = filters = -1;	/* 0 is valid, so -1 is unknown */
+  tiff_flip = flip = filters = UINT_MAX;	/* unknown */
   raw_height = raw_width = fuji_width = fuji_layout = cr2_slice[0] = 0;
   maximum = height = width = top_margin = left_margin = 0;
   cdesc[0] = desc[0] = artist[0] = make[0] = model[0] = model2[0] = 0;
   iso_speed = shutter = aperture = focal_len = unique_id = 0;
+  tiff_nifds = 0;
+  memset (tiff_ifd, 0, sizeof tiff_ifd);
   memset (gpsdata, 0, sizeof gpsdata);
+  memset (cblack, 0, sizeof cblack);
   memset (white, 0, sizeof white);
+  memset (mask, 0, sizeof mask);
   thumb_offset = thumb_length = thumb_width = thumb_height = 0;
   load_raw = thumb_load_raw = 0;
   write_thumb = &CLASS jpeg_thumb;
-  data_offset = meta_length = tiff_bps = tiff_compress = 0;
-  kodak_cbpp = zero_after_ff = dng_version = 0;
+  data_offset = meta_offset = meta_length = tiff_bps = tiff_compress = 0;
+  kodak_cbpp = zero_after_ff = dng_version = load_flags = 0;
   timestamp = shot_order = tiff_samples = black = is_foveon = 0;
   mix_green = profile_length = data_error = zero_is_bad = 0;
-  pixel_aspect = is_raw = raw_color = use_gamma = 1;
-  tile_width = tile_length = INT_MAX;
+  pixel_aspect = is_raw = raw_color = 1;
+  tile_width = tile_length = 0;
   for (i=0; i < 4; i++) {
     cam_mul[i] = i == 1;
     pre_mul[i] = i < 3;
@@ -6537,26 +8654,24 @@
     FORC3 rgb_cam[c][i] = c == i;
   }
   colors = 3;
-  tiff_bps = 12;
-  for (i=0; i < 0x4000; i++) curve[i] = i;
+  for (i=0; i < 0x10000; i++) curve[i] = i;
 
   order = get2();
   hlen = get4();
   fseek (ifp, 0, SEEK_SET);
   fread (head, 1, 32, ifp);
   fseek (ifp, 0, SEEK_END);
-  fsize = ftell(ifp);
+  flen = fsize = ftell(ifp);
   if ((cp = (char *) memmem (head, 32, "MMMM", 4)) ||
       (cp = (char *) memmem (head, 32, "IIII", 4))) {
     parse_phase_one (cp-head);
-    if (cp-head) parse_tiff(0);
+    if (cp-head && parse_tiff(0)) apply_tiff();
   } else if (order == 0x4949 || order == 0x4d4d) {
     if (!memcmp (head+6,"HEAPCCDR",8)) {
       data_offset = hlen;
-      parse_ciff (hlen, fsize - hlen);
-    } else {
-      parse_tiff(0);
-    }
+      parse_ciff (hlen, flen-hlen, 0);
+      load_raw = &CLASS canon_load_raw;
+    } else if (parse_tiff(0)) apply_tiff();
   } else if (!memcmp (head,"\xff\xd8\xff\xe1",4) &&
 	     !memcmp (head+6,"Exif",4)) {
     fseek (ifp, 4, SEEK_SET);
@@ -6578,9 +8693,11 @@
   } else if (!strcmp (head, "qktk")) {
     strcpy (make, "Apple");
     strcpy (model,"QuickTake 100");
+    load_raw = &CLASS quicktake_100_load_raw;
   } else if (!strcmp (head, "qktn")) {
     strcpy (make, "Apple");
     strcpy (model,"QuickTake 150");
+    load_raw = &CLASS kodak_radc_load_raw;
   } else if (!memcmp (head,"FUJIFILM",8)) {
     fseek (ifp, 84, SEEK_SET);
     thumb_offset = get4();
@@ -6593,12 +8710,24 @@
       if (is_raw == 2 && shot_select)
 	parse_fuji (i);
     }
-    fseek (ifp, 100, SEEK_SET);
-    data_offset = get4();
+    fseek (ifp, 100+28*(shot_select > 0), SEEK_SET);
+    parse_tiff (data_offset = get4());
     parse_tiff (thumb_offset+12);
+    apply_tiff();
+    if (!load_raw) {
+      load_raw = &CLASS unpacked_load_raw;
+      tiff_bps = 14;
+    }
   } else if (!memcmp (head,"RIFF",4)) {
     fseek (ifp, 0, SEEK_SET);
     parse_riff();
+  } else if (!memcmp (head+4,"ftypcrx ",8)) {
+    fseek (ifp, 0, SEEK_SET);
+    parse_crx (fsize);
+  } else if (!memcmp (head+4,"ftypqt   ",9)) {
+    fseek (ifp, 0, SEEK_SET);
+    parse_qt (fsize);
+    is_raw = 0;
   } else if (!memcmp (head,"\0\001\0\001\0@",6)) {
     fseek (ifp, 6, SEEK_SET);
     fread (make, 1, 8, ifp);
@@ -6608,8 +8737,53 @@
     get2();
     raw_width = get2();
     raw_height = get2();
-    load_raw = nokia_load_raw;
+    load_raw = &CLASS nokia_load_raw;
+    filters = 0x61616161;
+  } else if (!memcmp (head,"NOKIARAW",8)) {
+    strcpy (make, "NOKIA");
+    order = 0x4949;
+    fseek (ifp, 300, SEEK_SET);
+    data_offset = get4();
+    i = get4();
+    width = get2();
+    height = get2();
+    switch (tiff_bps = i*8 / (width * height)) {
+      case  8: load_raw = &CLASS eight_bit_load_raw;  break;
+      case 10: load_raw = &CLASS nokia_load_raw;
+    }
+    raw_height = height + (top_margin = i / (width * tiff_bps/8) - height);
+    mask[0][3] = 1;
+    filters = 0x61616161;
+  } else if (!memcmp (head,"ARRI",4)) {
+    order = 0x4949;
+    fseek (ifp, 20, SEEK_SET);
+    width = get4();
+    height = get4();
+    strcpy (make, "ARRI");
+    fseek (ifp, 668, SEEK_SET);
+    fread (model, 1, 64, ifp);
+    data_offset = 4096;
+    load_raw = &CLASS packed_load_raw;
+    load_flags = 88;
     filters = 0x61616161;
+  } else if (!memcmp (head,"XPDS",4)) {
+    order = 0x4949;
+    fseek (ifp, 0x800, SEEK_SET);
+    fread (make, 1, 41, ifp);
+    raw_height = get2();
+    raw_width  = get2();
+    fseek (ifp, 56, SEEK_CUR);
+    fread (model, 1, 30, ifp);
+    data_offset = 0x10000;
+    load_raw = &CLASS canon_rmf_load_raw;
+    gamma_curve (0, 12.25, 1, 1023);
+  } else if (!memcmp (head+4,"RED1",4)) {
+    strcpy (make, "Red");
+    strcpy (model,"One");
+    parse_redcine();
+    load_raw = &CLASS redcine_load_raw;
+    gamma_curve (1/2.4, 12.92, 1, 4095);
+    filters = 0x49494949;
   } else if (!memcmp (head,"DSC-Image",9))
     parse_rollei();
   else if (!memcmp (head,"PWAD",4))
@@ -6620,22 +8794,67 @@
     parse_foveon();
   else if (!memcmp (head,"CI",2))
     parse_cine();
-  else
-    for (i=0; i < sizeof table / sizeof *table; i++)
+  if (make[0] == 0)
+    for (zero_fsize=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)
+	flip = table[i].flags >> 2;
+	zero_is_bad = table[i].flags & 2;
+	if (table[i].flags & 1)
 	  parse_external_jpeg();
-      }
-  if (make[0] == 0) parse_smal (0, fsize);
-  if (make[0] == 0) parse_jpeg (is_raw = 0);
+	data_offset = table[i].offset;
+	raw_width   = table[i].rw;
+	raw_height  = table[i].rh;
+	left_margin = table[i].lm;
+	 top_margin = table[i].tm;
+	width  = raw_width - left_margin - table[i].rm;
+	height = raw_height - top_margin - table[i].bm;
+	filters = 0x1010101 * table[i].cf;
+	colors = 4 - !((filters & filters >> 1) & 0x5555);
+	load_flags = table[i].lf;
+	switch (tiff_bps = (fsize-data_offset)*8 / (raw_width*raw_height)) {
+	  case 6:
+	    load_raw = &CLASS minolta_rd175_load_raw;  break;
+	  case 8:
+	    load_raw = &CLASS eight_bit_load_raw;  break;
+	  case 10: case 12:
+	    load_flags |= 512;
+	    if (!strcmp(make,"Canon")) load_flags |= 256;
+	    load_raw = &CLASS packed_load_raw;     break;
+	  case 16:
+	    order = 0x4949 | 0x404 * (load_flags & 1);
+	    tiff_bps -= load_flags >> 4;
+	    tiff_bps -= load_flags = load_flags >> 1 & 7;
+	    load_raw = &CLASS unpacked_load_raw;
+	}
+	maximum = (1 << tiff_bps) - (1 << table[i].max);
+      }
+  if (zero_fsize) fsize = 0;
+  if (make[0] == 0) parse_smal (0, flen);
+  if (make[0] == 0) {
+    parse_jpeg(0);
+    if (!(strncmp(model,"ov",2) && strncmp(model,"RP_OV",5)) &&
+	!fseek (ifp, -6404096, SEEK_END) &&
+	fread (head, 1, 32, ifp) && !strcmp(head,"BRCMn")) {
+      strcpy (make, "OmniVision");
+      data_offset = ftell(ifp) + 0x8000-32;
+      width = raw_width;
+      raw_width = 2611;
+      load_raw = &CLASS nokia_load_raw;
+      filters = 0x16161616;
+    } else is_raw = 0;
+  }
 
   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;
+    if (strcasestr (make, corp[i]))	/* Simplify company names */
+	    strcpy (make, corp[i]);
+  if ((!strcmp(make,"Kodak") || !strcmp(make,"Leica")) &&
+	((cp = strcasestr(model," DIGITAL CAMERA")) ||
+	 (cp = strstr(model,"FILE VERSION"))))
+     *cp = 0;
+  if (!strncasecmp(model,"PENTAX",6))
+    strcpy (make, "Pentax");
   cp = make + strlen(make);		/* Remove trailing spaces */
   while (*--cp == ' ') *cp = 0;
   cp = model + strlen(model);
@@ -6643,57 +8862,128 @@
   i = strlen(make);			/* Remove make from model */
   if (!strncasecmp (model, make, i) && model[i++] == ' ')
     memmove (model, model+i, 64-i);
+  if (!strncmp (model,"FinePix ",8))
+    strcpy (model, model+8);
   if (!strncmp (model,"Digital Camera ",15))
     strcpy (model, model+15);
   desc[511] = artist[63] = make[63] = model[63] = model2[63] = 0;
   if (!is_raw) goto notraw;
 
-  if (!maximum) maximum = (1 << tiff_bps) - 1;
   if (!height) height = raw_height;
   if (!width)  width  = raw_width;
-  if (fuji_width) {
-    width = height + fuji_width;
-    height = width - 1;
-    pixel_aspect = 1;
-  }
   if (height == 2624 && width == 3936)	/* Pentax K10D and Samsung GX10 */
     { height  = 2616;   width  = 3896; }
-  if (height == 3136 && width == 4864)	/* Pentax K20D */
-    { height  = 3124;   width  = 4688; }
+  if (height == 3136 && width == 4864)  /* Pentax K20D and Samsung GX20 */
+    { height  = 3124;   width  = 4688; filters = 0x16161616; }
+  if (raw_height == 2868 && (!strcmp(model,"K-r") || !strcmp(model,"K-x")))
+    {			width  = 4309; filters = 0x16161616; }
+  if (raw_height == 3136 && !strcmp(model,"K-7"))
+    { height  = 3122;   width  = 4684; filters = 0x16161616; top_margin = 2; }
+  if (raw_height == 3284 && !strncmp(model,"K-5",3))
+    { left_margin = 10; width  = 4950; filters = 0x16161616; }
+  if (raw_height == 3300 && !strncmp(model,"K-50",4))
+    { height  = 3288,   width  = 4952;  left_margin = 0;  top_margin = 12; }
+  if (raw_height == 3664 && !strncmp(model,"K-S",3))
+    {			width  = 5492;  left_margin = 0; }
+  if (raw_height == 4032 && !strcmp(model,"K-3"))
+    { height  = 4032;   width  = 6040;  left_margin = 4; }
+  if (raw_height == 4060 && !strcmp(model,"KP"))
+    { height  = 4032;   width  = 6032;  left_margin = 52; top_margin = 28; }
+  if (raw_height == 4950 && !strcmp(model,"K-1"))
+    { height  = 4932;   width  = 7380;  left_margin = 4;  top_margin = 18; }
+  if (raw_height == 5552 && !strcmp(model,"645D"))
+    { height  = 5502;   width  = 7328;  left_margin = 48; top_margin = 29;
+      filters = 0x61616161; }
+  if (height == 3014 && width == 4096)	/* Ricoh GX200 */
+			width  = 4014;
   if (dng_version) {
     if (filters == UINT_MAX) filters = 0;
-    if (filters) is_raw = tiff_samples;
-    else	 colors = tiff_samples;
-    if (tiff_compress == 1)
-      load_raw = &CLASS adobe_dng_load_raw_nc;
-    if (tiff_compress == 7)
-      load_raw = &CLASS adobe_dng_load_raw_lj;
+    if (filters) is_raw *= tiff_samples;
+    else	 colors  = tiff_samples;
+    switch (tiff_compress) {
+      case 0:
+      case 1:     load_raw = &CLASS   packed_dng_load_raw;  break;
+      case 7:     load_raw = &CLASS lossless_dng_load_raw;  break;
+      case 34892: load_raw = &CLASS    lossy_dng_load_raw;  break;
+      default:    load_raw = 0;
+    }
     goto dng_skip;
   }
-  if ((is_canon = !strcmp(make,"Canon"))) {
-    load_raw = memcmp (head+6,"HEAPCCDR",8) ?
-	&CLASS lossless_jpeg_load_raw : &CLASS canon_compressed_load_raw;
-    maximum = 0xfff;
+  if (!strcmp(make,"Canon") && !fsize && tiff_bps != 15) {
+    if (!load_raw)
+      load_raw = &CLASS lossless_jpeg_load_raw;
+    for (i=0; i < sizeof canon / sizeof *canon; i++)
+      if (raw_width == canon[i][0] && raw_height == canon[i][1]) {
+	width  = raw_width - (left_margin = canon[i][2]);
+	height = raw_height - (top_margin = canon[i][3]);
+	width  -= canon[i][4];
+	height -= canon[i][5];
+	mask[0][1] =  canon[i][6];
+	mask[0][3] = -canon[i][7];
+	mask[1][1] =  canon[i][8];
+	mask[1][3] = -canon[i][9];
+	if (canon[i][10]) filters = canon[i][10] * 0x01010101;
+      }
+    if ((unique_id | 0x20000) == 0x2720000) {
+      left_margin = 8;
+      top_margin = 16;
+    }
+  }
+  for (i=0; i < sizeof unique / sizeof *unique; i++)
+    if (unique_id == 0x80000000 + unique[i].id) {
+      adobe_coeff ("Canon", unique[i].model);
+      if (model[4] == 'K' && strlen(model) == 8)
+	strcpy (model, unique[i].model);
+    }
+  for (i=0; i < sizeof sonique / sizeof *sonique; i++)
+    if (unique_id == sonique[i].id)
+      strcpy (model, sonique[i].model);
+  for (i=0; i < sizeof panalias / sizeof *panalias; i++)
+    if (panalias[i][0] == '@') orig = panalias[i]+1;
+    else if (!strcmp(model,panalias[i]))
+      adobe_coeff ("Panasonic", orig);
+  if (!strcmp(make,"Nikon")) {
+    if (!load_raw)
+      load_raw = &CLASS packed_load_raw;
+    if (model[0] == 'E')
+      load_flags |= !data_offset << 2 | 2;
   }
-  if (!strcmp(make,"NIKON") && !load_raw)
-    load_raw = &CLASS nikon_load_raw;
 
 /* Set parameters based on camera name (for non-DNG files). */
 
+  if (!strcmp(model,"KAI-0340")
+	&& find_green (16, 16, 3840, 5120) < 25) {
+    height = 480;
+    top_margin = filters = 0;
+    strcpy (model,"C603");
+  }
+  if (!strcmp(make,"Sony") && raw_width > 3888)
+    black = 128 << (tiff_bps - 12);
   if (is_foveon) {
     if (height*2 < width) pixel_aspect = 0.5;
     if (height   > width) pixel_aspect = 2;
     filters = 0;
-    load_raw = &CLASS foveon_load_raw;
     simple_coeff(0);
-  } else if (is_canon && tiff_samples == 4) {
+  } else if (!strcmp(make,"Canon") && tiff_bps == 15) {
+    switch (width) {
+      case 3344: width -= 66;
+      case 3872: width -= 6;
+    }
+    if (height > width) {
+      SWAP(height,width);
+      SWAP(raw_height,raw_width);
+    }
+    if (width == 7200 && height == 3888) {
+      raw_width  = width  = 6480;
+      raw_height = height = 4320;
+    }
     filters = 0;
+    tiff_samples = colors = 3;
     load_raw = &CLASS canon_sraw_load_raw;
   } else if (!strcmp(model,"PowerShot 600")) {
     height = 613;
     width  = 854;
     raw_width = 896;
-    pixel_aspect = 607/628.0;
     colors = 4;
     filters = 0xe1e4e1e4;
     load_raw = &CLASS canon_600_load_raw;
@@ -6703,197 +8993,37 @@
     width  = 960;
     raw_width = 992;
     pixel_aspect = 256/235.0;
-    colors = 4;
     filters = 0x1e4e1e4e;
-    load_raw = &CLASS canon_a5_load_raw;
+    goto canon_a5;
   } else if (!strcmp(model,"PowerShot A50")) {
     height =  968;
     width  = 1290;
     raw_width = 1320;
-    colors = 4;
     filters = 0x1b4e4b1e;
-    load_raw = &CLASS canon_a5_load_raw;
+    goto canon_a5;
   } else if (!strcmp(model,"PowerShot Pro70")) {
     height = 1024;
     width  = 1552;
-    colors = 4;
     filters = 0x1e4b4e1b;
-    load_raw = &CLASS canon_a5_load_raw;
-  } else if (!strcmp(model,"PowerShot A460")) {
-    height = 1960;
-    width  = 2616;
-    raw_height = 1968;
-    raw_width  = 2664;
-    top_margin  = 4;
-    left_margin = 4;
-    load_raw = &CLASS canon_a5_load_raw;
-  } else if (!strcmp(model,"PowerShot A530")) {
-    height = 1984;
-    width  = 2620;
-    raw_height = 1992;
-    raw_width  = 2672;
-    top_margin  = 6;
-    left_margin = 10;
-    load_raw = &CLASS canon_a5_load_raw;
-    raw_color = 0;
-  } else if (!strcmp(model,"PowerShot A610")) {
-    if (canon_s2is()) strcpy (model+10, "S2 IS");
-    height = 1960;
-    width  = 2616;
-    raw_height = 1968;
-    raw_width  = 2672;
-    top_margin  = 8;
-    left_margin = 12;
-    load_raw = &CLASS canon_a5_load_raw;
-  } else if (!strcmp(model,"PowerShot A620")) {
-    height = 2328;
-    width  = 3112;
-    raw_height = 2340;
-    raw_width  = 3152;
-    top_margin  = 12;
-    left_margin = 36;
-    load_raw = &CLASS canon_a5_load_raw;
-  } else if (!strcmp(model,"PowerShot A720")) {
-    height = 2472;
-    width  = 3298;
-    raw_height = 2480;
-    raw_width  = 3336;
-    top_margin  = 5;
-    left_margin = 6;
-    load_raw = &CLASS canon_a5_load_raw;
-  } else if (!strcmp(model,"PowerShot A630")) {
-    height = 2472;
-    width  = 3288;
-    raw_height = 2484;
-    raw_width  = 3344;
-    top_margin  = 6;
-    left_margin = 12;
-    load_raw = &CLASS canon_a5_load_raw;
-  } else if (!strcmp(model,"PowerShot A640")) {
-    height = 2760;
-    width  = 3672;
-    raw_height = 2772;
-    raw_width  = 3736;
-    top_margin  = 6;
-    left_margin = 12;
-    load_raw = &CLASS canon_a5_load_raw;
-  } else if (!strcmp(model,"PowerShot A650")) {
-    height = 3024;
-    width  = 4032;
-    raw_height = 3048;
-    raw_width  = 4104;
-    top_margin  = 12;
-    left_margin = 48;
-    load_raw = &CLASS canon_a5_load_raw;
-  } else if (!strcmp(model,"PowerShot S3 IS")) {
-    height = 2128;
-    width  = 2840;
-    raw_height = 2136;
-    raw_width  = 2888;
-    top_margin  = 8;
-    left_margin = 44;
-    load_raw = &CLASS canon_a5_load_raw;
-  } else if (!strcmp(model,"PowerShot Pro90 IS")) {
-    width  = 1896;
+canon_a5:
+    colors = 4;
+    tiff_bps = 10;
+    load_raw = &CLASS packed_load_raw;
+    load_flags = 264;
+  } else if (!strcmp(model,"PowerShot Pro90 IS") ||
+	     !strcmp(model,"PowerShot G1")) {
     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;
-    if (unique_id == 0x80000170)
-      adobe_coeff ("Canon","EOS 300D");
-    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,"PowerShot A610")) {
+    if (canon_s2is()) strcpy (model+10, "S2 IS");
+  } else if (!strcmp(model,"PowerShot SX220 HS")) {
+    mask[1][3] = -4;
   } else if (!strcmp(model,"EOS D2000C")) {
     filters = 0x61616161;
     black = curve[200];
-  } else if (is_canon && raw_width == 3516) {
-    top_margin  = 14;
-    left_margin = 42;
-    if (unique_id == 0x80000189)
-      adobe_coeff ("Canon","EOS 350D");
-    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 == 3944) {
-    height = 2602;
-    width  = 3908;
-    top_margin  = 18;
-    left_margin = 30;
-    maximum = 0x3f60;
-  } else if (is_canon && raw_width == 3948) {
-    top_margin  = 18;
-    left_margin = 42;
-    height -= 2;
-    if (unique_id == 0x80000236)
-      adobe_coeff ("Canon","EOS 400D");
-    goto canon_cr2;
-  } else if (is_canon && raw_width == 3984) {
-    top_margin  = 20;
-    left_margin = 76;
-    height -= 2;
-    maximum = 0x3bb0;
-    goto canon_cr2;
-  } else if (is_canon && raw_width == 4104) {
-    height = 3024;
-    width  = 4032;
-    top_margin  = 12;
-    left_margin = 48;
-  } else if (is_canon && raw_width == 4476) {
-    top_margin  = 34;
-    left_margin = 90;
-    maximum = 0xe6c;
-    goto canon_cr2;
-  } else if (is_canon && raw_width == 5108) {
-    top_margin  = 13;
-    left_margin = 98;
-    maximum = 0xe80;
-canon_cr2:
-    height -= top_margin;
-    width  -= left_margin;
-  } else if (is_canon && raw_width == 5712) {
-    height = 3752;
-    width  = 5640;
-    top_margin  = 20;
-    left_margin = 62;
-    maximum = 0x3bb0;
+  } else if (!strcmp(model,"EOS 80D")) {
+    top_margin -= 2;
+    height += 2;
   } else if (!strcmp(model,"D1")) {
     cam_mul[0] *= 256/527.0;
     cam_mul[2] *= 256/317.0;
@@ -6901,62 +9031,71 @@
     width -= 4;
     pixel_aspect = 0.5;
   } else if (!strcmp(model,"D40X") ||
-	     !strcmp(model,"D80")) {
+	     !strcmp(model,"D60")  ||
+	     !strcmp(model,"D80")  ||
+	     !strcmp(model,"D3000")) {
     height -= 3;
     width  -= 4;
+  } else if (!strcmp(model,"D3")   ||
+	     !strcmp(model,"D3S")  ||
+	     !strcmp(model,"D700")) {
+    width -= 4;
+    left_margin = 2;
+  } else if (!strcmp(model,"D3100")) {
+    width -= 28;
+    left_margin = 6;
+  } else if (!strcmp(model,"D5000") ||
+	     !strcmp(model,"D90")) {
+    width -= 42;
+  } else if (!strcmp(model,"D5100") ||
+	     !strcmp(model,"D7000") ||
+	     !strcmp(model,"COOLPIX A")) {
+    width -= 44;
+  } else if (!strcmp(model,"D3200") ||
+	    !strncmp(model,"D6",2)  ||
+	    !strncmp(model,"D800",4)) {
+    width -= 46;
+  } else if (!strcmp(model,"D4") ||
+	     !strcmp(model,"Df")) {
+    width -= 52;
+    left_margin = 2;
   } else if (!strncmp(model,"D40",3) ||
 	     !strncmp(model,"D50",3) ||
 	     !strncmp(model,"D70",3)) {
     width--;
   } else if (!strcmp(model,"D100")) {
-    if (tiff_compress == 34713 && !nikon_is_compressed()) {
-      load_raw = &CLASS nikon_load_raw;
+    if (load_flags)
       raw_width = (width += 3) + 3;
-    }
-    maximum = 0xf44;
   } else if (!strcmp(model,"D200")) {
     left_margin = 1;
     width -= 4;
-    maximum = 0xfbc;
     filters = 0x94949494;
   } else if (!strncmp(model,"D2H",3)) {
     left_margin = 6;
     width -= 14;
-  } else if (!strcmp(model,"D2X")) {
-    width -= 8;
-    maximum = 0xf35;
-  } else if (!strcmp(model,"D3")) {
-    width -= 4;
-    left_margin = 2;
-  } else if (!strcmp(model,"D300")) {
+  } else if (!strncmp(model,"D2X",3)) {
+    if (width == 3264) width -= 32;
+    else width -= 8;
+  } else if (!strncmp(model,"D300",4)) {
     width -= 32;
+  } else if (!strncmp(model,"COOLPIX B",9)) {
+    load_flags = 24;
+  } else if (!strncmp(model,"COOLPIX P",9) && raw_width != 4032) {
+    load_flags = 24;
+    filters = 0x94949494;
+    if (model[9] == '7' && iso_speed >= 400)
+      black = 255;
+  } else if (!strncmp(model,"1 ",2)) {
+    height -= 2;
   } else if (fsize == 1581060) {
-    height = 963;
-    width = 1287;
-    raw_width = 1632;
-    load_raw = &CLASS nikon_e900_load_raw;
-    maximum = 0x3f4;
-    colors = 4;
-    filters = 0x1e1e1e1e;
     simple_coeff(3);
     pre_mul[0] = 1.2085;
     pre_mul[1] = 1.0943;
     pre_mul[3] = 1.1103;
-  } else if (fsize == 2465792) {
-    height = 1203;
-    width  = 1616;
-    raw_width = 2048;
-    load_raw = &CLASS nikon_e900_load_raw;
-    maximum = 0x3dd;
-    colors = 4;
-    filters = 0x4b4b4b4b;
-    adobe_coeff ("NIKON","E950");
+  } else if (fsize == 3178560) {
+    cam_mul[0] *= 4;
+    cam_mul[2] *= 4;
   } else if (fsize == 4771840) {
-    height = 1540;
-    width  = 2064;
-    colors = 4;
-    filters = 0xe1e1e1e1;
-    load_raw = &CLASS nikon_load_raw;
     if (!timestamp && nikon_e995())
       strcpy (model, "E995");
     if (strcmp(model,"E995")) {
@@ -6966,89 +9105,70 @@
       pre_mul[1] = 1.246;
       pre_mul[2] = 1.018;
     }
-  } else if (!strcmp(model,"E2100")) {
-    if (!timestamp && !nikon_e2100()) goto cp_e2500;
-    height = 1206;
-    width  = 1616;
-    load_raw = &CLASS nikon_e2100_load_raw;
-  } else if (!strcmp(model,"E2500")) {
-cp_e2500:
-    strcpy (model, "E2500");
-    height = 1204;
-    width  = 1616;
-    colors = 4;
-    filters = 0x4b4b4b4b;
+  } else if (fsize == 2940928) {
+    if (!timestamp && !nikon_e2100())
+      strcpy (model,"E2500");
+    if (!strcmp(model,"E2500")) {
+      height -= 2;
+      load_flags = 6;
+      colors = 4;
+      filters = 0x4b4b4b4b;
+    }
   } else if (fsize == 4775936) {
-    height = 1542;
-    width  = 2064;
-    load_raw = &CLASS nikon_e2100_load_raw;
-    pre_mul[0] = 1.818;
-    pre_mul[2] = 1.618;
     if (!timestamp) nikon_3700();
     if (model[0] == 'E' && atoi(model+1) < 3700)
       filters = 0x49494949;
     if (!strcmp(model,"Optio 33WR")) {
       flip = 1;
       filters = 0x16161616;
-      pre_mul[0] = 1.331;
-      pre_mul[2] = 1.820;
+    }
+    if (make[0] == 'O') {
+      i = find_green (12, 32, 1188864, 3576832);
+      c = find_green (12, 32, 2383920, 2387016);
+      if (abs(i) < abs(c)) {
+	SWAP(i,c);
+	load_flags = 24;
+      }
+      if (i < 0) filters = 0x61616161;
     }
   } else if (fsize == 5869568) {
-    height = 1710;
-    width  = 2288;
-    filters = 0x16161616;
     if (!timestamp && minolta_z2()) {
       strcpy (make, "Minolta");
       strcpy (model,"DiMAGE Z2");
     }
-    if (make[0] == 'M')
-      load_raw = &CLASS nikon_e2100_load_raw;
-  } else if (!strcmp(model,"E4500")) {
-    height = 1708;
-    width  = 2288;
-    colors = 4;
-    filters = 0xb4b4b4b4;
-  } else if (fsize == 7438336) {
-    height = 1924;
-    width  = 2576;
-    colors = 4;
-    filters = 0xb4b4b4b4;
-  } else if (fsize == 8998912) {
-    height = 2118;
-    width  = 2832;
-    maximum = 0xf83;
-    load_raw = &CLASS nikon_e2100_load_raw;
-  } else if (!strcmp(model,"FinePix S5100") ||
-	     !strcmp(model,"FinePix S5500")) {
-    load_raw = &CLASS unpacked_load_raw;
-    maximum = 0x3e00;
-  } else if (!strcmp(make,"FUJIFILM")) {
+    load_flags = 6 + 24*(make[0] == 'M');
+  } else if (fsize == 6291456) {
+    fseek (ifp, 0x300000, SEEK_SET);
+    if ((order = guess_byte_order(0x10000)) == 0x4d4d) {
+      height -= (top_margin = 16);
+      width -= (left_margin = 28);
+      maximum = 0xf5c0;
+      strcpy (make, "ISG");
+      model[0] = 0;
+    }
+  } else if (!strcmp(make,"Fujifilm")) {
     if (!strcmp(model+7,"S2Pro")) {
-      strcpy (model+7," S2Pro");
+      strcpy (model,"S2Pro");
       height = 2144;
       width  = 2880;
       flip = 6;
-    } else
-      maximum = 0x3e00;
-    if (is_raw == 2 && shot_select)
-      maximum = 0x2f00;
-    top_margin = (raw_height - height)/2;
-    left_margin = (raw_width - width )/2;
-    if (is_raw == 2)
-      data_offset += (shot_select > 0) * ( fuji_layout ?
-		(raw_width *= 2) : raw_height*raw_width*2 );
-    fuji_width = width >> !fuji_layout;
-    width = (height >> fuji_layout) + fuji_width;
-    raw_height = height;
-    height = width - 1;
-    load_raw = &CLASS fuji_load_raw;
-    if (!(fuji_width & 1)) filters = 0x49494949;
-  } else if (!strcmp(model,"RD175")) {
-    height = 986;
-    width = 1534;
-    data_offset = 513;
-    filters = 0x61616161;
-    load_raw = &CLASS minolta_rd175_load_raw;
+    }
+    top_margin = (raw_height - height) >> 2 << 1;
+    left_margin = (raw_width - width ) >> 2 << 1;
+    if (width == 2848 || width == 3664) filters = 0x16161616;
+    if (width == 4032 || width == 4952 || width == 6032 || width == 8280) left_margin = 0;
+    if (width == 3328 && (width -= 66)) left_margin = 34;
+    if (width == 4936) left_margin = 4;
+    if (!strcmp(model,"HS50EXR") ||
+	!strcmp(model,"F900EXR")) {
+      width += 2;
+      left_margin = 0;
+      filters = 0x16161616;
+    }
+    if (fuji_layout) raw_width *= is_raw;
+    if (filters == 9)
+      FORC(36) ((char *)xtrans)[c] =
+	xtrans_abs[(c/6+top_margin) % 6][(c+left_margin) % 6];
   } else if (!strcmp(model,"KD-400Z")) {
     height = 1712;
     width  = 2312;
@@ -7056,21 +9176,20 @@
     goto konica_400z;
   } else if (!strcmp(model,"KD-510Z")) {
     goto konica_510z;
-  } else if (!strcasecmp(make,"MINOLTA")) {
-    load_raw = &CLASS unpacked_load_raw;
-    maximum = 0xf7d;
+  } else if (!strcasecmp(make,"Minolta")) {
+    if (!load_raw && (maximum = 0xfff))
+      load_raw = &CLASS unpacked_load_raw;
     if (!strncmp(model,"DiMAGE A",8)) {
       if (!strcmp(model,"DiMAGE A200"))
 	filters = 0x49494949;
-      load_raw = &CLASS packed_12_load_raw;
-      maximum = model[8] == '1' ? 0xf8b : 0xfff;
+      tiff_bps = 12;
+      load_raw = &CLASS packed_load_raw;
     } else if (!strncmp(model,"ALPHA",5) ||
 	       !strncmp(model,"DYNAX",5) ||
 	       !strncmp(model,"MAXXUM",6)) {
       sprintf (model+20, "DYNAX %-10s", model+6+(model[0]=='M'));
       adobe_coeff (make, model+20);
-      load_raw = &CLASS packed_12_load_raw;
-      maximum = 0xffb;
+      load_raw = &CLASS packed_load_raw;
     } else if (!strncmp(model,"DiMAGE G",8)) {
       if (model[8] == '4') {
 	height = 1716;
@@ -7091,127 +9210,69 @@
       maximum = 0x3df;
       order = 0x4d4d;
     }
+  } else if (!strcmp(model,"*ist D")) {
+    load_raw = &CLASS unpacked_load_raw;
+    data_error = -1;
   } else if (!strcmp(model,"*ist DS")) {
     height -= 2;
-  } else if (!strcmp(model,"K20D")) {
-    filters = 0x16161616;
-  } else if (!strcmp(model,"Optio S")) {
-    if (fsize == 3178560) {
-      height = 1540;
-      width  = 2064;
-      load_raw = &CLASS eight_bit_load_raw;
-      cam_mul[0] *= 4;
-      cam_mul[2] *= 4;
-      pre_mul[0] = 1.391;
-      pre_mul[2] = 1.188;
-    } else {
-      height = 1544;
-      width  = 2068;
-      raw_width = 3136;
-      load_raw = &CLASS packed_12_load_raw;
-      maximum = 0xf7c;
-      pre_mul[0] = 1.137;
-      pre_mul[2] = 1.453;
-    }
-  } else if (fsize == 6114240) {
-    height = 1737;
-    width  = 2324;
-    raw_width = 3520;
-    load_raw = &CLASS packed_12_load_raw;
-    maximum = 0xf7a;
-    pre_mul[0] = 1.980;
-    pre_mul[2] = 1.570;
-  } else if (!strcmp(model,"Optio 750Z")) {
-    height = 2302;
-    width  = 3072;
-    load_raw = &CLASS nikon_e2100_load_raw;
-  } else if (!strcmp(model,"STV680 VGA")) {
-    height = 484;
-    width  = 644;
-    load_raw = &CLASS eight_bit_load_raw;
-    flip = 2;
-    filters = 0x16161616;
-    black = 16;
-    pre_mul[0] = 1.097;
-    pre_mul[2] = 1.128;
-  } else if (!strcmp(model,"KAI-0340")) {
-    height = 477;
-    width  = 640;
+  } else if (!strcmp(make,"Samsung") && raw_width == 4704) {
+    height -= top_margin = 8;
+    width -= 2 * (left_margin = 8);
+    load_flags = 256;
+  } else if (!strcmp(make,"Samsung") && raw_height == 3714) {
+    height -= top_margin = 18;
+    left_margin = raw_width - (width = 5536);
+    if (raw_width != 5600)
+      left_margin = top_margin = 0;
+    filters = 0x61616161;
+    colors = 3;
+  } else if (!strcmp(make,"Samsung") && raw_width == 5632) {
     order = 0x4949;
-    data_offset = 3840;
-    load_raw = &CLASS unpacked_load_raw;
-    pre_mul[0] = 1.561;
-    pre_mul[2] = 2.454;
-  } else if (!strcmp(model,"N95")) {
-    height = raw_height - (top_margin = 2);
-  } else if (!strcmp(model,"531C")) {
-    height = 1200;
-    width  = 1600;
-    load_raw = &CLASS unpacked_load_raw;
-    filters = 0x49494949;
-    pre_mul[1] = 1.218;
-  } else if (!strcmp(model,"F-080C")) {
-    height = 768;
-    width  = 1024;
-    load_raw = &CLASS eight_bit_load_raw;
-  } else if (!strcmp(model,"F-145C")) {
-    height = 1040;
-    width  = 1392;
-    load_raw = &CLASS eight_bit_load_raw;
-  } else if (!strcmp(model,"F-201C")) {
-    height = 1200;
-    width  = 1600;
-    load_raw = &CLASS eight_bit_load_raw;
-  } else if (!strcmp(model,"F-510C")) {
-    height = 1958;
-    width  = 2588;
-    load_raw = fsize < 7500000 ?
-	&CLASS eight_bit_load_raw : &CLASS unpacked_load_raw;
-    maximum = 0xfff0;
-  } else if (!strcmp(model,"F-810C")) {
-    height = 2469;
-    width  = 3272;
-    load_raw = &CLASS unpacked_load_raw;
-    maximum = 0xfff0;
-  } else if (!strcmp(model,"XCD-SX910CR")) {
-    height = 1024;
-    width  = 1375;
-    raw_width = 1376;
+    height = 3694;
+    top_margin = 2;
+    width  = 5574 - (left_margin = 32 + tiff_bps);
+    if (tiff_bps == 12) load_flags = 80;
+  } else if (!strcmp(make,"Samsung") && raw_width == 5664) {
+    height -= top_margin = 17;
+    left_margin = 96;
+    width = 5544;
     filters = 0x49494949;
-    maximum = 0x3ff;
-    load_raw = fsize < 2000000 ?
-	&CLASS eight_bit_load_raw : &CLASS unpacked_load_raw;
-  } else if (!strcmp(model,"2010")) {
-    height = 1207;
-    width  = 1608;
-    order = 0x4949;
-    filters = 0x16161616;
-    data_offset = 3212;
-    maximum = 0x3ff;
-    load_raw = &CLASS unpacked_load_raw;
-  } else if (!strcmp(model,"A782")) {
-    height = 3000;
-    width  = 2208;
+  } else if (!strcmp(make,"Samsung") && raw_width == 6496) {
     filters = 0x61616161;
-    load_raw = fsize < 10000000 ?
-	&CLASS eight_bit_load_raw : &CLASS unpacked_load_raw;
-    maximum = 0xffc0;
-  } else if (!strcmp(model,"3320AF")) {
-    height = 1536;
-    raw_width = width = 2048;
-    filters = 0x61616161;
-    load_raw = &CLASS unpacked_load_raw;
-    maximum = 0x3ff;
-    pre_mul[0] = 1.717;
-    pre_mul[2] = 1.138;
-    fseek (ifp, 0x300000, SEEK_SET);
-    if ((order = guess_byte_order(0x10000)) == 0x4d4d) {
-      height -= (top_margin = 16);
-      width -= (left_margin = 28);
-      maximum = 0xf5c0;
-      strcpy (make, "ISG");
-      model[0] = 0;
+    black = 1 << (tiff_bps - 7);
+  } else if (!strcmp(model,"EX1")) {
+    order = 0x4949;
+    height -= 20;
+    top_margin = 2;
+    if ((width -= 6) > 3682) {
+      height -= 10;
+      width  -= 46;
+      top_margin = 8;
     }
+  } else if (!strcmp(model,"WB2000")) {
+    order = 0x4949;
+    height -= 3;
+    top_margin = 2;
+    if ((width -= 10) > 3718) {
+      height -= 28;
+      width  -= 56;
+      top_margin = 8;
+    }
+  } else if (strstr(model,"WB550")) {
+    strcpy (model, "WB550");
+  } else if (!strcmp(model,"EX2F")) {
+    height = 3045;
+    width  = 4070;
+    top_margin = 3;
+    order = 0x4949;
+    filters = 0x49494949;
+    load_raw = &CLASS unpacked_load_raw;
+  } else if (!strcmp(model,"STV680 VGA")) {
+    black = 16;
+  } else if (!strcmp(model,"N95")) {
+    height = raw_height - (top_margin = 2);
+  } else if (!strcmp(model,"640x480")) {
+    gamma_curve (0.45, 4.5, 1, 255);
   } else if (!strcmp(make,"Hasselblad")) {
     if (load_raw == &CLASS lossless_jpeg_load_raw)
       load_raw = &CLASS hasselblad_load_raw;
@@ -7221,22 +9282,48 @@
       top_margin  = 4;
       left_margin = 7;
       filters = 0x61616161;
-    }
-  } else if (!strcmp(make,"Sinar")) {
-    if (!memcmp(head,"8BPS",4)) {
-      fseek (ifp, 14, SEEK_SET);
-      height = get4();
-      width  = get4();
+    } else if (raw_width == 7410 || raw_width == 8282) {
+      height -= 84;
+      width  -= 82;
+      top_margin  = 4;
+      left_margin = 41;
       filters = 0x61616161;
-      data_offset = 68;
+    } else if (raw_width == 8384) {
+      height = 6208;
+      width  = 8280;
+      top_margin  = 96;
+      left_margin = 46;
+    } else if (raw_width == 9044) {
+      height = 6716;
+      width  = 8964;
+      top_margin  = 8;
+      left_margin = 40;
+      black += load_flags = 256;
+      maximum = 0x8101;
+    } else if (raw_width == 4090) {
+      strcpy (model, "V96C");
+      height -= (top_margin = 6);
+      width -= (left_margin = 3) + 7;
+      filters = 0x61616161;
+    }
+    if (tiff_samples > 1) {
+      is_raw = tiff_samples+1;
+      if (!shot_select && !half_size) filters = 0;
     }
+  } else if (!strcmp(make,"Sinar")) {
     if (!load_raw) load_raw = &CLASS unpacked_load_raw;
+    if (is_raw > 1 && !shot_select && !half_size) filters = 0;
     maximum = 0x3fff;
   } else if (!strcmp(make,"Leaf")) {
     maximum = 0x3fff;
+    fseek (ifp, data_offset, SEEK_SET);
+    if (ljpeg_start (&jh, 1) && jh.bits == 15)
+      maximum = 0x1fff;
     if (tiff_samples > 1) filters = 0;
-    if (tiff_samples > 1 || tile_length < raw_height)
+    if (tiff_samples > 1 || tile_length < raw_height) {
       load_raw = &CLASS leaf_hdr_load_raw;
+      raw_width = tile_width;
+    }
     if ((width | height) == 2048) {
       if (tiff_samples == 1) {
 	filters = 1;
@@ -7269,133 +9356,69 @@
       width -= 2 * (left_margin = 24);
       filters = 0x16161616;
     }
-  } else if (!strcmp(make,"LEICA") || !strcmp(make,"Panasonic")) {
-    maximum = 0xfff0;
-    if ((fsize-data_offset) / (width*8/7) == height)
+  } else if (!strcmp(make,"Leica") || !strcmp(make,"Panasonic")) {
+    if ((flen - data_offset) / (raw_width*8/7) == raw_height)
       load_raw = &CLASS panasonic_load_raw;
-    if (!load_raw) load_raw = &CLASS unpacked_load_raw;
-    switch (width) {
-      case 2568:
-	adobe_coeff ("Panasonic","DMC-LC1");  break;
-      case 3130:
-	left_margin = -14;
-      case 3170:
-	left_margin += 18;
-	width = 3096;
-	if (height > 2326) {
-	  height = 2326;
-	  top_margin = 13;
-	  filters = 0x49494949;
-	}
-	maximum = 0xf7f0;
-	zero_is_bad = 1;
-	adobe_coeff ("Panasonic","DMC-FZ8");  break;
-      case 3177:
-	width -= 10;
-	filters = 0x49494949;
-	maximum = 0xf7fc;
-	zero_is_bad = 1;
-	adobe_coeff ("Panasonic","DMC-L1");  break;
-      case 3304:
-	width -= 16;
-	maximum = 0xf94c;
-	zero_is_bad = 1;
-	adobe_coeff ("Panasonic","DMC-FZ30");  break;
-      case 3330:
-	width = 3291;
-	left_margin = 9;
-	maximum = 0xf7f0;
-	goto fz18;
-      case 3370:
-	width = 3288;
-	left_margin = 15;
-fz18:	if (height > 2480)
-	    height = 2480 - (top_margin = 10);
-	filters = 0x49494949;
-	zero_is_bad = 1;
-	break;
-      case 3690:
-	height += 36;
-	left_margin = -14;
-	filters = 0x49494949;
-	maximum = 0xf7f0;
-      case 3770:
-	width = 3672;
-	if ((height -= 39) == 2760)
-	  top_margin = 15;
-	left_margin += 17;
-	zero_is_bad = 1;
-	adobe_coeff ("Panasonic","DMC-FZ50");  break;
-      case 3710:
-	width = 3682;
-	filters = 0x49494949;
-	break;
-      case 3880:
-	width -= 22;
-	left_margin = 6;
-	maximum = 0xf7f0;
-	zero_is_bad = 1;
-	adobe_coeff ("Panasonic","DMC-LX1");  break;
-      case 4290:
-	height += 38;
-	left_margin = -14;
-	filters = 0x49494949;
-      case 4330:
-	width = 4248;
-	if ((height -= 39) == 2400)
-	  top_margin = 15;
-	left_margin += 17;
-	adobe_coeff ("Panasonic","DMC-LX2");  break;
+    if (!load_raw) {
+      load_raw = &CLASS unpacked_load_raw;
+      load_flags = 4;
     }
+    zero_is_bad = 1;
+    if ((height += 12) > raw_height) height = raw_height;
+    for (i=0; i < sizeof pana / sizeof *pana; i++)
+      if (raw_width == pana[i][0] && raw_height == pana[i][1]) {
+	left_margin = pana[i][2];
+	 top_margin = pana[i][3];
+	     width += pana[i][4];
+	    height += pana[i][5];
+      }
+    filters = 0x01010101 * (uchar) "\x94\x61\x49\x16"
+	[((filters-1) ^ (left_margin & 1) ^ (top_margin << 1)) & 3];
   } else if (!strcmp(model,"C770UZ")) {
     height = 1718;
     width  = 2304;
     filters = 0x16161616;
-    load_raw = &CLASS nikon_e2100_load_raw;
-  } else if (!strcmp(make,"OLYMPUS")) {
+    load_raw = &CLASS packed_load_raw;
+    load_flags = 30;
+  } else if (!strcmp(make,"Olympus")) {
     height += height & 1;
-    filters = exif_cfa;
-    if (!strcmp(model,"E-1") ||
-	!strcmp(model,"E-400")) {
-      maximum = 0xfff0;
-    } else if (!strcmp(model,"E-10") ||
-	      !strncmp(model,"E-20",4)) {
-      maximum = 0xffc0;
-      black <<= 2;
-    } else if (!strcmp(model,"E-300") ||
-	       !strcmp(model,"E-500")) {
+    if (exif_cfa) filters = exif_cfa;
+    if (width == 4100) width -= 4;
+    if (width == 4080) width -= 24;
+    if (width == 9280) { width -= 6; height -= 6; }
+    if (load_raw == &CLASS unpacked_load_raw)
+      load_flags = 4;
+    tiff_bps = 12;
+    if (!strcmp(model,"E-300") ||
+	!strcmp(model,"E-500")) {
       width -= 20;
       if (load_raw == &CLASS unpacked_load_raw) {
-	maximum = 0xfc30;
-	black = 0;
+	maximum = 0xfc3;
+	memset (cblack, 0, sizeof cblack);
       }
     } else if (!strcmp(model,"E-330")) {
       width -= 30;
       if (load_raw == &CLASS unpacked_load_raw)
-	maximum = 0xf790;
-    } else if (!strcmp(model,"E-3")) {
-      maximum = 0xf99;
-      goto e410;
-    } else if (!strcmp(model,"E-410") ||
-	       !strcmp(model,"E-510")) {
-      maximum = 0xf6a;
-e410: load_raw = &CLASS olympus_e410_load_raw;
-      black >>= 4;
+	maximum = 0xf79;
     } else if (!strcmp(model,"SP550UZ")) {
-      thumb_length = fsize - (thumb_offset = 0xa39800);
+      thumb_length = flen - (thumb_offset = 0xa39800);
       thumb_height = 480;
       thumb_width  = 640;
+    } else if (!strcmp(model,"TG-4")) {
+      width -= 16;
+    } else if (!strcmp(model,"TG-5")) {
+      width -= 6;
     }
   } else if (!strcmp(model,"N Digital")) {
     height = 2047;
     width  = 3072;
     filters = 0x61616161;
     data_offset = 0x1a00;
-    load_raw = &CLASS packed_12_load_raw;
-    maximum = 0xf1e;
+    load_raw = &CLASS packed_load_raw;
   } else if (!strcmp(model,"DSC-F828")) {
     width = 3288;
     left_margin = 5;
+    mask[1][3] = -17;
     data_offset = 862144;
     load_raw = &CLASS sony_load_raw;
     filters = 0x9c9c9c9c;
@@ -7404,82 +9427,94 @@
   } else if (!strcmp(model,"DSC-V3")) {
     width = 3109;
     left_margin = 59;
+    mask[0][1] = 9;
     data_offset = 787392;
     load_raw = &CLASS sony_load_raw;
-  } else if (!strcmp(make,"SONY") && raw_width == 3984) {
-    adobe_coeff ("SONY","DSC-R1");
+  } else if (!strcmp(make,"Sony") && raw_width == 3984) {
     width = 3925;
     order = 0x4d4d;
+  } else if (!strcmp(make,"Sony") && raw_width == 4288) {
+    width -= 32;
+  } else if (!strcmp(make,"Sony") && raw_width == 4600) {
+    if (!strcmp(model,"DSLR-A350"))
+      height -= 4;
+    black = 0;
+  } else if (!strcmp(make,"Sony") && raw_width == 4928) {
+    if (height < 3280) width -= 8;
+  } else if (!strcmp(make,"Sony") && raw_width == 5504) {
+    width -= height > 3664 ? 8 : 32;
+    if (!strncmp(model,"DSC",3))
+      black = 200 << (tiff_bps - 12);
+  } else if (!strcmp(make,"Sony") && raw_width == 6048) {
+    width -= 24;
+    if (strstr(model,"RX1") || strstr(model,"A99"))
+      width -= 6;
+  } else if (!strcmp(make,"Sony") && raw_width == 7392) {
+    width -= 30;
+  } else if (!strcmp(make,"Sony") && raw_width == 8000) {
+    width -= 32;
   } else if (!strcmp(model,"DSLR-A100")) {
-    height--;
-    load_raw = &CLASS sony_arw_load_raw;
-    maximum = 0xfeb;
-  } else if (!strcmp(model,"DSLR-A200")) {
-    height = raw_height += 8;
-    load_raw = &CLASS sony_arw_load_raw;
-  } else if (!strcmp(model,"DSLR-A350")) {
-    height = (raw_height += 8) - 4;
-    load_raw = &CLASS sony_arw_load_raw;
-    maximum = 0x1ffe;
-  } else if (!strncmp(model,"P850",4)) {
-    maximum = 0xf7c;
-  } else if (!strcmp(model,"C330")) {
-    height = 1744;
-    width  = 2336;
-    raw_height = 1779;
-    raw_width  = 2338;
-    top_margin = 33;
-    left_margin = 1;
+    if (width == 3880) {
+      height--;
+      width = ++raw_width;
+    } else {
+      height -= 4;
+      width  -= 4;
+      order = 0x4d4d;
+      load_flags = 2;
+    }
+    filters = 0x61616161;
+  } else if (!strcmp(model,"PIXL")) {
+    height -= top_margin = 4;
+    width -= left_margin = 32;
+    gamma_curve (0, 7, 1, 255);
+  } else if (!strcmp(model,"C603") || !strcmp(model,"C330")
+	|| !strcmp(model,"12MP")) {
     order = 0x4949;
-    if ((data_offset = fsize - raw_height*raw_width)) {
-      fseek (ifp, 168, SEEK_SET);
+    if (filters && data_offset) {
+      fseek (ifp, data_offset < 4096 ? 168 : 5252, SEEK_SET);
       read_shorts (curve, 256);
-    } else use_gamma = 0;
-    load_raw = &CLASS eight_bit_load_raw;
-  } else if (!strcasecmp(make,"KODAK")) {
+    } else gamma_curve (0, 3.875, 1, 255);
+    load_raw  =  filters   ? &CLASS eight_bit_load_raw :
+      strcmp(model,"C330") ? &CLASS kodak_c603_load_raw :
+			     &CLASS kodak_c330_load_raw;
+    load_flags = tiff_bps > 16;
+    tiff_bps = 8;
+  } else if (!strncasecmp(model,"EasyShare",9)) {
+    data_offset = data_offset < 0x15000 ? 0x15000 : 0x17000;
+    load_raw = &CLASS packed_load_raw;
+  } else if (!strcasecmp(make,"Kodak")) {
     if (filters == UINT_MAX) filters = 0x61616161;
-    if (!strncmp(model,"NC2000",6)) {
+    if (!strncmp(model,"NC2000",6) ||
+	!strncmp(model,"EOSDCS",6) ||
+	!strncmp(model,"DCS4",4)) {
       width -= 4;
       left_margin = 2;
-    } else if (!strcmp(model,"EOSDCS3B")) {
-      width -= 4;
-      left_margin = 2;
-    } else if (!strcmp(model,"EOSDCS1")) {
-      width -= 4;
-      left_margin = 2;
-    } 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;
+      if (model[6] == ' ') model[6] = 0;
+      if (!strcmp(model,"DCS460A")) goto bw;
     } else if (!strcmp(model,"DCS660M")) {
       black = 214;
-      colors = 1;
-      filters = 0;
+      goto bw;
     } else if (!strcmp(model,"DCS760M")) {
-      colors = 1;
+bw:   colors = 1;
       filters = 0;
     }
+    if (!strcmp(model+4,"20X"))
+      strcpy (cdesc, "MYCY");
     if (strstr(model,"DC25")) {
       strcpy (model, "DC25");
       data_offset = 15424;
     }
     if (!strncmp(model,"DC2",3)) {
-      height = 242;
-      if (fsize < 100000) {
+      raw_height = 2 + (height = 242);
+      if (flen < 100000) {
 	raw_width = 256; width = 249;
 	pixel_aspect = (4.0*height) / (3.0*width);
       } else {
 	raw_width = 512; width = 501;
 	pixel_aspect = (493.0*height) / (373.0*width);
       }
-      data_offset += raw_width + 1;
+      top_margin = left_margin = 1;
       colors = 4;
       filters = 0x8d8d8d8d;
       simple_coeff(1);
@@ -7493,6 +9528,7 @@
       width  = 768;
       data_offset = 1152;
       load_raw = &CLASS kodak_radc_load_raw;
+      tiff_bps = 12;
     } else if (strstr(model,"DC50")) {
       strcpy (model, "DC50");
       height = 512;
@@ -7512,10 +9548,6 @@
       thumb_offset = 6144;
       thumb_misc   = 360;
       write_thumb = &CLASS layer_thumb;
-      height = 1024;
-      width  = 1536;
-      data_offset = 79872;
-      load_raw = &CLASS eight_bit_load_raw;
       black = 17;
     }
   } else if (!strcmp(model,"Fotoman Pixtura")) {
@@ -7525,17 +9557,17 @@
     load_raw = &CLASS kodak_radc_load_raw;
     filters = 0x61616161;
     simple_coeff(2);
-  } else if (!strcmp(model,"QuickTake 100")) {
-    data_offset = 736;
-    load_raw = &CLASS quicktake_100_load_raw;
-    goto qt_common;
-  } else if (!strcmp(model,"QuickTake 150")) {
-    data_offset = 738 - head[5];
+  } else if (!strncmp(model,"QuickTake",9)) {
     if (head[5]) strcpy (model+10, "200");
-    load_raw = &CLASS kodak_radc_load_raw;
-qt_common:
-    height = 480;
-    width  = 640;
+    fseek (ifp, 544, SEEK_SET);
+    height = get2();
+    width  = get2();
+    data_offset = (get4(),get2()) == 30 ? 738:736;
+    if (height > width) {
+      SWAP(height,width);
+      fseek (ifp, data_offset-6, SEEK_SET);
+      flip = ~get2() & 3 ? 5:6;
+    }
     filters = 0x61616161;
   } else if (!strcmp(make,"Rollei") && !load_raw) {
     switch (raw_width) {
@@ -7553,100 +9585,10 @@
     }
     filters = 0x16161616;
     load_raw = &CLASS 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 = &CLASS 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 = &CLASS eight_bit_load_raw;
-  } else if (fsize == 3217760) {
-    height = 1546;
-    width  = 2070;
-    raw_width = 2080;
-    load_raw = &CLASS eight_bit_load_raw;
-  } else if (!strcmp(model,"QV-4000")) {
-    height = 1700;
-    width  = 2260;
-    load_raw = &CLASS unpacked_load_raw;
-    maximum = 0xffff;
-  } else if (!strcmp(model,"QV-5700")) {
-    height = 1924;
-    width  = 2576;
-    load_raw = &CLASS casio_qv5700_load_raw;
-  } else if (!strcmp(model,"QV-R41")) {
-    height = 1720;
-    width  = 2312;
-    raw_width = 3520;
-    left_margin = 2;
-    load_raw = &CLASS packed_12_load_raw;
-    maximum = 0xf7f;
-  } else if (!strcmp(model,"QV-R51")) {
-    height = 1926;
-    width  = 2580;
-    raw_width = 3904;
-    load_raw = &CLASS packed_12_load_raw;
-    maximum = 0xf7f;
-    pre_mul[0] = 1.340;
-    pre_mul[2] = 1.672;
-  } else if (!strcmp(model,"EX-S100")) {
-    height = 1544;
-    width  = 2058;
-    raw_width = 3136;
-    load_raw = &CLASS packed_12_load_raw;
-    pre_mul[0] = 1.631;
-    pre_mul[2] = 1.106;
-  } else if (!strcmp(model,"EX-Z50")) {
-    height = 1931;
-    width  = 2570;
-    raw_width = 3904;
-    load_raw = &CLASS packed_12_load_raw;
-    maximum = 0xf7f;
-    pre_mul[0] = 2.529;
-    pre_mul[2] = 1.185;
-  } else if (!strcmp(model,"EX-Z55")) {
-    height = 1960;
-    width  = 2570;
-    raw_width = 3904;
-    load_raw = &CLASS packed_12_load_raw;
-    maximum = 0xf7f;
-    pre_mul[0] = 1.520;
-    pre_mul[2] = 1.316;
-  } else if (!strcmp(model,"EX-P505")) {
-    height = 1928;
-    width  = 2568;
-    raw_width = 3852;
-    load_raw = &CLASS packed_12_load_raw;
-    pre_mul[0] = 2.07;
-    pre_mul[2] = 1.88;
-  } else if (fsize == 9313536) {	/* EX-P600 or QV-R61 */
-    height = 2142;
-    width  = 2844;
-    raw_width = 4288;
-    load_raw = &CLASS packed_12_load_raw;
-    maximum = 0xf7f;
-    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 = &CLASS packed_12_load_raw;
-    maximum = 0xf7f;
-    pre_mul[0] = 1.758;
-    pre_mul[2] = 1.504;
   }
   if (!model[0])
     sprintf (model, "%dx%d", width, height);
   if (filters == UINT_MAX) filters = 0x94949494;
-  if (raw_color) adobe_coeff (make, model);
   if (thumb_offset && !thumb_height) {
     fseek (ifp, thumb_offset, SEEK_SET);
     if (ljpeg_start (&jh, 1)) {
@@ -7655,31 +9597,58 @@
     }
   }
 dng_skip:
-  if (!load_raw || height < 22) is_raw = 0;
-#ifdef HAVE_JPEG
-  if (load_raw == kodak_jpeg_load_raw) {
-    fprintf (stderr,_("%s: You must link dcraw with libjpeg!!\n"), ifname);
+  if ((use_camera_matrix & (use_camera_wb || dng_version))
+	&& cmatrix[0][0] > 0.125) {
+    memcpy (rgb_cam, cmatrix, sizeof cmatrix);
+    raw_color = 0;
+  }
+  if (raw_color) adobe_coeff (make, model);
+  if (load_raw == &CLASS kodak_radc_load_raw)
+    if (raw_color) adobe_coeff ("Apple","Quicktake");
+  if (fuji_width) {
+    fuji_width = width >> !fuji_layout;
+    filters = fuji_width & 1 ? 0x94949494 : 0x49494949;
+    width = (height >> fuji_layout) + fuji_width;
+    height = width - 1;
+    pixel_aspect = 1;
+  } else {
+    if (raw_height < height) raw_height = height;
+    if (raw_width  < width ) raw_width  = width;
+  }
+  if (!tiff_bps) tiff_bps = 12;
+  if (!maximum) maximum = (1 << tiff_bps) - 1;
+  if (!load_raw || height < 22 || width < 22 ||
+	tiff_bps > 16 || tiff_samples > 6 || colors > 4)
+    is_raw = 0;
+#ifdef NO_JASPER
+  if (load_raw == &CLASS redcine_load_raw) {
+    fprintf (stderr,_("%s: You must link dcraw with %s!!\n"),
+	ifname, "libjasper");
+    is_raw = 0;
+  }
+#endif
+#ifdef NO_JPEG
+  if (load_raw == &CLASS kodak_jpeg_load_raw ||
+      load_raw == &CLASS lossy_dng_load_raw) {
+    fprintf (stderr,_("%s: You must link dcraw with %s!!\n"),
+	ifname, "libjpeg");
     is_raw = 0;
   }
 #endif
   if (!cdesc[0])
-    strcpy (cdesc, colors == 3 ? "RGB":"GMCY");
+    strcpy (cdesc, colors == 3 ? "RGBG":"GMCY");
   if (!raw_height) raw_height = height;
   if (!raw_width ) raw_width  = width;
-  if (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;
-    }
+  if (filters > 999 && colors == 3)
+    filters |= ((filters >> 2 & 0x22222222) |
+		(filters << 2 & 0x88888888)) & filters << 1;
 notraw:
-  if (flip == -1) flip = tiff_flip;
-  if (flip == -1) flip = 0;
+  if (flip == UINT_MAX) flip = tiff_flip;
+  if (flip == UINT_MAX) flip = 0;
 }
 
 #ifndef NO_LCMS
-void CLASS apply_profile (char *input, char *output)
+void CLASS apply_profile (const char *input, const char *output)
 {
   char *prof;
   cmsHPROFILE hInProfile=0, hOutProfile=0;
@@ -7687,9 +9656,6 @@
   FILE *fp;
   unsigned size;
 
-#if LCMS_VERSION < 2000
-  cmsErrorAction (LCMS_ERROR_SHOW);
-#endif
   if (strcmp (input, "embed"))
     hInProfile = cmsOpenProfileFromFile (input, "r");
   else if (profile_length) {
@@ -7755,10 +9721,14 @@
   { { 0.529317, 0.330092, 0.140588 },
     { 0.098368, 0.873465, 0.028169 },
     { 0.016879, 0.117663, 0.865457 } };
+  static const double aces_rgb[3][3] =
+  { { 0.432996, 0.375380, 0.189317 },
+    { 0.089427, 0.816523, 0.102989 },
+    { 0.019165, 0.118150, 0.941914 } };
   static const double (*out_rgb[])[3] =
-  { rgb_rgb, adobe_rgb, wide_rgb, prophoto_rgb, xyz_rgb };
+  { rgb_rgb, adobe_rgb, wide_rgb, prophoto_rgb, xyz_rgb, aces_rgb };
   static const char *name[] =
-  { "sRGB", "Adobe RGB (1998)", "WideGamut D65", "ProPhoto D65", "XYZ" };
+  { "sRGB", "Adobe RGB (1998)", "WideGamut D65", "ProPhoto D65", "XYZ", "ACES" };
   static const unsigned phead[] =
   { 1024, 0, 0x2100000, 0x6d6e7472, 0x52474220, 0x58595a20, 0, 0, 0,
     0x61637370, 0, 0, 0x6e6f6e65, 0, 0, 0, 0, 0xf6d6, 0x10000, 0xd32d };
@@ -7776,9 +9746,10 @@
   static const unsigned pwhite[] = { 0xf351, 0x10000, 0x116cc };
   unsigned pcurve[] = { 0x63757276, 0, 1, 0x1000000 };
 
+  gamma_curve (gamm[0], gamm[1], 0, 0);
   memcpy (out_cam, rgb_cam, sizeof out_cam);
   raw_color |= colors == 1 || document_mode ||
-		output_color < 1 || output_color > 5;
+		output_color < 1 || output_color > 6;
   if (!raw_color) {
     oprof = (unsigned *) calloc (phead[0], 1);
     merror (oprof, "convert_to_rgb()");
@@ -7793,12 +9764,7 @@
     memcpy (oprof+32, pbody, sizeof pbody);
     oprof[pbody[5]/4+2] = strlen(name[output_color-1]) + 1;
     memcpy ((char *)oprof+pbody[8]+8, pwhite, sizeof pwhite);
-    if (output_bps == 8)
-#ifdef SRGB_GAMMA
-      pcurve[3] = 0x2330000;
-#else
-      pcurve[3] = 0x1f00000;
-#endif
+    pcurve[3] = (short)(256/gamm[5]+0.5) << 16;
     for (i=4; i < 7; i++)
       memcpy ((char *)oprof+pbody[i*3+2], pcurve, sizeof pcurve);
     pseudoinverse ((double (*)[3]) out_rgb[output_color-1], inverse, 3);
@@ -7806,7 +9772,7 @@
       for (j=0; j < 3; j++) {
 	for (num = k=0; k < 3; k++)
 	  num += xyzd50_srgb[i][k] * inverse[j][k];
-        oprof[pbody[j*3+23]/4+i+2] = num * 0x10000 + 0.5;
+	oprof[pbody[j*3+23]/4+i+2] = num * 0x10000 + 0.5;
       }
     for (i=0; i < phead[0]/4; i++)
       oprof[i] = htonl(oprof[i]);
@@ -7834,7 +9800,7 @@
 	FORC3 img[c] = CLIP((int) out[c]);
       }
       else if (document_mode)
-	img[0] = img[FC(row,col)];
+	img[0] = img[fcol(row,col)];
       FORCC histogram[c][img[c] >> 3]++;
     }
   if (colors == 4 && output_color) colors = 3;
@@ -7856,7 +9822,7 @@
   step = sqrt(0.5);
   wide = fuji_width / step;
   high = (height - fuji_width) / step;
-  img = (ushort (*)[4]) calloc (wide*high, sizeof *img);
+  img = (ushort (*)[4]) calloc (high, wide*sizeof *img);
   merror (img, "fuji_rotate()");
 
   for (row=0; row < high; row++)
@@ -7889,7 +9855,7 @@
   if (verbose) fprintf (stderr,_("Stretching the image...\n"));
   if (pixel_aspect < 1) {
     newdim = height / pixel_aspect + 0.5;
-    img = (ushort (*)[4]) calloc (width*newdim, sizeof *img);
+    img = (ushort (*)[4]) calloc (width, newdim*sizeof *img);
     merror (img, "stretch()");
     for (rc=row=0; row < newdim; row++, rc+=pixel_aspect) {
       frac = rc - (c = rc);
@@ -7901,7 +9867,7 @@
     height = newdim;
   } else {
     newdim = width * pixel_aspect + 0.5;
-    img = (ushort (*)[4]) calloc (height*newdim, sizeof *img);
+    img = (ushort (*)[4]) calloc (height, newdim*sizeof *img);
     merror (img, "stretch()");
     for (rc=col=0; col < newdim; col++, rc+=1/pixel_aspect) {
       frac = rc - (c = rc);
@@ -7924,33 +9890,6 @@
   return row * iwidth + col;
 }
 
-void CLASS gamma_lut (uchar lut[0x10000])
-{
-  int perc, c, val, total, i;
-  float white=0, r;
-
-  perc = width * height * 0.01;		/* 99th percentile white level */
-  if (fuji_width) perc /= 2;
-  if ((highlight & ~2) || no_auto_bright) perc = -1;
-  FORCC {
-    for (val=0x2000, total=0; --val > 32; )
-      if ((total += histogram[c][val]) > perc) break;
-    if (white < val) white = val;
-  }
-  white *= 8 / bright;
-  for (i=0; i < 0x10000; i++) {
-    r = i / white;
-    val = 256 * ( !use_gamma ? r :
-#ifdef SRGB_GAMMA
-	r <= 0.00304 ? r*12.92 : pow(r,2.5/6)*1.055-0.055 );
-#else
-	r <= 0.018 ? r*4.5 : pow(r,0.45)*1.099-0.099 );
-#endif
-    if (val > 255) val = 255;
-    lut[i] = val;
-  }
-}
-
 struct tiff_tag {
   ushort tag, type;
   int count;
@@ -7973,21 +9912,25 @@
   char desc[512], make[64], model[64], soft[32], date[20], artist[64];
 };
 
-void CLASS tiff_set (ushort *ntag,
+void CLASS tiff_set (struct tiff_hdr *th, ushort *ntag,
 	ushort tag, ushort type, int count, int val)
 {
   struct tiff_tag *tt;
   int c;
 
   tt = (struct tiff_tag *)(ntag+1) + (*ntag)++;
-  tt->tag = tag;
-  tt->type = type;
-  tt->count = count;
-  if (type < 3 && count <= 4)
+  tt->val.i = val;
+  if (type == 1 && count <= 4)
     FORC(4) tt->val.c[c] = val >> (c << 3);
-  else if (type == 3 && count <= 2)
+  else if (type == 2) {
+    count = strnlen((char *)th + val, count-1) + 1;
+    if (count <= 4)
+      FORC(4) tt->val.c[c] = ((char *)th)[val+c];
+  } else if (type == 3 && count <= 2)
     FORC(2) tt->val.s[c] = val >> (c << 4);
-  else tt->val.i = val;
+  tt->count = count;
+  tt->type = type;
+  tt->tag = tag;
 }
 
 #define TOFF(ptr) ((char *)(&(ptr)) - (char *)th)
@@ -8001,55 +9944,6 @@
   th->order = htonl(0x4d4d4949) >> 16;
   th->magic = 42;
   th->ifd = 10;
-  if (full) {
-    tiff_set (&th->ntag, 254, 4, 1, 0);
-    tiff_set (&th->ntag, 256, 4, 1, width);
-    tiff_set (&th->ntag, 257, 4, 1, height);
-    tiff_set (&th->ntag, 258, 3, colors, output_bps);
-    if (colors > 2)
-      th->tag[th->ntag-1].val.i = TOFF(th->bps);
-    FORC4 th->bps[c] = output_bps;
-    tiff_set (&th->ntag, 259, 3, 1, 1);
-    tiff_set (&th->ntag, 262, 3, 1, 1 + (colors > 1));
-  }
-  tiff_set (&th->ntag, 270, 2, 512, TOFF(th->desc));
-  tiff_set (&th->ntag, 271, 2, 64, TOFF(th->make));
-  tiff_set (&th->ntag, 272, 2, 64, TOFF(th->model));
-  if (full) {
-    if (oprof) psize = ntohl(oprof[0]);
-    tiff_set (&th->ntag, 273, 4, 1, sizeof *th + psize);
-    tiff_set (&th->ntag, 277, 3, 1, colors);
-    tiff_set (&th->ntag, 278, 4, 1, height);
-    tiff_set (&th->ntag, 279, 4, 1, height*width*colors*output_bps/8);
-  } else
-    tiff_set (&th->ntag, 274, 3, 1, "12435867"[flip]-'0');
-  tiff_set (&th->ntag, 282, 5, 1, TOFF(th->rat[0]));
-  tiff_set (&th->ntag, 283, 5, 1, TOFF(th->rat[2]));
-  tiff_set (&th->ntag, 284, 3, 1, 1);
-  tiff_set (&th->ntag, 296, 3, 1, 2);
-  tiff_set (&th->ntag, 305, 2, 32, TOFF(th->soft));
-  tiff_set (&th->ntag, 306, 2, 20, TOFF(th->date));
-  tiff_set (&th->ntag, 315, 2, 64, TOFF(th->artist));
-  tiff_set (&th->ntag, 34665, 4, 1, TOFF(th->nexif));
-  if (psize) tiff_set (&th->ntag, 34675, 7, psize, sizeof *th);
-  tiff_set (&th->nexif, 33434, 5, 1, TOFF(th->rat[4]));
-  tiff_set (&th->nexif, 33437, 5, 1, TOFF(th->rat[6]));
-  tiff_set (&th->nexif, 34855, 3, 1, iso_speed);
-  tiff_set (&th->nexif, 37386, 5, 1, TOFF(th->rat[8]));
-  if (gpsdata[1]) {
-    tiff_set (&th->ntag, 34853, 4, 1, TOFF(th->ngps));
-    tiff_set (&th->ngps,  0, 1,  4, 0x202);
-    tiff_set (&th->ngps,  1, 2,  2, gpsdata[29]);
-    tiff_set (&th->ngps,  2, 5,  3, TOFF(th->gps[0]));
-    tiff_set (&th->ngps,  3, 2,  2, gpsdata[30]);
-    tiff_set (&th->ngps,  4, 5,  3, TOFF(th->gps[6]));
-    tiff_set (&th->ngps,  5, 1,  1, gpsdata[31]);
-    tiff_set (&th->ngps,  6, 5,  1, TOFF(th->gps[18]));
-    tiff_set (&th->ngps,  7, 5,  3, TOFF(th->gps[12]));
-    tiff_set (&th->ngps, 18, 2, 12, TOFF(th->gps[20]));
-    tiff_set (&th->ngps, 29, 2, 12, TOFF(th->gps[23]));
-    memcpy (th->gps, gpsdata, sizeof th->gps);
-  }
   th->rat[0] = th->rat[2] = 300;
   th->rat[1] = th->rat[3] = 1;
   FORC(6) th->rat[4+c] = 1000000;
@@ -8059,14 +9953,63 @@
   strncpy (th->desc, desc, 512);
   strncpy (th->make, make, 64);
   strncpy (th->model, model, 64);
-  strcpy (th->soft, "dcraw v" DCRAW_VERSION);
-  t = gmtime (&timestamp);
+  strcpy (th->soft, "dcraw v"DCRAW_VERSION);
+  t = localtime (&timestamp);
   sprintf (th->date, "%04d:%02d:%02d %02d:%02d:%02d",
       t->tm_year+1900,t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
   strncpy (th->artist, artist, 64);
+  if (full) {
+    tiff_set (th, &th->ntag, 254, 4, 1, 0);
+    tiff_set (th, &th->ntag, 256, 4, 1, width);
+    tiff_set (th, &th->ntag, 257, 4, 1, height);
+    tiff_set (th, &th->ntag, 258, 3, colors, output_bps);
+    if (colors > 2)
+      th->tag[th->ntag-1].val.i = TOFF(th->bps);
+    FORC4 th->bps[c] = output_bps;
+    tiff_set (th, &th->ntag, 259, 3, 1, 1);
+    tiff_set (th, &th->ntag, 262, 3, 1, 1 + (colors > 1));
+  }
+  tiff_set (th, &th->ntag, 270, 2, 512, TOFF(th->desc));
+  tiff_set (th, &th->ntag, 271, 2, 64, TOFF(th->make));
+  tiff_set (th, &th->ntag, 272, 2, 64, TOFF(th->model));
+  if (full) {
+    if (oprof) psize = ntohl(oprof[0]);
+    tiff_set (th, &th->ntag, 273, 4, 1, sizeof *th + psize);
+    tiff_set (th, &th->ntag, 277, 3, 1, colors);
+    tiff_set (th, &th->ntag, 278, 4, 1, height);
+    tiff_set (th, &th->ntag, 279, 4, 1, height*width*colors*output_bps/8);
+  } else
+    tiff_set (th, &th->ntag, 274, 3, 1, "12435867"[flip]-'0');
+  tiff_set (th, &th->ntag, 282, 5, 1, TOFF(th->rat[0]));
+  tiff_set (th, &th->ntag, 283, 5, 1, TOFF(th->rat[2]));
+  tiff_set (th, &th->ntag, 284, 3, 1, 1);
+  tiff_set (th, &th->ntag, 296, 3, 1, 2);
+  tiff_set (th, &th->ntag, 305, 2, 32, TOFF(th->soft));
+  tiff_set (th, &th->ntag, 306, 2, 20, TOFF(th->date));
+  tiff_set (th, &th->ntag, 315, 2, 64, TOFF(th->artist));
+  tiff_set (th, &th->ntag, 34665, 4, 1, TOFF(th->nexif));
+  if (psize) tiff_set (th, &th->ntag, 34675, 7, psize, sizeof *th);
+  tiff_set (th, &th->nexif, 33434, 5, 1, TOFF(th->rat[4]));
+  tiff_set (th, &th->nexif, 33437, 5, 1, TOFF(th->rat[6]));
+  tiff_set (th, &th->nexif, 34855, 3, 1, iso_speed);
+  tiff_set (th, &th->nexif, 37386, 5, 1, TOFF(th->rat[8]));
+  if (gpsdata[1]) {
+    tiff_set (th, &th->ntag, 34853, 4, 1, TOFF(th->ngps));
+    tiff_set (th, &th->ngps,  0, 1,  4, 0x202);
+    tiff_set (th, &th->ngps,  1, 2,  2, gpsdata[29]);
+    tiff_set (th, &th->ngps,  2, 5,  3, TOFF(th->gps[0]));
+    tiff_set (th, &th->ngps,  3, 2,  2, gpsdata[30]);
+    tiff_set (th, &th->ngps,  4, 5,  3, TOFF(th->gps[6]));
+    tiff_set (th, &th->ngps,  5, 1,  1, gpsdata[31]);
+    tiff_set (th, &th->ngps,  6, 5,  1, TOFF(th->gps[18]));
+    tiff_set (th, &th->ngps,  7, 5,  3, TOFF(th->gps[12]));
+    tiff_set (th, &th->ngps, 18, 2, 12, TOFF(th->gps[20]));
+    tiff_set (th, &th->ngps, 29, 2, 12, TOFF(th->gps[23]));
+    memcpy (th->gps, gpsdata, sizeof th->gps);
+  }
 }
 
-void CLASS jpeg_thumb (FILE *tfp)
+void CLASS jpeg_thumb()
 {
   char *thumb;
   ushort exif[5];
@@ -8075,26 +10018,36 @@
   thumb = (char *) malloc (thumb_length);
   merror (thumb, "jpeg_thumb()");
   fread (thumb, 1, thumb_length, ifp);
-  fputc (0xff, tfp);
-  fputc (0xd8, tfp);
+  fputc (0xff, ofp);
+  fputc (0xd8, ofp);
   if (strcmp (thumb+6, "Exif")) {
     memcpy (exif, "\xff\xe1  Exif\0\0", 10);
     exif[1] = htons (8 + sizeof th);
-    fwrite (exif, 1, sizeof exif, tfp);
+    fwrite (exif, 1, sizeof exif, ofp);
     tiff_head (&th, 0);
-    fwrite (&th, 1, sizeof th, tfp);
+    fwrite (&th, 1, sizeof th, ofp);
   }
-  fwrite (thumb+2, 1, thumb_length-2, tfp);
+  fwrite (thumb+2, 1, thumb_length-2, ofp);
   free (thumb);
 }
 
-void CLASS write_ppm_tiff (FILE *ofp)
+void CLASS write_ppm_tiff()
 {
   struct tiff_hdr th;
-  uchar *ppm, lut[0x10000];
+  uchar *ppm;
   ushort *ppm2;
   int c, row, col, soff, rstep, cstep;
+  int perc, val, total, white=0x2000;
 
+  perc = width * height * 0.01;		/* 99th percentile white level */
+  if (fuji_width) perc /= 2;
+  if (!((highlight & ~2) || no_auto_bright))
+    for (white=c=0; c < colors; c++) {
+      for (val=0x2000, total=0; --val > 32; )
+	if ((total += histogram[c][val]) > perc) break;
+      if (white < val) white = val;
+    }
+  gamma_curve (gamm[0], gamm[1], 2, (white << 3)/bright);
   iheight = height;
   iwidth  = width;
   if (flip & 4) SWAP(height,width);
@@ -8113,16 +10066,14 @@
   else
     fprintf (ofp, "P%d\n%d %d\n%d\n",
 	colors/2+5, width, height, (1 << output_bps)-1);
-
-  if (output_bps == 8) gamma_lut (lut);
   soff  = flip_index (0, 0);
   cstep = flip_index (0, 1) - soff;
   rstep = flip_index (1, 0) - flip_index (0, width);
   for (row=0; row < height; row++, soff += rstep) {
     for (col=0; col < width; col++, soff += cstep)
       if (output_bps == 8)
-	   FORCC ppm [col*colors+c] = lut[image[soff][c]];
-      else FORCC ppm2[col*colors+c] =     image[soff][c];
+	   FORCC ppm [col*colors+c] = curve[image[soff][c]] >> 8;
+      else FORCC ppm2[col*colors+c] = curve[image[soff][c]];
     if (output_bps == 16 && !output_tiff && htons(0x55aa) != 0x55aa)
       swab (ppm2, ppm2, width*colors*2);
     fwrite (ppm, colors*output_bps/8, width, ofp);
@@ -8130,22 +10081,21 @@
   free (ppm);
 }
 
-int CLASS main (int argc, char **argv)
+int CLASS main (int argc, const char **argv)
 {
-  int arg, status=0;
+  int arg, status=0, quality, i, c;
   int timestamp_only=0, thumbnail_only=0, identify_only=0;
   int user_qual=-1, user_black=-1, user_sat=-1, user_flip=-1;
-  int use_fuji_rotate=1, write_to_stdout=0, quality, i, c;
-  char opm, opt, *ofname, *sp, *cp, *bpfile=0, *dark_frame=0;
-  const char *write_ext;
+  int use_fuji_rotate=1, write_to_stdout=0, read_from_stdin=0;
+  const char *sp, *bpfile=0, *dark_frame=0, *write_ext;
+  char opm, opt, *ofname, *cp;
   struct utimbuf ut;
-  FILE *ofp;
 #ifndef NO_LCMS
-  char *cam_profile=0, *out_profile=0;
+  const char *cam_profile=0, *out_profile=0;
 #endif
 
 #ifndef LOCALTIME
-  putenv ("TZ=UTC");
+  putenv ((char *) "TZ=UTC");
 #endif
 #ifdef LOCALEDIR
   setlocale (LC_CTYPE, "");
@@ -8155,7 +10105,7 @@
 #endif
 
   if (argc == 1) {
-    printf(_("\nRaw photo decoder \"dcraw\" v%s"), VERSION);
+    printf(_("\nRaw photo decoder \"dcraw\" v%s"), DCRAW_VERSION);
     printf(_("\nby Dave Coffin, dcoffin a cybercom o net\n"));
     printf(_("\nUsage:  %s [OPTION]... [FILE]...\n\n"), argv[0]);
     puts(_("-v        Print verbose messages"));
@@ -8177,7 +10127,7 @@
     puts(_("-n <num>  Set threshold for wavelet denoising"));
     puts(_("-H [0-9]  Highlight mode (0=clip, 1=unclip, 2=blend, 3+=rebuild)"));
     puts(_("-t [0-7]  Flip image (0=none, 3=180, 5=90CCW, 6=90CW)"));
-    puts(_("-o [0-5]  Output colorspace (raw,sRGB,Adobe,Wide,ProPhoto,XYZ)"));
+    puts(_("-o [0-6]  Output colorspace (raw,sRGB,Adobe,Wide,ProPhoto,XYZ,ACES)"));
 #ifndef NO_LCMS
     puts(_("-o <file> Apply output ICC profile from file"));
     puts(_("-p <file> Apply camera ICC profile from file or \"embed\""));
@@ -8187,12 +10137,14 @@
     puts(_("-j        Don't stretch or rotate raw pixels"));
     puts(_("-W        Don't automatically brighten the image"));
     puts(_("-b <num>  Adjust brightness (default = 1.0)"));
+    puts(_("-g <p ts> Set custom gamma curve (default = 2.222 4.5)"));
     puts(_("-q [0-3]  Set the interpolation quality"));
     puts(_("-h        Half-size color image (twice as fast as \"-q 0\")"));
     puts(_("-f        Interpolate RGGB as four colors"));
     puts(_("-m <num>  Apply a 3x3 median filter to R-G and B-G"));
     puts(_("-s [0..N-1] Select one raw image or \"all\" from each file"));
-    puts(_("-4        Write 16-bit linear instead of 8-bit with gamma"));
+    puts(_("-6        Write 16-bit instead of 8-bit"));
+    puts(_("-4        Linear 16-bit, same as \"-6 -W -g 1 1\""));
     puts(_("-T        Write TIFF instead of PPM"));
     puts("");
     return 1;
@@ -8200,8 +10152,8 @@
   argv[argc] = "";
   for (arg=1; (((opm = argv[arg][0]) - 2) | 2) == '+'; ) {
     opt = argv[arg++][1];
-    if ((cp = strchr (sp="nbrkStqmHAC", opt)))
-      for (i=0; i < "11411111142"[cp-sp]-'0'; i++)
+    if ((cp = (char *) strchr (sp="nbrkStqmHACg", opt)))
+      for (i=0; i < "114111111422"[cp-sp]-'0'; i++)
 	if (!isdigit(argv[arg+i][0])) {
 	  fprintf (stderr,_("Non-numeric argument to \"-%c\"\n"), opt);
 	  return 1;
@@ -8213,6 +10165,9 @@
 	   FORC4 user_mul[c] = atof(argv[arg++]);  break;
       case 'C':  aber[0] = 1 / atof(argv[arg++]);
 		 aber[2] = 1 / atof(argv[arg++]);  break;
+      case 'g':  gamm[0] =     atof(argv[arg++]);
+		 gamm[1] =     atof(argv[arg++]);
+		 if (gamm[0]) gamm[0] = 1/gamm[0]; break;
       case 'k':  user_black  = atoi(argv[arg++]);  break;
       case 'S':  user_sat    = atoi(argv[arg++]);  break;
       case 't':  user_flip   = atoi(argv[arg++]);  break;
@@ -8239,25 +10194,27 @@
       case 'i':  identify_only     = 1;  break;
       case 'c':  write_to_stdout   = 1;  break;
       case 'v':  verbose           = 1;  break;
-      case 'h':  half_size         = 1;		/* "-h" implies "-f" */
+      case 'h':  half_size         = 1;  break;
       case 'f':  four_color_rgb    = 1;  break;
       case 'A':  FORC4 greybox[c]  = atoi(argv[arg++]);
       case 'a':  use_auto_wb       = 1;  break;
       case 'w':  use_camera_wb     = 1;  break;
-      case 'M':  use_camera_matrix = (opm == '+');  break;
-      case 'D':
-      case 'd':  document_mode = 1 + (opt == 'D');
+      case 'M':  use_camera_matrix = 3 * (opm == '+');  break;
+      case 'I':  read_from_stdin   = 1;  break;
+      case 'E':  document_mode++;
+      case 'D':  document_mode++;
+      case 'd':  document_mode++;
       case 'j':  use_fuji_rotate   = 0;  break;
       case 'W':  no_auto_bright    = 1;  break;
       case 'T':  output_tiff       = 1;  break;
-      case '4':  output_bps       = 16;  break;
+      case '4':  gamm[0] = gamm[1] =
+		 no_auto_bright    = 1;
+      case '6':  output_bps       = 16;  break;
       default:
 	fprintf (stderr,_("Unknown option \"-%c\".\n"), opt);
 	return 1;
     }
   }
-  if (use_camera_matrix < 0)
-      use_camera_matrix = use_camera_wb;
   if (arg == argc) {
     fprintf (stderr,_("No files to process.\n"));
     return 1;
@@ -8276,6 +10233,7 @@
   }
   for ( ; arg < argc; arg++) {
     status = 1;
+    raw_image = 0;
     image = 0;
     oprof = 0;
     meta_data = ofname = 0;
@@ -8323,6 +10281,7 @@
 	height = thumb_height;
 	width  = thumb_width;
 	filters = 0;
+	colors = 3;
       } else {
 	fseek (ifp, thumb_offset, SEEK_SET);
 	write_fun = write_thumb;
@@ -8336,8 +10295,7 @@
     if (identify_only && verbose && make[0]) {
       printf (_("\nFilename: %s\n"), ifname);
       printf (_("Timestamp: %s"), ctime(&timestamp));
-      printf (_("Camera: %s\n"), make);
-      printf (_("Model: %s\n"), model);
+      printf (_("Camera: %s %s\n"), make, model);
       if (artist[0])
 	printf (_("Owner: %s\n"), artist);
       if (dng_version) {
@@ -8362,12 +10320,19 @@
     } else if (!is_raw)
       fprintf (stderr,_("Cannot decode file %s\n"), ifname);
     if (!is_raw) goto next;
-    shrink = filters &&
-	(half_size || threshold || aber[0] != 1 || aber[2] != 1);
+    shrink = filters && (half_size || (!identify_only &&
+	(threshold || aber[0] != 1 || aber[2] != 1)));
     iheight = (height + shrink) >> shrink;
     iwidth  = (width  + shrink) >> shrink;
     if (identify_only) {
       if (verbose) {
+	if (document_mode == 3) {
+	  top_margin = left_margin = fuji_width = 0;
+	  height = raw_height;
+	  width  = raw_width;
+	}
+	iheight = (height + shrink) >> shrink;
+	iwidth  = (width  + shrink) >> shrink;
 	if (use_fuji_rotate) {
 	  if (fuji_width) {
 	    fuji_width = (fuji_width - 1 + shrink) >> shrink;
@@ -8384,10 +10349,15 @@
 	printf (_("Output size: %4d x %d\n"), iwidth, iheight);
 	printf (_("Raw colors: %d"), colors);
 	if (filters) {
+	  int fhigh = 2, fwide = 2;
+	  if ((filters ^ (filters >>  8)) & 0xff)   fhigh = 4;
+	  if ((filters ^ (filters >> 16)) & 0xffff) fhigh = 8;
+	  if (filters == 1) fhigh = fwide = 16;
+	  if (filters == 9) fhigh = fwide = 6;
 	  printf (_("\nFilter pattern: "));
-	  if (!cdesc[3]) cdesc[3] = 'G';
-	  for (i=0; i < 16; i++)
-	    putchar (cdesc[fc(i >> 1,i & 1)]);
+	  for (i=0; i < fhigh; i++)
+	    for (c = i && putchar('/') && 0; c < fwide; c++)
+	      putchar (cdesc[fcol(i,c)]);
 	}
 	printf (_("\nDaylight multipliers:"));
 	FORCC printf (" %f", pre_mul[c]);
@@ -8402,16 +10372,17 @@
       fclose(ifp);
       continue;
     }
-    if (use_camera_matrix && cmatrix[0][0] > 0.25) {
-      memcpy (rgb_cam, cmatrix, sizeof cmatrix);
-      raw_color = 0;
-    }
-    image = (ushort (*)[4]) calloc (iheight*iwidth, sizeof *image);
-    merror (image, "main()");
     if (meta_length) {
       meta_data = (char *) malloc (meta_length);
       merror (meta_data, "main()");
     }
+    if (filters || colors == 1) {
+      raw_image = (ushort *) calloc ((raw_height+7), raw_width*2);
+      merror (raw_image, "main()");
+    } else {
+      image = (ushort (*)[4]) calloc (iheight, iwidth*sizeof *image);
+      merror (image, "main()");
+    }
     if (verbose)
       fprintf (stderr,_("Loading %s %s image from %s ...\n"),
 	make, model, ifname);
@@ -8419,28 +10390,62 @@
       fprintf (stderr,_("%s: \"-s %d\" requests a nonexistent image!\n"),
 	ifname, shot_select);
     fseeko (ifp, data_offset, SEEK_SET);
-    (*load_raw)();
+    if (raw_image && read_from_stdin)
+      fread (raw_image, 2, raw_height*raw_width, stdin);
+    else (*load_raw)();
+    if (document_mode == 3) {
+      top_margin = left_margin = fuji_width = 0;
+      height = raw_height;
+      width  = raw_width;
+    }
+    iheight = (height + shrink) >> shrink;
+    iwidth  = (width  + shrink) >> shrink;
+    if (raw_image) {
+      image = (ushort (*)[4]) calloc (iheight, iwidth*sizeof *image);
+      merror (image, "main()");
+      crop_masked_pixels();
+      free (raw_image);
+    }
     if (zero_is_bad) remove_zeroes();
     bad_pixels (bpfile);
     if (dark_frame) subtract (dark_frame);
     quality = 2 + !fuji_width;
     if (user_qual >= 0) quality = user_qual;
+    i = cblack[3];
+    FORC3 if (i > cblack[c]) i = cblack[c];
+    FORC4 cblack[c] -= i;
+    black += i;
+    i = cblack[6];
+    FORC (cblack[4] * cblack[5])
+      if (i > cblack[6+c]) i = cblack[6+c];
+    FORC (cblack[4] * cblack[5])
+      cblack[6+c] -= i;
+    black += i;
     if (user_black >= 0) black = user_black;
+    FORC4 cblack[c] += black;
     if (user_sat > 0) maximum = user_sat;
 #ifdef COLORCHECK
     colorcheck();
 #endif
-    if (is_foveon && !document_mode) foveon_interpolate();
-    if (!is_foveon && document_mode < 2) scale_colors();
+    if (is_foveon) {
+      if (document_mode || load_raw == &CLASS foveon_dp_load_raw) {
+	for (i=0; i < height*width*4; i++)
+	  if ((short) image[0][i] < 0) image[0][i] = 0;
+      } else foveon_interpolate();
+    } else if (document_mode < 2)
+      scale_colors();
     pre_interpolate();
     if (filters && !document_mode) {
       if (quality == 0)
 	lin_interpolate();
       else if (quality == 1 || colors > 3)
 	vng_interpolate();
-      else if (quality == 2)
+      else if (quality == 2 && filters > 1000)
 	ppg_interpolate();
-      else ahd_interpolate();
+      else if (filters == 9)
+	xtrans_interpolate (quality*2-3);
+      else
+	ahd_interpolate();
     }
     if (mix_green)
       for (colors=3, i=0; i < height*width; i++)
@@ -8483,7 +10488,7 @@
     }
     if (verbose)
       fprintf (stderr,_("Writing data to %s ...\n"), ofname);
-    (*write_fun)(ofp);
+    (*write_fun)();
     fclose(ifp);
     if (ofp != stdout) fclose(ofp);
 cleanup:
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libkdcraw/kdcraw.cpp libkdcraw/libkdcraw/libkdcraw/kdcraw.cpp
--- libkdcraw-wrk/libkdcraw/libkdcraw/kdcraw.cpp	2022-11-07 08:15:53.610821808 +0300
+++ libkdcraw/libkdcraw/libkdcraw/kdcraw.cpp	2022-11-07 07:46:31.726795008 +0300
@@ -173,7 +173,8 @@
     raw.imgdata.params.half_size      = 1; // Half-size color image (3x faster than -q).
 
     // NOTE: new magic option introduced by LibRaw 0.7.0 to to make better noise filtration.
-    raw.imgdata.params.filtering_mode = LIBRAW_FILTERING_AUTOMATIC;
+    //not with us in libraw 0.14+
+    //raw.imgdata.params.filtering_mode = LIBRAW_FILTERING_AUTOMATIC;
 
     int ret = raw.open_file((const char*)(TQFile::encodeName(path)));
     if (ret != LIBRAW_SUCCESS)
@@ -292,7 +293,7 @@
     d->setProgress(0.3);
 
     raw.imgdata.params.output_bps    = 16;
-    raw.imgdata.params.document_mode = 2;
+    //raw.imgdata.params.document_mode = 2;
 
     ret = raw.unpack();
     if (ret != LIBRAW_SUCCESS)
@@ -387,12 +388,13 @@
     TQByteArray outputProfile = TQFile::encodeName(m_rawDecodingSettings.outputProfile);
 
     // NOTE: new magic option introduced by LibRaw 0.7.0 to to make better noise/etc filtration.
-    raw.imgdata.params.filtering_mode = LIBRAW_FILTERING_AUTOMATIC;
+    //Not in 0.14
+    //raw.imgdata.params.filtering_mode = LIBRAW_FILTERING_AUTOMATIC;
 
     if (m_rawDecodingSettings.gamma16bit)
     {
         // 16 bits color depth auto-gamma is not implemented in dcraw.
-        raw.imgdata.params.gamma_16bit = 1;
+        //raw.imgdata.params.gamma_16bit = 1;
     }
 
     if (m_rawDecodingSettings.sixteenBitsImage)
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libkdcraw/kdcrawprivate.cpp libkdcraw/libkdcraw/libkdcraw/kdcrawprivate.cpp
--- libkdcraw-wrk/libkdcraw/libkdcraw/kdcrawprivate.cpp	2022-11-07 08:15:53.610821808 +0300
+++ libkdcraw/libkdcraw/libkdcraw/kdcrawprivate.cpp	2022-11-07 07:46:31.726795008 +0300
@@ -135,7 +135,7 @@
     {
         if (!raw->imgdata.idata.cdesc[3]) raw->imgdata.idata.cdesc[3] = 'G';
         for (int i=0; i < 16; i++)
-            identify.filterPattern.append(raw->imgdata.idata.cdesc[raw->fc(i >> 1,i & 1)]);
+            identify.filterPattern.append(raw->imgdata.idata.cdesc[raw->FC(i >> 1,i & 1)]);
     }
 
     for(int c = 0 ; c < raw->imgdata.idata.colors ; c++)
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/CMakeLists.txt libkdcraw/libkdcraw/libraw/CMakeLists.txt
--- libkdcraw-wrk/libkdcraw/libraw/CMakeLists.txt	2022-11-07 08:15:53.610821808 +0300
+++ libkdcraw/libkdcraw/libraw/CMakeLists.txt	2022-11-07 07:46:31.726795008 +0300
@@ -20,9 +20,10 @@
   SOURCES
         src/libraw_cxx.cpp
         src/libraw_c_api.cpp
+        src/libraw_datastream.cpp
         internal/dcraw_common.cpp
+        internal/demosaic_packs.cpp
         internal/dcraw_fileio.cpp
-        internal/foveon.cpp
   LINK
      ${LCMS_LIBRARIES}
      
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/internal/dcraw_common.cpp libkdcraw/libkdcraw/libraw/internal/dcraw_common.cpp
--- libkdcraw-wrk/libkdcraw/libraw/internal/dcraw_common.cpp	2022-11-07 08:15:53.614821808 +0300
+++ libkdcraw/libkdcraw/libraw/internal/dcraw_common.cpp	2022-11-07 07:46:31.726795008 +0300
@@ -1,8295 +1,68 @@
-/* 
-   GENERATED FILE, DO NOT EDIT
-   Generated from dcraw/dcraw.c at Tue Apr  7 15:14:48 2009
-   Look into original file (probably http://cybercom.net/~dcoffin/dcraw/dcraw.c)
-   for copyright information.
-*/
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#define CLASS LibRaw::
-#include "libraw/libraw_types.h"
-#define LIBRAW_LIBRARY_BUILD
-#define LIBRAW_IO_REDEFINED
-#include "libraw/libraw.h"
-#include "internal/defines.h"
-#include "internal/var_defines.h"
-
-
-#ifndef __GLIBC__
-char *my_memmem (char *haystack, size_t haystacklen,
-	      char *needle, size_t needlelen)
-{
-  char *c;
-  for (c = haystack; c <= haystack + haystacklen - needlelen; c++)
-    if (!memcmp (c, needle, needlelen))
-      return c;
-  return 0;
-}
-#define memmem my_memmem
-#endif
-
-
-ushort CLASS sget2 (uchar *s)
-{
-  if (order == 0x4949)		/* "II" means little-endian */
-    return s[0] | s[1] << 8;
-  else				/* "MM" means big-endian */
-    return s[0] << 8 | s[1];
-}
-
-ushort CLASS get2()
-{
-  uchar str[2] = { 0xff,0xff };
-  fread (str, 1, 2, ifp);
-  return sget2(str);
-}
-
-unsigned CLASS sget4 (uchar *s)
-{
-  if (order == 0x4949)
-    return s[0] | s[1] << 8 | s[2] << 16 | s[3] << 24;
-  else
-    return s[0] << 24 | s[1] << 16 | s[2] << 8 | s[3];
-}
-#define sget4(s) sget4((uchar *)s)
-
-unsigned CLASS get4()
-{
-  uchar str[4] = { 0xff,0xff,0xff,0xff };
-  fread (str, 1, 4, ifp);
-  return sget4(str);
-}
-
-unsigned CLASS getint (int type)
-{
-  return type == 3 ? get2() : get4();
-}
-
-float CLASS int_to_float (int i)
-{
-  union { int i; float f; } u;
-  u.i = i;
-  return u.f;
-}
-
-double CLASS getreal (int type)
-{
-  union { char c[8]; double d; } u;
-  int i, rev;
-
-  switch (type) {
-    case 3: return (unsigned short) get2();
-    case 4: return (unsigned int) get4();
-    case 5:  u.d = (unsigned int) get4();
-      return u.d / (unsigned int) get4();
-    case 8: return (signed short) get2();
-    case 9: return (signed int) get4();
-    case 10: u.d = (signed int) get4();
-      return u.d / (signed int) get4();
-    case 11: return int_to_float (get4());
-    case 12:
-      rev = 7 * ((order == 0x4949) == (ntohs(0x1234) == 0x1234));
-      for (i=0; i < 8; i++)
-	u.c[i ^ rev] = fgetc(ifp);
-      return u.d;
-    default: return fgetc(ifp);
-  }
-}
-
-void CLASS read_shorts (ushort *pixel, int count)
-{
-  if (fread (pixel, 2, count, ifp) < count) derror();
-  if ((order == 0x4949) == (ntohs(0x1234) == 0x1234))
-      swab ((char*)pixel, (char*)pixel, count*2);
-}
-void CLASS canon_black (double dark[2])
-{
-  int c, diff, row, col;
-
-  if (raw_width < width+4) return;
-  FORC(2) dark[c] /= (raw_width-width-2) * height >> 1;
-#ifdef LIBRAW_LIBRARY_BUILD
-  if(!( filtering_mode & LIBRAW_FILTERING_NOBLACKS) )
-      {
-#endif
-  if ((diff = dark[0] - dark[1]))
-    for (row=0; row < height; row++)
-      for (col=1; col < width; col+=2)
-	BAYER(row,col) += diff;
-#ifdef LIBRAW_LIBRARY_BUILD
-      }
-#endif
-  dark[1] += diff;
-  black = (dark[0] + dark[1] + 1) / 2;
-}
-
-void CLASS canon_600_fixed_wb (int temp)
-{
-  static const short mul[4][5] = {
-    {  667, 358,397,565,452 },
-    {  731, 390,367,499,517 },
-    { 1119, 396,348,448,537 },
-    { 1399, 485,431,508,688 } };
-  int lo, hi, i;
-  float frac=0;
-
-  for (lo=4; --lo; )
-    if (*mul[lo] <= temp) break;
-  for (hi=0; hi < 3; hi++)
-    if (*mul[hi] >= temp) break;
-  if (lo != hi)
-    frac = (float) (temp - *mul[lo]) / (*mul[hi] - *mul[lo]);
-  for (i=1; i < 5; i++)
-    pre_mul[i-1] = 1 / (frac * mul[hi][i] + (1-frac) * mul[lo][i]);
-#ifdef LIBRAW_LIBRARY_BUILD
-  color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST;
-#endif
-}
-
-/* Return values:  0 = white  1 = near white  2 = not white */
-int CLASS canon_600_color (int ratio[2], int mar)
-{
-  int clipped=0, target, miss;
-
-  if (flash_used) {
-    if (ratio[1] < -104)
-      { ratio[1] = -104; clipped = 1; }
-    if (ratio[1] >   12)
-      { ratio[1] =   12; clipped = 1; }
-  } else {
-    if (ratio[1] < -264 || ratio[1] > 461) return 2;
-    if (ratio[1] < -50)
-      { ratio[1] = -50; clipped = 1; }
-    if (ratio[1] > 307)
-      { ratio[1] = 307; clipped = 1; }
-  }
-  target = flash_used || ratio[1] < 197
-	? -38 - (398 * ratio[1] >> 10)
-	: -123 + (48 * ratio[1] >> 10);
-  if (target - mar <= ratio[0] &&
-      target + 20  >= ratio[0] && !clipped) return 0;
-  miss = target - ratio[0];
-  if (abs(miss) >= mar*4) return 2;
-  if (miss < -20) miss = -20;
-  if (miss > mar) miss = mar;
-  ratio[0] = target - miss;
-  return 1;
-}
-
-void CLASS canon_600_auto_wb()
-{
-  int mar, row, col, i, j, st, count[] = { 0,0 };
-  int test[8], total[2][8], ratio[2][2], stat[2];
-
-  memset (&total, 0, sizeof total);
-  i = canon_ev + 0.5;
-  if      (i < 10) mar = 150;
-  else if (i > 12) mar = 20;
-  else mar = 280 - 20 * i;
-  if (flash_used) mar = 80;
-  for (row=14; row < height-14; row+=4)
-    for (col=10; col < width; col+=2) {
-      for (i=0; i < 8; i++)
-	test[(i & 4) + FC(row+(i >> 1),col+(i & 1))] =
-		    BAYER(row+(i >> 1),col+(i & 1));
-      for (i=0; i < 8; i++)
-	if (test[i] < 150 || test[i] > 1500) goto next;
-      for (i=0; i < 4; i++)
-	if (abs(test[i] - test[i+4]) > 50) goto next;
-      for (i=0; i < 2; i++) {
-	for (j=0; j < 4; j+=2)
-	  ratio[i][j >> 1] = ((test[i*4+j+1]-test[i*4+j]) << 10) / test[i*4+j];
-	stat[i] = canon_600_color (ratio[i], mar);
-      }
-      if ((st = stat[0] | stat[1]) > 1) goto next;
-      for (i=0; i < 2; i++)
-	if (stat[i])
-	  for (j=0; j < 2; j++)
-	    test[i*4+j*2+1] = test[i*4+j*2] * (0x400 + ratio[i][j]) >> 10;
-      for (i=0; i < 8; i++)
-	total[st][i] += test[i];
-      count[st]++;
-next: ;
-    }
-  if (count[0] | count[1]) {
-    st = count[0]*200 < count[1];
-    for (i=0; i < 4; i++)
-      pre_mul[i] = 1.0 / (total[st][i] + total[st][i+4]);
-#ifdef LIBRAW_LIBRARY_BUILD
-    color_flags.pre_mul_state = LIBRAW_COLORSTATE_CALCULATED;
-#endif
-  }
-}
-
-void CLASS canon_600_coeff()
-{
-  static const short table[6][12] = {
-    { -190,702,-1878,2390,   1861,-1349,905,-393, -432,944,2617,-2105  },
-    { -1203,1715,-1136,1648, 1388,-876,267,245,  -1641,2153,3921,-3409 },
-    { -615,1127,-1563,2075,  1437,-925,509,3,     -756,1268,2519,-2007 },
-    { -190,702,-1886,2398,   2153,-1641,763,-251, -452,964,3040,-2528  },
-    { -190,702,-1878,2390,   1861,-1349,905,-393, -432,944,2617,-2105  },
-    { -807,1319,-1785,2297,  1388,-876,769,-257,  -230,742,2067,-1555  } };
-  int t=0, i, c;
-  float mc, yc;
-
-  mc = pre_mul[1] / pre_mul[2];
-  yc = pre_mul[3] / pre_mul[2];
-  if (mc > 1 && mc <= 1.28 && yc < 0.8789) t=1;
-  if (mc > 1.28 && mc <= 2) {
-    if  (yc < 0.8789) t=3;
-    else if (yc <= 2) t=4;
-  }
-  if (flash_used) t=5;
-  for (raw_color = i=0; i < 3; i++)
-    FORCC rgb_cam[i][c] = table[t][i*4 + c] / 1024.0;
-#ifdef LIBRAW_LIBRARY_BUILD
-  color_flags.rgb_cam_state = LIBRAW_COLORSTATE_CALCULATED;
-#endif
-}
-
-void CLASS canon_600_load_raw()
-{
-  uchar  data[1120], *dp;
-  ushort pixel[896], *pix;
-  int irow, row, col, val;
-  static const short mul[4][2] =
-  { { 1141,1145 }, { 1128,1109 }, { 1178,1149 }, { 1128,1109 } };
-
-  for (irow=row=0; irow < height; irow++) {
-    if (fread (data, 1, raw_width*5/4, ifp) < raw_width*5/4) derror();
-    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(row,col) = pixel[col];
-    for (col=width; col < raw_width; col++)
-        {
-            black += pixel[col];
-#ifdef LIBRAW_LIBRARY_BUILD
-            ushort *dfp = get_masked_pointer(row,col);
-            if(dfp) *dfp = pixel[col];
-#endif
-        }
-    if ((row+=2) > height) row = 1;
-  }
-  if (raw_width > width)
-    black = black / ((raw_width - width) * height) - 4;
-  for (row=0; row < height; row++)
-    for (col=0; col < width; col++) {
-#ifdef LIBRAW_LIBRARY_BUILD
-     if( filtering_mode & LIBRAW_FILTERING_NOBLACKS)  
-         val = BAYER(row,col);
-     else
-#endif
-      if ((val = BAYER(row,col) - black) < 0) val = 0;
-      val = val * mul[row & 3][col & 1] >> 9;
-      BAYER(row,col) = val;
-    }
-  canon_600_fixed_wb(1311);
-  canon_600_auto_wb();
-  canon_600_coeff();
-  maximum = (0x3ff - black) * 1109 >> 9;
-  black = 0;
-}
-
-void CLASS remove_zeroes()
-{
-  unsigned row, col, tot, n, r, c;
-
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_REMOVE_ZEROES,0,2);
-#endif
-  for (row=0; row < height; row++)
-    for (col=0; col < width; col++)
-      if (BAYER(row,col) == 0) {
-	tot = n = 0;
-	for (r = row-2; r <= row+2; r++)
-	  for (c = col-2; c <= col+2; c++)
-	    if (r < height && c < width &&
-		FC(r,c) == FC(row,col) && BAYER(r,c))
-	      tot += (n++,BAYER(r,c));
-	if (n) BAYER(row,col) = tot/n;
-      }
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_REMOVE_ZEROES,1,2);
-#endif
-}
-
-int CLASS canon_s2is()
-{
-  unsigned row;
-
-  for (row=0; row < 100; row++) {
-    fseek (ifp, row*3340 + 3284, SEEK_SET);
-    if (getc(ifp) > 15) return 1;
-  }
-  return 0;
-}
-
-void CLASS canon_a5_load_raw()
-{
-  ushort data[2565], *dp, pixel;
-  int vbits=0, buf=0, row, col, bc=0;
-
-  order = 0x4949;
-  for (row=-top_margin; row < raw_height-top_margin; row++) {
-    read_shorts (dp=data, raw_width * 10 / 16);
-    for (col=-left_margin; col < raw_width-left_margin; col++) {
-      if ((vbits -= 10) < 0)
-	buf = (vbits += 16, (buf << 16) + *dp++);
-      pixel = buf >> vbits & 0x3ff;
-#ifdef LIBRAW_LIBRARY_BUILD
-          ushort *dfp = get_masked_pointer(row+top_margin,col+left_margin);
-          if(dfp) *dfp = pixel;
-#endif
-      if ((unsigned) row < height && (unsigned) col < width)
-	BAYER(row,col) = pixel;
-      else if (col > 1-left_margin && col != width)
-	black += (bc++,pixel);
-    }
-  }
-  if (bc) black /= bc;
-  maximum = 0x3ff;
-
-#ifdef LIBRAW_LIBRARY_BUILD
-  if(!(filtering_mode & LIBRAW_FILTERING_NOZEROES))
-#endif
-  if (raw_width > 1600) remove_zeroes();
-}
-
-/*
-   getbits(-1) initializes the buffer
-   getbits(n) where 0 <= n <= 25 returns an n-bit integer
- */
-unsigned CLASS getbits (int nbits)
-{
-#ifdef LIBRAW_NOTHREADS
-  static unsigned bitbuf=0;
-  static int vbits=0, reset=0;
-#else
-#define bitbuf tls->getbits.bitbuf
-#define vbits  tls->getbits.vbits
-#define reset  tls->getbits.reset
-#endif
-  unsigned c;
-
-  if (nbits == -1)
-    return bitbuf = vbits = reset = 0;
-  if (nbits == 0 || reset) return 0;
-  while (vbits < nbits) {
-    if ((c = fgetc(ifp)) == EOF) derror();
-    if ((reset = zero_after_ff && c == 0xff && fgetc(ifp))) return 0;
-    bitbuf = (bitbuf << 8) + (uchar) c;
-    vbits += 8;
-  }
-  vbits -= nbits;
-  return bitbuf << (32-nbits-vbits) >> (32-nbits);
-#ifndef LIBRAW_NOTHREADS
-#undef bitbuf
-#undef vbits
-#undef reset
-#endif
-}
-
-void CLASS init_decoder()
-{
-  memset (first_decode, 0, sizeof first_decode);
-  free_decode = first_decode;
-}
-
-/*
-   Construct a decode tree according to 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
- */
-uchar * CLASS make_decoder (const uchar *source, int level)
-{
-  struct decode *cur;
-#ifndef LIBRAW_NOTHREADS
-#define t_leaf tls->make_decoder_leaf
-#else
-  static int t_leaf;
-#endif
-  int i, next;
-
-  if (level==0) t_leaf=0;
-  cur = free_decode++;
-  if (free_decode > first_decode+2048) {
-#ifdef LIBRAW_LIBRARY_BUILD
-      throw LIBRAW_EXCEPTION_DECODE_RAW;
-#else
-    fprintf (stderr,_("%s: decoder table overflow\n"), ifname);
-    longjmp (failure, 2);
-#endif
-  }
-  for (i=next=0; i <= t_leaf && next < 16; )
-    i += source[next++];
-  if (i > t_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 + t_leaf++];
-  }
-  return (uchar *) source + 16 + t_leaf;
-#ifndef LIBRAW_NOTHREADS
-#undef t_leaf
-#endif
-}
-
-void CLASS crw_init_tables (unsigned table)
-{
-  static const uchar 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 uchar 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  }
-  };
-  if (table > 2) table = 2;
-  init_decoder();
-  make_decoder ( first_tree[table], 0);
-  second_decode = free_decode;
-  make_decoder (second_tree[table], 0);
-}
-
-/*
-   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.
- */
-int CLASS canon_has_lowbits()
-{
-  uchar 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 CLASS canon_compressed_load_raw()
-{
-  ushort *pixel, *prow;
-  int nblocks, 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];
-  double dark[2] = { 0,0 };
-  uchar c;
-
-  crw_init_tables (tiff_compress);
-  pixel = (ushort *) calloc (raw_width*8, sizeof *pixel);
-  merror (pixel, "canon_compressed_load_raw()");
-  lowbits = canon_has_lowbits();
-  if (!lowbits) maximum = 0x3ff;
-  fseek (ifp, 540 + lowbits*raw_height*raw_width/4, SEEK_SET);
-  zero_after_ff = 1;
-  getbits(-1);
-  for (row=0; row < raw_height; row+=8) {
-    nblocks = MIN (8, raw_height-row) * raw_width >> 6;
-    for (block=0; block < nblocks; 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(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(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;
-	if ((pixel[(block << 6) + i] = base[i & 1] += diffbuf[i]) >> 10)
-	  derror();
-      }
-    }
-    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;
-#ifndef LIBRAW_LIBRARY_BUILD
-      if (irow >= height) continue;
-#endif
-      for (col=0; col < raw_width; col++) {
-#ifdef LIBRAW_LIBRARY_BUILD
-          ushort *dfp = get_masked_pointer(row+r,col);
-          if(dfp) *dfp = pixel[r*raw_width+col];
-          if (irow >= height) continue; // skip for top/bottom rows
-#endif
-	icol = col - left_margin;
-	if (icol < width)
-	  BAYER(irow,icol) = pixel[r*raw_width+col];
-	else if (col > 1)
-	  dark[icol & 1] += pixel[r*raw_width+col];
-      }
-    }
-  }
-  free (pixel);
-  canon_black (dark);
-}
-
-int CLASS ljpeg_start (struct jhead *jh, int info_only)
-{
-  int c, tag, len;
-  uchar data[0x10000], *dp;
-
-  if (!info_only) init_decoder();
-  memset (jh, 0, sizeof *jh);
-  FORC(6) jh->huff[c] = free_decode;
-  jh->restart = INT_MAX;
-  fread (data, 2, 1, ifp);
-  if (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) return 0;
-    fread (data, 1, len, ifp);
-    switch (tag) {
-      case 0xffc3:
-	jh->sraw = ((data[7] >> 4) * (data[7] & 15) - 1) & 3;
-      case 0xffc0:
-	jh->bits = data[0];
-	jh->high = data[1] << 8 | data[2];
-	jh->wide = data[3] << 8 | data[4];
-	jh->clrs = data[5] + jh->sraw;
-	if (len == 9 && !dng_version) getc(ifp);
-	break;
-      case 0xffc4:
-	if (info_only) break;
-	for (dp = data; dp < data+len && *dp < 4; ) {
-	  jh->huff[*dp] = free_decode;
-	  dp = make_decoder (++dp, 0);
-	}
-	break;
-      case 0xffda:
-	jh->psv = data[1+data[0]*2];
-	jh->bits -= data[3+data[0]*2] & 15;
-	break;
-      case 0xffdd:
-	jh->restart = data[0] << 8 | data[1];
-    }
-  } while (tag != 0xffda);
-  if (info_only) return 1;
-  if (jh->sraw) {
-    FORC(4)        jh->huff[2+c] = jh->huff[1];
-    FORC(jh->sraw) jh->huff[1+c] = jh->huff[0];
-  }
-  jh->row = (ushort *) calloc (jh->wide*jh->clrs, 4);
-  merror (jh->row, "ljpeg_start()");
-  return zero_after_ff = 1;
-}
-
-int CLASS ljpeg_diff (struct decode *dindex)
-{
-  int len, diff;
-
-  while (dindex->branch[0])
-    dindex = dindex->branch[getbits(1)];
-  len = dindex->leaf;
-  if (len == 16 && (!dng_version || dng_version >= 0x1010000))
-    return -32768;
-  diff = getbits(len);
-  if ((diff & (1 << (len-1))) == 0)
-    diff -= (1 << len) - 1;
-  return diff;
-}
-
-ushort * CLASS ljpeg_row (int jrow, struct jhead *jh)
-{
-  int col, c, diff, pred, spred=0;
-  ushort mark=0, *row[3];
-
-  if (jrow * jh->wide % jh->restart == 0) {
-    FORC(6) jh->vpred[c] = 1 << (jh->bits-1);
-    if (jrow)
-      do mark = (mark << 8) + (c = fgetc(ifp));
-      while (c != EOF && mark >> 4 != 0xffd);
-    getbits(-1);
-  }
-  FORC3 row[c] = jh->row + jh->wide*jh->clrs*((jrow+c) & 1);
-  for (col=0; col < jh->wide; col++)
-    FORC(jh->clrs) {
-      diff = ljpeg_diff (jh->huff[c]);
-      if (jh->sraw && c <= jh->sraw && (col | c))
-		    pred = spred;
-      else if (col) pred = row[0][-jh->clrs];
-      else	    pred = (jh->vpred[c] += diff) - diff;
-      if (jrow && col) switch (jh->psv) {
-	case 1:	break;
-	case 2: pred = row[1][0];					break;
-	case 3: pred = row[1][-jh->clrs];				break;
-	case 4: pred = pred +   row[1][0] - row[1][-jh->clrs];		break;
-	case 5: pred = pred + ((row[1][0] - row[1][-jh->clrs]) >> 1);	break;
-	case 6: pred = row[1][0] + ((pred - row[1][-jh->clrs]) >> 1);	break;
-	case 7: pred = (pred + row[1][0]) >> 1;				break;
-	default: pred = 0;
-      }
-      if ((**row = pred + diff) >> jh->bits) derror();
-      if (c <= jh->sraw) spred = **row;
-      row[0]++; row[1]++;
-    }
-  return row[2];
-}
-
-void CLASS lossless_jpeg_load_raw()
-{
-  int jwide, jrow, jcol, val, jidx, i, j, row=0, col=0;
-  double dark[2] = { 0,0 };
-  struct jhead jh;
-  int min=INT_MAX;
-  ushort *rp;
-
-  if (!ljpeg_start (&jh, 0)) return;
-  jwide = jh.wide * jh.clrs;
-
-  for (jrow=0; jrow < jh.high; jrow++) {
-    rp = ljpeg_row (jrow, &jh);
-    for (jcol=0; jcol < jwide; jcol++) {
-      val = *rp++;
-      if (jh.bits <= 12)
-#ifdef LIBRAW_LIBRARY_BUILD
-          if( !(filtering_mode & LIBRAW_FILTERING_NORAWCURVE))
-#endif
-	val = curve[val & 0xfff];
-      if (cr2_slice[0]) {
-	jidx = jrow*jwide + jcol;
-	i = jidx / (cr2_slice[1]*jh.high);
-	if ((j = i >= cr2_slice[0]))
-		 i  = cr2_slice[0];
-	jidx -= i * (cr2_slice[1]*jh.high);
-	row = jidx / cr2_slice[1+j];
-	col = jidx % cr2_slice[1+j] + i*cr2_slice[1];
-      }
-      if (raw_width == 3984 && (col -= 2) < 0)
-	col += (row--,raw_width);
-#ifdef LIBRAW_LIBRARY_BUILD
-      ushort *dfp = get_masked_pointer(row,col);
-      if(dfp) *dfp = val;
-#endif
-      if ((unsigned) (row-top_margin) < height) {
-	if ((unsigned) (col-left_margin) < width) {
-	  BAYER(row-top_margin,col-left_margin) = val;
-	  if (min > val) min = val;
-	} else if (col > 1)
-	  dark[(col-left_margin) & 1] += val;
-      }
-      if (++col >= raw_width)
-	col = (row++,0);
-    }
-  }
-  free (jh.row);
-  canon_black (dark);
-  if (!strcasecmp(make,"KODAK"))
-    black = min;
-}
-
-void CLASS canon_sraw_load_raw()
-{
-  struct jhead jh;
-  short *rp=0, (*ip)[4];
-  int jwide, slice, scol, ecol, row, col, jrow=0, jcol=0, pix[3], c;
-  int v[3]={0,0,0}, ver, hue;
-  char *cp;
-
-  if (!ljpeg_start (&jh, 0)) return;
-  jwide = (jh.wide >>= 1) * jh.clrs;
-
-  for (ecol=slice=0; slice <= cr2_slice[0]; slice++) {
-    scol = ecol;
-    ecol += cr2_slice[1] * 2 / jh.clrs;
-    if (!cr2_slice[0] || ecol > raw_width-1) ecol = raw_width & -2;
-    for (row=0; row < height; row += (jh.clrs >> 1) - 1) {
-      ip = (short (*)[4]) image + row*width;
-      for (col=scol; col < ecol; col+=2, jcol+=jh.clrs) {
-	if ((jcol %= jwide) == 0)
-	  rp = (short *) ljpeg_row (jrow++, &jh);
-	if (col >= width) continue;
-	FORC (jh.clrs-2)
-	  ip[col + (c >> 1)*width + (c & 1)][0] = rp[jcol+c];
-	ip[col][1] = rp[jcol+jh.clrs-2] - 16384;
-	ip[col][2] = rp[jcol+jh.clrs-1] - 16384;
-      }
-    }
-  }
-  for (cp=model2; *cp && !isdigit(*cp); cp++);
-  sscanf (cp, "%d.%d.%d", v, v+1, v+2);
-  ver = (v[0]*1000 + v[1])*1000 + v[2];
-  hue = (jh.sraw+1) << 2;
-  if (unique_id == 0x80000218 && ver > 1000006 && ver < 3000000)
-    hue = jh.sraw << 1;
-  ip = (short (*)[4]) image;
-  rp = ip[0];
-  for (row=0; row < height; row++, ip+=width) {
-    if (row & (jh.sraw >> 1))
-      for (col=0; col < width; col+=2)
-	for (c=1; c < 3; c++)
-	  if (row == height-1)
-	       ip[col][c] =  ip[col-width][c];
-	  else ip[col][c] = (ip[col-width][c] + ip[col+width][c] + 1) >> 1;
-    for (col=1; col < width; col+=2)
-      for (c=1; c < 3; c++)
-	if (col == width-1)
-	     ip[col][c] =  ip[col-1][c];
-	else ip[col][c] = (ip[col-1][c] + ip[col+1][c] + 1) >> 1;
-  }
-  for ( ; rp < ip[0]; rp+=4) {
-    if (unique_id < 0x80000200) {
-      pix[0] = rp[0] + rp[2] - 512;
-      pix[2] = rp[0] + rp[1] - 512;
-      pix[1] = rp[0] + ((-778*rp[1] - (rp[2] << 11)) >> 12) - 512;
-    } else {
-      rp[1] = (rp[1] << 2) + hue;
-      rp[2] = (rp[2] << 2) + hue;
-      pix[0] = rp[0] + ((  200*rp[1] + 22929*rp[2]) >> 14);
-      pix[1] = rp[0] + ((-5640*rp[1] - 11751*rp[2]) >> 14);
-      pix[2] = rp[0] + ((29040*rp[1] -   101*rp[2]) >> 14);
-    }
-    FORC3 rp[c] = CLIP(pix[c] * sraw_mul[c] >> 10);
-  }
-  free (jh.row);
-  maximum = 0x3fff;
-}
-
-void CLASS adobe_copy_pixel (int row, int col, ushort **rp)
-{
-  unsigned r, c;
-
-  r = row -= top_margin;
-  c = col -= left_margin;
-  if (is_raw == 2 && shot_select) (*rp)++;
-  if (filters) {
-#ifndef LIBRAW_LIBRARY_BUILD
-    if (fuji_width) {
-      r = row + fuji_width - 1 - (col >> 1);
-      c = row + ((col+1) >> 1);
-    }
-#endif
-#ifdef LIBRAW_LIBRARY_BUILD
-    ushort val = **rp;
-    if(!(filtering_mode & LIBRAW_FILTERING_NORAWCURVE))
-        val = **rp < 0x1000 ? curve[**rp] : **rp;
-    if (r < height && c < width)
-        BAYER(r,c) = val;
-    else
-        {
-            ushort *dfp = get_masked_pointer(row+top_margin,col+left_margin);
-            if(dfp) *dfp = val;
-        }
-#else
-    if (r < height && c < width)
-      BAYER(r,c) = **rp < 0x1000 ? curve[**rp] : **rp;
-#endif
-    *rp += is_raw;
-  } else {
-    if (r < height && c < width)
-      FORC(tiff_samples)
-	image[row*width+col][c] = (*rp)[c] < 0x1000 ? curve[(*rp)[c]]:(*rp)[c];
-    *rp += tiff_samples;
-  }
-  if (is_raw == 2 && shot_select) (*rp)--;
-}
-
-void CLASS adobe_dng_load_raw_lj()
-{
-  unsigned save, trow=0, tcol=0, jwide, jrow, jcol, row, col;
-  struct jhead jh;
-  ushort *rp;
-
-  while (trow < raw_height) {
-    save = ftell(ifp);
-    if (tile_length < INT_MAX)
-      fseek (ifp, get4(), SEEK_SET);
-    if (!ljpeg_start (&jh, 0)) break;
-    jwide = jh.wide;
-    if (filters) jwide *= jh.clrs;
-    jwide /= is_raw;
-    for (row=col=jrow=0; jrow < jh.high; jrow++) {
-      rp = ljpeg_row (jrow, &jh);
-      for (jcol=0; jcol < jwide; jcol++) {
-	adobe_copy_pixel (trow+row, tcol+col, &rp);
-	if (++col >= tile_width || col >= raw_width)
-	  row += 1 + (col = 0);
-      }
-    }
-    fseek (ifp, save+4, SEEK_SET);
-    if ((tcol += tile_width) >= raw_width)
-      trow += tile_length + (tcol = 0);
-    free (jh.row);
-  }
-}
-
-void CLASS adobe_dng_load_raw_nc()
-{
-  ushort *pixel, *rp;
-  int row, col;
-
-  pixel = (ushort *) calloc (raw_width * tiff_samples, sizeof *pixel);
-  merror (pixel, "adobe_dng_load_raw_nc()");
-  for (row=0; row < raw_height; row++) {
-    if (tiff_bps == 16)
-      read_shorts (pixel, raw_width * tiff_samples);
-    else {
-      getbits(-1);
-      for (col=0; col < raw_width * tiff_samples; col++)
-	pixel[col] = getbits(tiff_bps);
-    }
-    for (rp=pixel, col=0; col < raw_width; col++)
-      adobe_copy_pixel (row, col, &rp);
-  }
-  free (pixel);
-}
-
-void CLASS pentax_tree()
-{
-  ushort bit[2][13];
-  struct decode *cur;
-  int c, i, j;
-
-  init_decoder();
-  FORC(13) bit[0][c] = get2();
-  FORC(13) bit[1][c] = fgetc(ifp) & 15;
-  FORC(13) {
-    cur = first_decode;
-    for (i=0; i < bit[1][c]; i++) {
-      j = bit[0][c] >> (11-i) & 1;
-      if (!cur->branch[j]) cur->branch[j] = ++free_decode;
-      cur = cur->branch[j];
-    }
-    cur->leaf = c;
-  }
-}
-
-void CLASS pentax_k10_load_raw()
-{
-  int row, col, diff;
-  ushort vpred[2][2] = {{0,0},{0,0}}, hpred[2];
-
-  getbits(-1);
-  for (row=0; row < raw_height; row++)
-      {
-#ifndef LIBRAW_LIBRARY_BUILD
-          if(row >= height) break;
-#endif
-    for (col=0; col < raw_width; col++) {
-      diff = ljpeg_diff (first_decode);
-      if (col < 2) hpred[col] = vpred[row & 1][col] += diff;
-      else	   hpred[col & 1] += diff;
-      if (col < width && row < height)
-	BAYER(row,col) = hpred[col & 1];
-#ifdef LIBRAW_LIBRARY_BUILD
-      else
-        {
-          ushort *dfp = get_masked_pointer(row,col);
-          if(dfp) *dfp = hpred[col & 1];
-        }
-      
-      if (col < width && row < height)
-#endif
-        if (hpred[col & 1] >> 12) derror();
-    }
-      }
-}
-
-void CLASS nikon_compressed_load_raw()
-{
-  static const uchar nikon_tree[][32] = {
-    { 0,1,5,1,1,1,1,1,1,2,0,0,0,0,0,0,	/* 12-bit lossy */
-      5,4,3,6,2,7,1,0,8,9,11,10,12 },
-    { 0,1,5,1,1,1,1,1,1,2,0,0,0,0,0,0,	/* 12-bit lossy after split */
-      0x39,0x5a,0x38,0x27,0x16,5,4,3,2,1,0,11,12,12 },
-    { 0,1,4,2,3,1,2,0,0,0,0,0,0,0,0,0,  /* 12-bit lossless */
-      5,4,6,3,7,2,8,1,9,0,10,11,12 },
-    { 0,1,4,3,1,1,1,1,1,2,0,0,0,0,0,0,	/* 14-bit lossy */
-      5,6,4,7,8,3,9,2,1,0,10,11,12,13,14 },
-    { 0,1,5,1,1,1,1,1,1,1,2,0,0,0,0,0,	/* 14-bit lossy after split */
-      8,0x5c,0x4b,0x3a,0x29,7,6,5,4,3,2,1,0,13,14 },
-    { 0,1,4,2,2,3,1,2,0,0,0,0,0,0,0,0,	/* 14-bit lossless */
-      7,6,8,5,9,4,10,3,11,12,2,0,1,13,14 } };
-  struct decode *dindex;
-  ushort ver0, ver1, vpred[2][2], hpred[2], csize;
-  int i, min, max, step=0, huff=0, split=0, row, col, len, shl, diff;
-
-  fseek (ifp, meta_offset, SEEK_SET);
-  ver0 = fgetc(ifp);
-  ver1 = fgetc(ifp);
-  if (ver0 == 0x49 || ver1 == 0x58)
-    fseek (ifp, 2110, SEEK_CUR);
-  if (ver0 == 0x46) huff = 2;
-  if (tiff_bps == 14) huff += 3;
-  read_shorts (vpred[0], 4);
-  max = 1 << tiff_bps & 0x7fff;
-  if ((csize = get2()) > 1)
-    step = max / (csize-1);
-  if (ver0 == 0x44 && ver1 == 0x20 && step > 0) {
-    for (i=0; i < csize; i++)
-      curve[i*step] = get2();
-    for (i=0; i < max; i++)
-      curve[i] = ( curve[i-i%step]*(step-i%step) +
-		   curve[i-i%step+step]*(i%step) ) / step;
-#ifdef LIBRAW_LIBRARY_BUILD
-    color_flags.curve_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-    fseek (ifp, meta_offset+562, SEEK_SET);
-    split = get2();
-  } else if (ver0 != 0x46 && csize <= 0x4001)
-      {
-    read_shorts (curve, max=csize);
-#ifdef LIBRAW_LIBRARY_BUILD
-    color_flags.curve_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-      }
-  while (curve[max-2] == curve[max-1]) max--;
-  init_decoder();
-  make_decoder (nikon_tree[huff], 0);
-  fseek (ifp, data_offset, SEEK_SET);
-  getbits(-1);
-  for (min=row=0; row < height; row++) {
-    if (split && row == split) {
-      init_decoder();
-      make_decoder (nikon_tree[huff+1], 0);
-      max += (min = 16) << 1;
-    }
-    for (col=0; col < raw_width; col++) {
-      for (dindex=first_decode; dindex->branch[0]; )
-	dindex = dindex->branch[getbits(1)];
-      len = dindex->leaf & 15;
-      shl = dindex->leaf >> 4;
-      diff = ((getbits(len-shl) << 1) + 1) << shl >> 1;
-      if ((diff & (1 << (len-1))) == 0)
-	diff -= (1 << len) - !shl;
-      if (col < 2) hpred[col] = vpred[row & 1][col] += diff;
-      else	   hpred[col & 1] += diff;
-      if ((ushort)(hpred[col & 1] + min) >= max) derror();
-#ifndef LIBRAW_LIBRARY_BUILD
-      if ((unsigned) (col-left_margin) < width)
-	BAYER(row,col-left_margin) =  curve[LIM((short)hpred[col & 1],0,0x3fff)];
-#else
-      ushort xval = hpred[col & 1];
-      if(!(filtering_mode & LIBRAW_FILTERING_NORAWCURVE))
-          xval = curve[LIM((short)xval,0,0x3fff)];
-      if ((unsigned) (col-left_margin) < width)
-          {
-              BAYER(row,col-left_margin) =  xval;
-          }
-      else
-        {
-          ushort *dfp = get_masked_pointer(row,col);
-          if(dfp) *dfp = xval;
-        }
-#endif
-
-    }
-  }
-}
-
-/*
-   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 CLASS nikon_is_compressed()
-{
-  uchar test[256];
-  int i;
-
-  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 995, 0 for anything else.
- */
-int CLASS nikon_e995()
-{
-  int i, histo[256];
-  const uchar often[] = { 0x00, 0x55, 0xaa, 0xff };
-
-  memset (histo, 0, sizeof histo);
-  fseek (ifp, -2000, SEEK_END);
-  for (i=0; i < 2000; i++)
-    histo[fgetc(ifp)]++;
-  for (i=0; i < 4; i++)
-    if (histo[often[i]] < 200)
-      return 0;
-  return 1;
-}
-
-/*
-   Returns 1 for a Coolpix 2100, 0 for anything else.
- */
-int CLASS nikon_e2100()
-{
-  uchar 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;
-}
-
-void CLASS nikon_3700()
-{
-  int bits, i;
-  uchar dp[24];
-  static const struct {
-    int bits;
-    char t_make[12], t_model[15];
-  } table[] = {
-    { 0x00, "PENTAX",  "Optio 33WR" },
-    { 0x03, "NIKON",   "E3200" },
-    { 0x32, "NIKON",   "E3700" },
-    { 0x33, "OLYMPUS", "C740UZ" } };
-
-  fseek (ifp, 3072, SEEK_SET);
-  fread (dp, 1, 24, ifp);
-  bits = (dp[8] & 3) << 4 | (dp[20] & 3);
-  for (i=0; i < sizeof table / sizeof *table; i++)
-    if (bits == table[i].bits) {
-      strcpy (make,  table[i].t_make );
-      strcpy (model, table[i].t_model);
-    }
-}
-
 /*
-   Separates a Minolta DiMAGE Z2 from a Nikon E4300.
- */
-int CLASS minolta_z2()
-{
-  int i, nz;
-  char tail[424];
-
-  fseek (ifp, -sizeof tail, SEEK_END);
-  fread (tail, 1, sizeof tail, ifp);
-  for (nz=i=0; i < sizeof tail; i++)
-    if (tail[i]) nz++;
-  return nz > 20;
-}
-
-/* Here raw_width is in bytes, not pixels. */
-void CLASS nikon_e900_load_raw()
-{
-  int offset=0, irow, row, col;
-
-  for (irow=0; irow < height; irow++) {
-    row = irow * 2 % height;
-    if (row == 1)
-      offset = - (-offset & -4096);
-    fseek (ifp, offset, SEEK_SET);
-    offset += raw_width;
-    getbits(-1);
-    for (col=0; col < width; col++)
-      BAYER(row,col) = getbits(10);
-  }
-}
-
-/*
-   The Fuji Super CCD is just a Bayer grid rotated 45 degrees.
- */
-void CLASS fuji_load_raw()
-{
-  ushort *pixel;
-#ifndef LIBRAW_LIBRARY_BUILD
-  int wide, row, col, r, c;
-
-  fseek (ifp, (top_margin*raw_width + left_margin) * 2, SEEK_CUR);
-  wide = fuji_width << !fuji_layout;
-  pixel = (ushort *) calloc (wide, sizeof *pixel);
-  merror (pixel, "fuji_load_raw()");
-  for (row=0; row < raw_height; row++) {
-    read_shorts (pixel, wide);
-    fseek (ifp, 2*(raw_width - wide), SEEK_CUR);
-    for (col=0; col < wide; col++) {
-      if (fuji_layout) {
-	r = fuji_width - 1 - col + (row >> 1);
-	c = col + ((row+1) >> 1);
-      } else {
-	r = fuji_width - 1 + row - (col >> 1);
-	c = row + ((col+1) >> 1);
-      }
-      BAYER(r,c) = pixel[col];
-    }
-  }
-  free (pixel);
-#else
-  int row,col;
-  int wide, r, c;
-  pixel = (ushort *) calloc (raw_width, sizeof *pixel);
-  merror (pixel, "fuji_load_raw()");
-  for (row=0; row < raw_height; row++) {
-    read_shorts (pixel, raw_width);
-    for (col=0; col < raw_width; col++) {
-        if(col >= left_margin && col < width+left_margin
-           && row >= top_margin && row < height+top_margin)
-            {
-                int rrow = row-top_margin;
-                int ccol = col-left_margin;
-                if (fuji_layout) {
-                    r = fuji_width - 1 - ccol + (rrow >> 1);
-                    c = ccol + ((rrow+1) >> 1);
-                } else {
-                    r = fuji_width - 1 + rrow - (ccol >> 1);
-                    c = rrow + ((ccol+1) >> 1);
-                }
-
-                image[((row-top_margin) >> shrink)*iwidth + ((col-left_margin) >> shrink)][FC(r,c)] = pixel[col];
-            }
-        else
-            {
-                ushort *dfp = get_masked_pointer(row,col);
-                if(dfp) *dfp = pixel[col];
-            }
-    }
-  }
-  free (pixel);
-#endif
-}
-void CLASS ppm_thumb (FILE *tfp)
-{
-  char *thumb;
-  thumb_length = thumb_width*thumb_height*3;
-  thumb = (char *) malloc (thumb_length);
-  merror (thumb, "ppm_thumb()");
-  fprintf (tfp, "P6\n%d %d\n255\n", thumb_width, thumb_height);
-  fread  (thumb, 1, thumb_length, ifp);
-  fwrite (thumb, 1, thumb_length, tfp);
-  free (thumb);
-}
-
-void CLASS layer_thumb (FILE *tfp)
-{
-  int i, c;
-  char *thumb, map[][4] = { "012","102" };
-
-  colors = thumb_misc >> 5 & 7;
-  thumb_length = thumb_width*thumb_height;
-  thumb = (char *) calloc (colors, thumb_length);
-  merror (thumb, "layer_thumb()");
-  fprintf (tfp, "P%d\n%d %d\n255\n",
-	5 + (colors >> 1), thumb_width, thumb_height);
-  fread (thumb, thumb_length, colors, ifp);
-  for (i=0; i < thumb_length; i++)
-    FORCC putc (thumb[i+thumb_length*(map[thumb_misc >> 8][c]-'0')], tfp);
-  free (thumb);
-}
-
-void CLASS rollei_thumb (FILE *tfp)
-{
-  unsigned i;
-  ushort *thumb;
-
-  thumb_length = thumb_width * thumb_height;
-  thumb = (ushort *) calloc (thumb_length, 2);
-  merror (thumb, "rollei_thumb()");
-  fprintf (tfp, "P6\n%d %d\n255\n", thumb_width, thumb_height);
-  read_shorts (thumb, thumb_length);
-  for (i=0; i < thumb_length; i++) {
-    putc (thumb[i] << 3, tfp);
-    putc (thumb[i] >> 5  << 2, tfp);
-    putc (thumb[i] >> 11 << 3, tfp);
-  }
-  free (thumb);
-}
-
-void CLASS rollei_load_raw()
-{
-  uchar 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);
-#ifdef LIBRAW_LIBRARY_BUILD
-      else
-          {
-              ushort *dfp = get_masked_pointer(todo[i] / raw_width,todo[i] % raw_width);
-              if(dfp) *dfp = (todo[i+1] & 0x3ff);
-          }
-#endif
-    }
-  }
-  maximum = 0x3ff;
-}
-
-int CLASS bayer (unsigned row, unsigned col)
-{
-  return (row < height && col < width) ? BAYER(row,col) : 0;
-}
-
-void CLASS phase_one_flat_field (int is_float, int nc)
-{
-  ushort head[8];
-  unsigned wide, y, x, c, rend, cend, row, col;
-  float *mrow, num, mult[4];
-
-  read_shorts (head, 8);
-  wide = head[2] / head[4];
-  mrow = (float *) calloc (nc*wide, sizeof *mrow);
-  merror (mrow, "phase_one_flat_field()");
-  for (y=0; y < head[3] / head[5]; y++) {
-    for (x=0; x < wide; x++)
-      for (c=0; c < nc; c+=2) {
-	num = is_float ? getreal(11) : get2()/32768.0;
-	if (y==0) mrow[c*wide+x] = num;
-	else mrow[(c+1)*wide+x] = (num - mrow[c*wide+x]) / head[5];
-      }
-    if (y==0) continue;
-    rend = head[1]-top_margin + y*head[5];
-    for (row = rend-head[5]; row < height && row < rend; row++) {
-      for (x=1; x < wide; x++) {
-	for (c=0; c < nc; c+=2) {
-	  mult[c] = mrow[c*wide+x-1];
-	  mult[c+1] = (mrow[c*wide+x] - mult[c]) / head[4];
-	}
-	cend = head[0]-left_margin + x*head[4];
-	for (col = cend-head[4]; col < width && col < cend; col++) {
-	  c = nc > 2 ? FC(row,col) : 0;
-	  if (!(c & 1)) {
-	    c = BAYER(row,col) * mult[c];
-	    BAYER(row,col) = LIM(c,0,65535);
-	  }
-	  for (c=0; c < nc; c+=2)
-	    mult[c] += mult[c+1];
-	}
-      }
-      for (x=0; x < wide; x++)
-	for (c=0; c < nc; c+=2)
-	  mrow[c*wide+x] += mrow[(c+1)*wide+x];
-    }
-  }
-  free (mrow);
-}
-
-void CLASS phase_one_correct()
-{
-  unsigned entries, tag, data, save, col, row, type;
-  int len, i, j, k, cip, val[4], dev[4], sum, max;
-  int head[9], diff, mindiff=INT_MAX, off_412=0;
-  static const signed char dir[12][2] =
-    { {-1,-1}, {-1,1}, {1,-1}, {1,1}, {-2,0}, {0,-2}, {0,2}, {2,0},
-      {-2,-2}, {-2,2}, {2,-2}, {2,2} };
-  float poly[8], num, cfrac, frac, mult[2], *yval[2];
-  ushort t_curve[0x10000], *xval[2];
-
-  if (half_size || !meta_length) return;
-#ifdef DCRAW_VERBOSE
-  if (verbose) fprintf (stderr,_("Phase One correction...\n"));
-#endif
-  fseek (ifp, meta_offset, SEEK_SET);
-  order = get2();
-  fseek (ifp, 6, SEEK_CUR);
-  fseek (ifp, meta_offset+get4(), SEEK_SET);
-  entries = get4();  get4();
-  while (entries--) {
-    tag  = get4();
-    len  = get4();
-    data = get4();
-    save = ftell(ifp);
-    fseek (ifp, meta_offset+data, SEEK_SET);
-    if (tag == 0x419) {				/* Polynomial curve */
-      for (get4(), i=0; i < 8; i++)
-	poly[i] = getreal(11);
-      poly[3] += (ph1.tag_210 - poly[7]) * poly[6] + 1;
-      for (i=0; i < 0x10000; i++) {
-	num = (poly[5]*i + poly[3])*i + poly[1];
-	t_curve[i] = LIM(num,0,65535);
-      } goto apply;				/* apply to right half */
-    } else if (tag == 0x41a) {			/* Polynomial curve */
-      for (i=0; i < 4; i++)
-	poly[i] = getreal(11);
-      for (i=0; i < 0x10000; i++) {
-	for (num=0, j=4; j--; )
-	  num = num * i + poly[j];
-	t_curve[i] = LIM(num+i,0,65535);
-      } apply:					/* apply to whole image */
-      for (row=0; row < height; row++)
-	for (col = (tag & 1)*ph1.split_col; col < width; col++)
-	  BAYER(row,col) = t_curve[BAYER(row,col)];
-    } else if (tag == 0x400) {			/* Sensor defects */
-      while ((len -= 8) >= 0) {
-	col  = get2() - left_margin;
-	row  = get2() - top_margin;
-	type = get2(); get2();
-	if (col >= width) continue;
-	if (type == 131)			/* Bad column */
-	  for (row=0; row < height; row++)
-	    if (FC(row,col) == 1) {
-	      for (sum=i=0; i < 4; i++)
-		sum += val[i] = bayer (row+dir[i][0], col+dir[i][1]);
-	      for (max=i=0; i < 4; i++) {
-		dev[i] = abs((val[i] << 2) - sum);
-		if (dev[max] < dev[i]) max = i;
-	      }
-	      BAYER(row,col) = (sum - val[max])/3.0 + 0.5;
-	    } else {
-	      for (sum=0, i=8; i < 12; i++)
-		sum += bayer (row+dir[i][0], col+dir[i][1]);
-	      BAYER(row,col) = 0.5 + sum * 0.0732233 +
-		(bayer(row,col-2) + bayer(row,col+2)) * 0.3535534;
-	    }
-	else if (type == 129) {			/* Bad pixel */
-	  if (row >= height) continue;
-	  j = (FC(row,col) != 1) * 4;
-	  for (sum=0, i=j; i < j+8; i++)
-	    sum += bayer (row+dir[i][0], col+dir[i][1]);
-	  BAYER(row,col) = (sum + 4) >> 3;
-	}
-      }
-    } else if (tag == 0x401) {			/* All-color flat fields */
-      phase_one_flat_field (1, 2);
-    } else if (tag == 0x416 || tag == 0x410) {
-      phase_one_flat_field (0, 2);
-    } else if (tag == 0x40b) {			/* Red+blue flat field */
-      phase_one_flat_field (0, 4);
-    } else if (tag == 0x412) {
-      fseek (ifp, 36, SEEK_CUR);
-      diff = abs (get2() - ph1.tag_21a);
-      if (mindiff > diff) {
-	mindiff = diff;
-	off_412 = ftell(ifp) - 38;
-      }
-    }
-    fseek (ifp, save, SEEK_SET);
-  }
-  if (off_412) {
-    fseek (ifp, off_412, SEEK_SET);
-    for (i=0; i < 9; i++) head[i] = get4() & 0x7fff;
-    yval[0] = (float *) calloc (head[1]*head[3] + head[2]*head[4], 6);
-    merror (yval[0], "phase_one_correct()");
-    yval[1] = (float  *) (yval[0] + head[1]*head[3]);
-    xval[0] = (ushort *) (yval[1] + head[2]*head[4]);
-    xval[1] = (ushort *) (xval[0] + head[1]*head[3]);
-    get2();
-    for (i=0; i < 2; i++)
-      for (j=0; j < head[i+1]*head[i+3]; j++)
-	yval[i][j] = getreal(11);
-    for (i=0; i < 2; i++)
-      for (j=0; j < head[i+1]*head[i+3]; j++)
-	xval[i][j] = get2();
-    for (row=0; row < height; row++)
-      for (col=0; col < width; col++) {
-	cfrac = (float) col * head[3] / raw_width;
-	cfrac -= cip = cfrac;
-	num = BAYER(row,col) * 0.5;
-	for (i=cip; i < cip+2; i++) {
-	  for (k=j=0; j < head[1]; j++)
-	    if (num < xval[0][k = head[1]*i+j]) break;
-	  frac = (j == 0 || j == head[1]) ? 0 :
-		(xval[0][k] - num) / (xval[0][k] - xval[0][k-1]);
-	  mult[i-cip] = yval[0][k-1] * frac + yval[0][k] * (1-frac);
-	}
-	i = ((mult[0] * (1-cfrac) + mult[1] * cfrac)
-		* (row + top_margin) + num) * 2;
-	BAYER(row,col) = LIM(i,0,65535);
-      }
-    free (yval[0]);
-  }
-}
-
-void CLASS phase_one_load_raw()
-{
-  int row, col, a, b;
-  ushort *pixel, akey, bkey, mask;
-
-  fseek (ifp, ph1.key_off, SEEK_SET);
-  akey = get2();
-  bkey = get2();
-  mask = ph1.format == 1 ? 0x5555:0x1354;
-#ifndef LIBRAW_LIBRARY_BUILD
-  fseek (ifp, data_offset + top_margin*raw_width*2, SEEK_SET);
-  pixel = (ushort *) calloc (raw_width, sizeof *pixel);
-  merror (pixel, "phase_one_load_raw()");
-  for (row=0; row < height; row++) {
-    read_shorts (pixel, raw_width);
-    for (col=0; col < raw_width; col+=2) {
-      a = pixel[col+0] ^ akey;
-      b = pixel[col+1] ^ bkey;
-      pixel[col+0] = (a & mask) | (b & ~mask);
-      pixel[col+1] = (b & mask) | (a & ~mask);
-    }
-    for (col=0; col < width; col++)
-      BAYER(row,col) = pixel[col+left_margin];
-  }
-  free (pixel);
-#else
-  fseek (ifp, data_offset, SEEK_SET);
-  pixel = (ushort *) calloc (raw_width, sizeof *pixel);
-  merror (pixel, "phase_one_load_raw()");
-  for (row=0; row < raw_height; row++) {
-    read_shorts (pixel, raw_width);
-    for (col=0; col < raw_width; col+=2) {
-      a = pixel[col+0] ^ akey;
-      b = pixel[col+1] ^ bkey;
-      pixel[col+0] = (a & mask) | (b & ~mask);
-      pixel[col+1] = (b & mask) | (a & ~mask);
-    }
-    for (col=0; col < raw_width; col++)
-        {
-            ushort *dfp = get_masked_pointer(row,col);
-            if(dfp)
-                *dfp = pixel[col];
-            else
-                BAYER(row,col-left_margin) = pixel[col];
-        }
-  }
-  free (pixel);
-  if(!( filtering_mode & LIBRAW_FILTERING_NORAWCURVE) )
-#endif
-  phase_one_correct();
-}
-
-unsigned CLASS ph1_bits (int nbits)
-{
-#ifndef LIBRAW_NOTHREADS
-#define bitbuf tls->ph1_bits.bitbuf
-#define vbits  tls->ph1_bits.vbits    
-#else
-  static UINT64 bitbuf=0;
-  static int vbits=0;
-#endif
-  if (nbits == -1)
-    return bitbuf = vbits = 0;
-  if (nbits == 0) return 0;
-  if ((vbits -= nbits) < 0) {
-    bitbuf = bitbuf << 32 | get4();
-    vbits += 32;
-  }
-  return bitbuf << (64-nbits-vbits) >> (64-nbits);
-#ifndef LIBRAW_NOTHREADS
-#undef bitbuf
-#undef vbits
-#endif
-}
-
-void CLASS phase_one_load_raw_c()
-{
-  static const int length[] = { 8,7,6,9,11,10,5,12,14,13 };
-  int *offset, len[2], pred[2], row, col, i, j;
-  ushort *pixel;
-  short (*t_black)[2];
-
-  pixel = (ushort *) calloc (raw_width + raw_height*4, 2);
-  merror (pixel, "phase_one_load_raw_c()");
-  offset = (int *) (pixel + raw_width);
-  fseek (ifp, strip_offset, SEEK_SET);
-  for (row=0; row < raw_height; row++)
-    offset[row] = get4();
-  t_black = (short (*)[2]) offset + raw_height;
-  fseek (ifp, ph1.black_off, SEEK_SET);
-  if (ph1.black_off)
-      {
-    read_shorts ((ushort *) t_black[0], raw_height*2);
-#ifdef LIBRAW_LIBRARY_BUILD
-    imgdata.masked_pixels.ph1_black = (ushort (*)[2])calloc(raw_height*2,sizeof(ushort));
-    merror (imgdata.masked_pixels.ph1_black, "phase_one_load_raw_c()");
-    memmove(imgdata.masked_pixels.ph1_black,(ushort *) t_black[0],raw_height*2*sizeof(ushort));
-#endif
-      }
-  for (i=0; i < 256; i++)
-    curve[i] = i*i / 3.969 + 0.5;
-#ifdef LIBRAW_LIBRARY_BUILD
-  color_flags.curve_state = LIBRAW_COLORSTATE_CALCULATED;
-#endif
-  for (row=0; row < raw_height; row++) {
-    fseek (ifp, data_offset + offset[row], SEEK_SET);
-    ph1_bits(-1);
-    pred[0] = pred[1] = 0;
-    for (col=0; col < raw_width; col++) {
-      if (col >= (raw_width & -8))
-	len[0] = len[1] = 14;
-      else if ((col & 7) == 0)
-	for (i=0; i < 2; i++) {
-	  for (j=0; j < 5 && !ph1_bits(1); j++);
-	  if (j--) len[i] = length[j*2 + ph1_bits(1)];
-	}
-      if ((i = len[col & 1]) == 14)
-	pixel[col] = pred[col & 1] = ph1_bits(16);
-      else
-	pixel[col] = pred[col & 1] += ph1_bits(i) + 1 - (1 << (i - 1));
-      if (pred[col & 1] >> 16) derror();
-#ifdef LIBRAW_LIBRARY_BUILD
-  if(!( filtering_mode & LIBRAW_FILTERING_NORAWCURVE) )
-#endif
-      if (ph1.format == 5 && pixel[col] < 256)
-	pixel[col] = curve[pixel[col]];
-    }
-    if ((unsigned) (row-top_margin) < height)
-#ifndef LIBRAW_LIBRARY_BUILD
-      for (col=0; col < width; col++) {
-	i = (pixel[col+left_margin] << 2)
-		- ph1.t_black + t_black[row][col >= ph1.split_col];
-	if (i > 0) BAYER(row-top_margin,col) = i;
-      }
-#else
-    {
-      for (col=0; col < raw_width; col++) {
-          if( filtering_mode & LIBRAW_FILTERING_NOBLACKS)  
-              i = (pixel[col] << 2);
-          else
-              i = (pixel[col] << 2)
-                  - ph1.t_black + t_black[row][(col /* - left_margin */) >= ph1.split_col]; // changed to fix Coffin's bug!
-          if(col >= left_margin && col < width+left_margin)
-              {
-                  if (i > 0) BAYER(row-top_margin,col-left_margin) = i;
-              }
-          else
-              {
-                  ushort *dfp = get_masked_pointer(row,col);
-                  if(i>0 && dfp) *dfp = i;
-              }
-      }
-    }
-    else
-        {
-            // top-bottom fields
-            for (col=0; col < raw_width; col++) {
-                i = (pixel[col] << 2)
-                    - ph1.t_black + t_black[row][(col+left_margin) >= ph1.split_col];
-                if (i > 0) 
-                    {
-                        ushort *dfp = get_masked_pointer(row,col);
-                        if(dfp) *dfp = i;
-                    }
-            }
-        }
-#endif
-  }
-  free (pixel);
-#ifdef LIBRAW_LIBRARY_BUILD
-  if(!( filtering_mode & LIBRAW_FILTERING_NORAWCURVE) )
-#endif
-  phase_one_correct();
-  maximum = 0xfffc - ph1.t_black;
-}
-
-void CLASS hasselblad_load_raw()
-{
-  struct jhead jh;
-  struct decode *dindex;
-  int row, col, pred[2], len[2], diff, i;
-
-  if (!ljpeg_start (&jh, 0)) return;
-  free (jh.row);
-  order = 0x4949;
-  ph1_bits(-1);
-  for (row=-top_margin; row < raw_height-top_margin; row++) {
-    pred[0] = pred[1] = 0x8000;
-    for (col=-left_margin; col < raw_width-left_margin; col+=2) {
-      for (i=0; i < 2; i++) {
-	for (dindex=jh.huff[0]; dindex->branch[0]; )
-	  dindex = dindex->branch[ph1_bits(1)];
-	len[i] = dindex->leaf;
-      }
-      for (i=0; i < 2; i++) {
-	diff = ph1_bits(len[i]);
-	if ((diff & (1 << (len[i]-1))) == 0)
-	  diff -= (1 << len[i]) - 1;
-	if (diff == 65535) diff = -32768;
-	pred[i] += diff;
-	if (row >= 0 && row < height && (unsigned)(col+i) < width)
-	  BAYER(row,col+i) = pred[i];
-#ifdef LIBRAW_LIBRARY_BUILD
-        else
-            {
-                ushort *dfp = get_masked_pointer(row+top_margin,col+left_margin);
-                if(dfp) *dfp = pred[i];
-            }
-#endif
-      }
-    }
-  }
-  maximum = 0xffff;
-}
-
-void CLASS leaf_hdr_load_raw()
-{
-  ushort *pixel;
-  unsigned tile=0, r, c, row, col;
-
-  pixel = (ushort *) calloc (raw_width, sizeof *pixel);
-  merror (pixel, "leaf_hdr_load_raw()");
-  FORC(tiff_samples)
-    for (r=0; r < raw_height; r++) {
-      if (r % tile_length == 0) {
-	fseek (ifp, data_offset + 4*tile++, SEEK_SET);
-	fseek (ifp, get4() + 2*left_margin, SEEK_SET);
-      }
-      if (filters && c != shot_select) continue;
-      read_shorts (pixel, raw_width);
-      if ((row = r - top_margin) >= height) continue;
-      for (col=0; col < width; col++)
-	if (filters)  BAYER(row,col) = pixel[col];
-	else image[row*width+col][c] = pixel[col];
-    }
-  free (pixel);
-  if (!filters) {
-    maximum = 0xffff;
-    raw_color = 1;
-  }
-}
-
-void CLASS sinar_4shot_load_raw()
-{
-  ushort *pixel;
-  unsigned shot, row, col, r, c;
-
-  if ((shot = shot_select) || half_size) {
-    if (shot) shot--;
-    if (shot > 3) shot = 3;
-    fseek (ifp, data_offset + shot*4, SEEK_SET);
-    fseek (ifp, get4(), SEEK_SET);
-    unpacked_load_raw();
-    return;
-  }
-  free (image);
-  image = (ushort (*)[4])
-	calloc ((iheight=height)*(iwidth=width), sizeof *image);
-  merror (image, "sinar_4shot_load_raw()");
-  pixel = (ushort *) calloc (raw_width, sizeof *pixel);
-  merror (pixel, "sinar_4shot_load_raw()");
-  for (shot=0; shot < 4; shot++) {
-    fseek (ifp, data_offset + shot*4, SEEK_SET);
-    fseek (ifp, get4(), SEEK_SET);
-    for (row=0; row < raw_height; row++) {
-      read_shorts (pixel, raw_width);
-      if ((r = row-top_margin - (shot >> 1 & 1)) >= height) continue;
-      for (col=0; col < raw_width; col++) {
-	if ((c = col-left_margin - (shot & 1)) >= width) continue;
-        image[r*width+c][FC(row,col)] = pixel[col];
-      }
-    }
-  }
-  free (pixel);
-  shrink = filters = 0;
-}
-
-void CLASS imacon_full_load_raw()
-{
-  int row, col;
-
-  for (row=0; row < height; row++)
-    for (col=0; col < width; col++)
-      read_shorts (image[row*width+col], 3);
-}
-
-void CLASS packed_12_load_raw()
-{
-  int vbits=0, rbits=0, irow, row, col;
-  UINT64 bitbuf=0;
-
-  if (raw_width * 2 >= width * 3) {	/* If raw_width is in bytes, */
-    rbits = raw_width * 8;
-    raw_width = raw_width * 2 / 3;	/* convert it to pixels and  */
-    rbits -= raw_width * 12;		/* save the remainder.       */
-  }
-  order = load_flags & 1 ? 0x4949 : 0x4d4d;
-  for (irow=0; irow < height; irow++) {
-    row = irow;
-    if (load_flags & 2 &&
-	(row = irow * 2 % height + irow / (height/2)) == 1 &&
-	load_flags & 4) {
-      if (vbits=0, tiff_compress)
-	fseek (ifp, data_offset - (-width*height*3/4 & -2048), SEEK_SET);
-      else {
-	fseek (ifp, 0, SEEK_END);
-	fseek (ifp, ftell(ifp)/2, SEEK_SET);
-      }
-    }
-    for (col=0; col < raw_width; col++) {
-      if ((vbits -= 12) < 0) {
-	bitbuf = bitbuf << 32 | get4();
-	vbits += 32;
-      }
-      if ((unsigned) (col-left_margin) < width)
-	BAYER(row,col-left_margin) = bitbuf << (52-vbits) >> 52;
-#ifdef LIBRAW_LIBRARY_BUILD
-      else
-          {
-              ushort *dfp = get_masked_pointer(row,col);
-              if(dfp) *dfp = bitbuf << (52-vbits) >> 52;
-          }
-#endif
-      if (load_flags & 8 && (col % 10) == 9)
-	if (vbits=0, bitbuf & 255) derror();
-    }
-    vbits -= rbits;
-  }
-  if (!strcmp(make,"OLYMPUS")) black >>= 4;
-}
-
-void CLASS unpacked_load_raw()
-{
-  ushort *pixel;
-  int row, col, bits=0;
-
-  while (1 << ++bits < maximum);
-#ifndef LIBRAW_LIBRARY_BUILD
-  fseek (ifp, (top_margin*raw_width + left_margin) * 2, SEEK_CUR);
-  pixel = (ushort *) calloc (width, sizeof *pixel);
-  merror (pixel, "unpacked_load_raw()");
-  for (row=0; row < height; row++) {
-    read_shorts (pixel, width);
-    fseek (ifp, 2*(raw_width - width), SEEK_CUR);
-    for (col=0; col < width; col++)
-      if ((BAYER2(row,col) = pixel[col]) >> bits) derror();
-  }
-  free (pixel);
-#else
-  // fseek (ifp, (top_margin*raw_width + left_margin) * 2, SEEK_CUR);
-  pixel = (ushort *) calloc (raw_width, sizeof *pixel);
-  merror (pixel, "unpacked_load_raw()");
-  for (row=0; row < raw_height; row++) {
-    read_shorts (pixel, raw_width);
-    //fseek (ifp, 2*(raw_width - width), SEEK_CUR);
-    for (col=0; col < raw_width; col++)
-        {
-            ushort *dfp = get_masked_pointer(row,col);
-            if(dfp) 
-                *dfp = pixel[col];
-            else
-                {
-                    if ((BAYER2(row-top_margin,col-left_margin) = pixel[col]) >> bits) derror();
-                }
-        }
-  }
-  free (pixel);
-#endif
-}
-
-void CLASS nokia_load_raw()
-{
-  uchar  *data,  *dp;
-  ushort *pixel, *pix;
-  int dwide, row, c;
-
-  dwide = raw_width * 5 / 4;
-  data = (uchar *) malloc (dwide + raw_width*2);
-  merror (data, "nokia_load_raw()");
-  pixel = (ushort *) (data + dwide);
-  for (row=0; row < raw_height; row++) {
-    if (fread (data, 1, dwide, ifp) < dwide) derror();
-    for (dp=data, pix=pixel; pix < pixel+raw_width; dp+=5, pix+=4)
-      FORC4 pix[c] = (dp[c] << 2) | (dp[4] >> (c << 1) & 3);
-    if (row < top_margin)
-#ifdef LIBRAW_LIBRARY_BUILD
-        {
-            int col;
-            for(col=0;col<width;col++)
-                {
-                    ushort *dfp = get_masked_pointer(row,col);
-                    if(dfp) 
-                        *dfp = pixel[col];
-                }
-            FORC(width) black += pixel[c];
-        }
-#else
-      FORC(width) black += pixel[c];
-#endif
-    else
-      FORC(width) BAYER(row-top_margin,c) = pixel[c];
-  }
-  free (data);
-  if (top_margin) black /= top_margin * width;
-  maximum = 0x3ff;
-}
-
-unsigned CLASS pana_bits (int nbits)
-{
-#ifndef LIBRAW_NOTHREADS
-#define buf tls->pana_bits.buf
-#define vbits tls->pana_bits.vbits   
-#else
-  static uchar buf[0x4000];
-  static int vbits;
-#endif
-  int byte;
-
-  if (!nbits) return vbits=0;
-  if (!vbits) {
-    fread (buf+load_flags, 1, 0x4000-load_flags, ifp);
-    fread (buf, 1, load_flags, ifp);
-  }
-  vbits = (vbits - nbits) & 0x1ffff;
-  byte = vbits >> 3 ^ 0x3ff0;
-  return (buf[byte] | buf[byte+1] << 8) >> (vbits & 7) & ~(-1 << nbits);
-#ifndef LIBRAW_NOTHREADS
-#undef buf
-#undef vbits
-#endif
-}
-
-void CLASS panasonic_load_raw()
-{
-  int row, col, i, j, sh=0, pred[2], nonz[2];
-
-  pana_bits(0);
-  for (row=0; row < height; row++)
-    for (col=0; col < raw_width; col++) {
-      if ((i = col % 14) == 0)
-	pred[0] = pred[1] = nonz[0] = nonz[1] = 0;
-      if (i % 3 == 2) sh = 4 >> (3 - pana_bits(2));
-      if (nonz[i & 1]) {
-	if ((j = pana_bits(8))) {
-	  if ((pred[i & 1] -= 0x80 << sh) < 0 || sh == 4)
-	       pred[i & 1] &= ~(-1 << sh);
-	  pred[i & 1] += j << sh;
-	}
-      } else if ((nonz[i & 1] = pana_bits(8)) || i > 11)
-	pred[i & 1] = nonz[i & 1] << 4 | pana_bits(4);
-      if (col < width)
-      if ((BAYER(row,col) = pred[col & 1]) > 4098) derror();
-#ifdef LIBRAW_LIBRARY_BUILD
-      if(col>=width)
-          {
-              ushort *dfp = get_masked_pointer(row,col);
-              if(dfp)*dfp = pred[col & 1];
-          }
-#endif
-    }
-}
-
-void CLASS olympus_e300_load_raw()
-{
-  uchar  *data,  *dp;
-  ushort *pixel, *pix;
-  int dwide, row, col;
-
-  dwide = raw_width * 16 / 10;
-#ifndef LIBRAW_LIBRARY_BUILD
-  fseek (ifp, dwide*top_margin, SEEK_CUR);
-#endif
-  data = (uchar *) malloc (dwide + raw_width*2);
-  merror (data, "olympus_e300_load_raw()");
-  pixel = (ushort *) (data + dwide);
-#ifndef LIBRAW_LIBRARY_BUILD
-  for (row=0; row < height; row++)
-#else
-  for (row=0; row < raw_height; row++)
-#endif
-      {
-    if (fread (data, 1, dwide, ifp) < dwide) derror();
-    for (dp=data, pix=pixel; pix < pixel+raw_width; dp+=3, pix+=2) {
-      if (((dp-data) & 15) == 15)
-	if (*dp++ && pix < pixel+width+left_margin) derror();
-      pix[0] = dp[1] << 8 | dp[0];
-      pix[1] = dp[2] << 4 | dp[1] >> 4;
-    }
-#ifndef LIBRAW_LIBRARY_BUILD
-    for (col=0; col < width; col++)
-      BAYER(row,col) = (pixel[col+left_margin] & 0xfff);
-#else
-    for (col=0; col < raw_width; col++)
-        {
-            ushort *dfp = get_masked_pointer(row,col);
-            if(dfp)
-                *dfp = (pixel[col] & 0xfff);
-            else
-                BAYER(row-top_margin,col-left_margin) = (pixel[col] & 0xfff);
-        }
-    
-#endif
-      }
-  free (data);
-  maximum >>= 4;
-  black >>= 4;
-}
-
-void CLASS olympus_e410_load_raw()
-{
-  int row, col, nbits, sign, low, high, i, w, n, nw;
-  int acarry[2][3], *carry, pred, diff;
-
-  fseek (ifp, 7, SEEK_CUR);
-  getbits(-1);
-  for (row=0; row < height; row++) {
-    memset (acarry, 0, sizeof acarry);
-    for (col=0; col < width; col++) {
-      carry = acarry[col & 1];
-      i = 2 * (carry[2] < 3);
-      for (nbits=2+i; (ushort) carry[0] >> (nbits+i); nbits++);
-      sign = getbits(1) * -1;
-      low  = getbits(2);
-      for (high=0; high < 12; high++)
-	if (getbits(1)) break;
-      if (high == 12)
-	high = getbits(16-nbits) >> 1;
-      carry[0] = (high << nbits) | getbits(nbits);
-      diff = (carry[0] ^ sign) + carry[1];
-      carry[1] = (diff*3 + carry[1]) >> 5;
-      carry[2] = carry[0] > 16 ? 0 : carry[2]+1;
-      if (row < 2 && col < 2) pred = 0;
-      else if (row < 2) pred = BAYER(row,col-2);
-      else if (col < 2) pred = BAYER(row-2,col);
-      else {
-	w  = BAYER(row,col-2);
-	n  = BAYER(row-2,col);
-	nw = BAYER(row-2,col-2);
-	if ((w < nw && nw < n) || (n < nw && nw < w)) {
-	  if (ABS(w-nw) > 32 || ABS(n-nw) > 32)
-	    pred = w + n - nw;
-	  else pred = (w + n) >> 1;
-	} else pred = ABS(w-nw) > ABS(n-nw) ? w : n;
-      }
-      if ((BAYER(row,col) = pred + ((diff << 2) | low)) >> 12) derror();
-    }
-  }
-}
-
-void CLASS minolta_rd175_load_raw()
-{
-  uchar pixel[768];
-  unsigned irow, box, row, col;
-
-  for (irow=0; irow < 1481; irow++) {
-    if (fread (pixel, 1, 768, ifp) < 768) derror();
-    box = irow / 82;
-    row = irow % 82 * 12 + ((box < 12) ? box | 1 : (box-12)*2);
-    switch (irow) {
-      case 1477: case 1479: continue;
-      case 1476: row = 984; break;
-      case 1480: row = 985; break;
-      case 1478: row = 985; box = 1;
-    }
-    if ((box < 12) && (box & 1)) {
-      for (col=0; col < 1533; col++, row ^= 1)
-	if (col != 1) BAYER(row,col) = (col+1) & 2 ?
-		   pixel[col/2-1] + pixel[col/2+1] : pixel[col/2] << 1;
-      BAYER(row,1)    = pixel[1]   << 1;
-      BAYER(row,1533) = pixel[765] << 1;
-    } else
-      for (col=row & 1; col < 1534; col+=2)
-	BAYER(row,col) = pixel[col/2] << 1;
-  }
-  maximum = 0xff << 1;
-}
-
-void CLASS casio_qv5700_load_raw()
-{
-  uchar  data[3232],  *dp;
-  ushort 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 CLASS quicktake_100_load_raw()
-{
-  uchar pixel[484][644];
-  static const short gstep[16] =
-  { -89,-60,-44,-32,-22,-15,-8,-2,2,8,15,22,32,44,60,89 };
-  static const short rstep[6][4] =
-  { {  -3,-1,1,3  }, {  -5,-1,1,5  }, {  -8,-2,2,8  },
-    { -13,-3,3,13 }, { -19,-4,4,19 }, { -28,-6,6,28 } };
-  static const short t_curve[256] =
-  { 0,1,2,3,4,5,6,7,8,9,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,
-    28,29,30,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,53,
-    54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,74,75,76,77,78,
-    79,80,81,82,83,84,86,88,90,92,94,97,99,101,103,105,107,110,112,114,116,
-    118,120,123,125,127,129,131,134,136,138,140,142,144,147,149,151,153,155,
-    158,160,162,164,166,168,171,173,175,177,179,181,184,186,188,190,192,195,
-    197,199,201,203,205,208,210,212,214,216,218,221,223,226,230,235,239,244,
-    248,252,257,261,265,270,274,278,283,287,291,296,300,305,309,313,318,322,
-    326,331,335,339,344,348,352,357,361,365,370,374,379,383,387,392,396,400,
-    405,409,413,418,422,426,431,435,440,444,448,453,457,461,466,470,474,479,
-    483,487,492,496,500,508,519,531,542,553,564,575,587,598,609,620,631,643,
-    654,665,676,687,698,710,721,732,743,754,766,777,788,799,810,822,833,844,
-    855,866,878,889,900,911,922,933,945,956,967,978,989,1001,1012,1023 };
-  int rb, row, col, sharp, val=0;
-
-  getbits(-1);
-  memset (pixel, 0x80, sizeof pixel);
-  for (row=2; row < height+2; row++) {
-    for (col=2+(row & 1); col < width+2; col+=2) {
-      val = ((pixel[row-1][col-1] + 2*pixel[row-1][col+1] +
-		pixel[row][col-2]) >> 2) + gstep[getbits(4)];
-      pixel[row][col] = val = LIM(val,0,255);
-      if (col < 4)
-	pixel[row][col-2] = pixel[row+1][~row & 1] = val;
-      if (row == 2)
-	pixel[row-1][col+1] = pixel[row-1][col+3] = val;
-    }
-    pixel[row][col] = val;
-  }
-  for (rb=0; rb < 2; rb++)
-    for (row=2+rb; row < height+2; row+=2)
-      for (col=3-(row & 1); col < width+2; col+=2) {
-	if (row < 4 || col < 4) sharp = 2;
-	else {
-	  val = ABS(pixel[row-2][col] - pixel[row][col-2])
-	      + ABS(pixel[row-2][col] - pixel[row-2][col-2])
-	      + ABS(pixel[row][col-2] - pixel[row-2][col-2]);
-	  sharp = val <  4 ? 0 : val <  8 ? 1 : val < 16 ? 2 :
-		  val < 32 ? 3 : val < 48 ? 4 : 5;
-	}
-	val = ((pixel[row-2][col] + pixel[row][col-2]) >> 1)
-	      + rstep[sharp][getbits(2)];
-	pixel[row][col] = val = LIM(val,0,255);
-	if (row < 4) pixel[row-2][col+2] = val;
-	if (col < 4) pixel[row+2][col-2] = val;
-      }
-  for (row=2; row < height+2; row++)
-    for (col=3-(row & 1); col < width+2; col+=2) {
-      val = ((pixel[row][col-1] + (pixel[row][col] << 2) +
-	      pixel[row][col+1]) >> 1) - 0x100;
-      pixel[row][col] = LIM(val,0,255);
-    }
-  for (row=0; row < height; row++)
-    for (col=0; col < width; col++)
-      BAYER(row,col) = t_curve[pixel[row+2][col+2]];
-  maximum = 0x3ff;
-}
-
-const int * CLASS make_decoder_int (const int *source, int level)
-{
-  struct decode *cur;
-
-  cur = free_decode++;
-  if (level < source[0]) {
-    cur->branch[0] = free_decode;
-    source = make_decoder_int (source, level+1);
-    cur->branch[1] = free_decode;
-    source = make_decoder_int (source, level+1);
-  } else {
-    cur->leaf = source[1];
-    source += 2;
-  }
-  return source;
-}
-
-int CLASS radc_token (int tree)
-{
-  int t;
-#ifndef LIBRAW_NOTHREADS
-#define dstart tls->radc_token.dstart
-#define dindex tls->radc_token.dindex
-#define s       tls->radc_token.s
-
-  static const  int source[] = {
-#else
-  static struct decode *dstart[18], *dindex;
-  static const int *s, source[] = {
-#endif
-    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 (kodak_cbpp == 243)
-      return (getbits(6) << 2) + 2;	/* most DC50 photos */
-    else
-      return (getbits(5) << 3) + 4;	/* DC40, Fotoman Pixtura */
-  }
-  for (dindex = dstart[tree]; dindex->branch[0]; )
-    dindex = dindex->branch[getbits(1)];
-  return dindex->leaf;
-
-#ifndef LIBRAW_NOTHREADS
-#undef dstart
-#undef dindex
-#undef s
-#endif
-}
-
-#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 CLASS 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(-1);
-  for (i=0; i < sizeof(buf)/sizeof(short); i++)
-    buf[0][0][i] = 2048;
-  for (row=0; row < height; row+=4) {
-    FORC3 mul[c] = getbits(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) {
-	  r = x ? x-1 : x+1;
-	  s = x+1 < width ? x+1 : x-1;
-	  val = (BAYER(y,x)-2048)*2 + (BAYER(y,r)+BAYER(y,s))/2;
-	  if (val < 0) val = 0;
-	  BAYER(y,x) = val;
-	}
-  }
-  maximum = 0xfff;
-  use_gamma = 0;
-}
-
-#undef FORYX
-#undef PREDICTOR
-
-#ifdef NO_JPEG
-void CLASS kodak_jpeg_load_raw() {}
-#else
-
-METHODDEF(boolean)
-fill_input_buffer (j_decompress_ptr cinfo)
-{
-#ifndef LIBRAW_NOTHREADS
-#define jpeg_buffer tls->jpeg_buffer
-#else
-  static uchar jpeg_buffer[4096];
-#endif
-  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;
-#ifndef LIBRAW_NOTHREADS
-#undef jpeg_buffer
-#endif
-}
-
-void CLASS 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      )) {
-#ifdef DCRAW_VERBOSE
-    fprintf (stderr,_("%s: incorrect JPEG dimensions\n"), ifname);
-#endif
-    jpeg_destroy_decompress (&cinfo);
-#ifdef LIBRAW_LIBRARY_BUILD
-    throw LIBRAW_EXCEPTION_DECODE_JPEG;
-#else
-    longjmp (failure, 3);
-#endif
-  }
-  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 = (JSAMPLE (*)[3]) 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 CLASS kodak_dc120_load_raw()
-{
-  static const int mul[4] = { 162, 192, 187,  92 };
-  static const int add[4] = {   0, 636, 424, 212 };
-  uchar pixel[848];
-  int row, shift, col;
-
-  for (row=0; row < height; row++) {
-    if (fread (pixel, 1, 848, ifp) < 848) derror();
-    shift = row * mul[row & 3] + add[row & 3];
-    for (col=0; col < width; col++)
-      BAYER(row,col) = (ushort) pixel[(col + shift) % 848];
-  }
-  maximum = 0xff;
-}
-
-void CLASS eight_bit_load_raw()
-{
-  uchar *pixel;
-  unsigned row, col, val, lblack=0;
-
-  pixel = (uchar *) calloc (raw_width, sizeof *pixel);
-  merror (pixel, "eight_bit_load_raw()");
-#ifndef LIBRAW_LIBRARY_BUILD
-  fseek (ifp, top_margin*raw_width, SEEK_CUR);
-  for (row=0; row < height; row++) {
-    if (fread (pixel, 1, raw_width, ifp) < raw_width) derror();
-    for (col=0; col < raw_width; col++) {
-      val = curve[pixel[col]];
-      if ((unsigned) (col-left_margin) < width)
-        BAYER(row,col-left_margin) = val;
-      else lblack += val;
-    }
-  }
-#else
-  for (row=0; row < raw_height; row++) {
-    if (fread (pixel, 1, raw_width, ifp) < raw_width) derror();
-    for (col=0; col < raw_width; col++) {
-        if(filtering_mode & LIBRAW_FILTERING_NORAWCURVE)
-            {
-                val = pixel[col];
-                if(val>maximum) maximum = val;
-            }
-        else
-            val = curve[pixel[col]];
-      if((unsigned) (row-top_margin)< height)
-          {
-              if ((unsigned) (col-left_margin) < width)
-                  BAYER(row,col-left_margin) = val;
-              else
-                  {
-                      ushort *dfp = get_masked_pointer(row,col);
-                      if(dfp) *dfp = val;
-                      lblack += val;
-                  }
-          }
-      else // top/bottom margins
-          {
-              ushort *dfp = get_masked_pointer(row,col);
-              if(dfp) *dfp = val;
-          }
-    }
-  }
-#endif
-
-  free (pixel);
-  if (raw_width > width+1)
-    black = lblack / ((raw_width - width) * height);
-  if (!strncmp(model,"DC2",3))
-    black = 0;
-#ifdef LIBRAW_LIBRARY_BUILD
-  if(!(filtering_mode & LIBRAW_FILTERING_NORAWCURVE))
-#endif
-  maximum = curve[0xff];
-}
-
-void CLASS kodak_yrgb_load_raw()
-{
-  uchar *pixel;
-  int row, col, y, cb, cr, rgb[3], c;
-
-  pixel = (uchar *) calloc (raw_width, 3*sizeof *pixel);
-  merror (pixel, "kodak_yrgb_load_raw()");
-  for (row=0; row < height; row++) {
-    if (~row & 1)
-      if (fread (pixel, raw_width, 3, ifp) < 3) derror();
-    for (col=0; col < raw_width; col++) {
-      y  = pixel[width*2*(row & 1) + col];
-      cb = pixel[width + (col & -2)]   - 128;
-      cr = pixel[width + (col & -2)+1] - 128;
-      rgb[1] = y-((cb + cr + 2) >> 2);
-      rgb[2] = rgb[1] + cb;
-      rgb[0] = rgb[1] + cr;
-      FORC3 image[row*width+col][c] = LIM(rgb[c],0,255);
-    }
-  }
-  free (pixel);
-  use_gamma = 0;
-}
-
-void CLASS kodak_262_load_raw()
-{
-  static const uchar kodak_tree[2][26] =
-  { { 0,1,5,1,1,2,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9 },
-    { 0,3,1,1,1,1,1,2,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9 } };
-  struct decode *decode[2];
-  uchar *pixel;
-  int *strip, ns, i, row, col, chess, pi=0, pi1, pi2, pred, val;
-
-  init_decoder();
-  for (i=0; i < 2; i++) {
-    decode[i] = free_decode;
-    make_decoder (kodak_tree[i], 0);
-  }
-  ns = (raw_height+63) >> 5;
-  pixel = (uchar *) malloc (raw_width*32 + ns*4);
-  merror (pixel, "kodak_262_load_raw()");
-  strip = (int *) (pixel + raw_width*32);
-  order = 0x4d4d;
-  for (i=0; i < ns; i++)
-    strip[i] = get4();
-  for (row=0; row < raw_height; row++) {
-    if ((row & 31) == 0) {
-      fseek (ifp, strip[row >> 5], SEEK_SET);
-      getbits(-1);
-      pi = 0;
-    }
-    for (col=0; col < raw_width; col++) {
-      chess = (row + col) & 1;
-      pi1 = chess ? pi-2           : pi-raw_width-1;
-      pi2 = chess ? pi-2*raw_width : pi-raw_width+1;
-      if (col <= chess) pi1 = -1;
-      if (pi1 < 0) pi1 = pi2;
-      if (pi2 < 0) pi2 = pi1;
-      if (pi1 < 0 && col > 1) pi1 = pi2 = pi-2;
-      pred = (pi1 < 0) ? 0 : (pixel[pi1] + pixel[pi2]) >> 1;
-      pixel[pi] = val = pred + ljpeg_diff (decode[chess]);
-      if (val >> 8) derror();
-#ifdef LIBRAW_LIBRARY_BUILD
-      if(filtering_mode & LIBRAW_FILTERING_NORAWCURVE)
-          val = pixel[pi++];
-      else
-          val = curve[pixel[pi++]];
-#else
-      val = curve[pixel[pi++]];
-#endif
-      if ((unsigned) (col-left_margin) < width)
-	BAYER(row,col-left_margin) = val;
-      else
-#ifndef LIBRAW_LIBRARY_BUILD
-          black += val;
-#else
-      {
-          ushort *dfp = get_masked_pointer(row,col);
-          if(dfp) *dfp = val;
-          black += val;
-      }
-#endif
-    }
-  }
-  free (pixel);
-  if (raw_width > width)
-    black /= (raw_width - width) * height;
-}
-
-int CLASS kodak_65000_decode (short *out, int bsize)
-{
-  uchar c, blen[768];
-  ushort raw[6];
-  INT64 bitbuf=0;
-  int save, bits=0, i, j, len, diff;
-
-  save = ftell(ifp);
-  bsize = (bsize + 3) & -4;
-  for (i=0; i < bsize; i+=2) {
-    c = fgetc(ifp);
-    if ((blen[i  ] = c & 15) > 12 ||
-	(blen[i+1] = c >> 4) > 12 ) {
-      fseek (ifp, save, SEEK_SET);
-      for (i=0; i < bsize; i+=8) {
-	read_shorts (raw, 6);
-	out[i  ] = raw[0] >> 12 << 8 | raw[2] >> 12 << 4 | raw[4] >> 12;
-	out[i+1] = raw[1] >> 12 << 8 | raw[3] >> 12 << 4 | raw[5] >> 12;
-	for (j=0; j < 6; j++)
-	  out[i+2+j] = raw[j] & 0xfff;
-      }
-      return 1;
-    }
-  }
-  if ((bsize & 7) == 4) {
-    bitbuf  = fgetc(ifp) << 8;
-    bitbuf += fgetc(ifp);
-    bits = 16;
-  }
-  for (i=0; i < bsize; i++) {
-    len = blen[i];
-    if (bits < len) {
-      for (j=0; j < 32; j+=8)
-	bitbuf += (INT64) fgetc(ifp) << (bits+(j^8));
-      bits += 32;
-    }
-    diff = bitbuf & (0xffff >> (16-len));
-    bitbuf >>= len;
-    bits -= len;
-    if ((diff & (1 << (len-1))) == 0)
-      diff -= (1 << len) - 1;
-    out[i] = diff;
-  }
-  return 0;
-}
-
-void CLASS kodak_65000_load_raw()
-{
-  short buf[256];
-  int row, col, len, pred[2], ret, i;
-
-  for (row=0; row < height; row++)
-    for (col=0; col < width; col+=256) {
-      pred[0] = pred[1] = 0;
-      len = MIN (256, width-col);
-      ret = kodak_65000_decode (buf, len);
-      for (i=0; i < len; i++)
-#ifndef LIBRAW_LIBRARY_BUILD
-	if ((BAYER(row,col+i) =	curve[ret ? buf[i] :
-		(pred[i & 1] += buf[i])]) >> 12) derror();
-#else
-      {
-          ushort val = ret ? buf[i] : (pred[i & 1] += buf[i]);
-          if(!(filtering_mode & LIBRAW_FILTERING_NORAWCURVE))
-              val = curve[val];
-          BAYER(row,col+i)=val;
-          if(curve[val]>>12) derror();
-      }
-#endif
-    }
-}
+  Copyright 2008-2020 LibRaw LLC (info@libraw.org)
 
-void CLASS kodak_ycbcr_load_raw()
-{
-  short buf[384], *bp;
-  int row, col, len, c, i, j, k, y[2][2], cb, cr, rgb[3];
-  ushort *ip;
+ * This file is provided for compatibility w/ old build scripts/tools:
+ * It includes multiple separate files that should be built separately
+ * if new build tools are used
 
-  for (row=0; row < height; row+=2)
-    for (col=0; col < width; col+=128) {
-      len = MIN (128, width-col);
-      kodak_65000_decode (buf, len*3);
-      y[0][1] = y[1][1] = cb = cr = 0;
-      for (bp=buf, i=0; i < len; i+=2, bp+=2) {
-	cb += bp[4];
-	cr += bp[5];
-	rgb[1] = -((cb + cr + 2) >> 2);
-	rgb[2] = rgb[1] + cb;
-	rgb[0] = rgb[1] + cr;
-	for (j=0; j < 2; j++)
-	  for (k=0; k < 2; k++) {
-	    if ((y[j][k] = y[j][k^1] + *bp++) >> 10) derror();
-	    ip = image[(row+j)*width + col+i+k];
-#ifndef LIBRAW_LIBRARY_BUILD
-	    FORC3 ip[c] = curve[LIM(y[j][k]+rgb[c], 0, 0xfff)];
-#else
-          if(!(filtering_mode & LIBRAW_FILTERING_NORAWCURVE))
-              FORC3 ip[c] = curve[LIM(y[j][k]+rgb[c], 0, 0xfff)];
-          else
-              FORC3 ip[c] = y[j][k]+rgb[c];;
-#endif
-	  }
-      }
-    }
-}
+  LibRaw is free software; you can redistribute it and/or modify
+  it under the terms of the one of two licenses as you choose:
 
-void CLASS kodak_rgb_load_raw()
-{
-  short buf[768], *bp;
-  int row, col, len, c, i, rgb[3];
-  ushort *ip=image[0];
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
 
-  for (row=0; row < height; row++)
-    for (col=0; col < width; col+=256) {
-      len = MIN (256, width-col);
-      kodak_65000_decode (buf, len*3);
-      memset (rgb, 0, sizeof rgb);
-      for (bp=buf, i=0; i < len; i++, ip+=4)
-	FORC3 if ((ip[c] = rgb[c] += *bp++) >> 12) derror();
-    }
-}
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
 
-void CLASS kodak_thumb_load_raw()
-{
-  int row, col;
-  colors = thumb_misc >> 5;
-  for (row=0; row < height; row++)
-    for (col=0; col < width; col++)
-      read_shorts (image[row*width+col], colors);
-  maximum = (1 << (thumb_misc & 31)) - 1;
-}
-
-void CLASS sony_decrypt (unsigned *data, int len, int start, int key)
-{
-#ifndef LIBRAW_NOTHREADS
-#define pad tls->sony_decrypt.pad
-#define p   tls->sony_decrypt.p
-#else
-  static unsigned pad[128], p;
-#endif
-
-  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;
-    for (p=0; p < 127; p++)
-      pad[p] = htonl(pad[p]);
-  }
-  while (len--)
-    *data++ ^= pad[p++ & 127] = pad[(p+1) & 127] ^ pad[(p+65) & 127];
-#ifndef LIBRAW_NOTHREADS
-#undef pad
-#undef p
-#endif
-}
-
-void CLASS sony_load_raw()
-{
-  uchar head[40];
-  ushort *pixel;
-  unsigned i, key, row, col;
-
-  fseek (ifp, 200896, SEEK_SET);
-  fseek (ifp, (unsigned) fgetc(ifp)*4 - 1, SEEK_CUR);
-  order = 0x4d4d;
-  key = get4();
-  fseek (ifp, 164600, SEEK_SET);
-  fread (head, 1, 40, ifp);
-  sony_decrypt ((unsigned int *) head, 10, 1, key);
-  for (i=26; i-- > 22; )
-    key = key << 8 | head[i];
-  fseek (ifp, data_offset, SEEK_SET);
-  pixel = (ushort *) calloc (raw_width, sizeof *pixel);
-  merror (pixel, "sony_load_raw()");
-  for (row=0; row < height; row++) {
-    if (fread (pixel, 2, raw_width, ifp) < raw_width) derror();
-    sony_decrypt ((unsigned int *) pixel, raw_width/2, !row, key);
-#ifdef LIBRAW_LIBRARY_BUILD
-    for (col=0; col < left_margin; col++)
-          {
-              ushort *dfp = get_masked_pointer(row,col);
-              if(dfp) *dfp = ntohs(pixel[col]);
-          }
-    for (col=left_margin+width; col < raw_width; col++)
-          {
-              ushort *dfp = get_masked_pointer(row,col);
-              if(dfp) *dfp = ntohs(pixel[col]);
-          }
-#endif
-    for (col=9; col < left_margin; col++)
-      black += ntohs(pixel[col]);
-    for (col=0; col < width; col++)
-      if ((BAYER(row,col) = ntohs(pixel[col+left_margin])) >> 14)
-	derror();
-  }
-  free (pixel);
-  if (left_margin > 9)
-    black /= (left_margin-9) * height;
-  maximum = 0x3ff0;
-}
-
-void CLASS sony_arw_load_raw()
-{
-  int col, row, len, diff, sum=0;
-
-  getbits(-1);
-  for (col = raw_width; col--; )
-    for (row=0; row < raw_height+1; row+=2) {
-      if (row == raw_height) row = 1;
-      len = 4 - getbits(2);
-      if (len == 3 && getbits(1)) len = 0;
-      if (len == 4)
-	while (len < 17 && !getbits(1)) len++;
-      diff = getbits(len);
-      if ((diff & (1 << (len-1))) == 0)
-	diff -= (1 << len) - 1;
-      if ((sum += diff) >> 12) derror();
-      if (row < height) BAYER(row,col) = sum;
-#ifdef LIBRAW_LIBRARY_BUILD
-      else
-          {
-              ushort *dfp = get_masked_pointer(row,col);
-              if(dfp) *dfp = sum;
-          }
-#endif
-    }
-}
-
-void CLASS sony_arw2_load_raw()
-{
-  uchar *data, *dp;
-  ushort pix[16];
-  int row, col, val, max, min, imax, imin, sh, bit, i;
-
-  data = (uchar *) malloc (raw_width*tiff_bps >> 3);
-  merror (data, "sony_arw2_load_raw()");
-  for (row=0; row < height; row++) {
-    fread (data, 1, raw_width*tiff_bps >> 3, ifp);
-    if (tiff_bps == 8) {
-      for (dp=data, col=0; col < width-30; dp+=16) {
-	max = 0x7ff & (val = sget4(dp));
-	min = 0x7ff & val >> 11;
-	imax = 0x0f & val >> 22;
-	imin = 0x0f & val >> 26;
-	for (sh=0; sh < 4 && 0x80 << sh <= max-min; sh++);
-	for (bit=30, i=0; i < 16; i++)
-	  if      (i == imax) pix[i] = max;
-	  else if (i == imin) pix[i] = min;
-	  else {
-	    pix[i] = ((sget2(dp+(bit >> 3)) >> (bit & 7) & 0x7f) << sh) + min;
-	    if (pix[i] > 0x7ff) pix[i] = 0x7ff;
-	    bit += 7;
-	  }
-	for (i=0; i < 16; i++, col+=2)
-#ifndef LIBRAW_LIBRARY_BUILD
-	  BAYER(row,col) = curve[pix[i] << 1] >> 1;
-#else
-        {
-            ushort val = pix[i];
-            if(!(filtering_mode & LIBRAW_FILTERING_NORAWCURVE))
-                val = curve[val<<1]>>1;
-            BAYER(row,col)=val;
-        }
-#endif
-	col -= col & 1 ? 1:31;
-      }
-    } else if (tiff_bps == 12)
-      for (dp=data, col=0; col < width; dp+=3, col+=2) {
-	BAYER(row,col)   = ((dp[1] << 8 | dp[0]) & 0xfff) << 1;
-	BAYER(row,col+1) =  (dp[2] << 4 | dp[1] >> 4) << 1;
-      }
-  }
-  free (data);
-}
-
-#define HOLE(row) ((holes >> (((row) - raw_height) & 7)) & 1)
-
-/* Kudos to Rich Taylor for figuring out SMaL's compression algorithm. */
-void CLASS smal_decode_segment (unsigned seg[2][2], int holes)
-{
-  uchar hist[3][13] = {
-    { 7, 7, 0, 0, 63, 55, 47, 39, 31, 23, 15, 7, 0 },
-    { 7, 7, 0, 0, 63, 55, 47, 39, 31, 23, 15, 7, 0 },
-    { 3, 3, 0, 0, 63,     47,     31,     15,    0 } };
-  int low, high=0xff, carry=0, nbits=8;
-  int s, count, bin, next, i, sym[3];
-  uchar diff, pred[]={0,0};
-  ushort data=0, range=0;
-  unsigned pix, row, col;
-
-  fseek (ifp, seg[0][1]+1, SEEK_SET);
-  getbits(-1);
-  for (pix=seg[0][0]; pix < seg[1][0]; pix++) {
-    for (s=0; s < 3; s++) {
-      data = data << nbits | getbits(nbits);
-      if (carry < 0)
-	carry = (nbits += carry+1) < 1 ? nbits-1 : 0;
-      while (--nbits >= 0)
-	if ((data >> nbits & 0xff) == 0xff) break;
-      if (nbits > 0)
-	  data = ((data & ((1 << (nbits-1)) - 1)) << 1) |
-	((data + (((data & (1 << (nbits-1)))) << 1)) & (-1 << nbits));
-      if (nbits >= 0) {
-	data += getbits(1);
-	carry = nbits - 8;
-      }
-      count = ((((data-range+1) & 0xffff) << 2) - 1) / (high >> 4);
-      for (bin=0; hist[s][bin+5] > count; bin++);
-		low = hist[s][bin+5] * (high >> 4) >> 2;
-      if (bin) high = hist[s][bin+4] * (high >> 4) >> 2;
-      high -= low;
-      for (nbits=0; high << nbits < 128; nbits++);
-      range = (range+low) << nbits;
-      high <<= nbits;
-      next = hist[s][1];
-      if (++hist[s][2] > hist[s][3]) {
-	next = (next+1) & hist[s][0];
-	hist[s][3] = (hist[s][next+4] - hist[s][next+5]) >> 2;
-	hist[s][2] = 1;
-      }
-      if (hist[s][hist[s][1]+4] - hist[s][hist[s][1]+5] > 1) {
-	if (bin < hist[s][1])
-	  for (i=bin; i < hist[s][1]; i++) hist[s][i+5]--;
-	else if (next <= bin)
-	  for (i=hist[s][1]; i < bin; i++) hist[s][i+5]++;
-      }
-      hist[s][1] = next;
-      sym[s] = bin;
-    }
-    diff = sym[2] << 5 | sym[1] << 2 | (sym[0] & 3);
-    if (sym[0] & 4)
-      diff = diff ? -diff : 0x80;
-    if (ftell(ifp) + 12 >= seg[1][1])
-      diff = 0;
-    pred[pix & 1] += diff;
-    row = pix / raw_width - top_margin;
-    col = pix % raw_width - left_margin;
-    if (row < height && col < width)
-      BAYER(row,col) = pred[pix & 1];
-#ifdef LIBRAW_LIBRARY_BUILD
-    else
-        {
-            ushort *dfp = get_masked_pointer(row+top_margin,col+left_margin);
-            if(dfp) *dfp = pred[pix &1];
-        }
-#endif
-    if (!(pix & 1) && HOLE(row)) pix += 2;
-  }
-  maximum = 0xff;
-}
-
-void CLASS smal_v6_load_raw()
-{
-  unsigned seg[2][2];
-
-  fseek (ifp, 16, SEEK_SET);
-  seg[0][0] = 0;
-  seg[0][1] = get2();
-  seg[1][0] = raw_width * raw_height;
-  seg[1][1] = INT_MAX;
-  smal_decode_segment (seg, 0);
-  use_gamma = 0;
-}
-
-int CLASS median4 (int *p)
-{
-  int min, max, sum, i;
-
-  min = max = sum = p[0];
-  for (i=1; i < 4; i++) {
-    sum += p[i];
-    if (min > p[i]) min = p[i];
-    if (max < p[i]) max = p[i];
-  }
-  return (sum - min - max) >> 1;
-}
-
-void CLASS fill_holes (int holes)
-{
-  int row, col, val[4];
-
-  for (row=2; row < height-2; row++) {
-    if (!HOLE(row)) continue;
-    for (col=1; col < width-1; col+=4) {
-      val[0] = BAYER(row-1,col-1);
-      val[1] = BAYER(row-1,col+1);
-      val[2] = BAYER(row+1,col-1);
-      val[3] = BAYER(row+1,col+1);
-      BAYER(row,col) = median4(val);
-    }
-    for (col=2; col < width-2; col+=4)
-      if (HOLE(row-2) || HOLE(row+2))
-	BAYER(row,col) = (BAYER(row,col-2) + BAYER(row,col+2)) >> 1;
-      else {
-	val[0] = BAYER(row,col-2);
-	val[1] = BAYER(row,col+2);
-	val[2] = BAYER(row-2,col);
-	val[3] = BAYER(row+2,col);
-	BAYER(row,col) = median4(val);
-      }
-  }
-}
-
-void CLASS smal_v9_load_raw()
-{
-  unsigned seg[256][2], offset, nseg, holes, i;
-
-  fseek (ifp, 67, SEEK_SET);
-  offset = get4();
-  nseg = fgetc(ifp);
-  fseek (ifp, offset, SEEK_SET);
-  for (i=0; i < nseg*2; i++)
-    seg[0][i] = get4() + data_offset*(i & 1);
-  fseek (ifp, 78, SEEK_SET);
-  holes = fgetc(ifp);
-  fseek (ifp, 88, SEEK_SET);
-  seg[nseg][0] = raw_height * raw_width;
-  seg[nseg][1] = get4() + data_offset;
-  for (i=0; i < nseg; i++)
-    smal_decode_segment (seg+i, holes);
-  if (holes) fill_holes (holes);
-}
-
-void CLASS pseudoinverse (double (*in)[3], double (*out)[3], int size)
-{
-  double work[3][6], num;
-  int i, j, k;
-
-  for (i=0; i < 3; i++) {
-    for (j=0; j < 6; j++)
-      work[i][j] = j == i+3;
-    for (j=0; j < 3; j++)
-      for (k=0; k < size; k++)
-	work[i][j] += in[k][i] * in[k][j];
-  }
-  for (i=0; i < 3; i++) {
-    num = work[i][i];
-    for (j=0; j < 6; j++)
-      work[i][j] /= num;
-    for (k=0; k < 3; k++) {
-      if (k==i) continue;
-      num = work[k][i];
-      for (j=0; j < 6; j++)
-	work[k][j] -= work[i][j] * num;
-    }
-  }
-  for (i=0; i < size; i++)
-    for (j=0; j < 3; j++)
-      for (out[i][j]=k=0; k < 3; k++)
-	out[i][j] += work[j][k+3] * in[i][k];
-}
-
-void CLASS cam_xyz_coeff (double cam_xyz[4][3])
-{
-  double cam_rgb[4][3], inverse[4][3], num;
-  int i, j, k;
-
-  for (i=0; i < colors; i++)		/* Multiply out XYZ colorspace */
-    for (j=0; j < 3; j++)
-      for (cam_rgb[i][j] = k=0; k < 3; k++)
-	cam_rgb[i][j] += cam_xyz[i][k] * xyz_rgb[k][j];
-
-  for (i=0; i < colors; i++) {		/* Normalize cam_rgb so that */
-    for (num=j=0; j < 3; j++)		/* cam_rgb * (1,1,1) is (1,1,1,1) */
-      num += cam_rgb[i][j];
-    for (j=0; j < 3; j++)
-      cam_rgb[i][j] /= num;
-    pre_mul[i] = 1 / num;
-  }
-  pseudoinverse (cam_rgb, inverse, colors);
-  for (raw_color = i=0; i < 3; i++)
-    for (j=0; j < colors; j++)
-      rgb_cam[i][j] = inverse[j][i];
-#ifdef LIBRAW_LIBRARY_BUILD
-  color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST;
-  color_flags.rgb_cam_state = LIBRAW_COLORSTATE_CONST;
-#endif
-}
-
-#ifdef COLORCHECK
-void CLASS colorcheck()
-{
-#define NSQ 24
-// Coordinates of the GretagMacbeth ColorChecker squares
-// width, height, 1st_column, 1st_row
-  static const int cut[NSQ][4] = {
-    { 241, 231, 234, 274 },
-    { 251, 235, 534, 274 },
-    { 255, 239, 838, 272 },
-    { 255, 240, 1146, 274 },
-    { 251, 237, 1452, 278 },
-    { 243, 238, 1758, 288 },
-    { 253, 253, 218, 558 },
-    { 255, 249, 524, 562 },
-    { 261, 253, 830, 562 },
-    { 260, 255, 1144, 564 },
-    { 261, 255, 1450, 566 },
-    { 247, 247, 1764, 576 },
-    { 255, 251, 212, 862 },
-    { 259, 259, 518, 862 },
-    { 263, 261, 826, 864 },
-    { 265, 263, 1138, 866 },
-    { 265, 257, 1450, 872 },
-    { 257, 255, 1762, 874 },
-    { 257, 253, 212, 1164 },
-    { 262, 251, 516, 1172 },
-    { 263, 257, 826, 1172 },
-    { 263, 255, 1136, 1176 },
-    { 255, 252, 1452, 1182 },
-    { 257, 253, 1760, 1180 } };
-// ColorChecker Chart under 6500-kelvin illumination
-  static const double gmb_xyY[NSQ][3] = {
-    { 0.400, 0.350, 10.1 },		// Dark Skin
-    { 0.377, 0.345, 35.8 },		// Light Skin
-    { 0.247, 0.251, 19.3 },		// Blue Sky
-    { 0.337, 0.422, 13.3 },		// Foliage
-    { 0.265, 0.240, 24.3 },		// Blue Flower
-    { 0.261, 0.343, 43.1 },		// Bluish Green
-    { 0.506, 0.407, 30.1 },		// Orange
-    { 0.211, 0.175, 12.0 },		// Purplish Blue
-    { 0.453, 0.306, 19.8 },		// Moderate Red
-    { 0.285, 0.202, 6.6 },		// Purple
-    { 0.380, 0.489, 44.3 },		// Yellow Green
-    { 0.473, 0.438, 43.1 },		// Orange Yellow
-    { 0.187, 0.129, 6.1 },		// Blue
-    { 0.305, 0.478, 23.4 },		// Green
-    { 0.539, 0.313, 12.0 },		// Red
-    { 0.448, 0.470, 59.1 },		// Yellow
-    { 0.364, 0.233, 19.8 },		// Magenta
-    { 0.196, 0.252, 19.8 },		// Cyan
-    { 0.310, 0.316, 90.0 },		// White
-    { 0.310, 0.316, 59.1 },		// Neutral 8
-    { 0.310, 0.316, 36.2 },		// Neutral 6.5
-    { 0.310, 0.316, 19.8 },		// Neutral 5
-    { 0.310, 0.316, 9.0 },		// Neutral 3.5
-    { 0.310, 0.316, 3.1 } };		// Black
-  double gmb_cam[NSQ][4], gmb_xyz[NSQ][3];
-  double inverse[NSQ][3], cam_xyz[4][3], num;
-  int c, i, j, k, sq, row, col, count[4];
-
-  memset (gmb_cam, 0, sizeof gmb_cam);
-  for (sq=0; sq < NSQ; sq++) {
-    FORCC count[c] = 0;
-    for   (row=cut[sq][3]; row < cut[sq][3]+cut[sq][1]; row++)
-      for (col=cut[sq][2]; col < cut[sq][2]+cut[sq][0]; col++) {
-	c = FC(row,col);
-	if (c >= colors) c -= 2;
-	gmb_cam[sq][c] += BAYER(row,col);
-	count[c]++;
-      }
-    FORCC gmb_cam[sq][c] = gmb_cam[sq][c]/count[c] - black;
-    gmb_xyz[sq][0] = gmb_xyY[sq][2] * gmb_xyY[sq][0] / gmb_xyY[sq][1];
-    gmb_xyz[sq][1] = gmb_xyY[sq][2];
-    gmb_xyz[sq][2] = gmb_xyY[sq][2] *
-		(1 - gmb_xyY[sq][0] - gmb_xyY[sq][1]) / gmb_xyY[sq][1];
-  }
-  pseudoinverse (gmb_xyz, inverse, NSQ);
-  for (i=0; i < colors; i++)
-    for (j=0; j < 3; j++)
-      for (cam_xyz[i][j] = k=0; k < NSQ; k++)
-	cam_xyz[i][j] += gmb_cam[k][i] * inverse[k][j];
-  cam_xyz_coeff (cam_xyz);
-#ifdef DCRAW_VERBOSE
-  if (verbose) {
-    printf ("    { \"%s %s\", %d,\n\t{", make, model, black);
-    num = 10000 / (cam_xyz[1][0] + cam_xyz[1][1] + cam_xyz[1][2]);
-    FORCC for (j=0; j < 3; j++)
-      printf ("%c%d", (c | j) ? ',':' ', (int) (cam_xyz[c][j] * num + 0.5));
-    puts (" } },");
-  }
-#endif
-#undef NSQ
-}
-#endif
-
-void CLASS hat_transform (float *temp, float *base, int st, int size, int sc)
-{
-  int i;
-  for (i=0; i < sc; i++)
-    temp[i] = 2*base[st*i] + base[st*(sc-i)] + base[st*(i+sc)];
-  for (; i+sc < size; i++)
-    temp[i] = 2*base[st*i] + base[st*(i-sc)] + base[st*(i+sc)];
-  for (; i < size; i++)
-    temp[i] = 2*base[st*i] + base[st*(i-sc)] + base[st*(2*size-2-(i+sc))];
-}
-
-#ifndef _OPENMP
-void CLASS wavelet_denoise()
-{
-  float *fimg=0, *temp, thold, mul[2], avg, diff;
-  int scale=1, size, lev, hpass, lpass, row, col, nc, c, i, wlast;
-  ushort *window[4];
-  static const float noise[] =
-  { 0.8002,0.2735,0.1202,0.0585,0.0291,0.0152,0.0080,0.0044 };
-
-#ifdef DCRAW_VERBOSE
-  if (verbose) fprintf (stderr,_("Wavelet denoising...\n"));
-#endif
-
-  while (maximum << scale < 0x10000) scale++;
-  maximum <<= --scale;
-  black <<= scale;
-  if ((size = iheight*iwidth) < 0x15550000)
-    fimg = (float *) malloc ((size*3 + iheight + iwidth) * sizeof *fimg);
-  merror (fimg, "wavelet_denoise()");
-  temp = fimg + size*3;
-  if ((nc = colors) == 3 && filters) nc++;
-  FORC(nc) {			/* denoise R,G1,B,G3 individually */
-    for (i=0; i < size; i++)
-        fimg[i] = 256 * sqrt((double)(image[i][c] << scale));
-    for (hpass=lev=0; lev < 5; lev++) {
-      lpass = size*((lev & 1)+1);
-      for (row=0; row < iheight; row++) {
-	hat_transform (temp, fimg+hpass+row*iwidth, 1, iwidth, 1 << lev);
-        for (col=0; col < iwidth; col++)
-	  fimg[lpass + row*iwidth + col] = temp[col] * 0.25;
-      }
-      for (col=0; col < iwidth; col++) {
-	hat_transform (temp, fimg+lpass+col, iwidth, iheight, 1 << lev);
-	for (row=0; row < iheight; row++)
-	  fimg[lpass + row*iwidth + col] = temp[row] * 0.25;
-      }
-      thold = threshold * noise[lev];
-      for (i=0; i < size; i++) {
-	fimg[hpass+i] -= fimg[lpass+i];
-	if	(fimg[hpass+i] < -thold) fimg[hpass+i] += thold;
-	else if (fimg[hpass+i] >  thold) fimg[hpass+i] -= thold;
-	else	 fimg[hpass+i] = 0;
-	if (hpass) fimg[i] += fimg[hpass+i];
-      }
-      hpass = lpass;
-    }
-    for (i=0; i < size; i++)
-      image[i][c] = CLIP(SQR(fimg[i]+fimg[lpass+i])/0x10000);
-  }
-  if (filters && colors == 3) {  /* pull G1 and G3 closer together */
-    for (row=0; row < 2; row++)
-      mul[row] = 0.125 * pre_mul[FC(row+1,0) | 1] / pre_mul[FC(row,0) | 1];
-    for (i=0; i < 4; i++)
-      window[i] = (ushort *) fimg + width*i;
-    for (wlast=-1, row=1; row < height-1; row++) {
-      while (wlast < row+1) {
-	for (wlast++, i=0; i < 4; i++)
-	  window[(i+3) & 3] = window[i];
-	for (col = FC(wlast,1) & 1; col < width; col+=2)
-	  window[2][col] = BAYER(wlast,col);
-      }
-      thold = threshold/512;
-      for (col = (FC(row,0) & 1)+1; col < width-1; col+=2) {
-	avg = ( window[0][col-1] + window[0][col+1] +
-		window[2][col-1] + window[2][col+1] - black*4 )
-	      * mul[row & 1] + (window[1][col] - black) * 0.5 + black;
-	avg = avg < 0 ? 0 : sqrt(avg);
-	diff = sqrt((double)(BAYER(row,col))) - avg;
-	if      (diff < -thold) diff += thold;
-	else if (diff >  thold) diff -= thold;
-	else diff = 0;
-	BAYER(row,col) = CLIP(SQR(avg+diff) + 0.5);
-      }
-    }
-  }
-  free (fimg);
-}
-#else
-void CLASS wavelet_denoise()
-{
-  float *fimg=0, *temp, thold, mul[2], avg, diff;
-  int scale=1, size, lev, hpass, lpass, row, col, nc, c, i, wlast;
-  ushort *window[4];
-  static const float noise[] =
-  { 0.8002,0.2735,0.1202,0.0585,0.0291,0.0152,0.0080,0.0044 };
-
-#ifdef DCRAW_VERBOSE
-  if (verbose) fprintf (stderr,_("Wavelet denoising...\n"));
-#endif
-
-  while (maximum << scale < 0x10000) scale++;
-  maximum <<= --scale;
-  black <<= scale;
-  if ((size = iheight*iwidth) < 0x15550000)
-    fimg = (float *) malloc ((size*3 + iheight + iwidth) * sizeof *fimg);
-  merror (fimg, "wavelet_denoise()");
-  temp = fimg + size*3;
-  if ((nc = colors) == 3 && filters) nc++;
-#ifdef LIBRAW_LIBRARY_BUILD
-#pragma omp parallel default(shared) private(i,col,row,thold,lev,lpass,hpass,temp) firstprivate(c,scale,size) 
-#endif
-  {
-      temp = (float*)malloc( (iheight + iwidth) * sizeof *fimg);
-    FORC(nc) {			/* denoise R,G1,B,G3 individually */
-#ifdef LIBRAW_LIBRARY_BUILD
-#pragma omp for
-#endif
-      for (i=0; i < size; i++)
-        fimg[i] = 256 * sqrt((double)(image[i][c] << scale));
-      for (hpass=lev=0; lev < 5; lev++) {
-	lpass = size*((lev & 1)+1);
-#ifdef LIBRAW_LIBRARY_BUILD
-#pragma omp for
-#endif
-	for (row=0; row < iheight; row++) {
-	  hat_transform (temp, fimg+hpass+row*iwidth, 1, iwidth, 1 << lev);
-	  for (col=0; col < iwidth; col++)
-	    fimg[lpass + row*iwidth + col] = temp[col] * 0.25;
-	}
-#ifdef LIBRAW_LIBRARY_BUILD
-#pragma omp for
-#endif
-	for (col=0; col < iwidth; col++) {
-	  hat_transform (temp, fimg+lpass+col, iwidth, iheight, 1 << lev);
-	  for (row=0; row < iheight; row++)
-	    fimg[lpass + row*iwidth + col] = temp[row] * 0.25;
-	}
-	thold = threshold * noise[lev];
-#ifdef LIBRAW_LIBRARY_BUILD
-#pragma omp for
-#endif
-	for (i=0; i < size; i++) {
-	  fimg[hpass+i] -= fimg[lpass+i];
-	  if	(fimg[hpass+i] < -thold) fimg[hpass+i] += thold;
-	  else if (fimg[hpass+i] >  thold) fimg[hpass+i] -= thold;
-	  else	 fimg[hpass+i] = 0;
-	  if (hpass) fimg[i] += fimg[hpass+i];
-	}
-	hpass = lpass;
-      }
-#ifdef LIBRAW_LIBRARY_BUILD
-#pragma omp for
-#endif
-      for (i=0; i < size; i++)
-	image[i][c] = CLIP(SQR(fimg[i]+fimg[lpass+i])/0x10000);
-    }
-    free(temp);
-  } /* end omp parallel */
-/* the following loops are hard to parallize, no idea yes,
- * problem is wlast which is carrying dependency
- * second part should be easyer, but did not yet get it right.
- */
-  if (filters && colors == 3) {  /* pull G1 and G3 closer together */
-    for (row=0; row < 2; row++)
-      mul[row] = 0.125 * pre_mul[FC(row+1,0) | 1] / pre_mul[FC(row,0) | 1];
-    for (i=0; i < 4; i++)
-      window[i] = (ushort *) fimg + width*i;
-    for (wlast=-1, row=1; row < height-1; row++) {
-      while (wlast < row+1) {
-	for (wlast++, i=0; i < 4; i++)
-	  window[(i+3) & 3] = window[i];
-	for (col = FC(wlast,1) & 1; col < width; col+=2)
-	  window[2][col] = BAYER(wlast,col);
-      }
-      thold = threshold/512;
-      for (col = (FC(row,0) & 1)+1; col < width-1; col+=2) {
-	avg = ( window[0][col-1] + window[0][col+1] +
-		window[2][col-1] + window[2][col+1] - black*4 )
-	      * mul[row & 1] + (window[1][col] - black) * 0.5 + black;
-	avg = avg < 0 ? 0 : sqrt(avg);
-	diff = sqrt(BAYER(row,col)) - avg;
-	if      (diff < -thold) diff += thold;
-	else if (diff >  thold) diff -= thold;
-	else diff = 0;
-	BAYER(row,col) = CLIP(SQR(avg+diff) + 0.5);
-      }
-    }
-  }
-  free (fimg);
-}
-
-#endif
-
-void CLASS scale_colors()
-{
-  unsigned bottom, right, size, row, col, ur, uc, i, x, y, c, sum[8];
-  int val, dark, sat;
-  double dsum[8], dmin, dmax;
-  float scale_mul[4], fr, fc;
-  ushort *img=0, *pix;
-
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_SCALE_COLORS,0,2);
-#endif
-
-  if (user_mul[0])
-    memcpy (pre_mul, user_mul, sizeof pre_mul);
-  if (use_auto_wb || (use_camera_wb && cam_mul[0] == -1)) {
-    memset (dsum, 0, sizeof dsum);
-    bottom = MIN (greybox[1]+greybox[3], height);
-    right  = MIN (greybox[0]+greybox[2], width);
-    for (row=greybox[1]; row < bottom; row += 8)
-      for (col=greybox[0]; col < right; col += 8) {
-	memset (sum, 0, sizeof sum);
-	for (y=row; y < row+8 && y < bottom; y++)
-	  for (x=col; x < col+8 && x < right; x++)
-	    FORC4 {
-	      if (filters) {
-		c = FC(y,x);
-		val = BAYER(y,x);
-	      } else
-		val = image[y*width+x][c];
-	      if (val > maximum-25) goto skip_block;
-	      if ((val -= black) < 0) val = 0;
-	      sum[c] += val;
-	      sum[c+4]++;
-	      if (filters) break;
-	    }
-	FORC(8) dsum[c] += sum[c];
-skip_block: ;
-      }
-    FORC4 if (dsum[c]) pre_mul[c] = dsum[c+4] / dsum[c];
-#ifdef LIBRAW_LIBRARY_BUILD
-    color_flags.pre_mul_state = LIBRAW_COLORSTATE_CALCULATED;
-#endif
-  }
-  if (use_camera_wb && cam_mul[0] != -1) {
-    memset (sum, 0, sizeof sum);
-    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;
-	sum[c+4]++;
-      }
-    if (sum[0] && sum[1] && sum[2] && sum[3])
-        {
-            FORC4 pre_mul[c] = (float) sum[c+4] / sum[c];
-#ifdef LIBRAW_LIBRARY_BUILD
-            color_flags.pre_mul_state = LIBRAW_COLORSTATE_CALCULATED;
-#endif
-        }
-    else if (cam_mul[0] && cam_mul[2])
-        {
-            memcpy (pre_mul, cam_mul, sizeof pre_mul);
-#ifdef LIBRAW_LIBRARY_BUILD
-            color_flags.pre_mul_state =color_flags.pre_mul_state;
-#endif
-        }
-    else
-        {
-#ifdef LIBRAW_LIBRARY_BUILD
-            imgdata.process_warnings |= LIBRAW_WARN_BAD_CAMERA_WB;
-#endif
-#ifdef DCRAW_VERBOSE
-            fprintf (stderr,_("%s: Cannot use camera white balance.\n"), ifname);
-#endif
-        }
-  }
-  if (pre_mul[3] == 0) pre_mul[3] = colors < 4 ? pre_mul[1] : 1;
-  dark = black;
-  sat = maximum;
-  if (threshold) wavelet_denoise();
-  maximum -= black;
-  for (dmin=DBL_MAX, dmax=c=0; c < 4; c++) {
-    if (dmin > pre_mul[c])
-	dmin = pre_mul[c];
-    if (dmax < pre_mul[c])
-	dmax = pre_mul[c];
-  }
-  if (!highlight) dmax = dmin;
-  FORC4 scale_mul[c] = (pre_mul[c] /= dmax) * 65535.0 / maximum;
-#ifdef DCRAW_VERBOSE
-  if (verbose) {
-    fprintf (stderr,
-      _("Scaling with darkness %d, saturation %d, and\nmultipliers"), dark, sat);
-    FORC4 fprintf (stderr, " %f", pre_mul[c]);
-    fputc ('\n', stderr);
-  }
-#endif
-  size = iheight*iwidth;
-  for (i=0; i < size*4; i++) {
-    val = image[0][i];
-    if (!val) continue;
-    val -= black;
-    val *= scale_mul[i & 3];
-    image[0][i] = CLIP(val);
-  }
-  if ((aber[0] != 1 || aber[2] != 1) && colors == 3) {
-#ifdef DCRAW_VERBOSE
-    if (verbose)
-      fprintf (stderr,_("Correcting chromatic aberration...\n"));
-#endif
-    for (c=0; c < 4; c+=2) {
-      if (aber[c] == 1) continue;
-      img = (ushort *) malloc (size * sizeof *img);
-      merror (img, "scale_colors()");
-      for (i=0; i < size; i++)
-	img[i] = image[i][c];
-      for (row=0; row < iheight; row++) {
-	ur = fr = (row - iheight*0.5) * aber[c] + iheight*0.5;
-	if (ur > iheight-2) continue;
-	fr -= ur;
-	for (col=0; col < iwidth; col++) {
-	  uc = fc = (col - iwidth*0.5) * aber[c] + iwidth*0.5;
-	  if (uc > iwidth-2) continue;
-	  fc -= uc;
-	  pix = img + ur*iwidth + uc;
-	  image[row*iwidth+col][c] =
-	    (pix[     0]*(1-fc) + pix[       1]*fc) * (1-fr) +
-	    (pix[iwidth]*(1-fc) + pix[iwidth+1]*fc) * fr;
-	}
-      }
-      free(img);
-    }
-  }
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_SCALE_COLORS,1,2);
-#endif
-}
-
-void CLASS pre_interpolate()
-{
-  ushort (*img)[4];
-  int row, col, c;
-
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_PRE_INTERPOLATE,0,2);
-#endif
-  if (shrink) {
-    if (half_size) {
-      height = iheight;
-      width  = iwidth;
-    } else {
-      img = (ushort (*)[4]) calloc (height*width, sizeof *img);
-      merror (img, "pre_interpolate()");
-      for (row=0; row < height; row++)
-	for (col=0; col < width; col++) {
-	  c = fc(row,col);
-	  img[row*width+col][c] = image[(row >> 1)*iwidth+(col >> 1)][c];
-	}
-      free (image);
-      image = img;
-      shrink = 0;
-    }
-  }
-  if (filters && colors == 3) {
-    if ((mix_green = four_color_rgb)) colors++;
-    else {
-      for (row = FC(1,0) >> 1; row < height; row+=2)
-	for (col = FC(row,1) & 1; col < width; col+=2)
-	  image[row*width+col][1] = image[row*width+col][3];
-      filters &= ~((filters & 0x55555555) << 1);
-    }
-  }
-  if (half_size) filters = 0;
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_PRE_INTERPOLATE,1,2);
-#endif
-}
-
-void CLASS border_interpolate (int border)
-{
-  unsigned row, col, y, x, f, c, sum[8];
-
-  for (row=0; row < height; row++)
-    for (col=0; col < width; col++) {
-      if (col==border && row >= border && row < height-border)
-	col = width-border;
-      memset (sum, 0, sizeof sum);
-      for (y=row-1; y != row+2; y++)
-	for (x=col-1; x != col+2; x++)
-	  if (y < height && x < width) {
-	    f = fc(y,x);
-	    sum[f] += image[y*width+x][f];
-	    sum[f+4]++;
-	  }
-      f = fc(row,col);
-      FORCC if (c != f && sum[c+4])
-	image[row*width+col][c] = sum[c] / sum[c+4];
-    }
-}
-
-void CLASS lin_interpolate()
-{
-  int code[16][16][32], *ip, sum[4];
-  int c, i, x, y, row, col, shift, color;
-  ushort *pix;
-
-#ifdef DCRAW_VERBOSE
-  if (verbose) fprintf (stderr,_("Bilinear interpolation...\n"));
-#endif
-
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE,0,3);
-#endif
-  border_interpolate(1);
-  for (row=0; row < 16; row++)
-    for (col=0; col < 16; col++) {
-      ip = code[row][col];
-      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;
-	}
-      FORCC
-	if (c != fc(row,col)) {
-	  *ip++ = c;
-	  *ip++ = 256 / sum[c];
-	}
-    }
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE,1,3);
-#endif
-  for (row=1; row < height-1; row++)
-    for (col=1; col < width-1; col++) {
-      pix = image[row*width+col];
-      ip = code[row & 15][col & 15];
-      memset (sum, 0, sizeof sum);
-      for (i=8; i--; ip+=3)
-	sum[ip[2]] += pix[ip[0]] << ip[1];
-      for (i=colors; --i; ip+=2)
-	pix[ip[0]] = sum[ip[0]] * ip[1] >> 8;
-    }
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE,2,3);
-#endif
-}
-
-/*
-   This algorithm is officially called:
-
-   "Interpolation using a Threshold-based variable number of gradients"
-
-   described in http://scien.stanford.edu/class/psych221/projects/99/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.
- */
-void CLASS vng_interpolate()
-{
-  struct interpolate_terms {
-    signed char y1, x1, y2, x2, weight;
-    unsigned char grads;
-  };
-  static const interpolate_terms terms[] = {
-    {-2,-2,+0,-1,0,0x01}, {-2,-2,+0,+0,1,0x01}, {-2,-1,-1,+0,0,0x01},
-    {-2,-1,+0,-1,0,0x02}, {-2,-1,+0,+0,0,0x03}, {-2,-1,+0,+1,1,0x01},
-    {-2,+0,+0,-1,0,0x06}, {-2,+0,+0,+0,1,0x02}, {-2,+0,+0,+1,0,0x03},
-    {-2,+1,-1,+0,0,0x04}, {-2,+1,+0,-1,1,0x04}, {-2,+1,+0,+0,0,0x06},
-    {-2,+1,+0,+1,0,0x02}, {-2,+2,+0,+0,1,0x04}, {-2,+2,+0,+1,0,0x04},
-    {-1,-2,-1,+0,0,0x80}, {-1,-2,+0,-1,0,0x01}, {-1,-2,+1,-1,0,0x01},
-    {-1,-2,+1,+0,1,0x01}, {-1,-1,-1,+1,0,0x88}, {-1,-1,+1,-2,0,0x40},
-    {-1,-1,+1,-1,0,0x22}, {-1,-1,+1,+0,0,0x33}, {-1,-1,+1,+1,1,0x11},
-    {-1,+0,-1,+2,0,0x08}, {-1,+0,+0,-1,0,0x44}, {-1,+0,+0,+1,0,0x11},
-    {-1,+0,+1,-2,1,0x40}, {-1,+0,+1,-1,0,0x66}, {-1,+0,+1,+0,1,0x22},
-    {-1,+0,+1,+1,0,0x33}, {-1,+0,+1,+2,1,0x10}, {-1,+1,+1,-1,1,0x44},
-    {-1,+1,+1,+0,0,0x66}, {-1,+1,+1,+1,0,0x22}, {-1,+1,+1,+2,0,0x10},
-    {-1,+2,+0,+1,0,0x04}, {-1,+2,+1,+0,1,0x04}, {-1,+2,+1,+1,0,0x04},
-    {+0,-2,+0,+0,1,0x80}, {+0,-1,+0,+1,1,0x88}, {+0,-1,+1,-2,0,0x40},
-    {+0,-1,+1,+0,0,0x11}, {+0,-1,+2,-2,0,0x40}, {+0,-1,+2,-1,0,0x20},
-    {+0,-1,+2,+0,0,0x30}, {+0,-1,+2,+1,1,0x10}, {+0,+0,+0,+2,1,0x08},
-    {+0,+0,+2,-2,1,0x40}, {+0,+0,+2,-1,0,0x60}, {+0,+0,+2,+0,1,0x20},
-    {+0,+0,+2,+1,0,0x30}, {+0,+0,+2,+2,1,0x10}, {+0,+1,+1,+0,0,0x44},
-    {+0,+1,+1,+2,0,0x10}, {+0,+1,+2,-1,1,0x40}, {+0,+1,+2,+0,0,0x60},
-    {+0,+1,+2,+1,0,0x20}, {+0,+1,+2,+2,0,0x10}, {+1,-2,+1,+0,0,0x80},
-    {+1,-1,+1,+1,0,0x88}, {+1,+0,+1,+2,0,0x08}, {+1,+0,+2,-1,0,0x40},
-    {+1,+0,+2,+1,0,0x10}
-  };
-  const interpolate_terms *cpt;
-  signed char *cp;
-  signed char chood[] = { -1,-1, -1,0, -1,+1, 0,+1, +1,+1, +1,0, +1,-1, 0,-1 };
-  ushort (*brow[5])[4], *pix;
-  int prow=7, pcol=1, *ip, *code[16][16], gval[8], gmin, gmax, sum[4];
-  int row, col, x, y, x1, x2, y1, y2, t, weight, grads, color, diag;
-  int g, diff, thold, num, c;
-  lin_interpolate();
-#ifdef DCRAW_VERBOSE
-  if (verbose) fprintf (stderr,_("VNG interpolation...\n"));
-#endif
-
-  if (filters == 1) prow = pcol = 15;
-  ip = (int *) calloc ((prow+1)*(pcol+1), 1280);
-  merror (ip, "vng_interpolate()");
-  for (row=0; row <= prow; row++)		/* Precalculate for VNG */
-    for (col=0; col <= pcol; col++) {
-      code[row][col] = ip;
-      for (cpt=&terms[0], t=0; t < 64, cpt = &terms[t]; t++) {
-	y1 = cpt->y1;  x1 = cpt->x1;
-	y2 = cpt->y2;  x2 = cpt->x2;
-	weight = cpt->weight;
-	grads = cpt->grads;
-	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] = (ushort (*)[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 */
-#ifdef LIBRAW_LIBRARY_BUILD
-      if(!((row-2)%256))RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE,(row-2)/256+1,((height-3)/256)+1);
-#endif
-    for (col=2; col < width-2; col++) {
-      pix = image[row*width+col];
-      ip = code[row & prow][col & pcol];
-      memset (gval, 0, sizeof gval);
-      while ((g = ip[0]) != INT_MAX) {		/* Calculate gradients */
-	diff = ABS(pix[g] - pix[ip[1]]) << ip[2];
-	gval[ip[3]] += diff;
-	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) {
-	  FORCC
-	    if (c == color && ip[1])
-	      sum[c] += (pix[c] + pix[ip[1]]) >> 1;
-	    else
-	      sum[c] += pix[ip[0] + c];
-	  num++;
-	}
-      }
-      FORCC {					/* Save to buffer */
-	t = pix[color];
-	if (c != color)
-	  t += (sum[c] - sum[color]) / num;
-	brow[2][col][c] = CLIP(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]);
-  free (code[0][0]);
-}
-
-/*
-   Patterned Pixel Grouping Interpolation by Alain Desbiolles
 */
-void CLASS ppg_interpolate()
-{
-  int dir[5] = { 1, width, -1, -width, 1 };
-  int row, col, diff[2], guess[2], c, d, i;
-  ushort (*pix)[4];
-
-  border_interpolate(3);
-#ifdef DCRAW_VERBOSE
-  if (verbose) fprintf (stderr,_("PPG interpolation...\n"));
-#endif
-
-/*  Fill in the green layer with gradients and pattern recognition: */
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE,0,3);
-#endif
-  for (row=3; row < height-3; row++)
-    for (col=3+(FC(row,3) & 1), c=FC(row,col); col < width-3; col+=2) {
-      pix = image + row*width+col;
-      for (i=0; (d=dir[i]) > 0; i++) {
-	guess[i] = (pix[-d][1] + pix[0][c] + pix[d][1]) * 2
-		      - pix[-2*d][c] - pix[2*d][c];
-	diff[i] = ( ABS(pix[-2*d][c] - pix[ 0][c]) +
-		    ABS(pix[ 2*d][c] - pix[ 0][c]) +
-		    ABS(pix[  -d][1] - pix[ d][1]) ) * 3 +
-		  ( ABS(pix[ 3*d][1] - pix[ d][1]) +
-		    ABS(pix[-3*d][1] - pix[-d][1]) ) * 2;
-      }
-      d = dir[i = diff[0] > diff[1]];
-      pix[0][1] = ULIM(guess[i] >> 2, pix[d][1], pix[-d][1]);
-    }
-/*  Calculate red and blue for each green pixel:		*/
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE,1,3);
-#endif
-  for (row=1; row < height-1; row++)
-    for (col=1+(FC(row,2) & 1), c=FC(row,col+1); col < width-1; col+=2) {
-      pix = image + row*width+col;
-      for (i=0; (d=dir[i]) > 0; c=2-c, i++)
-	pix[0][c] = CLIP((pix[-d][c] + pix[d][c] + 2*pix[0][1]
-			- pix[-d][1] - pix[d][1]) >> 1);
-    }
-/*  Calculate blue for red pixels and vice versa:		*/
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE,2,3);
-#endif
-  for (row=1; row < height-1; row++)
-    for (col=1+(FC(row,1) & 1), c=2-FC(row,col); col < width-1; col+=2) {
-      pix = image + row*width+col;
-      for (i=0; (d=dir[i]+dir[i+1]) > 0; i++) {
-	diff[i] = ABS(pix[-d][c] - pix[d][c]) +
-		  ABS(pix[-d][1] - pix[0][1]) +
-		  ABS(pix[ d][1] - pix[0][1]);
-	guess[i] = pix[-d][c] + pix[d][c] + 2*pix[0][1]
-		 - pix[-d][1] - pix[d][1];
-      }
-      if (diff[0] != diff[1])
-	pix[0][c] = CLIP(guess[diff[0] > diff[1]] >> 1);
-      else
-	pix[0][c] = CLIP((guess[0]+guess[1]) >> 2);
-    }
-}
-
-/*
-   Adaptive Homogeneity-Directed interpolation is based on
-   the work of Keigo Hirakawa, Thomas Parks, and Paul Lee.
- */
-#define TS 256		/* Tile Size */
-#ifndef _OPENMP
-void CLASS ahd_interpolate()
-{
-  int i, j, k, top, left, row, col, tr, tc, c, d, val, hm[2];
-  ushort (*pix)[4], (*rix)[3];
-  static const int dir[4] = { -1, 1, -TS, TS };
-  unsigned ldiff[2][4], abdiff[2][4], leps, abeps;
-  float r, cbrt[0x10000], xyz[3], xyz_cam[3][4];
-  ushort (*rgb)[TS][TS][3];
-   short (*lab)[TS][TS][3], (*lix)[3];
-   char (*homo)[TS][TS], *buffer;
-
-#ifdef DCRAW_VERBOSE
-  if (verbose) fprintf (stderr,_("AHD interpolation...\n"));
-#endif
-
-  for (i=0; i < 0x10000; i++) {
-    r = i / 65535.0;
-    cbrt[i] = r > 0.008856 ? pow((double)r,1/3.0) : 7.787*r + 16/116.0;
-  }
-  for (i=0; i < 3; i++)
-    for (j=0; j < colors; j++)
-      for (xyz_cam[i][j] = k=0; k < 3; k++)
-	xyz_cam[i][j] += xyz_rgb[i][k] * rgb_cam[k][j] / d65_white[i];
-
-  border_interpolate(5);
-  buffer = (char *) malloc (26*TS*TS);		/* 1664 kB */
-  merror (buffer, "ahd_interpolate()");
-  rgb  = (ushort(*)[TS][TS][3]) buffer;
-  lab  = (short (*)[TS][TS][3])(buffer + 12*TS*TS);
-  homo = (char  (*)[TS][TS])   (buffer + 24*TS*TS);
-
-  for (top=2; top < height-5; top += TS-6)
-  {    
-      
-#ifdef LIBRAW_LIBRARY_BUILD
-      RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE,(top-2)/(TS-6),(height-7)/(TS-6)+1);
-#endif
-    for (left=2; left < width-5; left += TS-6) {
-
-/*  Interpolate green horizontally and vertically:		*/
-      for (row = top; row < top+TS && row < height-2; row++) {
-	col = left + (FC(row,left) & 1);
-	for (c = FC(row,col); col < left+TS && col < width-2; col+=2) {
-	  pix = image + row*width+col;
-	  val = ((pix[-1][1] + pix[0][c] + pix[1][1]) * 2
-		- pix[-2][c] - pix[2][c]) >> 2;
-	  rgb[0][row-top][col-left][1] = ULIM(val,pix[-1][1],pix[1][1]);
-	  val = ((pix[-width][1] + pix[0][c] + pix[width][1]) * 2
-		- pix[-2*width][c] - pix[2*width][c]) >> 2;
-	  rgb[1][row-top][col-left][1] = ULIM(val,pix[-width][1],pix[width][1]);
-	}
-      }
-/*  Interpolate red and blue, and convert to CIELab:		*/
-      for (d=0; d < 2; d++)
-	for (row=top+1; row < top+TS-1 && row < height-3; row++)
-	  for (col=left+1; col < left+TS-1 && col < width-3; col++) {
-	    pix = image + row*width+col;
-	    rix = &rgb[d][row-top][col-left];
-	    lix = &lab[d][row-top][col-left];
-	    if ((c = 2 - FC(row,col)) == 1) {
-	      c = FC(row+1,col);
-	      val = pix[0][1] + (( pix[-1][2-c] + pix[1][2-c]
-				 - rix[-1][1] - rix[1][1] ) >> 1);
-	      rix[0][2-c] = CLIP(val);
-	      val = pix[0][1] + (( pix[-width][c] + pix[width][c]
-				 - rix[-TS][1] - rix[TS][1] ) >> 1);
-	    } else
-	      val = rix[0][1] + (( pix[-width-1][c] + pix[-width+1][c]
-				 + pix[+width-1][c] + pix[+width+1][c]
-				 - rix[-TS-1][1] - rix[-TS+1][1]
-				 - rix[+TS-1][1] - rix[+TS+1][1] + 1) >> 2);
-	    rix[0][c] = CLIP(val);
-	    c = FC(row,col);
-	    rix[0][c] = pix[0][c];
-	    xyz[0] = xyz[1] = xyz[2] = 0.5;
-	    FORCC {
-	      xyz[0] += xyz_cam[0][c] * rix[0][c];
-	      xyz[1] += xyz_cam[1][c] * rix[0][c];
-	      xyz[2] += xyz_cam[2][c] * rix[0][c];
-	    }
-	    xyz[0] = cbrt[CLIP((int) xyz[0])];
-	    xyz[1] = cbrt[CLIP((int) xyz[1])];
-	    xyz[2] = cbrt[CLIP((int) xyz[2])];
-	    lix[0][0] = 64 * (116 * xyz[1] - 16);
-	    lix[0][1] = 64 * 500 * (xyz[0] - xyz[1]);
-	    lix[0][2] = 64 * 200 * (xyz[1] - xyz[2]);
-	  }
-/*  Build homogeneity maps from the CIELab images:		*/
-      memset (homo, 0, 2*TS*TS);
-      for (row=top+2; row < top+TS-2 && row < height-4; row++) {
-	tr = row-top;
-	for (col=left+2; col < left+TS-2 && col < width-4; col++) {
-	  tc = col-left;
-	  for (d=0; d < 2; d++) {
-	    lix = &lab[d][tr][tc];
-	    for (i=0; i < 4; i++) {
-	       ldiff[d][i] = ABS(lix[0][0]-lix[dir[i]][0]);
-	      abdiff[d][i] = SQR(lix[0][1]-lix[dir[i]][1])
-			   + SQR(lix[0][2]-lix[dir[i]][2]);
-	    }
-	  }
-	  leps = MIN(MAX(ldiff[0][0],ldiff[0][1]),
-		     MAX(ldiff[1][2],ldiff[1][3]));
-	  abeps = MIN(MAX(abdiff[0][0],abdiff[0][1]),
-		      MAX(abdiff[1][2],abdiff[1][3]));
-	  for (d=0; d < 2; d++)
-	    for (i=0; i < 4; i++)
-	      if (ldiff[d][i] <= leps && abdiff[d][i] <= abeps)
-		homo[d][tr][tc]++;
-	}
-      }
-/*  Combine the most homogenous pixels for the final result:	*/
-      for (row=top+3; row < top+TS-3 && row < height-5; row++) {
-	tr = row-top;
-	for (col=left+3; col < left+TS-3 && col < width-5; col++) {
-	  tc = col-left;
-	  for (d=0; d < 2; d++)
-	    for (hm[d]=0, i=tr-1; i <= tr+1; i++)
-	      for (j=tc-1; j <= tc+1; j++)
-		hm[d] += homo[d][i][j];
-	  if (hm[0] != hm[1])
-	    FORC3 image[row*width+col][c] = rgb[hm[1] > hm[0]][tr][tc][c];
-	  else
-	    FORC3 image[row*width+col][c] =
-		(rgb[0][tr][tc][c] + rgb[1][tr][tc][c]) >> 1;
-	}
-      }
-    }
-  }
-  free (buffer);
-}
-#else
-void CLASS ahd_interpolate()
-{
-  int i, j, k, top, left, row, col, tr, tc, c, d, val, hm[2];
-  ushort (*pix)[4], (*rix)[3];
-  static const int dir[4] = { -1, 1, -TS, TS };
-  unsigned ldiff[2][4], abdiff[2][4], leps, abeps;
-  float r, cbrt[0x10000], xyz[3], xyz_cam[3][4];
-  ushort (*rgb)[TS][TS][3];
-  short (*lab)[TS][TS][3], (*lix)[3];
-  char (*homo)[TS][TS], *buffer;
-
-#ifdef DCRAW_VERBOSE
-  if (verbose) fprintf (stderr,_("AHD interpolation...\n"));
-#endif
-
-  for (i=0; i < 0x10000; i++) {
-    r = i / 65535.0;
-    cbrt[i] = r > 0.008856 ? pow(r,1/3.0) : 7.787*r + 16/116.0;
-  }
-  for (i=0; i < 3; i++)
-    for (j=0; j < colors; j++)
-      for (xyz_cam[i][j] = k=0; k < 3; k++)
-	xyz_cam[i][j] += xyz_rgb[i][k] * rgb_cam[k][j] / d65_white[i];
-
-  border_interpolate(5);
-
-#ifdef LIBRAW_LIBRARY_BUILD
-#pragma omp parallel private(buffer,rgb,lab,homo,top,left,row,c,col,pix,val,d,rix,xyz,lix,tc,tr,ldiff,abdiff,leps,abeps,hm,i,j) firstprivate(cbrt) shared(xyz_cam)
-#endif
-  {
-    buffer = (char *) malloc (26*TS*TS);		/* 1664 kB */
-    merror (buffer, "ahd_interpolate()");
-    rgb  = (ushort(*)[TS][TS][3]) buffer;
-    lab  = (short (*)[TS][TS][3])(buffer + 12*TS*TS);
-    homo = (char  (*)[TS][TS])   (buffer + 24*TS*TS);
-
-#pragma omp for schedule(dynamic)
-    for (top=2; top < height-5; top += TS-6){
-#ifdef LIBRAW_LIBRARY_BUILD
-        RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE,(top-2)/(TS-6)+1,(height-7)/(TS-6)+1);
-#endif
-      for (left=2; left < width-5; left += TS-6) {
-
-  /*  Interpolate green horizontally and vertically:		*/
-        for (row = top; row < top+TS && row < height-2; row++) {
-          col = left + (FC(row,left) & 1);
-          for (c = FC(row,col); col < left+TS && col < width-2; col+=2) {
-            pix = image + row*width+col;
-            val = ((pix[-1][1] + pix[0][c] + pix[1][1]) * 2
-                  - pix[-2][c] - pix[2][c]) >> 2;
-            rgb[0][row-top][col-left][1] = ULIM(val,pix[-1][1],pix[1][1]);
-            val = ((pix[-width][1] + pix[0][c] + pix[width][1]) * 2
-                  - pix[-2*width][c] - pix[2*width][c]) >> 2;
-            rgb[1][row-top][col-left][1] = ULIM(val,pix[-width][1],pix[width][1]);
-          }
-        }
-  /*  Interpolate red and blue, and convert to CIELab:		*/
-        for (d=0; d < 2; d++)
-          for (row=top+1; row < top+TS-1 && row < height-3; row++)
-            for (col=left+1; col < left+TS-1 && col < width-3; col++) {
-              pix = image + row*width+col;
-              rix = &rgb[d][row-top][col-left];
-              lix = &lab[d][row-top][col-left];
-              if ((c = 2 - FC(row,col)) == 1) {
-                c = FC(row+1,col);
-                val = pix[0][1] + (( pix[-1][2-c] + pix[1][2-c]
-                                   - rix[-1][1] - rix[1][1] ) >> 1);
-                rix[0][2-c] = CLIP(val);
-                val = pix[0][1] + (( pix[-width][c] + pix[width][c]
-                                   - rix[-TS][1] - rix[TS][1] ) >> 1);
-              } else
-                val = rix[0][1] + (( pix[-width-1][c] + pix[-width+1][c]
-                                   + pix[+width-1][c] + pix[+width+1][c]
-                                   - rix[-TS-1][1] - rix[-TS+1][1]
-                                   - rix[+TS-1][1] - rix[+TS+1][1] + 1) >> 2);
-              rix[0][c] = CLIP(val);
-              c = FC(row,col);
-              rix[0][c] = pix[0][c];
-              xyz[0] = xyz[1] = xyz[2] = 0.5;
-              FORCC {
-                xyz[0] += xyz_cam[0][c] * rix[0][c];
-                xyz[1] += xyz_cam[1][c] * rix[0][c];
-                xyz[2] += xyz_cam[2][c] * rix[0][c];
-              }
-              xyz[0] = cbrt[CLIP((int) xyz[0])];
-              xyz[1] = cbrt[CLIP((int) xyz[1])];
-              xyz[2] = cbrt[CLIP((int) xyz[2])];
-              lix[0][0] = 64 * (116 * xyz[1] - 16);
-              lix[0][1] = 64 * 500 * (xyz[0] - xyz[1]);
-              lix[0][2] = 64 * 200 * (xyz[1] - xyz[2]);
-            }
-  /*  Build homogeneity maps from the CIELab images:		*/
-        memset (homo, 0, 2*TS*TS);
-        for (row=top+2; row < top+TS-2 && row < height-4; row++) {
-          tr = row-top;
-          for (col=left+2; col < left+TS-2 && col < width-4; col++) {
-            tc = col-left;
-            for (d=0; d < 2; d++) {
-              lix = &lab[d][tr][tc];
-              for (i=0; i < 4; i++) {
-                 ldiff[d][i] = ABS(lix[0][0]-lix[dir[i]][0]);
-                abdiff[d][i] = SQR(lix[0][1]-lix[dir[i]][1])
-                             + SQR(lix[0][2]-lix[dir[i]][2]);
-              }
-            }
-            leps = MIN(MAX(ldiff[0][0],ldiff[0][1]),
-                       MAX(ldiff[1][2],ldiff[1][3]));
-            abeps = MIN(MAX(abdiff[0][0],abdiff[0][1]),
-                        MAX(abdiff[1][2],abdiff[1][3]));
-            for (d=0; d < 2; d++)
-              for (i=0; i < 4; i++)
-                if (ldiff[d][i] <= leps && abdiff[d][i] <= abeps)
-                  homo[d][tr][tc]++;
-          }
-        }
-  /*  Combine the most homogenous pixels for the final result:	*/
-        for (row=top+3; row < top+TS-3 && row < height-5; row++) {
-          tr = row-top;
-          for (col=left+3; col < left+TS-3 && col < width-5; col++) {
-            tc = col-left;
-            for (d=0; d < 2; d++)
-              for (hm[d]=0, i=tr-1; i <= tr+1; i++)
-                for (j=tc-1; j <= tc+1; j++)
-                  hm[d] += homo[d][i][j];
-            if (hm[0] != hm[1])
-              FORC3 image[row*width+col][c] = rgb[hm[1] > hm[0]][tr][tc][c];
-            else
-              FORC3 image[row*width+col][c] =
-                  (rgb[0][tr][tc][c] + rgb[1][tr][tc][c]) >> 1;
-          }
-        }
-      }
-    }
-    free (buffer);
-  }
-}
-
-#endif
-#undef TS
-
-void CLASS median_filter()
-{
-  ushort (*pix)[4];
-  int pass, c, i, j, k, med[9];
-  static const uchar opt[] =	/* Optimal 9-element median search */
-  { 1,2, 4,5, 7,8, 0,1, 3,4, 6,7, 1,2, 4,5, 7,8,
-    0,3, 5,8, 4,7, 3,6, 1,4, 2,5, 4,7, 4,2, 6,4, 4,2 };
-
-  for (pass=1; pass <= med_passes; pass++) {
-#ifdef LIBRAW_LIBRARY_BUILD
-      RUN_CALLBACK(LIBRAW_PROGRESS_MEDIAN_FILTER,pass-1,med_passes);
-#endif
-#ifdef DCRAW_VERBOSE
-    if (verbose)
-      fprintf (stderr,_("Median filter pass %d...\n"), pass);
-#endif
-    for (c=0; c < 3; c+=2) {
-      for (pix = image; pix < image+width*height; pix++)
-	pix[0][3] = pix[0][c];
-      for (pix = image+width; pix < image+width*(height-1); pix++) {
-	if ((pix-image+1) % width < 2) continue;
-	for (k=0, i = -width; i <= width; i += width)
-	  for (j = i-1; j <= i+1; j++)
-	    med[k++] = pix[j][3] - pix[j][1];
-	for (i=0; i < sizeof opt; i+=2)
-	  if     (med[opt[i]] > med[opt[i+1]])
-	    SWAP (med[opt[i]] , med[opt[i+1]]);
-	pix[0][c] = CLIP(med[4] + pix[0][1]);
-      }
-    }
-  }
-}
-
-void CLASS blend_highlights()
-{
-  int clip=INT_MAX, row, col, c, i, j;
-  static const float trans[2][4][4] =
-  { { { 1,1,1 }, { 1.7320508,-1.7320508,0 }, { -1,-1,2 } },
-    { { 1,1,1,1 }, { 1,-1,1,-1 }, { 1,1,-1,-1 }, { 1,-1,-1,1 } } };
-  static const float itrans[2][4][4] =
-  { { { 1,0.8660254,-0.5 }, { 1,-0.8660254,-0.5 }, { 1,0,1 } },
-    { { 1,1,1,1 }, { 1,-1,1,-1 }, { 1,1,-1,-1 }, { 1,-1,-1,1 } } };
-  float cam[2][4], lab[2][4], sum[2], chratio;
-
-  if ((unsigned) (colors-3) > 1) return;
-#ifdef DCRAW_VERBOSE
-  if (verbose) fprintf (stderr,_("Blending highlights...\n"));
-#endif
-  FORCC if (clip > (i = 65535*pre_mul[c])) clip = i;
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_HIGHLIGHTS,0,2);
-#endif
-  for (row=0; row < height; row++)
-    for (col=0; col < width; col++) {
-      FORCC if (image[row*width+col][c] > clip) break;
-      if (c == colors) continue;
-      FORCC {
-	cam[0][c] = image[row*width+col][c];
-	cam[1][c] = MIN(cam[0][c],clip);
-      }
-      for (i=0; i < 2; i++) {
-	FORCC for (lab[i][c]=j=0; j < colors; j++)
-	  lab[i][c] += trans[colors-3][c][j] * cam[i][j];
-	for (sum[i]=0,c=1; c < colors; c++)
-	  sum[i] += SQR(lab[i][c]);
-      }
-      chratio = sqrt(sum[1]/sum[0]);
-      for (c=1; c < colors; c++)
-	lab[0][c] *= chratio;
-      FORCC for (cam[0][c]=j=0; j < colors; j++)
-	cam[0][c] += itrans[colors-3][c][j] * lab[0][j];
-      FORCC image[row*width+col][c] = cam[0][c] / colors;
-    }
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_HIGHLIGHTS,1,2);
-#endif
-}
-
-#define SCALE (4 >> shrink)
-void CLASS recover_highlights()
-{
-  float *map, sum, wgt, grow;
-  int hsat[4], count, spread, change, val, i;
-  unsigned high, wide, mrow, mcol, row, col, kc, c, d, y, x;
-  ushort *pixel;
-  static const signed char dir[8][2] =
-    { {-1,-1}, {-1,0}, {-1,1}, {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1} };
-
-#ifdef DCRAW_VERBOSE
-  if (verbose) fprintf (stderr,_("Rebuilding highlights...\n"));
-#endif
-
-  grow = pow (2.0, 4.0-highlight);
-  FORCC hsat[c] = 32000 * pre_mul[c];
-  for (kc=0, c=1; c < colors; c++)
-    if (pre_mul[kc] < pre_mul[c]) kc = c;
-  high = height / SCALE;
-  wide =  width / SCALE;
-  map = (float *) calloc (high*wide, sizeof *map);
-  merror (map, "recover_highlights()");
-  FORCC if (c != kc) {
-#ifdef LIBRAW_LIBRARY_BUILD
-      RUN_CALLBACK(LIBRAW_PROGRESS_HIGHLIGHTS,c-1,colors-1);
-#endif
-    memset (map, 0, high*wide*sizeof *map);
-    for (mrow=0; mrow < high; mrow++)
-      for (mcol=0; mcol < wide; mcol++) {
-	sum = wgt = count = 0;
-	for (row = mrow*SCALE; row < (mrow+1)*SCALE; row++)
-	  for (col = mcol*SCALE; col < (mcol+1)*SCALE; col++) {
-	    pixel = image[row*width+col];
-	    if (pixel[c] / hsat[c] == 1 && pixel[kc] > 24000) {
-	      sum += pixel[c];
-	      wgt += pixel[kc];
-	      count++;
-	    }
-	  }
-	if (count == SCALE*SCALE)
-	  map[mrow*wide+mcol] = sum / wgt;
-      }
-    for (spread = 32/grow; spread--; ) {
-      for (mrow=0; mrow < high; mrow++)
-	for (mcol=0; mcol < wide; mcol++) {
-	  if (map[mrow*wide+mcol]) continue;
-	  sum = count = 0;
-	  for (d=0; d < 8; d++) {
-	    y = mrow + dir[d][0];
-	    x = mcol + dir[d][1];
-	    if (y < high && x < wide && map[y*wide+x] > 0) {
-	      sum  += (1 + (d & 1)) * map[y*wide+x];
-	      count += 1 + (d & 1);
-	    }
-	  }
-	  if (count > 3)
-	    map[mrow*wide+mcol] = - (sum+grow) / (count+grow);
-	}
-      for (change=i=0; i < high*wide; i++)
-	if (map[i] < 0) {
-	  map[i] = -map[i];
-	  change = 1;
-	}
-      if (!change) break;
-    }
-    for (i=0; i < high*wide; i++)
-      if (map[i] == 0) map[i] = 1;
-    for (mrow=0; mrow < high; mrow++)
-      for (mcol=0; mcol < wide; mcol++) {
-	for (row = mrow*SCALE; row < (mrow+1)*SCALE; row++)
-	  for (col = mcol*SCALE; col < (mcol+1)*SCALE; col++) {
-	    pixel = image[row*width+col];
-	    if (pixel[c] / hsat[c] > 1) {
-	      val = pixel[kc] * map[mrow*wide+mcol];
-	      if (pixel[c] < val) pixel[c] = CLIP(val);
-	    }
-	  }
-      }
-  }
-  free (map);
-}
-#undef SCALE
-
-void CLASS tiff_get (unsigned base,
-	unsigned *tag, unsigned *type, unsigned *len, unsigned *save)
-{
-  *tag  = get2();
-  *type = get2();
-  *len  = get4();
-  *save = ftell(ifp) + 4;
-  if (*len * ("11124811248488"[*type < 14 ? *type:0]-'0') > 4)
-    fseek (ifp, get4()+base, SEEK_SET);
-}
-
-void CLASS parse_thumb_note (int base, unsigned toff, unsigned tlen)
-{
-  unsigned entries, tag, type, len, save;
-
-  entries = get2();
-  while (entries--) {
-    tiff_get (base, &tag, &type, &len, &save);
-    if (tag == toff) thumb_offset = get4()+base;
-    if (tag == tlen) thumb_length = get4();
-    fseek (ifp, save, SEEK_SET);
-  }
-}
-
-void CLASS parse_makernote (int base, int uptag)
-{
-  static const uchar xlat[2][256] = {
-  { 0xc1,0xbf,0x6d,0x0d,0x59,0xc5,0x13,0x9d,0x83,0x61,0x6b,0x4f,0xc7,0x7f,0x3d,0x3d,
-    0x53,0x59,0xe3,0xc7,0xe9,0x2f,0x95,0xa7,0x95,0x1f,0xdf,0x7f,0x2b,0x29,0xc7,0x0d,
-    0xdf,0x07,0xef,0x71,0x89,0x3d,0x13,0x3d,0x3b,0x13,0xfb,0x0d,0x89,0xc1,0x65,0x1f,
-    0xb3,0x0d,0x6b,0x29,0xe3,0xfb,0xef,0xa3,0x6b,0x47,0x7f,0x95,0x35,0xa7,0x47,0x4f,
-    0xc7,0xf1,0x59,0x95,0x35,0x11,0x29,0x61,0xf1,0x3d,0xb3,0x2b,0x0d,0x43,0x89,0xc1,
-    0x9d,0x9d,0x89,0x65,0xf1,0xe9,0xdf,0xbf,0x3d,0x7f,0x53,0x97,0xe5,0xe9,0x95,0x17,
-    0x1d,0x3d,0x8b,0xfb,0xc7,0xe3,0x67,0xa7,0x07,0xf1,0x71,0xa7,0x53,0xb5,0x29,0x89,
-    0xe5,0x2b,0xa7,0x17,0x29,0xe9,0x4f,0xc5,0x65,0x6d,0x6b,0xef,0x0d,0x89,0x49,0x2f,
-    0xb3,0x43,0x53,0x65,0x1d,0x49,0xa3,0x13,0x89,0x59,0xef,0x6b,0xef,0x65,0x1d,0x0b,
-    0x59,0x13,0xe3,0x4f,0x9d,0xb3,0x29,0x43,0x2b,0x07,0x1d,0x95,0x59,0x59,0x47,0xfb,
-    0xe5,0xe9,0x61,0x47,0x2f,0x35,0x7f,0x17,0x7f,0xef,0x7f,0x95,0x95,0x71,0xd3,0xa3,
-    0x0b,0x71,0xa3,0xad,0x0b,0x3b,0xb5,0xfb,0xa3,0xbf,0x4f,0x83,0x1d,0xad,0xe9,0x2f,
-    0x71,0x65,0xa3,0xe5,0x07,0x35,0x3d,0x0d,0xb5,0xe9,0xe5,0x47,0x3b,0x9d,0xef,0x35,
-    0xa3,0xbf,0xb3,0xdf,0x53,0xd3,0x97,0x53,0x49,0x71,0x07,0x35,0x61,0x71,0x2f,0x43,
-    0x2f,0x11,0xdf,0x17,0x97,0xfb,0x95,0x3b,0x7f,0x6b,0xd3,0x25,0xbf,0xad,0xc7,0xc5,
-    0xc5,0xb5,0x8b,0xef,0x2f,0xd3,0x07,0x6b,0x25,0x49,0x95,0x25,0x49,0x6d,0x71,0xc7 },
-  { 0xa7,0xbc,0xc9,0xad,0x91,0xdf,0x85,0xe5,0xd4,0x78,0xd5,0x17,0x46,0x7c,0x29,0x4c,
-    0x4d,0x03,0xe9,0x25,0x68,0x11,0x86,0xb3,0xbd,0xf7,0x6f,0x61,0x22,0xa2,0x26,0x34,
-    0x2a,0xbe,0x1e,0x46,0x14,0x68,0x9d,0x44,0x18,0xc2,0x40,0xf4,0x7e,0x5f,0x1b,0xad,
-    0x0b,0x94,0xb6,0x67,0xb4,0x0b,0xe1,0xea,0x95,0x9c,0x66,0xdc,0xe7,0x5d,0x6c,0x05,
-    0xda,0xd5,0xdf,0x7a,0xef,0xf6,0xdb,0x1f,0x82,0x4c,0xc0,0x68,0x47,0xa1,0xbd,0xee,
-    0x39,0x50,0x56,0x4a,0xdd,0xdf,0xa5,0xf8,0xc6,0xda,0xca,0x90,0xca,0x01,0x42,0x9d,
-    0x8b,0x0c,0x73,0x43,0x75,0x05,0x94,0xde,0x24,0xb3,0x80,0x34,0xe5,0x2c,0xdc,0x9b,
-    0x3f,0xca,0x33,0x45,0xd0,0xdb,0x5f,0xf5,0x52,0xc3,0x21,0xda,0xe2,0x22,0x72,0x6b,
-    0x3e,0xd0,0x5b,0xa8,0x87,0x8c,0x06,0x5d,0x0f,0xdd,0x09,0x19,0x93,0xd0,0xb9,0xfc,
-    0x8b,0x0f,0x84,0x60,0x33,0x1c,0x9b,0x45,0xf1,0xf0,0xa3,0x94,0x3a,0x12,0x77,0x33,
-    0x4d,0x44,0x78,0x28,0x3c,0x9e,0xfd,0x65,0x57,0x16,0x94,0x6b,0xfb,0x59,0xd0,0xc8,
-    0x22,0x36,0xdb,0xd2,0x63,0x98,0x43,0xa1,0x04,0x87,0x86,0xf7,0xa6,0x26,0xbb,0xd6,
-    0x59,0x4d,0xbf,0x6a,0x2e,0xaa,0x2b,0xef,0xe6,0x78,0xb6,0x4e,0xe0,0x2f,0xdc,0x7c,
-    0xbe,0x57,0x19,0x32,0x7e,0x2a,0xd0,0xb8,0xba,0x29,0x00,0x3c,0x52,0x7d,0xa8,0x49,
-    0x3b,0x2d,0xeb,0x25,0x49,0xfa,0xa3,0xaa,0x39,0xa7,0xc5,0xa7,0x50,0x11,0x36,0xfb,
-    0xc6,0x67,0x4a,0xf5,0xa5,0x12,0x65,0x7e,0xb0,0xdf,0xaf,0x4e,0xb3,0x61,0x7f,0x2f } };
-  unsigned offset=0, entries, tag, type, len, save, c;
-  unsigned ver97=0, serial=0, i, wbi=0, wb[4]={0,0,0,0};
-  uchar buf97[324], ci, cj, ck;
-  short sorder=order;
-  char buf[10];
-/*
-   The MakerNote might have its own TIFF header (possibly with
-   its own byte-order!), or it might just be a table.
- */
-  fread (buf, 1, 10, ifp);
-  if (!strncmp (buf,"KDK" ,3) ||	/* these aren't TIFF tables */
-      !strncmp (buf,"VER" ,3) ||
-      !strncmp (buf,"IIII",4) ||
-      !strncmp (buf,"MMMM",4)) return;
-  if (!strncmp (buf,"KC"  ,2) ||	/* Konica KD-400Z, KD-510Z */
-      !strncmp (buf,"MLY" ,3)) {	/* Minolta DiMAGE G series */
-    order = 0x4d4d;
-    while ((i=ftell(ifp)) < data_offset && i < 16384) {
-      wb[0] = wb[2];  wb[2] = wb[1];  wb[1] = wb[3];
-      wb[3] = get2();
-      if (wb[1] == 256 && wb[3] == 256 &&
-	  wb[0] > 256 && wb[0] < 640 && wb[2] > 256 && wb[2] < 640)
-	FORC4 cam_mul[c] = wb[c];
-#ifdef LIBRAW_LIBRARY_BUILD
-      color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-    }
-    goto quit;
-  }
-  if (!strcmp (buf,"Nikon")) {
-    base = ftell(ifp);
-    order = get2();
-    if (get2() != 42) goto quit;
-    offset = get4();
-    fseek (ifp, offset-8, SEEK_CUR);
-  } else if (!strcmp (buf,"OLYMPUS")) {
-    base = ftell(ifp)-10;
-    fseek (ifp, -2, SEEK_CUR);
-    order = get2();  get2();
-  } else if (!strncmp (buf,"FUJIFILM",8) ||
-	     !strncmp (buf,"SONY",4) ||
-	     !strcmp  (buf,"Panasonic")) {
-    order = 0x4949;
-    fseek (ifp,  2, SEEK_CUR);
-  } else if (!strcmp (buf,"OLYMP") ||
-	     !strcmp (buf,"LEICA") ||
-	     !strcmp (buf,"Ricoh") ||
-	     !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();
-  if (entries > 1000) return;
-  while (entries--) {
-    tiff_get (base, &tag, &type, &len, &save);
-    tag |= uptag << 16;
-    if (tag == 2 && strstr(make,"NIKON"))
-      iso_speed = (get2(),get2());
-    if (tag == 4 && len > 26 && len < 35) {
-      if ((i=(get4(),get2())) != 0x7fff && !iso_speed)
-	iso_speed = 50 * pow (2, i/32.0 - 4);
-      if ((i=(get2(),get2())) != 0x7fff && !aperture)
-	aperture = pow (2, i/64.0);
-      if ((i=get2()) != 0xffff && !shutter)
-	shutter = pow (2, (short) i/-32.0);
-      wbi = (get2(),get2());
-      shot_order = (get2(),get2());
-    }
-    if (tag == 7 && type == 2 && len > 20)
-      fgets (model2, 64, ifp);
-    if (tag == 8 && type == 4)
-      shot_order = get4();
-    if (tag == 9 && !strcmp(make,"Canon"))
-      fread (artist, 64, 1, ifp);
-    if (tag == 0xc && len == 4) {
-      cam_mul[0] = getreal(type);
-      cam_mul[2] = getreal(type);
-#ifdef LIBRAW_LIBRARY_BUILD
-      color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-    }
-    if (tag == 0x10 && type == 4)
-      unique_id = get4();
-    if (tag == 0x11 && is_raw && !strncmp(make,"NIKON",5)) {
-      fseek (ifp, get4()+base, SEEK_SET);
-      parse_tiff_ifd (base);
-    }
-    if (tag == 0x14 && len == 2560 && type == 7) {
-      fseek (ifp, 1248, SEEK_CUR);
-      goto get2_256;
-    }
-    if (tag == 0x15 && type == 2 && is_raw)
-      fread (model, 64, 1, ifp);
-    if (strstr(make,"PENTAX")) {
-      if (tag == 0x1b) tag = 0x1018;
-      if (tag == 0x1c) tag = 0x1017;
-    }
-    if (tag == 0x1d)
-      while ((c = fgetc(ifp)) && c != EOF)
-	serial = serial*10 + (isdigit(c) ? c - '0' : c % 10);
-    if (tag == 0x81 && type == 4) {
-      data_offset = get4();
-      fseek (ifp, data_offset + 41, SEEK_SET);
-      raw_height = get2() * 2;
-      raw_width  = get2();
-      filters = 0x61616161;
-    }
-    if (tag == 0x29 && type == 1) {
-      c = wbi < 18 ? "012347800000005896"[wbi]-'0' : 0;
-      fseek (ifp, 8 + c*32, SEEK_CUR);
-      FORC4 cam_mul[c ^ (c >> 1) ^ 1] = get4();
-#ifdef LIBRAW_LIBRARY_BUILD
-      color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-    }
-    if ((tag == 0x81  && type == 7) ||
-	(tag == 0x100 && type == 7) ||
-	(tag == 0x280 && type == 1)) {
-      thumb_offset = ftell(ifp);
-      thumb_length = len;
-    }
-    if (tag == 0x88 && type == 4 && (thumb_offset = get4()))
-      thumb_offset += base;
-    if (tag == 0x89 && type == 4)
-      thumb_length = get4();
-    if (tag == 0x8c || tag == 0x96)
-      meta_offset = ftell(ifp);
-    if (tag == 0x97) {
-      for (i=0; i < 4; i++)
-	ver97 = ver97 * 10 + fgetc(ifp)-'0';
-      switch (ver97) {
-	case 100:
-	  fseek (ifp, 68, SEEK_CUR);
-	  FORC4 cam_mul[(c >> 1) | ((c & 1) << 1)] = get2();
-#ifdef LIBRAW_LIBRARY_BUILD
-          color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-	  break;
-	case 102:
-	  fseek (ifp, 6, SEEK_CUR);
-	  goto get2_rggb;
-	case 103:
-	  fseek (ifp, 16, SEEK_CUR);
-	  FORC4 cam_mul[c] = get2();
-#ifdef LIBRAW_LIBRARY_BUILD
-          color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-      }
-      if (ver97 >= 200) {
-	if (ver97 != 205) fseek (ifp, 280, SEEK_CUR);
-	fread (buf97, 324, 1, ifp);
-      }
-    }
-    if (tag == 0xa4 && type == 3) {
-      fseek (ifp, wbi*48, SEEK_CUR);
-      FORC3 cam_mul[c] = get2();
-#ifdef LIBRAW_LIBRARY_BUILD
-      color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-    }
-    if (tag == 0xa7 && (unsigned) (ver97-200) < 12 && !cam_mul[0]) {
-      ci = xlat[0][serial & 0xff];
-      cj = xlat[1][fgetc(ifp)^fgetc(ifp)^fgetc(ifp)^fgetc(ifp)];
-      ck = 0x60;
-      for (i=0; i < 324; i++)
-	buf97[i] ^= (cj += ci * ck++);
-      i = "66666>666;6A"[ver97-200] - '0';
-      FORC4 cam_mul[c ^ (c >> 1) ^ (i & 1)] =
-	sget2 (buf97 + (i & -2) + c*2);
-#ifdef LIBRAW_LIBRARY_BUILD
-      color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-    }
-    if (tag == 0x200 && len == 3)
-      shot_order = (get4(),get4());
-    if (tag == 0x200 && len == 4)
-      black = (get2()+get2()+get2()+get2())/4;
-    if (tag == 0x201 && len == 4)
-      goto get2_rggb;
-    if (tag == 0x220 && len == 53) {
-      fseek (ifp, 14, SEEK_CUR);
-      pentax_tree();
-    }
-    if (tag == 0x401 && len == 4) {
-      black = (get4()+get4()+get4()+get4())/4;
-    }
-    if (tag == 0xe01) {		/* Nikon Capture Note */
-      type = order;
-      order = 0x4949;
-      fseek (ifp, 22, SEEK_CUR);
-      for (offset=22; offset+22 < len; offset += 22+i) {
-	tag = get4();
-	fseek (ifp, 14, SEEK_CUR);
-	i = get4()-4;
-	if (tag == 0x76a43207) flip = get2();
-	else fseek (ifp, i, SEEK_CUR);
-      }
-      order = type;
-    }
-    if (tag == 0xe80 && len == 256 && type == 7) {
-      fseek (ifp, 48, SEEK_CUR);
-      cam_mul[0] = get2() * 508 * 1.078 / 0x10000;
-      cam_mul[2] = get2() * 382 * 1.173 / 0x10000;
-#ifdef LIBRAW_LIBRARY_BUILD
-      color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-    }
-    if (tag == 0xf00 && type == 7) {
-      if (len == 614)
-	fseek (ifp, 176, SEEK_CUR);
-      else if (len == 734 || len == 1502)
-	fseek (ifp, 148, SEEK_CUR);
-      else goto next;
-      goto get2_256;
-    }
-    if ((tag == 0x1011 && len == 9) || tag == 0x20400200)
-        {
-      for (i=0; i < 3; i++)
-	FORC3 cmatrix[i][c] = ((short) get2()) / 256.0;
-#ifdef LIBRAW_LIBRARY_BUILD
-      color_flags.cmatrix_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-        }
-    if ((tag == 0x1012 || tag == 0x20400600) && len == 4)
-      for (black = i=0; i < 4; i++)
-	black += get2() << 2;
-    if (tag == 0x1017 || tag == 0x20400100)
-        {
-      cam_mul[0] = get2() / 256.0;
-#ifdef LIBRAW_LIBRARY_BUILD
-      color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-        }
-    if (tag == 0x1018 || tag == 0x20400100)
-        {
-      cam_mul[2] = get2() / 256.0;
-#ifdef LIBRAW_LIBRARY_BUILD
-      color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-        }
-    if (tag == 0x2011 && len == 2) {
-get2_256:
-      order = 0x4d4d;
-      cam_mul[0] = get2() / 256.0;
-      cam_mul[2] = get2() / 256.0;
-#ifdef LIBRAW_LIBRARY_BUILD
-      color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-    }
-    if ((tag | 0x70) == 0x2070 && type == 4)
-      fseek (ifp, get4()+base, SEEK_SET);
-    if (tag == 0x2010 && type != 7)
-      load_raw = &CLASS olympus_e410_load_raw;
-    if (tag == 0x2020)
-      parse_thumb_note (base, 257, 258);
-    if (tag == 0x2040)
-      parse_makernote (base, 0x2040);
-    if (tag == 0xb028) {
-      fseek (ifp, get4(), SEEK_SET);
-      parse_thumb_note (base, 136, 137);
-    }
-    if (tag == 0x4001 && len > 500) {
-      i = len == 582 ? 50 : len == 653 ? 68 : len == 5120 ? 142 : 126;
-      fseek (ifp, i, SEEK_CUR);
-get2_rggb:
-      FORC4 cam_mul[c ^ (c >> 1)] = get2();
-#ifdef LIBRAW_LIBRARY_BUILD
-      color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-      fseek (ifp, 22, SEEK_CUR);
-      FORC4 sraw_mul[c ^ (c >> 1)] = get2();
-    }
-next:
-    fseek (ifp, save, SEEK_SET);
-  }
-quit:
-  order = sorder;
-}
-
-/*
-   Since the TIFF DateTime string has no timezone information,
-   assume that the camera's clock was set to Universal Time.
- */
-void CLASS get_timestamp (int reversed)
-{
-  struct tm t;
-  char str[20];
-  int i;
-
-  str[19] = 0;
-  if (reversed)
-    for (i=19; i--; ) str[i] = fgetc(ifp);
-  else
-    fread (str, 19, 1, ifp);
-  memset (&t, 0, sizeof t);
-  if (sscanf (str, "%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;
-  if (mktime(&t) > 0)
-    timestamp = mktime(&t);
-}
-
-void CLASS parse_exif (int base)
-{
-  unsigned kodak, entries, tag, type, len, save, c;
-  double expo;
-
-  kodak = !strncmp(make,"EASTMAN",7);
-  entries = get2();
-  while (entries--) {
-    tiff_get (base, &tag, &type, &len, &save);
-    switch (tag) {
-      case 33434:  shutter = getreal(type);		break;
-      case 33437:  aperture = getreal(type);		break;
-      case 34855:  iso_speed = get2();			break;
-      case 36867:
-      case 36868:  get_timestamp(0);			break;
-      case 37377:  if ((expo = -getreal(type)) < 128)
-		     shutter = pow (2, expo);		break;
-      case 37378:  aperture = pow (2, getreal(type)/2);	break;
-      case 37386:  focal_len = getreal(type);		break;
-      case 37500:  parse_makernote (base, 0);		break;
-      case 40962:  if (kodak) raw_width  = get4();	break;
-      case 40963:  if (kodak) raw_height = get4();	break;
-      case 41730:
-	if (get4() == 0x20002)
-	  for (exif_cfa=c=0; c < 8; c+=2)
-	    exif_cfa |= fgetc(ifp) * 0x01010101 << c;
-    }
-    fseek (ifp, save, SEEK_SET);
-  }
-}
-
-void CLASS parse_gps (int base)
-{
-  unsigned entries, tag, type, len, save, c;
-
-  entries = get2();
-  while (entries--) {
-    tiff_get (base, &tag, &type, &len, &save);
-    switch (tag) {
-      case 1: case 3: case 5:
-	gpsdata[29+tag/2] = getc(ifp);			break;
-      case 2: case 4: case 7:
-	FORC(6) gpsdata[tag/3*6+c] = get4();		break;
-      case 6:
-	FORC(2) gpsdata[18+c] = get4();			break;
-      case 18: case 29:
-	fgets ((char *) (gpsdata+14+tag/3), MIN(len,12), ifp);
-    }
-    fseek (ifp, save, SEEK_SET);
-  }
-}
-
-void CLASS romm_coeff (float romm_cam[3][3])
-{
-  static const float rgb_romm[3][3] =	/* ROMM == Kodak ProPhoto */
-  { {  2.034193, -0.727420, -0.306766 },
-    { -0.228811,  1.231729, -0.002922 },
-    { -0.008565, -0.153273,  1.161839 } };
-  int i, j, k;
-
-  for (i=0; i < 3; i++)
-    for (j=0; j < 3; j++)
-      for (cmatrix[i][j] = k=0; k < 3; k++)
-	cmatrix[i][j] += rgb_romm[i][k] * romm_cam[k][j];
-#ifdef LIBRAW_LIBRARY_BUILD
-  color_flags.cmatrix_state = LIBRAW_COLORSTATE_CALCULATED;
-#endif
-}
-
-void CLASS parse_mos (int offset)
-{
-  char data[40];
-  int skip, from, i, c, neut[4], planes=0, frot=0;
-  static const char *mod[] =
-  { "","DCB2","Volare","Cantare","CMost","Valeo 6","Valeo 11","Valeo 22",
-    "Valeo 11p","Valeo 17","","Aptus 17","Aptus 22","Aptus 75","Aptus 65",
-    "Aptus 54S","Aptus 65S","Aptus 75S","AFi 5","AFi 6","AFi 7" };
-  float romm_cam[3][3];
-
-  fseek (ifp, offset, SEEK_SET);
-  while (1) {
-    if (get4() != 0x504b5453) break;
-    get4();
-    fread (data, 1, 40, ifp);
-    skip = get4();
-    from = ftell(ifp);
-    if (!strcmp(data,"JPEG_preview_data")) {
-      thumb_offset = from;
-      thumb_length = skip;
-    }
-    if (!strcmp(data,"icc_camera_profile")) {
-      profile_offset = from;
-      profile_length = skip;
-    }
-    if (!strcmp(data,"ShootObj_back_type")) {
-      fscanf (ifp, "%d", &i);
-      if ((unsigned) i < sizeof mod / sizeof (*mod))
-	strcpy (model, mod[i]);
-    }
-    if (!strcmp(data,"icc_camera_to_tone_matrix")) {
-      for (i=0; i < 9; i++)
-	romm_cam[0][i] = int_to_float(get4());
-      romm_coeff (romm_cam);
-    }
-    if (!strcmp(data,"CaptProf_color_matrix")) {
-      for (i=0; i < 9; i++)
-	fscanf (ifp, "%f", &romm_cam[0][i]);
-      romm_coeff (romm_cam);
-    }
-    if (!strcmp(data,"CaptProf_number_of_planes"))
-      fscanf (ifp, "%d", &planes);
-    if (!strcmp(data,"CaptProf_raw_data_rotation"))
-      fscanf (ifp, "%d", &flip);
-    if (!strcmp(data,"CaptProf_mosaic_pattern"))
-      FORC4 {
-	fscanf (ifp, "%d", &i);
-	if (i == 1) frot = c ^ (c >> 1);
-      }
-    if (!strcmp(data,"ImgProf_rotation_angle")) {
-      fscanf (ifp, "%d", &i);
-      flip = i - flip;
-    }
-    if (!strcmp(data,"NeutObj_neutrals") && !cam_mul[0]) {
-      FORC4 fscanf (ifp, "%d", neut+c);
-      FORC3 cam_mul[c] = (float) neut[0] / neut[c+1];
-#ifdef LIBRAW_LIBRARY_BUILD
-      color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-    }
-    parse_mos (from);
-    fseek (ifp, skip+from, SEEK_SET);
-  }
-  if (planes)
-    filters = (planes == 1) * 0x01010101 *
-	(uchar) "\x94\x61\x16\x49"[(flip/90 + frot) & 3];
-}
-
-void CLASS linear_table (unsigned len)
-{
-  int i;
-  if (len > 0x1000) len = 0x1000;
-  read_shorts (curve, len);
-#ifdef LIBRAW_LIBRARY_BUILD
-  color_flags.curve_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-  for (i=len; i < 0x1000; i++)
-    curve[i] = curve[i-1];
-  maximum = curve[0xfff];
-}
-
-void CLASS parse_kodak_ifd (int base)
-{
-  unsigned entries, tag, type, len, save;
-  int i, c, wbi=-2, wbtemp=6500;
-  float mul[3], num;
-
-  entries = get2();
-  if (entries > 1024) return;
-  while (entries--) {
-    tiff_get (base, &tag, &type, &len, &save);
-    if (tag == 1020) wbi = getint(type);
-    if (tag == 1021 && len == 72) {		/* WB set in software */
-      fseek (ifp, 40, SEEK_CUR);
-      FORC3 cam_mul[c] = 2048.0 / get2();
-#ifdef LIBRAW_LIBRARY_BUILD
-      color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-      wbi = -2;
-    }
-    if (tag == 2118) wbtemp = getint(type);
-    if (tag == 2130 + wbi)
-      FORC3 mul[c] = getreal(type);
-    if (tag == 2140 + wbi && wbi >= 0)
-        {
-      FORC3 {
-	for (num=i=0; i < 4; i++)
-	  num += getreal(type) * pow (wbtemp/100.0, i);
-	cam_mul[c] = 2048 / (num * mul[c]);
-      }
-#ifdef LIBRAW_LIBRARY_BUILD
-      color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-        }
-    if (tag == 2317) linear_table (len);
-    if (tag == 6020) iso_speed = getint(type);
-    fseek (ifp, save, SEEK_SET);
-  }
-}
-
-int CLASS parse_tiff_ifd (int base)
-{
-  unsigned entries, tag, type, len, plen=16, save;
-  int ifd, use_cm=0, cfa, i, j, c, ima_len=0;
-  char software[64], *cbuf, *cp;
-  uchar cfa_pat[16], cfa_pc[] = { 0,1,2,3 }, tab[256];
-  double dblack, cc[4][4], cm[4][3], cam_xyz[4][3], num;
-  double ab[]={ 1,1,1,1 }, asn[] = { 0,0,0,0 }, xyz[] = { 1,1,1 };
-  unsigned sony_curve[] = { 0,0,0,0,0,4095 };
-  unsigned *buf, sony_offset=0, sony_length=0, sony_key=0;
-  struct jhead jh;
-  FILE *sfp;
-
-  if (tiff_nifds >= sizeof tiff_ifd / sizeof tiff_ifd[0])
-    return 1;
-  ifd = tiff_nifds++;
-  for (j=0; j < 4; j++)
-    for (i=0; i < 4; i++)
-      cc[j][i] = i == j;
-  entries = get2();
-  if (entries > 512) return 1;
-  while (entries--) {
-    tiff_get (base, &tag, &type, &len, &save);
-    switch (tag) {
-      case 17: case 18:
-	if (type == 3 && len == 1)
-            {
-	  cam_mul[(tag-17)*2] = get2() / 256.0;
-#ifdef LIBRAW_LIBRARY_BUILD
-          color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-            }
-	break;
-      case 23:
-	if (type == 3) iso_speed = get2();
-	break;
-      case 36: case 37: case 38:
-	cam_mul[tag-0x24] = get2();
-	break;
-      case 39:
-	if (len < 50 || cam_mul[0]) break;
-	fseek (ifp, 12, SEEK_CUR);
-	FORC3 cam_mul[c] = get2();
-#ifdef LIBRAW_LIBRARY_BUILD
-        color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-	break;
-      case 46:
-	if (type != 7 || fgetc(ifp) != 0xff || fgetc(ifp) != 0xd8) break;
-	thumb_offset = ftell(ifp) - 2;
-	thumb_length = len;
-	break;
-      case 2: case 256:			/* ImageWidth */
-	tiff_ifd[ifd].t_width = getint(type);
-	break;
-      case 3: case 257:			/* ImageHeight */
-	tiff_ifd[ifd].t_height = getint(type);
-	break;
-      case 258:				/* BitsPerSample */
-	tiff_ifd[ifd].samples = len & 7;
-	tiff_ifd[ifd].bps = get2();
-	break;
-      case 259:				/* Compression */
-	tiff_ifd[ifd].comp = get2();
-	break;
-      case 262:				/* PhotometricInterpretation */
-	tiff_ifd[ifd].phint = get2();
-	break;
-      case 270:				/* ImageDescription */
-	fread (desc, 512, 1, ifp);
-	break;
-      case 271:				/* Make */
-	fgets (make, 64, ifp);
-	break;
-      case 272:				/* Model */
-	fgets (model, 64, ifp);
-	break;
-      case 280:				/* Panasonic RW2 offset */
-	if (type != 4) break;
-	load_raw = &CLASS panasonic_load_raw;
-	load_flags = 0x2008;
-      case 273:				/* StripOffset */
-      case 513:
-	tiff_ifd[ifd].offset = get4()+base;
-	if (!tiff_ifd[ifd].bps) {
-	  fseek (ifp, tiff_ifd[ifd].offset, SEEK_SET);
-	  if (ljpeg_start (&jh, 1)) {
-	    tiff_ifd[ifd].comp    = 6;
-	    tiff_ifd[ifd].t_width   = jh.wide << (jh.clrs == 2);
-	    tiff_ifd[ifd].t_height  = jh.high;
-	    tiff_ifd[ifd].bps     = jh.bits;
-	    tiff_ifd[ifd].samples = jh.clrs;
-	  }
-	}
-	break;
-      case 274:				/* Qt::Orientation */
-	tiff_ifd[ifd].t_flip = "50132467"[get2() & 7]-'0';
-	break;
-      case 277:				/* SamplesPerPixel */
-	tiff_ifd[ifd].samples = getint(type) & 7;
-	break;
-      case 279:				/* StripByteCounts */
-      case 514:
-	tiff_ifd[ifd].bytes = get4();
-	break;
-      case 305:  case 11:		/* Software */
-	fgets (software, 64, ifp);
-	if (!strncmp(software,"Adobe",5) ||
-	    !strncmp(software,"dcraw",5) ||
-	    !strncmp(software,"UFRaw",5) ||
-	    !strncmp(software,"Bibble",6) ||
-	    !strncmp(software,"Nikon Scan",10) ||
-	    !strcmp (software,"Digital Photo Professional"))
-	  is_raw = 0;
-	break;
-      case 306:				/* DateTime */
-	get_timestamp(0);
-	break;
-      case 315:				/* Artist */
-	fread (artist, 64, 1, ifp);
-	break;
-      case 322:				/* TileWidth */
-	tile_width = getint(type);
-	break;
-      case 323:				/* TileLength */
-	tile_length = getint(type);
-	break;
-      case 324:				/* TileOffsets */
-	tiff_ifd[ifd].offset = len > 1 ? ftell(ifp) : get4();
-	if (len == 4) {
-	  load_raw = &CLASS sinar_4shot_load_raw;
-	  is_raw = 5;
-	}
-	break;
-      case 330:				/* SubIFDs */
-	if (!strcmp(model,"DSLR-A100") && tiff_ifd[ifd].t_width == 3872) {
-	  load_raw = &CLASS sony_arw_load_raw;
-	  data_offset = get4()+base;
-	  ifd++;  break;
-	}
-	while (len--) {
-	  i = ftell(ifp);
-	  fseek (ifp, get4()+base, SEEK_SET);
-	  if (parse_tiff_ifd (base)) break;
-	  fseek (ifp, i+4, SEEK_SET);
-	}
-	break;
-      case 400:
-	strcpy (make, "Sarnoff");
-	maximum = 0xfff;
-	break;
-      case 28688:
-	FORC4 sony_curve[c+1] = get2() >> 2 & 0xfff;
-	for (i=0; i < 5; i++)
-	  for (j = sony_curve[i]+1; j <= sony_curve[i+1]; j++)
-	    curve[j] = curve[j-1] + (1 << i);
-#ifdef LIBRAW_LIBRARY_BUILD
-        color_flags.curve_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-	break;
-      case 29184: sony_offset = get4();  break;
-      case 29185: sony_length = get4();  break;
-      case 29217: sony_key    = get4();  break;
-      case 29264:
-	parse_minolta (ftell(ifp));
-	raw_width = 0;
-	break;
-      case 29443:
-	FORC4 cam_mul[c ^ (c < 2)] = get2();
-#ifdef LIBRAW_LIBRARY_BUILD
-        color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-	break;
-      case 29459:
-	FORC4 cam_mul[c ^ (c >> 1)] = get2();
-#ifdef LIBRAW_LIBRARY_BUILD
-        color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-	break;
-      case 33405:			/* Model2 */
-	fgets (model2, 64, ifp);
-	break;
-      case 33422:			/* CFAPattern */
-      case 64777:			/* Kodak P-series */
-	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 33424:
-	fseek (ifp, get4()+base, SEEK_SET);
-	parse_kodak_ifd (base);
-	break;
-      case 33434:			/* ExposureTime */
-	shutter = getreal(type);
-	break;
-      case 33437:			/* FNumber */
-	aperture = getreal(type);
-	break;
-      case 34306:			/* Leaf white balance */
-	FORC4 cam_mul[c ^ 1] = 4096.0 / get2();
-#ifdef LIBRAW_LIBRARY_BUILD
-        color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-	break;
-      case 34307:			/* Leaf CatchLight color matrix */
-	fread (software, 1, 7, ifp);
-	if (strncmp(software,"MATRIX",6)) break;
-	colors = 4;
-	for (raw_color = i=0; i < 3; i++) {
-	  FORC4 fscanf (ifp, "%f", &rgb_cam[i][c^1]);
-	  if (!use_camera_wb) continue;
-	  num = 0;
-	  FORC4 num += rgb_cam[i][c];
-	  FORC4 rgb_cam[i][c] /= num;
-#ifdef LIBRAW_LIBRARY_BUILD
-          color_flags.rgb_cam_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-	}
-	break;
-      case 34310:			/* Leaf metadata */
-	parse_mos (ftell(ifp));
-      case 34303:
-	strcpy (make, "Leaf");
-	break;
-      case 34665:			/* EXIF tag */
-	fseek (ifp, get4()+base, SEEK_SET);
-	parse_exif (base);
-	break;
-      case 34853:			/* GPSInfo tag */
-	fseek (ifp, get4()+base, SEEK_SET);
-	parse_gps (base);
-	break;
-      case 34675:			/* InterColorProfile */
-      case 50831:			/* AsShotICCProfile */
-	profile_offset = ftell(ifp);
-	profile_length = len;
-	break;
-      case 37122:			/* CompressedBitsPerPixel */
-	kodak_cbpp = get4();
-	break;
-      case 37386:			/* FocalLength */
-	focal_len = getreal(type);
-	break;
-      case 37393:			/* ImageNumber */
-	shot_order = getint(type);
-	break;
-      case 37400:			/* old Kodak KDC tag */
-	for (raw_color = i=0; i < 3; i++) {
-	  getreal(type);
-	  FORC3 rgb_cam[i][c] = getreal(type);
-	}
-#ifdef LIBRAW_LIBRARY_BUILD
-        color_flags.rgb_cam_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-	break;
-      case 46275:			/* Imacon tags */
-	strcpy (make, "Imacon");
-	data_offset = ftell(ifp);
-	ima_len = len;
-	break;
-      case 46279:
-	if (!ima_len) break;
-	fseek (ifp, 78, SEEK_CUR);
-	raw_width  = get4();
-	raw_height = get4();
-	left_margin = get4() & 7;
-	width = raw_width - left_margin - (get4() & 7);
-	top_margin = get4() & 7;
-	height = raw_height - top_margin - (get4() & 7);
-	if (raw_width == 7262) {
-	  height = 5444;
-	  width  = 7244;
-	  left_margin = 7;
-	}
-	fseek (ifp, 52, SEEK_CUR);
-	FORC3 cam_mul[c] = getreal(11);
-#ifdef LIBRAW_LIBRARY_BUILD
-        color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-	fseek (ifp, 114, SEEK_CUR);
-	flip = (get2() >> 7) * 90;
-	if (width * height * 6 == ima_len) {
-	  if (flip % 180 == 90) SWAP(width,height);
-	  filters = flip = 0;
-	}
-	sprintf (model, "Ixpress %d-Mp", height*width/1000000);
-	load_raw = &CLASS imacon_full_load_raw;
-	if (filters) {
-	  if (left_margin & 1) filters = 0x61616161;
-	  load_raw = &CLASS unpacked_load_raw;
-	}
-	maximum = 0xffff;
-	break;
-      case 50454:			/* Sinar tag */
-      case 50455:
-	if (!(cbuf = (char *) malloc(len))) break;
-	fread (cbuf, 1, len, ifp);
-	for (cp = cbuf-1; cp && cp < cbuf+len; cp = strchr(cp,'\n'))
-	  if (!strncmp (++cp,"Neutral ",8))
-              {
-	    sscanf (cp+8, "%f %f %f", cam_mul, cam_mul+1, cam_mul+2);
-#ifdef LIBRAW_LIBRARY_BUILD
-            color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-              }
-	free (cbuf);
-	break;
-      case 50458:
-	if (!make[0]) strcpy (make, "Hasselblad");
-	break;
-      case 50459:			/* Hasselblad tag */
-	i = order;
-	j = ftell(ifp);
-	c = tiff_nifds;
-	order = get2();
-	fseek (ifp, j+(get2(),get4()), SEEK_SET);
-	parse_tiff_ifd (j);
-	maximum = 0xffff;
-	tiff_nifds = c;
-	order = i;
-	break;
-      case 50706:			/* DNGVersion */
-	FORC4 dng_version = (dng_version << 8) + fgetc(ifp);
-	if (!make[0]) strcpy (make, "DNG");
-	is_raw = 1;
-	break;
-      case 50710:			/* CFAPlaneColor */
-	if (len > 4) len = 4;
-	colors = len;
-	fread (cfa_pc, 1, colors, ifp);
-guess_cfa_pc:
-	FORCC tab[cfa_pc[c]] = c;
-	cdesc[c] = 0;
-	for (i=16; i--; )
-	  filters = filters << 2 | tab[cfa_pat[i % plen]];
-	break;
-      case 50711:			/* CFALayout */
-	if (get2() == 2) {
-	  fuji_width = 1;
-	  filters = 0x49494949;
-	}
-	break;
-      case 291:
-      case 50712:			/* LinearizationTable */
-	linear_table (len);
-	break;
-      case 50714:			/* BlackLevel */
-      case 50715:			/* BlackLevelDeltaH */
-      case 50716:			/* BlackLevelDeltaV */
-	for (dblack=i=0; i < len; i++)
-	  dblack += getreal(type);
-	black += dblack/len + 0.5;
-	break;
-      case 50717:			/* WhiteLevel */
-	maximum = getint(type);
-	break;
-      case 50718:			/* DefaultScale */
-	pixel_aspect  = getreal(type);
-	pixel_aspect /= getreal(type);
-	break;
-      case 50721:			/* ColorMatrix1 */
-      case 50722:			/* ColorMatrix2 */
-	FORCC for (j=0; j < 3; j++)
-	  cm[c][j] = getreal(type);
-	use_cm = 1;
-	break;
-      case 50723:			/* CameraCalibration1 */
-      case 50724:			/* CameraCalibration2 */
-	for (i=0; i < colors; i++)
-	  FORCC cc[i][c] = getreal(type);
-      case 50727:			/* AnalogBalance */
-	FORCC ab[c] = getreal(type);
-	break;
-      case 50728:			/* AsShotNeutral */
-	FORCC asn[c] = getreal(type);
-	break;
-      case 50729:			/* AsShotWhiteXY */
-	xyz[0] = getreal(type);
-	xyz[1] = getreal(type);
-	xyz[2] = 1 - xyz[0] - xyz[1];
-	FORC3 xyz[c] /= d65_white[c];
-	break;
-      case 50740:			/* DNGPrivateData */
-	if (dng_version) break;
-	parse_minolta (j = get4()+base);
-	fseek (ifp, j, SEEK_SET);
-	parse_tiff_ifd (base);
-	break;
-      case 50752:
-	read_shorts (cr2_slice, 3);
-	break;
-      case 50829:			/* ActiveArea */
-	top_margin = getint(type);
-	left_margin = getint(type);
-	height = getint(type) - top_margin;
-	width = getint(type) - left_margin;
-	break;
-      case 64772:			/* Kodak P-series */
-	fseek (ifp, 16, SEEK_CUR);
-	data_offset = get4();
-	fseek (ifp, 28, SEEK_CUR);
-	data_offset += get4();
-	load_raw = &CLASS packed_12_load_raw;
-    }
-    fseek (ifp, save, SEEK_SET);
-  }
-  if (sony_length && (buf = (unsigned *) malloc(sony_length))) {
-    fseek (ifp, sony_offset, SEEK_SET);
-    fread (buf, sony_length, 1, ifp);
-    sony_decrypt (buf, sony_length/4, 1, sony_key);
-#ifndef LIBRAW_LIBRARY_BUILD
-    sfp = ifp;
-    if ((ifp = tmpfile())) {
-      fwrite (buf, sony_length, 1, ifp);
-      fseek (ifp, 0, SEEK_SET);
-      parse_tiff_ifd (-sony_offset);
-      fclose (ifp);
-    }
-    ifp = sfp;
-#else
-    if( !ifp->tempbuffer_open(buf,sony_length))
-        {
-            parse_tiff_ifd(-sony_offset);
-            ifp->tempbuffer_close();
-        }
-#endif
-    free (buf);
-  }
-  for (i=0; i < colors; i++)
-    FORCC cc[i][c] *= ab[i];
-  if (use_cm) {
-    FORCC for (i=0; i < 3; i++)
-      for (cam_xyz[c][i]=j=0; j < colors; j++)
-	cam_xyz[c][i] += cc[c][j] * cm[j][i] * xyz[i];
-    cam_xyz_coeff (cam_xyz);
-  }
-  if (asn[0]) {
-    cam_mul[3] = 0;
-    FORCC cam_mul[c] = 1 / asn[c];
-#ifdef LIBRAW_LIBRARY_BUILD
-    color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-  }
-  if (!use_cm)
-      {
-    FORCC pre_mul[c] /= cc[c][c];
-#ifdef LIBRAW_LIBRARY_BUILD
-    color_flags.pre_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-      }
-
-  return 0;
-}
-
-void CLASS parse_tiff (int base)
-{
-  int doff, max_samp=0, raw=-1, thm=-1, i;
-  struct jhead jh;
-
-  fseek (ifp, base, SEEK_SET);
-  order = get2();
-  if (order != 0x4949 && order != 0x4d4d) return;
-  get2();
-  memset (tiff_ifd, 0, sizeof tiff_ifd);
-  tiff_nifds = 0;
-  while ((doff = get4())) {
-    fseek (ifp, doff+base, SEEK_SET);
-    if (parse_tiff_ifd (base)) break;
-  }
-  thumb_misc = 16;
-  if (thumb_offset) {
-    fseek (ifp, thumb_offset, SEEK_SET);
-    if (ljpeg_start (&jh, 1)) {
-      thumb_misc   = jh.bits;
-      thumb_width  = jh.wide;
-      thumb_height = jh.high;
-    }
-  }
-  for (i=0; i < tiff_nifds; i++) {
-    if (max_samp < tiff_ifd[i].samples)
-	max_samp = tiff_ifd[i].samples;
-    if (max_samp > 3) max_samp = 3;
-    if ((tiff_ifd[i].comp != 6 || tiff_ifd[i].samples != 3) &&
-	tiff_ifd[i].t_width*tiff_ifd[i].t_height > raw_width*raw_height) {
-      raw_width     = tiff_ifd[i].t_width;
-      raw_height    = tiff_ifd[i].t_height;
-      tiff_bps      = tiff_ifd[i].bps;
-      tiff_compress = tiff_ifd[i].comp;
-      data_offset   = tiff_ifd[i].offset;
-      tiff_flip     = tiff_ifd[i].t_flip;
-      tiff_samples  = tiff_ifd[i].samples;
-      raw = i;
-    }
-  }
-  fuji_width *= (raw_width+1)/2;
-  if (tiff_ifd[0].t_flip) tiff_flip = tiff_ifd[0].t_flip;
-  if (raw >= 0 && !load_raw)
-    switch (tiff_compress) {
-      case 0:  case 1:
-	switch (tiff_bps) {
-	  case  8: load_raw = &CLASS eight_bit_load_raw;	break;
-	  case 12: load_raw = &CLASS packed_12_load_raw;
-		   if (tiff_ifd[raw].phint == 2)
-		     load_flags = 6;
-		   if (strncmp(make,"PENTAX",6)) break;
-	  case 14:
-	  case 16: load_raw = &CLASS unpacked_load_raw;		break;
-	}
-	if (tiff_ifd[raw].bytes*5 == raw_width*raw_height*8)
-	  load_raw = &CLASS olympus_e300_load_raw;
-	break;
-      case 6:  case 7:  case 99:
-	load_raw = &CLASS lossless_jpeg_load_raw;		break;
-      case 262:
-	load_raw = &CLASS kodak_262_load_raw;			break;
-      case 32767:
-	load_raw = &CLASS sony_arw2_load_raw;
-	if (tiff_ifd[raw].bytes*8 == raw_width*raw_height*tiff_bps)
-	  break;
-	raw_height += 8;
-	load_raw = &CLASS sony_arw_load_raw;			break;
-      case 32769:
-	load_flags = 8;
-      case 32773:
-	load_raw = &CLASS packed_12_load_raw;			break;
-      case 34713:
-	load_raw = &CLASS nikon_compressed_load_raw;		break;
-      case 65535:
-	load_raw = &CLASS pentax_k10_load_raw;			break;
-      case 65000:
-	switch (tiff_ifd[raw].phint) {
-	  case 2: load_raw = &CLASS kodak_rgb_load_raw;   filters = 0;  break;
-	  case 6: load_raw = &CLASS kodak_ycbcr_load_raw; filters = 0;  break;
-	  case 32803: load_raw = &CLASS kodak_65000_load_raw;
-	}
-      case 32867: break;
-      default: is_raw = 0;
-    }
-  if (!dng_version && tiff_samples == 3)
-    if (tiff_ifd[raw].bytes && tiff_bps != 14 && tiff_bps != 2048)
-      is_raw = 0;
-  if (!dng_version && tiff_bps == 8 && tiff_compress == 1 &&
-	tiff_ifd[raw].phint == 1) is_raw = 0;
-  if (tiff_bps == 8 && tiff_samples == 4) is_raw = 0;
-  for (i=0; i < tiff_nifds; i++)
-    if (i != raw && tiff_ifd[i].samples == max_samp &&
-	tiff_ifd[i].t_width * tiff_ifd[i].t_height / SQR(tiff_ifd[i].bps+1) >
-	      thumb_width *       thumb_height / SQR(thumb_misc+1)) {
-      thumb_width  = tiff_ifd[i].t_width;
-      thumb_height = tiff_ifd[i].t_height;
-      thumb_offset = tiff_ifd[i].offset;
-      thumb_length = tiff_ifd[i].bytes;
-      thumb_misc   = tiff_ifd[i].bps;
-      thm = i;
-    }
-  if (thm >= 0) {
-    thumb_misc |= tiff_ifd[thm].samples << 5;
-    switch (tiff_ifd[thm].comp) {
-      case 0:
-	write_thumb = &CLASS layer_thumb;
-	break;
-      case 1:
-	if (tiff_ifd[thm].bps > 8)
-	  thumb_load_raw = &CLASS kodak_thumb_load_raw;
-	else
-	  write_thumb = &CLASS ppm_thumb;
-	break;
-      case 65000:
-	thumb_load_raw = tiff_ifd[thm].phint == 6 ?
-		&CLASS kodak_ycbcr_load_raw : &CLASS kodak_rgb_load_raw;
-    }
-  }
-}
-
-void CLASS parse_minolta (int base)
-{
-  int save, tag, len, offset, high=0, wide=0, i, c;
-  short sorder=order;
-
-  fseek (ifp, base, SEEK_SET);
-  if (fgetc(ifp) || fgetc(ifp)-'M' || fgetc(ifp)-'R') return;
-  order = fgetc(ifp) * 0x101;
-  offset = base + get4() + 8;
-  while ((save=ftell(ifp)) < offset) {
-    for (tag=i=0; i < 4; i++)
-      tag = tag << 8 | fgetc(ifp);
-    len = get4();
-    switch (tag) {
-      case 0x505244:				/* PRD */
-	fseek (ifp, 8, SEEK_CUR);
-	high = get2();
-	wide = get2();
-	break;
-      case 0x574247:				/* WBG */
-	get4();
-	i = strcmp(model,"DiMAGE A200") ? 0:3;
-	FORC4 cam_mul[c ^ (c >> 1) ^ i] = get2();
-#ifdef LIBRAW_LIBRARY_BUILD
-        color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-	break;
-      case 0x545457:				/* TTW */
-	parse_tiff (ftell(ifp));
-	data_offset = offset;
-    }
-    fseek (ifp, save+len+8, SEEK_SET);
-  }
-  raw_height = high;
-  raw_width  = wide;
-  order = sorder;
-}
-
-/*
-   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 CLASS parse_external_jpeg()
-{
-  char *file, *ext, *jname, *jfile, *jext;
-#ifndef LIBRAW_LIBRARY_BUILD
-  FILE *save=ifp;
-#else
-  if(!ifp->fname())
-      {
-          imgdata.process_warnings |= LIBRAW_WARN_NO_METADATA ;
-          return;
-      }
-#endif
-
-  ext  = strrchr (ifname, '.');
-  file = strrchr (ifname, '/');
-  if (!file) file = strrchr (ifname, '\\');
-#ifndef LIBRAW_LIBRARY_BUILD
-  if (!file) file = ifname-1;
-#else
-  if (!file) file = (char*)ifname-1;
-#endif
-  file++;
-  if (!ext || strlen(ext) != 4 || ext-file != 8) return;
-  jname = (char *) malloc (strlen(ifname) + 1);
-  merror (jname, "parse_external_jpeg()");
-  strcpy (jname, ifname);
-  jfile = file - ifname + jname;
-  jext  = ext  - ifname + jname;
-  if (strcasecmp (ext, ".jpg")) {
-    strcpy (jext, isupper(ext[1]) ? ".JPG":".jpg");
-    if (isdigit(*file)) {
-      memcpy (jfile, file+4, 4);
-      memcpy (jfile+4, file, 4);
-    }
-  } else
-    while (isdigit(*--jext)) {
-      if (*jext != '9') {
-        (*jext)++;
-	break;
-      }
-      *jext = '0';
-    }
-#ifndef LIBRAW_LIBRARY_BUILD
-  if (strcmp (jname, ifname)) {
-    if ((ifp = fopen (jname, "rb"))) {
-#ifdef DCRAW_VERBOSE
-      if (verbose)
-	fprintf (stderr,_("Reading metadata from %s ...\n"), jname);
-#endif
-      parse_tiff (12);
-      thumb_offset = 0;
-      is_raw = 1;
-      fclose (ifp);
-    }
-  }
-#else
-  if (strcmp (jname, ifname)) 
-      {
-          if(!ifp->subfile_open(jname))
-              {
-                  parse_tiff (12);
-                  thumb_offset = 0;
-                  is_raw = 1;
-                  ifp->subfile_close();
-              }
-          else
-              imgdata.process_warnings |= LIBRAW_WARN_NO_METADATA ;
-      }
-#endif
-  if (!timestamp)
-      {
-#ifdef LIBRAW_LIBRARY_BUILD
-          imgdata.process_warnings |= LIBRAW_WARN_NO_METADATA ;
-#endif
-#ifdef DCRAW_VERBOSE
-          fprintf (stderr,_("Failed to read metadata from %s\n"), jname);
-#endif
-      }
-  free (jname);
-#ifndef LIBRAW_LIBRARY_BUILD
-  ifp = save;
-#endif
-}
-
-/*
-   CIFF block 0x1030 contains an 8x8 white sample.
-   Load this into white[][] for use in scale_colors().
- */
-void CLASS ciff_block_1030()
-{
-  static const ushort key[] = { 0x410, 0x45f3 };
-  int i, bpp, row, col, vbits=0;
-  unsigned long bitbuf=0;
-
-  if ((get2(),get4()) != 0x80008 || !get4()) return;
-  bpp = get2();
-  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() ^ 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 CLASS parse_ciff (int offset, int length)
-{
-  int tboff, nrecs, c, type, len, save, wbi=-1;
-  ushort key[] = { 0x410, 0x45f3 };
-
-  fseek (ifp, offset+length-4, SEEK_SET);
-  tboff = get4() + offset;
-  fseek (ifp, tboff, SEEK_SET);
-  nrecs = get2();
-  if (nrecs > 100) return;
-  while (nrecs--) {
-    type = get2();
-    len  = get4();
-    save = ftell(ifp) + 4;
-    fseek (ifp, offset+get4(), SEEK_SET);
-    if ((((type >> 8) + 8) | 8) == 0x38)
-      parse_ciff (ftell(ifp), len);	/* Parse a sub-table */
-
-    if (type == 0x0810)
-      fread (artist, 64, 1, ifp);
-    if (type == 0x080a) {
-      fread (make, 64, 1, ifp);
-      fseek (ifp, strlen(make) - 63, SEEK_CUR);
-      fread (model, 64, 1, ifp);
-    }
-    if (type == 0x1810) {
-      fseek (ifp, 12, SEEK_CUR);
-      flip = get4();
-    }
-    if (type == 0x1835)			/* Get the decoder table */
-      tiff_compress = get4();
-    if (type == 0x2007) {
-      thumb_offset = ftell(ifp);
-      thumb_length = len;
-    }
-    if (type == 0x1818) {
-      shutter = pow (2.0f, -int_to_float((get4(),get4())));
-      aperture = pow (2.0f, int_to_float(get4())/2);
-    }
-    if (type == 0x102a) {
-      iso_speed = pow (2, (get4(),get2())/32.0 - 4) * 50;
-      aperture  = pow (2, (get2(),(short)get2())/64.0);
-      shutter   = pow (2,-((short)get2())/32.0);
-      wbi = (get2(),get2());
-      if (wbi > 17) wbi = 0;
-      fseek (ifp, 32, SEEK_CUR);
-      if (shutter > 1e6) shutter = get2()/10.0;
-    }
-    if (type == 0x102c) {
-      if (get2() > 512) {		/* Pro90, G1 */
-	fseek (ifp, 118, SEEK_CUR);
-	FORC4 cam_mul[c ^ 2] = get2();
-#ifdef LIBRAW_LIBRARY_BUILD
-        color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-      } else {				/* G2, S30, S40 */
-	fseek (ifp, 98, SEEK_CUR);
-	FORC4 cam_mul[c ^ (c >> 1) ^ 1] = get2();
-#ifdef LIBRAW_LIBRARY_BUILD
-        color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-      }
-    }
-    if (type == 0x0032) {
-      if (len == 768) {			/* EOS D30 */
-	fseek (ifp, 72, SEEK_CUR);
-	FORC4 cam_mul[c ^ (c >> 1)] = 1024.0 / get2();
-#ifdef LIBRAW_LIBRARY_BUILD
-        color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-	if (!wbi) cam_mul[0] = -1;	/* use my auto white balance */
-      } else if (!cam_mul[0]) {
-	if (get2() == key[0])		/* Pro1, G6, S60, S70 */
-	  c = (strstr(model,"Pro1") ?
-	      "012346000000000000":"01345:000000006008")[wbi]-'0'+ 2;
-	else {				/* G3, G5, S45, S50 */
-	  c = "023457000000006000"[wbi]-'0';
-	  key[0] = key[1] = 0;
-	}
-	fseek (ifp, 78 + c*8, SEEK_CUR);
-	FORC4 cam_mul[c ^ (c >> 1) ^ 1] = get2() ^ key[c & 1];
-#ifdef LIBRAW_LIBRARY_BUILD
-        color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-	if (!wbi) cam_mul[0] = -1;
-      }
-    }
-    if (type == 0x10a9) {		/* D60, 10D, 300D, and clones */
-      if (len > 66) wbi = "0134567028"[wbi]-'0';
-      fseek (ifp, 2 + wbi*8, SEEK_CUR);
-      FORC4 cam_mul[c ^ (c >> 1)] = get2();
-#ifdef LIBRAW_LIBRARY_BUILD
-      color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-    }
-    if (type == 0x1030 && (0x18040 >> wbi & 1))
-      ciff_block_1030();		/* all that don't have 0x10a9 */
-    if (type == 0x1031) {
-      raw_width = (get2(),get2());
-      raw_height = get2();
-    }
-    if (type == 0x5029) {
-      focal_len = len >> 16;
-      if ((len & 0xffff) == 2) focal_len /= 32;
-    }
-    if (type == 0x5813) flash_used = int_to_float(len);
-    if (type == 0x5814) canon_ev   = int_to_float(len);
-    if (type == 0x5817) shot_order = len;
-    if (type == 0x5834) unique_id  = len;
-    if (type == 0x580e) timestamp  = len;
-    if (type == 0x180e) timestamp  = get4();
-#ifdef LOCALTIME
-    if ((type | 0x4000) == 0x580e)
-      timestamp = mktime (gmtime (&timestamp));
-#endif
-    fseek (ifp, save, SEEK_SET);
-  }
-}
-
-void CLASS parse_rollei()
-{
-  char line[128], *val;
-  struct tm t;
-
-  fseek (ifp, 0, SEEK_SET);
-  memset (&t, 0, sizeof t);
-  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"))
-      thumb_offset = atoi(val);
-    if (!strcmp(line,"X  "))
-      raw_width = atoi(val);
-    if (!strcmp(line,"Y  "))
-      raw_height = atoi(val);
-    if (!strcmp(line,"TX "))
-      thumb_width = atoi(val);
-    if (!strcmp(line,"TY "))
-      thumb_height = atoi(val);
-  } while (strncmp(line,"EOHD",4));
-  data_offset = thumb_offset + thumb_width * thumb_height * 2;
-  t.tm_year -= 1900;
-  t.tm_mon -= 1;
-  if (mktime(&t) > 0)
-    timestamp = mktime(&t);
-  strcpy (make, "Rollei");
-  strcpy (model,"d530flex");
-  write_thumb = &CLASS rollei_thumb;
-}
-
-void CLASS parse_sinar_ia()
-{
-  int entries, off;
-  char str[8], *cp;
-
-  order = 0x4949;
-  fseek (ifp, 4, SEEK_SET);
-  entries = get4();
-  fseek (ifp, get4(), SEEK_SET);
-  while (entries--) {
-    off = get4(); get4();
-    fread (str, 8, 1, ifp);
-    if (!strcmp(str,"META"))   meta_offset = off;
-    if (!strcmp(str,"THUMB")) thumb_offset = off;
-    if (!strcmp(str,"RAW0"))   data_offset = off;
-  }
-  fseek (ifp, meta_offset+20, SEEK_SET);
-  fread (make, 64, 1, ifp);
-  make[63] = 0;
-  if ((cp = strchr(make,' '))) {
-    strcpy (model, cp+1);
-    *cp = 0;
-  }
-  raw_width  = get2();
-  raw_height = get2();
-  load_raw = &CLASS unpacked_load_raw;
-  thumb_width = (get4(),get2());
-  thumb_height = get2();
-  write_thumb = &CLASS ppm_thumb;
-  maximum = 0x3fff;
-}
-
-void CLASS parse_phase_one (int base)
-{
-  unsigned entries, tag, type, len, data, save, i, c;
-  float romm_cam[3][3];
-  char *cp;
-
-  memset (&ph1, 0, sizeof ph1);
-  fseek (ifp, base, SEEK_SET);
-  order = get4() & 0xffff;
-  if (get4() >> 8 != 0x526177) return;		/* "Raw" */
-  fseek (ifp, get4()+base, SEEK_SET);
-  entries = get4();
-  get4();
-  while (entries--) {
-    tag  = get4();
-    type = get4();
-    len  = get4();
-    data = get4();
-    save = ftell(ifp);
-    fseek (ifp, base+data, SEEK_SET);
-    switch (tag) {
-      case 0x100:  flip = "0653"[data & 3]-'0';  break;
-      case 0x106:
-	for (i=0; i < 9; i++)
-	  romm_cam[0][i] = getreal(11);
-	romm_coeff (romm_cam);
-	break;
-      case 0x107:
-	FORC3 cam_mul[c] = getreal(11);
-#ifdef LIBRAW_LIBRARY_BUILD
-        color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-	break;
-      case 0x108:  raw_width     = data;	break;
-      case 0x109:  raw_height    = data;	break;
-      case 0x10a:  left_margin   = data;	break;
-      case 0x10b:  top_margin    = data;	break;
-      case 0x10c:  width         = data;	break;
-      case 0x10d:  height        = data;	break;
-      case 0x10e:  ph1.format    = data;	break;
-      case 0x10f:  data_offset   = data+base;	break;
-      case 0x110:  meta_offset   = data+base;
-		   meta_length   = len;			break;
-      case 0x112:  ph1.key_off   = save - 4;		break;
-      case 0x210:  ph1.tag_210   = int_to_float(data);	break;
-      case 0x21a:  ph1.tag_21a   = data;		break;
-      case 0x21c:  strip_offset  = data+base;		break;
-      case 0x21d:  ph1.t_black     = data;		break;
-      case 0x222:  ph1.split_col = data - left_margin;	break;
-      case 0x223:  ph1.black_off = data+base;		break;
-      case 0x301:
-	model[63] = 0;
-	fread (model, 1, 63, ifp);
-	if ((cp = strstr(model," camera"))) *cp = 0;
-    }
-    fseek (ifp, save, SEEK_SET);
-  }
-  load_raw = ph1.format < 3 ?
-	&CLASS phase_one_load_raw : &CLASS phase_one_load_raw_c;
-  maximum = 0xffff;
-  strcpy (make, "Phase One");
-  if (model[0]) return;
-  switch (raw_height) {
-    case 2060: strcpy (model,"LightPhase");	break;
-    case 2682: strcpy (model,"H 10");		break;
-    case 4128: strcpy (model,"H 20");		break;
-    case 5488: strcpy (model,"H 25");		break;
-  }
-}
-
-void CLASS parse_fuji (int offset)
-{
-  unsigned entries, tag, len, save, c;
-
-  fseek (ifp, offset, SEEK_SET);
-  entries = get4();
-  if (entries > 255) return;
-  while (entries--) {
-    tag = get2();
-    len = get2();
-    save = ftell(ifp);
-    if (tag == 0x100) {
-      raw_height = get2();
-      raw_width  = get2();
-    } else if (tag == 0x121) {
-      height = get2();
-      if ((width = get2()) == 4284) width += 3;
-    } else if (tag == 0x130)
-      fuji_layout = fgetc(ifp) >> 7;
-    if (tag == 0x2ff0)
-        {
-      FORC4 cam_mul[c ^ 1] = get2();
-#ifdef LIBRAW_LIBRARY_BUILD
-      color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-        }
-    fseek (ifp, save+len, SEEK_SET);
-  }
-  height <<= fuji_layout;
-  width  >>= fuji_layout;
-}
-
-int CLASS parse_jpeg (int offset)
-{
-  int len, save, hlen, mark;
-
-  fseek (ifp, offset, SEEK_SET);
-  if (fgetc(ifp) != 0xff || fgetc(ifp) != 0xd8) return 0;
-
-  while (fgetc(ifp) == 0xff && (mark = fgetc(ifp)) != 0xda) {
-    order = 0x4d4d;
-    len   = get2() - 2;
-    save  = ftell(ifp);
-    if (mark == 0xc0 || mark == 0xc3) {
-      fgetc(ifp);
-      raw_height = get2();
-      raw_width  = get2();
-    }
-    order = get2();
-    hlen  = get4();
-    if (get4() == 0x48454150)		/* "HEAP" */
-      parse_ciff (save+hlen, len-hlen);
-    parse_tiff (save+6);
-    fseek (ifp, save+len, SEEK_SET);
-  }
-  return 1;
-}
-
-void CLASS parse_riff()
-{
-  unsigned i, size, end;
-  char tag[4], date[64], month[64];
-  static const char mon[12][4] =
-  { "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" };
-  struct tm t;
-
-  order = 0x4949;
-  fread (tag, 4, 1, ifp);
-  size = get4();
-  end = ftell(ifp) + size;
-  if (!memcmp(tag,"RIFF",4) || !memcmp(tag,"LIST",4)) {
-    get4();
-    while (ftell(ifp)+7 < end)
-      parse_riff();
-  } else if (!memcmp(tag,"nctg",4)) {
-    while (ftell(ifp)+7 < end) {
-      i = get2();
-      size = get2();
-      if ((i+1) >> 1 == 10 && size == 20)
-	get_timestamp(0);
-      else fseek (ifp, size, SEEK_CUR);
-    }
-  } else if (!memcmp(tag,"IDIT",4) && size < 64) {
-    fread (date, 64, 1, ifp);
-    date[size] = 0;
-    memset (&t, 0, sizeof t);
-    if (sscanf (date, "%*s %s %d %d:%d:%d %d", month, &t.tm_mday,
-	&t.tm_hour, &t.tm_min, &t.tm_sec, &t.tm_year) == 6) {
-      for (i=0; i < 12 && strcasecmp(mon[i],month); i++);
-      t.tm_mon = i;
-      t.tm_year -= 1900;
-      if (mktime(&t) > 0)
-	timestamp = mktime(&t);
-    }
-  } else
-    fseek (ifp, size, SEEK_CUR);
-}
-
-void CLASS parse_smal (int offset, int fsize)
-{
-  int ver;
-
-  fseek (ifp, offset+2, SEEK_SET);
-  order = 0x4949;
-  ver = fgetc(ifp);
-  if (ver == 6)
-    fseek (ifp, 5, SEEK_CUR);
-  if (get4() != fsize) return;
-  if (ver > 6) data_offset = get4();
-  raw_height = height = get2();
-  raw_width  = width  = get2();
-  strcpy (make, "SMaL");
-  sprintf (model, "v%d %dx%d", ver, width, height);
-  if (ver == 6) load_raw = &CLASS smal_v6_load_raw;
-  if (ver == 9) load_raw = &CLASS smal_v9_load_raw;
-}
-
-void CLASS parse_cine()
-{
-  unsigned off_head, off_setup, off_image, i;
-
-  order = 0x4949;
-  fseek (ifp, 4, SEEK_SET);
-  is_raw = get2() == 2;
-  fseek (ifp, 14, SEEK_CUR);
-  is_raw *= get4();
-  off_head = get4();
-  off_setup = get4();
-  off_image = get4();
-  timestamp = get4();
-  if ((i = get4())) timestamp = i;
-  fseek (ifp, off_head+4, SEEK_SET);
-  raw_width = get4();
-  raw_height = get4();
-  switch (get2(),get2()) {
-    case  8:  load_raw = &CLASS eight_bit_load_raw;  break;
-    case 16:  load_raw = &CLASS  unpacked_load_raw;
-  }
-  fseek (ifp, off_setup+792, SEEK_SET);
-  strcpy (make, "CINE");
-  sprintf (model, "%d", get4());
-  fseek (ifp, 12, SEEK_CUR);
-  switch ((i=get4()) & 0xffffff) {
-    case  3:  filters = 0x94949494;  break;
-    case  4:  filters = 0x49494949;  break;
-    default:  is_raw = 0;
-  }
-  fseek (ifp, 72, SEEK_CUR);
-  switch ((get4()+3600) % 360) {
-    case 270:  flip = 4;  break;
-    case 180:  flip = 1;  break;
-    case  90:  flip = 7;  break;
-    case   0:  flip = 2;
-  }
-  cam_mul[0] = getreal(11);
-  cam_mul[2] = getreal(11);
-#ifdef LIBRAW_LIBRARY_BUILD
-  color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-  maximum = ~(-1 << get4());
-  fseek (ifp, 668, SEEK_CUR);
-  shutter = get4()/1000000000.0;
-  fseek (ifp, off_image, SEEK_SET);
-  if (shot_select < is_raw)
-    fseek (ifp, shot_select*8, SEEK_CUR);
-  data_offset  = (INT64) get4() + 8;
-  data_offset += (INT64) get4() << 32;
-}
-#ifdef LIBRAW_LIBRARY_BUILD
-void CLASS adobe_coeff (const char *p_make, const char *p_model)
-#else
-void CLASS adobe_coeff (char *p_make, char *p_model)
-#endif
-{
-  static const struct {
-    const char *prefix;
-    unsigned short t_black, t_maximum;
-    short trans[12];
-  } table[] = {
-    { "Apple QuickTake", 0, 0,		/* DJC */
-	{ 17576,-3191,-3318,5210,6733,-1942,9031,1280,-124 } },
-    { "Canon EOS D2000", 0, 0,
-	{ 24542,-10860,-3401,-1490,11370,-297,2858,-605,3225 } },
-    { "Canon EOS D6000", 0, 0,
-	{ 20482,-7172,-3125,-1033,10410,-285,2542,226,3136 } },
-    { "Canon EOS D30", 0, 0,
-	{ 9805,-2689,-1312,-5803,13064,3068,-2438,3075,8775 } },
-    { "Canon EOS D60", 0, 0xfa0,
-	{ 6188,-1341,-890,-7168,14489,2937,-2640,3228,8483 } },
-    { "Canon EOS 5D Mark II", 0, 0x3cf0,
-	{ 4716,603,-830,-7798,15474,2480,-1496,1937,6651 } },
-    { "Canon EOS 5D", 0, 0xe6c,
-	{ 6347,-479,-972,-8297,15954,2480,-1968,2131,7649 } },
-    { "Canon EOS 10D", 0, 0xfa0,
-	{ 8197,-2000,-1118,-6714,14335,2592,-2536,3178,8266 } },
-    { "Canon EOS 20Da", 0, 0,
-	{ 14155,-5065,-1382,-6550,14633,2039,-1623,1824,6561 } },
-    { "Canon EOS 20D", 0, 0xfff,
-	{ 6599,-537,-891,-8071,15783,2424,-1983,2234,7462 } },
-    { "Canon EOS 30D", 0, 0,
-	{ 6257,-303,-1000,-7880,15621,2396,-1714,1904,7046 } },
-    { "Canon EOS 40D", 0, 0x3f60,
-	{ 6071,-747,-856,-7653,15365,2441,-2025,2553,7315 } },
-    { "Canon EOS 50D", 0, 0x3d93,
-	{ 4920,616,-593,-6493,13964,2784,-1774,3178,7005 } },
-    { "Canon EOS 300D", 0, 0xfa0,
-	{ 8197,-2000,-1118,-6714,14335,2592,-2536,3178,8266 } },
-    { "Canon EOS 350D", 0, 0xfff,
-	{ 6018,-617,-965,-8645,15881,2975,-1530,1719,7642 } },
-    { "Canon EOS 400D", 0, 0xe8e,
-	{ 7054,-1501,-990,-8156,15544,2812,-1278,1414,7796 } },
-    { "Canon EOS 450D", 0, 0x390d,
-	{ 5784,-262,-821,-7539,15064,2672,-1982,2681,7427 } },
-    { "Canon EOS 1000D", 0, 0xe43,
-	{ 6771,-1139,-977,-7818,15123,2928,-1244,1437,7533 } },
-    { "Canon EOS-1Ds Mark III", 0, 0x3bb0,
-	{ 5859,-211,-930,-8255,16017,2353,-1732,1887,7448 } },
-    { "Canon EOS-1Ds Mark II", 0, 0xe80,
-	{ 6517,-602,-867,-8180,15926,2378,-1618,1771,7633 } },
-    { "Canon EOS-1D Mark II N", 0, 0xe80,
-	{ 6240,-466,-822,-8180,15825,2500,-1801,1938,8042 } },
-    { "Canon EOS-1D Mark III", 0, 0x3bb0,
-	{ 6291,-540,-976,-8350,16145,2311,-1714,1858,7326 } },
-    { "Canon EOS-1D Mark II", 0, 0xe80,
-	{ 6264,-582,-724,-8312,15948,2504,-1744,1919,8664 } },
-    { "Canon EOS-1DS", 0, 0xe20,
-	{ 4374,3631,-1743,-7520,15212,2472,-2892,3632,8161 } },
-    { "Canon EOS-1D", 0, 0xe20,
-	{ 6806,-179,-1020,-8097,16415,1687,-3267,4236,7690 } },
-    { "Canon EOS", 0, 0,
-	{ 8197,-2000,-1118,-6714,14335,2592,-2536,3178,8266 } },
-    { "Canon PowerShot A50", 0, 0,
-	{ -5300,9846,1776,3436,684,3939,-5540,9879,6200,-1404,11175,217 } },
-    { "Canon PowerShot A5", 0, 0,
-	{ -4801,9475,1952,2926,1611,4094,-5259,10164,5947,-1554,10883,547 } },
-    { "Canon PowerShot G10", 0, 0,
-	{ 11093,-3906,-1028,-5047,12492,2879,-1003,1750,5561 } },
-    { "Canon PowerShot G1", 0, 0,
-	{ -4778,9467,2172,4743,-1141,4344,-5146,9908,6077,-1566,11051,557 } },
-    { "Canon PowerShot G2", 0, 0,
-	{ 9087,-2693,-1049,-6715,14382,2537,-2291,2819,7790 } },
-    { "Canon PowerShot G3", 0, 0,
-	{ 9212,-2781,-1073,-6573,14189,2605,-2300,2844,7664 } },
-    { "Canon PowerShot G5", 0, 0,
-	{ 9757,-2872,-933,-5972,13861,2301,-1622,2328,7212 } },
-    { "Canon PowerShot G6", 0, 0,
-	{ 9877,-3775,-871,-7613,14807,3072,-1448,1305,7485 } },
-    { "Canon PowerShot G9", 0, 0,
-	{ 7368,-2141,-598,-5621,13254,2625,-1418,1696,5743 } },
-    { "Canon PowerShot Pro1", 0, 0,
-	{ 10062,-3522,-999,-7643,15117,2730,-765,817,7323 } },
-    { "Canon PowerShot Pro70", 34, 0,
-	{ -4155,9818,1529,3939,-25,4522,-5521,9870,6610,-2238,10873,1342 } },
-    { "Canon PowerShot Pro90", 0, 0,
-	{ -4963,9896,2235,4642,-987,4294,-5162,10011,5859,-1770,11230,577 } },
-    { "Canon PowerShot S30", 0, 0,
-	{ 10566,-3652,-1129,-6552,14662,2006,-2197,2581,7670 } },
-    { "Canon PowerShot S40", 0, 0,
-	{ 8510,-2487,-940,-6869,14231,2900,-2318,2829,9013 } },
-    { "Canon PowerShot S45", 0, 0,
-	{ 8163,-2333,-955,-6682,14174,2751,-2077,2597,8041 } },
-    { "Canon PowerShot S50", 0, 0,
-	{ 8882,-2571,-863,-6348,14234,2288,-1516,2172,6569 } },
-    { "Canon PowerShot S60", 0, 0,
-	{ 8795,-2482,-797,-7804,15403,2573,-1422,1996,7082 } },
-    { "Canon PowerShot S70", 0, 0,
-	{ 9976,-3810,-832,-7115,14463,2906,-901,989,7889 } },
-    { "Canon PowerShot A610", 0, 0,	/* DJC */
-	{ 15591,-6402,-1592,-5365,13198,2168,-1300,1824,5075 } },
-    { "Canon PowerShot A620", 0, 0,	/* DJC */
-	{ 15265,-6193,-1558,-4125,12116,2010,-888,1639,5220 } },
-    { "Canon PowerShot A630", 0, 0,	/* DJC */
-	{ 14201,-5308,-1757,-6087,14472,1617,-2191,3105,5348 } },
-    { "Canon PowerShot A640", 0, 0,	/* DJC */
-	{ 13124,-5329,-1390,-3602,11658,1944,-1612,2863,4885 } },
-    { "Canon PowerShot A650", 0, 0,	/* DJC */
-	{ 9427,-3036,-959,-2581,10671,1911,-1039,1982,4430 } },
-    { "Canon PowerShot A720", 0, 0,	/* DJC */
-	{ 14573,-5482,-1546,-1266,9799,1468,-1040,1912,3810 } },
-    { "Canon PowerShot S3 IS", 0, 0,	/* DJC */
-	{ 14062,-5199,-1446,-4712,12470,2243,-1286,2028,4836 } },
-    { "CINE 650", 0, 0,
-	{ 3390,480,-500,-800,3610,340,-550,2336,1192 } },
-    { "CINE 660", 0, 0,
-	{ 3390,480,-500,-800,3610,340,-550,2336,1192 } },
-    { "CINE", 0, 0,
-	{ 20183,-4295,-423,-3940,15330,3985,-280,4870,9800 } },
-    { "Contax N Digital", 0, 0xf1e,
-	{ 7777,1285,-1053,-9280,16543,2916,-3677,5679,7060 } },
-    { "EPSON R-D1", 0, 0,
-	{ 6827,-1878,-732,-8429,16012,2564,-704,592,7145 } },
-    { "FUJIFILM FinePix E550", 0, 0,
-	{ 11044,-3888,-1120,-7248,15168,2208,-1531,2277,8069 } },
-    { "FUJIFILM FinePix E900", 0, 0,
-	{ 9183,-2526,-1078,-7461,15071,2574,-2022,2440,8639 } },
-    { "FUJIFILM FinePix F8", 0, 0,
-	{ 11044,-3888,-1120,-7248,15168,2208,-1531,2277,8069 } },
-    { "FUJIFILM FinePix F7", 0, 0,
-	{ 10004,-3219,-1201,-7036,15047,2107,-1863,2565,7736 } },
-    { "FUJIFILM FinePix S100FS", 514, 0,
-	{ 11521,-4355,-1065,-6524,13767,3058,-1466,1984,6045 } },
-    { "FUJIFILM FinePix S20Pro", 0, 0,
-	{ 10004,-3219,-1201,-7036,15047,2107,-1863,2565,7736 } },
-    { "FUJIFILM FinePix S2Pro", 128, 0,
-	{ 12492,-4690,-1402,-7033,15423,1647,-1507,2111,7697 } },
-    { "FUJIFILM FinePix S3Pro", 0, 0,
-	{ 11807,-4612,-1294,-8927,16968,1988,-2120,2741,8006 } },
-    { "FUJIFILM FinePix S5Pro", 0, 0,
-	{ 12300,-5110,-1304,-9117,17143,1998,-1947,2448,8100 } },
-    { "FUJIFILM FinePix S5000", 0, 0,
-	{ 8754,-2732,-1019,-7204,15069,2276,-1702,2334,6982 } },
-    { "FUJIFILM FinePix S5100", 0, 0x3e00,
-	{ 11940,-4431,-1255,-6766,14428,2542,-993,1165,7421 } },
-    { "FUJIFILM FinePix S5500", 0, 0x3e00,
-	{ 11940,-4431,-1255,-6766,14428,2542,-993,1165,7421 } },
-    { "FUJIFILM FinePix S5200", 0, 0,
-	{ 9636,-2804,-988,-7442,15040,2589,-1803,2311,8621 } },
-    { "FUJIFILM FinePix S5600", 0, 0,
-	{ 9636,-2804,-988,-7442,15040,2589,-1803,2311,8621 } },
-    { "FUJIFILM FinePix S6", 0, 0,
-	{ 12628,-4887,-1401,-6861,14996,1962,-2198,2782,7091 } },
-    { "FUJIFILM FinePix S7000", 0, 0,
-	{ 10190,-3506,-1312,-7153,15051,2238,-2003,2399,7505 } },
-    { "FUJIFILM FinePix S9000", 0, 0,
-	{ 10491,-3423,-1145,-7385,15027,2538,-1809,2275,8692 } },
-    { "FUJIFILM FinePix S9500", 0, 0,
-	{ 10491,-3423,-1145,-7385,15027,2538,-1809,2275,8692 } },
-    { "FUJIFILM FinePix S9100", 0, 0,
-	{ 12343,-4515,-1285,-7165,14899,2435,-1895,2496,8800 } },
-    { "FUJIFILM FinePix S9600", 0, 0,
-	{ 12343,-4515,-1285,-7165,14899,2435,-1895,2496,8800 } },
-    { "FUJIFILM IS-1", 0, 0,
-	{ 21461,-10807,-1441,-2332,10599,1999,289,875,7703 } },
-    { "FUJIFILM IS Pro", 0, 0,
-	{ 12300,-5110,-1304,-9117,17143,1998,-1947,2448,8100 } },
-    { "Imacon Ixpress", 0, 0,		/* DJC */
-	{ 7025,-1415,-704,-5188,13765,1424,-1248,2742,6038 } },
-    { "KODAK NC2000", 0, 0,
-	{ 13891,-6055,-803,-465,9919,642,2121,82,1291 } },
-    { "Kodak DCS315C", 8, 0,
-	{ 17523,-4827,-2510,756,8546,-137,6113,1649,2250 } },
-    { "Kodak DCS330C", 8, 0,
-	{ 20620,-7572,-2801,-103,10073,-396,3551,-233,2220 } },
-    { "KODAK DCS420", 0, 0,
-	{ 10868,-1852,-644,-1537,11083,484,2343,628,2216 } },
-    { "KODAK DCS460", 0, 0,
-	{ 10592,-2206,-967,-1944,11685,230,2206,670,1273 } },
-    { "KODAK EOSDCS1", 0, 0,
-	{ 10592,-2206,-967,-1944,11685,230,2206,670,1273 } },
-    { "KODAK EOSDCS3B", 0, 0,
-	{ 9898,-2700,-940,-2478,12219,206,1985,634,1031 } },
-    { "Kodak DCS520C", 180, 0,
-	{ 24542,-10860,-3401,-1490,11370,-297,2858,-605,3225 } },
-    { "Kodak DCS560C", 188, 0,
-	{ 20482,-7172,-3125,-1033,10410,-285,2542,226,3136 } },
-    { "Kodak DCS620C", 180, 0,
-	{ 23617,-10175,-3149,-2054,11749,-272,2586,-489,3453 } },
-    { "Kodak DCS620X", 185, 0,
-	{ 13095,-6231,154,12221,-21,-2137,895,4602,2258 } },
-    { "Kodak DCS660C", 214, 0,
-	{ 18244,-6351,-2739,-791,11193,-521,3711,-129,2802 } },
-    { "Kodak DCS720X", 0, 0,
-	{ 11775,-5884,950,9556,1846,-1286,-1019,6221,2728 } },
-    { "Kodak DCS760C", 0, 0,
-	{ 16623,-6309,-1411,-4344,13923,323,2285,274,2926 } },
-    { "Kodak DCS Pro SLR", 0, 0,
-	{ 5494,2393,-232,-6427,13850,2846,-1876,3997,5445 } },
-    { "Kodak DCS Pro 14nx", 0, 0,
-	{ 5494,2393,-232,-6427,13850,2846,-1876,3997,5445 } },
-    { "Kodak DCS Pro 14", 0, 0,
-	{ 7791,3128,-776,-8588,16458,2039,-2455,4006,6198 } },
-    { "Kodak ProBack645", 0, 0,
-	{ 16414,-6060,-1470,-3555,13037,473,2545,122,4948 } },
-    { "Kodak ProBack", 0, 0,
-	{ 21179,-8316,-2918,-915,11019,-165,3477,-180,4210 } },
-    { "KODAK P712", 0, 0,
-	{ 9658,-3314,-823,-5163,12695,2768,-1342,1843,6044 } },
-    { "KODAK P850", 0, 0xf7c,
-	{ 10511,-3836,-1102,-6946,14587,2558,-1481,1792,6246 } },
-    { "KODAK P880", 0, 0xfff,
-	{ 12805,-4662,-1376,-7480,15267,2360,-1626,2194,7904 } },
-    { "Leaf CMost", 0, 0,
-	{ 3952,2189,449,-6701,14585,2275,-4536,7349,6536 } },
-    { "Leaf Valeo 6", 0, 0,
-	{ 3952,2189,449,-6701,14585,2275,-4536,7349,6536 } },
-    { "Leaf Aptus 54S", 0, 0,
-	{ 8236,1746,-1314,-8251,15953,2428,-3673,5786,5771 } },
-    { "Leaf Aptus 65", 0, 0,
-	{ 7914,1414,-1190,-8777,16582,2280,-2811,4605,5562 } },
-    { "Leaf Aptus 75", 0, 0,
-	{ 7914,1414,-1190,-8777,16582,2280,-2811,4605,5562 } },
-    { "Leaf", 0, 0,
-	{ 8236,1746,-1314,-8251,15953,2428,-3673,5786,5771 } },
-    { "Mamiya ZD", 0, 0,
-	{ 7645,2579,-1363,-8689,16717,2015,-3712,5941,5961 } },
-    { "Micron 2010", 110, 0,		/* DJC */
-	{ 16695,-3761,-2151,155,9682,163,3433,951,4904 } },
-    { "Minolta DiMAGE 5", 0, 0xf7d,
-	{ 8983,-2942,-963,-6556,14476,2237,-2426,2887,8014 } },
-    { "Minolta DiMAGE 7Hi", 0, 0xf7d,
-	{ 11368,-3894,-1242,-6521,14358,2339,-2475,3056,7285 } },
-    { "Minolta DiMAGE 7", 0, 0xf7d,
-	{ 9144,-2777,-998,-6676,14556,2281,-2470,3019,7744 } },
-    { "Minolta DiMAGE A1", 0, 0xf8b,
-	{ 9274,-2547,-1167,-8220,16323,1943,-2273,2720,8340 } },
-    { "MINOLTA DiMAGE A200", 0, 0,
-	{ 8560,-2487,-986,-8112,15535,2771,-1209,1324,7743 } },
-    { "Minolta DiMAGE A2", 0, 0xf8f,
-	{ 9097,-2726,-1053,-8073,15506,2762,-966,981,7763 } },
-    { "Minolta DiMAGE Z2", 0, 0,	/* DJC */
-	{ 11280,-3564,-1370,-4655,12374,2282,-1423,2168,5396 } },
-    { "MINOLTA DYNAX 5", 0, 0xffb,
-	{ 10284,-3283,-1086,-7957,15762,2316,-829,882,6644 } },
-    { "MINOLTA DYNAX 7", 0, 0xffb,
-	{ 10239,-3104,-1099,-8037,15727,2451,-927,925,6871 } },
-    { "NIKON D100", 0, 0,
-	{ 5902,-933,-782,-8983,16719,2354,-1402,1455,6464 } },
-    { "NIKON D1H", 0, 0,
-	{ 7577,-2166,-926,-7454,15592,1934,-2377,2808,8606 } },
-    { "NIKON D1X", 0, 0,
-	{ 7702,-2245,-975,-9114,17242,1875,-2679,3055,8521 } },
-    { "NIKON D1", 0, 0, /* multiplied by 2.218750, 1.0, 1.148438 */
-	{ 16772,-4726,-2141,-7611,15713,1972,-2846,3494,9521 } },
-    { "NIKON D2H", 0, 0,
-	{ 5710,-901,-615,-8594,16617,2024,-2975,4120,6830 } },
-    { "NIKON D2X", 0, 0,
-	{ 10231,-2769,-1255,-8301,15900,2552,-797,680,7148 } },
-    { "NIKON D40X", 0, 0,
-	{ 8819,-2543,-911,-9025,16928,2151,-1329,1213,8449 } },
-    { "NIKON D40", 0, 0,
-	{ 6992,-1668,-806,-8138,15748,2543,-874,850,7897 } },
-    { "NIKON D50", 0, 0,
-	{ 7732,-2422,-789,-8238,15884,2498,-859,783,7330 } },
-    { "NIKON D60", 0, 0,
-	{ 8736,-2458,-935,-9075,16894,2251,-1354,1242,8263 } },
-    { "NIKON D700", 0, 0,
-	{ 8139,-2171,-663,-8747,16541,2295,-1925,2008,8093 } },
-    { "NIKON D70", 0, 0,
-	{ 7732,-2422,-789,-8238,15884,2498,-859,783,7330 } },
-    { "NIKON D80", 0, 0,
-	{ 8629,-2410,-883,-9055,16940,2171,-1490,1363,8520 } },
-    { "NIKON D90", 0, 0xf00,
-	{ 7309,-1403,-519,-8474,16008,2622,-2434,2826,8064 } },
-    { "NIKON D200", 0, 0xfbc,
-	{ 8367,-2248,-763,-8758,16447,2422,-1527,1550,8053 } },
-    { "NIKON D300", 0, 0,
-	{ 9030,-1992,-715,-8465,16302,2255,-2689,3217,8069 } },
-    { "NIKON D3", 0, 0,
-	{ 8139,-2171,-663,-8747,16541,2295,-1925,2008,8093 } },
-    { "NIKON E950", 0, 0x3dd,		/* DJC */
-	{ -3746,10611,1665,9621,-1734,2114,-2389,7082,3064,3406,6116,-244 } },
-    { "NIKON E995", 0, 0,	/* copied from E5000 */
-	{ -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } },
-    { "NIKON E2100", 0, 0,	/* copied from Z2, new white balance */
-	{ 13142,-4152,-1596,-4655,12374,2282,-1769,2696,6711} },
-    { "NIKON E2500", 0, 0,
-	{ -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } },
-    { "NIKON E4300", 0, 0,	/* copied from Minolta DiMAGE Z2 */
-	{ 11280,-3564,-1370,-4655,12374,2282,-1423,2168,5396 } },
-    { "NIKON E4500", 0, 0,
-	{ -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } },
-    { "NIKON E5000", 0, 0,
-	{ -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } },
-    { "NIKON E5400", 0, 0,
-	{ 9349,-2987,-1001,-7919,15766,2266,-2098,2680,6839 } },
-    { "NIKON E5700", 0, 0,
-	{ -5368,11478,2368,5537,-113,3148,-4969,10021,5782,778,9028,211 } },
-    { "NIKON E8400", 0, 0,
-	{ 7842,-2320,-992,-8154,15718,2599,-1098,1342,7560 } },
-    { "NIKON E8700", 0, 0,
-	{ 8489,-2583,-1036,-8051,15583,2643,-1307,1407,7354 } },
-    { "NIKON E8800", 0, 0,
-	{ 7971,-2314,-913,-8451,15762,2894,-1442,1520,7610 } },
-    { "NIKON COOLPIX P6000", 0, 0,
-	{ 9698,-3367,-914,-4706,12584,2368,-837,968,5801 } },
-    { "OLYMPUS C5050", 0, 0,
-	{ 10508,-3124,-1273,-6079,14294,1901,-1653,2306,6237 } },
-    { "OLYMPUS C5060", 0, 0,
-	{ 10445,-3362,-1307,-7662,15690,2058,-1135,1176,7602 } },
-    { "OLYMPUS C7070", 0, 0,
-	{ 10252,-3531,-1095,-7114,14850,2436,-1451,1723,6365 } },
-    { "OLYMPUS C70", 0, 0,
-	{ 10793,-3791,-1146,-7498,15177,2488,-1390,1577,7321 } },
-    { "OLYMPUS C80", 0, 0,
-	{ 8606,-2509,-1014,-8238,15714,2703,-942,979,7760 } },
-    { "OLYMPUS E-10", 0, 0xffc0,
-	{ 12745,-4500,-1416,-6062,14542,1580,-1934,2256,6603 } },
-    { "OLYMPUS E-1", 0, 0xfff0,
-	{ 11846,-4767,-945,-7027,15878,1089,-2699,4122,8311 } },
-    { "OLYMPUS E-20", 0, 0xffc0,
-	{ 13173,-4732,-1499,-5807,14036,1895,-2045,2452,7142 } },
-    { "OLYMPUS E-300", 0, 0,
-	{ 7828,-1761,-348,-5788,14071,1830,-2853,4518,6557 } },
-    { "OLYMPUS E-330", 0, 0,
-	{ 8961,-2473,-1084,-7979,15990,2067,-2319,3035,8249 } },
-    { "OLYMPUS E-3", 0, 0xf99,
-	{ 9487,-2875,-1115,-7533,15606,2010,-1618,2100,7389 } },
-    { "OLYMPUS E-400", 0, 0xfff0,
-	{ 6169,-1483,-21,-7107,14761,2536,-2904,3580,8568 } },
-    { "OLYMPUS E-410", 0, 0xf6a,
-	{ 8856,-2582,-1026,-7761,15766,2082,-2009,2575,7469 } },
-    { "OLYMPUS E-420", 0, 0xfd7,
-	{ 8746,-2425,-1095,-7594,15612,2073,-1780,2309,7416 } },
-    { "OLYMPUS E-500", 0, 0,
-	{ 8136,-1968,-299,-5481,13742,1871,-2556,4205,6630 } },
-    { "OLYMPUS E-510", 0, 0xf6a,
-	{ 8785,-2529,-1033,-7639,15624,2112,-1783,2300,7817 } },
-    { "OLYMPUS E-520", 0, 0xfd2,
-	{ 8344,-2322,-1020,-7596,15635,2048,-1748,2269,7287 } },
-    { "OLYMPUS SP350", 0, 0,
-	{ 12078,-4836,-1069,-6671,14306,2578,-786,939,7418 } },
-    { "OLYMPUS SP3", 0, 0,
-	{ 11766,-4445,-1067,-6901,14421,2707,-1029,1217,7572 } },
-    { "OLYMPUS SP500UZ", 0, 0xfff,
-	{ 9493,-3415,-666,-5211,12334,3260,-1548,2262,6482 } },
-    { "OLYMPUS SP510UZ", 0, 0xffe,
-	{ 10593,-3607,-1010,-5881,13127,3084,-1200,1805,6721 } },
-    { "OLYMPUS SP550UZ", 0, 0xffe,
-	{ 11597,-4006,-1049,-5432,12799,2957,-1029,1750,6516 } },
-    { "OLYMPUS SP560UZ", 0, 0xff9,
-	{ 10915,-3677,-982,-5587,12986,2911,-1168,1968,6223 } },
-    { "OLYMPUS SP570UZ", 0, 0,
-	{ 11522,-4044,-1146,-4736,12172,2904,-988,1829,6039 } },
-    { "PENTAX *ist DL2", 0, 0,
-	{ 10504,-2438,-1189,-8603,16207,2531,-1022,863,12242 } },
-    { "PENTAX *ist DL", 0, 0,
-	{ 10829,-2838,-1115,-8339,15817,2696,-837,680,11939 } },
-    { "PENTAX *ist DS2", 0, 0,
-	{ 10504,-2438,-1189,-8603,16207,2531,-1022,863,12242 } },
-    { "PENTAX *ist DS", 0, 0,
-	{ 10371,-2333,-1206,-8688,16231,2602,-1230,1116,11282 } },
-    { "PENTAX *ist D", 0, 0,
-	{ 9651,-2059,-1189,-8881,16512,2487,-1460,1345,10687 } },
-    { "PENTAX K10D", 0, 0,
-	{ 9566,-2863,-803,-7170,15172,2112,-818,803,9705 } },
-    { "PENTAX K1", 0, 0,
-	{ 11095,-3157,-1324,-8377,15834,2720,-1108,947,11688 } },
-    { "PENTAX K20D", 0, 0,
-	{ 9427,-2714,-868,-7493,16092,1373,-2199,3264,7180 } },
-    { "PENTAX K200D", 0, 0,
-	{ 9186,-2678,-907,-8693,16517,2260,-1129,1094,8524 } },
-    { "PENTAX K2000", 0, 0,
-	{ 11057,-3604,-1155,-5152,13046,2329,-282,375,8104 } },
-    { "PENTAX K-m", 0, 0,
-	{ 11057,-3604,-1155,-5152,13046,2329,-282,375,8104 } },
-    { "Panasonic DMC-FZ8", 0, 0xf7f0,
-	{ 8986,-2755,-802,-6341,13575,3077,-1476,2144,6379 } },
-    { "Panasonic DMC-FZ18", 0, 0,
-	{ 9932,-3060,-935,-5809,13331,2753,-1267,2155,5575 } },
-    { "Panasonic DMC-FZ28", 15, 0xfff,
-	{ 10109,-3488,-993,-5412,12812,2916,-1305,2140,5543 } },
-    { "Panasonic DMC-FZ30", 0, 0xf94c,
-	{ 10976,-4029,-1141,-7918,15491,2600,-1670,2071,8246 } },
-    { "Panasonic DMC-FZ50", 0, 0xfff0,	/* aka "LEICA V-LUX1" */
-	{ 7906,-2709,-594,-6231,13351,3220,-1922,2631,6537 } },
-    { "Panasonic DMC-L10", 15, 0xf96,
-	{ 8025,-1942,-1050,-7920,15904,2100,-2456,3005,7039 } },
-    { "Panasonic DMC-L1", 0, 0xf7fc,	/* aka "LEICA DIGILUX 3" */
-	{ 8054,-1885,-1025,-8349,16367,2040,-2805,3542,7629 } },
-    { "Panasonic DMC-LC1", 0, 0,	/* aka "LEICA DIGILUX 2" */
-	{ 11340,-4069,-1275,-7555,15266,2448,-2960,3426,7685 } },
-    { "Panasonic DMC-LX1", 0, 0xf7f0,	/* aka "LEICA D-LUX2" */
-	{ 10704,-4187,-1230,-8314,15952,2501,-920,945,8927 } },
-    { "Panasonic DMC-LX2", 0, 0,	/* aka "LEICA D-LUX3" */
-	{ 8048,-2810,-623,-6450,13519,3272,-1700,2146,7049 } },
-    { "Panasonic DMC-LX3", 15, 0xfff,	/* aka "LEICA D-LUX4" */
-	{ 8128,-2668,-655,-6134,13307,3161,-1782,2568,6083 } },
-    { "Panasonic DMC-FX150", 15, 0xfff,
-	{ 9082,-2907,-925,-6119,13377,3058,-1797,2641,5609 } },
-    { "Panasonic DMC-G1", 15, 0xfff,
-	{ 8199,-2065,-1056,-8124,16156,2033,-2458,3022,7220 } },
-    { "Phase One H 20", 0, 0,		/* DJC */
-	{ 1313,1855,-109,-6715,15908,808,-327,1840,6020 } },
-    { "Phase One P 2", 0, 0,
-	{ 2905,732,-237,-8134,16626,1476,-3038,4253,7517 } },
-    { "Phase One P 30", 0, 0,
-	{ 4516,-245,-37,-7020,14976,2173,-3206,4671,7087 } },
-    { "Phase One P 45", 0, 0,
-	{ 5053,-24,-117,-5684,14076,1702,-2619,4492,5849 } },
-    { "SAMSUNG GX-1", 0, 0,
-	{ 10504,-2438,-1189,-8603,16207,2531,-1022,863,12242 } },
-    { "Sinar", 0, 0,			/* DJC */
-	{ 16442,-2956,-2422,-2877,12128,750,-1136,6066,4559 } },
-    { "SONY DSC-F828", 491, 0,
-	{ 7924,-1910,-777,-8226,15459,2998,-1517,2199,6818,-7242,11401,3481 } },
-    { "SONY DSC-R1", 512, 0,
-	{ 8512,-2641,-694,-8042,15670,2526,-1821,2117,7414 } },
-    { "SONY DSC-V3", 0, 0,
-	{ 7511,-2571,-692,-7894,15088,3060,-948,1111,8128 } },
-    { "SONY DSLR-A100", 0, 0xfeb,
-	{ 9437,-2811,-774,-8405,16215,2290,-710,596,7181 } },
-    { "SONY DSLR-A200", 0, 0,
-	{ 9847,-3091,-928,-8485,16345,2225,-715,595,7103 } },
-    { "SONY DSLR-A300", 0, 0,
-	{ 9847,-3091,-928,-8485,16345,2225,-715,595,7103 } },
-    { "SONY DSLR-A350", 0, 0xffc,
-	{ 6038,-1484,-578,-9146,16746,2513,-875,746,7217 } },
-    { "SONY DSLR-A700", 254, 0x1ffe,
-	{ 5775,-805,-359,-8574,16295,2391,-1943,2341,7249 } },
-    { "SONY DSLR-A900", 254, 0x1ffe,
-	{ 5209,-1072,-397,-8845,16120,2919,-1618,1803,8654 } }
-  };
-  double cam_xyz[4][3];
-  char name[130];
-  int i, j;
-
-  sprintf (name, "%s %s", p_make, p_model);
-  for (i=0; i < sizeof table / sizeof *table; i++)
-    if (!strncmp (name, table[i].prefix, strlen(table[i].prefix))) {
-      if (table[i].t_black)   black   = (ushort) table[i].t_black;
-      if (table[i].t_maximum) maximum = (ushort) table[i].t_maximum;
-      for (j=0; j < 12; j++)
-#ifdef LIBRAW_LIBRARY_BUILD
-          imgdata.color.cam_xyz[0][j] = 
-#endif
-	cam_xyz[0][j] = table[i].trans[j] / 10000.0;
-      cam_xyz_coeff (cam_xyz);
-      break;
-    }
-}
-
-void CLASS simple_coeff (int index)
-{
-  static const float table[][12] = {
-  /* index 0 -- all Foveon cameras */
-  { 1.4032,-0.2231,-0.1016,-0.5263,1.4816,0.017,-0.0112,0.0183,0.9113 },
-  /* index 1 -- Kodak DC20 and DC25 */
-  { 2.25,0.75,-1.75,-0.25,-0.25,0.75,0.75,-0.25,-0.25,-1.75,0.75,2.25 },
-  /* index 2 -- Logitech Fotoman Pixtura */
-  { 1.893,-0.418,-0.476,-0.495,1.773,-0.278,-1.017,-0.655,2.672 },
-  /* index 3 -- Nikon E880, E900, and E990 */
-  { -1.936280,  1.800443, -1.448486,  2.584324,
-     1.405365, -0.524955, -0.289090,  0.408680,
-    -1.204965,  1.082304,  2.941367, -1.818705 }
-  };
-  int i, c;
-
-  for (raw_color = i=0; i < 3; i++)
-    FORCC rgb_cam[i][c] = table[index][i*colors+c];
-#ifdef LIBRAW_LIBRARY_BUILD
-  color_flags.rgb_cam_state = LIBRAW_COLORSTATE_CALCULATED;
-#endif
-}
-
-short CLASS guess_byte_order (int words)
-{
-  uchar test[4][2];
-  int t=2, msb;
-  double diff, sum[2] = {0,0};
-
-  fread (test[0], 2, 2, ifp);
-  for (words-=2; words--; ) {
-    fread (test[t], 2, 1, ifp);
-    for (msb=0; msb < 2; msb++) {
-      diff = (test[t^2][msb] << 8 | test[t^2][!msb])
-	   - (test[t  ][msb] << 8 | test[t  ][!msb]);
-      sum[msb] += diff*diff;
-    }
-    t = (t+1) & 3;
-  }
-  return sum[0] < sum[1] ? 0x4d4d : 0x4949;
-}
-
-/*
-   Identify which camera created this file, and set global variables
-   accordingly.
- */
-void CLASS identify()
-{
-  char head[32], *cp;
-  unsigned hlen, fsize, i, c, is_canon;
-  struct jhead jh;
-  static const struct {
-    int fsize;
-    char t_make[12], t_model[19], withjpeg;
-  } table[] = {
-    {    62464, "Kodak",    "DC20"            ,0 },
-    {   124928, "Kodak",    "DC20"            ,0 },
-    {  1652736, "Kodak",    "DCS200"          ,0 },
-    {  4159302, "Kodak",    "C330"            ,0 },
-    {  4162462, "Kodak",    "C330"            ,0 },
-    {   460800, "Kodak",    "C603v"           ,0 },
-    {   614400, "Kodak",    "C603v"           ,0 },
-    {  6163328, "Kodak",    "C603"            ,0 },
-    {  6166488, "Kodak",    "C603"            ,0 },
-    {  9116448, "Kodak",    "C603y"           ,0 },
-    {   311696, "ST Micro", "STV680 VGA"      ,0 },  /* SPYz */
-    {   614400, "Kodak",    "KAI-0340"        ,0 },
-    {   787456, "Creative", "PC-CAM 600"      ,0 },
-    {  1138688, "Minolta",  "RD175"           ,0 },
-    {  3840000, "Foculus",  "531C"            ,0 },
-    {   786432, "AVT",      "F-080C"          ,0 },
-    {  1447680, "AVT",      "F-145C"          ,0 },
-    {  1920000, "AVT",      "F-201C"          ,0 },
-    {  5067304, "AVT",      "F-510C"          ,0 },
-    { 10134608, "AVT",      "F-510C"          ,0 },
-    { 16157136, "AVT",      "F-810C"          ,0 },
-    {  1409024, "Sony",     "XCD-SX910CR"     ,0 },
-    {  2818048, "Sony",     "XCD-SX910CR"     ,0 },
-    {  3884928, "Micron",   "2010"            ,0 },
-    {  6624000, "Pixelink", "A782"            ,0 },
-    { 13248000, "Pixelink", "A782"            ,0 },
-    {  6291456, "RoverShot","3320AF"          ,0 },
-    {  6553440, "Canon",    "PowerShot A460"  ,0 },
-    {  6653280, "Canon",    "PowerShot A530"  ,0 },
-    {  6573120, "Canon",    "PowerShot A610"  ,0 },
-    {  9219600, "Canon",    "PowerShot A620"  ,0 },
-    { 10341600, "Canon",    "PowerShot A720"  ,0 },
-    { 10383120, "Canon",    "PowerShot A630"  ,0 },
-    { 12945240, "Canon",    "PowerShot A640"  ,0 },
-    { 15636240, "Canon",    "PowerShot A650"  ,0 },
-    {  5298000, "Canon",    "PowerShot SD300" ,0 },
-    {  7710960, "Canon",    "PowerShot S3 IS" ,0 },
-    {  5939200, "OLYMPUS",  "C770UZ"          ,0 },
-    {  1581060, "NIKON",    "E900"            ,1 },  /* or E900s,E910 */
-    {  2465792, "NIKON",    "E950"            ,1 },  /* or E800,E700 */
-    {  2940928, "NIKON",    "E2100"           ,1 },  /* or E2500 */
-    {  4771840, "NIKON",    "E990"            ,1 },  /* or E995, Oly C3030Z */
-    {  4775936, "NIKON",    "E3700"           ,1 },  /* or Optio 33WR */
-    {  5869568, "NIKON",    "E4300"           ,1 },  /* or DiMAGE Z2 */
-    {  5865472, "NIKON",    "E4500"           ,1 },
-    {  7438336, "NIKON",    "E5000"           ,1 },  /* or E5700 */
-    {  8998912, "NIKON",    "COOLPIX S6"      ,1 },
-    {  1976352, "CASIO",    "QV-2000UX"       ,1 },
-    {  3217760, "CASIO",    "QV-3*00EX"       ,1 },
-    {  6218368, "CASIO",    "QV-5700"         ,1 },
-    {  6054400, "CASIO",    "QV-R41"          ,1 },
-    {  7530816, "CASIO",    "QV-R51"          ,1 },
-    {  7684000, "CASIO",    "QV-4000"         ,1 },
-    {  4948608, "CASIO",    "EX-S100"         ,1 },
-    {  7542528, "CASIO",    "EX-Z50"          ,1 },
-    {  7753344, "CASIO",    "EX-Z55"          ,1 },
-    {  7426656, "CASIO",    "EX-P505"         ,1 },
-    {  9313536, "CASIO",    "EX-P600"         ,1 },
-    { 10979200, "CASIO",    "EX-P700"         ,1 },
-    {  3178560, "PENTAX",   "Optio S"         ,1 },
-    {  4841984, "PENTAX",   "Optio S"         ,1 },
-    {  6114240, "PENTAX",   "Optio S4"        ,1 },  /* or S4i, CASIO EX-Z4 */
-    { 10702848, "PENTAX",   "Optio 750Z"      ,1 },
-    { 16098048, "SAMSUNG",  "S85"             ,1 },
-    { 16215552, "SAMSUNG",  "S85"             ,1 },
-    { 12582980, "Sinar",    ""                ,0 },
-    { 33292868, "Sinar",    ""                ,0 },
-    { 44390468, "Sinar",    ""                ,0 } };
-  static const char *corp[] =
-    { "Canon", "NIKON", "EPSON", "KODAK", "Kodak", "OLYMPUS", "PENTAX",
-      "MINOLTA", "Minolta", "Konica", "CASIO", "Sinar", "Phase One",
-      "SAMSUNG", "Mamiya" };
-
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_IDENTIFY,0,2);
-#endif
-
-  tiff_flip = flip = filters = -1;	/* 0 is valid, so -1 is unknown */
-  raw_height = raw_width = fuji_width = fuji_layout = cr2_slice[0] = 0;
-  maximum = height = width = top_margin = left_margin = 0;
-  cdesc[0] = desc[0] = artist[0] = make[0] = model[0] = model2[0] = 0;
-  iso_speed = shutter = aperture = focal_len = unique_id = 0;
-  memset (gpsdata, 0, sizeof gpsdata);
-  memset (white, 0, sizeof white);
-  thumb_offset = thumb_length = thumb_width = thumb_height = 0;
-  load_raw = thumb_load_raw = 0;
-  write_thumb = &CLASS jpeg_thumb;
-  data_offset = meta_length = tiff_bps = tiff_compress = 0;
-  kodak_cbpp = zero_after_ff = dng_version = load_flags = 0;
-  timestamp = shot_order = tiff_samples = black = is_foveon = 0;
-  mix_green = profile_length = data_error = zero_is_bad = 0;
-  pixel_aspect = is_raw = raw_color = use_gamma = 1;
-  tile_width = tile_length = INT_MAX;
-  for (i=0; i < 4; i++) {
-    cam_mul[i] = i == 1;
-    pre_mul[i] = i < 3;
-    FORC3 cmatrix[c][i] = 0;
-    FORC3 rgb_cam[c][i] = c == i;
-  }
-#ifdef LIBRAW_LIBRARY_BUILD
-  color_flags.cmatrix_state = LIBRAW_COLORSTATE_INIT;
-  color_flags.rgb_cam_state = LIBRAW_COLORSTATE_INIT;
-  color_flags.pre_mul_state = LIBRAW_COLORSTATE_INIT;
-  color_flags.cam_mul_state = LIBRAW_COLORSTATE_INIT;
-#endif
-  colors = 3;
-  tiff_bps = 12;
-  for (i=0; i < 0x4000; i++) curve[i] = i;
-#ifdef LIBRAW_LIBRARY_BUILD
-  color_flags.curve_state = LIBRAW_COLORSTATE_INIT;
-#endif
-
-  order = get2();
-  hlen = get4();
-  fseek (ifp, 0, SEEK_SET);
-  fread (head, 1, 32, ifp);
-  fseek (ifp, 0, SEEK_END);
-  fsize = ftell(ifp);
-  if ((cp = (char *) memmem (head, 32, "MMMM", 4)) ||
-      (cp = (char *) memmem (head, 32, "IIII", 4))) {
-    parse_phase_one (cp-head);
-    if (cp-head) parse_tiff(0);
-  } else if (order == 0x4949 || order == 0x4d4d) {
-    if (!memcmp (head+6,"HEAPCCDR",8)) {
-      data_offset = hlen;
-      parse_ciff (hlen, fsize - hlen);
-    } else {
-      parse_tiff(0);
-    }
-  } else if (!memcmp (head,"\xff\xd8\xff\xe1",4) &&
-	     !memcmp (head+6,"Exif",4)) {
-    fseek (ifp, 4, SEEK_SET);
-    data_offset = 4 + get2();
-    fseek (ifp, data_offset, SEEK_SET);
-    if (fgetc(ifp) != 0xff)
-      parse_tiff(12);
-    thumb_offset = 0;
-  } else if (!memcmp (head+25,"ARECOYK",7)) {
-    strcpy (make, "Contax");
-    strcpy (model,"N Digital");
-    fseek (ifp, 33, SEEK_SET);
-    get_timestamp(1);
-    fseek (ifp, 60, SEEK_SET);
-    FORC4 cam_mul[c ^ (c >> 1)] = get4();
-#ifdef LIBRAW_LIBRARY_BUILD
-    color_flags.cam_mul_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-  } else if (!strcmp (head, "PXN")) {
-    strcpy (make, "Logitech");
-    strcpy (model,"Fotoman Pixtura");
-  } else if (!strcmp (head, "qktk")) {
-    strcpy (make, "Apple");
-    strcpy (model,"QuickTake 100");
-  } else if (!strcmp (head, "qktn")) {
-    strcpy (make, "Apple");
-    strcpy (model,"QuickTake 150");
-  } else if (!memcmp (head,"FUJIFILM",8)) {
-    fseek (ifp, 84, SEEK_SET);
-    thumb_offset = get4();
-    thumb_length = get4();
-    fseek (ifp, 92, SEEK_SET);
-    parse_fuji (get4());
-    if (thumb_offset > 120) {
-      fseek (ifp, 120, SEEK_SET);
-      is_raw += (i = get4()) && 1;
-      if (is_raw == 2 && shot_select)
-	parse_fuji (i);
-    }
-    fseek (ifp, 100, SEEK_SET);
-    data_offset = get4();
-    parse_tiff (thumb_offset+12);
-  } else if (!memcmp (head,"RIFF",4)) {
-    fseek (ifp, 0, SEEK_SET);
-    parse_riff();
-  } else if (!memcmp (head,"\0\001\0\001\0@",6)) {
-    fseek (ifp, 6, SEEK_SET);
-    fread (make, 1, 8, ifp);
-    fread (model, 1, 8, ifp);
-    fread (model2, 1, 16, ifp);
-    data_offset = get2();
-    get2();
-    raw_width = get2();
-    raw_height = get2();
-    load_raw = &CLASS nokia_load_raw;
-    filters = 0x61616161;
-  } else if (!memcmp (head,"DSC-Image",9))
-    parse_rollei();
-  else if (!memcmp (head,"PWAD",4))
-    parse_sinar_ia();
-  else if (!memcmp (head,"\0MRM",4))
-    parse_minolta(0);
-  else if (!memcmp (head,"FOVb",4))
-    parse_foveon();
-  else if (!memcmp (head,"CI",2))
-    parse_cine();
-  else
-    for (i=0; i < sizeof table / sizeof *table; i++)
-      if (fsize == table[i].fsize) {
-	strcpy (make,  table[i].t_make );
-	strcpy (model, table[i].t_model);
-	if (table[i].withjpeg)
-	  parse_external_jpeg();
-      }
-  if (make[0] == 0) parse_smal (0, fsize);
-  if (make[0] == 0) parse_jpeg (is_raw = 0);
-
-  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;
-  cp = make + strlen(make);		/* Remove trailing spaces */
-  while (*--cp == ' ') *cp = 0;
-  cp = model + strlen(model);
-  while (*--cp == ' ') *cp = 0;
-  i = strlen(make);			/* Remove make from model */
-  if (!strncasecmp (model, make, i) && model[i++] == ' ')
-    memmove (model, model+i, 64-i);
-  if (!strncmp (model,"Digital Camera ",15))
-    strcpy (model, model+15);
-  desc[511] = artist[63] = make[63] = model[63] = model2[63] = 0;
-  if (!is_raw) goto notraw;
-
-  if (!maximum) maximum = (1 << tiff_bps) - 1;
-  if (!height) height = raw_height;
-  if (!width)  width  = raw_width;
-  if (fuji_width) {
-    width = height + fuji_width;
-    height = width - 1;
-    pixel_aspect = 1;
-  }
-  if (height == 2624 && width == 3936)	/* Pentax K10D and Samsung GX10 */
-    { height  = 2616;   width  = 3896; }
-  if (height == 3136 && width == 4864)	/* Pentax K20D */
-    { height  = 3124;   width  = 4688; }
-  if (height == 3014 && width == 4096)	/* Ricoh GX200 */
-			width  = 4014;
-  if (dng_version) {
-    if (filters == UINT_MAX) filters = 0;
-    if (filters) is_raw = tiff_samples;
-    else	 colors = tiff_samples;
-    if (tiff_compress == 1)
-      load_raw = &CLASS adobe_dng_load_raw_nc;
-    if (tiff_compress == 7)
-      load_raw = &CLASS adobe_dng_load_raw_lj;
-    goto dng_skip;
-  }
-  if ((is_canon = !strcmp(make,"Canon")))
-    load_raw = memcmp (head+6,"HEAPCCDR",8) ?
-	&CLASS lossless_jpeg_load_raw : &CLASS canon_compressed_load_raw;
-  if (!strcmp(make,"NIKON")) {
-    if (!load_raw)
-      load_raw = &CLASS packed_12_load_raw;
-    if (model[0] == 'E')
-      load_flags |= !data_offset << 2 | 2;
-  }
-  if (!strcmp(make,"CASIO")) {
-    load_raw = &CLASS packed_12_load_raw;
-    maximum = 0xf7f;
-  }
-
-/* Set parameters based on camera name (for non-DNG files). */
-
-  if (is_foveon) {
-    if (height*2 < width) pixel_aspect = 0.5;
-    if (height   > width) pixel_aspect = 2;
-    filters = 0;
-    load_raw = &CLASS foveon_load_raw;
-    simple_coeff(0);
-  } else if (is_canon && tiff_bps == 15) {
-    switch (width) {
-      case 3344: width -= 66;
-      case 3872: width -= 6;
-    }
-    filters = 0;
-    load_raw = &CLASS canon_sraw_load_raw;
-  } else if (!strcmp(model,"PowerShot 600")) {
-    height = 613;
-    width  = 854;
-    raw_width = 896;
-    pixel_aspect = 607/628.0;
-    colors = 4;
-    filters = 0xe1e4e1e4;
-    load_raw = &CLASS canon_600_load_raw;
-  } else if (!strcmp(model,"PowerShot A5") ||
-	     !strcmp(model,"PowerShot A5 Zoom")) {
-    height = 773;
-    width  = 960;
-    raw_width = 992;
-    pixel_aspect = 256/235.0;
-    colors = 4;
-    filters = 0x1e4e1e4e;
-    load_raw = &CLASS canon_a5_load_raw;
-  } else if (!strcmp(model,"PowerShot A50")) {
-    height =  968;
-    width  = 1290;
-    raw_width = 1320;
-    colors = 4;
-    filters = 0x1b4e4b1e;
-    load_raw = &CLASS canon_a5_load_raw;
-  } else if (!strcmp(model,"PowerShot Pro70")) {
-    height = 1024;
-    width  = 1552;
-    colors = 4;
-    filters = 0x1e4b4e1b;
-    load_raw = &CLASS canon_a5_load_raw;
-  } else if (!strcmp(model,"PowerShot SD300")) {
-    height = 1752;
-    width  = 2344;
-    raw_height = 1766;
-    raw_width  = 2400;
-    top_margin  = 12;
-    left_margin = 12;
-    load_raw = &CLASS canon_a5_load_raw;
-  } else if (!strcmp(model,"PowerShot A460")) {
-    height = 1960;
-    width  = 2616;
-    raw_height = 1968;
-    raw_width  = 2664;
-    top_margin  = 4;
-    left_margin = 4;
-    load_raw = &CLASS canon_a5_load_raw;
-  } else if (!strcmp(model,"PowerShot A530")) {
-    height = 1984;
-    width  = 2620;
-    raw_height = 1992;
-    raw_width  = 2672;
-    top_margin  = 6;
-    left_margin = 10;
-    load_raw = &CLASS canon_a5_load_raw;
-    raw_color = 0;
-  } else if (!strcmp(model,"PowerShot A610")) {
-    if (canon_s2is()) strcpy (model+10, "S2 IS");
-    height = 1960;
-    width  = 2616;
-    raw_height = 1968;
-    raw_width  = 2672;
-    top_margin  = 8;
-    left_margin = 12;
-    load_raw = &CLASS canon_a5_load_raw;
-  } else if (!strcmp(model,"PowerShot A620")) {
-    height = 2328;
-    width  = 3112;
-    raw_height = 2340;
-    raw_width  = 3152;
-    top_margin  = 12;
-    left_margin = 36;
-    load_raw = &CLASS canon_a5_load_raw;
-  } else if (!strcmp(model,"PowerShot A720")) {
-    height = 2472;
-    width  = 3298;
-    raw_height = 2480;
-    raw_width  = 3336;
-    top_margin  = 5;
-    left_margin = 6;
-    load_raw = &CLASS canon_a5_load_raw;
-  } else if (!strcmp(model,"PowerShot A630")) {
-    height = 2472;
-    width  = 3288;
-    raw_height = 2484;
-    raw_width  = 3344;
-    top_margin  = 6;
-    left_margin = 12;
-    load_raw = &CLASS canon_a5_load_raw;
-  } else if (!strcmp(model,"PowerShot A640")) {
-    height = 2760;
-    width  = 3672;
-    raw_height = 2772;
-    raw_width  = 3736;
-    top_margin  = 6;
-    left_margin = 12;
-    load_raw = &CLASS canon_a5_load_raw;
-  } else if (!strcmp(model,"PowerShot A650")) {
-    height = 3024;
-    width  = 4032;
-    raw_height = 3048;
-    raw_width  = 4104;
-    top_margin  = 12;
-    left_margin = 48;
-    load_raw = &CLASS canon_a5_load_raw;
-  } else if (!strcmp(model,"PowerShot S3 IS")) {
-    height = 2128;
-    width  = 2840;
-    raw_height = 2136;
-    raw_width  = 2888;
-    top_margin  = 8;
-    left_margin = 44;
-    load_raw = &CLASS canon_a5_load_raw;
-  } 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;
-    if (unique_id == 0x80000170)
-      adobe_coeff ("Canon","EOS 300D");
-  } 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 (is_canon && raw_width == 3516) {
-    top_margin  = 14;
-    left_margin = 42;
-    if (unique_id == 0x80000189)
-      adobe_coeff ("Canon","EOS 350D");
-    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 == 3944) {
-    height = 2602;
-    width  = 3908;
-    top_margin  = 18;
-    left_margin = 30;
-  } else if (is_canon && raw_width == 3948) {
-    top_margin  = 18;
-    left_margin = 42;
-    height -= 2;
-    if (unique_id == 0x80000236)
-      adobe_coeff ("Canon","EOS 400D");
-    if (unique_id == 0x80000254)
-      adobe_coeff ("Canon","EOS 1000D");
-    goto canon_cr2;
-  } else if (is_canon && raw_width == 3984) {
-    top_margin  = 20;
-    left_margin = 76;
-    height -= 2;
-    goto canon_cr2;
-  } else if (is_canon && raw_width == 4104) {
-    height = 3024;
-    width  = 4032;
-    top_margin  = 12;
-    left_margin = 48;
-  } else if (is_canon && raw_width == 4312) {
-    top_margin  = 18;
-    left_margin = 22;
-    height -= 2;
-    if (unique_id == 0x80000176)
-      adobe_coeff ("Canon","EOS 450D");
-    goto canon_cr2;
-  } else if (is_canon && raw_width == 4476) {
-    top_margin  = 34;
-    left_margin = 90;
-    goto canon_cr2;
-  } else if (is_canon && raw_width == 4480) {
-    height = 3326;
-    width  = 4432;
-    top_margin  = 10;
-    left_margin = 12;
-    filters = 0x49494949;
-  } else if (is_canon && raw_width == 1208) {
-    top_margin  = 51;
-    left_margin = 62;
-    raw_width = width *= 4;
-    goto canon_cr2;
-  } else if (is_canon && raw_width == 1448) {
-    top_margin  = 51;
-    left_margin = 158;
-    raw_width = width *= 4;
-    goto canon_cr2;
-  } else if (is_canon && raw_width == 5108) {
-    top_margin  = 13;
-    left_margin = 98;
-canon_cr2:
-    height -= top_margin;
-    width  -= left_margin;
-  } else if (is_canon && raw_width == 5712) {
-    height = 3752;
-    width  = 5640;
-    top_margin  = 20;
-    left_margin = 62;
-  } else if (!strcmp(model,"D1")) {
-    cam_mul[0] *= 256/527.0;
-    cam_mul[2] *= 256/317.0;
-  } else if (!strcmp(model,"D1X")) {
-    width -= 4;
-    pixel_aspect = 0.5;
-  } else if (!strcmp(model,"D40X") ||
-	     !strcmp(model,"D60")  ||
-	     !strcmp(model,"D80")) {
-    height -= 3;
-    width  -= 4;
-  } else if (!strcmp(model,"D3")   ||
-	     !strcmp(model,"D700")) {
-    width -= 4;
-    left_margin = 2;
-  } else if (!strncmp(model,"D40",3) ||
-	     !strncmp(model,"D50",3) ||
-	     !strncmp(model,"D70",3)) {
-    width--;
-  } else if (!strcmp(model,"D90")) {
-    width -= 42;
-  } else if (!strcmp(model,"D100")) {
-    if (tiff_compress == 34713 && !nikon_is_compressed()) {
-      load_raw = &CLASS packed_12_load_raw;
-      load_flags |= 8;
-      raw_width = (width += 3) + 3;
-    }
-  } else if (!strcmp(model,"D200")) {
-    left_margin = 1;
-    width -= 4;
-    filters = 0x94949494;
-  } else if (!strncmp(model,"D2H",3)) {
-    left_margin = 6;
-    width -= 14;
-  } else if (!strncmp(model,"D2X",3)) {
-    if (width == 3264) width -= 32;
-    else width -= 8;
-  } else if (!strcmp(model,"D300")) {
-    width -= 32;
-  } else if (!strcmp(model,"COOLPIX P6000")) {
-    load_flags = 1;
-    filters = 0x94949494;
-  } else if (fsize == 1581060) {
-    height = 963;
-    width = 1287;
-    raw_width = 1632;
-    load_raw = &CLASS nikon_e900_load_raw;
-    maximum = 0x3f4;
-    colors = 4;
-    filters = 0x1e1e1e1e;
-    simple_coeff(3);
-    pre_mul[0] = 1.2085;
-    pre_mul[1] = 1.0943;
-    pre_mul[3] = 1.1103;
-#ifdef LIBRAW_LIBRARY_BUILD
-    color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST;
-#endif
-  } else if (fsize == 2465792) {
-    height = 1203;
-    width  = 1616;
-    raw_width = 2048;
-    load_raw = &CLASS nikon_e900_load_raw;
-    colors = 4;
-    filters = 0x4b4b4b4b;
-    adobe_coeff ("NIKON","E950");
-  } else if (fsize == 4771840) {
-    height = 1540;
-    width  = 2064;
-    colors = 4;
-    filters = 0xe1e1e1e1;
-    load_raw = &CLASS packed_12_load_raw;
-    load_flags = 6;
-    if (!timestamp && nikon_e995())
-      strcpy (model, "E995");
-    if (strcmp(model,"E995")) {
-      filters = 0xb4b4b4b4;
-      simple_coeff(3);
-      pre_mul[0] = 1.196;
-      pre_mul[1] = 1.246;
-      pre_mul[2] = 1.018;
-#ifdef LIBRAW_LIBRARY_BUILD
-      color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST;
-#endif
-    }
-  } else if (!strcmp(model,"E2100")) {
-    if (!timestamp && !nikon_e2100()) goto cp_e2500;
-    height = 1206;
-    width  = 1616;
-    load_flags = 7;
-  } else if (!strcmp(model,"E2500")) {
-cp_e2500:
-    strcpy (model, "E2500");
-    height = 1204;
-    width  = 1616;
-    colors = 4;
-    filters = 0x4b4b4b4b;
-  } else if (fsize == 4775936) {
-    height = 1542;
-    width  = 2064;
-    load_raw = &CLASS packed_12_load_raw;
-    load_flags = 7;
-    pre_mul[0] = 1.818;
-    pre_mul[2] = 1.618;
-#ifdef LIBRAW_LIBRARY_BUILD
-    color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST;
-#endif
-    if (!timestamp) nikon_3700();
-    if (model[0] == 'E' && atoi(model+1) < 3700)
-      filters = 0x49494949;
-    if (!strcmp(model,"Optio 33WR")) {
-      flip = 1;
-      filters = 0x16161616;
-      pre_mul[0] = 1.331;
-      pre_mul[2] = 1.820;
-#ifdef LIBRAW_LIBRARY_BUILD
-      color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST;
-#endif
-    }
-  } else if (fsize == 5869568) {
-    height = 1710;
-    width  = 2288;
-    filters = 0x16161616;
-    if (!timestamp && minolta_z2()) {
-      strcpy (make, "Minolta");
-      strcpy (model,"DiMAGE Z2");
-    }
-    load_raw = &CLASS packed_12_load_raw;
-    load_flags = 6 + (make[0] == 'M');
-  } else if (!strcmp(model,"E4500")) {
-    height = 1708;
-    width  = 2288;
-    colors = 4;
-    filters = 0xb4b4b4b4;
-  } else if (fsize == 7438336) {
-    height = 1924;
-    width  = 2576;
-    colors = 4;
-    filters = 0xb4b4b4b4;
-  } else if (fsize == 8998912) {
-    height = 2118;
-    width  = 2832;
-    maximum = 0xf83;
-    load_raw = &CLASS packed_12_load_raw;
-    load_flags = 7;
-  } else if (!strcmp(model,"FinePix S5100") ||
-	     !strcmp(model,"FinePix S5500")) {
-    load_raw = &CLASS unpacked_load_raw;
-  } else if (!strcmp(make,"FUJIFILM")) {
-    if (!strcmp(model+7,"S2Pro")) {
-      strcpy (model+7," S2Pro");
-      height = 2144;
-      width  = 2880;
-      flip = 6;
-    } else
-      maximum = 0x3e00;
-    if (is_raw == 2 && shot_select)
-      maximum = 0x2f00;
-    top_margin = (raw_height - height)/2;
-    left_margin = (raw_width - width )/2;
-    if (is_raw == 2)
-      data_offset += (shot_select > 0) * ( fuji_layout ?
-		(raw_width *= 2) : raw_height*raw_width*2 );
-    fuji_width = width >> !fuji_layout;
-    width = (height >> fuji_layout) + fuji_width;
-    raw_height = height;
-    height = width - 1;
-    load_raw = &CLASS fuji_load_raw;
-    if (!(fuji_width & 1)) filters = 0x49494949;
-  } else if (!strcmp(model,"RD175")) {
-    height = 986;
-    width = 1534;
-    data_offset = 513;
-    filters = 0x61616161;
-    load_raw = &CLASS minolta_rd175_load_raw;
-  } else if (!strcmp(model,"KD-400Z")) {
-    height = 1712;
-    width  = 2312;
-    raw_width = 2336;
-    goto konica_400z;
-  } else if (!strcmp(model,"KD-510Z")) {
-    goto konica_510z;
-  } else if (!strcasecmp(make,"MINOLTA")) {
-    load_raw = &CLASS unpacked_load_raw;
-    maximum = 0xfff;
-    if (!strncmp(model,"DiMAGE A",8)) {
-      if (!strcmp(model,"DiMAGE A200"))
-	filters = 0x49494949;
-      load_raw = &CLASS packed_12_load_raw;
-    } else if (!strncmp(model,"ALPHA",5) ||
-	       !strncmp(model,"DYNAX",5) ||
-	       !strncmp(model,"MAXXUM",6)) {
-      sprintf (model+20, "DYNAX %-10s", model+6+(model[0]=='M'));
-      adobe_coeff (make, model+20);
-      load_raw = &CLASS packed_12_load_raw;
-    } else if (!strncmp(model,"DiMAGE G",8)) {
-      if (model[8] == '4') {
-	height = 1716;
-	width  = 2304;
-      } else if (model[8] == '5') {
-konica_510z:
-	height = 1956;
-	width  = 2607;
-	raw_width = 2624;
-      } else if (model[8] == '6') {
-	height = 2136;
-	width  = 2848;
-      }
-      data_offset += 14;
-      filters = 0x61616161;
-konica_400z:
-      load_raw = &CLASS unpacked_load_raw;
-      maximum = 0x3df;
-      order = 0x4d4d;
-    }
-  } else if (!strcmp(model,"*ist DS")) {
-    height -= 2;
-  } else if (!strcmp(model,"K20D")) {
-    filters = 0x16161616;
-  } else if (!strcmp(model,"Optio S")) {
-    if (fsize == 3178560) {
-      height = 1540;
-      width  = 2064;
-      load_raw = &CLASS eight_bit_load_raw;
-      cam_mul[0] *= 4;
-      cam_mul[2] *= 4;
-      pre_mul[0] = 1.391;
-      pre_mul[2] = 1.188;
-#ifdef LIBRAW_LIBRARY_BUILD
-      color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST;
-#endif
-    } else {
-      height = 1544;
-      width  = 2068;
-      raw_width = 3136;
-      load_raw = &CLASS packed_12_load_raw;
-      maximum = 0xf7c;
-      pre_mul[0] = 1.137;
-      pre_mul[2] = 1.453;
-#ifdef LIBRAW_LIBRARY_BUILD
-      color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST;
-#endif
-    }
-  } else if (fsize == 6114240) {
-    height = 1737;
-    width  = 2324;
-    raw_width = 3520;
-    load_raw = &CLASS packed_12_load_raw;
-    maximum = 0xf7a;
-    pre_mul[0] = 1.980;
-    pre_mul[2] = 1.570;
-#ifdef LIBRAW_LIBRARY_BUILD
-    color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST;
-#endif
-  } else if (!strcmp(model,"Optio 750Z")) {
-    height = 2302;
-    width  = 3072;
-    load_raw = &CLASS packed_12_load_raw;
-    load_flags = 7;
-  } else if (!strcmp(model,"S85")) {
-    height = 2448;
-    width  = 3264;
-    raw_width = fsize/height/2;
-    order = 0x4d4d;
-    load_raw = &CLASS unpacked_load_raw;
-    maximum = 0xffff;
-  } else if (!strcmp(model,"STV680 VGA")) {
-    height = 484;
-    width  = 644;
-    load_raw = &CLASS eight_bit_load_raw;
-    flip = 2;
-    filters = 0x16161616;
-    black = 16;
-    pre_mul[0] = 1.097;
-    pre_mul[2] = 1.128;
-#ifdef LIBRAW_LIBRARY_BUILD
-    color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST;
-#endif
-  } else if (!strcmp(model,"KAI-0340")) {
-    height = 477;
-    width  = 640;
-    order = 0x4949;
-    data_offset = 3840;
-    load_raw = &CLASS unpacked_load_raw;
-    pre_mul[0] = 1.561;
-    pre_mul[2] = 2.454;
-#ifdef LIBRAW_LIBRARY_BUILD
-    color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST;
-#endif
-  } else if (!strcmp(model,"N95")) {
-    height = raw_height - (top_margin = 2);
-  } else if (!strcmp(model,"531C")) {
-    height = 1200;
-    width  = 1600;
-    load_raw = &CLASS unpacked_load_raw;
-    filters = 0x49494949;
-    pre_mul[1] = 1.218;
-#ifdef LIBRAW_LIBRARY_BUILD
-    color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST;
-#endif
-  } else if (!strcmp(model,"F-080C")) {
-    height = 768;
-    width  = 1024;
-    load_raw = &CLASS eight_bit_load_raw;
-  } else if (!strcmp(model,"F-145C")) {
-    height = 1040;
-    width  = 1392;
-    load_raw = &CLASS eight_bit_load_raw;
-  } else if (!strcmp(model,"F-201C")) {
-    height = 1200;
-    width  = 1600;
-    load_raw = &CLASS eight_bit_load_raw;
-  } else if (!strcmp(model,"F-510C")) {
-    height = 1958;
-    width  = 2588;
-    load_raw = fsize < 7500000 ?
-	&CLASS eight_bit_load_raw : &CLASS unpacked_load_raw;
-    maximum = 0xfff0;
-  } else if (!strcmp(model,"F-810C")) {
-    height = 2469;
-    width  = 3272;
-    load_raw = &CLASS unpacked_load_raw;
-    maximum = 0xfff0;
-  } else if (!strcmp(model,"XCD-SX910CR")) {
-    height = 1024;
-    width  = 1375;
-    raw_width = 1376;
-    filters = 0x49494949;
-    maximum = 0x3ff;
-    load_raw = fsize < 2000000 ?
-	&CLASS eight_bit_load_raw : &CLASS unpacked_load_raw;
-  } else if (!strcmp(model,"2010")) {
-    height = 1207;
-    width  = 1608;
-    order = 0x4949;
-    filters = 0x16161616;
-    data_offset = 3212;
-    maximum = 0x3ff;
-    load_raw = &CLASS unpacked_load_raw;
-  } else if (!strcmp(model,"A782")) {
-    height = 3000;
-    width  = 2208;
-    filters = 0x61616161;
-    load_raw = fsize < 10000000 ?
-	&CLASS eight_bit_load_raw : &CLASS unpacked_load_raw;
-    maximum = 0xffc0;
-  } else if (!strcmp(model,"3320AF")) {
-    height = 1536;
-    raw_width = width = 2048;
-    filters = 0x61616161;
-    load_raw = &CLASS unpacked_load_raw;
-    maximum = 0x3ff;
-    pre_mul[0] = 1.717;
-    pre_mul[2] = 1.138;
-#ifdef LIBRAW_LIBRARY_BUILD
-    color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST;
-#endif
-    fseek (ifp, 0x300000, SEEK_SET);
-    if ((order = guess_byte_order(0x10000)) == 0x4d4d) {
-      height -= (top_margin = 16);
-      width -= (left_margin = 28);
-      maximum = 0xf5c0;
-      strcpy (make, "ISG");
-      model[0] = 0;
-    }
-  } else if (!strcmp(make,"Hasselblad")) {
-    if (load_raw == &CLASS lossless_jpeg_load_raw)
-      load_raw = &CLASS hasselblad_load_raw;
-    if (raw_width == 7262) {
-      height = 5444;
-      width  = 7248;
-      top_margin  = 4;
-      left_margin = 7;
-      filters = 0x61616161;
-    } else if (raw_width == 4090) {
-      strcpy (model, "V96C");
-      height -= (top_margin = 6);
-      width -= (left_margin = 3) + 7;
-      filters = 0x61616161;
-    }
-  } else if (!strcmp(make,"Sinar")) {
-    if (!memcmp(head,"8BPS",4)) {
-      fseek (ifp, 14, SEEK_SET);
-      height = get4();
-      width  = get4();
-      filters = 0x61616161;
-      data_offset = 68;
-    }
-    if (!load_raw) load_raw = &CLASS unpacked_load_raw;
-    maximum = 0x3fff;
-  } else if (!strcmp(make,"Leaf")) {
-    maximum = 0x3fff;
-    fseek (ifp, data_offset, SEEK_SET);
-    if (ljpeg_start (&jh, 1) && jh.bits == 15)
-      maximum = 0x1fff;
-    if (tiff_samples > 1) filters = 0;
-    if (tiff_samples > 1 || tile_length < raw_height)
-      load_raw = &CLASS leaf_hdr_load_raw;
-    if ((width | height) == 2048) {
-      if (tiff_samples == 1) {
-	filters = 1;
-	strcpy (cdesc, "RBTG");
-	strcpy (model, "CatchLight");
-	top_margin =  8; left_margin = 18; height = 2032; width = 2016;
-      } else {
-	strcpy (model, "DCB2");
-	top_margin = 10; left_margin = 16; height = 2028; width = 2022;
-      }
-    } else if (width+height == 3144+2060) {
-      if (!model[0]) strcpy (model, "Cantare");
-      if (width > height) {
-	 top_margin = 6; left_margin = 32; height = 2048;  width = 3072;
-	filters = 0x61616161;
-      } else {
-	left_margin = 6;  top_margin = 32;  width = 2048; height = 3072;
-	filters = 0x16161616;
-      }
-      if (!cam_mul[0] || model[0] == 'V') filters = 0;
-      else is_raw = tiff_samples;
-    } else if (width == 2116) {
-      strcpy (model, "Valeo 6");
-      height -= 2 * (top_margin = 30);
-      width -= 2 * (left_margin = 55);
-      filters = 0x49494949;
-    } else if (width == 3171) {
-      strcpy (model, "Valeo 6");
-      height -= 2 * (top_margin = 24);
-      width -= 2 * (left_margin = 24);
-      filters = 0x16161616;
-    }
-  } else if (!strcmp(make,"LEICA") || !strcmp(make,"Panasonic")) {
-    maximum = 0xfff0;
-    if ((fsize-data_offset) / (width*8/7) == height)
-      load_raw = &CLASS panasonic_load_raw;
-    if (!load_raw) load_raw = &CLASS unpacked_load_raw;
-    switch (width) {
-      case 2568:
-	adobe_coeff ("Panasonic","DMC-LC1");  break;
-      case 3130:
-	left_margin = -14;
-      case 3170:
-	left_margin += 18;
-	width = 3096;
-	if (height > 2326) {
-	  height = 2326;
-	  top_margin = 13;
-	  filters = 0x49494949;
-	}
-	zero_is_bad = 1;
-	adobe_coeff ("Panasonic","DMC-FZ8");  break;
-      case 3213:
-	width -= 27;
-      case 3177:
-	width -= 10;
-	filters = 0x49494949;
-	zero_is_bad = 1;
-	adobe_coeff ("Panasonic","DMC-L1");  break;
-      case 3304:
-	width -= 17;
-	zero_is_bad = 1;
-	adobe_coeff ("Panasonic","DMC-FZ30");  break;
-      case 3330:
-	width += 43;
-	left_margin = -6;
-	maximum = 0xf7f0;
-      case 3370:
-	width -= 82;
-	left_margin += 15;
-	if (height > 2480)
-	    height = 2480 - (top_margin = 10);
-	filters = 0x49494949;
-	zero_is_bad = 1;
-	adobe_coeff ("Panasonic","DMC-FZ18");  break;
-      case 3690:
-	height -= 2;
-	left_margin = -14;
-	maximum = 0xf7f0;
-      case 3770:
-	width = 3672;
-	if (--height == 2798 && (height = 2760))
-	  top_margin = 15;
-	else filters = 0x49494949;
-	left_margin += 17;
-	zero_is_bad = 1;
-	adobe_coeff ("Panasonic","DMC-FZ50");  break;
-      case 3710:
-	width = 3682;
-	filters = 0x49494949;
-	adobe_coeff ("Panasonic","DMC-L10");  break;
-      case 3724:
-	width -= 14;
-      case 3836:
-	width -= 42;
-lx3:	filters = 0x16161616;
-	if (make[0] != 'P')
-	  adobe_coeff ("Panasonic","DMC-LX3");
-	break;
-      case 3880:
-	width -= 22;
-	left_margin = 6;
-	zero_is_bad = 1;
-	adobe_coeff ("Panasonic","DMC-LX1");  break;
-      case 4060:
-	width = 3982;
-	if (height == 2250) goto lx3;
-	width = 4018;
-	filters = 0x49494949;
-	zero_is_bad = 1;
-	adobe_coeff ("Panasonic","DMC-G1");  break;
-      case 4290:
-	height += 38;
-	left_margin = -14;
-	filters = 0x49494949;
-      case 4330:
-	width = 4248;
-	if ((height -= 39) == 2400)
-	  top_margin = 15;
-	left_margin += 17;
-	adobe_coeff ("Panasonic","DMC-LX2");  break;
-      case 4508:
-	height -= 6;
-	width = 4429;
-	filters = 0x16161616;
-	adobe_coeff ("Panasonic","DMC-FX150");  break;
-    }
-  } else if (!strcmp(model,"C770UZ")) {
-    height = 1718;
-    width  = 2304;
-    filters = 0x16161616;
-    load_raw = &CLASS packed_12_load_raw;
-    load_flags = 7;
-  } else if (!strcmp(make,"OLYMPUS")) {
-    height += height & 1;
-    filters = exif_cfa;
-    if (load_raw == &CLASS olympus_e410_load_raw) {
-      black >>= 4;
-    } else if (!strcmp(model,"E-10") ||
-	      !strncmp(model,"E-20",4)) {
-      black <<= 2;
-    } else if (!strcmp(model,"E-300") ||
-	       !strcmp(model,"E-500")) {
-      width -= 20;
-      if (load_raw == &CLASS unpacked_load_raw) {
-	maximum = 0xfc30;
-	black = 0;
-      }
-    } else if (!strcmp(model,"E-330")) {
-      width -= 30;
-      if (load_raw == &CLASS unpacked_load_raw)
-	maximum = 0xf790;
-    } else if (!strcmp(model,"SP550UZ")) {
-      thumb_length = fsize - (thumb_offset = 0xa39800);
-      thumb_height = 480;
-      thumb_width  = 640;
-    }
-  } else if (!strcmp(model,"N Digital")) {
-    height = 2047;
-    width  = 3072;
-    filters = 0x61616161;
-    data_offset = 0x1a00;
-    load_raw = &CLASS packed_12_load_raw;
-  } else if (!strcmp(model,"DSC-F828")) {
-    width = 3288;
-    left_margin = 5;
-    data_offset = 862144;
-    load_raw = &CLASS sony_load_raw;
-    filters = 0x9c9c9c9c;
-    colors = 4;
-    strcpy (cdesc, "RGBE");
-  } else if (!strcmp(model,"DSC-V3")) {
-    width = 3109;
-    left_margin = 59;
-    data_offset = 787392;
-    load_raw = &CLASS sony_load_raw;
-  } else if (!strcmp(make,"SONY") && raw_width == 3984) {
-    adobe_coeff ("SONY","DSC-R1");
-    width = 3925;
-    order = 0x4d4d;
-  } else if (!strcmp(model,"DSLR-A100")) {
-    height--;
-  } else if (!strcmp(model,"DSLR-A350")) {
-    height -= 4;
-  } else if (!strcmp(model,"C603v")) {
-    height = 480;
-    width  = 640;
-    goto c603v;
-  } else if (!strcmp(model,"C603y")) {
-    height = 2134;
-    width  = 2848;
-c603v:
-    filters = 0;
-    load_raw = &CLASS kodak_yrgb_load_raw;
-  } else if (!strcmp(model,"C603")) {
-    raw_height = height = 2152;
-    raw_width  = width  = 2864;
-    goto c603;
-  } else if (!strcmp(model,"C330")) {
-    height = 1744;
-    width  = 2336;
-    raw_height = 1779;
-    raw_width  = 2338;
-    top_margin = 33;
-    left_margin = 1;
-c603:
-    order = 0x4949;
-    if ((data_offset = fsize - raw_height*raw_width)) {
-      fseek (ifp, 168, SEEK_SET);
-      read_shorts (curve, 256);
-#ifdef LIBRAW_LIBRARY_BUILD
-      color_flags.curve_state = LIBRAW_COLORSTATE_LOADED;
-#endif
-    } else use_gamma = 0;
-    load_raw = &CLASS eight_bit_load_raw;
-  } else if (!strcasecmp(make,"KODAK")) {
-    if (filters == UINT_MAX) filters = 0x61616161;
-    if (!strncmp(model,"NC2000",6)) {
-      width -= 4;
-      left_margin = 2;
-    } else if (!strcmp(model,"EOSDCS3B")) {
-      width -= 4;
-      left_margin = 2;
-    } else if (!strcmp(model,"EOSDCS1")) {
-      width -= 4;
-      left_margin = 2;
-    } 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,"DCS660M")) {
-      black = 214;
-      colors = 1;
-      filters = 0;
-    } else if (!strcmp(model,"DCS760M")) {
-      colors = 1;
-      filters = 0;
-    }
-    if (!strcmp(model+4,"20X"))
-      strcpy (cdesc, "MYCY");
-    if (strstr(model,"DC25")) {
-      strcpy (model, "DC25");
-      data_offset = 15424;
-    }
-    if (!strncmp(model,"DC2",3)) {
-      height = 242;
-      if (fsize < 100000) {
-	raw_width = 256; width = 249;
-	pixel_aspect = (4.0*height) / (3.0*width);
-      } else {
-	raw_width = 512; width = 501;
-	pixel_aspect = (493.0*height) / (373.0*width);
-      }
-      data_offset += raw_width + 1;
-      colors = 4;
-      filters = 0x8d8d8d8d;
-      simple_coeff(1);
-      pre_mul[1] = 1.179;
-      pre_mul[2] = 1.209;
-      pre_mul[3] = 1.036;
-#ifdef LIBRAW_LIBRARY_BUILD
-      color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST;
-#endif
-      load_raw = &CLASS eight_bit_load_raw;
-    } else if (!strcmp(model,"40")) {
-      strcpy (model, "DC40");
-      height = 512;
-      width  = 768;
-      data_offset = 1152;
-      load_raw = &CLASS kodak_radc_load_raw;
-    } else if (strstr(model,"DC50")) {
-      strcpy (model, "DC50");
-      height = 512;
-      width  = 768;
-      data_offset = 19712;
-      load_raw = &CLASS kodak_radc_load_raw;
-    } else if (strstr(model,"DC120")) {
-      strcpy (model, "DC120");
-      height = 976;
-      width  = 848;
-      pixel_aspect = height/0.75/width;
-      load_raw = tiff_compress == 7 ?
-	&CLASS kodak_jpeg_load_raw : &CLASS kodak_dc120_load_raw;
-    } else if (!strcmp(model,"DCS200")) {
-      thumb_height = 128;
-      thumb_width  = 192;
-      thumb_offset = 6144;
-      thumb_misc   = 360;
-      write_thumb = &CLASS layer_thumb;
-      height = 1024;
-      width  = 1536;
-      data_offset = 79872;
-      load_raw = &CLASS eight_bit_load_raw;
-      black = 17;
-    }
-  } else if (!strcmp(model,"Fotoman Pixtura")) {
-    height = 512;
-    width  = 768;
-    data_offset = 3632;
-    load_raw = &CLASS kodak_radc_load_raw;
-    filters = 0x61616161;
-    simple_coeff(2);
-  } else if (!strcmp(model,"QuickTake 100")) {
-    fseek (ifp, 544, SEEK_SET);
-    height = get2();
-    width  = get2();
-    data_offset = (get4(),get2()) == 30 ? 738:736;
-    if (height > width) {
-      SWAP(height,width);
-      fseek (ifp, data_offset-6, SEEK_SET);
-      flip = ~get2() & 3 ? 5:6;
-    }
-    load_raw = &CLASS quicktake_100_load_raw;
-    filters = 0x61616161;
-  } else if (!strcmp(model,"QuickTake 150")) {
-    data_offset = 738 - head[5];
-    if (head[5]) strcpy (model+10, "200");
-    load_raw = &CLASS kodak_radc_load_raw;
-    height = 480;
-    width  = 640;
-    filters = 0x61616161;
-  } else if (!strcmp(make,"Rollei") && !load_raw) {
-    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 = &CLASS rollei_load_raw;
-    pre_mul[0] = 1.8;
-    pre_mul[2] = 1.3;
-#ifdef LIBRAW_LIBRARY_BUILD
-    color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST;
-#endif
-  } else if (!strcmp(model,"PC-CAM 600")) {
-    height = 768;
-    data_offset = width = 1024;
-    filters = 0x49494949;
-    load_raw = &CLASS eight_bit_load_raw;
-    pre_mul[0] = 1.14;
-    pre_mul[2] = 2.73;
-#ifdef LIBRAW_LIBRARY_BUILD
-    color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST;
-#endif
-  } else if (!strcmp(model,"QV-2000UX")) {
-    height = 1208;
-    width  = 1632;
-    data_offset = width * 2;
-    load_raw = &CLASS eight_bit_load_raw;
-  } else if (fsize == 3217760) {
-    height = 1546;
-    width  = 2070;
-    raw_width = 2080;
-    load_raw = &CLASS eight_bit_load_raw;
-  } else if (!strcmp(model,"QV-4000")) {
-    height = 1700;
-    width  = 2260;
-    load_raw = &CLASS unpacked_load_raw;
-    maximum = 0xffff;
-  } else if (!strcmp(model,"QV-5700")) {
-    height = 1924;
-    width  = 2576;
-    load_raw = &CLASS casio_qv5700_load_raw;
-  } else if (!strcmp(model,"QV-R41")) {
-    height = 1720;
-    width  = 2312;
-    raw_width = 3520;
-    left_margin = 2;
-  } else if (!strcmp(model,"QV-R51")) {
-    height = 1926;
-    width  = 2580;
-    raw_width = 3904;
-    pre_mul[0] = 1.340;
-    pre_mul[2] = 1.672;
-#ifdef LIBRAW_LIBRARY_BUILD
-    color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST;
-#endif
-  } else if (!strcmp(model,"EX-S100")) {
-    height = 1544;
-    width  = 2058;
-    raw_width = 3136;
-    pre_mul[0] = 1.631;
-    pre_mul[2] = 1.106;
-#ifdef LIBRAW_LIBRARY_BUILD
-    color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST;
-#endif
-  } else if (!strcmp(model,"EX-Z50")) {
-    height = 1931;
-    width  = 2570;
-    raw_width = 3904;
-    pre_mul[0] = 2.529;
-    pre_mul[2] = 1.185;
-#ifdef LIBRAW_LIBRARY_BUILD
-    color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST;
-#endif
-  } else if (!strcmp(model,"EX-Z55")) {
-    height = 1960;
-    width  = 2570;
-    raw_width = 3904;
-    pre_mul[0] = 1.520;
-    pre_mul[2] = 1.316;
-#ifdef LIBRAW_LIBRARY_BUILD
-    color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST;
-#endif
-  } else if (!strcmp(model,"EX-P505")) {
-    height = 1928;
-    width  = 2568;
-    raw_width = 3852;
-    maximum = 0xfff;
-    pre_mul[0] = 2.07;
-    pre_mul[2] = 1.88;
-#ifdef LIBRAW_LIBRARY_BUILD
-    color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST;
-#endif
-  } else if (fsize == 9313536) {	/* EX-P600 or QV-R61 */
-    height = 2142;
-    width  = 2844;
-    raw_width = 4288;
-    pre_mul[0] = 1.797;
-    pre_mul[2] = 1.219;
-#ifdef LIBRAW_LIBRARY_BUILD
-    color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST;
-#endif
-  } else if (!strcmp(model,"EX-P700")) {
-    height = 2318;
-    width  = 3082;
-    raw_width = 4672;
-    pre_mul[0] = 1.758;
-    pre_mul[2] = 1.504;
-#ifdef LIBRAW_LIBRARY_BUILD
-    color_flags.pre_mul_state = LIBRAW_COLORSTATE_CONST;
-#endif
-  }
-  if (!model[0])
-    sprintf (model, "%dx%d", width, height);
-  if (filters == UINT_MAX) filters = 0x94949494;
-  if (raw_color) adobe_coeff (make, model);
-  if (thumb_offset && !thumb_height) {
-    fseek (ifp, thumb_offset, SEEK_SET);
-    if (ljpeg_start (&jh, 1)) {
-      thumb_width  = jh.wide;
-      thumb_height = jh.high;
-    }
-  }
-dng_skip:
-  if (!load_raw || height < 22) is_raw = 0;
-#ifdef NO_JPEG
-  if (load_raw == &CLASS kodak_jpeg_load_raw) {
-#ifdef DCRAW_VERBOSE
-    fprintf (stderr,_("%s: You must link dcraw with libjpeg!!\n"), ifname);
-#endif
-    is_raw = 0;
-#ifdef LIBRAW_LIBRARY_BUILD
-    imgdata.process_warnings |= LIBRAW_WARN_NO_JPEGLIB;
-#endif
-  }
-#endif
-  if (!cdesc[0])
-    strcpy (cdesc, colors == 3 ? "RGB":"GMCY");
-  if (!raw_height) raw_height = height;
-  if (!raw_width ) raw_width  = width;
-  if (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;
-    }
-notraw:
-  if (flip == -1) flip = tiff_flip;
-  if (flip == -1) flip = 0;
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_IDENTIFY,1,2);
-#endif
-}
-void CLASS convert_to_rgb()
-{
-  int row, col, c, i, j, k;
-  ushort *img;
-  float out[3], out_cam[3][4];
-  double num, inverse[3][3], bnd[2]={0,0};
-  static const double xyzd50_srgb[3][3] =
-  { { 0.436083, 0.385083, 0.143055 },
-    { 0.222507, 0.716888, 0.060608 },
-    { 0.013930, 0.097097, 0.714022 } };
-  static const double rgb_rgb[3][3] =
-  { { 1,0,0 }, { 0,1,0 }, { 0,0,1 } };
-  static const double adobe_rgb[3][3] =
-  { { 0.715146, 0.284856, 0.000000 },
-    { 0.000000, 1.000000, 0.000000 },
-    { 0.000000, 0.041166, 0.958839 } };
-  static const double wide_rgb[3][3] =
-  { { 0.593087, 0.404710, 0.002206 },
-    { 0.095413, 0.843149, 0.061439 },
-    { 0.011621, 0.069091, 0.919288 } };
-  static const double prophoto_rgb[3][3] =
-  { { 0.529317, 0.330092, 0.140588 },
-    { 0.098368, 0.873465, 0.028169 },
-    { 0.016879, 0.117663, 0.865457 } };
-  static const double (*out_rgb[])[3] =
-  { rgb_rgb, adobe_rgb, wide_rgb, prophoto_rgb, xyz_rgb };
-  static const char *name[] =
-  { "sRGB", "Adobe RGB (1998)", "WideGamut D65", "ProPhoto D65", "XYZ" };
-  static const unsigned phead[] =
-  { 1024, 0, 0x2100000, 0x6d6e7472, 0x52474220, 0x58595a20, 0, 0, 0,
-    0x61637370, 0, 0, 0x6e6f6e65, 0, 0, 0, 0, 0xf6d6, 0x10000, 0xd32d };
-  unsigned pbody[] =
-  { 10, 0x63707274, 0, 36,	/* cprt */
-	0x64657363, 0, 40,	/* desc */
-	0x77747074, 0, 20,	/* wtpt */
-	0x626b7074, 0, 20,	/* bkpt */
-	0x72545243, 0, 14,	/* rTRC */
-	0x67545243, 0, 14,	/* gTRC */
-	0x62545243, 0, 14,	/* bTRC */
-	0x7258595a, 0, 20,	/* rXYZ */
-	0x6758595a, 0, 20,	/* gXYZ */
-	0x6258595a, 0, 20 };	/* bXYZ */
-  static const unsigned pwhite[] = { 0xf351, 0x10000, 0x116cc };
-  unsigned pcurve[] = { 0x63757276, 0, 1, 0x1000000 };
-
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_CONVERT_RGB,0,2);
-#endif
-
-  bnd[gamm[1] >= 1] = 1;
-  if (gamm[1] && (gamm[1]-1)*(gamm[0]-1) <= 0) {
-    for (i=0; i < 36; i++) {
-      gamm[2] = (bnd[0] + bnd[1])/2;
-      bnd[(pow(gamm[2]/gamm[1],-gamm[0])-1)/gamm[0]-1/gamm[2] > -1] = gamm[2];
-    }
-    gamm[3] = gamm[2]*(1/gamm[0]-1);
-    gamm[2] /= gamm[1];
-  }
-  gamm[4] = 1 / (gamm[1]/2*SQR(gamm[2]) - gamm[3]*(1-gamm[2]) +
-		(1-pow(gamm[2],1+gamm[0]))*(1+gamm[3])/(1+gamm[0])) - 1;
-
-  memcpy (out_cam, rgb_cam, sizeof out_cam);
-  raw_color |= colors == 1 || document_mode ||
-		output_color < 1 || output_color > 5;
-  if (!raw_color) {
-    oprof = (unsigned *) calloc (phead[0], 1);
-    merror (oprof, "convert_to_rgb()");
-    memcpy (oprof, phead, sizeof phead);
-    if (output_color == 5) oprof[4] = oprof[5];
-    oprof[0] = 132 + 12*pbody[0];
-    for (i=0; i < pbody[0]; i++) {
-      oprof[oprof[0]/4] = i ? (i > 1 ? 0x58595a20 : 0x64657363) : 0x74657874;
-      pbody[i*3+2] = oprof[0];
-      oprof[0] += (pbody[i*3+3] + 3) & -4;
-    }
-    memcpy (oprof+32, pbody, sizeof pbody);
-    oprof[pbody[5]/4+2] = strlen(name[output_color-1]) + 1;
-    memcpy ((char *)oprof+pbody[8]+8, pwhite, sizeof pwhite);
-    if (output_bps == 8 | gamma_16bit)
-      pcurve[3] = (short)(256/gamm[4]+0.5) << 16;
-    for (i=4; i < 7; i++)
-      memcpy ((char *)oprof+pbody[i*3+2], pcurve, sizeof pcurve);
-    pseudoinverse ((double (*)[3]) out_rgb[output_color-1], inverse, 3);
-    for (i=0; i < 3; i++)
-      for (j=0; j < 3; j++) {
-	for (num = k=0; k < 3; k++)
-	  num += xyzd50_srgb[i][k] * inverse[j][k];
-        oprof[pbody[j*3+23]/4+i+2] = num * 0x10000 + 0.5;
-      }
-    for (i=0; i < phead[0]/4; i++)
-      oprof[i] = htonl(oprof[i]);
-    strcpy ((char *)oprof+pbody[2]+8, "auto-generated by dcraw");
-    strcpy ((char *)oprof+pbody[5]+12, name[output_color-1]);
-    for (i=0; i < 3; i++)
-      for (j=0; j < colors; j++)
-	for (out_cam[i][j] = k=0; k < 3; k++)
-	  out_cam[i][j] += out_rgb[output_color-1][i][k] * rgb_cam[k][j];
-  }
-#ifdef DCRAW_VERBOSE
-  if (verbose)
-    fprintf (stderr, raw_color ? _("Building histograms...\n") :
-	_("Converting to %s colorspace...\n"), name[output_color-1]);
-
-#endif
-#ifdef LIBRAW_LIBRARY_BUILD
-  memset(histogram,0,sizeof(int)*LIBRAW_HISTOGRAM_SIZE*4);
-#else
-  memset (histogram, 0, sizeof histogram);
-#endif
-  for (img=image[0], row=0; row < height; row++)
-    for (col=0; col < width; col++, img+=4) {
-      if (!raw_color) {
-	out[0] = out[1] = out[2] = 0;
-	FORCC {
-	  out[0] += out_cam[0][c] * img[c];
-	  out[1] += out_cam[1][c] * img[c];
-	  out[2] += out_cam[2][c] * img[c];
-	}
-	FORC3 img[c] = CLIP((int) out[c]);
-      }
-      else if (document_mode)
-	img[0] = img[FC(row,col)];
-      FORCC histogram[c][img[c] >> 3]++;
-    }
-  if (colors == 4 && output_color) colors = 3;
-  if (document_mode && filters) colors = 1;
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_CONVERT_RGB,1,2);
-#endif
-}
-
-void CLASS fuji_rotate()
-{
-  int i, row, col;
-  double step;
-  float r, c, fr, fc;
-  unsigned ur, uc;
-  ushort wide, high, (*img)[4], (*pix)[4];
-
-  if (!fuji_width) return;
-#ifdef DCRAW_VERBOSE
-  if (verbose)
-    fprintf (stderr,_("Rotating image 45 degrees...\n"));
-#endif
-  fuji_width = (fuji_width - 1 + shrink) >> shrink;
-  step = sqrt(0.5);
-  wide = fuji_width / step;
-  high = (height - fuji_width) / step;
-  img = (ushort (*)[4]) calloc (wide*high, sizeof *img);
-  merror (img, "fuji_rotate()");
-
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_FUJI_ROTATE,0,2);
-#endif
-
-  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;
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_FUJI_ROTATE,1,2);
-#endif
-}
-
-void CLASS stretch()
-{
-  ushort newdim, (*img)[4], *pix0, *pix1;
-  int row, col, c;
-  double rc, frac;
-
-  if (pixel_aspect == 1) return;
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_STRETCH,0,2);
-#endif
-#ifdef DCRAW_VERBOSE
-  if (verbose) fprintf (stderr,_("Stretching the image...\n"));
-#endif
-  if (pixel_aspect < 1) {
-    newdim = height / pixel_aspect + 0.5;
-    img = (ushort (*)[4]) calloc (width*newdim, sizeof *img);
-    merror (img, "stretch()");
-    for (rc=row=0; row < newdim; row++, rc+=pixel_aspect) {
-      frac = rc - (c = rc);
-      pix0 = pix1 = image[c*width];
-      if (c+1 < height) pix1 += width*4;
-      for (col=0; col < width; col++, pix0+=4, pix1+=4)
-	FORCC img[row*width+col][c] = pix0[c]*(1-frac) + pix1[c]*frac + 0.5;
-    }
-    height = newdim;
-  } else {
-    newdim = width * pixel_aspect + 0.5;
-    img = (ushort (*)[4]) calloc (height*newdim, sizeof *img);
-    merror (img, "stretch()");
-    for (rc=col=0; col < newdim; col++, rc+=1/pixel_aspect) {
-      frac = rc - (c = rc);
-      pix0 = pix1 = image[c];
-      if (c+1 < width) pix1 += 4;
-      for (row=0; row < height; row++, pix0+=width*4, pix1+=width*4)
-	FORCC img[row*newdim+col][c] = pix0[c]*(1-frac) + pix1[c]*frac + 0.5;
-    }
-    width = newdim;
-  }
-  free (image);
-  image = img;
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_STRETCH,1,2);
-#endif
-}
-
-int CLASS flip_index (int row, int col)
-{
-  if (flip & 4) SWAP(row,col);
-  if (flip & 2) row = iheight - 1 - row;
-  if (flip & 1) col = iwidth  - 1 - col;
-  return row * iwidth + col;
-}
-
-void CLASS gamma_lut (ushort lut[0x10000])
-{
-  int perc, c, val, total, i;
-  float t_white=0, r;
-
-#ifdef LIBRAW_LIBRARY_BUILD
-  perc = width * height * imgdata.params.auto_bright_thr;
-#else
-  perc = width * height * 0.01;		/* 99th percentile white level */
-#endif
-  if (fuji_width) perc /= 2;
-  if ((highlight & ~2) || no_auto_bright) perc = -1;
-  FORCC {
-    for (val=0x2000, total=0; --val > 32; )
-      if ((total += histogram[c][val]) > perc) break;
-    if (t_white < val) t_white = val;
-  }
-  t_white *= 8 / bright;
-  for (i=0; i < 0x10000; i++) {
-    r = i / t_white;
-    val = 65535 * ( !use_gamma ? r :
-                    r <= gamm[2] ? r*gamm[1] : pow((double)r,gamm[0])*(1+gamm[3])-gamm[3]);
-    if (val > 65535) val = 65535;
-    lut[i] = val;
-  }
-}
-
-
-void CLASS tiff_set (ushort *ntag,
-	ushort tag, ushort type, int count, int val)
-{
-  struct tiff_tag *tt;
-  int c;
-
-  tt = (struct tiff_tag *)(ntag+1) + (*ntag)++;
-  tt->tag = tag;
-  tt->type = type;
-  tt->count = count;
-  if (type < 3 && count <= 4)
-    FORC(4) tt->val.c[c] = val >> (c << 3);
-  else if (type == 3 && count <= 2)
-    FORC(2) tt->val.s[c] = val >> (c << 4);
-  else tt->val.i = val;
-}
-
-#define TOFF(ptr) ((char *)(&(ptr)) - (char *)th)
-
-void CLASS tiff_head (struct tiff_hdr *th, int full)
-{
-  int c, psize=0;
-  struct tm *t;
-
-  memset (th, 0, sizeof *th);
-  th->t_order = htonl(0x4d4d4949) >> 16;
-  th->magic = 42;
-  th->ifd = 10;
-  if (full) {
-    tiff_set (&th->ntag, 254, 4, 1, 0);
-    tiff_set (&th->ntag, 256, 4, 1, width);
-    tiff_set (&th->ntag, 257, 4, 1, height);
-    tiff_set (&th->ntag, 258, 3, colors, output_bps);
-    if (colors > 2)
-      th->tag[th->ntag-1].val.i = TOFF(th->bps);
-    FORC4 th->bps[c] = output_bps;
-    tiff_set (&th->ntag, 259, 3, 1, 1);
-    tiff_set (&th->ntag, 262, 3, 1, 1 + (colors > 1));
-  }
-  tiff_set (&th->ntag, 270, 2, 512, TOFF(th->t_desc));
-  tiff_set (&th->ntag, 271, 2, 64, TOFF(th->t_make));
-  tiff_set (&th->ntag, 272, 2, 64, TOFF(th->t_model));
-  if (full) {
-    if (oprof) psize = ntohl(oprof[0]);
-    tiff_set (&th->ntag, 273, 4, 1, sizeof *th + psize);
-    tiff_set (&th->ntag, 277, 3, 1, colors);
-    tiff_set (&th->ntag, 278, 4, 1, height);
-    tiff_set (&th->ntag, 279, 4, 1, height*width*colors*output_bps/8);
-  } else
-    tiff_set (&th->ntag, 274, 3, 1, "12435867"[flip]-'0');
-  tiff_set (&th->ntag, 282, 5, 1, TOFF(th->rat[0]));
-  tiff_set (&th->ntag, 283, 5, 1, TOFF(th->rat[2]));
-  tiff_set (&th->ntag, 284, 3, 1, 1);
-  tiff_set (&th->ntag, 296, 3, 1, 2);
-  tiff_set (&th->ntag, 305, 2, 32, TOFF(th->soft));
-  tiff_set (&th->ntag, 306, 2, 20, TOFF(th->date));
-  tiff_set (&th->ntag, 315, 2, 64, TOFF(th->t_artist));
-  tiff_set (&th->ntag, 34665, 4, 1, TOFF(th->nexif));
-  if (psize) tiff_set (&th->ntag, 34675, 7, psize, sizeof *th);
-  tiff_set (&th->nexif, 33434, 5, 1, TOFF(th->rat[4]));
-  tiff_set (&th->nexif, 33437, 5, 1, TOFF(th->rat[6]));
-  tiff_set (&th->nexif, 34855, 3, 1, iso_speed);
-  tiff_set (&th->nexif, 37386, 5, 1, TOFF(th->rat[8]));
-  if (gpsdata[1]) {
-    tiff_set (&th->ntag, 34853, 4, 1, TOFF(th->ngps));
-    tiff_set (&th->ngps,  0, 1,  4, 0x202);
-    tiff_set (&th->ngps,  1, 2,  2, gpsdata[29]);
-    tiff_set (&th->ngps,  2, 5,  3, TOFF(th->gps[0]));
-    tiff_set (&th->ngps,  3, 2,  2, gpsdata[30]);
-    tiff_set (&th->ngps,  4, 5,  3, TOFF(th->gps[6]));
-    tiff_set (&th->ngps,  5, 1,  1, gpsdata[31]);
-    tiff_set (&th->ngps,  6, 5,  1, TOFF(th->gps[18]));
-    tiff_set (&th->ngps,  7, 5,  3, TOFF(th->gps[12]));
-    tiff_set (&th->ngps, 18, 2, 12, TOFF(th->gps[20]));
-    tiff_set (&th->ngps, 29, 2, 12, TOFF(th->gps[23]));
-    memcpy (th->gps, gpsdata, sizeof th->gps);
-  }
-  th->rat[0] = th->rat[2] = 300;
-  th->rat[1] = th->rat[3] = 1;
-  FORC(6) th->rat[4+c] = 1000000;
-  th->rat[4] *= shutter;
-  th->rat[6] *= aperture;
-  th->rat[8] *= focal_len;
-  strncpy (th->t_desc, desc, 512);
-  strncpy (th->t_make, make, 64);
-  strncpy (th->t_model, model, 64);
-  strcpy (th->soft, "dcraw v" DCRAW_VERSION);
-  t = gmtime (&timestamp);
-  sprintf (th->date, "%04d:%02d:%02d %02d:%02d:%02d",
-      t->tm_year+1900,t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
-  strncpy (th->t_artist, artist, 64);
-}
-
-void CLASS jpeg_thumb_writer (FILE *tfp,char *t_humb,int t_humb_length)
-{
-  ushort exif[5];
-  struct tiff_hdr th;
-  fputc (0xff, tfp);
-  fputc (0xd8, tfp);
-  if (strcmp (t_humb+6, "Exif")) {
-    memcpy (exif, "\xff\xe1  Exif\0\0", 10);
-    exif[1] = htons (8 + sizeof th);
-    fwrite (exif, 1, sizeof exif, tfp);
-    tiff_head (&th, 0);
-    fwrite (&th, 1, sizeof th, tfp);
-  }
-  fwrite (t_humb+2, 1, t_humb_length-2, tfp);
-}
-
-
-void CLASS jpeg_thumb (FILE *tfp)
-{
-  char *thumb;
-  ushort exif[5];
-  struct tiff_hdr th;
-
-  thumb = (char *) malloc (thumb_length);
-  merror (thumb, "jpeg_thumb()");
-  fread (thumb, 1, thumb_length, ifp);
-#if 0
-  fputc (0xff, tfp);
-  fputc (0xd8, tfp);
-  if (strcmp (thumb+6, "Exif")) {
-    memcpy (exif, "\xff\xe1  Exif\0\0", 10);
-    exif[1] = htons (8 + sizeof th);
-    fwrite (exif, 1, sizeof exif, tfp);
-    tiff_head (&th, 0);
-    fwrite (&th, 1, sizeof th, tfp);
-  }
-  fwrite (thumb+2, 1, thumb_length-2, tfp);
-#else
-  jpeg_thumb_writer(tfp,thumb,thumb_length);
-#endif
-  free (thumb);
-}
-
-void CLASS write_ppm_tiff (FILE *ofp)
-{
-  struct tiff_hdr th;
-  uchar *ppm;
-  ushort *ppm2,lut16[0x10000];
-  int c, row, col, soff, rstep, cstep;
-
-  iheight = height;
-  iwidth  = width;
-  if (flip & 4) SWAP(height,width);
-  ppm = (uchar *) calloc (width, colors*output_bps/8);
-  ppm2 = (ushort *) ppm;
-  merror (ppm, "write_ppm_tiff()");
-  if (output_tiff) {
-    tiff_head (&th, 1);
-    fwrite (&th, sizeof th, 1, ofp);
-    if (oprof)
-      fwrite (oprof, ntohl(oprof[0]), 1, ofp);
-  } else if (colors > 3)
-    fprintf (ofp,
-      "P7\nWIDTH %d\nHEIGHT %d\nDEPTH %d\nMAXVAL %d\nTUPLTYPE %s\nENDHDR\n",
-	width, height, colors, (1 << output_bps)-1, cdesc);
-  else
-    fprintf (ofp, "P%d\n%d %d\n%d\n",
-	colors/2+5, width, height, (1 << output_bps)-1);
 
-  if (output_bps == 8 || gamma_16bit ) gamma_lut (lut16);
+#include "dcraw_defs.h"
 
-  soff  = flip_index (0, 0);
-  cstep = flip_index (0, 1) - soff;
-  rstep = flip_index (1, 0) - flip_index (0, width);
-  for (row=0; row < height; row++, soff += rstep) {
-    for (col=0; col < width; col++, soff += cstep)
-      if (output_bps == 8)
-	   FORCC ppm [col*colors+c] = lut16[image[soff][c]]/256;
-      else if(gamma_16bit) FORCC ppm2[col*colors+c] =     lut16[image[soff][c]];
-      else FORCC ppm2[col*colors+c] =     image[soff][c];
-    if (output_bps == 16 && !output_tiff && htons(0x55aa) != 0x55aa)
-        swab ((char*)ppm2, (char*)ppm2, width*colors*2);
-    fwrite (ppm, colors*output_bps/8, width, ofp);
-  }
-  free (ppm);
-}
+#include "../src/utils/read_utils.cpp"
+#include "../src/utils/curves.cpp"
+#include "../src/utils/utils_dcraw.cpp"
+
+#include "../src/tables/colordata.cpp"
+
+#include "../src/decoders/canon_600.cpp"
+#include "../src/decoders/decoders_dcraw.cpp"
+#include "../src/decoders/decoders_libraw_dcrdefs.cpp"
+#include "../src/decoders/generic.cpp"
+#include "../src/decoders/kodak_decoders.cpp"
+#include "../src/decoders/dng.cpp"
+#include "../src/decoders/smal.cpp"
+#include "../src/decoders/load_mfbacks.cpp"
+
+#include "../src/metadata/sony.cpp"
+#include "../src/metadata/nikon.cpp"
+#include "../src/metadata/samsung.cpp"
+#include "../src/metadata/cr3_parser.cpp"
+#include "../src/metadata/canon.cpp"
+#include "../src/metadata/epson.cpp"
+#include "../src/metadata/olympus.cpp"
+#include "../src/metadata/leica.cpp"
+#include "../src/metadata/fuji.cpp"
+#include "../src/metadata/adobepano.cpp"
+#include "../src/metadata/pentax.cpp"
+#include "../src/metadata/p1.cpp"
+#include "../src/metadata/makernotes.cpp"
+#include "../src/metadata/exif_gps.cpp"
+#include "../src/metadata/kodak.cpp"
+#include "../src/metadata/tiff.cpp"
+#include "../src/metadata/ciff.cpp"
+#include "../src/metadata/mediumformat.cpp"
+#include "../src/metadata/minolta.cpp"
+#include "../src/metadata/identify_tools.cpp"
+#include "../src/metadata/normalize_model.cpp"
+#include "../src/metadata/identify.cpp"
+#include "../src/metadata/hasselblad_model.cpp"
+#include "../src/metadata/misc_parsers.cpp"
+#include "../src/tables/wblists.cpp"
+#include "../src/postprocessing/postprocessing_aux.cpp"
+#include "../src/postprocessing/postprocessing_utils_dcrdefs.cpp"
+#include "../src/postprocessing/aspect_ratio.cpp"
+
+#include "../src/demosaic/misc_demosaic.cpp"
+#include "../src/demosaic/xtrans_demosaic.cpp"
+#include "../src/demosaic/ahd_demosaic.cpp"
+#include "../src/write/file_write.cpp"
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/internal/dcraw_defs.h libkdcraw/libkdcraw/libraw/internal/dcraw_defs.h
--- libkdcraw-wrk/libkdcraw/libraw/internal/dcraw_defs.h	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/internal/dcraw_defs.h	2022-11-07 07:46:31.726795008 +0300
@@ -0,0 +1,62 @@
+/* -*- C++ -*-
+ * Copyright 2020 LibRaw LLC (info@libraw.org)
+ *
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#ifndef DCRAW_DEFS_H
+#define DCRAW_DEFS_H
+
+#include <math.h>
+#define LIBRAW_LIBRARY_BUILD
+#define LIBRAW_IO_REDEFINED
+#include "libraw/libraw.h"
+#include "libraw/libraw_types.h"
+#include "internal/defines.h"
+#include "internal/var_defines.h"
+
+#define stmread(buf, maxlen, fp) stread(buf, MIN(maxlen, sizeof(buf)), fp)
+#define strbuflen(buf) strnlen(buf, sizeof(buf) - 1)
+#define makeIs(idx) (maker_index == idx)
+#define strnXcat(buf, string)                                                  \
+  strncat(buf, string, LIM(sizeof(buf) - strbuflen(buf) - 1, 0, sizeof(buf)))
+
+// DNG was written by:
+#define nonDNG 0
+#define CameraDNG 1
+#define AdobeDNG 2
+
+// Makernote tag type:
+#define is_0x927c 0 /* most cameras */
+#define is_0xc634 2 /* Adobe DNG, Sony SR2, Pentax */
+#define ilm imgdata.lens.makernotes
+#define icWBC imgdata.color.WB_Coeffs
+#define icWBCCTC imgdata.color.WBCT_Coeffs
+#define imCanon imgdata.makernotes.canon
+#define imFuji imgdata.makernotes.fuji
+#define imHassy imgdata.makernotes.hasselblad
+#define imKodak imgdata.makernotes.kodak
+#define imNikon imgdata.makernotes.nikon
+#define imOly imgdata.makernotes.olympus
+#define imPana imgdata.makernotes.panasonic
+#define imPentax imgdata.makernotes.pentax
+#define imSamsung imgdata.makernotes.samsung
+#define imSony imgdata.makernotes.sony
+#define imCommon imgdata.makernotes.common
+
+
+#define ph1_bits(n) ph1_bithuff(n, 0)
+#define ph1_huff(h) ph1_bithuff(*h, h + 1)
+#define getbits(n) getbithuff(n, 0)
+#define gethuff(h) getbithuff(*h, h + 1)
+
+#endif
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/internal/dcraw_fileio.cpp libkdcraw/libkdcraw/libraw/internal/dcraw_fileio.cpp
--- libkdcraw-wrk/libkdcraw/libraw/internal/dcraw_fileio.cpp	2022-11-07 08:15:53.614821808 +0300
+++ libkdcraw/libkdcraw/libraw/internal/dcraw_fileio.cpp	2022-11-07 07:46:31.726795008 +0300
@@ -1,213 +1,28 @@
-/* 
-   GENERATED FILE, DO NOT EDIT
-   Generated from dcraw/dcraw.c at Tue Apr  7 15:14:50 2009
-   Look into original file (probably http://cybercom.net/~dcoffin/dcraw/dcraw.c)
-   for copyright information.
+/*
+  Copyright 2008-2020 LibRaw LLC (info@libraw.org)
+
+ * This file is provided for compatibility w/ old build scripts/tools:
+ * It includes multiple separate files that should be built separately
+ * if new build tools are used
+
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+   This file is generated from Dave Coffin's dcraw.c
+   dcraw.c -- Dave Coffin's raw photo decoder
+   Copyright 1997-2010 by Dave Coffin, dcoffin a cybercom o net
+
+   Look into dcraw homepage (probably http://cybercom.net/~dcoffin/dcraw/)
+   for more information
 */
 
-#define CLASS LibRaw::
-#include "libraw/libraw_types.h"
-#define LIBRAW_LIBRARY_BUILD
-#include "libraw/libraw.h"
-#include "internal/defines.h"
-#include "internal/var_defines.h"
+#include "dcraw_fileio_defs.h"
 
-/*
-   Seach from the current directory up to the root looking for
-   a ".badpixels" file, and fix those pixels now.
- */
-void CLASS bad_pixels (char *fname)
-{
-  FILE *fp=0;
-  char *cp, line[128];
-  int len, time, row, col, r, c, rad, tot, n, fixed=0;
-
-  if (!filters) return;
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_BAD_PIXELS,0,2);
-#endif
-  if (fname)
-    fp = fopen (fname, "r");
-  if (!fp) 
-      {
-#ifdef LIBRAW_LIBRARY_BUILD
-          imgdata.process_warnings |= LIBRAW_WARN_NO_BADPIXELMAP;
-#endif
-          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 += BAYER2(r,c);
-	    n++;
-	  }
-    BAYER2(row,col) = tot/n;
-#ifdef DCRAW_VERBOSE
-    if (verbose) {
-      if (!fixed++)
-	fprintf (stderr,_("Fixed dead pixels at:"));
-      fprintf (stderr, " %d,%d", col, row);
-    }
-#endif
-  }
-#ifdef DCRAW_VERBOSE
-  if (fixed) fputc ('\n', stderr);
-#endif
-  fclose (fp);
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_BAD_PIXELS,1,2);
-#endif
-}
-
-void CLASS subtract (char *fname)
-{
-  FILE *fp;
-  int dim[3]={0,0,0}, comment=0, number=0, error=0, nd=0, c, row, col;
-  ushort *pixel;
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_DARK_FRAME,0,2);
-#endif
-
-  if (!(fp = fopen (fname, "rb"))) {
-#ifdef DCRAW_VERBOSE
-    perror (fname); 
-#endif
-#ifdef LIBRAW_LIBRARY_BUILD
-    imgdata.process_warnings |= LIBRAW_WARN_BAD_DARKFRAME_FILE;
-#endif
-    return;
-  }
-  if (fgetc(fp) != 'P' || fgetc(fp) != '5') error = 1;
-  while (!error && nd < 3 && (c = fgetc(fp)) != EOF) {
-    if (c == '#')  comment = 1;
-    if (c == '\n') comment = 0;
-    if (comment) continue;
-    if (isdigit(c)) number = 1;
-    if (number) {
-      if (isdigit(c)) dim[nd] = dim[nd]*10 + c -'0';
-      else if (isspace(c)) {
-	number = 0;  nd++;
-      } else error = 1;
-    }
-  }
-  if (error || nd < 3) {
-    fprintf (stderr,_("%s is not a valid PGM file!\n"), fname);
-    fclose (fp);  return;
-  } else if (dim[0] != width || dim[1] != height || dim[2] != 65535) {
-#ifdef DCRAW_VERBOSE
-      fprintf (stderr,_("%s has the wrong dimensions!\n"), fname);
-#endif
-#ifdef LIBRAW_LIBRARY_BUILD
-      imgdata.process_warnings |= LIBRAW_WARN_BAD_DARKFRAME_DIM;
-#endif
-    fclose (fp);  return;
-  }
-  pixel = (ushort *) calloc (width, sizeof *pixel);
-  merror (pixel, "subtract()");
-  for (row=0; row < height; row++) {
-    fread (pixel, 2, width, fp);
-    for (col=0; col < width; col++)
-      BAYER(row,col) = MAX (BAYER(row,col) - ntohs(pixel[col]), 0);
-  }
-  free (pixel);
-  black = 0;
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_DARK_FRAME,1,2);
-#endif
-}
-
-#ifndef NO_LCMS
-void CLASS apply_profile (char *input, char *output)
-{
-  char *prof;
-  cmsHPROFILE hInProfile=0, hOutProfile=0;
-  cmsHTRANSFORM hTransform;
-  FILE *fp;
-  unsigned size;
-
-#if LCMS_VERSION < 2000
-  cmsErrorAction (LCMS_ERROR_SHOW);
-#endif
-  if (strcmp (input, "embed"))
-    hInProfile = cmsOpenProfileFromFile (input, "r");
-  else if (profile_length) {
-#ifndef LIBRAW_LIBRARY_BUILD
-    prof = (char *) 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);
-#else
-    hInProfile = cmsOpenProfileFromMem (imgdata.color.profile, profile_length);
-#endif
-  } else
-      {
-#ifdef LIBRAW_LIBRARY_BUILD
-          imgdata.process_warnings |= LIBRAW_WARN_NO_EMBEDDED_PROFILE;
-#endif
-#ifdef DCRAW_VERBOSE
-          fprintf (stderr,_("%s has no embedded profile.\n"), ifname);
-#endif
-      }
-  if (!hInProfile)
-      {
-#ifdef LIBRAW_LIBRARY_BUILD
-          imgdata.process_warnings |= LIBRAW_WARN_NO_INPUT_PROFILE;
-#endif
-          return;
-      }
-  if (!output)
-    hOutProfile = cmsCreate_sRGBProfile();
-  else if ((fp = fopen (output, "rb"))) {
-    fread (&size, 4, 1, fp);
-    fseek (fp, 0, SEEK_SET);
-    oprof = (unsigned *) malloc (size = ntohl(size));
-    merror (oprof, "apply_profile()");
-    fread (oprof, 1, size, fp);
-    fclose (fp);
-    if (!(hOutProfile = cmsOpenProfileFromMem (oprof, size))) {
-      free (oprof);
-      oprof = 0;
-    }
-#ifdef DCRAW_VERBOSE
-  } else
-    fprintf (stderr,_("Cannot open file %s!\n"), output);
-#else
-}
-#endif
-  if (!hOutProfile)
-      {
-#ifdef LIBRAW_LIBRARY_BUILD
-          imgdata.process_warnings |= LIBRAW_WARN_BAD_OUTPUT_PROFILE;
-#endif
-          goto quit;
-      }
-#ifdef DCRAW_VERBOSE
-  if (verbose)
-    fprintf (stderr,_("Applying color profile...\n"));
-#endif
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_APPLY_PROFILE,0,2);
-#endif
-  hTransform = cmsCreateTransform (hInProfile, TYPE_RGBA_16,
-	hOutProfile, TYPE_RGBA_16, INTENT_PERCEPTUAL, 0);
-  cmsDoTransform (hTransform, image, image, width*height);
-  raw_color = 1;		/* Don't use rgb_cam with a profile */
-  cmsDeleteTransform (hTransform);
-  cmsCloseProfile (hOutProfile);
-quit:
-  cmsCloseProfile (hInProfile);
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_APPLY_PROFILE,1,2);
-#endif
-}
-#endif
+#include "../src/preprocessing/ext_preprocess.cpp"
+#include "../src/write/apply_profile.cpp"
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/internal/dcraw_fileio_defs.h libkdcraw/libkdcraw/libraw/internal/dcraw_fileio_defs.h
--- libkdcraw-wrk/libkdcraw/libraw/internal/dcraw_fileio_defs.h	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/internal/dcraw_fileio_defs.h	2022-11-07 07:46:31.726795008 +0300
@@ -0,0 +1,25 @@
+/* -*- C++ -*-
+ * Copyright 2020 LibRaw LLC (info@libraw.org)
+ *
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#ifndef DCRAW_FILEIO_DEFS_H
+#define DCRAW_FILEIO_DEFS_H
+
+#include <math.h>
+#define LIBRAW_LIBRARY_BUILD
+#include "libraw/libraw.h"
+#include "internal/defines.h"
+#include "internal/var_defines.h"
+
+#endif
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/internal/defines.h libkdcraw/libkdcraw/libraw/internal/defines.h
--- libkdcraw-wrk/libkdcraw/libraw/internal/defines.h	2022-11-07 08:15:53.614821808 +0300
+++ libkdcraw/libkdcraw/libraw/internal/defines.h	2022-11-07 07:46:31.726795008 +0300
@@ -1,17 +1,37 @@
-/* 
-   GENERATED FILE, DO NOT EDIT
-   Generated from dcraw/dcraw.c at Tue Apr  7 15:14:48 2009
-   Look into original file (probably http://cybercom.net/~dcoffin/dcraw/dcraw.c)
-   for copyright information.
+/*
+  Copyright 2008-2020 LibRaw LLC (info@libraw.org)
+
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+   This file is generated from Dave Coffin's dcraw.c
+   dcraw.c -- Dave Coffin's raw photo decoder
+   Copyright 1997-2010 by Dave Coffin, dcoffin a cybercom o net
+
+   Look into dcraw homepage (probably http://cybercom.net/~dcoffin/dcraw/)
+   for more information
 */
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
 
+#ifndef LIBRAW_INT_DEFINES_H
+#define LIBRAW_INT_DEFINES_H
+#ifndef USE_JPEG
 #define NO_JPEG
-#define DCRAW_VERSION "8.93"
-#define _USE_MATH_DEFINES
+#endif
+#ifndef USE_JASPER
+#define NO_JASPER
+#endif
+#define DCRAW_VERSION "9.26"
 
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#define _USE_MATH_DEFINES
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -24,38 +44,18 @@
 #include <string.h>
 #include <time.h>
 #include <sys/types.h>
-
-#ifdef _OPENMP
-#include <omp.h>
-#endif
-/*
-   NO_JPEG disables decoding of compressed Kodak DC120 files.
-   NO_LCMS disables the "-p" option.
- */
-#ifndef NO_JPEG
-#include <jpeglib.h>
-#endif
-#ifndef NO_LCMS
-#include LCMS_HEADER
-#endif
-#ifdef LOCALEDIR
-#include <libintl.h>
-#define _(String) gettext(String)
-#else
-#define _(String) (String)
-#endif
 #ifdef __CYGWIN__
 #include <io.h>
 #endif
-#ifdef WIN32
+#if defined LIBRAW_WIN32_CALLS
 #include <sys/utime.h>
+#ifndef LIBRAW_NO_WINSOCK2
 #include <winsock2.h>
 #pragma comment(lib, "ws2_32.lib")
+#endif
 #define snprintf _snprintf
-#define strcasecmp _stricmp
+#define strcasecmp stricmp
 #define strncasecmp strnicmp
-typedef __int64 INT64;
-typedef unsigned __int64 UINT64;
 #else
 #include <unistd.h>
 #include <utime.h>
@@ -64,27 +64,74 @@
 typedef unsigned long long UINT64;
 #endif
 
+#ifdef NODEPS
+#define NO_JASPER
+#define NO_JPEG
+#define NO_LCMS
+#endif
+#ifndef NO_JASPER
+#include <jasper/jasper.h> /* Decode Red camera movies */
+#endif
+#ifndef NO_JPEG
+#include <jpeglib.h> /* Decode compressed Kodak DC120 photos */
+#endif               /* and Adobe Lossy DNGs */
+#ifndef NO_LCMS
+#ifdef USE_LCMS
+#include <lcms.h> /* Support color profiles */
+#else
+#include <lcms2.h> /* Support color profiles */
+#endif
+#endif
+#ifdef LOCALEDIR
+#include <libintl.h>
+#define _(String) gettext(String)
+#else
+#define _(String) (String)
+#endif
+
 #ifdef LJPEG_DECODE
 #error Please compile dcraw.c by itself.
 #error Do not link it with ljpeg_decode.
 #endif
 
 #ifndef LONG_BIT
-#define LONG_BIT (8 * sizeof (long))
+#define LONG_BIT (8 * sizeof(long))
 #endif
-#define FORC(cnt) for (c=0; c < cnt; c++)
+#define FORC(cnt) for (c = 0; c < cnt; c++)
 #define FORC3 FORC(3)
 #define FORC4 FORC(4)
-#define FORCC FORC(colors)
+#define FORCC for (c = 0; c < colors && c < 4; c++)
 
-#define SQR(x) ((x)*(x))
+#define SQR(x) ((x) * (x))
 #define ABS(x) (((int)(x) ^ ((int)(x) >> 31)) - ((int)(x) >> 31))
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
-#define MAX(a,b) ((a) > (b) ? (a) : (b))
-#define LIM(x,min,max) MAX(min,MIN(x,max))
-#define ULIM(x,y,z) ((y) < (z) ? LIM(x,y,z) : LIM(x,z,y))
-#define CLIP(x) LIM(x,0,65535)
-#define SWAP(a,b) { a ^= b; a ^= (b ^= a); }
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define LIM(x, min, max) MAX(min, MIN(x, max))
+#define ULIM(x, y, z) ((y) < (z) ? LIM(x, y, z) : LIM(x, z, y))
+#define CLIP(x) LIM((int)(x), 0, 65535)
+#define CLIP15(x) LIM((int)(x), 0, 32767)
+#define SWAP(a, b)                                                             \
+  {                                                                            \
+    a = a + b;                                                                 \
+    b = a - b;                                                                 \
+    a = a - b;                                                                 \
+  }
+
+#define my_swap(type, i, j)                                                    \
+  {                                                                            \
+    type t = i;                                                                \
+    i = j;                                                                     \
+    j = t;                                                                     \
+  }
+
+#ifdef __GNUC__
+inline
+#elif defined(_MSC_VER)
+__forceinline
+#else
+static
+#endif
+float fMAX(float a, float b) { return MAX(a, b); }
 
 /*
    In order to inline this calculation, I make the risky
@@ -96,37 +143,50 @@
 
    Return values are either 0/1/2/3 = G/M/C/Y or 0/1/2/3 = R/G1/B/G2
 
-	PowerShot 600	PowerShot A50	PowerShot Pro70	Pro90 & G1
-	0xe1e4e1e4:	0x1b4e4b1e:	0x1e4b4e1b:	0xb4b4b4b4:
+        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
+          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:
+        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
+          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
  */
 
-#define BAYER(row,col) \
-	image[((row) >> shrink)*iwidth + ((col) >> shrink)][FC(row,col)]
+// _RGBG means R, G1, B, G2 sequence
+#define GRBG_2_RGBG(q)    (q ^ (q >> 1) ^ 1)
+#define RGGB_2_RGBG(q)    (q ^ (q >> 1))
+#define BG2RG1_2_RGBG(q)  (q ^ 2)
+#define GRGB_2_RGBG(q)    (q ^ 1)
+#define RBGG_2_RGBG(q)    ((q >> 1) | ((q & 1) << 1))
+
+#define RAWINDEX(row, col) ((row)*raw_width + (col))
+#define RAW(row, col) raw_image[(row)*raw_width + (col)]
+#define BAYER(row, col)                                                        \
+  image[((row) >> shrink) * iwidth + ((col) >> shrink)][FC(row, col)]
+
+#define BAYER2(row, col)                                                       \
+  image[((row) >> shrink) * iwidth + ((col) >> shrink)][fcol(row, col)]
+#define BAYERC(row, col, c)                                                    \
+  imgdata.image[((row) >> IO.shrink) * S.iwidth + ((col) >> IO.shrink)][c]
 
-#define BAYER2(row,col) \
-	image[((row) >> shrink)*iwidth + ((col) >> shrink)][fc(row,col)]
+#endif
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/internal/demosaic_packs.cpp libkdcraw/libkdcraw/libraw/internal/demosaic_packs.cpp
--- libkdcraw-wrk/libkdcraw/libraw/internal/demosaic_packs.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/internal/demosaic_packs.cpp	2022-11-07 07:46:31.726795008 +0300
@@ -0,0 +1,35 @@
+/*
+  Copyright 2008-2013 LibRaw LLC (info@libraw.org)
+
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of two licenses as you choose:
+
+ * This file is provided for compatibility w/ old build scripts/tools:
+ * It includes multiple separate files that should be built separately
+ * if new build tools are used
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+*/
+
+#include <math.h>
+
+#define LIBRAW_LIBRARY_BUILD
+#define LIBRAW_IO_REDEFINED
+#include "libraw/libraw.h"
+#include "internal/defines.h"
+#define SRC_USES_SHRINK
+#define SRC_USES_BLACK
+#define SRC_USES_CURVE
+
+/* DHT and AAHD are LGPL licensed, so include them */
+#include "../src/demosaic/dht_demosaic.cpp"
+#include "../src/demosaic/aahd_demosaic.cpp"
+#include "internal/var_defines.h"
+
+/* DCB is BSD licensed, so include it */
+#include "../src/demosaic/dcb_demosaic.cpp"
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/internal/dmp_include.h libkdcraw/libkdcraw/libraw/internal/dmp_include.h
--- libkdcraw-wrk/libkdcraw/libraw/internal/dmp_include.h	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/internal/dmp_include.h	2022-11-07 07:46:31.726795008 +0300
@@ -0,0 +1,27 @@
+/* -*- C++ -*-
+ * Copyright 2020 LibRaw LLC (info@libraw.org)
+ *
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#ifndef DMP_INCLUDE_H
+#define DMP_INCLUDE_H
+
+#define LIBRAW_LIBRARY_BUILD
+#define LIBRAW_IO_REDEFINED
+#include "libraw/libraw.h"
+#include "internal/defines.h"
+#define SRC_USES_SHRINK
+#define SRC_USES_BLACK
+#define SRC_USES_CURVE
+
+#endif
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/internal/foveon.cpp libkdcraw/libkdcraw/libraw/internal/foveon.cpp
--- libkdcraw-wrk/libkdcraw/libraw/internal/foveon.cpp	2022-11-07 08:15:53.614821808 +0300
+++ libkdcraw/libkdcraw/libraw/internal/foveon.cpp	1970-01-01 03:00:00.000000000 +0300
@@ -1,812 +0,0 @@
-/* 
-   GENERATED FILE, DO NOT EDIT
-   Generated from dcraw/dcraw.c at Tue Apr  7 15:14:48 2009
-   Look into original file (probably http://cybercom.net/~dcoffin/dcraw/dcraw.c)
-   for copyright information.
-*/
-
-#define CLASS LibRaw::
-#include "libraw/libraw_types.h"
-#define LIBRAW_IO_REDEFINED
-#define LIBRAW_LIBRARY_BUILD
-#include "libraw/libraw.h"
-#include "internal/defines.h"
-#define SRC_USES_SHRINK
-#define SRC_USES_BLACK
-#define SRC_USES_CURVE
-#include "internal/var_defines.h"
-#define sget4(s) sget4((uchar *)s)
-
-/* RESTRICTED code starts here */
-
-void CLASS foveon_decoder (unsigned size, unsigned code)
-{
-#ifndef LIBRAW_NOTHREADS
-#define huff tls->foveon_decoder_huff
-#else
-  static unsigned huff[1024];
-#endif
-  struct decode *cur;
-  int i, len;
-
-  if (!code) {
-    for (i=0; i < size; i++)
-      huff[i] = get4();
-    init_decoder();
-  }
-  cur = free_decode++;
-  if (free_decode > first_decode+2048) {
-#ifdef LIBRAW_LIBRARY_BUILD
-      throw LIBRAW_EXCEPTION_DECODE_RAW;
-#else
-    fprintf (stderr,_("%s: decoder table overflow\n"), ifname);
-    longjmp (failure, 2);
-#endif
-  }
-  if (code)
-    for (i=0; i < size; i++)
-      if (huff[i] == code) {
-	cur->leaf = i;
-	return;
-      }
-  if ((len = code >> 27) > 26) return;
-  code = (len+1) << 27 | (code & 0x3ffffff) << 1;
-
-  cur->branch[0] = free_decode;
-  foveon_decoder (size, code);
-  cur->branch[1] = free_decode;
-  foveon_decoder (size, code+1);
-#ifndef LIBRAW_NOTHREADS
-#undef huff
-#endif
-}
-
-void CLASS foveon_thumb (FILE *tfp)
-{
-  unsigned bwide, row, col, bitbuf=0, bit=1, c, i;
-  char *buf;
-  struct decode *dindex;
-  short pred[3];
-
-  bwide = get4();
-  fprintf (tfp, "P6\n%d %d\n255\n", thumb_width, thumb_height);
-  if (bwide > 0) {
-    if (bwide < thumb_width*3) return;
-    buf = (char *) malloc (bwide);
-    merror (buf, "foveon_thumb()");
-    for (row=0; row < thumb_height; row++) {
-      fread  (buf, 1, bwide, ifp);
-      fwrite (buf, 3, thumb_width, tfp);
-    }
-    free (buf);
-    return;
-  }
-  foveon_decoder (256, 0);
-
-  for (row=0; row < thumb_height; row++) {
-    memset (pred, 0, sizeof pred);
-    if (!bit) get4();
-    for (bit=col=0; col < thumb_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] += dindex->leaf;
-	fputc (pred[c], tfp);
-      }
-  }
-}
-
-void CLASS foveon_load_camf()
-{
-  unsigned key, i, val;
-
-  fseek (ifp, meta_offset, SEEK_SET);
-  key = get4();
-  fread (meta_data, 1, meta_length, ifp);
-  for (i=0; i < meta_length; i++) {
-    key = (key * 1597 + 51749) % 244944;
-    val = key * (INT64) 301593171 >> 24;
-    meta_data[i] ^= ((((key << 8) - val) >> 1) + val) >> 17;
-  }
-}
-
-void CLASS foveon_load_raw()
-{
-  struct decode *dindex;
-  short diff[1024];
-  unsigned bitbuf=0;
-  int pred[3], fixed, row, col, bit=-1, c, i;
-
-  fixed = get4();
-  read_shorts ((ushort *) diff, 1024);
-  if (!fixed) foveon_decoder (1024, 0);
-
-  for (row=0; row < height; row++) {
-    memset (pred, 0, sizeof pred);
-    if (!bit && !fixed && atoi(model+2) < 14) get4();
-    for (col=bit=0; col < width; col++) {
-      if (fixed) {
-	bitbuf = get4();
-	FORC3 pred[2-c] += diff[bitbuf >> c*10 & 0x3ff];
-      }
-      else 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];
-	if (pred[c] >> 16 && ~pred[c] >> 16) derror();
-      }
-      FORC3 image[row*width+col][c] = pred[c];
-    }
-  }
-  if (document_mode)
-    for (i=0; i < height*width*4; i++)
-      if ((short) image[0][i] < 0) image[0][i] = 0;
-  foveon_load_camf();
-}
-
-const char * CLASS foveon_camf_param (const char *block, const char *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 0;
-}
-
-void * CLASS foveon_camf_matrix (unsigned dim[3], const char *name)
-{
-  unsigned i, idx, type, ndim, size, *mat;
-  char *pos, *cp, *dp;
-  double dsize;
-
-  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 ((dsize = (double) dim[0]*dim[1]*dim[2]) > meta_length/4) break;
-    mat = (unsigned *) malloc ((size = dsize) * 4);
-    merror (mat, "foveon_camf_matrix()");
-    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;
-  }
-#ifdef LIBRAW_LIBRARY_BUILD
-  imgdata.process_warnings |= LIBRAW_WARN_FOVEON_NOMATRIX;
-#endif
-#ifdef DCRAW_VERBOSE
-  fprintf (stderr,_("%s: \"%s\" matrix not found!\n"), ifname, name);
-#endif
-  return 0;
-}
-
-int CLASS foveon_fixed (void *ptr, int size, const char *name)
-{
-  void *dp;
-  unsigned dim[3];
-
-  dp = foveon_camf_matrix (dim, name);
-  if (!dp) return 0;
-  memcpy (ptr, dp, size*4);
-  free (dp);
-  return 1;
-}
-
-float CLASS foveon_avg (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 = pix[i*4] + (pix[i*4]-pix[(i-1)*4]) * cfilt;
-    if (min > val) min = val;
-    if (max < val) max = val;
-  }
-  if (range[1] - range[0] == 1) return sum/2;
-  return (sum - min - max) / (range[1] - range[0] - 1);
-}
-
-short * CLASS foveon_make_curve (double max, double mul, double filt)
-{
-  short *curve;
-  unsigned i, size;
-  double x;
-
-  if (!filt) filt = 0.8;
-  size = 4*M_PI*max / filt;
-  if (size == UINT_MAX) size--;
-  curve = (short *) calloc (size+1, sizeof *curve);
-  merror (curve, "foveon_make_curve()");
-  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;
-}
-
-void CLASS 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);
-}
-
-int CLASS foveon_apply_curve (short *curve, int i)
-{
-  if (abs(i) >= curve[0]) return 0;
-  return i < 0 ? -curve[1-i] : curve[1+i];
-}
-
-#ifdef image
-#undef image
-#endif
-#define image ((short(*)[4]) imgdata.image)
-
-void CLASS foveon_interpolate()
-{
-  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, 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 dscr[2][2], dstb[4], (*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 dim[3], *badpix;
-  double dsum=0, trsum[3];
-  char str[128];
-  const char* cp;
-
-
-#ifdef DCRAW_VERBOSE
-  if (verbose)
-    fprintf(stderr,_("Foveon interpolation...\n"));
-#endif
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_FOVEON_INTERPOLATE,0,9);
-#endif  
-
-  foveon_fixed (dscr, 4, "DarkShieldColRange");
-  foveon_fixed (ppm[0][0], 27, "PostPolyMatrix");
-  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" : "ColorDTQCamRGB");
-  if (foveon_camf_param ("IncludeBlocks", "ColumnFilter"))
-  		 foveon_fixed (&cfilt, 1, "ColumnFilter");
-
-  memset (ddft, 0, sizeof ddft);
-  if (!foveon_camf_param ("IncludeBlocks", "DarkDrift")
-	 || !foveon_fixed (ddft[1][0], 12, "DarkDrift"))
-    for (i=0; i < 2; i++) {
-      foveon_fixed (dstb, 4, i ? "DarkShieldBottom":"DarkShieldTop");
-      for (row = dstb[1]; row <= dstb[3]; row++)
-	for (col = dstb[0]; col <= dstb[2]; col++)
-	  FORC3 ddft[i+1][c][1] += (short) image[row*width+col][c];
-      FORC3 ddft[i+1][c][1] /= (dstb[3]-dstb[1]+1) * (dstb[2]-dstb[0]+1);
-    }
-
-  if (!(cp = foveon_camf_param ("WhiteBalanceIlluminants", model2)))
-  { 
-#ifdef DCRAW_VERBOSE
-      fprintf (stderr,_("%s: Invalid white balance \"%s\"\n"), ifname, model2);
-#endif
-#ifdef LIBRAW_LIBRARY_BUILD
-      imgdata.process_warnings |= LIBRAW_WARN_FOVEON_INVALIDWB;
-#endif
-      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];
-
-  #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;
-  sprintf (str, "%sRGBNeutral", model2);
-  if (foveon_camf_param ("IncludeBlocks", str))
-    foveon_fixed (div, 3, str);
-  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] += rgb_cam[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 = (float (*)[3]) foveon_camf_matrix (dim, "SpatialGain");
-  if (!sgain) return;
-  sgrow = (float (*)[3]) calloc (dim[1], sizeof *sgrow);
-  sgx = (width + dim[1]-2) / (dim[1]-1);
-
-  black = (float (*)[3]) calloc (height, sizeof *black);
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_FOVEON_INTERPOLATE,1,9);
-#endif
-  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);
-
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_FOVEON_INTERPOLATE,2,9);
-#endif
-  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 = 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);
-
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_FOVEON_INTERPOLATE,3,9);
-#endif
-  if ((badpix = (unsigned int *) 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] += (short)
-		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] = (int (*)[3]) calloc (width*5, sizeof **smrow);
-  merror (smrow[6], "foveon_interpolate()");
-  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 = 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 = 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;
-    }
-  }
-
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_FOVEON_INTERPOLATE,4,9);
-#endif
-  /* Adjust the brighter pixels for better linearity */
-  min = 0xffff;
-  FORC3 {
-    i = satlev[c] / div[c];
-    if (min > i) min = i;
-  }
-  limit = min * 9 >> 4;
-  for (pix=image[0]; pix < 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];
-    }
-    if (min >= limit*2) {
-      pix[0] = pix[1] = pix[2] = max;
-    } else {
-      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.
- */
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_FOVEON_INTERPOLATE,5,9);
-#endif
-  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 = 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 = 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 = 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 = 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;
-    }
-  }
-
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_FOVEON_INTERPOLATE,6,9);
-#endif
-  /* Transform the image to a different colorspace */
-  for (pix=image[0]; pix < 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 */
-  shrink = (short (*)[3]) calloc ((width/4) * (height/4), sizeof *shrink);
-  merror (shrink, "foveon_interpolate()");
-  for (row = height/4; 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 */
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_FOVEON_INTERPOLATE,7,9);
-#endif
-  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]);
-#ifdef LIBRAW_LIBRARY_BUILD
-  RUN_CALLBACK(LIBRAW_PROGRESS_FOVEON_INTERPOLATE,8,9);
-#endif
-
-  /* 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;
-}
-#undef image
-
-/* RESTRICTED code ends here */
-char * CLASS foveon_gets (int offset, char *str, int len)
-{
-  int i;
-  fseek (ifp, offset, SEEK_SET);
-  for (i=0; i < len-1; i++)
-    if ((str[i] = get2()) == 0) break;
-  str[i] = 0;
-  return str;
-}
-
-void CLASS parse_foveon()
-{
-  int entries, img=0, off, len, tag, save, i, wide, high, pent, poff[256][2];
-  char name[64], value[64];
-
-  order = 0x4949;			/* Little-endian */
-  fseek (ifp, 36, SEEK_SET);
-  flip = get4();
-  fseek (ifp, -4, SEEK_END);
-  fseek (ifp, get4(), SEEK_SET);
-  if (get4() != 0x64434553) return;	/* SECd */
-  entries = (get4(),get4());
-  while (entries--) {
-    off = get4();
-    len = get4();
-    tag = get4();
-    save = ftell(ifp);
-    fseek (ifp, off, SEEK_SET);
-    if (get4() != (0x20434553 | (tag << 24))) return;
-    switch (tag) {
-      case 0x47414d49:			/* IMAG */
-      case 0x32414d49:			/* IMA2 */
-	fseek (ifp, 12, SEEK_CUR);
-	wide = get4();
-	high = get4();
-	if (wide > raw_width && high > raw_height) {
-	  raw_width  = wide;
-	  raw_height = high;
-	  data_offset = off+24;
-	}
-	fseek (ifp, off+28, SEEK_SET);
-	if (fgetc(ifp) == 0xff && fgetc(ifp) == 0xd8
-		&& thumb_length < len-28) {
-	  thumb_offset = off+28;
-	  thumb_length = len-28;
-	  write_thumb = &CLASS jpeg_thumb;
-	}
-	if (++img == 2 && !thumb_length) {
-	  thumb_offset = off+24;
-	  thumb_width = wide;
-	  thumb_height = high;
-	  write_thumb = &CLASS foveon_thumb;
-	}
-	break;
-      case 0x464d4143:			/* CAMF */
-	meta_offset = off+24;
-	meta_length = len-28;
-	if (meta_length > 0x20000)
-	    meta_length = 0x20000;
-	break;
-      case 0x504f5250:			/* PROP */
-	pent = (get4(),get4());
-	fseek (ifp, 12, SEEK_CUR);
-	off += pent*8 + 24;
-	if ((unsigned) pent > 256) pent=256;
-	for (i=0; i < pent*2; i++)
-	  poff[0][i] = off + get4()*2;
-	for (i=0; i < pent; i++) {
-	  foveon_gets (poff[i][0], name, 64);
-	  foveon_gets (poff[i][1], value, 64);
-	  if (!strcmp (name, "ISO"))
-	    iso_speed = atoi(value);
-	  if (!strcmp (name, "CAMMANUF"))
-	    strcpy (make, value);
-	  if (!strcmp (name, "CAMMODEL"))
-	    strcpy (model, value);
-	  if (!strcmp (name, "WB_DESC"))
-	    strcpy (model2, value);
-	  if (!strcmp (name, "TIME"))
-	    timestamp = atoi(value);
-	  if (!strcmp (name, "EXPTIME"))
-	    shutter = atoi(value) / 1000000.0;
-	  if (!strcmp (name, "APERTURE"))
-	    aperture = atof(value);
-	  if (!strcmp (name, "FLENGTH"))
-	    focal_len = atof(value);
-	}
-#ifdef LOCALTIME
-	timestamp = mktime (gmtime (&timestamp));
-#endif
-    }
-    fseek (ifp, save, SEEK_SET);
-  }
-  is_foveon = 1;
-}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/internal/libraw_cameraids.h libkdcraw/libkdcraw/libraw/internal/libraw_cameraids.h
--- libkdcraw-wrk/libkdcraw/libraw/internal/libraw_cameraids.h	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/internal/libraw_cameraids.h	2022-11-07 07:46:31.726795008 +0300
@@ -0,0 +1,300 @@
+/* -*- C++ -*-
+ * File: internal/libraw_cameraids.h
+ * Copyright 2008-2020 LibRaw LLC (info@libraw.org)
+ * Created: Sat Aug  17, 2020
+
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#ifndef LIBRAW_CONST_H
+#define LIBRAW_CONST_H
+
+#define CanonID_EOS_M50            0x00000412ULL
+#define CanonID_EOS_M6_Mark_II     0x00000811ULL
+#define CanonID_EOS_M200           0x00000812ULL
+#define CanonID_EOS_D30            0x01140000ULL
+#define CanonID_EOS_D60            0x01668000ULL
+#define CanonID_EOS_M3             0x03740000ULL
+#define CanonID_EOS_M10            0x03840000ULL
+#define CanonID_EOS_M5             0x03940000ULL
+#define CanonID_EOS_M100           0x03980000ULL
+#define CanonID_EOS_M6             0x04070000ULL
+#define CanonID_EOS_1D            (0x80000000ULL + 0x001ULL)
+#define CanonID_EOS_1DS           (0x80000000ULL + 0x167ULL)
+#define CanonID_EOS_10D           (0x80000000ULL + 0x168ULL)
+#define CanonID_EOS_1D_Mark_III   (0x80000000ULL + 0x169ULL)
+#define CanonID_EOS_300D          (0x80000000ULL + 0x170ULL)
+#define CanonID_EOS_1D_Mark_II    (0x80000000ULL + 0x174ULL)
+#define CanonID_EOS_20D           (0x80000000ULL + 0x175ULL)
+#define CanonID_EOS_450D          (0x80000000ULL + 0x176ULL)
+#define CanonID_EOS_1Ds_Mark_II   (0x80000000ULL + 0x188ULL)
+#define CanonID_EOS_350D          (0x80000000ULL + 0x189ULL)
+#define CanonID_EOS_40D           (0x80000000ULL + 0x190ULL)
+#define CanonID_EOS_5D            (0x80000000ULL + 0x213ULL)
+#define CanonID_EOS_1Ds_Mark_III  (0x80000000ULL + 0x215ULL)
+#define CanonID_EOS_5D_Mark_II    (0x80000000ULL + 0x218ULL)
+#define CanonID_EOS_1D_Mark_II_N  (0x80000000ULL + 0x232ULL)
+#define CanonID_EOS_30D           (0x80000000ULL + 0x234ULL)
+#define CanonID_EOS_400D          (0x80000000ULL + 0x236ULL)
+#define CanonID_EOS_7D            (0x80000000ULL + 0x250ULL)
+#define CanonID_EOS_500D          (0x80000000ULL + 0x252ULL)
+#define CanonID_EOS_1000D         (0x80000000ULL + 0x254ULL)
+#define CanonID_EOS_50D           (0x80000000ULL + 0x261ULL)
+#define CanonID_EOS_1D_X          (0x80000000ULL + 0x269ULL)
+#define CanonID_EOS_550D          (0x80000000ULL + 0x270ULL)
+#define CanonID_EOS_1D_Mark_IV    (0x80000000ULL + 0x281ULL)
+#define CanonID_EOS_5D_Mark_III   (0x80000000ULL + 0x285ULL)
+#define CanonID_EOS_600D          (0x80000000ULL + 0x286ULL)
+#define CanonID_EOS_60D           (0x80000000ULL + 0x287ULL)
+#define CanonID_EOS_1100D         (0x80000000ULL + 0x288ULL)
+#define CanonID_EOS_7D_Mark_II    (0x80000000ULL + 0x289ULL)
+#define CanonID_EOS_650D          (0x80000000ULL + 0x301ULL)
+#define CanonID_EOS_6D            (0x80000000ULL + 0x302ULL)
+#define CanonID_EOS_1D_C          (0x80000000ULL + 0x324ULL)
+#define CanonID_EOS_70D           (0x80000000ULL + 0x325ULL)
+#define CanonID_EOS_700D          (0x80000000ULL + 0x326ULL)
+#define CanonID_EOS_1200D         (0x80000000ULL + 0x327ULL)
+#define CanonID_EOS_1D_X_Mark_II  (0x80000000ULL + 0x328ULL)
+#define CanonID_EOS_M             (0x80000000ULL + 0x331ULL)
+#define CanonID_EOS_100D          (0x80000000ULL + 0x346ULL)
+#define CanonID_EOS_760D          (0x80000000ULL + 0x347ULL)
+#define CanonID_EOS_5D_Mark_IV    (0x80000000ULL + 0x349ULL)
+#define CanonID_EOS_80D           (0x80000000ULL + 0x350ULL)
+#define CanonID_EOS_M2            (0x80000000ULL + 0x355ULL)
+#define CanonID_EOS_5DS           (0x80000000ULL + 0x382ULL)
+#define CanonID_EOS_750D          (0x80000000ULL + 0x393ULL)
+#define CanonID_EOS_5DS_R         (0x80000000ULL + 0x401ULL)
+#define CanonID_EOS_1300D         (0x80000000ULL + 0x404ULL)
+#define CanonID_EOS_800D          (0x80000000ULL + 0x405ULL)
+#define CanonID_EOS_6D_Mark_II    (0x80000000ULL + 0x406ULL)
+#define CanonID_EOS_77D           (0x80000000ULL + 0x408ULL)
+#define CanonID_EOS_200D          (0x80000000ULL + 0x417ULL)
+#define CanonID_EOS_3000D         (0x80000000ULL + 0x422ULL)
+#define CanonID_EOS_R             (0x80000000ULL + 0x424ULL)
+#define CanonID_EOS_1D_X_Mark_III (0x80000000ULL + 0x428ULL)
+#define CanonID_EOS_1500D         (0x80000000ULL + 0x432ULL)
+#define CanonID_EOS_RP            (0x80000000ULL + 0x433ULL)
+#define CanonID_EOS_250D          (0x80000000ULL + 0x436ULL)
+#define CanonID_EOS_90D           (0x80000000ULL + 0x437ULL)
+
+// CanonID_EOS_D2000C after Canon's TIFF2CR2 convertor:
+#define CanonID_EOS_D2000C        (0x80000000ULL + 0x520ULL)
+// CanonID_EOS_D6000C id after Canon's TIFF2CR2 convertor:
+#define CanonID_EOS_D6000C        (0x80000000ULL + 0x560ULL)
+
+#define OlyID_str2hex(str) ((unsigned long long)str[0]<<32 | str[1]<<24 | str[2]<<16 | str[3]<<8 | str[4])
+#define OlyID_E_20            OlyID_str2hex("D4029")
+#define OlyID_E_1             OlyID_str2hex("D4040")
+#define OlyID_E_300           OlyID_str2hex("D4041")
+#define OlyID_SP_550UZ        OlyID_str2hex("D4321")
+#define OlyID_SP_510UZ        OlyID_str2hex("D4322")
+#define OlyID_SP_560UZ        OlyID_str2hex("D4355")
+#define OlyID_SP_570UZ        OlyID_str2hex("D4364")
+#define OlyID_SP_565UZ        OlyID_str2hex("D4374")
+#define OlyID_XZ_1            OlyID_str2hex("D4401")
+#define OlyID_XZ_2            OlyID_str2hex("D4531")
+#define OlyID_XZ_10           OlyID_str2hex("D4546")
+#define OlyID_STYLUS_1        OlyID_str2hex("D4572")
+#define OlyID_SH_2            OlyID_str2hex("D4585")
+#define OlyID_TG_4            OlyID_str2hex("D4586")
+#define OlyID_TG_5            OlyID_str2hex("D4593")
+#define OlyID_TG_6            OlyID_str2hex("D4603")
+#define OlyID_E_10            OlyID_str2hex("D4842")
+#define OlyID_AIR_A01         OlyID_str2hex("K0055")
+#define OlyID_NORMA           OlyID_str2hex("NORMA")
+#define OlyID_E_330           OlyID_str2hex("S0003")
+#define OlyID_E_500           OlyID_str2hex("S0004")
+#define OlyID_E_400           OlyID_str2hex("S0009")
+#define OlyID_E_510           OlyID_str2hex("S0010")
+#define OlyID_E_3             OlyID_str2hex("S0011")
+#define OlyID_E_410           OlyID_str2hex("S0013")
+#define OlyID_E_420           OlyID_str2hex("S0016")
+#define OlyID_E_30            OlyID_str2hex("S0017")
+#define OlyID_E_520           OlyID_str2hex("S0018")
+#define OlyID_E_P1            OlyID_str2hex("S0019")
+#define OlyID_E_620           OlyID_str2hex("S0023")
+#define OlyID_E_P2            OlyID_str2hex("S0026")
+#define OlyID_E_PL1           OlyID_str2hex("S0027")
+#define OlyID_E_450           OlyID_str2hex("S0029")
+#define OlyID_E_600           OlyID_str2hex("S0030")
+#define OlyID_E_P3            OlyID_str2hex("S0032")
+#define OlyID_E_5             OlyID_str2hex("S0033")
+#define OlyID_E_PL2           OlyID_str2hex("S0034")
+#define OlyID_E_M5            OlyID_str2hex("S0036")
+#define OlyID_E_PL3           OlyID_str2hex("S0038")
+#define OlyID_E_PM1           OlyID_str2hex("S0039")
+#define OlyID_E_PL1s          OlyID_str2hex("S0040")
+#define OlyID_E_PL5           OlyID_str2hex("S0042")
+#define OlyID_E_PM2           OlyID_str2hex("S0043")
+#define OlyID_E_P5            OlyID_str2hex("S0044")
+#define OlyID_E_PL6           OlyID_str2hex("S0045")
+#define OlyID_E_PL7           OlyID_str2hex("S0046")
+#define OlyID_E_M1            OlyID_str2hex("S0047")
+#define OlyID_E_M10           OlyID_str2hex("S0051")
+#define OlyID_E_M5_Mark_II    OlyID_str2hex("S0052")
+#define OlyID_E_M10_Mark_II   OlyID_str2hex("S0059")
+#define OlyID_PEN_F           OlyID_str2hex("S0061")
+#define OlyID_E_PL8           OlyID_str2hex("S0065")
+#define OlyID_E_M1_Mark_II    OlyID_str2hex("S0067")
+#define OlyID_E_M10_Mark_III  OlyID_str2hex("S0068")
+#define OlyID_E_PL9           OlyID_str2hex("S0076")
+#define OlyID_E_M1X           OlyID_str2hex("S0080")
+#define OlyID_E_PL10          OlyID_str2hex("S0085")
+#define OlyID_E_M5_Mark_III   OlyID_str2hex("S0089")
+#define OlyID_E_M1_Mark_III   OlyID_str2hex("S0092")
+#define OlyID_C_3030Z         OlyID_str2hex("SX351")
+#define OlyID_C_5050Z         OlyID_str2hex("SX558")
+#define OlyID_C_350Z          OlyID_str2hex("SX751")
+#define OlyID_C_740UZ         OlyID_str2hex("SX754")
+#define OlyID_C_5060WZ        OlyID_str2hex("SX756")
+#define OlyID_C_8080WZ        OlyID_str2hex("SX757")
+#define OlyID_C_770UZ         OlyID_str2hex("SX772")
+#define OlyID_C_7070WZ        OlyID_str2hex("SX851")
+#define OlyID_C_7000Z         OlyID_str2hex("SX852")
+#define OlyID_SP_500UZ        OlyID_str2hex("SX853")
+#define OlyID_SP_310          OlyID_str2hex("SX854")
+#define OlyID_SP_350          OlyID_str2hex("SX855")
+#define OlyID_SP_320          OlyID_str2hex("SX873")
+
+#define PentaxID_Optio_S      0x1296cULL
+#define PentaxID_Optio_S_V101 0x12971ULL
+#define PentaxID_staristD     0x12994ULL
+#define PentaxID_Optio_33WR   0x129c6ULL
+#define PentaxID_Optio_S4     0x129d5ULL
+#define PentaxID_Optio_750Z   0x12a66ULL
+#define PentaxID_staristDS    0x12aa2ULL
+#define PentaxID_staristDL    0x12b1aULL
+#define PentaxID_staristDS2   0x12b60ULL
+#define PentaxID_GX_1S        0x12b62ULL
+#define PentaxID_staristDL2   0x12b7eULL
+#define PentaxID_GX_1L        0x12b80ULL
+#define PentaxID_K100D        0x12b9cULL
+#define PentaxID_K110D        0x12b9dULL
+#define PentaxID_K100D_Super  0x12ba2ULL
+#define PentaxID_K10D         0x12c1eULL
+#define PentaxID_GX10         0x12c20ULL
+#define PentaxID_K20D         0x12cd2ULL
+#define PentaxID_GX20         0x12cd4ULL
+#define PentaxID_K200D        0x12cfaULL
+#define PentaxID_K2000        0x12d72ULL
+#define PentaxID_K_m          0x12d73ULL
+#define PentaxID_K_7          0x12db8ULL
+#define PentaxID_K_x          0x12dfeULL
+#define PentaxID_645D         0x12e08ULL
+#define PentaxID_K_r          0x12e6cULL
+#define PentaxID_K_5          0x12e76ULL
+#define PentaxID_Q            0x12ee4ULL
+#define PentaxID_K_01         0x12ef8ULL
+#define PentaxID_K_30         0x12f52ULL
+#define PentaxID_Q10          0x12f66ULL
+#define PentaxID_K_5_II       0x12f70ULL
+#define PentaxID_K_5_II_s     0x12f71ULL
+#define PentaxID_Q7           0x12f7aULL
+#define PentaxID_MX_1         0x12f84ULL
+#define PentaxID_K_50         0x12fb6ULL
+#define PentaxID_K_3          0x12fc0ULL
+#define PentaxID_K_500        0x12fcaULL
+#define PentaxID_645Z         0x13010ULL
+#define PentaxID_K_S1         0x1301aULL
+#define PentaxID_K_S2         0x13024ULL
+#define PentaxID_Q_S1         0x1302eULL
+#define PentaxID_K_1          0x13092ULL
+#define PentaxID_K_3_II       0x1309cULL
+#define PentaxID_GR_III       0x1320eULL
+#define PentaxID_K_70         0x13222ULL
+#define PentaxID_KP           0x1322cULL
+#define PentaxID_K_1_Mark_II  0x13240ULL
+
+#define SonyID_DSC_R1           0x002ULL
+#define SonyID_DSLR_A100        0x100ULL
+#define SonyID_DSLR_A900        0x101ULL
+#define SonyID_DSLR_A700        0x102ULL
+#define SonyID_DSLR_A200        0x103ULL
+#define SonyID_DSLR_A350        0x104ULL
+#define SonyID_DSLR_A300        0x105ULL
+#define SonyID_DSLR_A900_APSC   0x106ULL
+#define SonyID_DSLR_A380        0x107ULL
+#define SonyID_DSLR_A330        0x108ULL
+#define SonyID_DSLR_A230        0x109ULL
+#define SonyID_DSLR_A290        0x10aULL
+#define SonyID_DSLR_A850        0x10dULL
+#define SonyID_DSLR_A850_APSC   0x10eULL
+#define SonyID_DSLR_A550        0x111ULL
+#define SonyID_DSLR_A500        0x112ULL
+#define SonyID_DSLR_A450        0x113ULL
+#define SonyID_NEX_5            0x116ULL
+#define SonyID_NEX_3            0x117ULL
+#define SonyID_SLT_A33          0x118ULL
+#define SonyID_SLT_A55          0x119ULL
+#define SonyID_DSLR_A560        0x11aULL
+#define SonyID_DSLR_A580        0x11bULL
+#define SonyID_NEX_C3           0x11cULL
+#define SonyID_SLT_A35          0x11dULL
+#define SonyID_SLT_A65          0x11eULL
+#define SonyID_SLT_A77          0x11fULL
+#define SonyID_NEX_5N           0x120ULL
+#define SonyID_NEX_7            0x121ULL
+#define SonyID_NEX_VG20         0x122ULL
+#define SonyID_SLT_A37          0x123ULL
+#define SonyID_SLT_A57          0x124ULL
+#define SonyID_NEX_F3           0x125ULL
+#define SonyID_SLT_A99          0x126ULL
+#define SonyID_NEX_6            0x127ULL
+#define SonyID_NEX_5R           0x128ULL
+#define SonyID_DSC_RX100        0x129ULL
+#define SonyID_DSC_RX1          0x12aULL
+#define SonyID_NEX_VG900        0x12bULL
+#define SonyID_NEX_VG30         0x12cULL
+#define SonyID_ILCE_3000        0x12eULL
+#define SonyID_SLT_A58          0x12fULL
+#define SonyID_NEX_3N           0x131ULL
+#define SonyID_ILCE_7           0x132ULL
+#define SonyID_NEX_5T           0x133ULL
+#define SonyID_DSC_RX100M2      0x134ULL
+#define SonyID_DSC_RX10         0x135ULL
+#define SonyID_DSC_RX1R         0x136ULL
+#define SonyID_ILCE_7R          0x137ULL
+#define SonyID_ILCE_6000        0x138ULL
+#define SonyID_ILCE_5000        0x139ULL
+#define SonyID_DSC_RX100M3      0x13dULL
+#define SonyID_ILCE_7S          0x13eULL
+#define SonyID_ILCA_77M2        0x13fULL
+#define SonyID_ILCE_5100        0x153ULL
+#define SonyID_ILCE_7M2         0x154ULL
+#define SonyID_DSC_RX100M4      0x155ULL
+#define SonyID_DSC_RX10M2       0x156ULL
+#define SonyID_DSC_RX1RM2       0x158ULL
+#define SonyID_ILCE_QX1         0x15aULL
+#define SonyID_ILCE_7RM2        0x15bULL
+#define SonyID_ILCE_7SM2        0x15eULL
+#define SonyID_ILCA_68          0x161ULL
+#define SonyID_ILCA_99M2        0x162ULL
+#define SonyID_DSC_RX10M3       0x163ULL
+#define SonyID_DSC_RX100M5      0x164ULL
+#define SonyID_ILCE_6300        0x165ULL
+#define SonyID_ILCE_9           0x166ULL
+#define SonyID_ILCE_6500        0x168ULL
+#define SonyID_ILCE_7RM3        0x16aULL
+#define SonyID_ILCE_7M3         0x16bULL
+#define SonyID_DSC_RX0          0x16cULL
+#define SonyID_DSC_RX10M4       0x16dULL
+#define SonyID_DSC_RX100M6      0x16eULL
+#define SonyID_DSC_HX99         0x16fULL
+#define SonyID_DSC_RX100M5A     0x171ULL
+#define SonyID_ILCE_6400        0x173ULL
+#define SonyID_DSC_RX0M2        0x174ULL
+#define SonyID_DSC_RX100M7      0x176ULL
+#define SonyID_ILCE_7RM4        0x177ULL
+#define SonyID_ILCE_9M2         0x178ULL
+#define SonyID_ILCE_6600        0x17aULL
+#define SonyID_ILCE_6100        0x17bULL
+
+#endif
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/internal/libraw_cxx_defs.h libkdcraw/libkdcraw/libraw/internal/libraw_cxx_defs.h
--- libkdcraw-wrk/libkdcraw/libraw/internal/libraw_cxx_defs.h	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/internal/libraw_cxx_defs.h	2022-11-07 07:46:31.726795008 +0300
@@ -0,0 +1,130 @@
+/* -*- C++ -*-
+ * File: internal/libraw_cxx_defs.h
+ * Copyright 2008-2020 LibRaw LLC (info@libraw.org)
+ * Created: Sat Aug  17, 2020
+
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#ifndef _LIBRAW_CXX_DEFS_H
+#define _LIBRAW_CXX_DEFS_H
+
+#include <math.h>
+#include <errno.h>
+#include <float.h>
+#include <new>
+#include <exception>
+#include <sys/types.h>
+#include <sys/stat.h>
+#define LIBRAW_LIBRARY_BUILD
+#include "libraw/libraw.h"
+#include "internal/defines.h"
+#ifdef USE_ZLIB
+#include <zlib.h>
+#endif
+
+#ifndef LIBRAW_WIN32_CALLS
+#include <netinet/in.h>
+#else
+#ifndef LIBRAW_NO_WINSOCK2
+#include <winsock2.h>
+#endif
+#include <io.h>
+#endif
+
+#ifdef USE_RAWSPEED
+#include <RawSpeed/StdAfx.h>
+#include <RawSpeed/FileMap.h>
+#include <RawSpeed/RawParser.h>
+#include <RawSpeed/RawDecoder.h>
+#include <RawSpeed/CameraMetaData.h>
+#include <RawSpeed/ColorFilterArray.h>
+extern const char *_rawspeed_data_xml[];
+extern const int RAWSPEED_DATA_COUNT;
+class CameraMetaDataLR : public RawSpeed::CameraMetaData
+{
+public:
+  CameraMetaDataLR() : CameraMetaData() {}
+  CameraMetaDataLR(char *filename) : RawSpeed::CameraMetaData(filename) {}
+  CameraMetaDataLR(char *data, int sz);
+};
+
+CameraMetaDataLR *make_camera_metadata();
+
+#endif
+
+#ifdef USE_DNGSDK
+#include "dng_host.h"
+#include "dng_negative.h"
+#include "dng_simple_image.h"
+#include "dng_info.h"
+#endif
+
+#define P1 imgdata.idata
+#define S  imgdata.sizes
+#define O  imgdata.params
+#define C  imgdata.color
+#define T  imgdata.thumbnail
+#define MN imgdata.makernotes
+#define IO libraw_internal_data.internal_output_params
+#define ID libraw_internal_data.internal_data
+
+#define makeIs(idx) (imgdata.idata.maker_index == idx)
+#define mnCamID imgdata.lens.makernotes.CamID
+
+#define EXCEPTION_HANDLER(e)                                                   \
+  do                                                                           \
+  {                                                                            \
+    switch (e)                                                                 \
+    {                                                                          \
+    case LIBRAW_EXCEPTION_MEMPOOL:                                             \
+      recycle();                                                               \
+      return LIBRAW_MEMPOOL_OVERFLOW;                                          \
+    case LIBRAW_EXCEPTION_ALLOC:                                               \
+      recycle();                                                               \
+      return LIBRAW_UNSUFFICIENT_MEMORY;                                       \
+    case LIBRAW_EXCEPTION_TOOBIG:                                              \
+      recycle();                                                               \
+      return LIBRAW_TOO_BIG;                                                   \
+    case LIBRAW_EXCEPTION_DECODE_RAW:                                          \
+    case LIBRAW_EXCEPTION_DECODE_JPEG:                                         \
+      recycle();                                                               \
+      return LIBRAW_DATA_ERROR;                                                \
+    case LIBRAW_EXCEPTION_DECODE_JPEG2000:                                     \
+      recycle();                                                               \
+      return LIBRAW_DATA_ERROR;                                                \
+    case LIBRAW_EXCEPTION_IO_EOF:                                              \
+    case LIBRAW_EXCEPTION_IO_CORRUPT:                                          \
+      recycle();                                                               \
+      return LIBRAW_IO_ERROR;                                                  \
+    case LIBRAW_EXCEPTION_CANCELLED_BY_CALLBACK:                               \
+      recycle();                                                               \
+      return LIBRAW_CANCELLED_BY_CALLBACK;                                     \
+    case LIBRAW_EXCEPTION_BAD_CROP:                                            \
+      recycle();                                                               \
+      return LIBRAW_BAD_CROP;                                                  \
+    default:                                                                   \
+      return LIBRAW_UNSPECIFIED_ERROR;                                         \
+    }                                                                          \
+  } while (0)
+
+// copy-n-paste from image pipe
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define LIM(x, min, max) MAX(min, MIN(x, max))
+#ifndef CLIP
+#define CLIP(x) LIM(x, 0, 65535)
+#endif
+#define THUMB_READ_BEYOND 16384
+
+#define ZERO(a) memset(&a, 0, sizeof(a))
+
+#endif
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/internal/libraw_internal_funcs.h libkdcraw/libkdcraw/libraw/internal/libraw_internal_funcs.h
--- libkdcraw-wrk/libkdcraw/libraw/internal/libraw_internal_funcs.h	2022-11-07 08:15:53.614821808 +0300
+++ libkdcraw/libkdcraw/libraw/internal/libraw_internal_funcs.h	2022-11-07 07:46:31.726795008 +0300
@@ -1,24 +1,17 @@
-/*
+/* -*- C++ -*-
  * File: libraw_internal_funcs.h
- * Copyright 2008 Alex Tutubalin <lexa@lexa.ru>
+ * Copyright 2008-2020 LibRaw LLC (info@libraw.org)
  * Created: Sat Mar  14, 2008
- *
- * LibRaw internal data structures (not visible outside)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
+
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
  */
 
 #ifndef _LIBRAW_INTERNAL_FUNCS_H
@@ -27,76 +20,193 @@
 #ifndef LIBRAW_LIBRARY_BUILD
 #error "This file should be used only for libraw library build"
 #else
-// inline functions
-    ushort      sget2 (uchar *s);
+
+/* inline functions */
+    static int stread(char *buf, size_t len, LibRaw_abstract_datastream *fp);
+    static int getwords(char *line, char *words[], int maxwords, int maxlen);
+    static void remove_trailing_spaces(char *string, size_t len);
+    static void remove_caseSubstr(char *string, char *remove);
+    static void removeExcessiveSpaces(char *string);
+    static void trimSpaces(char *s);
+/* static tables/variables */
+    static libraw_static_table_t tagtype_dataunit_bytes;
+    static libraw_static_table_t Canon_wbi2std;
+    static libraw_static_table_t Canon_KeyIsZero_Len2048_linenums_2_StdWBi;
+    static libraw_static_table_t Canon_KeyIs0x0410_Len3072_linenums_2_StdWBi;
+    static libraw_static_table_t Canon_KeyIs0x0410_Len2048_linenums_2_StdWBi;
+    static libraw_static_table_t Canon_D30_linenums_2_StdWBi;
+    static libraw_static_table_t Canon_G9_linenums_2_StdWBi;
+
+    static libraw_static_table_t Fuji_wb_list1;
+    static libraw_static_table_t FujiCCT_K;
+    static libraw_static_table_t Fuji_wb_list2;
+
+    static libraw_static_table_t Pentax_wb_list1;
+    static libraw_static_table_t Pentax_wb_list2;
+
+    static libraw_static_table_t Oly_wb_list1;
+    static libraw_static_table_t Oly_wb_list2;
+
+    static libraw_static_table_t Sony_SRF_wb_list;
+    static libraw_static_table_t Sony_SR2_wb_list;
+    static libraw_static_table_t Sony_SR2_wb_list1;
+/*  */
+    int     find_ifd_by_offset(int );
+    ushort	sget2 (uchar *s);
+    ushort	sget2Rev(uchar *s);
+    int 	parseCR3(unsigned long long oAtomList, unsigned long long szAtomList, short &nesting, char *AtomNameStack, short& nTrack, short &TrackType);
+    void	selectCRXTrack(short maxTrack);
+    void	setCanonBodyFeatures (unsigned long long id);
+    void	processCanonCameraInfo (unsigned long long id, uchar *CameraInfo, unsigned maxlen, unsigned type, unsigned dng_writer);
+    static float _CanonConvertAperture(ushort in);
+    void	Canon_CameraSettings(unsigned len);
+    void	Canon_WBpresets (int skip1, int skip2);
+    void	Canon_WBCTpresets (short WBCTversion);
+    void	parseCanonMakernotes (unsigned tag, unsigned type, unsigned len, unsigned dng_writer);
+    void	processNikonLensData (uchar *LensData, unsigned len);
+    void	Nikon_NRW_WBtag (int wb, int skip);
+    void	parseNikonMakernote (int base, int uptag, unsigned dng_writer);
+    void	parseEpsonMakernote (int base, int uptag, unsigned dng_writer);
+    void	parseSigmaMakernote (int base, int uptag, unsigned dng_writer);
+    void	setOlympusBodyFeatures (unsigned long long id);
+    void	getOlympus_CameraType2 ();
+    void	getOlympus_SensorTemperature (unsigned len);
+    void	parseOlympus_Equipment (unsigned tag, unsigned type, unsigned len, unsigned dng_writer);
+    void	parseOlympus_CameraSettings (int base, unsigned tag, unsigned type, unsigned len, unsigned dng_writer);
+    void	parseOlympus_ImageProcessing (unsigned tag, unsigned type, unsigned len, unsigned dng_writer);
+    void	parseOlympus_RawInfo (unsigned tag, unsigned type, unsigned len, unsigned dng_writer);
+    void	setPhaseOneFeatures (unsigned long long id);
+    void	setPentaxBodyFeatures (unsigned long long id);
+    void	PentaxISO (ushort c);
+    void	PentaxLensInfo (unsigned long long id, unsigned len);
+    void	parsePentaxMakernotes(int base, unsigned tag, unsigned type, unsigned len, unsigned dng_writer);
+    void	parseRicohMakernotes(int base, unsigned tag, unsigned type, unsigned len, unsigned dng_writer);
+    void	parseSamsungMakernotes(int base, unsigned tag, unsigned type, unsigned len, unsigned dng_writer);
+    void	setSonyBodyFeatures (unsigned long long id);
+    void	fixupArri();
+    void	parseSonyLensType2 (uchar a, uchar b);
+    void	parseSonyLensFeatures (uchar a, uchar b);
+    void	process_Sony_0x0116 (uchar * buf, ushort, unsigned long long id);
+    void	process_Sony_0x2010 (uchar * buf, ushort);
+    void	process_Sony_0x9050 (uchar * buf, ushort, unsigned long long id);
+    void	process_Sony_0x9400 (uchar * buf, ushort, unsigned long long id);
+    void	process_Sony_0x9402 (uchar * buf, ushort);
+    void	process_Sony_0x9403 (uchar * buf, ushort);
+    void	process_Sony_0x9406 (uchar * buf, ushort);
+    void	process_Sony_0x940c (uchar * buf, ushort);
+    void	process_Sony_0x940e (uchar * buf, ushort, unsigned long long id);
+    void	parseSonyMakernotes (int base, unsigned tag, unsigned type, unsigned len, unsigned dng_writer,
+                               uchar *&table_buf_0x0116, ushort &table_buf_0x0116_len,
+                               uchar *&table_buf_0x2010, ushort &table_buf_0x2010_len,
+                               uchar *&table_buf_0x9050, ushort &table_buf_0x9050_len,
+                               uchar *&table_buf_0x9400, ushort &table_buf_0x9400_len,
+                               uchar *&table_buf_0x9402, ushort &table_buf_0x9402_len,
+                               uchar *&table_buf_0x9403, ushort &table_buf_0x9403_len,
+                               uchar *&table_buf_0x9406, ushort &table_buf_0x9406_len,
+                               uchar *&table_buf_0x940c, ushort &table_buf_0x940c_len,
+                               uchar *&table_buf_0x940e, ushort &table_buf_0x940e_len);
+    void	parseSonySR2 (uchar *cbuf_SR2, unsigned SR2SubIFDOffset, unsigned SR2SubIFDLength, unsigned dng_writer);
+    void	parseSonySRF (unsigned len);
+    void	parseFujiMakernotes (unsigned tag, unsigned type, unsigned len, unsigned dng_writer);
+    const char* HassyRawFormat_idx2HR(unsigned idx);
+    void	process_Hassy_Lens (int LensMount);
+    void parseHassyModel ();
+
+    void	setLeicaBodyFeatures(int LeicaMakernoteSignature);
+    void	parseLeicaLensID();
+    int 	parseLeicaLensName(unsigned len);
+    int 	parseLeicaInternalBodySerial(unsigned len);
+    void	parseLeicaMakernote(int base, int uptag, unsigned MakernoteTagType);
+    void	parseAdobePanoMakernote ();
+    void	parseAdobeRAFMakernote ();
+    void	GetNormalizedModel ();
+    void	SetStandardIlluminants (unsigned, const char* );
+
     ushort      get2();
     unsigned    sget4 (uchar *s);
     unsigned    getint (int type);
     float       int_to_float (int i);
     double      getreal (int type);
-    void        read_shorts (ushort *pixel, int count);
+    double      sgetreal(int type, uchar *s);
+    void        read_shorts (ushort *pixel, unsigned count);
 
-// Canon P&S cameras
+/* Canon P&S cameras */
     void        canon_600_fixed_wb (int temp);
     int         canon_600_color (int ratio[2], int mar);
     void        canon_600_auto_wb();
     void        canon_600_coeff();
     void        canon_600_load_raw();
+    void        canon_600_correct();
     int         canon_s2is();
-    void        canon_a5_load_raw();
-    void        parse_ciff (int offset, int length);
+    void        parse_ciff (int offset, int length, int);
     void        ciff_block_1030();
 
+
 // LJPEG decoder
-    unsigned    getbits (int nbits);
-    void        init_decoder();
-    uchar *     make_decoder (const uchar *source, int level);
+    unsigned    getbithuff (int nbits, ushort *huff);
+    ushort*     make_decoder_ref (const uchar **source);
+    ushort*     make_decoder (const uchar *source);
     int         ljpeg_start (struct jhead *jh, int info_only);
-    int         ljpeg_diff (struct decode *dindex);
+    void        ljpeg_end(struct jhead *jh);
+    int         ljpeg_diff (ushort *huff);
     ushort *    ljpeg_row (int jrow, struct jhead *jh);
+    ushort *    ljpeg_row_unrolled (int jrow, struct jhead *jh);
+    void	    ljpeg_idct (struct jhead *jh);
+    unsigned    ph1_bithuff (int nbits, ushort *huff);
 
 // Canon DSLRs
-    void        crw_init_tables (unsigned table);
+    void        crw_init_tables (unsigned table, ushort *huff[2]);
     int         canon_has_lowbits();
-    void        canon_compressed_load_raw();
+    void        canon_load_raw();
     void        lossless_jpeg_load_raw();
     void        canon_sraw_load_raw();
-    void        canon_black(double *);
 // Adobe DNG
-    void        adobe_copy_pixel (int row, int col, ushort **rp);
-    void        adobe_dng_load_raw_lj();
-    void        adobe_dng_load_raw_nc();
+    void        adobe_copy_pixel (unsigned int row, unsigned int col, ushort **rp);
+    void        lossless_dng_load_raw();
+    void        deflate_dng_load_raw();
+    void        packed_dng_load_raw();
+    void        lossy_dng_load_raw();
+//void        adobe_dng_load_raw_nc();
 
 // Pentax
-    void        pentax_k10_load_raw();
+    void        pentax_load_raw();
+    void	pentax_4shot_load_raw();
+
     void        pentax_tree();
 
 // Nikon (and Minolta Z2)
-    void        nikon_compressed_load_raw();
     void        nikon_load_raw();
-    int         nikon_is_compressed();
+    void        nikon_read_curve();
+    void        nikon_load_striped_packed_raw();
+    void        nikon_load_padded_packed_raw();
+    void        nikon_load_sraw();
+    void        nikon_yuv_load_raw();
+    void        nikon_coolscan_load_raw();
     int         nikon_e995();
     int         nikon_e2100();
     void        nikon_3700();
     int         minolta_z2();
-    void        nikon_e900_load_raw();
-    void        nikon_e2100_load_raw();
+//    void        nikon_e2100_load_raw();
 
 // Fuji
-    void        fuji_load_raw();
+//void        fuji_load_raw();
     void        parse_fuji (int offset);
 
-
-
+// RedCine
+    void        parse_redcine();
+    void        redcine_load_raw();
 
 // Rollei
     void        rollei_load_raw();
     void        parse_rollei();
 
+// Contax
+    void        parse_kyocera ();
+
 // MF backs
-    int         bayer (unsigned row, unsigned col);
+//int         bayer (unsigned row, unsigned col);
+    int         p1raw(unsigned,unsigned);
     void        phase_one_flat_field (int is_float, int nc);
-    void        phase_one_correct();
     void        phase_one_load_raw();
     unsigned    ph1_bits (int nbits);
     void        phase_one_load_raw_c();
@@ -104,20 +214,29 @@
     void        leaf_hdr_load_raw();
     void        sinar_4shot_load_raw();
     void        imacon_full_load_raw();
-    void        packed_12_load_raw();
+    void        hasselblad_full_load_raw();
+    void        packed_load_raw();
+    float       find_green(int,int,int,int);
     void        unpacked_load_raw();
+    void        unpacked_load_raw_FujiDBP();
+    void        unpacked_load_raw_reversed();
+    void        unpacked_load_raw_fuji_f700s20();
     void        parse_sinar_ia();
     void        parse_phase_one (int base);
 
 // Misc P&S cameras
+    void        parse_broadcom();
+    void        broadcom_load_raw();
     void        nokia_load_raw();
-    unsigned    pana_bits (int nbits);
+    void        android_loose_load_raw();
+    void        android_tight_load_raw();
+    void        canon_rmf_load_raw();
+    unsigned    pana_data (int nb, unsigned *bytes);
     void        panasonic_load_raw();
-    void        olympus_e300_load_raw();
-    void        olympus_e410_load_raw();
-    void        olympus_cseries_load_raw();
+//    void        panasonic_16x10_load_raw();
+    void        olympus_load_raw();
+//    void        olympus_cseries_load_raw();
     void        minolta_rd175_load_raw();
-    void        casio_qv5700_load_raw();
     void        quicktake_100_load_raw();
     const int*  make_decoder_int (const int *source, int level);
     int         radc_token (int tree);
@@ -140,62 +259,130 @@
     int         kodak_65000_decode (short *out, int bsize);
     void        kodak_65000_load_raw();
     void        kodak_rgb_load_raw();
-    void        kodak_yrgb_load_raw();
-
+    void        kodak_ycbcr_load_raw();
+//    void        kodak_yrgb_load_raw();
+    void        kodak_c330_load_raw();
+    void        kodak_c603_load_raw();
+    void        kodak_rgb_load_thumb();
+    void        kodak_ycbcr_load_thumb();
+    void        float_dng_load_raw_placeholder();
+    void        vc5_dng_load_raw_placeholder();
 // It's a Sony (and K&M)
     void        sony_decrypt (unsigned *data, int len, int start, int key);
     void        sony_load_raw();
     void        sony_arw_load_raw();
     void        sony_arw2_load_raw();
+    void        sony_arq_load_raw();
+    void        samsung_load_raw();
+    void        samsung2_load_raw();
+    void        samsung3_load_raw();
     void        parse_minolta (int base);
 
-#ifndef NO_FOVEON
+#ifdef USE_X3FTOOLS
 // Foveon/Sigma
-    void        foveon_load_camf();
-    void        foveon_load_raw();
-    const char* foveon_camf_param (const char *block, const char *param);
-    void *      foveon_camf_matrix (unsigned dim[3], const char *name);
-    int         foveon_fixed (void *ptr, int size, const char *name);
-    float       foveon_avg (short *pix, int range[2], float cfilt);
-    short *     foveon_make_curve (double max, double mul, double filt);
-    void        foveon_make_curves(short **curvep, float dq[3], float div[3], float filt);
-    int         foveon_apply_curve (short *curve, int i);
-    void        foveon_interpolate();
-    char *      foveon_gets (int offset, char *str, int len);
-    void        parse_foveon();
+// We always have x3f code compiled in!
+    void        parse_x3f();
+    void        x3f_load_raw();
+    void        x3f_dpq_interpolate_rg();
+	void        x3f_dpq_interpolate_af(int xstep, int ystep, int scale); // 1x1 af pixels
+	void        x3f_dpq_interpolate_af_sd(int xstart,int ystart, int xend, int yend, int xstep, int ystep, int scale); // sd Quattro interpolation
+#else
+	void        parse_x3f() {}
+	void        x3f_load_raw(){}
+#endif
+#ifdef USE_6BY9RPI
+	void		rpi_load_raw8();
+	void		rpi_load_raw12();
+	void		rpi_load_raw14();
+	void		rpi_load_raw16();
+	void		parse_raspberrypi();
 #endif
-
 
 // CAM/RGB
     void        pseudoinverse (double (*in)[3], double (*out)[3], int size);
-    void        cam_xyz_coeff (double cam_xyz[4][3]);
-    void        adobe_coeff (const char *, const char *);
     void        simple_coeff (int index);
 
 
 // Tiff/Exif parsers
     void        tiff_get (unsigned base,unsigned *tag, unsigned *type, unsigned *len, unsigned *save);
+    short       tiff_sget(unsigned save, uchar *buf, unsigned buf_len, INT64 *tag_offset,
+                          unsigned *tag_id, unsigned *tag_type, INT64 *tag_dataoffset,
+                          unsigned *tag_datalen, int *tag_dataunit_len);
     void        parse_thumb_note (int base, unsigned toff, unsigned tlen);
     void        parse_makernote (int base, int uptag);
+    void        parse_makernote_0xc634(int base, int uptag, unsigned dng_writer);
     void        parse_exif (int base);
-    void        linear_table (unsigned len);
+	void        parse_exif_interop(int base);
+	void        linear_table(unsigned len);
+    void        Kodak_DCR_WBtags(int wb, unsigned type, int wbi);
+    void        Kodak_KDC_WBtags(int wb, int wbi);
+    short       KodakIllumMatrix (unsigned type, float *romm_camIllum);
     void        parse_kodak_ifd (int base);
     int         parse_tiff_ifd (int base);
-    void        parse_tiff (int base);
+    int         parse_tiff (int base);
+    void        apply_tiff(void);
     void        parse_gps (int base);
-    void        romm_coeff (float romm_cam[3][3]);
+    void        parse_gps_libraw(int base);
+    void        aRGB_coeff(double aRGB_cam[3][3]);
+    void        romm_coeff(float romm_cam[3][3]);
     void        parse_mos (int offset);
+    void        parse_qt (int end);
     void        get_timestamp (int reversed);
 
-// External JPEGs, what cameras uses it ?
-    void        parse_external_jpeg();
-
 // The identify
     short       guess_byte_order (int words);
-
+	void		identify_process_dng_fields();
+	void		identify_finetune_pentax();
+	void		identify_finetune_by_filesize(int);
+	void		identify_finetune_dcr(char head[64],int,int);
 // Tiff writer
-    void        tiff_set (ushort *ntag, ushort tag, ushort type, int count, int val);
+    void        tiff_set(struct tiff_hdr *th, ushort *ntag,ushort tag, ushort type, int count, int val);
     void        tiff_head (struct tiff_hdr *th, int full);
+
+// split AHD code
+    void ahd_interpolate_green_h_and_v(int top, int left, ushort (*out_rgb)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3]);
+    void ahd_interpolate_r_and_b_in_rgb_and_convert_to_cielab(int top, int left, ushort (*inout_rgb)[LIBRAW_AHD_TILE][3], short (*out_lab)[LIBRAW_AHD_TILE][3]);
+    void ahd_interpolate_r_and_b_and_convert_to_cielab(int top, int left, ushort (*inout_rgb)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3], short (*out_lab)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3]);
+    void ahd_interpolate_build_homogeneity_map(int top, int left, short (*lab)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3], char (*out_homogeneity_map)[LIBRAW_AHD_TILE][2]);
+    void ahd_interpolate_combine_homogeneous_pixels(int top, int left, ushort (*rgb)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3], char (*homogeneity_map)[LIBRAW_AHD_TILE][2]);
+
+	void init_fuji_compr(struct fuji_compressed_params* info);
+	void init_fuji_block(struct fuji_compressed_block* info, const struct fuji_compressed_params *params, INT64 raw_offset, unsigned dsize);
+	void copy_line_to_xtrans(struct fuji_compressed_block* info, int cur_line, int cur_block, int cur_block_width);
+	void copy_line_to_bayer(struct fuji_compressed_block* info, int cur_line, int cur_block, int cur_block_width);
+	void xtrans_decode_block(struct fuji_compressed_block* info, const struct fuji_compressed_params *params, int cur_line);
+	void fuji_bayer_decode_block(struct fuji_compressed_block* info, const struct fuji_compressed_params *params, int cur_line);
+	void fuji_compressed_load_raw();
+	void fuji_14bit_load_raw();
+	void parse_fuji_compressed_header();
+    void crxLoadRaw();
+    int  crxParseImageHeader(uchar *cmp1TagData, int nTrack);
+	void panasonicC6_load_raw();
+	void panasonicC7_load_raw();
+
+	void nikon_14bit_load_raw();
+
+// DCB
+    void  	dcb_pp();
+    void  	dcb_copy_to_buffer(float (*image2)[3]);
+    void  	dcb_restore_from_buffer(float (*image2)[3]);
+    void  	dcb_color();
+    void  	dcb_color_full();
+    void  	dcb_map();
+    void  	dcb_correction();
+    void  	dcb_correction2();
+    void  	dcb_refinement();
+    void  	rgb_to_lch(double (*image3)[3]);
+    void  	lch_to_rgb(double (*image3)[3]);
+    void  	fbdd_correction();
+    void  	fbdd_correction2(double (*image3)[3]);
+    void  	fbdd_green();
+    void  	dcb_ver(float (*image3)[3]);
+    void 	dcb_hor(float (*image2)[3]);
+    void 	dcb_color2(float (*image2)[3]);
+    void 	dcb_color3(float (*image3)[3]);
+    void 	dcb_decide(float (*image2)[3], float (*image3)[3]);
+    void 	dcb_nyquist();
 #endif
 
 #endif
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/internal/var_defines.h libkdcraw/libkdcraw/libraw/internal/var_defines.h
--- libkdcraw-wrk/libkdcraw/libraw/internal/var_defines.h	2022-11-07 08:15:53.614821808 +0300
+++ libkdcraw/libkdcraw/libraw/internal/var_defines.h	2022-11-07 07:46:31.726795008 +0300
@@ -1,45 +1,51 @@
-/*
+/* -*- C++ -*-
  * File: var_defines.h
- * Copyright 2008-2009 Alex Tutubalin <lexa@lexa.ru>
+ * Copyright 2008-2020 LibRaw LLC (info@libraw.org)
  * Created: Sat Mar  8, 2008
  *
  * LibRaw redefinitions of dcraw internal variables
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
+
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
  */
 
 #ifndef VAR_DEFINES_H
 #define VAR_DEFINES_H
 
-// imgdata.idata
-#define make            (imgdata.idata.make)
-#define model           (imgdata.idata.model)
-#define is_raw          (imgdata.idata.raw_count)
-#define dng_version     (imgdata.idata.dng_version)
-#define is_foveon       (imgdata.idata.is_foveon)
-#define colors          (imgdata.idata.colors)
-#define cdesc           (imgdata.idata.cdesc)
-#define filters         (imgdata.idata.filters)
 
+// imgdata.idata
+#define make             (imgdata.idata.make)
+#define model            (imgdata.idata.model)
+#define software         (imgdata.idata.software)
+#define is_raw           (imgdata.idata.raw_count)
+#define dng_version      (imgdata.idata.dng_version)
+#define is_foveon        (imgdata.idata.is_foveon)
+#define colors           (imgdata.idata.colors)
+#define cdesc            (imgdata.idata.cdesc)
+#define filters          (imgdata.idata.filters)
+#define xtrans           (imgdata.idata.xtrans)
+#define xtrans_abs       (imgdata.idata.xtrans_abs)
+#define xmpdata          (imgdata.idata.xmpdata)
+#define xmplen           (imgdata.idata.xmplen)
 //imgdata image
-#define image           (imgdata.image)
+#define image            (imgdata.image)
+#define raw_image        (imgdata.rawdata.raw_image)
+#define color_image      (imgdata.rawdata.color_image)
+#define normalized_make  (imgdata.idata.normalized_make)
+#define normalized_model (imgdata.idata.normalized_model)
+#define maker_index      (imgdata.idata.maker_index)
 
 // imgdata.sizes
 #define raw_height      (imgdata.sizes.raw_height)
 #define raw_width       (imgdata.sizes.raw_width)
+#define raw_pitch       (imgdata.sizes.raw_pitch)
 #define height          (imgdata.sizes.height)
 #define width           (imgdata.sizes.width)
 #define top_margin      (imgdata.sizes.top_margin)
@@ -50,6 +56,8 @@
 #define iwidth          (imgdata.sizes.iwidth)
 #define pixel_aspect    (imgdata.sizes.pixel_aspect)
 #define flip            (imgdata.sizes.flip)
+#define mask            (imgdata.sizes.mask)
+#define raw_stride		(libraw_internal_data.unpacker_data.raw_stride)
 
 //imgdata.color
 #define white           (imgdata.color.white)
@@ -62,8 +70,10 @@
 #endif
 #ifndef SRC_USES_BLACK
 #define black           (imgdata.color.black)
+#define cblack           (imgdata.color.cblack)
 #endif
 #define maximum         (imgdata.color.maximum)
+#define channel_maximum         (imgdata.color.channel_maximum)
 #define profile_length  (imgdata.color.profile_length)
 #define color_flags     (imgdata.color.color_flags)
 #define ph1             (imgdata.color.phase_one_data)
@@ -72,25 +82,27 @@
 #define model2          (imgdata.color.model2)
 
 //imgdata.thumbnail
-
 #define thumb_width     (imgdata.thumbnail.twidth)
 #define thumb_height    (imgdata.thumbnail.theight)
 #define thumb_length    (imgdata.thumbnail.tlength)
 
 
 //imgdata.others
-#define iso_speed       (imgdata.other.iso_speed)
-#define shutter         (imgdata.other.shutter)
-#define aperture        (imgdata.other.aperture)
-#define focal_len       (imgdata.other.focal_len)
-#define timestamp       (imgdata.other.timestamp)
-#define shot_order      (imgdata.other.shot_order)
-#define gpsdata         (imgdata.other.gpsdata)
-#define desc            (imgdata.other.desc)
-#define artist          (imgdata.other.artist)
+#define iso_speed        (imgdata.other.iso_speed)
+#define shutter          (imgdata.other.shutter)
+#define aperture         (imgdata.other.aperture)
+#define focal_len        (imgdata.other.focal_len)
+#define timestamp        (imgdata.other.timestamp)
+#define shot_order       (imgdata.other.shot_order)
+#define gpsdata          (imgdata.other.gpsdata)
+#define desc             (imgdata.other.desc)
+#define artist           (imgdata.other.artist)
+
+#define FujiCropMode     (imgdata.makernotes.fuji.CropMode)
 
 //imgdata.output
 #define greybox         (imgdata.params.greybox)
+#define cropbox         (imgdata.params.cropbox)
 #define aber            (imgdata.params.aber)
 #define gamm            (imgdata.params.gamm)
 #define user_mul        (imgdata.params.user_mul)
@@ -99,9 +111,7 @@
 #define threshold       (imgdata.params.threshold)
 #define half_size       (imgdata.params.half_size)
 #define four_color_rgb  (imgdata.params.four_color_rgb)
-#define document_mode   (imgdata.params.document_mode)
 #define highlight       (imgdata.params.highlight)
-//#define verbose         (imgdata.params.verbose)
 #define use_auto_wb     (imgdata.params.use_auto_wb)
 #define use_camera_wb   (imgdata.params.use_camera_wb)
 #define use_camera_matrix (imgdata.params.use_camera_matrix)
@@ -111,19 +121,23 @@
 #define output_tiff     (imgdata.params.output_tiff)
 #define med_passes      (imgdata.params.med_passes)
 #define no_auto_bright  (imgdata.params.no_auto_bright)
+#define auto_bright_thr  (imgdata.params.auto_bright_thr)
 #define use_fuji_rotate (imgdata.params.use_fuji_rotate)
 #define filtering_mode (imgdata.params.filtering_mode)
 
-//rgb_constants
-#define xyz_rgb         (rgb_constants.xyz_rgb)
-#define d65_white       (rgb_constants.d65_white)
+// DCB
+#define dcb_iterations   (imgdata.params.iterations)
+#define dcb_enhance_fl   (imgdata.params.dcb_enhance)
+#define fbdd_noiserd     (imgdata.params.fbdd_noiserd)
 
 //libraw_internal_data.internal_data
 #define meta_data       (libraw_internal_data.internal_data.meta_data)
 #define ifp             libraw_internal_data.internal_data.input
 #define ifname          ((char*)libraw_internal_data.internal_data.input->fname())
+#define ofp             libraw_internal_data.internal_data.output
 #define profile_offset  (libraw_internal_data.internal_data.profile_offset)
 #define thumb_offset    (libraw_internal_data.internal_data.toffset)
+#define pana_black		(libraw_internal_data.internal_data.pana_black)
 
 //libraw_internal_data.internal_output_params
 #define mix_green       (libraw_internal_data.internal_output_params.mix_green)
@@ -143,8 +157,10 @@
 //libraw_internal_data.identify_data
 #define exif_cfa        (libraw_internal_data.identify_data.olympus_exif_cfa)
 #define unique_id       (libraw_internal_data.identify_data.unique_id)
+#define OlyID           (libraw_internal_data.identify_data.OlyID)
 #define tiff_nifds      (libraw_internal_data.identify_data.tiff_nifds)
 #define tiff_flip       (libraw_internal_data.identify_data.tiff_flip)
+#define metadata_blocks (libraw_internal_data.identify_data.metadata_blocks)
 
 //libraw_internal_data.unpacker_data
 #define order           (libraw_internal_data.unpacker_data.order)
@@ -154,6 +170,7 @@
 #define kodak_cbpp      (libraw_internal_data.unpacker_data.kodak_cbpp)
 #define strip_offset    (libraw_internal_data.unpacker_data.strip_offset)
 #define data_offset     (libraw_internal_data.unpacker_data.data_offset)
+#define data_size     (libraw_internal_data.unpacker_data.data_size)
 #define meta_offset     (libraw_internal_data.unpacker_data.meta_offset)
 #define meta_length     (libraw_internal_data.unpacker_data.meta_length)
 #define thumb_misc      (libraw_internal_data.unpacker_data.thumb_misc)
@@ -161,10 +178,21 @@
 #define tiff_samples    (libraw_internal_data.unpacker_data.tiff_samples)
 #define tiff_bps        (libraw_internal_data.unpacker_data.tiff_bps)
 #define tiff_compress   (libraw_internal_data.unpacker_data.tiff_compress)
+#define tiff_sampleformat (libraw_internal_data.unpacker_data.tiff_sampleformat)
 #define zero_after_ff   (libraw_internal_data.unpacker_data.zero_after_ff)
 #define tile_width      (libraw_internal_data.unpacker_data.tile_width)
 #define tile_length     (libraw_internal_data.unpacker_data.tile_length)
 #define load_flags      (libraw_internal_data.unpacker_data.load_flags)
+#define pana_encoding   (libraw_internal_data.unpacker_data.pana_encoding)
+#define pana_bpp        (libraw_internal_data.unpacker_data.pana_bpp)
+#define CM_found        (libraw_internal_data.unpacker_data.CM_found)
+
+#define is_NikonTransfer (libraw_internal_data.unpacker_data.is_NikonTransfer)
+#define is_Sony          (libraw_internal_data.unpacker_data.is_Sony)
+#define is_4K_RAFdata    (libraw_internal_data.unpacker_data.is_4K_RAFdata)
+#define is_PentaxRicohMakernotes    (libraw_internal_data.unpacker_data.is_PentaxRicohMakernotes)
+#define is_pana_raw      (libraw_internal_data.unpacker_data.is_pana_raw)
+
 
 #ifdef LIBRAW_IO_REDEFINED
 #define fread(ptr,size,n,stream) stream->read(ptr,size,n)
@@ -172,8 +200,13 @@
 #define fseeko(stream,o,w)	 stream->seek(o,w)
 #define ftell(stream)		 stream->tell()
 #define ftello(stream)		 stream->tell()
+#define feof(stream)		 stream->eof()
+#ifdef getc
+#undef getc
+#endif
 #define getc(stream)		 stream->get_char()
 #define fgetc(stream)		 stream->get_char()
+#define fgetcb(stream)		 stream->get_char_buf()
 #define fgets(str,n,stream)	 stream->gets(str,n)
 #define fscanf(stream,fmt,ptr)	 stream->scanf_one(fmt,ptr)
 #endif
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/internal/x3f_tools.h libkdcraw/libkdcraw/libraw/internal/x3f_tools.h
--- libkdcraw-wrk/libkdcraw/libraw/internal/x3f_tools.h	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/internal/x3f_tools.h	2022-11-07 07:46:31.726795008 +0300
@@ -0,0 +1,539 @@
+/* -*- C++ -*-
+ * Copyright 2020 LibRaw LLC (info@libraw.org)
+ *
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+ */
+
+/* Library for accessing X3F Files
+----------------------------------------------------------------
+BSD-style License
+----------------------------------------------------------------
+
+* Copyright (c) 2010, Roland Karlsson (roland@proxel.se)
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above copyright
+*       notice, this list of conditions and the following disclaimer in the
+*       documentation and/or other materials provided with the distribution.
+*     * Neither the name of the organization nor the
+*       names of its contributors may be used to endorse or promote products
+*       derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY ROLAND KARLSSON ''AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+* DISCLAIMED. IN NO EVENT SHALL ROLAND KARLSSON BE LIABLE FOR ANY
+* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#ifndef X3F_TOOLS_H
+#define X3F_TOOLS_H
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <stdio.h>
+#include "../libraw/libraw_datastream.h"
+
+/* From X3F_IO.H */
+
+#define SIZE_UNIQUE_IDENTIFIER 16
+#define SIZE_WHITE_BALANCE 32
+#define SIZE_COLOR_MODE 32
+#define NUM_EXT_DATA_2_1 32
+#define NUM_EXT_DATA_3_0 64
+#define NUM_EXT_DATA NUM_EXT_DATA_3_0
+
+#define X3F_VERSION(MAJ, MIN) (uint32_t)(((MAJ) << 16) + MIN)
+#define X3F_VERSION_2_0 X3F_VERSION(2, 0)
+#define X3F_VERSION_2_1 X3F_VERSION(2, 1)
+#define X3F_VERSION_2_2 X3F_VERSION(2, 2)
+#define X3F_VERSION_2_3 X3F_VERSION(2, 3)
+#define X3F_VERSION_3_0 X3F_VERSION(3, 0)
+#define X3F_VERSION_4_0 X3F_VERSION(4, 0)
+
+/* Main file identifier */
+#define X3F_FOVb (uint32_t)(0x62564f46)
+/* Directory identifier */
+#define X3F_SECd (uint32_t)(0x64434553)
+/* Property section identifiers */
+#define X3F_PROP (uint32_t)(0x504f5250)
+#define X3F_SECp (uint32_t)(0x70434553)
+/* Image section identifiers */
+#define X3F_IMAG (uint32_t)(0x46414d49)
+#define X3F_IMA2 (uint32_t)(0x32414d49)
+#define X3F_SECi (uint32_t)(0x69434553)
+/* CAMF section identifiers */
+#define X3F_CAMF (uint32_t)(0x464d4143)
+#define X3F_SECc (uint32_t)(0x63434553)
+/* CAMF entry identifiers */
+#define X3F_CMbP (uint32_t)(0x50624d43)
+#define X3F_CMbT (uint32_t)(0x54624d43)
+#define X3F_CMbM (uint32_t)(0x4d624d43)
+#define X3F_CMb (uint32_t)(0x00624d43)
+/* SDQ section identifiers ? - TODO */
+#define X3F_SPPA (uint32_t)(0x41505053)
+#define X3F_SECs (uint32_t)(0x73434553)
+
+#define X3F_IMAGE_THUMB_PLAIN (uint32_t)(0x00020003)
+#define X3F_IMAGE_THUMB_HUFFMAN (uint32_t)(0x0002000b)
+#define X3F_IMAGE_THUMB_JPEG (uint32_t)(0x00020012)
+#define X3F_IMAGE_THUMB_SDQ (uint32_t)(0x00020019) /* SDQ ? - TODO */
+
+#define X3F_IMAGE_RAW_HUFFMAN_X530 (uint32_t)(0x00030005)
+#define X3F_IMAGE_RAW_HUFFMAN_10BIT (uint32_t)(0x00030006)
+#define X3F_IMAGE_RAW_TRUE (uint32_t)(0x0003001e)
+#define X3F_IMAGE_RAW_MERRILL (uint32_t)(0x0001001e)
+#define X3F_IMAGE_RAW_QUATTRO (uint32_t)(0x00010023)
+#define X3F_IMAGE_RAW_SDQ (uint32_t)(0x00010025)
+#define X3F_IMAGE_RAW_SDQH (uint32_t)(0x00010027)
+#define X3F_IMAGE_RAW_SDQH2 (uint32_t)(0x00010029)
+
+#define X3F_IMAGE_HEADER_SIZE 28
+#define X3F_CAMF_HEADER_SIZE 28
+#define X3F_PROPERTY_LIST_HEADER_SIZE 24
+
+typedef uint16_t utf16_t;
+
+typedef int bool_t;
+
+typedef enum x3f_extended_types_e
+{
+  X3F_EXT_TYPE_NONE = 0,
+  X3F_EXT_TYPE_EXPOSURE_ADJUST = 1,
+  X3F_EXT_TYPE_CONTRAST_ADJUST = 2,
+  X3F_EXT_TYPE_SHADOW_ADJUST = 3,
+  X3F_EXT_TYPE_HIGHLIGHT_ADJUST = 4,
+  X3F_EXT_TYPE_SATURATION_ADJUST = 5,
+  X3F_EXT_TYPE_SHARPNESS_ADJUST = 6,
+  X3F_EXT_TYPE_RED_ADJUST = 7,
+  X3F_EXT_TYPE_GREEN_ADJUST = 8,
+  X3F_EXT_TYPE_BLUE_ADJUST = 9,
+  X3F_EXT_TYPE_FILL_LIGHT_ADJUST = 10
+} x3f_extended_types_t;
+
+typedef struct x3f_property_s
+{
+  /* Read from file */
+  uint32_t name_offset;
+  uint32_t value_offset;
+
+  /* Computed */
+  utf16_t *name;  /* 0x0000 terminated UTF 16 */
+  utf16_t *value; /* 0x0000 terminated UTF 16 */
+} x3f_property_t;
+
+typedef struct x3f_property_table_s
+{
+  uint32_t size;
+  x3f_property_t *element;
+} x3f_property_table_t;
+
+typedef struct x3f_property_list_s
+{
+  /* 2.0 Fields */
+  uint32_t num_properties;
+  uint32_t character_format;
+  uint32_t reserved;
+  uint32_t total_length;
+
+  x3f_property_table_t property_table;
+
+  void *data;
+
+  uint32_t data_size;
+
+} x3f_property_list_t;
+
+typedef struct x3f_table8_s
+{
+  uint32_t size;
+  uint8_t *element;
+} x3f_table8_t;
+
+typedef struct x3f_table16_s
+{
+  uint32_t size;
+  uint16_t *element;
+} x3f_table16_t;
+
+typedef struct x3f_table32_s
+{
+  uint32_t size;
+  uint32_t *element;
+} x3f_table32_t;
+
+typedef struct
+{
+  uint8_t *data; /* Pointer to actual image data */
+  void *buf;     /* Pointer to allocated buffer for free() */
+  uint32_t rows;
+  uint32_t columns;
+  uint32_t channels;
+  uint32_t row_stride;
+} x3f_area8_t;
+
+typedef struct
+{
+  uint16_t *data; /* Pointer to actual image data */
+  void *buf;      /* Pointer to allocated buffer for free() */
+  uint32_t rows;
+  uint32_t columns;
+  uint32_t channels;
+  uint32_t row_stride;
+} x3f_area16_t;
+
+#define UNDEFINED_LEAF 0xffffffff
+
+typedef struct x3f_huffnode_s
+{
+  struct x3f_huffnode_s *branch[2];
+  uint32_t leaf;
+} x3f_huffnode_t;
+
+typedef struct x3f_hufftree_s
+{
+  uint32_t free_node_index; /* Free node index in huffman tree array */
+  uint32_t total_node_index;
+  x3f_huffnode_t *nodes;    /* Coding tree */
+} x3f_hufftree_t;
+
+typedef struct x3f_true_huffman_element_s
+{
+  uint8_t code_size;
+  uint8_t code;
+} x3f_true_huffman_element_t;
+
+typedef struct x3f_true_huffman_s
+{
+  uint32_t size;
+  x3f_true_huffman_element_t *element;
+} x3f_true_huffman_t;
+
+/* 0=bottom, 1=middle, 2=top */
+#define TRUE_PLANES 3
+
+typedef struct x3f_true_s
+{
+  uint16_t seed[TRUE_PLANES]; /* Always 512,512,512 */
+  uint16_t unknown;           /* Always 0 */
+  x3f_true_huffman_t table;   /* Huffman table - zero
+                     terminated. size is the number of
+                     leaves plus 1.*/
+
+  x3f_table32_t plane_size;            /* Size of the 3 planes */
+  uint8_t *plane_address[TRUE_PLANES]; /* computed offset to the planes */
+  x3f_hufftree_t tree;                 /* Coding tree */
+  x3f_area16_t x3rgb16;                /* 3x16 bit X3-RGB data */
+} x3f_true_t;
+
+typedef struct x3f_quattro_s
+{
+  struct
+  {
+    uint16_t columns;
+    uint16_t rows;
+  } plane[TRUE_PLANES];
+  uint32_t unknown;
+
+  bool_t quattro_layout;
+  x3f_area16_t top16; /* Container for the bigger top layer */
+} x3f_quattro_t;
+
+typedef struct x3f_huffman_s
+{
+  x3f_table16_t mapping;     /* Value Mapping = X3F lossy compression */
+  x3f_table32_t table;       /* Coding Table */
+  x3f_hufftree_t tree;       /* Coding tree */
+  x3f_table32_t row_offsets; /* Row offsets */
+  x3f_area8_t rgb8;          /* 3x8 bit RGB data */
+  x3f_area16_t x3rgb16;      /* 3x16 bit X3-RGB data */
+} x3f_huffman_t;
+
+typedef struct x3f_image_data_s
+{
+  /* 2.0 Fields */
+  /* ------------------------------------------------------------------ */
+  /* Known combinations of type and format are:
+     1-6, 2-3, 2-11, 2-18, 3-6 */
+  uint32_t type;        /* 1 = RAW X3 (SD1)
+                           2 = thumbnail or maybe just RGB
+                           3 = RAW X3 */
+  uint32_t format;      /* 3 = 3x8 bit pixmap
+                           6 = 3x10 bit huffman with map table
+                           11 = 3x8 bit huffman
+                           18 = JPEG */
+  uint32_t type_format; /* type<<16 + format */
+  /* ------------------------------------------------------------------ */
+
+  uint32_t columns;    /* width / row size in pixels */
+  uint32_t rows;       /* height */
+  uint32_t row_stride; /* row size in bytes */
+
+  /* NULL if not used */
+  x3f_huffman_t *huffman; /* Huffman help data */
+  x3f_true_t *tru;        /* TRUE help data */
+  x3f_quattro_t *quattro; /* Quattro help data */
+
+  void *data; /* Take from file if NULL. Otherwise,
+                 this is the actual data bytes in
+                 the file. */
+  uint32_t data_size;
+
+} x3f_image_data_t;
+
+typedef struct camf_dim_entry_s
+{
+  uint32_t size;
+  uint32_t name_offset;
+  uint32_t n; /* 0,1,2,3... */
+  char *name;
+} camf_dim_entry_t;
+
+typedef enum
+{
+  M_FLOAT,
+  M_INT,
+  M_UINT
+} matrix_type_t;
+
+typedef struct camf_entry_s
+{
+  /* pointer into decoded data */
+  void *entry;
+
+  /* entry header */
+  uint32_t id;
+  uint32_t version;
+  uint32_t entry_size;
+  uint32_t name_offset;
+  uint32_t value_offset;
+
+  /* computed values */
+  char *name_address;
+  void *value_address;
+  uint32_t name_size;
+  uint32_t value_size;
+
+  /* extracted values for explicit CAMF entry types*/
+  uint32_t text_size;
+  char *text;
+
+  uint32_t property_num;
+  char **property_name;
+  uint8_t **property_value;
+
+  uint32_t matrix_dim;
+  camf_dim_entry_t *matrix_dim_entry;
+
+  /* Offset, pointer and size and type of raw data */
+  uint32_t matrix_type;
+  uint32_t matrix_data_off;
+  void *matrix_data;
+  uint32_t matrix_element_size;
+
+  /* Pointer and type of copied data */
+  matrix_type_t matrix_decoded_type;
+  void *matrix_decoded;
+
+  /* Help data to try to estimate element size */
+  uint32_t matrix_elements;
+  uint32_t matrix_used_space;
+  double matrix_estimated_element_size;
+
+} camf_entry_t;
+
+typedef struct camf_entry_table_s
+{
+  uint32_t size;
+  camf_entry_t *element;
+} camf_entry_table_t;
+
+typedef struct x3f_camf_typeN_s
+{
+  uint32_t val0;
+  uint32_t val1;
+  uint32_t val2;
+  uint32_t val3;
+} x3f_camf_typeN_t;
+
+typedef struct x3f_camf_type2_s
+{
+  uint32_t reserved;
+  uint32_t infotype;
+  uint32_t infotype_version;
+  uint32_t crypt_key;
+} x3f_camf_type2_t;
+
+typedef struct x3f_camf_type4_s
+{
+  uint32_t decoded_data_size;
+  uint32_t decode_bias;
+  uint32_t block_size;
+  uint32_t block_count;
+} x3f_camf_type4_t;
+
+typedef struct x3f_camf_type5_s
+{
+  uint32_t decoded_data_size;
+  uint32_t decode_bias;
+  uint32_t unknown2;
+  uint32_t unknown3;
+} x3f_camf_type5_t;
+
+typedef struct x3f_camf_s
+{
+
+  /* Header info */
+  uint32_t type;
+  union {
+    x3f_camf_typeN_t tN;
+    x3f_camf_type2_t t2;
+    x3f_camf_type4_t t4;
+    x3f_camf_type5_t t5;
+  };
+
+  /* The encrypted raw data */
+  void *data;
+  uint32_t data_size;
+
+  /* Help data for type 4 Huffman compression */
+  x3f_true_huffman_t table;
+  x3f_hufftree_t tree;
+  uint8_t *decoding_start;
+  uint32_t decoding_size;
+
+  /* The decrypted data */
+  void *decoded_data;
+  uint32_t decoded_data_size;
+
+  /* Pointers into the decrypted data */
+  camf_entry_table_t entry_table;
+} x3f_camf_t;
+
+typedef struct x3f_directory_entry_header_s
+{
+  uint32_t identifier; /* Should be ´SECp´, "SECi", ... */
+  uint32_t version;    /* 0x00020001 is version 2.1  */
+  union {
+    x3f_property_list_t property_list;
+    x3f_image_data_t image_data;
+    x3f_camf_t camf;
+  } data_subsection;
+} x3f_directory_entry_header_t;
+
+typedef struct x3f_directory_entry_s
+{
+  struct
+  {
+    uint32_t offset;
+    uint32_t size;
+  } input, output;
+
+  uint32_t type;
+
+  x3f_directory_entry_header_t header;
+} x3f_directory_entry_t;
+
+typedef struct x3f_directory_section_s
+{
+  uint32_t identifier; /* Should be ´SECd´ */
+  uint32_t version;    /* 0x00020001 is version 2.1  */
+
+  /* 2.0 Fields */
+  uint32_t num_directory_entries;
+  x3f_directory_entry_t *directory_entry;
+} x3f_directory_section_t;
+
+typedef struct x3f_header_s
+{
+  /* 2.0 Fields */
+  uint32_t identifier; /* Should be ´FOVb´ */
+  uint32_t version;    /* 0x00020001 means 2.1 */
+  uint8_t unique_identifier[SIZE_UNIQUE_IDENTIFIER];
+  uint32_t mark_bits;
+  uint32_t columns;  /* Columns and rows ... */
+  uint32_t rows;     /* ... before rotation */
+  uint32_t rotation; /* 0, 90, 180, 270 */
+
+  char white_balance[SIZE_WHITE_BALANCE]; /* Introduced in 2.1 */
+  char color_mode[SIZE_COLOR_MODE];       /* Introduced in 2.3 */
+
+  /* Introduced in 2.1 and extended from 32 to 64 in 3.0 */
+  uint8_t extended_types[NUM_EXT_DATA]; /* x3f_extended_types_t */
+  float extended_data[NUM_EXT_DATA];    /* 32 bits, but do type differ? */
+} x3f_header_t;
+
+typedef struct x3f_info_s
+{
+  char *error;
+  struct
+  {
+    LibRaw_abstract_datastream *file; /* Use if more data is needed */
+  } input, output;
+} x3f_info_t;
+
+typedef struct x3f_s
+{
+  x3f_info_t info;
+  x3f_header_t header;
+  x3f_directory_section_t directory_section;
+} x3f_t;
+
+typedef enum x3f_return_e
+{
+  X3F_OK = 0,
+  X3F_ARGUMENT_ERROR = 1,
+  X3F_INFILE_ERROR = 2,
+  X3F_OUTFILE_ERROR = 3,
+  X3F_INTERNAL_ERROR = 4
+} x3f_return_t;
+
+x3f_return_t x3f_delete(x3f_t *x3f);
+
+/* Hacky external flags                                                 */
+/* --------------------------------------------------------------------- */
+
+extern int legacy_offset;
+extern bool_t auto_legacy_offset;
+
+/* --------------------------------------------------------------------- */
+/* Huffman Decode Macros                                                 */
+/* --------------------------------------------------------------------- */
+
+#define HUF_TREE_MAX_LENGTH 27
+#define HUF_TREE_MAX_NODES(_leaves) ((HUF_TREE_MAX_LENGTH + 1) * (_leaves))
+#define HUF_TREE_GET_LENGTH(_v) (((_v) >> 27) & 0x1f)
+#define HUF_TREE_GET_CODE(_v) ((_v)&0x07ffffff)
+
+x3f_t *x3f_new_from_file(LibRaw_abstract_datastream *infile);
+x3f_return_t x3f_delete(x3f_t *x3f);
+x3f_directory_entry_t *x3f_get_raw(x3f_t *x3f);
+x3f_directory_entry_t *x3f_get_thumb_plain(x3f_t *x3f);
+x3f_return_t x3f_load_data(x3f_t *x3f, x3f_directory_entry_t *DE);
+x3f_directory_entry_t *x3f_get_thumb_huffman(x3f_t *x3f);
+x3f_directory_entry_t *x3f_get_thumb_jpeg(x3f_t *x3f);
+x3f_directory_entry_t *x3f_get_camf(x3f_t *x3f);
+x3f_directory_entry_t *x3f_get_prop(x3f_t *x3f);
+/* extern */ int64_t x3f_load_data_size(x3f_t *x3f, x3f_directory_entry_t *DE);
+
+#endif
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/libraw/libraw.h libkdcraw/libkdcraw/libraw/libraw/libraw.h
--- libkdcraw-wrk/libkdcraw/libraw/libraw/libraw.h	2022-11-07 08:15:53.614821808 +0300
+++ libkdcraw/libkdcraw/libraw/libraw/libraw.h	2022-11-07 07:46:31.726795008 +0300
@@ -1,38 +1,68 @@
-/*
+/* -*- C++ -*-
  * File: libraw.h
- * Copyright 2008-2009 Alex Tutubalin <lexa@lexa.ru>
- * Created: Sat Mar  8, 2008 
+ * Copyright 2008-2020 LibRaw LLC (info@libraw.org)
+ * Created: Sat Mar  8, 2008
  *
  * LibRaw C++ interface
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- */
+
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+*/
 
 #ifndef _LIBRAW_CLASS_H
 #define _LIBRAW_CLASS_H
 
-#ifdef HAVE_CONFIG_H
-#include "config.h"
+#ifdef __linux__
+#define _FILE_OFFSET_BITS 64
 #endif
 
+/* maximum file size to use LibRaw_file_datastream (fully buffered) I/O */
+#define LIBRAW_USE_STREAMS_DATASTREAM_MAXSIZE (250 * 1024L * 1024L)
+
 #include <limits.h>
 #include <memory.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <math.h>
+
+/* better WIN32 defines */
 
+/* better WIN32 defines */
+
+#if defined(WIN32) || defined(_WIN32)
+
+/* Win32 API */
+#  ifndef LIBRAW_WIN32_CALLS
+#   define LIBRAW_WIN32_CALLS
+#  endif
+
+/* DLLs: Microsoft or Intel compiler */
+# if defined(_MSC_VER) || defined(__INTEL_COMPILER)
+# ifndef LIBRAW_WIN32_DLLDEFS
+#  define LIBRAW_WIN32_DLLDEFS
+# endif
+#endif
+
+/* wchar_t* API for std::filebuf */
+# if (defined(_MSC_VER)  && (_MSC_VER > 1310)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 910))
+#  ifndef LIBRAW_WIN32_UNICODEPATHS
+#   define LIBRAW_WIN32_UNICODEPATHS
+#  endif
+# elif _GLIBCXX_HAVE__WFOPEN && _GLIBCXX_USE_WCHAR_T
+#  ifndef LIBRAW_WIN32_UNICODEPATHS
+#    define LIBRAW_WIN32_UNICODEPATHS
+#  endif
+# endif
+
+#endif
 
 #include "libraw_datastream.h"
 #include "libraw_types.h"
@@ -40,194 +70,416 @@
 #include "libraw_internal.h"
 #include "libraw_alloc.h"
 
-//#define DCRAW_VERBOSE
-
 #ifdef __cplusplus
-extern "C" 
+extern "C"
 {
 #endif
-DllDef    const char          *libraw_strerror(int errorcode);
-DllDef    const char          *libraw_strprogress(enum LibRaw_progress);
-    // LibRaw C API
-DllDef    libraw_data_t       *libraw_init(unsigned int flags);
-DllDef    int                 libraw_open_file(libraw_data_t*, const char *);
-DllDef    int                 libraw_open_buffer(libraw_data_t*, void * buffer, size_t size);
-DllDef    int                 libraw_unpack(libraw_data_t*);
-DllDef    int                 libraw_unpack_thumb(libraw_data_t*);
-DllDef    void                libraw_recycle(libraw_data_t*);
-DllDef    void                libraw_close(libraw_data_t*);
-    // version helpers
-DllDef    const char*               libraw_version();
-DllDef    int                 libraw_versionNumber();
-    // Camera list
-DllDef    const char**        libraw_cameraList();
-DllDef    int                 libraw_cameraCount();
-
-DllDef    void                libraw_set_memerror_handler(libraw_data_t*, memory_callback cb, void *datap);
-DllDef    void                libraw_set_dataerror_handler(libraw_data_t*,data_callback func,void *datap);
-DllDef    void                libraw_set_progress_handler(libraw_data_t*,progress_callback cb,void *datap);
-DllDef    int                 libraw_add_masked_borders_to_bitmap(libraw_data_t* lr);
-DllDef    const char *        libraw_unpack_function_name(libraw_data_t* lr);
-DllDef    int                 libraw_rotate_fuji_raw(libraw_data_t* lr);
-
-    // DCRAW compatibility
-DllDef    int                 libraw_adjust_sizes_info_only(libraw_data_t*);
-DllDef    int                 libraw_dcraw_document_mode_processing(libraw_data_t*);
-DllDef    int                 libraw_dcraw_ppm_tiff_writer(libraw_data_t* lr,const char *filename);
-DllDef    int                 libraw_dcraw_thumb_writer(libraw_data_t* lr,const char *fname);
-DllDef    int                 libraw_dcraw_process(libraw_data_t* lr);
-DllDef    libraw_processed_image_t* dcraw_make_mem_image(libraw_data_t* lr, int *errc);
-DllDef    libraw_processed_image_t* dcraw_make_mem_thumb(libraw_data_t* lr, int *errc);
+  DllDef const char *libraw_strerror(int errorcode);
+  DllDef const char *libraw_strprogress(enum LibRaw_progress);
+  /* LibRaw C API */
+  DllDef libraw_data_t *libraw_init(unsigned int flags);
+  DllDef int libraw_open_file(libraw_data_t *, const char *);
+  DllDef int libraw_open_file_ex(libraw_data_t *, const char *,
+                                 INT64 max_buff_sz);
+#if defined(_WIN32) || defined(WIN32)
+  DllDef int libraw_open_wfile(libraw_data_t *, const wchar_t *);
+  DllDef int libraw_open_wfile_ex(libraw_data_t *, const wchar_t *,
+                                  INT64 max_buff_sz);
+#endif
+  DllDef int libraw_open_buffer(libraw_data_t *, void *buffer, size_t size);
+  DllDef int libraw_unpack(libraw_data_t *);
+  DllDef int libraw_unpack_thumb(libraw_data_t *);
+  DllDef void libraw_recycle_datastream(libraw_data_t *);
+  DllDef void libraw_recycle(libraw_data_t *);
+  DllDef void libraw_close(libraw_data_t *);
+  DllDef void libraw_subtract_black(libraw_data_t *);
+  DllDef int libraw_raw2image(libraw_data_t *);
+  DllDef void libraw_free_image(libraw_data_t *);
+  /* version helpers */
+  DllDef const char *libraw_version();
+  DllDef int libraw_versionNumber();
+  /* Camera list */
+  DllDef const char **libraw_cameraList();
+  DllDef int libraw_cameraCount();
+
+  /* helpers */
+  DllDef void libraw_set_memerror_handler(libraw_data_t *, memory_callback cb,
+                                          void *datap);
+  DllDef void libraw_set_exifparser_handler(libraw_data_t *,
+                                            exif_parser_callback cb,
+                                            void *datap);
+  DllDef void libraw_set_dataerror_handler(libraw_data_t *, data_callback func,
+                                           void *datap);
+  DllDef void libraw_set_progress_handler(libraw_data_t *, progress_callback cb,
+                                          void *datap);
+  DllDef const char *libraw_unpack_function_name(libraw_data_t *lr);
+  DllDef int libraw_get_decoder_info(libraw_data_t *lr,
+                                     libraw_decoder_info_t *d);
+  DllDef int libraw_COLOR(libraw_data_t *, int row, int col);
+  DllDef unsigned libraw_capabilities();
+
+  /* DCRAW compatibility */
+  DllDef int libraw_adjust_sizes_info_only(libraw_data_t *);
+  DllDef int libraw_dcraw_ppm_tiff_writer(libraw_data_t *lr,
+                                          const char *filename);
+  DllDef int libraw_dcraw_thumb_writer(libraw_data_t *lr, const char *fname);
+  DllDef int libraw_dcraw_process(libraw_data_t *lr);
+  DllDef libraw_processed_image_t *
+  libraw_dcraw_make_mem_image(libraw_data_t *lr, int *errc);
+  DllDef libraw_processed_image_t *
+  libraw_dcraw_make_mem_thumb(libraw_data_t *lr, int *errc);
+  DllDef void libraw_dcraw_clear_mem(libraw_processed_image_t *);
+  /* getters/setters used by 3DLut Creator */
+  DllDef void libraw_set_demosaic(libraw_data_t *lr, int value);
+  DllDef void libraw_set_output_color(libraw_data_t *lr, int value);
+  DllDef void libraw_set_user_mul(libraw_data_t *lr, int index, float val);
+  DllDef void libraw_set_output_bps(libraw_data_t *lr, int value);
+  DllDef void libraw_set_gamma(libraw_data_t *lr, int index, float value);
+  DllDef void libraw_set_no_auto_bright(libraw_data_t *lr, int value);
+  DllDef void libraw_set_bright(libraw_data_t *lr, float value);
+  DllDef void libraw_set_highlight(libraw_data_t *lr, int value);
+  DllDef void libraw_set_fbdd_noiserd(libraw_data_t *lr, int value);
+  DllDef int libraw_get_raw_height(libraw_data_t *lr);
+  DllDef int libraw_get_raw_width(libraw_data_t *lr);
+  DllDef int libraw_get_iheight(libraw_data_t *lr);
+  DllDef int libraw_get_iwidth(libraw_data_t *lr);
+  DllDef float libraw_get_cam_mul(libraw_data_t *lr, int index);
+  DllDef float libraw_get_pre_mul(libraw_data_t *lr, int index);
+  DllDef float libraw_get_rgb_cam(libraw_data_t *lr, int index1, int index2);
+  DllDef int libraw_get_color_maximum(libraw_data_t *lr);
+  DllDef void libraw_set_output_tif(libraw_data_t *lr, int value);
+  DllDef libraw_iparams_t *libraw_get_iparams(libraw_data_t *lr);
+  DllDef libraw_lensinfo_t *libraw_get_lensinfo(libraw_data_t *lr);
+  DllDef libraw_imgother_t *libraw_get_imgother(libraw_data_t *lr);
 
 #ifdef __cplusplus
 }
 #endif
 
-
 #ifdef __cplusplus
 
 class DllDef LibRaw
 {
-  public:
-    libraw_data_t imgdata;
-    int verbose;
-
-    LibRaw(unsigned int flags = LIBRAW_OPTIONS_NONE);
-    
-    libraw_output_params_t*     output_params_ptr() { return &imgdata.params;}
-    int                         open_file(const char *fname);
-    int                         open_buffer(void *buffer, size_t size);
-    int                         open_datastream(LibRaw_abstract_datastream *);
-    int                         unpack(void);
-    int                         unpack_thumb(void);
-
-    int                         adjust_sizes_info_only(void);
-    void                        set_memerror_handler( memory_callback cb,void *data) {callbacks.memcb_data = data; callbacks.mem_cb = cb; }
-    void                        set_dataerror_handler(data_callback func, void *data) { callbacks.datacb_data = data; callbacks.data_cb = func;}
-    void                        set_progress_handler(progress_callback pcb, void *data) { callbacks.progresscb_data = data; callbacks.progress_cb = pcb;}
-
-    // helpers
-    static const char*          version() { return LIBRAW_VERSION_STR;}
-    static int                  versionNumber() { return LIBRAW_VERSION; }
-    static const char**         cameraList();
-    static int                  cameraCount();
-    static const char*          strprogress(enum LibRaw_progress);
-    static const char*          strerror(int p) { return libraw_strerror(p);}
-    // dcraw emulation
-    int                         dcraw_document_mode_processing();
-    int                         dcraw_ppm_tiff_writer(const char *filename);
-    int                         dcraw_thumb_writer(const char *fname);
-    int                         dcraw_process(void);
-    // memory writers
-    libraw_processed_image_t*   dcraw_make_mem_image(int *errcode=NULL);  
-    libraw_processed_image_t*   dcraw_make_mem_thumb(int *errcode=NULL);  
-
-    // free all internal data structures
-    void         recycle(); 
-    ~LibRaw(void) { recycle(); delete tls; }
-
-    int FC(int row,int col) { return (imgdata.idata.filters >> ((((row) << 1 & 14) + ((col) & 1)) << 1) & 3);}
-    int         fc (int row, int col);
-    int add_masked_borders_to_bitmap();
-    
-    const char *unpack_function_name();
-    int         rotate_fuji_raw();
-
-  private:
-    void*        malloc(size_t t);
-    void*        calloc(size_t n,size_t t);
-    void        free(void *p);
-    void        merror (void *ptr, const char *where);
-    void        derror();
-
-// data
-
-    LibRaw_TLS  *tls;
-    libraw_internal_data_t libraw_internal_data;
-    decode      first_decode[2048], *second_decode, *free_decode;
-    tiff_ifd_t  tiff_ifd[10];
-    libraw_memmgr memmgr;
-    libraw_callbacks_t callbacks;
-
-    LibRaw_constants rgb_constants;
-    void        (LibRaw:: *write_thumb)(FILE *), 
-                (LibRaw:: *write_fun)(FILE *);
-    void        (LibRaw:: *load_raw)(),
-                (LibRaw:: *thumb_load_raw)();
-
-    void        kodak_thumb_loader();
-    void        write_thumb_ppm_tiff(FILE *); // kodak
-    void        foveon_thumb_loader (void); //Sigma
-
-    
-    // moved from implementation level to private: visibility
-    void init_masked_ptrs();
-    ushort *get_masked_pointer(int row, int col); 
-    
-    int         own_filtering_supported(){ return 0;}
-    void        identify();
-    void        write_ppm_tiff (FILE *ofp);
-    void        convert_to_rgb();
-    void        kodak_ycbcr_load_raw();
-    void        remove_zeroes();
-#ifndef NO_LCMS
-    void	apply_profile(char*,char*);
-#endif
-// Iterpolators
-    void        pre_interpolate();
-    void        border_interpolate (int border);
-    void        lin_interpolate();
-    void        vng_interpolate();
-    void        ppg_interpolate();
-    void        ahd_interpolate();
-
-// Image filters
-    void        bad_pixels(char*);
-    void        subtract(char*);
-    void        hat_transform (float *temp, float *base, int st, int size, int sc);
-    void        wavelet_denoise();
-    void        scale_colors();
-    void        median_filter ();
-    void        blend_highlights();
-    void        recover_highlights();
-
-    void        fuji_rotate();
-    void        stretch();
-
-// Thmbnail functions
-    void        foveon_thumb (FILE *tfp);
-    void        jpeg_thumb_writer (FILE *tfp,char *thumb,int thumb_length);
-    void        jpeg_thumb (FILE *tfp);
-    void        ppm_thumb (FILE *tfp);
-    void        layer_thumb (FILE *tfp);
-    void        rollei_thumb (FILE *tfp);
-    void        kodak_thumb_load_raw();
-
-    // utility for cut'n'pasted code
-    void        foveon_decoder (unsigned size, unsigned code);
-    unsigned    get4();
+public:
+  libraw_data_t imgdata;
 
-    int         flip_index (int row, int col);
-    void        gamma_lut(ushort lut[0x10000]);
+  LibRaw(unsigned int flags = LIBRAW_OPTIONS_NONE);
+  libraw_output_params_t *output_params_ptr() { return &imgdata.params; }
+  int open_file(const char *fname,
+                INT64 max_buffered_sz = LIBRAW_USE_STREAMS_DATASTREAM_MAXSIZE);
+#if defined(_WIN32) || defined(WIN32)
+  int open_file(const wchar_t *fname,
+                INT64 max_buffered_sz = LIBRAW_USE_STREAMS_DATASTREAM_MAXSIZE);
+#endif
+  int open_buffer(void *buffer, size_t size);
+  virtual int open_datastream(LibRaw_abstract_datastream *);
+  virtual int open_bayer(unsigned char *data, unsigned datalen,
+                         ushort _raw_width, ushort _raw_height,
+                         ushort _left_margin, ushort _top_margin,
+                         ushort _right_margin, ushort _bottom_margin,
+                         unsigned char procflags, unsigned char bayer_pattern,
+                         unsigned unused_bits, unsigned otherflags,
+                         unsigned black_level);
+  int error_count() { return libraw_internal_data.unpacker_data.data_error; }
+  void recycle_datastream();
+  int unpack(void);
+  int unpack_thumb(void);
+  int thumbOK(INT64 maxsz = -1);
+  int adjust_sizes_info_only(void);
+  int subtract_black();
+  int subtract_black_internal();
+  int raw2image();
+  int raw2image_ex(int do_subtract_black);
+  void raw2image_start();
+  void free_image();
+  int adjust_maximum();
+  void set_exifparser_handler(exif_parser_callback cb, void *data)
+  {
+    callbacks.exifparser_data = data;
+    callbacks.exif_cb = cb;
+  }
+  void set_memerror_handler(memory_callback cb, void *data)
+  {
+    callbacks.memcb_data = data;
+    callbacks.mem_cb = cb;
+  }
+  void set_dataerror_handler(data_callback func, void *data)
+  {
+    callbacks.datacb_data = data;
+    callbacks.data_cb = func;
+  }
+  void set_progress_handler(progress_callback pcb, void *data)
+  {
+    callbacks.progresscb_data = data;
+    callbacks.progress_cb = pcb;
+  }
+
+  static const char* cameramakeridx2maker(unsigned maker);
+  int setMakeFromIndex(unsigned index);
+
+  void convertFloatToInt(float dmin = 4096.f, float dmax = 32767.f,
+                         float dtarget = 16383.f);
+  /* helpers */
+  static unsigned capabilities();
+  static const char *version();
+  static int versionNumber();
+  static const char **cameraList();
+  static int cameraCount();
+  static const char *strprogress(enum LibRaw_progress);
+  static const char *strerror(int p);
+  /* dcraw emulation */
+  int dcraw_ppm_tiff_writer(const char *filename);
+  int dcraw_thumb_writer(const char *fname);
+  int dcraw_process(void);
+  /* information calls */
+  int is_fuji_rotated()
+  {
+    return libraw_internal_data.internal_output_params.fuji_width;
+  }
+  int is_sraw();
+  int sraw_midpoint();
+  int is_nikon_sraw();
+  int is_coolscan_nef();
+  int is_jpeg_thumb();
+  int is_floating_point();
+  int have_fpdata();
+  /* memory writers */
+  virtual libraw_processed_image_t *dcraw_make_mem_image(int *errcode = NULL);
+  virtual libraw_processed_image_t *dcraw_make_mem_thumb(int *errcode = NULL);
+  static void dcraw_clear_mem(libraw_processed_image_t *);
+
+  /* Additional calls for make_mem_image */
+  void get_mem_image_format(int *width, int *height, int *colors,
+                            int *bps) const;
+  int copy_mem_image(void *scan0, int stride, int bgr);
+
+  /* free all internal data structures */
+  void recycle();
+  virtual ~LibRaw(void);
+
+  int COLOR(int row, int col)
+  {
+    if (!imgdata.idata.filters)
+      return 6; /* Special value 0+1+2+3 */
+    if (imgdata.idata.filters < 1000)
+      return fcol(row, col);
+    return libraw_internal_data.internal_output_params.fuji_width
+               ? FCF(row, col)
+               : FC(row, col);
+  }
+
+  int FC(int row, int col)
+  {
+    return (imgdata.idata.filters >> (((row << 1 & 14) | (col & 1)) << 1) & 3);
+  }
+  int fcol(int row, int col);
+
+  const char *unpack_function_name();
+  virtual int get_decoder_info(libraw_decoder_info_t *d_info);
+  libraw_internal_data_t *get_internal_data_pointer()
+  {
+    return &libraw_internal_data;
+  }
+
+  static float powf_lim(float a, float b, float limup)
+  {
+    return (b > limup || b < -limup) ? 0.f : powf(a, b);
+  }
+  static float libraw_powf64l(float a, float b) { return powf_lim(a, b, 64.f); }
+
+  static unsigned sgetn(int n, uchar *s)
+  {
+    unsigned result = 0;
+    while (n-- > 0)
+      result = (result << 8) | (*s++);
+    return result;
+  }
+
+  /* Phase one correction/subtractBL calls */
+  /* Returns libraw error code */
+
+  int phase_one_subtract_black(ushort *src, ushort *dest);
+  int phase_one_correct();
+
+  int set_rawspeed_camerafile(char *filename);
+  virtual void setCancelFlag();
+  virtual void clearCancelFlag();
+  virtual int adobe_coeff(unsigned, const char *, int internal_only = 0);
+
+  void set_dng_host(void *);
+
+protected:
+  static void *memmem(char *haystack, size_t haystacklen, char *needle,
+                      size_t needlelen);
+  static char *strcasestr(char *h, const char *n);
+  static size_t strnlen(const char *s, size_t n);
+  int is_curve_linear();
+  void checkCancel();
+  void cam_xyz_coeff(float _rgb_cam[3][4], double cam_xyz[4][3]);
+  void phase_one_allocate_tempbuffer();
+  void phase_one_free_tempbuffer();
+  virtual int is_phaseone_compressed();
+  virtual int is_canon_600();
+  /* Hotspots */
+  virtual void copy_fuji_uncropped(unsigned short cblack[4],
+                                   unsigned short *dmaxp);
+  virtual void copy_bayer(unsigned short cblack[4], unsigned short *dmaxp);
+  virtual void fuji_rotate();
+  virtual void convert_to_rgb_loop(float out_cam[3][4]);
+  virtual void lin_interpolate_loop(int *code, int size);
+  virtual void scale_colors_loop(float scale_mul[4]);
+
+  /* Fujifilm compressed decoder public interface (to make parallel decoder) */
+  virtual void
+  fuji_decode_loop(const struct fuji_compressed_params *common_info, int count,
+                   INT64 *offsets, unsigned *sizes);
+  void fuji_decode_strip(const struct fuji_compressed_params *info_common,
+                         int cur_block, INT64 raw_offset, unsigned size);
+  /* CR3 decoder public interface to make parallel decoder */
+  virtual void crxLoadDecodeLoop(void *, int);
+  int crxDecodePlane(void *, uint32_t planeNumber);
+  virtual void crxLoadFinalizeLoopE3(void *, int);
+  void crxConvertPlaneLineDf(void *, int);
+
+  int FCF(int row, int col)
+  {
+    int rr, cc;
+    if (libraw_internal_data.unpacker_data.fuji_layout)
+    {
+      rr = libraw_internal_data.internal_output_params.fuji_width - 1 - col +
+           (row >> 1);
+      cc = col + ((row + 1) >> 1);
+    }
+    else
+    {
+      rr = libraw_internal_data.internal_output_params.fuji_width - 1 + row -
+           (col >> 1);
+      cc = row + ((col + 1) >> 1);
+    }
+    return FC(rr, cc);
+  }
 
+  void adjust_bl();
+  void *malloc(size_t t);
+  void *calloc(size_t n, size_t t);
+  void *realloc(void *p, size_t s);
+  void free(void *p);
+  void merror(void *ptr, const char *where);
+  void derror();
+
+  LibRaw_TLS *tls;
+  libraw_internal_data_t libraw_internal_data;
+  decode first_decode[2048], *second_decode, *free_decode;
+  tiff_ifd_t tiff_ifd[LIBRAW_IFD_MAXCOUNT];
+  libraw_memmgr memmgr;
+  libraw_callbacks_t callbacks;
+
+  void (LibRaw::*write_thumb)();
+  void (LibRaw::*write_fun)();
+  void (LibRaw::*load_raw)();
+  void (LibRaw::*thumb_load_raw)();
+  void (LibRaw::*pentax_component_load_raw)();
+
+  void kodak_thumb_loader();
+  void write_thumb_ppm_tiff(FILE *);
+#ifdef USE_X3FTOOLS
+  void x3f_thumb_loader();
+  INT64 x3f_thumb_size();
+#endif
 
-// == internal functions
+  int own_filtering_supported() { return 0; }
+  void identify();
+  void initdata();
+  unsigned parse_custom_cameras(unsigned limit, libraw_custom_camera_t table[],
+                                char **list);
+  void write_ppm_tiff();
+  void convert_to_rgb();
+  void remove_zeroes();
+  void crop_masked_pixels();
+#ifndef NO_LCMS
+  void apply_profile(const char *, const char *);
+#endif
+  void pre_interpolate();
+  void border_interpolate(int border);
+  void lin_interpolate();
+  void vng_interpolate();
+  void ppg_interpolate();
+  void cielab(ushort rgb[3], short lab[3]);
+  void xtrans_interpolate(int);
+  void ahd_interpolate();
+  void dht_interpolate();
+  void aahd_interpolate();
+
+  void dcb(int iterations, int dcb_enhance);
+  void fbdd(int noiserd);
+  void exp_bef(float expos, float preser);
+
+  void bad_pixels(const char *);
+  void subtract(const char *);
+  void hat_transform(float *temp, float *base, int st, int size, int sc);
+  void wavelet_denoise();
+  void scale_colors();
+  void median_filter();
+  void blend_highlights();
+  void recover_highlights();
+  void green_matching();
+
+  void stretch();
+
+  void jpeg_thumb_writer(FILE *tfp, char *thumb, int thumb_length);
+  void jpeg_thumb();
+  void ppm_thumb();
+  void ppm16_thumb();
+  void layer_thumb();
+  void rollei_thumb();
+  void kodak_thumb_load_raw();
+
+  unsigned get4();
+
+  int flip_index(int row, int col);
+  void gamma_curve(double pwr, double ts, int mode, int imax);
+  void cubic_spline(const int *x_, const int *y_, const int len);
+
+  /* RawSpeed data */
+  void *_rawspeed_camerameta;
+  void *_rawspeed_decoder;
+  void fix_after_rawspeed(int bl);
+  int try_rawspeed(); /* returns LIBRAW_SUCCESS on success */
+  /* Fast cancel flag */
+  long _exitflag;
+
+  /* DNG SDK data */
+  void *dnghost;
+  void *dngnegative;
+  void *dngimage;
+  int valid_for_dngsdk();
+  int try_dngsdk();
+  /* X3F data */
+  void *_x3f_data; /* keep it even if USE_X3FTOOLS is not defined to do not change sizeof(LibRaw)*/
+
+  int raw_was_read()
+  {
+    return imgdata.rawdata.raw_image || imgdata.rawdata.color4_image ||
+           imgdata.rawdata.color3_image || imgdata.rawdata.float_image ||
+           imgdata.rawdata.float3_image || imgdata.rawdata.float4_image;
+  }
 
-#ifdef LIBRAW_LIBRARY_BUILD 
+#ifdef LIBRAW_LIBRARY_BUILD
 #include "internal/libraw_internal_funcs.h"
 #endif
-
 };
 
-#ifdef LIBRAW_LIBRARY_BUILD 
-#define RUN_CALLBACK(stage,iter,expect)  if(callbacks.progress_cb) { \
-        int rr = (*callbacks.progress_cb)(callbacks.progresscb_data,stage,iter,expect); \
-        if(rr!=0) throw LIBRAW_EXCEPTION_CANCELLED_BY_CALLBACK; \
-    }
+#ifdef LIBRAW_LIBRARY_BUILD
+#define RUN_CALLBACK(stage, iter, expect)                                      \
+  if (callbacks.progress_cb)                                                   \
+  {                                                                            \
+    int rr = (*callbacks.progress_cb)(callbacks.progresscb_data, stage, iter,  \
+                                      expect);                                 \
+    if (rr != 0)                                                               \
+      throw LIBRAW_EXCEPTION_CANCELLED_BY_CALLBACK;                            \
+  }
 #endif
 
+#endif /* __cplusplus */
 
-#endif // __cplusplus
-
-
-#endif // _LIBRAW_CLASS_H
+#endif /* _LIBRAW_CLASS_H */
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/libraw/libraw_alloc.h libkdcraw/libkdcraw/libraw/libraw/libraw_alloc.h
--- libkdcraw-wrk/libkdcraw/libraw/libraw/libraw_alloc.h	2022-11-07 08:15:53.614821808 +0300
+++ libkdcraw/libkdcraw/libraw/libraw/libraw_alloc.h	2022-11-07 07:46:31.726795008 +0300
@@ -1,24 +1,19 @@
-/*
+/* -*- C++ -*-
  * File: libraw_alloc.h
- * Copyright 2008-2009 Alex Tutubalin <lexa@lexa.ru>
- * Created: Sat Mar  22, 2008 
+ * Copyright 2008-2020 LibRaw LLC (info@libraw.org)
+ * Created: Sat Mar  22, 2008
  *
  * LibRaw C++ interface
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
  */
 
 #ifndef __LIBRAW_ALLOC_H
@@ -26,73 +21,97 @@
 
 #include <stdlib.h>
 #include <string.h>
-#ifdef WIN32
-#define bzero(p,sz) memset(p,0,sz)
-#endif
+#include "libraw_const.h"
 
 #ifdef __cplusplus
 
-#define MSIZE 32
+#define LIBRAW_MSIZE 512
 
-class libraw_memmgr
+class DllDef libraw_memmgr
 {
-  public:
-    libraw_memmgr()
-        {
-            bzero(mems,sizeof(mems));
-            calloc_cnt=0;
-        }
-    void *malloc(size_t sz)
+public:
+  libraw_memmgr(unsigned ee) : extra_bytes(ee)
+  {
+    size_t alloc_sz = LIBRAW_MSIZE * sizeof(void *);
+    mems = (void **)::malloc(alloc_sz);
+    memset(mems, 0, alloc_sz);
+  }
+  ~libraw_memmgr()
+  {
+    cleanup();
+    ::free(mems);
+  }
+  void *malloc(size_t sz)
+  {
+#ifdef LIBRAW_USE_CALLOC_INSTEAD_OF_MALLOC
+    void *ptr = ::calloc(sz + extra_bytes, 1);
+#else
+    void *ptr = ::malloc(sz + extra_bytes);
+#endif
+    mem_ptr(ptr);
+    return ptr;
+  }
+  void *calloc(size_t n, size_t sz)
+  {
+    void *ptr = ::calloc(n + (extra_bytes + sz - 1) / (sz ? sz : 1), sz);
+    mem_ptr(ptr);
+    return ptr;
+  }
+  void *realloc(void *ptr, size_t newsz)
+  {
+    void *ret = ::realloc(ptr, newsz + extra_bytes);
+    forget_ptr(ptr);
+    mem_ptr(ret);
+    return ret;
+  }
+  void free(void *ptr)
+  {
+    forget_ptr(ptr);
+    ::free(ptr);
+  }
+  void cleanup(void)
+  {
+    for (int i = 0; i < LIBRAW_MSIZE; i++)
+      if (mems[i])
+      {
+        ::free(mems[i]);
+        mems[i] = NULL;
+      }
+  }
+
+private:
+  void **mems;
+  unsigned extra_bytes;
+  void mem_ptr(void *ptr)
+  {
+    if (ptr)
+    {
+      for (int i = 0; i < LIBRAW_MSIZE - 1; i++)
+        if (!mems[i])
         {
-            void *ptr = ::malloc(sz);
-            mem_ptr(ptr);
-            return ptr;
+          mems[i] = ptr;
+          return;
         }
-    void *calloc(size_t n, size_t sz)
+#ifdef LIBRAW_MEMPOOL_CHECK
+      /* remember ptr in last mems item to be free'ed at cleanup */
+      if (!mems[LIBRAW_MSIZE - 1])
+        mems[LIBRAW_MSIZE - 1] = ptr;
+      throw LIBRAW_EXCEPTION_MEMPOOL;
+#endif
+    }
+  }
+  void forget_ptr(void *ptr)
+  {
+    if (ptr)
+      for (int i = 0; i < LIBRAW_MSIZE; i++)
+        if (mems[i] == ptr)
         {
-            void *ptr =  ::calloc(n,sz);
-            mem_ptr(ptr);
-            return ptr;
+          mems[i] = NULL;
+          break;
         }
-    void  free(void *ptr)
-    {
-        ::free(ptr);
-        forget_ptr(ptr);
-    }
-    void cleanup(void)
-    {
-        for(int i = 0; i< MSIZE; i++)
-            if(mems[i])
-                {
-//                    fprintf(stderr,"Found lost fragment at 0x%x\n",mems[i]);
-                    free(mems[i]);
-                    mems[i] = NULL;
-                }
-    }
-
-  private:
-    void *mems[MSIZE];
-    int calloc_cnt;
-    void mem_ptr(void *ptr)
-    {
-        if(ptr)
-            for(int i=0;i < MSIZE; i++)
-                if(!mems[i])
-                    {
-                        mems[i] = ptr;
-                        break;
-                    }
-    }
-    void forget_ptr(void *ptr)
-    {
-        if(ptr)
-            for(int i=0;i < MSIZE; i++)
-                if(mems[i] == ptr)
-                    mems[i] = NULL;
-    }
-
+  }
 };
 
-#endif //C++
+#endif /* C++ */
 
 #endif
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/libraw/libraw_const.h libkdcraw/libkdcraw/libraw/libraw/libraw_const.h
--- libkdcraw-wrk/libkdcraw/libraw/libraw/libraw_const.h	2022-11-07 08:15:53.614821808 +0300
+++ libkdcraw/libkdcraw/libraw/libraw/libraw_const.h	2022-11-07 07:46:31.726795008 +0300
@@ -1,159 +1,667 @@
-/*
+/* -*- C++ -*-
  * File: libraw_const.h
- * Copyright 2008-2009 Alex Tutubalin <lexa@lexa.ru>
+ * Copyright 2008-2020 LibRaw LLC (info@libraw.org)
  * Created: Sat Mar  8 , 2008
- *
  * LibRaw error codes
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
  */
 
 #ifndef _LIBRAW_ERRORS_H
 #define _LIBRAW_ERRORS_H
 
-enum LibRaw_constructor_flags
+#define LIBRAW_DEFAULT_ADJUST_MAXIMUM_THRESHOLD 0.75
+#define LIBRAW_DEFAULT_AUTO_BRIGHTNESS_THRESHOLD 0.01
+/* limit allocation size, default is 2Gb */
+#ifndef LIBRAW_MAX_ALLOC_MB_DEFAULT
+#define LIBRAW_MAX_ALLOC_MB_DEFAULT 2048L
+#endif
+
+/* limit thumbnail size, default is 512Mb*/
+#ifndef LIBRAW_MAX_THUMBNAIL_MB
+#define LIBRAW_MAX_THUMBNAIL_MB 512L
+#endif
+
+/* Check if enough file space exists before tag read */
+#ifndef LIBRAW_NO_IOSPACE_CHECK
+#define LIBRAW_IOSPACE_CHECK
+#endif
+#ifndef LIBRAW_NO_CR3_MEMPOOL
+#define LIBRAW_CR3_MEMPOOL
+#endif
+
+
+/* LibRaw uses own memory pool management, with LIBRAW_MSIZE (512)
+entries. It is enough for parsing/decoding non-damaged files, but
+may overflow on specially crafted files (eg. with many string values
+like XMP blocks.
+LIBRAW_MEMPOOL_CHECK define will result in error on pool overflow */
+#ifndef LIBRAW_NO_MEMPOOL_CHECK
+#define LIBRAW_MEMPOOL_CHECK
+#endif
+
+#define LIBRAW_MAX_METADATA_BLOCKS 1024
+#define LIBRAW_CBLACK_SIZE 4104
+#define LIBRAW_IFD_MAXCOUNT 10
+#define LIBRAW_CRXTRACKS_MAXCOUNT 16
+
+#define LIBRAW_AHD_TILE 512
+
+enum LibRaw_open_flags
 {
-    LIBRAW_OPTIONS_NONE         =0,
-    LIBRAW_OPIONS_NO_MEMERR_CALLBACK=1,
-    LIBRAW_OPIONS_NO_DATAERR_CALLBACK=1<<1
+	LIBRAW_OPEN_BIGFILE=1,
+	LIBRAW_OPEN_FILE= 1<<1
 };
 
-enum LibRaw_warnings
+enum LibRaw_openbayer_patterns
 {
-    LIBRAW_WARN_NONE            =0,
-    LIBRAW_WARN_FOVEON_NOMATRIX =1,
-    LIBRAW_WARN_FOVEON_INVALIDWB =1<<1,
-    LIBRAW_WARN_BAD_CAMERA_WB   =1<<2,
-    LIBRAW_WARN_NO_METADATA     =1<<3,
-    LIBRAW_WARN_NO_JPEGLIB     = 1<<4,
-    LIBRAW_WARN_NO_EMBEDDED_PROFILE = 1<<5,
-    LIBRAW_WARN_NO_INPUT_PROFILE = 1<<6,
-    LIBRAW_WARN_BAD_OUTPUT_PROFILE= 1<<7,
-    LIBRAW_WARN_NO_BADPIXELMAP=1<<8,
-    LIBRAW_WARN_BAD_DARKFRAME_FILE=1<<9,
-    LIBRAW_WARN_BAD_DARKFRAME_DIM=1<<10
+  LIBRAW_OPENBAYER_RGGB = 0x94,
+  LIBRAW_OPENBAYER_BGGR = 0x16,
+  LIBRAW_OPENBAYER_GRBG = 0x61,
+  LIBRAW_OPENBAYER_GBRG = 0x49
 };
 
-enum LibRaw_exceptions
+enum LibRaw_dngfields_marks
+{
+  LIBRAW_DNGFM_FORWARDMATRIX = 1,
+  LIBRAW_DNGFM_ILLUMINANT = 1 << 1,
+  LIBRAW_DNGFM_COLORMATRIX = 1 << 2,
+  LIBRAW_DNGFM_CALIBRATION = 1 << 3,
+  LIBRAW_DNGFM_ANALOGBALANCE = 1 << 4,
+  LIBRAW_DNGFM_BLACK = 1 << 5,
+  LIBRAW_DNGFM_WHITE = 1 << 6,
+  LIBRAW_DNGFM_OPCODE2 = 1 << 7,
+  LIBRAW_DNGFM_LINTABLE = 1 << 8,
+  LIBRAW_DNGFM_CROPORIGIN = 1 << 9,
+  LIBRAW_DNGFM_CROPSIZE = 1 << 10,
+  LIBRAW_DNGFM_PREVIEWCS = 1 << 11,
+  LIBRAW_DNGFM_ASSHOTNEUTRAL = 1 << 12,
+  LIBRAW_DNGFM_BASELINEEXPOSURE = 1 << 13,
+  LIBRAW_DNGFM_LINEARRESPONSELIMIT = 1 << 14
+};
+
+enum LibRaw_As_Shot_WB_Applied_codes
+{
+  LIBRAW_ASWB_APPLIED = 1,
+  LIBRAW_ASWB_CANON = 2,
+  LIBRAW_ASWB_NIKON = 4,
+  LIBRAW_ASWB_NIKON_SRAW = 8,
+  LIBRAW_ASWB_PENTAX = 16
+};
+
+#define tagtypeIs(typex) (type == typex)
+enum LibRaw_ExifTagTypes {
+  LIBRAW_EXIFTAG_TYPE_UNKNOWN   =  0,
+  LIBRAW_EXIFTAG_TYPE_BYTE      =  1,
+  LIBRAW_EXIFTAG_TYPE_ASCII     =  2,
+  LIBRAW_EXIFTAG_TYPE_SHORT     =  3,
+  LIBRAW_EXIFTAG_TYPE_LONG      =  4,
+  LIBRAW_EXIFTAG_TYPE_RATIONAL  =  5,
+  LIBRAW_EXIFTAG_TYPE_SBYTE     =  6,
+  LIBRAW_EXIFTAG_TYPE_UNDEFINED =  7,
+  LIBRAW_EXIFTAG_TYPE_SSHORT    =  8,
+  LIBRAW_EXIFTAG_TYPE_SLONG     =  9,
+  LIBRAW_EXIFTAG_TYPE_SRATIONAL = 10,
+  LIBRAW_EXIFTAG_TYPE_FLOAT     = 11,
+  LIBRAW_EXIFTAG_TYPE_DOUBLE    = 12,
+  LIBRAW_EXIFTAG_TYPE_IFD       = 13,
+  LIBRAW_EXIFTAG_TYPE_UNICODE   = 14,
+  LIBRAW_EXIFTAG_TYPE_COMPLEX   = 15,
+  LIBRAW_EXIFTAG_TYPE_LONG8     = 16,
+  LIBRAW_EXIFTAG_TYPE_SLONG8    = 17,
+  LIBRAW_EXIFTAG_TYPE_IFD8      = 18
+};
+
+#define LIBRAW_EXIFTOOLTAGTYPE_int8u       LIBRAW_EXIFTAG_TYPE_BYTE
+#define LIBRAW_EXIFTOOLTAGTYPE_string      LIBRAW_EXIFTAG_TYPE_ASCII
+#define LIBRAW_EXIFTOOLTAGTYPE_int16u      LIBRAW_EXIFTAG_TYPE_SHORT
+#define LIBRAW_EXIFTOOLTAGTYPE_int32u      LIBRAW_EXIFTAG_TYPE_LONG
+#define LIBRAW_EXIFTOOLTAGTYPE_rational64u LIBRAW_EXIFTAG_TYPE_RATIONAL
+#define LIBRAW_EXIFTOOLTAGTYPE_int8s       LIBRAW_EXIFTAG_TYPE_SBYTE
+#define LIBRAW_EXIFTOOLTAGTYPE_undef       LIBRAW_EXIFTAG_TYPE_UNDEFINED
+#define LIBRAW_EXIFTOOLTAGTYPE_binary      LIBRAW_EXIFTAG_TYPE_UNDEFINED
+#define LIBRAW_EXIFTOOLTAGTYPE_int16s      LIBRAW_EXIFTAG_TYPE_SSHORT
+#define LIBRAW_EXIFTOOLTAGTYPE_int32s      LIBRAW_EXIFTAG_TYPE_SLONG
+#define LIBRAW_EXIFTOOLTAGTYPE_rational64s LIBRAW_EXIFTAG_TYPE_SRATIONAL
+#define LIBRAW_EXIFTOOLTAGTYPE_float       LIBRAW_EXIFTAG_TYPE_FLOAT
+#define LIBRAW_EXIFTOOLTAGTYPE_double      LIBRAW_EXIFTAG_TYPE_DOUBLE
+#define LIBRAW_EXIFTOOLTAGTYPE_ifd         LIBRAW_EXIFTAG_TYPE_IFD
+#define LIBRAW_EXIFTOOLTAGTYPE_unicode     LIBRAW_EXIFTAG_TYPE_UNICODE
+#define LIBRAW_EXIFTOOLTAGTYPE_complex     LIBRAW_EXIFTAG_TYPE_COMPLEX
+#define LIBRAW_EXIFTOOLTAGTYPE_int64u      LIBRAW_EXIFTAG_TYPE_LONG8
+#define LIBRAW_EXIFTOOLTAGTYPE_int64s      LIBRAW_EXIFTAG_TYPE_SLONG8
+#define LIBRAW_EXIFTOOLTAGTYPE_ifd64       LIBRAW_EXIFTAG_TYPE_IFD8
+
+#define LIBRAW_LENS_NOT_SET 0xffffffffffffffffULL
+
+enum LibRaw_whitebalance_code
+{
+// clang-format off
+  /*
+      EXIF light sources
+      12 = FL-D; Daylight fluorescent (D 5700K – 7100K) (F1,F5)
+      13 = FL-N; Day white fluorescent (N 4600K – 5400K) (F7,F8)
+      14 = FL-W; Cool white fluorescent (W 3900K – 4500K) (F2,F6, office, store, warehouse)
+      15 = FL-WW; White fluorescent (WW 3200K – 3700K) (F3, residential)
+      16 = FL-L; Soft/Warm white fluorescent (L 2600K - 3250K) (F4, kitchen, bath)
+  */
+//clang-format on
+  LIBRAW_WBI_Unknown = 0,
+  LIBRAW_WBI_Daylight = 1,
+  LIBRAW_WBI_Fluorescent = 2,
+  LIBRAW_WBI_Tungsten = 3,
+  LIBRAW_WBI_Flash = 4,
+  LIBRAW_WBI_FineWeather = 9,
+  LIBRAW_WBI_Cloudy = 10,
+  LIBRAW_WBI_Shade = 11,
+  LIBRAW_WBI_FL_D = 12,
+  LIBRAW_WBI_FL_N = 13,
+  LIBRAW_WBI_FL_W = 14,
+  LIBRAW_WBI_FL_WW = 15,
+  LIBRAW_WBI_FL_L = 16,
+  LIBRAW_WBI_Ill_A = 17,
+  LIBRAW_WBI_Ill_B = 18,
+  LIBRAW_WBI_Ill_C = 19,
+  LIBRAW_WBI_D55 = 20,
+  LIBRAW_WBI_D65 = 21,
+  LIBRAW_WBI_D75 = 22,
+  LIBRAW_WBI_D50 = 23,
+  LIBRAW_WBI_StudioTungsten = 24,
+  LIBRAW_WBI_Sunset = 64,
+  LIBRAW_WBI_Underwater = 65,
+  LIBRAW_WBI_FluorescentHigh = 66,
+  LIBRAW_WBI_HT_Mercury = 67,
+  LIBRAW_WBI_AsShot = 81,
+  LIBRAW_WBI_Auto = 82,
+  LIBRAW_WBI_Custom = 83,
+  LIBRAW_WBI_Auto1 = 85,
+  LIBRAW_WBI_Auto2 = 86,
+  LIBRAW_WBI_Auto3 = 87,
+  LIBRAW_WBI_Auto4 = 88,
+  LIBRAW_WBI_Custom1 = 90,
+  LIBRAW_WBI_Custom2 = 91,
+  LIBRAW_WBI_Custom3 = 92,
+  LIBRAW_WBI_Custom4 = 93,
+  LIBRAW_WBI_Custom5 = 94,
+  LIBRAW_WBI_Custom6 = 95,
+  LIBRAW_WBI_PC_Set1 = 96,
+  LIBRAW_WBI_PC_Set2 = 97,
+  LIBRAW_WBI_PC_Set3 = 98,
+  LIBRAW_WBI_PC_Set4 = 99,
+  LIBRAW_WBI_PC_Set5 = 100,
+  LIBRAW_WBI_Measured = 110,
+  LIBRAW_WBI_BW = 120,
+  LIBRAW_WBI_Kelvin = 254,
+  LIBRAW_WBI_Other = 255,
+  LIBRAW_WBI_None = 0xffff
+};
+
+enum LibRaw_MultiExposure_related
+{
+  LIBRAW_ME_NONE = 0,
+  LIBRAW_ME_SIMPLE = 1,
+  LIBRAW_ME_OVERLAY = 2,
+  LIBRAW_ME_HDR = 3
+};
+
+enum LibRaw_dng_processing
+{
+  LIBRAW_DNG_NONE = 0,
+  LIBRAW_DNG_FLOAT = 1,
+  LIBRAW_DNG_LINEAR = 2,
+  LIBRAW_DNG_DEFLATE = 4,
+  LIBRAW_DNG_XTRANS = 8,
+  LIBRAW_DNG_OTHER = 16,
+  LIBRAW_DNG_8BIT = 32,
+  /*LIBRAW_DNG_LARGERANGE=64,*/ /* more than 16 bit integer */
+  LIBRAW_DNG_ALL =
+      LIBRAW_DNG_FLOAT | LIBRAW_DNG_LINEAR | LIBRAW_DNG_XTRANS |
+      LIBRAW_DNG_8BIT | LIBRAW_DNG_OTHER /* |LIBRAW_DNG_LARGERANGE */,
+  LIBRAW_DNG_DEFAULT = LIBRAW_DNG_FLOAT | LIBRAW_DNG_LINEAR |
+                       LIBRAW_DNG_DEFLATE | LIBRAW_DNG_8BIT
+};
+
+enum LibRaw_runtime_capabilities
 {
-    LIBRAW_EXCEPTION_NONE       =0,
-    LIBRAW_EXCEPTION_ALLOC      =1,
-    LIBRAW_EXCEPTION_DECODE_RAW =2,
-    LIBRAW_EXCEPTION_DECODE_JPEG=3,
-    LIBRAW_EXCEPTION_IO_EOF     =4,
-    LIBRAW_EXCEPTION_IO_CORRUPT =5,
-    LIBRAW_EXCEPTION_CANCELLED_BY_CALLBACK=6
+  LIBRAW_CAPS_RAWSPEED = 1,
+  LIBRAW_CAPS_DNGSDK = 2,
+  LIBRAW_CAPS_GPRSDK = 4,
+  LIBRAW_CAPS_UNICODEPATHS = 8,
+  LIBRAW_CAPS_X3FTOOLS = 16,
+  LIBRAW_CAPS_RPI6BY9 = 32
 };
 
+enum LibRaw_colorspace {
+  LIBRAW_COLORSPACE_NotFound = 0,
+  LIBRAW_COLORSPACE_sRGB,
+  LIBRAW_COLORSPACE_AdobeRGB,
+  LIBRAW_COLORSPACE_WideGamutRGB,
+  LIBRAW_COLORSPACE_ProPhotoRGB,
+  LIBRAW_COLORSPACE_ICC,
+  LIBRAW_COLORSPACE_Uncalibrated, // Tag 0x0001 InteropIndex containing "R03" + LIBRAW_COLORSPACE_Uncalibrated = Adobe RGB
+  LIBRAW_COLORSPACE_CameraLinearUniWB,
+  LIBRAW_COLORSPACE_CameraLinear,
+  LIBRAW_COLORSPACE_CameraGammaUniWB,
+  LIBRAW_COLORSPACE_CameraGamma,
+  LIBRAW_COLORSPACE_MonochromeLinear,
+  LIBRAW_COLORSPACE_MonochromeGamma,
+  LIBRAW_COLORSPACE_Unknown = 255
+};
 
-enum LibRaw_colorstate
-{
-    LIBRAW_COLORSTATE_UNKNOWN   =0,
-    LIBRAW_COLORSTATE_INIT      =1,
-    LIBRAW_COLORSTATE_CONST     =2,
-    LIBRAW_COLORSTATE_LOADED    =3,
-    LIBRAW_COLORSTATE_CALCULATED=4,
-    LIBRAW_COLORSTATE_RESERVED1 =5,
-    LIBRAW_COLORSTATE_RESERVED2 =6,
-    LIBRAW_COLORSTATE_RESERVED3 =7
-};
-
-enum LibRaw_filtering
-{
-    LIBRAW_FILTERING_DEFAULT            =0,
-    LIBRAW_FILTERING_NOZEROES           =1,  //  no remove zeroes
-    LIBRAW_FILTERING_NOBLACKS           =2,  //  no black subtraction
-    LIBRAW_FILTERING_NORAWCURVE         =4,  //  no raw data postprocessing (e.g. PhaseOne corrections etc)
-    LIBRAW_FILTERING_NONE               =7,  //  (_NOZEROES | _NOBLACKS | _NORAWCURVE)
-    LIBRAW_FILTERING_LIBRAWOWN          =(8 | LIBRAW_FILTERING_NONE), // NONE + 8 
-    LIBRAW_FILTERING_AUTOMATIC_BIT      =16,  //  - restore automatic mode after processing
-    LIBRAW_FILTERING_AUTOMATIC          = (LIBRAW_FILTERING_LIBRAWOWN | LIBRAW_FILTERING_AUTOMATIC_BIT)
+enum LibRaw_cameramaker_index
+{
+  LIBRAW_CAMERAMAKER_Unknown = 0,
+  LIBRAW_CAMERAMAKER_Agfa,
+  LIBRAW_CAMERAMAKER_Alcatel,
+  LIBRAW_CAMERAMAKER_Apple,
+  LIBRAW_CAMERAMAKER_Aptina,
+  LIBRAW_CAMERAMAKER_AVT,
+  LIBRAW_CAMERAMAKER_Baumer,
+  LIBRAW_CAMERAMAKER_Broadcom,
+  LIBRAW_CAMERAMAKER_Canon,
+  LIBRAW_CAMERAMAKER_Casio,
+  LIBRAW_CAMERAMAKER_CINE,
+  LIBRAW_CAMERAMAKER_Clauss,
+  LIBRAW_CAMERAMAKER_Contax,
+  LIBRAW_CAMERAMAKER_Creative,
+  LIBRAW_CAMERAMAKER_DJI,
+  LIBRAW_CAMERAMAKER_DXO,
+  LIBRAW_CAMERAMAKER_Epson,
+  LIBRAW_CAMERAMAKER_Foculus,
+  LIBRAW_CAMERAMAKER_Fujifilm,
+  LIBRAW_CAMERAMAKER_Generic,
+  LIBRAW_CAMERAMAKER_Gione,
+  LIBRAW_CAMERAMAKER_GITUP,
+  LIBRAW_CAMERAMAKER_Google,
+  LIBRAW_CAMERAMAKER_GoPro,
+  LIBRAW_CAMERAMAKER_Hasselblad,
+  LIBRAW_CAMERAMAKER_HTC,
+  LIBRAW_CAMERAMAKER_I_Mobile,
+  LIBRAW_CAMERAMAKER_Imacon,
+  LIBRAW_CAMERAMAKER_JK_Imaging,
+  LIBRAW_CAMERAMAKER_Kodak,
+  LIBRAW_CAMERAMAKER_Konica,
+  LIBRAW_CAMERAMAKER_Leaf,
+  LIBRAW_CAMERAMAKER_Leica,
+  LIBRAW_CAMERAMAKER_Lenovo,
+  LIBRAW_CAMERAMAKER_LG,
+  LIBRAW_CAMERAMAKER_Logitech,
+  LIBRAW_CAMERAMAKER_Mamiya,
+  LIBRAW_CAMERAMAKER_Matrix,
+  LIBRAW_CAMERAMAKER_Meizu,
+  LIBRAW_CAMERAMAKER_Micron,
+  LIBRAW_CAMERAMAKER_Minolta,
+  LIBRAW_CAMERAMAKER_Motorola,
+  LIBRAW_CAMERAMAKER_NGM,
+  LIBRAW_CAMERAMAKER_Nikon,
+  LIBRAW_CAMERAMAKER_Nokia,
+  LIBRAW_CAMERAMAKER_Olympus,
+  LIBRAW_CAMERAMAKER_OmniVison,
+  LIBRAW_CAMERAMAKER_Panasonic,
+  LIBRAW_CAMERAMAKER_Parrot,
+  LIBRAW_CAMERAMAKER_Pentax,
+  LIBRAW_CAMERAMAKER_PhaseOne,
+  LIBRAW_CAMERAMAKER_PhotoControl,
+  LIBRAW_CAMERAMAKER_Photron,
+  LIBRAW_CAMERAMAKER_Pixelink,
+  LIBRAW_CAMERAMAKER_Polaroid,
+  LIBRAW_CAMERAMAKER_RED,
+  LIBRAW_CAMERAMAKER_Ricoh,
+  LIBRAW_CAMERAMAKER_Rollei,
+  LIBRAW_CAMERAMAKER_RoverShot,
+  LIBRAW_CAMERAMAKER_Samsung,
+  LIBRAW_CAMERAMAKER_Sigma,
+  LIBRAW_CAMERAMAKER_Sinar,
+  LIBRAW_CAMERAMAKER_SMaL,
+  LIBRAW_CAMERAMAKER_Sony,
+  LIBRAW_CAMERAMAKER_ST_Micro,
+  LIBRAW_CAMERAMAKER_THL,
+  LIBRAW_CAMERAMAKER_VLUU,
+  LIBRAW_CAMERAMAKER_Xiaomi,
+  LIBRAW_CAMERAMAKER_XIAOYI,
+  LIBRAW_CAMERAMAKER_YI,
+  LIBRAW_CAMERAMAKER_Yuneec,
+  LIBRAW_CAMERAMAKER_Zeiss,
+  // Insert additional indexes here
+  LIBRAW_CAMERAMAKER_TheLastOne
 };
 
+enum LibRaw_camera_mounts
+{
+  LIBRAW_MOUNT_Unknown = 0,
+  LIBRAW_MOUNT_Alpa,
+  LIBRAW_MOUNT_C,              /* C-mount */
+  LIBRAW_MOUNT_Canon_EF_M,
+  LIBRAW_MOUNT_Canon_EF_S,
+  LIBRAW_MOUNT_Canon_EF,
+  LIBRAW_MOUNT_Canon_RF,
+  LIBRAW_MOUNT_Contax_N,
+  LIBRAW_MOUNT_Contax645,
+  LIBRAW_MOUNT_FT,             /* original 4/3 */
+  LIBRAW_MOUNT_mFT,            /* micro 4/3 */
+  LIBRAW_MOUNT_Fuji_GF,        /* Fujifilm GFX cameras, G mount */
+  LIBRAW_MOUNT_Fuji_GX,        /* Fujifilm GX680 */
+  LIBRAW_MOUNT_Fuji_X,
+  LIBRAW_MOUNT_Hasselblad_H,   /* Hasselblad Hn cameras, HC & HCD lenses */
+  LIBRAW_MOUNT_Hasselblad_V,
+  LIBRAW_MOUNT_Hasselblad_XCD, /* Hasselblad Xn cameras, XCD lenses */
+  LIBRAW_MOUNT_Leica_M,        /* Leica rangefinder bayonet */
+  LIBRAW_MOUNT_Leica_R,        /* Leica SLRs, 'R' for reflex */
+  LIBRAW_MOUNT_Leica_S,        /* LIBRAW_FORMAT_LeicaS 'MF' */
+  LIBRAW_MOUNT_Leica_SL,       /* lens, mounts on 'L' throat, FF */
+  LIBRAW_MOUNT_Leica_TL,       /* lens, mounts on 'L' throat, APS-C */
+  LIBRAW_MOUNT_LPS_L,          /* Leica/Panasonic/Sigma camera mount, takes L, SL and TL lenses */
+  LIBRAW_MOUNT_Mamiya67,       /* Mamiya RB67, RZ67 */
+  LIBRAW_MOUNT_Mamiya645,
+  LIBRAW_MOUNT_Minolta_A,
+  LIBRAW_MOUNT_Nikon_CX,       /* used in 'Nikon 1' series */
+  LIBRAW_MOUNT_Nikon_F,
+  LIBRAW_MOUNT_Nikon_Z,
+  LIBRAW_MOUNT_Pentax_645,
+  LIBRAW_MOUNT_Pentax_K,
+  LIBRAW_MOUNT_Pentax_Q,
+  LIBRAW_MOUNT_RicohModule,
+  LIBRAW_MOUNT_Rollei_bayonet, /* Rollei Hy-6: Leaf AFi, Sinar Hy6- models */
+  LIBRAW_MOUNT_Samsung_NX_M,
+  LIBRAW_MOUNT_Samsung_NX,
+  LIBRAW_MOUNT_Sigma_X3F,
+  LIBRAW_MOUNT_Sony_E,
+  LIBRAW_MOUNT_LF,
+  LIBRAW_MOUNT_DigitalBack,
+  LIBRAW_MOUNT_FixedLens,
+  LIBRAW_MOUNT_IL_UM,          /* Interchangeable lens, mount unknown */
+  LIBRAW_MOUNT_TheLastOne
+};
+
+enum LibRaw_camera_formats
+{
+  LIBRAW_FORMAT_Unknown = 0,
+  LIBRAW_FORMAT_APSC,
+  LIBRAW_FORMAT_FF,
+  LIBRAW_FORMAT_MF,
+  LIBRAW_FORMAT_APSH,
+  LIBRAW_FORMAT_1INCH,
+  LIBRAW_FORMAT_1div2p3INCH,  /* 1/2.3" */
+  LIBRAW_FORMAT_1div1p7INCH,  /* 1/1.7" */
+  LIBRAW_FORMAT_FT,           /* sensor size in FT & mFT cameras */
+  LIBRAW_FORMAT_CROP645,      /* 44x33mm */
+  LIBRAW_FORMAT_LeicaS,       /* 'MF' Leicas */
+  LIBRAW_FORMAT_645,
+  LIBRAW_FORMAT_66,
+  LIBRAW_FORMAT_69,
+  LIBRAW_FORMAT_LF,
+  LIBRAW_FORMAT_Leica_DMR,
+  LIBRAW_FORMAT_67,
+  LIBRAW_FORMAT_SigmaAPSC,    /* DP1, DP2, SD15, SD14, SD10, SD9 */
+  LIBRAW_FORMAT_SigmaMerrill, /* SD1,  'SD1 Merrill',  'DP1 Merrill',  'DP2 Merrill' */
+  LIBRAW_FORMAT_SigmaAPSH,    /* 'sd Quattro H' */
+  LIBRAW_FORMAT_3648,         /* DALSA FTF4052C (Mamiya ZD) */
+  LIBRAW_FORMAT_68,           /* Fujifilm GX680 */
+  LIBRAW_FORMAT_TheLastOne
+};
+
+enum LibRawImageAspects
+{
+  LIBRAW_IMAGE_ASPECT_UNKNOWN = 0,
+  LIBRAW_IMAGE_ASPECT_3to2 = 1,
+  LIBRAW_IMAGE_ASPECT_1to1 = 2,
+  LIBRAW_IMAGE_ASPECT_4to3 = 3,
+  LIBRAW_IMAGE_ASPECT_16to9 = 4,
+  LIBRAW_IMAGE_ASPECT_5to4 = 5,
+  LIBRAW_IMAGE_ASPECT_OTHER = 6
+};
+
+enum LibRaw_lens_focal_types
+{
+  LIBRAW_FT_UNDEFINED = 0,
+  LIBRAW_FT_PRIME_LENS = 1,
+  LIBRAW_FT_ZOOM_LENS = 2,
+  LIBRAW_FT_ZOOM_LENS_CONSTANT_APERTURE = 3,
+  LIBRAW_FT_ZOOM_LENS_VARIABLE_APERTURE = 4
+};
+
+enum LibRaw_Canon_RecordModes {
+  LIBRAW_Canon_RecordMode_UNDEFINED = 0,
+  LIBRAW_Canon_RecordMode_JPEG,
+  LIBRAW_Canon_RecordMode_CRW_THM,
+  LIBRAW_Canon_RecordMode_AVI_THM,
+  LIBRAW_Canon_RecordMode_TIF,
+  LIBRAW_Canon_RecordMode_TIF_JPEG,
+  LIBRAW_Canon_RecordMode_CR2,
+  LIBRAW_Canon_RecordMode_CR2_JPEG,
+  LIBRAW_Canon_RecordMode_UNKNOWN,
+  LIBRAW_Canon_RecordMode_MOV,
+  LIBRAW_Canon_RecordMode_MP4,
+  LIBRAW_Canon_RecordMode_CRM,
+  LIBRAW_Canon_RecordMode_CR3,
+  LIBRAW_Canon_RecordMode_CR3_JPEG,
+  LIBRAW_Canon_RecordMode_HEIF,
+  LIBRAW_Canon_RecordMode_CR3_HEIF,
+  LIBRAW_Canon_RecordMode_TheLastOne
+};
+
+enum LibRaw_sony_cameratypes
+{
+  LIBRAW_SONY_DSC = 1,
+  LIBRAW_SONY_DSLR = 2,
+  LIBRAW_SONY_NEX = 3,
+  LIBRAW_SONY_SLT = 4,
+  LIBRAW_SONY_ILCE = 5,
+  LIBRAW_SONY_ILCA = 6
+};
+
+enum LibRaw_KodakSensors
+{
+  LIBRAW_Kodak_UnknownSensor = 0,
+  LIBRAW_Kodak_M1 = 1,
+  LIBRAW_Kodak_M15 = 2,
+  LIBRAW_Kodak_M16 = 3,
+  LIBRAW_Kodak_M17 = 4,
+  LIBRAW_Kodak_M2 = 5,
+  LIBRAW_Kodak_M23 = 6,
+  LIBRAW_Kodak_M24 = 7,
+  LIBRAW_Kodak_M3 = 8,
+  LIBRAW_Kodak_M5 = 9,
+  LIBRAW_Kodak_M6 = 10,
+  LIBRAW_Kodak_C14 = 11,
+  LIBRAW_Kodak_X14 = 12,
+  LIBRAW_Kodak_M11 = 13
+};
+
+enum LibRaw_HasselbladFormatCodes {
+  LIBRAW_HF_Unknown = 0,
+  LIBRAW_HF_3FR,
+  LIBRAW_HF_FFF,
+  LIBRAW_HF_Imacon,
+  LIBRAW_HF_HasselbladDNG,
+  LIBRAW_HF_AdobeDNG,
+  LIBRAW_HF_AdobeDNG_fromPhocusDNG
+};
+
+enum LibRaw_processing_options
+{
+  LIBRAW_PROCESSING_SONYARW2_NONE = 0,
+  LIBRAW_PROCESSING_SONYARW2_BASEONLY = 1,
+  LIBRAW_PROCESSING_SONYARW2_DELTAONLY = 1 << 1,
+  LIBRAW_PROCESSING_SONYARW2_DELTAZEROBASE = 1 << 2,
+  LIBRAW_PROCESSING_SONYARW2_DELTATOVALUE = 1 << 3,
+  LIBRAW_PROCESSING_SONYARW2_ALLFLAGS =
+      LIBRAW_PROCESSING_SONYARW2_BASEONLY +
+      LIBRAW_PROCESSING_SONYARW2_DELTAONLY +
+      LIBRAW_PROCESSING_SONYARW2_DELTAZEROBASE +
+      LIBRAW_PROCESSING_SONYARW2_DELTATOVALUE,
+  LIBRAW_PROCESSING_DP2Q_INTERPOLATERG = 1 << 4,
+  LIBRAW_PROCESSING_DP2Q_INTERPOLATEAF = 1 << 5,
+  LIBRAW_PROCESSING_PENTAX_PS_ALLFRAMES = 1 << 6,
+  LIBRAW_PROCESSING_CONVERTFLOAT_TO_INT = 1 << 7,
+  LIBRAW_PROCESSING_SRAW_NO_RGB = 1 << 8,
+  LIBRAW_PROCESSING_SRAW_NO_INTERPOLATE = 1 << 9,
+  LIBRAW_PROCESSING_ARQ_SKIP_CHANNEL_SWAP = 1 << 10,
+  LIBRAW_PROCESSING_NO_ROTATE_FOR_KODAK_THUMBNAILS = 1 << 11,
+  LIBRAW_PROCESSING_USE_DNG_DEFAULT_CROP = 1 << 12,
+  LIBRAW_PROCESSING_USE_PPM16_THUMBS = 1 << 13,
+  LIBRAW_PROCESSING_SKIP_MAKERNOTES = 1 << 14,
+  LIBRAW_PROCESSING_DONT_CHECK_DNG_ILLUMINANT = 1 << 15,
+  LIBRAW_PROCESSING_DNGSDK_ZEROCOPY = 1 << 16,
+  LIBRAW_PROCESSING_ZEROFILTERS_FOR_MONOCHROMETIFFS = 1 << 17,
+  LIBRAW_PROCESSING_DNG_ADD_ENHANCED = 1 << 18,
+  LIBRAW_PROCESSING_DNG_ADD_PREVIEWS = 1 << 19,
+  LIBRAW_PROCESSING_DNG_PREFER_LARGEST_IMAGE = 1 << 20,
+  LIBRAW_PROCESSING_DNG_STAGE2 = 1 << 21,
+  LIBRAW_PROCESSING_DNG_STAGE3 = 1 << 22,
+  LIBRAW_PROCESSING_DNG_ALLOWSIZECHANGE = 1 << 23,
+  LIBRAW_PROCESSING_DNG_DISABLEWBADJUST = 1 << 24,
+  LIBRAW_PROCESSING_PROVIDE_NONSTANDARD_WB = 1 << 25,
+  LIBRAW_PROCESSING_CAMERAWB_FALLBACK_TO_DAYLIGHT = 1 << 26
+};
+
+enum LibRaw_decoder_flags
+{
+  LIBRAW_DECODER_HASCURVE = 1 << 4,
+  LIBRAW_DECODER_SONYARW2 = 1 << 5,
+  LIBRAW_DECODER_TRYRAWSPEED = 1 << 6,
+  LIBRAW_DECODER_OWNALLOC = 1 << 7,
+  LIBRAW_DECODER_FIXEDMAXC = 1 << 8,
+  LIBRAW_DECODER_ADOBECOPYPIXEL = 1 << 9,
+  LIBRAW_DECODER_LEGACY_WITH_MARGINS = 1 << 10,
+  LIBRAW_DECODER_3CHANNEL = 1 << 11,
+  LIBRAW_DECODER_SINAR4SHOT = 1 << 11,
+  LIBRAW_DECODER_FLATDATA = 1 << 12,
+  LIBRAW_DECODER_FLAT_BG2_SWAPPED = 1<<13,
+  LIBRAW_DECODER_NOTSET = 1 << 15
+};
+
+#define LIBRAW_XTRANS 9
+
+enum LibRaw_constructor_flags
+{
+  LIBRAW_OPTIONS_NONE = 0,
+  LIBRAW_OPIONS_NO_MEMERR_CALLBACK = 1,
+  LIBRAW_OPIONS_NO_DATAERR_CALLBACK = 1 << 1
+};
+
+enum LibRaw_warnings
+{
+  LIBRAW_WARN_NONE = 0,
+  LIBRAW_WARN_BAD_CAMERA_WB = 1 << 2,
+  LIBRAW_WARN_NO_METADATA = 1 << 3,
+  LIBRAW_WARN_NO_JPEGLIB = 1 << 4,
+  LIBRAW_WARN_NO_EMBEDDED_PROFILE = 1 << 5,
+  LIBRAW_WARN_NO_INPUT_PROFILE = 1 << 6,
+  LIBRAW_WARN_BAD_OUTPUT_PROFILE = 1 << 7,
+  LIBRAW_WARN_NO_BADPIXELMAP = 1 << 8,
+  LIBRAW_WARN_BAD_DARKFRAME_FILE = 1 << 9,
+  LIBRAW_WARN_BAD_DARKFRAME_DIM = 1 << 10,
+  LIBRAW_WARN_NO_JASPER = 1 << 11,
+  LIBRAW_WARN_RAWSPEED_PROBLEM = 1 << 12,
+  LIBRAW_WARN_RAWSPEED_UNSUPPORTED = 1 << 13,
+  LIBRAW_WARN_RAWSPEED_PROCESSED = 1 << 14,
+  LIBRAW_WARN_FALLBACK_TO_AHD = 1 << 15,
+  LIBRAW_WARN_PARSEFUJI_PROCESSED = 1 << 16,
+  LIBRAW_WARN_DNGSDK_PROCESSED = 1 << 17,
+  LIBRAW_WARN_DNG_IMAGES_REORDERED = 1 << 18,
+  LIBRAW_WARN_DNG_STAGE2_APPLIED = 1 << 19,
+  LIBRAW_WARN_DNG_STAGE3_APPLIED = 1 << 20,
+};
+
+enum LibRaw_exceptions
+{
+  LIBRAW_EXCEPTION_NONE = 0,
+  LIBRAW_EXCEPTION_ALLOC = 1,
+  LIBRAW_EXCEPTION_DECODE_RAW = 2,
+  LIBRAW_EXCEPTION_DECODE_JPEG = 3,
+  LIBRAW_EXCEPTION_IO_EOF = 4,
+  LIBRAW_EXCEPTION_IO_CORRUPT = 5,
+  LIBRAW_EXCEPTION_CANCELLED_BY_CALLBACK = 6,
+  LIBRAW_EXCEPTION_BAD_CROP = 7,
+  LIBRAW_EXCEPTION_IO_BADFILE = 8,
+  LIBRAW_EXCEPTION_DECODE_JPEG2000 = 9,
+  LIBRAW_EXCEPTION_TOOBIG = 10,
+  LIBRAW_EXCEPTION_MEMPOOL = 11
+};
 
 enum LibRaw_progress
 {
-    LIBRAW_PROGRESS_START               = 0,
-    LIBRAW_PROGRESS_OPEN                = 1,
-    LIBRAW_PROGRESS_IDENTIFY            = 1<<1,
-    LIBRAW_PROGRESS_SIZE_ADJUST         = 1<<2,
-    LIBRAW_PROGRESS_LOAD_RAW            = 1<<3,
-    LIBRAW_PROGRESS_REMOVE_ZEROES       = 1<<4,
-    LIBRAW_PROGRESS_BAD_PIXELS          = 1<<5,
-    LIBRAW_PROGRESS_DARK_FRAME          = 1<<6,
-    LIBRAW_PROGRESS_FOVEON_INTERPOLATE  = 1<<7,
-    LIBRAW_PROGRESS_SCALE_COLORS        = 1<<8,
-    LIBRAW_PROGRESS_PRE_INTERPOLATE     = 1<<9,
-    LIBRAW_PROGRESS_INTERPOLATE         = 1<<10,
-    LIBRAW_PROGRESS_MIX_GREEN           = 1<<11,
-    LIBRAW_PROGRESS_MEDIAN_FILTER       = 1<<12,
-    LIBRAW_PROGRESS_HIGHLIGHTS          = 1<<13,
-    LIBRAW_PROGRESS_FUJI_ROTATE         = 1<<14,
-    LIBRAW_PROGRESS_FLIP                = 1<<15,
-    LIBRAW_PROGRESS_APPLY_PROFILE       = 1<<16,
-    LIBRAW_PROGRESS_CONVERT_RGB         = 1<<17,
-    LIBRAW_PROGRESS_STRETCH             = 1<<18,
-// reserved
-    LIBRAW_PROGRESS_STAGE19             = 1<<19,
-    LIBRAW_PROGRESS_STAGE20             = 1<<20,
-    LIBRAW_PROGRESS_STAGE21             = 1<<21,
-    LIBRAW_PROGRESS_STAGE22             = 1<<22,
-    LIBRAW_PROGRESS_STAGE23             = 1<<23,
-    LIBRAW_PROGRESS_STAGE24             = 1<<24,
-    LIBRAW_PROGRESS_STAGE25             = 1<<25,
-    LIBRAW_PROGRESS_STAGE26             = 1<<26,
-    LIBRAW_PROGRESS_STAGE27             = 1<<27,
-
-    LIBRAW_PROGRESS_THUMB_LOAD          = 1<<28,
-    LIBRAW_PROGRESS_TRESERVED1          = 1<<29,
-    LIBRAW_PROGRESS_TRESERVED2          = 1<<30
+  LIBRAW_PROGRESS_START = 0,
+  LIBRAW_PROGRESS_OPEN = 1,
+  LIBRAW_PROGRESS_IDENTIFY = 1 << 1,
+  LIBRAW_PROGRESS_SIZE_ADJUST = 1 << 2,
+  LIBRAW_PROGRESS_LOAD_RAW = 1 << 3,
+  LIBRAW_PROGRESS_RAW2_IMAGE = 1 << 4,
+  LIBRAW_PROGRESS_REMOVE_ZEROES = 1 << 5,
+  LIBRAW_PROGRESS_BAD_PIXELS = 1 << 6,
+  LIBRAW_PROGRESS_DARK_FRAME = 1 << 7,
+  LIBRAW_PROGRESS_FOVEON_INTERPOLATE = 1 << 8,
+  LIBRAW_PROGRESS_SCALE_COLORS = 1 << 9,
+  LIBRAW_PROGRESS_PRE_INTERPOLATE = 1 << 10,
+  LIBRAW_PROGRESS_INTERPOLATE = 1 << 11,
+  LIBRAW_PROGRESS_MIX_GREEN = 1 << 12,
+  LIBRAW_PROGRESS_MEDIAN_FILTER = 1 << 13,
+  LIBRAW_PROGRESS_HIGHLIGHTS = 1 << 14,
+  LIBRAW_PROGRESS_FUJI_ROTATE = 1 << 15,
+  LIBRAW_PROGRESS_FLIP = 1 << 16,
+  LIBRAW_PROGRESS_APPLY_PROFILE = 1 << 17,
+  LIBRAW_PROGRESS_CONVERT_RGB = 1 << 18,
+  LIBRAW_PROGRESS_STRETCH = 1 << 19,
+  /* reserved */
+  LIBRAW_PROGRESS_STAGE20 = 1 << 20,
+  LIBRAW_PROGRESS_STAGE21 = 1 << 21,
+  LIBRAW_PROGRESS_STAGE22 = 1 << 22,
+  LIBRAW_PROGRESS_STAGE23 = 1 << 23,
+  LIBRAW_PROGRESS_STAGE24 = 1 << 24,
+  LIBRAW_PROGRESS_STAGE25 = 1 << 25,
+  LIBRAW_PROGRESS_STAGE26 = 1 << 26,
+  LIBRAW_PROGRESS_STAGE27 = 1 << 27,
+
+  LIBRAW_PROGRESS_THUMB_LOAD = 1 << 28,
+  LIBRAW_PROGRESS_TRESERVED1 = 1 << 29,
+  LIBRAW_PROGRESS_TRESERVED2 = 1 << 30
 };
 #define LIBRAW_PROGRESS_THUMB_MASK 0x0fffffff
 
 enum LibRaw_errors
 {
-    LIBRAW_SUCCESS = 0,
-    LIBRAW_UNSPECIFIED_ERROR=-1,
-    LIBRAW_FILE_UNSUPPORTED = -2,
-    LIBRAW_REQUEST_FOR_NONEXISTENT_IMAGE=-3,
-    LIBRAW_OUT_OF_ORDER_CALL=-4,
-    LIBRAW_NO_THUMBNAIL=-5,
-    LIBRAW_UNSUPPORTED_THUMBNAIL=-6,
-    LIBRAW_CANNOT_ADDMASK=-7,
-    LIBRAW_UNSUFFICIENT_MEMORY=-100007,
-    LIBRAW_DATA_ERROR=-100008,
-    LIBRAW_IO_ERROR=-100009,
-    LIBRAW_CANCELLED_BY_CALLBACK=-100010
+  LIBRAW_SUCCESS = 0,
+  LIBRAW_UNSPECIFIED_ERROR = -1,
+  LIBRAW_FILE_UNSUPPORTED = -2,
+  LIBRAW_REQUEST_FOR_NONEXISTENT_IMAGE = -3,
+  LIBRAW_OUT_OF_ORDER_CALL = -4,
+  LIBRAW_NO_THUMBNAIL = -5,
+  LIBRAW_UNSUPPORTED_THUMBNAIL = -6,
+  LIBRAW_INPUT_CLOSED = -7,
+  LIBRAW_NOT_IMPLEMENTED = -8,
+  LIBRAW_UNSUFFICIENT_MEMORY = -100007,
+  LIBRAW_DATA_ERROR = -100008,
+  LIBRAW_IO_ERROR = -100009,
+  LIBRAW_CANCELLED_BY_CALLBACK = -100010,
+  LIBRAW_BAD_CROP = -100011,
+  LIBRAW_TOO_BIG = -100012,
+  LIBRAW_MEMPOOL_OVERFLOW = -100013
 };
 
-#define LIBRAW_FATAL_ERROR(ec) ((ec)<-100000)
+#define LIBRAW_FATAL_ERROR(ec) ((ec) < -100000)
 
 enum LibRaw_thumbnail_formats
 {
-    LIBRAW_THUMBNAIL_UNKNOWN=0,
-    LIBRAW_THUMBNAIL_JPEG=1,
-    LIBRAW_THUMBNAIL_BITMAP=2,
-    LIBRAW_THUMBNAIL_LAYER=4,
-    LIBRAW_THUMBNAIL_ROLLEI=5
+  LIBRAW_THUMBNAIL_UNKNOWN = 0,
+  LIBRAW_THUMBNAIL_JPEG = 1,
+  LIBRAW_THUMBNAIL_BITMAP = 2,
+  LIBRAW_THUMBNAIL_BITMAP16 = 3,
+  LIBRAW_THUMBNAIL_LAYER = 4,
+  LIBRAW_THUMBNAIL_ROLLEI = 5
 };
 
 enum LibRaw_image_formats
 {
-    LIBRAW_IMAGE_BITMAP=1,
-    LIBRAW_IMAGE_JPEG=2
+  LIBRAW_IMAGE_JPEG = 1,
+  LIBRAW_IMAGE_BITMAP = 2
 };
 
 #endif
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/libraw/libraw_datastream.h libkdcraw/libkdcraw/libraw/libraw/libraw_datastream.h
--- libkdcraw-wrk/libkdcraw/libraw/libraw/libraw_datastream.h	2022-11-07 08:15:53.614821808 +0300
+++ libkdcraw/libkdcraw/libraw/libraw/libraw_datastream.h	2022-11-07 07:46:31.726795008 +0300
@@ -1,24 +1,19 @@
 /* -*- C -*-
  * File: libraw_datastream.h
- * Copyright 2008-2009 Alex Tutubalin <lexa@lexa.ru>
+ * Copyright 2008-2020 LibRaw LLC (info@libraw.org)
  * Created: Sun Jan 18 13:07:35 2009
  *
  * LibRaw Data stream interface
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
+
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
  */
 
 #ifndef __LIBRAW_DATASTREAM_H
@@ -31,273 +26,274 @@
 
 #ifndef __cplusplus
 
-struct LibRaw_abstract_datastream;
-
-#else // __cplusplus
+#else /* __cplusplus */
+#if defined _WIN32
+#ifndef LIBRAW_NO_WINSOCK2
+#include <winsock2.h>
+#endif
+#endif
+/* No unique_ptr on Apple ?? */
+#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) ||  \
+    (defined(_MSC_VER) && _MSVC_LANG >= 201103L)
+/* OK - use unique_ptr unless LIBRAW_USE_AUTOPTR defined externally*/
+#else
+/* Force to use auto_ptr */
+#ifndef LIBRAW_USE_AUTOPTR
+#define LIBRAW_USE_AUTOPTR
+#endif
+#endif
 
 #include "libraw_const.h"
+#include "libraw_types.h"
+#include <fstream>
+#include <memory>
+#include <vector>
 
-class LibRaw_buffer_datastream;
+#if defined(_WIN32) && (_MSC_VER) >= 1500
+#define WIN32SECURECALLS
+#endif
 
-class LibRaw_abstract_datastream
-{
-  public:
-    LibRaw_abstract_datastream(){substream=0;};
-    virtual             ~LibRaw_abstract_datastream(void){if(substream) delete substream;}
-    virtual int         valid(){return 0;}
-    // file input emulation
-    virtual int         read(void *,size_t, size_t ){ return -1;}
-    virtual int         seek(off_t o, int whence){return -1;}
-    virtual int         tell(){return -1;}
-    virtual int         get_char(){return -1;}
-    virtual char*       gets(char *, int){ return NULL;}
-    virtual int         scanf_one(const char *, void *){return -1;}
-    virtual int         eof(){return -1;}
-
-    virtual const char* fname(){ return NULL;};
-    virtual int         subfile_open(const char*){ return EINVAL;}
-    virtual void        subfile_close(){}
-    virtual int		tempbuffer_open(void*, size_t);
-    virtual void	tempbuffer_close()
-    {
-        if(substream) delete substream;
-        substream = NULL;
-    }
+#ifdef USE_DNGSDK
 
-  protected:
-    LibRaw_abstract_datastream *substream;
-};
+#if defined LIBRAW_WIN32_CALLS
+#define qWinOS 1
+#define qMacOS 0
+#elif defined(__APPLE__)
+#define qWinOS 0
+#define qMacOS 1
+#else
+/* define OS types for DNG here */
+#endif
+#define qDNGXMPDocOps 0
+#define qDNGUseLibJPEG 1
+#define qDNGXMPFiles 0
+#define qDNGExperimental 1
+#define qDNGThreadSafe 1
+#include "dng_stream.h"
+#endif /* DNGSDK */
+
+#define IOERROR()                                                              \
+  do                                                                           \
+  {                                                                            \
+    throw LIBRAW_EXCEPTION_IO_EOF;                                             \
+  } while (0)
 
+class LibRaw_buffer_datastream;
+class LibRaw_bit_buffer;
 
-class LibRaw_file_datastream : public LibRaw_abstract_datastream
+class DllDef LibRaw_abstract_datastream
 {
-  public:
-    LibRaw_file_datastream(const char *fname) 
-        { 
-            if(fname)
-                {filename = fname; f = fopen(fname,"rb");}
-            else 
-                {filename=0;f=0;}
-            sav=0;
-        }
-
-    virtual ~LibRaw_file_datastream() {if(f)fclose(f); if(sav)fclose(sav);}
-
-    virtual int valid() { return f?1:0;}
-
-#define CHK() do {if(!f) throw LIBRAW_EXCEPTION_IO_EOF;}while(0)
-    virtual int read(void * ptr,size_t size, size_t nmemb) 
-    { 
-        CHK(); 
-        return substream?substream->read(ptr,size,nmemb):int(fread(ptr,size,nmemb,f));
-    }
-    virtual int eof() 
-    { 
-        CHK(); 
-        return substream?substream->eof():feof(f);
-    }
-    virtual int seek(off_t o, int whence) 
-    { 
-        CHK(); 
-        return substream?substream->seek(o,whence):fseek(f,o,whence);
-    }
-    virtual int tell() 
-    { 
-        CHK(); 
-        return substream?substream->tell():ftell(f);
-    }
-    virtual int get_char() 
-    { 
-        CHK(); 
-        return substream?substream->get_char():fgetc(f);
-    }
-    virtual char* gets(char *str, int sz) 
-    { 
-        CHK(); 
-        return substream?substream->gets(str,sz):fgets(str,sz,f);
-    }
-    virtual int scanf_one(const char *fmt, void*val) 
-    { 
-        CHK(); 
-        return substream?substream->scanf_one(fmt,val):fscanf(f,fmt,val);
-    }
+public:
+  LibRaw_abstract_datastream() { };
+  virtual ~LibRaw_abstract_datastream(void) { }
+  virtual int valid() = 0;
+  virtual int read(void *, size_t, size_t) = 0;
+  virtual int seek(INT64, int) = 0;
+  virtual INT64 tell() = 0;
+  virtual INT64 size() = 0;
+  virtual int get_char() = 0;
+  virtual char *gets(char *, int) = 0;
+  virtual int scanf_one(const char *, void *) = 0;
+  virtual int eof() = 0;
+  virtual void *make_jas_stream() = 0;
+  virtual int jpeg_src(void *);
+  virtual void buffering_off() {}
+  /* reimplement in subclass to use parallel access in xtrans_load_raw() if
+   * OpenMP is not used */
+  virtual int lock() { return 1; } /* success */
+  virtual void unlock() {}
+  virtual const char *fname() { return NULL; };
+#ifdef LIBRAW_WIN32_UNICODEPATHS
+  virtual const wchar_t *wfname() { return NULL; };
+#endif
+};
 
-    virtual const char *fname() { return filename; }
+#ifdef LIBRAW_WIN32_DLLDEFS
+#ifdef LIBRAW_USE_AUTOPTR
+template class DllDef std::auto_ptr<std::streambuf>;
+#else
+template class DllDef std::unique_ptr<std::streambuf>;
+#endif
+#endif
 
-    // secondary 
-    virtual int subfile_open(const char *fn)
-    {
-        if(sav) return EBUSY;
-        sav = f;
-        f = fopen(fn,"rb");
-        if(!f)
-            {
-                f = sav;
-                sav = NULL;
-                return ENOENT;
-            }
-        else
-            return 0;
-    }
-    virtual void subfile_close()
-    {
-        if(!sav) return;
-        fclose(f);
-        f = sav;
-        sav = 0;
-    }
+class DllDef LibRaw_file_datastream : public LibRaw_abstract_datastream
+{
+protected:
+#ifdef LIBRAW_USE_AUTOPTR
+  std::auto_ptr<std::streambuf> f; /* will close() automatically through dtor */
+#else
+  std::unique_ptr<std::streambuf> f;
+#endif
+  std::string filename;
+  INT64 _fsize;
+#ifdef LIBRAW_WIN32_UNICODEPATHS
+  std::wstring wfilename;
+#endif
+  FILE *jas_file;
 
-  private:
-    FILE *f,*sav;
-    const char *filename;
+public:
+  virtual ~LibRaw_file_datastream();
+  LibRaw_file_datastream(const char *fname);
+#ifdef LIBRAW_WIN32_UNICODEPATHS
+  LibRaw_file_datastream(const wchar_t *fname);
+#endif
+  virtual void *make_jas_stream();
+  virtual int valid();
+  virtual int read(void *ptr, size_t size, size_t nmemb);
+  virtual int eof();
+  virtual int seek(INT64 o, int whence);
+  virtual INT64 tell();
+  virtual INT64 size() { return _fsize; }
+  virtual int get_char() {return f->sbumpc();}
+  virtual char *gets(char *str, int sz);
+  virtual int scanf_one(const char *fmt, void *val);
+  virtual const char *fname();
+#ifdef LIBRAW_WIN32_UNICODEPATHS
+  virtual const wchar_t *wfname();
+#endif
 };
-#undef CHK
 
-class LibRaw_buffer_datastream : public LibRaw_abstract_datastream
+class DllDef LibRaw_buffer_datastream : public LibRaw_abstract_datastream
 {
-  public:
-    LibRaw_buffer_datastream(void *buffer, size_t bsize)
-        {
-            buf = (unsigned char*)buffer; streampos = 0; streamsize = bsize;
-        }
-    virtual ~LibRaw_buffer_datastream(){}
-    virtual int valid() { return buf?1:0;}
-    virtual int read(void * ptr,size_t sz, size_t nmemb) 
-    { 
-        if(substream) return substream->read(ptr,sz,nmemb);
-        size_t to_read = sz*nmemb;
-        if(to_read > streamsize - streampos)
-            to_read = streamsize-streampos;
-        if(to_read<1) 
-            return 0;
-        memmove(ptr,buf+streampos,to_read);
-        streampos+=to_read;
-        return int((to_read+sz-1)/sz);
-    }
+public:
+  LibRaw_buffer_datastream(void *buffer, size_t bsize);
+  virtual ~LibRaw_buffer_datastream();
+  virtual int valid();
+  virtual void *make_jas_stream();
+  virtual int jpeg_src(void *jpegdata);
+  virtual int read(void *ptr, size_t sz, size_t nmemb);
+  virtual int eof();
+  virtual int seek(INT64 o, int whence);
+  virtual INT64 tell();
+  virtual INT64 size() { return streamsize; }
+  virtual char *gets(char *s, int sz);
+  virtual int scanf_one(const char *fmt, void *val);
+  virtual int get_char()
+  {
+    if (streampos >= streamsize)   return -1;
+    return buf[streampos++];
+  }
+
+private:
+  unsigned char *buf;
+  size_t streampos, streamsize;
+};
 
-    virtual int eof() 
-    { 
-        if(substream) return substream->eof();
-        return streampos >= streamsize;
-    }
+class DllDef LibRaw_bigfile_datastream : public LibRaw_abstract_datastream
+{
+public:
+  LibRaw_bigfile_datastream(const char *fname);
+#ifdef LIBRAW_WIN32_UNICODEPATHS
+  LibRaw_bigfile_datastream(const wchar_t *fname);
+#endif
+  virtual ~LibRaw_bigfile_datastream();
+  virtual int valid();
+  virtual void *make_jas_stream();
+
+  virtual int read(void *ptr, size_t size, size_t nmemb);
+  virtual int eof();
+  virtual int seek(INT64 o, int whence);
+  virtual INT64 tell();
+  virtual INT64 size() { return _fsize; }
+  virtual char *gets(char *str, int sz);
+  virtual int scanf_one(const char *fmt, void *val);
+  virtual const char *fname();
+#ifdef LIBRAW_WIN32_UNICODEPATHS
+  virtual const wchar_t *wfname();
+#endif
+  virtual int get_char()
+  {
+#ifndef LIBRAW_WIN32_CALLS
+    return getc_unlocked(f);
+#else
+    return fgetc(f);
+#endif
+  }
 
-    virtual int seek(off_t o, int whence) 
-    { 
-        if(substream) return substream->seek(o,whence);
-        switch(whence)
-            {
-            case SEEK_SET:
-                if(o<0)
-                    streampos = 0;
-                else if (size_t(o) > streamsize)
-                    streampos = streamsize;
-                else
-                    streampos = size_t(o);
-                return 0;
-            case SEEK_CUR:
-                if(o<0)
-                    {
-                        if(size_t(-o) >= streampos)
-                            streampos = 0;
-                        else
-                            streampos += o;
-                    }
-                else if (o>0)
-                    {
-                        if(o+streampos> streamsize)
-                            streampos = streamsize;
-                        else
-                            streampos += o;
-                    }
-                return 0;
-            case SEEK_END:
-                if(o>0)
-                    streampos = streamsize;
-                else if ( size_t(-o) > streamsize)
-                    streampos = 0;
-                else
-                    streampos = streamsize+o;
-                return 0;
-            default:
-                return 0;
-            }
-    }
-    
-    virtual int tell() 
-    { 
-        if(substream) return substream->tell();
-        return int(streampos);
-    }
+protected:
+  FILE *f;
+  std::string filename;
+  INT64 _fsize;
+#ifdef LIBRAW_WIN32_UNICODEPATHS
+  std::wstring wfilename;
+#endif
+};
 
-    virtual int get_char() 
-    { 
-        if(substream) return substream->get_char();
-        if(streampos>=streamsize)
-            return -1;
-        return buf[streampos++];
-    }
-    virtual char* gets(char *s, int sz) 
-    { 
-        if (substream) return substream->gets(s,sz);
-        unsigned char *psrc,*pdest,*str;
-        str = (unsigned char *)s;
-        psrc = buf+streampos;
-        pdest = str;
-        while ( (size_t(psrc - buf) < streamsize)
-               &&
-                ((pdest-str)<sz)
-		)
-	  {
-                *pdest = *psrc;
-                if(*psrc == '\n')
-                    break;
-                psrc++;
-                pdest++;
-            }
-        if(size_t(psrc-buf) < streamsize)
-            psrc++;
-        if((pdest-str)<sz)
-            *(++pdest)=0;
-        streampos = psrc - buf;
-        return s;
-    }
-    virtual int scanf_one(const char *fmt, void* val) 
-    { 
-        if(substream) return substream->scanf_one(fmt,val);
-        int scanf_res;
-        if(streampos>streamsize) return 0;
-        scanf_res = sscanf((char*)(buf+streampos),fmt,val);
-        if(scanf_res>0)
-            {
-                int xcnt=0;
-                while(streampos<streamsize)
-                    {
-                        streampos++;
-                        xcnt++;
-                        if(buf[streampos] == 0
-                           || buf[streampos]==' '
-                           || buf[streampos]=='\t'
-                           || buf[streampos]=='\n'
-                           || xcnt>24)
-                            break;
-                    }
-            }
-        return scanf_res;
-    }
-  private:
-    unsigned char *buf;
-    size_t   streampos,streamsize;
+#ifdef LIBRAW_WIN32_CALLS
+class DllDef LibRaw_windows_datastream : public LibRaw_buffer_datastream
+{
+public:
+  /* ctor: high level constructor opens a file by name */
+  LibRaw_windows_datastream(const TCHAR *sFile);
+  /* ctor: construct with a file handle - caller is responsible for closing the
+   * file handle */
+  LibRaw_windows_datastream(HANDLE hFile);
+  /* dtor: unmap and close the mapping handle */
+  virtual ~LibRaw_windows_datastream();
+  virtual INT64 size() { return cbView_; }
+
+protected:
+  void Open(HANDLE hFile);
+  inline void reconstruct_base()
+  {
+    /* this subterfuge is to overcome the private-ness of
+     * LibRaw_buffer_datastream */
+    (LibRaw_buffer_datastream &)*this =
+        LibRaw_buffer_datastream(pView_, (size_t)cbView_);
+  }
+
+  HANDLE hMap_;    /* handle of the file mapping */
+  void *pView_;    /* pointer to the mapped memory */
+  __int64 cbView_; /* size of the mapping in bytes */
 };
 
-inline int LibRaw_abstract_datastream::tempbuffer_open(void  *buf, size_t size)
+#endif
+
+#ifdef USE_DNGSDK
+
+class libraw_dng_stream : public dng_stream
 {
-    if(substream) return EBUSY;
-    substream = new LibRaw_buffer_datastream(buf,size);
-    return substream?0:EINVAL;
-}
+public:
+  libraw_dng_stream(LibRaw_abstract_datastream *p)
+      : dng_stream((dng_abort_sniffer *)NULL, kBigBufferSize, 0),
+        parent_stream(p)
+  {
+    if (parent_stream)
+    {
+        parent_stream->buffering_off();
+      off = parent_stream->tell();
+      parent_stream->seek(0UL, SEEK_SET); /* seek to start */
+    }
+  }
+  ~libraw_dng_stream()
+  {
+    if (parent_stream)
+      parent_stream->seek(off, SEEK_SET);
+  }
+  virtual uint64 DoGetLength()
+  {
+    if (parent_stream)
+      return parent_stream->size();
+    return 0;
+  }
+  virtual void DoRead(void *data, uint32 count, uint64 offset)
+  {
+    if (parent_stream)
+    {
+      parent_stream->seek(offset, SEEK_SET);
+      parent_stream->read(data, 1, count);
+    }
+  }
 
+private:
+  libraw_dng_stream(const libraw_dng_stream &stream);
+  libraw_dng_stream &operator=(const libraw_dng_stream &stream);
+  LibRaw_abstract_datastream *parent_stream;
+  INT64 off;
+};
 
 #endif
 
-#endif
+#endif /* cplusplus */
 
+#endif
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/libraw/libraw_internal.h libkdcraw/libkdcraw/libraw/libraw/libraw_internal.h
--- libkdcraw-wrk/libkdcraw/libraw/libraw/libraw_internal.h	2022-11-07 08:15:53.614821808 +0300
+++ libkdcraw/libkdcraw/libraw/libraw/libraw_internal.h	2022-11-07 07:46:31.730795008 +0300
@@ -1,240 +1,319 @@
-/*
+/* -*- C++ -*-
  * File: libraw_internal.h
- * Copyright 2008-2009 Alex Tutubalin <lexa@lexa.ru>
+ * Copyright 2008-2020 LibRaw LLC (info@libraw.org)
  * Created: Sat Mar  8 , 2008
  *
  * LibRaw internal data structures (not visible outside)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- */
 
-#ifndef _LIBRAW_INTERNAL_TYPES_H
-#define _LIBRAW_INTERNAL_TYPES_H
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of two licenses as you choose:
 
-#include <stdio.h>
-#ifdef __cplusplus
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
 
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
 
-#ifndef CLASS
-#define CLASS LibRaw::
-#endif
+ */
 
-#else
-// C build
-#ifndef CLASS
-#define CLASS
-#endif
-#endif
+#ifndef _LIBRAW_INTERNAL_TYPES_H
+#define _LIBRAW_INTERNAL_TYPES_H
 
+#include <stdio.h>
 
 #ifdef __cplusplus
 
 #include "libraw_datastream.h"
+#include "libraw_types.h"
 
 class LibRaw_TLS
 {
 public:
-    struct 
-    {
-         unsigned bitbuf;
-         int vbits, reset;
-    }getbits;
-    struct 
-    {
-         UINT64 bitbuf;
-         int vbits;
-
-    }ph1_bits;
-    int make_decoder_leaf;
-    struct
-    {
-        struct decode *dstart[18], *dindex;
-        const int *s;
-    }radc_token;
-    struct
-    {
-         unsigned pad[128], p;
-    }sony_decrypt;
-    unsigned foveon_decoder_huff[1024];
-    uchar jpeg_buffer[4096];
-    struct
-    {
-        uchar buf[0x4000];
-        int vbits, padding;
-    }pana_bits;
-
-    // init - should use in constructor/recycle
-    void init() 
-        { 
-            getbits.bitbuf = 0; getbits.vbits = getbits.reset = 0;
-            ph1_bits.bitbuf = 0; ph1_bits.vbits = 0;
-            pana_bits.vbits = 0;
-        }
+  struct
+  {
+    unsigned bitbuf;
+    int vbits, reset;
+  } getbits;
+  struct
+  {
+    UINT64 bitbuf;
+    int vbits;
+
+  } ph1_bits;
+  struct
+  {
+    unsigned pad[128], p;
+  } sony_decrypt;
+  struct
+  {
+    uchar buf[0x4002];
+    int vpos, padding;
+  } pana_data;
+  uchar jpeg_buffer[4096];
+  struct
+  {
+    float cbrt[0x10000], xyz_cam[3][4];
+  } ahd_data;
+  void init()
+  {
+    getbits.bitbuf = 0;
+    getbits.vbits = getbits.reset = 0;
+    ph1_bits.bitbuf = 0;
+    ph1_bits.vbits = 0;
+    pana_data.vpos = 0;
+    ahd_data.cbrt[0] = -2.0f;
+  }
 };
 
-
 class LibRaw_constants
 {
-  public:
-    static const float d65_white[3];
-    static const double xyz_rgb[3][3];
+public:
+  static const float d65_white[3];
+  static const double xyz_rgb[3][3];
+  static const double xyzd50_srgb[3][3];
+  static const double rgb_rgb[3][3];
+  static const double adobe_rgb[3][3];
+  static const double wide_rgb[3][3];
+  static const double prophoto_rgb[3][3];
+  static const double aces_rgb[3][3];
 };
-#endif // __cplusplus
-
-#ifdef WIN32
-typedef long off_t;
-#endif
+#endif /* __cplusplus */
 
 typedef struct
 {
 #ifndef __cplusplus
-    struct
+  struct
 #endif
-    LibRaw_abstract_datastream *input;
-    int         input_internal;
-//    char        *ifname;
-    char        *meta_data;
-    off_t       profile_offset;
-    off_t       toffset;
+      LibRaw_abstract_datastream *input;
+  FILE *output;
+  int input_internal;
+  char *meta_data;
+  INT64 profile_offset;
+  INT64 toffset;
+  unsigned pana_black[4];
 
 } internal_data_t;
 
-typedef struct
-{
-    unsigned    mix_green;
-    unsigned    raw_color;
-    unsigned    use_gamma;
-    unsigned    zero_is_bad;
-    ushort      shrink;
-    ushort      fuji_width;
-    ushort      fwidth,fheight;
-} internal_output_params_t;
-
 #define LIBRAW_HISTOGRAM_SIZE 0x2000
 typedef struct
 {
-    int         (*histogram)[LIBRAW_HISTOGRAM_SIZE];
-    unsigned    *oprof;
+  int (*histogram)[LIBRAW_HISTOGRAM_SIZE];
+  unsigned *oprof;
 } output_data_t;
 
 typedef struct
 {
-    unsigned olympus_exif_cfa;
-    unsigned     unique_id;
-    unsigned tiff_nifds;
-    int  tiff_flip;
-}identify_data_t;
+  unsigned olympus_exif_cfa;
+  unsigned long long unique_id;
+  unsigned long long OlyID;
+  unsigned tiff_nifds;
+  int tiff_flip;
+  int metadata_blocks;
+} identify_data_t;
 
+// contents of tag CMP1 for relevant track in CR3 file
 typedef struct
 {
-    short       order; // II* / MM* - file word byte order
-    ushort      sraw_mul[4],cr2_slice[3];
-    unsigned    kodak_cbpp;
-    off_t       strip_offset, data_offset;
-    off_t       meta_offset;
-    unsigned     meta_length;
-    unsigned    thumb_misc;
-    unsigned    fuji_layout;
-    unsigned    tiff_samples;
-    unsigned    tiff_bps;
-    unsigned    tiff_compress;
-    unsigned    zero_after_ff;
-    unsigned    tile_width, tile_length,load_flags;
-    unsigned    data_error;
-}unpacker_data_t;
-
+  int32_t version;
+  int32_t f_width;
+  int32_t f_height;
+  int32_t tileWidth;
+  int32_t tileHeight;
+  int32_t nBits;
+  int32_t nPlanes;
+  int32_t cfaLayout;
+  int32_t encType;
+  int32_t imageLevels;
+  int32_t hasTileCols;
+  int32_t hasTileRows;
+  int32_t mdatHdrSize;
+  // Not from header, but from datastream
+  uint32_t MediaSize;
+  INT64 MediaOffset;
+  uint32_t MediaType; /* 1 -> /C/RAW, 2-> JPEG */
+} crx_data_header_t;
 
+typedef struct
+{
+  short order;
+  ushort sraw_mul[4], cr2_slice[3];
+  unsigned kodak_cbpp;
+  INT64 strip_offset, data_offset;
+  INT64 meta_offset;
+  unsigned data_size;
+  unsigned meta_length;
+  unsigned thumb_misc;
+  unsigned fuji_layout;
+  unsigned tiff_samples;
+  unsigned tiff_bps;
+  unsigned tiff_compress;
+  unsigned tiff_sampleformat;
+  unsigned zero_after_ff;
+  unsigned tile_width, tile_length, load_flags;
+  unsigned data_error;
+  int hasselblad_parser_flag;
+  long long posRAFData;
+  unsigned lenRAFData;
+  int fuji_total_lines, fuji_total_blocks, fuji_block_width, fuji_bits,
+      fuji_raw_type;
+  int pana_encoding, pana_bpp;
+  crx_data_header_t crx_header[LIBRAW_CRXTRACKS_MAXCOUNT];
+  int crx_track_selected;
+  short CR3_CTMDtag;
+  short CR3_Version;
+  int CM_found;
+  unsigned is_NikonTransfer;
+  unsigned is_Sony;
+  unsigned is_pana_raw;
+  unsigned
+      is_4K_RAFdata; /* =1 for Fuji X-A3, X-A5, X-A7, X-A10, X-A20, X-T100, XF10 */
+  unsigned is_PentaxRicohMakernotes; /* =1 for Ricoh software by Pentax, Camera DNG */
+
+  unsigned dng_frames[LIBRAW_IFD_MAXCOUNT*2]; /* bits: 0-7: shot_select, 8-15: IFD#, 16-31: low 16 bit of newsubfile type */
+  unsigned short raw_stride;
+} unpacker_data_t;
 
 typedef struct
 {
-    internal_data_t internal_data;
-    internal_output_params_t internal_output_params;    
-    output_data_t output_data;
-    identify_data_t identify_data;
-    unpacker_data_t unpacker_data;
-//    callbacks_t callbacks;
+  internal_data_t internal_data;
+  libraw_internal_output_params_t internal_output_params;
+  output_data_t output_data;
+  identify_data_t identify_data;
+  unpacker_data_t unpacker_data;
 } libraw_internal_data_t;
 
-
-struct decode 
+struct decode
 {
-    struct decode *branch[2];
-    int leaf;
+  struct decode *branch[2];
+  int leaf;
 };
 
-struct tiff_ifd_t 
+struct tiff_ifd_t
 {
-    int t_width, t_height, bps, comp, phint, offset, t_flip, samples, bytes;
+  int t_width, t_height, bps, comp, phint, offset, t_flip, samples, bytes, extrasamples;
+  int t_tile_width, t_tile_length, sample_format, predictor;
+  int rows_per_strip;
+  int *strip_offsets, strip_offsets_count;
+  int *strip_byte_counts, strip_byte_counts_count;
+  unsigned t_filters;
+  int t_vwidth, t_vheight, t_lm,t_tm;
+  int t_fuji_width;
+  float t_shutter;
+  /* Per-IFD DNG fields */
+  INT64 opcode2_offset;
+  INT64 lineartable_offset;
+  int lineartable_len;
+  libraw_dng_color_t dng_color[2];
+  libraw_dng_levels_t dng_levels;
+  int newsubfiletype;
 };
 
-
-struct jhead {
-  int bits, high, wide, clrs, sraw, psv, restart, vpred[6];
-  struct decode *huff[6];
-  ushort *row;
+struct jhead
+{
+  int algo, bits, high, wide, clrs, sraw, psv, restart, vpred[6];
+  ushort quant[64], idct[64], *huff[20], *free[20], *row;
 };
-struct tiff_tag {
+
+struct libraw_tiff_tag
+{
   ushort tag, type;
   int count;
-  union { char c[4]; short s[2]; int i; } val;
+  union {
+    char c[4];
+    short s[2];
+    int i;
+  } val;
 };
 
-struct tiff_hdr {
+struct tiff_hdr
+{
   ushort t_order, magic;
   int ifd;
   ushort pad, ntag;
-  struct tiff_tag tag[23];
+  struct libraw_tiff_tag tag[23];
   int nextifd;
   ushort pad2, nexif;
-  struct tiff_tag exif[4];
+  struct libraw_tiff_tag exif[4];
   ushort pad3, ngps;
-  struct tiff_tag gpst[10];
+  struct libraw_tiff_tag gpst[10];
   short bps[4];
   int rat[10];
   unsigned gps[26];
   char t_desc[512], t_make[64], t_model[64], soft[32], date[20], t_artist[64];
 };
 
-
-
 #ifdef DEBUG_STAGE_CHECKS
-#define CHECK_ORDER_HIGH(expected_stage) \
-    do { if((imgdata.progress_flags & LIBRAW_PROGRESS_THUMB_MASK) >= expected_stage) {fprintf(stderr,"CHECK_HIGH: check %d >=  %d\n",imgdata.progress_flags & LIBRAW_PROGRESS_THUMB_MASK,expected_stage);return LIBRAW_OUT_OF_ORDER_CALL;} } while(0)
-
-#define CHECK_ORDER_LOW(expected_stage) \
-    do { printf("Checking LOW %d/%d : %d\n",imgdata.progress_flags,expected_stage,imgdata.progress_flags<expected_stage); if( (imgdata.progress_flags&LIBRAW_PROGRESS_THUMB_MASK) < expected_stage ) { printf("failed!\n"); return LIBRAW_OUT_OF_ORDER_CALL;} } while(0)
-#define CHECK_ORDER_BIT(expected_stage) \
-    do { if(imgdata.progress_flags & expected_stage) return LIBRAW_OUT_OF_ORDER_CALL; } while(0)
-
-#define SET_PROC_FLAG(stage) do {imgdata.progress_flags |= stage; fprintf(stderr,"SET_FLAG: %d\n",stage); } while (0)
+#define CHECK_ORDER_HIGH(expected_stage)                                       \
+  do                                                                           \
+  {                                                                            \
+    if ((imgdata.progress_flags & LIBRAW_PROGRESS_THUMB_MASK) >=               \
+        expected_stage)                                                        \
+    {                                                                          \
+      fprintf(stderr, "CHECK_HIGH: check %d >=  %d\n",                         \
+              imgdata.progress_flags &LIBRAW_PROGRESS_THUMB_MASK,              \
+              expected_stage);                                                 \
+      return LIBRAW_OUT_OF_ORDER_CALL;                                         \
+    }                                                                          \
+  } while (0)
+
+#define CHECK_ORDER_LOW(expected_stage)                                        \
+  do                                                                           \
+  {                                                                            \
+    printf("Checking LOW %d/%d : %d\n", imgdata.progress_flags,                \
+           expected_stage, imgdata.progress_flags < expected_stage);           \
+    if ((imgdata.progress_flags & LIBRAW_PROGRESS_THUMB_MASK) <                \
+        expected_stage)                                                        \
+    {                                                                          \
+      printf("failed!\n");                                                     \
+      return LIBRAW_OUT_OF_ORDER_CALL;                                         \
+    }                                                                          \
+  } while (0)
+#define CHECK_ORDER_BIT(expected_stage)                                        \
+  do                                                                           \
+  {                                                                            \
+    if (imgdata.progress_flags & expected_stage)                               \
+      return LIBRAW_OUT_OF_ORDER_CALL;                                         \
+  } while (0)
+
+#define SET_PROC_FLAG(stage)                                                   \
+  do                                                                           \
+  {                                                                            \
+    imgdata.progress_flags |= stage;                                           \
+    fprintf(stderr, "SET_FLAG: %d\n", stage);                                  \
+  } while (0)
 
 #else
 
-#define CHECK_ORDER_HIGH(expected_stage) \
-    do { if((imgdata.progress_flags & LIBRAW_PROGRESS_THUMB_MASK) >= expected_stage) \
-            {return LIBRAW_OUT_OF_ORDER_CALL;} } while(0)
-
-#define CHECK_ORDER_LOW(expected_stage) \
-    do { if((imgdata.progress_flags&LIBRAW_PROGRESS_THUMB_MASK) < expected_stage) \
-            return LIBRAW_OUT_OF_ORDER_CALL; } while(0)
-
-#define CHECK_ORDER_BIT(expected_stage) \
-    do { if(imgdata.progress_flags & expected_stage) return LIBRAW_OUT_OF_ORDER_CALL; } while(0)
-
-#define SET_PROC_FLAG(stage) do {imgdata.progress_flags |= stage;} while (0)
+#define CHECK_ORDER_HIGH(expected_stage)                                       \
+  do                                                                           \
+  {                                                                            \
+    if ((imgdata.progress_flags & LIBRAW_PROGRESS_THUMB_MASK) >=               \
+        expected_stage)                                                        \
+    {                                                                          \
+      return LIBRAW_OUT_OF_ORDER_CALL;                                         \
+    }                                                                          \
+  } while (0)
+
+#define CHECK_ORDER_LOW(expected_stage)                                        \
+  do                                                                           \
+  {                                                                            \
+    if ((imgdata.progress_flags & LIBRAW_PROGRESS_THUMB_MASK) <                \
+        expected_stage)                                                        \
+      return LIBRAW_OUT_OF_ORDER_CALL;                                         \
+  } while (0)
+
+#define CHECK_ORDER_BIT(expected_stage)                                        \
+  do                                                                           \
+  {                                                                            \
+    if (imgdata.progress_flags & expected_stage)                               \
+      return LIBRAW_OUT_OF_ORDER_CALL;                                         \
+  } while (0)
+
+#define SET_PROC_FLAG(stage)                                                   \
+  do                                                                           \
+  {                                                                            \
+    imgdata.progress_flags |= stage;                                           \
+  } while (0)
 
 #endif
 
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/libraw/libraw_types.h libkdcraw/libkdcraw/libraw/libraw/libraw_types.h
--- libkdcraw-wrk/libkdcraw/libraw/libraw/libraw_types.h	2022-11-07 08:15:53.614821808 +0300
+++ libkdcraw/libkdcraw/libraw/libraw/libraw_types.h	2022-11-07 07:46:31.730795008 +0300
@@ -1,285 +1,1032 @@
-/*
+/* -*- C++ -*-
  * File: libraw_types.h
- * Copyright 2008-2009 Alex Tutubalin <lexa@lexa.ru>
+ * Copyright 2008-2020 LibRaw LLC (info@libraw.org)
  * Created: Sat Mar  8 , 2008
  *
  * LibRaw C data structures
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
+
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
  */
 
 #ifndef _LIBRAW_TYPES_H
 #define _LIBRAW_TYPES_H
 
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#ifndef WIN32
+#include <sys/types.h>
+#ifndef _WIN32
 #include <sys/time.h>
 #endif
+
 #include <stdio.h>
-#ifdef _OPENMP
-#include <omp.h>
+
+#if defined(_WIN32)
+#if defined(_MSC_VER) && (_MSC_VER <= 1500)
+typedef signed __int8 int8_t;
+typedef unsigned __int8 uint8_t;
+typedef signed __int16 int16_t;
+typedef unsigned __int16 uint16_t;
+typedef signed __int32 int32_t;
+typedef unsigned __int32 uint32_t;
+typedef signed __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+#else /* WIN32, but not old MSVC */
+#include <stdint.h>
+#endif /* _WIN32 */
+#include <sys/types.h>
+#else
+#include <inttypes.h>
 #endif
 
+#if defined(_OPENMP)
+
+#if defined(_WIN32)
+#if defined(_MSC_VER) &&                                                       \
+    (_MSC_VER >= 1600 || (_MSC_VER == 1500 && _MSC_FULL_VER >= 150030729))
+/* VS2010+ : OpenMP works OK, VS2008: have tested by cgilles */
+#define LIBRAW_USE_OPENMP
+#elif defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 910)
+/*  Have not tested on 9.x and 10.x, but Intel documentation claims OpenMP 2.5
+ * support in 9.1 */
+#define LIBRAW_USE_OPENMP
+#else
+#undef LIBRAW_USE_OPENMP
+#endif
+/* Not Win32 */
+#elif (defined(__APPLE__) || defined(__MACOSX__)) && defined(_REENTRANT)
+/* Latest XCode works with OpenMP, need to recheck here */
+#undef LIBRAW_USE_OPENMP
+#else
+#define LIBRAW_USE_OPENMP
+#endif
+#endif
+
+#ifdef LIBRAW_USE_OPENMP
+#include <omp.h>
+#endif
 
 #ifdef __cplusplus
-extern "C" {
+extern "C"
+{
 #endif
 
-#ifndef USE_LCMS
+#if defined(USE_LCMS)
+#include <lcms.h>
+#elif defined(USE_LCMS2)
+#include <lcms2.h>
+#else
 #define NO_LCMS
 #endif
 
 #include "libraw_const.h"
 #include "libraw_version.h"
 
+#ifdef _WIN32
+  typedef __int64 INT64;
+  typedef unsigned __int64 UINT64;
+#else
 typedef long long INT64;
 typedef unsigned long long UINT64;
-//#define ushort UshORt
-typedef unsigned char uchar;
-typedef unsigned short ushort;
+#endif
+
+  typedef unsigned char uchar;
+  typedef unsigned short ushort;
 
-#ifdef WIN32
+#ifdef LIBRAW_WIN32_DLLDEFS
 #ifdef LIBRAW_NODLL
-# define DllDef
+#define DllDef
 #else
-# ifdef LIBRAW_BUILDLIB
-#    define DllDef   __declspec( dllexport )
-# else
-#    define DllDef   __declspec( dllimport )
-# endif
+#ifdef LIBRAW_BUILDLIB
+#define DllDef __declspec(dllexport)
+#else
+#define DllDef __declspec(dllimport)
+#endif
 #endif
-// NO Win32
 #else
-#  define DllDef
+#define DllDef
 #endif
 
+  typedef struct
+  {
+    const char *decoder_name;
+    unsigned decoder_flags;
+  } libraw_decoder_info_t;
+
+  typedef struct
+  {
+    unsigned mix_green;
+    unsigned raw_color;
+    unsigned zero_is_bad;
+    ushort shrink;
+    ushort fuji_width;
+  } libraw_internal_output_params_t;
+
+  typedef void (*memory_callback)(void *data, const char *file,
+                                  const char *where);
+  typedef void (*exif_parser_callback)(void *context, int tag, int type,
+                                       int len, unsigned int ord, void *ifp,
+                                       INT64 base);
+
+  DllDef void default_memory_callback(void *data, const char *file,
+                                      const char *where);
+
+  typedef void (*data_callback)(void *data, const char *file, const int offset);
+
+  DllDef void default_data_callback(void *data, const char *file,
+                                    const int offset);
+
+  typedef int (*progress_callback)(void *data, enum LibRaw_progress stage,
+                                   int iteration, int expected);
+  typedef int (*pre_identify_callback)(void *ctx);
+  typedef void (*post_identify_callback)(void *ctx);
+  typedef void (*process_step_callback)(void *ctx);
 
-//class LibRaw;
-
-typedef void (* memory_callback)(void * data, const char *file, const char *where);
-
-DllDef void default_memory_callback(void *data,const char *file, const char *where);
-
-typedef void (*data_callback)(void *data,const char *file, const int offset);
-
-DllDef void default_data_callback(void *data,const char *file, const int offset);
-
-typedef int (* progress_callback) (void *data,enum LibRaw_progress stage, int iteration,int expected);
-
-typedef struct
-{
+  typedef struct
+  {
     memory_callback mem_cb;
-    void*  memcb_data;
+    void *memcb_data;
 
     data_callback data_cb;
-    void*       datacb_data;
+    void *datacb_data;
 
     progress_callback progress_cb;
     void *progresscb_data;
-} libraw_callbacks_t;
-
-// Output bitmap type
-
-typedef struct
-{
-    enum LibRaw_image_formats type; 
-    ushort      height,
-                width,
-                colors,
-                bits,
-                gamma_corrected;
-    unsigned int  data_size; // ðàçìåð ïîëÿ äàííûõ â áàéòàõ
-    unsigned char data[1]; // we'll allocate more!
-}libraw_processed_image_t;
-
 
-//Decoded from exif and used in calculations
-typedef struct
-{
-    char        make[64];
-    char        model[64];
-
-    unsigned    raw_count;
-    unsigned    dng_version;
-    unsigned    is_foveon;
-    int         colors;
-
-    unsigned    filters; // camera CFA pattern mask
-    char        cdesc[5];
+    exif_parser_callback exif_cb;
+    void *exifparser_data;
+    pre_identify_callback pre_identify_cb;
+    post_identify_callback post_identify_cb;
+    process_step_callback pre_subtractblack_cb, pre_scalecolors_cb,
+        pre_preinterpolate_cb, pre_interpolate_cb, interpolate_bayer_cb,
+        interpolate_xtrans_cb, post_interpolate_cb, pre_converttorgb_cb,
+        post_converttorgb_cb;
+  } libraw_callbacks_t;
+
+  typedef struct
+  {
+    enum LibRaw_image_formats type;
+    ushort height, width, colors, bits;
+    unsigned int data_size;
+    unsigned char data[1];
+  } libraw_processed_image_t;
+
+  typedef struct
+  {
+    char guard[4];
+    char make[64];
+    char model[64];
+    char software[64];
+    char normalized_make[64];
+    char normalized_model[64];
+    unsigned maker_index;
+    unsigned raw_count;
+    unsigned dng_version;
+    unsigned is_foveon;
+    int colors;
+    unsigned filters;
+    char xtrans[6][6];
+    char xtrans_abs[6][6];
+    char cdesc[5];
+    unsigned xmplen;
+    char *xmpdata;
+
+  } libraw_iparams_t;
+
+  typedef struct
+  {
+    ushort cleft, ctop, cwidth, cheight, aspect;
+  } libraw_raw_inset_crop_t;
+
+  typedef struct
+  {
+    ushort raw_height, raw_width, height, width, top_margin, left_margin;
+    ushort iheight, iwidth;
+    unsigned raw_pitch;
+    double pixel_aspect;
+    int flip;
+    int mask[8][4];
+    libraw_raw_inset_crop_t raw_inset_crop;
+  } libraw_image_sizes_t;
+
+  struct ph1_t
+  {
+    int format, key_off, tag_21a;
+    int t_black, split_col, black_col, split_row, black_row;
+    float tag_210;
+  };
 
-}libraw_iparams_t;
+  typedef struct
+  {
+    unsigned parsedfields;
+    ushort illuminant;
+    float calibration[4][4];
+    float colormatrix[4][3];
+    float forwardmatrix[3][4];
+  } libraw_dng_color_t;
+
+  typedef struct
+  {
+    unsigned parsedfields;
+    unsigned dng_cblack[LIBRAW_CBLACK_SIZE];
+    unsigned dng_black;
+    float dng_fcblack[LIBRAW_CBLACK_SIZE];
+    float dng_fblack;
+    unsigned dng_whitelevel[4];
+    unsigned default_crop[4]; /* Origin and size */
+    unsigned preview_colorspace;
+    float analogbalance[4];
+    float asshotneutral[4];
+    float baseline_exposure;
+    float LinearResponseLimit;
+  } libraw_dng_levels_t;
+
+  typedef struct
+  {
+    float romm_cam[9];
+  } libraw_P1_color_t;
+
+  typedef struct
+  {
+    int ColorDataVer;
+    int ColorDataSubVer;
+    int SpecularWhiteLevel;
+    int NormalWhiteLevel;
+    int ChannelBlackLevel[4];
+    int AverageBlackLevel;
+    /* multishot */
+    unsigned int multishot[4];
+    /* metering */
+    short MeteringMode;
+    short SpotMeteringMode;
+    uchar FlashMeteringMode;
+    short FlashExposureLock;
+    short ExposureMode;
+    short AESetting;
+    uchar HighlightTonePriority;
+    /* stabilization */
+    short ImageStabilization;
+    /* focus */
+    short FocusMode;
+    short AFPoint;
+    short FocusContinuous;
+    short AFPointsInFocus30D;
+    uchar AFPointsInFocus1D[8];
+    ushort AFPointsInFocus5D; /* bytes in reverse*/
+                              /* AFInfo */
+    ushort AFAreaMode;
+    ushort NumAFPoints;
+    ushort ValidAFPoints;
+    ushort AFImageWidth;
+    ushort AFImageHeight;
+    short AFAreaWidths[61];     /* cycle to NumAFPoints */
+    short AFAreaHeights[61];    /* --''--               */
+    short AFAreaXPositions[61]; /* --''--               */
+    short AFAreaYPositions[61]; /* --''--               */
+    short AFPointsInFocus[4];   /* cycle to floor((NumAFPoints+15)/16) */
+    short AFPointsSelected[4];  /* --''--               */
+    ushort PrimaryAFPoint;
+    /* flash */
+    short FlashMode;
+    short FlashActivity;
+    short FlashBits;
+    short ManualFlashOutput;
+    short FlashOutput;
+    short FlashGuideNumber;
+    /* drive */
+    short ContinuousDrive;
+    /* sensor */
+    short SensorWidth;
+    short SensorHeight;
+    short SensorLeftBorder;
+    short SensorTopBorder;
+    short SensorRightBorder;
+    short SensorBottomBorder;
+    short BlackMaskLeftBorder;
+    short BlackMaskTopBorder;
+    short BlackMaskRightBorder;
+    short BlackMaskBottomBorder;
+    int   AFMicroAdjMode;
+    float AFMicroAdjValue;
+    short MakernotesFlip;
+    short RecordMode;
+    short SRAWQuality;
+    unsigned wbi;
+    float firmware;
+    short RF_lensID;
+  } libraw_canon_makernotes_t;
+
+  typedef struct
+  {
+    int    BaseISO;
+    double Gain;
+    char   Sensor[8];
+    char   SensorUnit[64]; // SU
+    char   HostBody[64];   // HB
+    int    SensorCode;
+    int    SensorSubCode;
+    int    CoatingCode;
+    int    uncropped;
+
+/* CaptureSequenceInitiator is based on the content of the 'model' tag
+  - values like 'Pinhole', 'Flash Sync', '500 Mech.' etc in .3FR 'model' tag
+    come from MAIN MENU > SETTINGS > Camera;
+  - otherwise 'model' contains:
+    1. if CF/CFV/CFH, SU enclosure, can be with SU type if '-' is present
+    2. else if '-' is present, HB + SU type;
+    3. HB;
+*/
+    char CaptureSequenceInitiator[32];
+
+/* SensorUnitConnector, makernotes 0x0015 tag:
+ - in .3FR - SU side
+ - in .FFF - HB side
+*/
+    char SensorUnitConnector[64];
+
+    int format; // 3FR, FFF, Imacon (H3D-39 and maybe others), Hasselblad/Phocus DNG, Adobe DNG
+    int nIFD_CM[2]; // number of IFD containing CM
+    int RecommendedCrop[2];
+
+/* mnColorMatrix is in makernotes tag 0x002a;
+  not present in .3FR files and Imacon/H3D-39 .FFF files;
+  when present in .FFF and Phocus .DNG files, it is a copy of CM1 from .3FR;
+  available samples contain all '1's in the first 3 elements
+*/
+    double mnColorMatrix[4][3];
+
+  } libraw_hasselblad_makernotes_t;
+
+  typedef struct
+  {
+    float  ExpoMidPointShift;
+    ushort DynamicRange;
+    ushort FilmMode;
+    ushort DynamicRangeSetting;
+    ushort DevelopmentDynamicRange;
+    ushort AutoDynamicRange;
+    ushort DRangePriority;
+    ushort DRangePriorityAuto;
+    ushort DRangePriorityFixed;
+
+    /*
+    tag 0x9200, converted to BrightnessCompensation
+    F700, S3Pro, S5Pro, S20Pro, S200EXR
+    E550, E900, F810, S5600, S6500fd, S9000, S9500, S100FS
+    */
+    float BrightnessCompensation; /* in EV, if =4, raw data * 2^4 */
+
+    ushort FocusMode;
+    ushort AFMode;
+    ushort FocusPixel[2];
+    ushort ImageStabilization[3];
+    ushort FlashMode;
+    ushort WB_Preset;
+
+    /* ShutterType:
+       0 - mechanical
+       1 = electronic
+       2 = electronic, long shutter speed
+       3 = electronic, front curtain
+    */
+    ushort ShutterType;
+    ushort ExrMode;
+    ushort Macro;
+    unsigned Rating;
+
+    /* CropMode:
+       1 - FF on GFX,
+       2 - sports finder (mechanical shutter),
+       4 - 1.25x crop (electronic shutter, continuous high)
+    */
+    ushort CropMode;
+    ushort FrameRate;
+    ushort FrameWidth;
+    ushort FrameHeight;
+    char   SerialSignature[0x0c + 1];
+    char   RAFVersion[4 + 1];
+    ushort RAFDataVersion;
+    int    isTSNERDTS;
+
+    /* DriveMode:
+       0 - single frame
+       1 - continuous low
+       2 - continuous high
+    */
+    short DriveMode;
+  } libraw_fuji_info_t;
+
+  typedef struct
+  {
+    ushort cleft, ctop, cwidth, cheight;
+  } libraw_sensor_highspeed_crop_t;
+
+  typedef struct
+  {
+    double ExposureBracketValue;
+    ushort ActiveDLighting;
+    ushort ShootingMode;
+    /* stabilization */
+    uchar ImageStabilization[7];
+    uchar VibrationReduction;
+    uchar VRMode;
+    /* focus */
+    char  FocusMode[7];
+    uchar AFPoint;
+    ushort AFPointsInFocus;
+    uchar  ContrastDetectAF;
+    uchar  AFAreaMode;
+    uchar  PhaseDetectAF;
+    uchar  PrimaryAFPoint;
+    uchar  AFPointsUsed[29];
+    ushort AFImageWidth;
+    ushort AFImageHeight;
+    ushort AFAreaXPposition;
+    ushort AFAreaYPosition;
+    ushort AFAreaWidth;
+    ushort AFAreaHeight;
+    uchar  ContrastDetectAFInFocus;
+    /* flash */
+    char  FlashSetting[13];
+    char  FlashType[20];
+    uchar FlashExposureCompensation[4];
+    uchar ExternalFlashExposureComp[4];
+    uchar FlashExposureBracketValue[4];
+    uchar FlashMode;
+    signed char FlashExposureCompensation2;
+    signed char FlashExposureCompensation3;
+    signed char FlashExposureCompensation4;
+    uchar  FlashSource;
+    uchar  FlashFirmware[2];
+    uchar  ExternalFlashFlags;
+    uchar  FlashControlCommanderMode;
+    uchar  FlashOutputAndCompensation;
+    uchar  FlashFocalLength;
+    uchar  FlashGNDistance;
+    uchar  FlashGroupControlMode[4];
+    uchar  FlashGroupOutputAndCompensation[4];
+    uchar  FlashColorFilter;
+    ushort NEFCompression;
+    int    ExposureMode;
+    int    ExposureProgram;
+    int    nMEshots;
+    int    MEgainOn;
+    double ME_WB[4];
+    uchar  AFFineTune;
+    uchar  AFFineTuneIndex;
+    int8_t AFFineTuneAdj;
+    unsigned LensDataVersion;
+    unsigned FlashInfoVersion;
+    unsigned ColorBalanceVersion;
+    uchar key;
+    ushort NEFBitDepth[4];
+    ushort HighSpeedCropFormat; /* 1 -> 1.3x; 2 -> DX; 3 -> 5:4; 4 -> 3:2; 6 ->
+                                   16:9; 11 -> FX uncropped; 12 -> DX uncropped;
+                                   17 -> 1:1 */
+    libraw_sensor_highspeed_crop_t SensorHighSpeedCrop;
+    ushort SensorWidth;
+    ushort SensorHeight;
+  } libraw_nikon_makernotes_t;
+
+  typedef struct
+  {
+    int      SensorCalibration[2];
+    ushort   FocusMode[2];
+    ushort   AutoFocus;
+    ushort   AFPoint;
+    unsigned AFAreas[64];
+    double   AFPointSelected[5];
+    ushort   AFResult;
+    ushort   DriveMode[5];
+    ushort   ColorSpace;
+    uchar    AFFineTune;
+    short    AFFineTuneAdj[3];
+    char     CameraType2[6];
+  } libraw_olympus_makernotes_t;
+
+  typedef struct
+  {
+    /* Compression:
+     34826 (Panasonic RAW 2): LEICA DIGILUX 2;
+     34828 (Panasonic RAW 3): LEICA D-LUX 3; LEICA V-LUX 1; Panasonic DMC-LX1;
+     Panasonic DMC-LX2; Panasonic DMC-FZ30; Panasonic DMC-FZ50; 34830 (not in
+     exiftool): LEICA DIGILUX 3; Panasonic DMC-L1; 34316 (Panasonic RAW 1):
+     others (LEICA, Panasonic, YUNEEC);
+    */
+    ushort   Compression;
+    ushort   BlackLevelDim;
+    float    BlackLevel[8];
+    unsigned Multishot; /* 0 is Off, 65536 is Pixel Shift */
+    float    gamma;
+    int      HighISOMultiplier[3]; /* 0->R, 1->G, 2->B */
+  } libraw_panasonic_makernotes_t;
+
+  typedef struct
+  {
+    ushort   FocusMode;
+    ushort   AFPointSelected;
+    unsigned AFPointsInFocus;
+    ushort   FocusPosition;
+    uchar    DriveMode[4];
+    short    AFAdjustment;
+    uchar    MultiExposure; /* last bit is not "1" if ME is not used */
+    ushort   Quality; /* 4 is raw, 7 is raw w/ pixel shift, 8 is raw w/ dynamic
+                       pixel shift */
+    /*    uchar AFPointMode;     */
+    /*    uchar SRResult;        */
+    /*    uchar ShakeReduction;  */
+  } libraw_pentax_makernotes_t;
+
+  typedef struct
+  {
+    unsigned ImageSizeFull[4];
+    unsigned ImageSizeCrop[4];
+    int      ColorSpace[2];
+    unsigned key[11];
+    double   DigitalGain; /* PostAEGain, digital stretch */
+    int      DeviceType;
+    char     LensFirmware[32];
+  } libraw_samsung_makernotes_t;
+
+  typedef struct
+  {
+    ushort BlackLevelTop;
+    ushort BlackLevelBottom;
+    short offset_left, offset_top; /* KDC files, negative values or zeros */
+    ushort clipBlack, clipWhite;   /* valid for P712, P850, P880 */
+    float romm_camDaylight[3][3];
+    float romm_camTungsten[3][3];
+    float romm_camFluorescent[3][3];
+    float romm_camFlash[3][3];
+    float romm_camCustom[3][3];
+    float romm_camAuto[3][3];
+    ushort val018percent, val100percent, val170percent;
+    short MakerNoteKodak8a;
+    float ISOCalibrationGain;
+    float AnalogISO;
+  } libraw_kodak_makernotes_t;
+
+  typedef struct {
+    char Software[64];        // tag 0x0203
+    char SystemType[64];      // tag 0x0204
+    char FirmwareString[256]; // tag 0x0301
+    char SystemModel[64];
+  } libraw_p1_makernotes_t;
+
+  typedef struct
+  {
+    ushort   CameraType;
+    uchar    Sony0x9400_version; /* 0 if not found/deciphered, 0xa, 0xb, 0xc
+                                 following exiftool convention */
+    uchar    Sony0x9400_ReleaseMode2;
+    unsigned Sony0x9400_SequenceImageNumber;
+    uchar    Sony0x9400_SequenceLength1;
+    unsigned Sony0x9400_SequenceFileNumber;
+    uchar    Sony0x9400_SequenceLength2;
+    uint8_t  AFAreaModeSetting;
+    ushort   FlexibleSpotPosition[2];
+    uint8_t  AFPointSelected;
+    uint8_t  AFPointsUsed[10];
+    uint8_t  AFTracking;
+    uint8_t  AFType;
+    ushort   FocusLocation[4];
+    int8_t   AFMicroAdjValue;
+    int8_t   AFMicroAdjOn;
+    uchar    AFMicroAdjRegisteredLenses;
+    ushort   VariableLowPassFilter;
+    unsigned LongExposureNoiseReduction;
+    ushort   HighISONoiseReduction;
+    ushort   HDR[2];
+    ushort   group2010;
+    ushort   real_iso_offset;
+    ushort   MeteringMode_offset;
+    ushort   ExposureProgram_offset;
+    ushort   ReleaseMode2_offset;
+    unsigned MinoltaCamID;
+    float    firmware;
+    ushort   ImageCount3_offset;
+    unsigned ImageCount3;
+    unsigned ElectronicFrontCurtainShutter;
+    ushort   MeteringMode2;
+    char     SonyDateTime[20];
+    unsigned ShotNumberSincePowerUp;
+    ushort   PixelShiftGroupPrefix;
+    unsigned PixelShiftGroupID;
+    char     nShotsInPixelShiftGroup;
+    char     numInPixelShiftGroup; /* '0' if ARQ, first shot in the group has '1'
+                                  here */
+    ushort   prd_ImageHeight, prd_ImageWidth;
+    ushort   prd_RawBitDepth;
+    ushort   prd_StorageMethod; /* 82 -> Padded; 89 -> Linear */
+    ushort   prd_BayerPattern;  /* 0 -> not valid; 1 -> RGGB; 4 -> GBRG */
+
+    ushort   SonyRawFileType; /* takes precedence over RAWFileType and Quality:
+                               0  for uncompressed 14-bit raw
+                               1  for uncompressed 12-bit raw
+                               2  for compressed raw
+                               3  for lossless compressed raw
+                            */
+    ushort RAWFileType;     /* takes precedence over Quality
+                               0 for compressed raw, 1 for uncompressed;
+                            */
+    unsigned Quality;       /* 0 or 6 for raw, 7 or 8 for compressed raw */
+    ushort FileFormat;      /*  1000 SR2
+                                2000 ARW 1.0
+                                3000 ARW 2.0
+                                3100 ARW 2.1
+                                3200 ARW 2.2
+                                3300 ARW 2.3
+                                3310 ARW 2.3.1
+                                3320 ARW 2.3.2
+                                3330 ARW 2.3.3
+                                3350 ARW 2.3.5
+                             */
+  } libraw_sony_info_t;
+
+  typedef struct
+  {
+    ushort curve[0x10000];
+    unsigned cblack[LIBRAW_CBLACK_SIZE];
+    unsigned black;
+    unsigned data_maximum;
+    unsigned maximum;
+    long linear_max[4];
+    float fmaximum;
+    float fnorm;
+    ushort white[8][8];
+    float cam_mul[4];
+    float pre_mul[4];
+    float cmatrix[3][4];
+    float ccm[3][4];
+    float rgb_cam[3][4];
+    float cam_xyz[4][3];
+    struct ph1_t phase_one_data;
+    float flash_used;
+    float canon_ev;
+    char model2[64];
+    char UniqueCameraModel[64];
+    char LocalizedCameraModel[64];
+    char ImageUniqueID[64];
+    char RawDataUniqueID[17];
+    char OriginalRawFileName[64];
+    void *profile;
+    unsigned profile_length;
+    unsigned black_stat[8];
+    libraw_dng_color_t dng_color[2];
+    libraw_dng_levels_t dng_levels;
+    int WB_Coeffs[256][4];    /* R, G1, B, G2 coeffs */
+    float WBCT_Coeffs[64][5]; /* CCT, than R, G1, B, G2 coeffs */
+    int as_shot_wb_applied;
+    libraw_P1_color_t P1_color[2];
+    unsigned raw_bps; /* for Phase One, raw format */
+                      /* Phase One raw format values, makernotes tag 0x010e:
+                      0    Name unknown
+                      1    "RAW 1"
+                      2    "RAW 2"
+                      3    "IIQ L"
+                      4    Never seen
+                      5    "IIQ S"
+                      6    "IIQ S v.2"
+                      7    Never seen
+                      8    Name unknown
+                      */
+	int ExifColorSpace;
+  } libraw_colordata_t;
 
-typedef struct
-{
-    ushort      raw_height, 
-                raw_width, 
-                height, 
-                width, 
-                top_margin, 
-                left_margin;
-    ushort      iheight,
-                iwidth;
-    double      pixel_aspect;
-    int         flip;
+  typedef struct
+  {
+    enum LibRaw_thumbnail_formats tformat;
+    ushort twidth, theight;
+    unsigned tlength;
+    int tcolors;
+    char *thumb;
+  } libraw_thumbnail_t;
+
+  typedef struct
+  {
+    float latitude[3];     /* Deg,min,sec */
+    float longitude[3];    /* Deg,min,sec */
+    float gpstimestamp[3]; /* Deg,min,sec */
+    float altitude;
+    char  altref, latref, longref, gpsstatus;
+    char  gpsparsed;
+  } libraw_gps_info_t;
+
+  typedef struct
+  {
+    float iso_speed;
+    float shutter;
+    float aperture;
+    float focal_len;
+    time_t timestamp;
+    unsigned shot_order;
+    unsigned gpsdata[32];
+    libraw_gps_info_t parsed_gps;
+    char desc[512], artist[64];
+    float analogbalance[4];
+  } libraw_imgother_t;
+
+  typedef struct {
+    float FlashEC;
+    float FlashGN;
+    float CameraTemperature;
+    float SensorTemperature;
+    float SensorTemperature2;
+    float LensTemperature;
+    float AmbientTemperature;
+    float BatteryTemperature;
+    float exifAmbientTemperature;
+    float exifHumidity;
+    float exifPressure;
+    float exifWaterDepth;
+    float exifAcceleration;
+    float exifCameraElevationAngle;
+    float real_ISO;
+    float exifExposureIndex;
+    ushort ColorSpace;
+    char firmware[128];
+  } libraw_metadata_common_t;
+
+  typedef struct
+  {
+    unsigned greybox[4];   /* -A  x1 y1 x2 y2 */
+    unsigned cropbox[4];   /* -B x1 y1 x2 y2 */
+    double aber[4];        /* -C */
+    double gamm[6];        /* -g */
+    float user_mul[4];     /* -r mul0 mul1 mul2 mul3 */
+    unsigned shot_select;  /* -s */
+    float bright;          /* -b */
+    float threshold;       /* -n */
+    int half_size;         /* -h */
+    int four_color_rgb;    /* -f */
+    int highlight;         /* -H */
+    int use_auto_wb;       /* -a */
+    int use_camera_wb;     /* -w */
+    int use_camera_matrix; /* +M/-M */
+    int output_color;      /* -o */
+    char *output_profile;  /* -o */
+    char *camera_profile;  /* -p */
+    char *bad_pixels;      /* -P */
+    char *dark_frame;      /* -K */
+    int output_bps;        /* -4 */
+    int output_tiff;       /* -T */
+    int user_flip;         /* -t */
+    int user_qual;         /* -q */
+    int user_black;        /* -k */
+    int user_cblack[4];
+    int user_sat;          /* -S */
+    int med_passes;        /* -m */
+    float auto_bright_thr;
+    float adjust_maximum_thr;
+    int no_auto_bright;    /* -W */
+    int use_fuji_rotate;   /* -j */
+    int green_matching;
+    /* DCB parameters */
+    int dcb_iterations;
+    int dcb_enhance_fl;
+    int fbdd_noiserd;
+    int exp_correc;
+    float exp_shift;
+    float exp_preser;
+    /* Raw speed */
+    int use_rawspeed;
+    /* DNG SDK */
+    int use_dngsdk;
+    /* Disable Auto-scale */
+    int no_auto_scale;
+    /* Disable intepolation */
+    int no_interpolation;
+    /*  int x3f_flags; */
+    /* Sony ARW2 digging mode */
+    /* int sony_arw2_options; */
+    unsigned raw_processing_options;
+    unsigned max_raw_memory_mb;
+    int sony_arw2_posterization_thr;
+    /* Nikon Coolscan */
+    float coolscan_nef_gamma;
+    char p4shot_order[5];
+    /* Custom camera list */
+    char **custom_camera_strings;
+  } libraw_output_params_t;
+
+  typedef struct
+  {
+    /* really allocated bitmap */
+    void *raw_alloc;
+    /* alias to single_channel variant */
+    ushort *raw_image;
+    /* alias to 4-channel variant */
+    ushort (*color4_image)[4];
+    /* alias to 3-color variand decoded by RawSpeed */
+    ushort (*color3_image)[3];
+    /* float bayer */
+    float *float_image;
+    /* float 3-component */
+    float (*float3_image)[3];
+    /* float 4-component */
+    float (*float4_image)[4];
+
+    /* Phase One black level data; */
+    short (*ph1_cblack)[2];
+    short (*ph1_rblack)[2];
+    /* save color and sizes here, too.... */
+    libraw_iparams_t iparams;
+    libraw_image_sizes_t sizes;
+    libraw_internal_output_params_t ioparams;
+    libraw_colordata_t color;
+  } libraw_rawdata_t;
+
+  typedef struct
+  {
+    unsigned long long LensID;
+    char Lens[128];
+    ushort LensFormat; /* to characterize the image circle the lens covers */
+    ushort LensMount;  /* 'male', lens itself */
+    unsigned long long CamID;
+    ushort CameraFormat; /* some of the sensor formats */
+    ushort CameraMount;  /* 'female', body throat */
+    char   body[64];
+    short  FocalType; /* -1/0 is unknown; 1 is fixed focal; 2 is zoom */
+    char   LensFeatures_pre[16], LensFeatures_suf[16];
+    float  MinFocal, MaxFocal;
+    float  MaxAp4MinFocal, MaxAp4MaxFocal, MinAp4MinFocal, MinAp4MaxFocal;
+    float  MaxAp, MinAp;
+    float  CurFocal, CurAp;
+    float  MaxAp4CurFocal, MinAp4CurFocal;
+    float  MinFocusDistance;
+    float  FocusRangeIndex;
+    float  LensFStops;
+    unsigned long long TeleconverterID;
+    char Teleconverter[128];
+    unsigned long long AdapterID;
+    char Adapter[128];
+    unsigned long long AttachmentID;
+    char   Attachment[128];
+    ushort FocalUnits;
+    float  FocalLengthIn35mmFormat;
+  } libraw_makernotes_lens_t;
+
+  typedef struct
+  {
+    float EffectiveMaxAp;
+    uchar LensIDNumber, LensFStops, MCUVersion, LensType;
+  } libraw_nikonlens_t;
+
+  typedef struct
+  {
+    float MinFocal, MaxFocal, MaxAp4MinFocal, MaxAp4MaxFocal;
+  } libraw_dnglens_t;
+
+  typedef struct
+  {
+    float MinFocal, MaxFocal, MaxAp4MinFocal, MaxAp4MaxFocal, EXIF_MaxAp;
+    char LensMake[128], Lens[128], LensSerial[128], InternalLensSerial[128];
+    ushort FocalLengthIn35mmFormat;
+    libraw_nikonlens_t nikon;
+    libraw_dnglens_t dng;
+    libraw_makernotes_lens_t makernotes;
+  } libraw_lensinfo_t;
+
+  typedef struct
+  {
+    libraw_canon_makernotes_t canon;
+    libraw_nikon_makernotes_t nikon;
+    libraw_hasselblad_makernotes_t hasselblad;
+    libraw_fuji_info_t fuji;
+    libraw_olympus_makernotes_t olympus;
+    libraw_sony_info_t sony;
+    libraw_kodak_makernotes_t kodak;
+    libraw_panasonic_makernotes_t panasonic;
+    libraw_pentax_makernotes_t pentax;
+    libraw_p1_makernotes_t phaseone;
+    libraw_samsung_makernotes_t samsung;
+    libraw_metadata_common_t common;
+  } libraw_makernotes_t;
+
+  typedef struct
+  {
+    short DriveMode;
+    short FocusMode;
+    short MeteringMode;
+    short AFPoint;
+    short ExposureMode;
+    short ExposureProgram;
+    short ImageStabilization;
+    char BodySerial[64];
+    char InternalBodySerial[64]; /* this may be PCB or sensor serial, depends on
+                                    make/model */
+  } libraw_shootinginfo_t;
+
+  typedef struct
+  {
+    unsigned fsize;
+    ushort rw, rh;
+    uchar lm, tm, rm, bm;
+    ushort lf;
+    uchar cf, max, flags;
+    char t_make[10], t_model[20];
+    ushort offset;
+  } libraw_custom_camera_t;
+
+  typedef struct
+  {
+    ushort (*image)[4];
+    libraw_image_sizes_t sizes;
+    libraw_iparams_t idata;
+    libraw_lensinfo_t lens;
+    libraw_makernotes_t makernotes;
+    libraw_shootinginfo_t shootinginfo;
+    libraw_output_params_t params;
+    unsigned int progress_flags;
+    unsigned int process_warnings;
+    libraw_colordata_t color;
+    libraw_imgother_t other;
+    libraw_thumbnail_t thumbnail;
+    libraw_rawdata_t rawdata;
+    void *parent_class;
+  } libraw_data_t;
+
+  struct fuji_compressed_params
+  {
+    int8_t *q_table; /* quantization table */
+    int q_point[5];  /* quantization points */
+    int max_bits;
+    int min_value;
+    int raw_bits;
+    int total_values;
+    int maxDiff;
+    ushort line_width;
+  };
 
-    // masked border sizes
-    ushort      right_margin,bottom_margin; // right masked width and bottom height, inited after idendify()
+#ifdef __cplusplus
+}
+#endif
 
-} libraw_image_sizes_t;
+#if defined (LIBRAW_LIBRARY_BUILD) && defined(__cplusplus)
 
-//Phase One  data
-struct ph1_t
+class libraw_static_table_t
 {
-    int format, key_off, t_black, black_off, split_col, tag_21a;
-    float tag_210;
+public:
+    libraw_static_table_t(const int *a, const unsigned s): data(a),_size(s) {}
+    libraw_static_table_t(): data(0),_size(0){}
+    libraw_static_table_t(const libraw_static_table_t& s) : data(s.data), _size(s._size) {}
+    unsigned size() const { return _size; }
+    libraw_static_table_t& operator = (const libraw_static_table_t& s)
+    {
+        _size = s._size;
+        data = s.data;
+        return *this;
+    }
+    int operator [] (unsigned idx) const
+    {
+        if (idx < _size) return data[idx];
+        if(_size>0 && data) return data[0];
+        return 0;
+    }
+private:
+    const int *data;
+    unsigned _size;
 };
 
+#endif
 
-typedef struct
-{
-    // 32 bits total
-    unsigned curve_state        : 3;
-    unsigned rgb_cam_state      : 3;
-    unsigned cmatrix_state      : 3;
-    unsigned pre_mul_state      : 3;
-    unsigned cam_mul_state      : 3;
-    unsigned filler             : 17;
-} color_data_state_t;
 
-typedef struct
-{
-    color_data_state_t   color_flags;
-    ushort      white[8][8]; // white block extracted from ciff/CRW
-    float       cam_mul[4]; // camera white balance (from RAW)
-    float       pre_mul[4]; // either set in identify() or calculated. Used on output
-    float       cmatrix[3][4]; // camera color matrix
-    float       rgb_cam[3][4]; // another way to set color matrix
-    float       cam_xyz[4][3]; // Camera to XYZ matrix (DNG coeffs)
-    ushort      curve[0x4001]; // camera tone curve/ljpeg curve
-    unsigned    black;
-    unsigned    maximum;
-    struct ph1_t       phase_one_data;
-    float       flash_used; // canon/CRW only
-    float       canon_ev; // canon/CRW only
-    char        model2[64];
-    // profile
-    void        *profile;
-    unsigned    profile_length;
-}libraw_colordata_t;
+/* Byte order */
+#if defined(__POWERPC__)
+#define LibRawBigEndian 1
 
-typedef struct
-{
-    enum LibRaw_thumbnail_formats tformat;
-    ushort      twidth, 
-                theight;
-    unsigned    tlength;
-    int         tcolors;
-    
-    // thumbnail buffer
-    char       *thumb;
-}libraw_thumbnail_t;
+#elif defined(__INTEL__)
+#define LibRawBigEndian 0
 
-// Decoded from exif/raw, but not used in real calculations
-typedef struct
-{
-    float       iso_speed; 
-    float       shutter;
-    float       aperture;
-    float       focal_len;
-    time_t      timestamp; 
-    unsigned    shot_order;
-    unsigned    gpsdata[32];
-    // string variables
-    char        desc[512],
-                artist[64];
-} libraw_imgother_t;
+#elif defined(_M_IX86) || defined(__i386__)
+#define LibRawBigEndian 0
 
-typedef struct
-{
-    unsigned    greybox[4];     /* -A  x1 y1 x2 y2 */
-    double      aber[4];        /* -C */
-    double      gamm[5];        /* -g */
-    float       user_mul[4];    /* -r mul0 mul1 mul2 mul3 */
-    unsigned    shot_select;    /* -s */
-    float       bright;         /* -b */
-    float       threshold;      /*  -n */
-    int         half_size;      /* -h */
-    int         four_color_rgb; /* -f */
-    int         document_mode;  /* -d/-D */
-    int         highlight;      /* -H */
-//    int         verbose;      /* -v */
-    int         use_auto_wb;    /* -a */
-    int         use_camera_wb;  /* -w */
-    int         use_camera_matrix; /* +M/-M */
-    int         output_color;   /* -o */
-    char        *output_profile; /* -o */
-    char        *camera_profile; /* -p */
-    char        *bad_pixels;    /* -P */
-    char        *dark_frame;    /* -K */
-    int         output_bps;     /* -4 */
-    int         gamma_16bit;    /* -1 */
-    int         output_tiff;    /* -T */
-    int         user_flip;      /* -t */
-    int         user_qual;      /* -q */
-    int         user_black;     /* -k */
-    int         user_sat;       /* -S */
-
-    int         med_passes;     /* -m */
-    float       auto_bright_thr; 
-    int         no_auto_bright; /* -W */
-    int         use_fuji_rotate;/* -j */
-    enum LibRaw_filtering    filtering_mode; 
-}libraw_output_params_t;
+#elif defined(_M_X64) || defined(__amd64__) || defined(__x86_64__)
+#define LibRawBigEndian 0
 
-typedef struct
-{
-    ushort  *buffer; // actual pixel buffer size=(raw_width*raw_height - width*height)
-    ushort  *tl;     // top left   size=(top_margin*left_margin)
-    ushort  *top;    // top        size=(top_margin*width)
-    ushort  *tr;     // top right  size=((raw_width-width-left_margin)*top_margin)
-    ushort  *left;   // left       size=(left_margin*height)
-    ushort  *right;  // right      size=(raw_width-width-left_margin)*height;
-    ushort  *bl;     // bottom left size=(raw_height-height-top_margin)*left_margin
-    ushort  *bottom; // bottom      size=(raw_height-height-top_margin)*width
-    ushort  *br;     // bottom right size=(raw_height-height-top_margin)*
-    ushort  (*ph1_black)[2]; // Phase One black
-}libraw_masked_t;
+#elif defined(__LITTLE_ENDIAN__)
+#define LibRawBigEndian 0
 
-typedef struct
-{
-    unsigned int                progress_flags;
-    unsigned int                process_warnings;
-    libraw_iparams_t            idata;
-    libraw_image_sizes_t        sizes;
-    libraw_colordata_t          color;
-    libraw_imgother_t           other;
-    libraw_thumbnail_t          thumbnail;
-    libraw_masked_t             masked_pixels;
-    ushort                      (*image)[4] ;
-    libraw_output_params_t     params;
-    // pointer to LibRaw class for use in C calls
-    void                *parent_class;      
-} libraw_data_t;
+#elif defined(__BIG_ENDIAN__)
+#define LibRawBigEndian 1
+#elif defined(_ARM_)
+#define LibRawBigEndian 0
 
+#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define LibRawBigEndian 0
 
-#ifdef __cplusplus
-}
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#define LibRawBigEndian 1
+#else
+#ifndef qXCodeRez
+#error Unable to figure out byte order.
+#endif
 #endif
 
 #endif
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/libraw/libraw_version.h libkdcraw/libkdcraw/libraw/libraw/libraw_version.h
--- libkdcraw-wrk/libkdcraw/libraw/libraw/libraw_version.h	2022-11-07 08:15:53.614821808 +0300
+++ libkdcraw/libkdcraw/libraw/libraw/libraw_version.h	2022-11-07 07:46:31.730795008 +0300
@@ -1,47 +1,63 @@
-/*
+/* -*- C++ -*-
  * File: libraw_version.h
- * Copyright 2008-2009 Alex Tutubalin <lexa@lexa.ru>
- * Created: Mon Sept  8, 2008 
+ * Copyright 2008-2020 LibRaw LLC (info@libraw.org)
+ * Created: Mon Sept  8, 2008
  *
  * LibRaw C++ interface
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
+
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+(See the file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+(See the file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
  */
 
 #ifndef __VERSION_H
 #define __VERSION_H
 
-#define LIBRAW_MAJOR_VERSION  0
-#define LIBRAW_MINOR_VERSION  7
-#define LIBRAW_PATCH_VERSION  2
-#define LIBRAW_VERSION_TAIL   Release
+#define LIBRAW_MAJOR_VERSION 0
+#define LIBRAW_MINOR_VERSION 20
+#define LIBRAW_PATCH_VERSION 2
+#define LIBRAW_VERSION_TAIL Release
+
+#define LIBRAW_SHLIB_CURRENT 20
+#define LIBRAW_SHLIB_REVISION 0
+#define LIBRAW_SHLIB_AGE 0
+
+#define _LIBRAW_VERSION_MAKE(a, b, c, d) #a "." #b "." #c "-" #d
+#define LIBRAW_VERSION_MAKE(a, b, c, d) _LIBRAW_VERSION_MAKE(a, b, c, d)
+
+#define LIBRAW_VERSION_STR                                                     \
+  LIBRAW_VERSION_MAKE(LIBRAW_MAJOR_VERSION, LIBRAW_MINOR_VERSION,              \
+                      LIBRAW_PATCH_VERSION, LIBRAW_VERSION_TAIL)
+
+#define LIBRAW_MAKE_VERSION(major, minor, patch)                               \
+  (((major) << 16) | ((minor) << 8) | (patch))
 
-#define _LIBRAW_VERSION_MAKE(a,b,c,d) #a"."#b"."#c"-"#d
-#define LIBRAW_VERSION_MAKE(a,b,c,d) _LIBRAW_VERSION_MAKE(a,b,c,d)
+#define LIBRAW_VERSION                                                         \
+  LIBRAW_MAKE_VERSION(LIBRAW_MAJOR_VERSION, LIBRAW_MINOR_VERSION,              \
+                      LIBRAW_PATCH_VERSION)
 
-#define LIBRAW_VERSION_STR LIBRAW_VERSION_MAKE(LIBRAW_MAJOR_VERSION,LIBRAW_MINOR_VERSION,LIBRAW_PATCH_VERSION,LIBRAW_VERSION_TAIL)
+#define LIBRAW_CHECK_VERSION(major, minor, patch)                              \
+  (LibRaw::versionNumber() >= LIBRAW_MAKE_VERSION(major, minor, patch))
 
-#define LIBRAW_MAKE_VERSION(major,minor,patch) \
-    (((major) << 16) | ((minor) << 8) | (patch))
+#define LIBRAW_RUNTIME_CHECK_VERSION_EXACT()                                   \
+  ((LibRaw::versionNumber() & 0xffff00) ==                                     \
+   LIBRAW_MAKE_VERSION(LIBRAW_MAJOR_VERSION, LIBRAW_MINOR_VERSION, 0))
 
-#define LIBRAW_VERSION \
-    LIBRAW_MAKE_VERSION(LIBRAW_MAJOR_VERSION,LIBRAW_MINOR_VERSION,LIBRAW_PATCH_VERSION)
+#define LIBRAW_RUNTIME_CHECK_VERSION_NOTLESS()                                 \
+  ((LibRaw::versionNumber() & 0xffff00) >=                                     \
+   LIBRAW_MAKE_VERSION(LIBRAW_MAJOR_VERSION, LIBRAW_MINOR_VERSION, 0))
 
-#define LIBRAW_CHECK_VERSION(major,minor,patch) \
-    ( LibRaw::versionNumber() >= LIBRAW_MAKE_VERSION(major,minor,patch) )
+#define LIBRAW_COMPILE_CHECK_VERSION(major, minor)                             \
+  (LIBRAW_MAKE_VERSION(major, minor, 0) == (LIBRAW_VERSION & 0xffff00))
 
+#define LIBRAW_COMPILE_CHECK_VERSION_NOTLESS(major, minor)                     \
+  (LIBRAW_MAKE_VERSION(major, minor, 0) <= (LIBRAW_VERSION & 0xffff00))
 
 #endif
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/samples/4channels.cpp libkdcraw/libkdcraw/libraw/samples/4channels.cpp
--- libkdcraw-wrk/libkdcraw/libraw/samples/4channels.cpp	2022-11-07 08:15:53.614821808 +0300
+++ libkdcraw/libkdcraw/libraw/samples/4channels.cpp	2022-11-07 07:46:31.730795008 +0300
@@ -1,65 +1,59 @@
-/*
+/* -*- C++ -*-
  * File: 4channels.cpp
- * Copyright 2009 Alex Tutubalin <lexa@lexa.ru>
+ * Copyright 2008-2020 LibRaw LLC (info@libraw.org)
  * Created: Mon Feb 09, 2009
  *
  * LibRaw sample
  * Generates 4 TIFF file from RAW data, one file per channel
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
+
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+
  */
 #include <stdio.h>
 #include <string.h>
 #include <math.h>
-#ifndef WIN32
+#include "libraw/libraw.h"
+
+#ifndef LIBRAW_WIN32_CALLS
 #include <netinet/in.h>
 #else
 #include <winsock2.h>
 #endif
 
-#include "libraw/libraw.h"
-
-#ifdef WIN32
+#ifdef LIBRAW_WIN32_CALLS
 #define snprintf _snprintf
 #endif
 
 int main(int ac, char *av[])
 {
-    int  i, ret;
-    int autoscale=0,filtering_mode=LIBRAW_FILTERING_DEFAULT,black_subtraction=1;
-    char outfn[1024],thumbfn[1024]; 
-
-    LibRaw RawProcessor;
-    if(ac<2) 
-        {
-          usage:
-            printf(
-                "4channeld - LibRaw %s sample. %d cameras supported\n"
-                "Usage: %s [-s N] [-g] [-A] [-B] [-N] raw-files....\n"
-                "\t-s N - select Nth image in file (default=0)\n"
-                "\t-g - use gamma correction with gamma 2.2 (not precise,use for visual inspection only)\n"
-                "\t-A - autoscaling (by integer factor)\n"
-                "\t-B - no black subtraction\n"
-                "\t-N - no raw curve\n"
-                ,LibRaw::version(),
-                LibRaw::cameraCount(),
-                av[0]);
-            return 0;
-        }
-    
+  int i, ret;
+  int autoscale = 0, black_subtraction = 1, use_gamma = 0;
+  char outfn[1024];
+
+  LibRaw RawProcessor;
+  if (ac < 2)
+  {
+  usage:
+    printf("4channels - LibRaw %s sample. %d cameras supported\n"
+           "Usage: %s [-s N] [-g] [-A] [-B] [-N] raw-files....\n"
+           "\t-s N - select Nth image in file (default=0)\n"
+           "\t-g - use gamma correction with gamma 2.2 (not precise,use for "
+           "visual inspection only)\n"
+           "\t-A - autoscaling (by integer factor)\n"
+           "\t-B - no black subtraction\n",
+           LibRaw::version(), LibRaw::cameraCount(), av[0]);
+    return 0;
+  }
+
 #define P1 RawProcessor.imgdata.idata
 #define S RawProcessor.imgdata.sizes
 #define C RawProcessor.imgdata.color
@@ -67,121 +61,113 @@
 #define P2 RawProcessor.imgdata.other
 #define OUT RawProcessor.imgdata.params
 
-    OUT.document_mode=2;
-    OUT.output_bps=16;
-    OUT.output_tiff=1;
-    OUT.user_flip=0;
-    OUT.no_auto_bright = 1;
-    OUT.half_size=1;
-    OUT.filtering_mode= LIBRAW_FILTERING_AUTOMATIC;
-
-    for (i=1;i<ac;i++)
-        {
-            if(av[i][0]=='-')
-                {
-                    if(av[i][1]=='s' && av[i][2]==0)
-                        {
-                            i++;
-                            OUT.shot_select=atoi(av[i]);
-                        }
-                    else if(av[i][1]=='g' && av[i][2]==0)
-                        OUT.gamma_16bit=1;
-                    else if(av[i][1]=='A' && av[i][2]==0)
-                        autoscale=1;
-                    else if(av[i][1]=='B' && av[i][2]==0)
-                        {
-                            filtering_mode |= (LIBRAW_FILTERING_NOZEROES | LIBRAW_FILTERING_NOBLACKS);
-                            black_subtraction=0;
-                        }
-                    else if(av[i][1]=='N' && av[i][2]==0)
-                        filtering_mode |= LIBRAW_FILTERING_NORAWCURVE;
-                    else
-                        goto usage;
-                    continue;
-                }
-            if(filtering_mode)
-                OUT.filtering_mode = (LibRaw_filtering) filtering_mode;
-            int r,c;
-            printf("Processing file %s\n",av[i]);
-            if( (ret = RawProcessor.open_file(av[i])) != LIBRAW_SUCCESS)
-                {
-                    fprintf(stderr,"Cannot open %s: %s\n",av[i],libraw_strerror(ret));
-                    continue; // no recycle b/c open file will recycle itself
-                }
-            if(P1.is_foveon)
-                {
-                    printf("Cannot process foveon image %s\n",av[i]);
-                    continue ;
-                }
-            if( (ret = RawProcessor.unpack() ) != LIBRAW_SUCCESS)
-                {
-                    fprintf(stderr,"Cannot unpack %s: %s\n",av[i],libraw_strerror(ret));
-                    continue;
-                }
-            if(black_subtraction && C.black>0)
-                {
-                    for(int j=0; j<S.iheight*S.iwidth; j++)
-                        for(int c = 0; c< 4; c++)
-                            if(RawProcessor.imgdata.image[j][c]>C.black)
-                                RawProcessor.imgdata.image[j][c]-=C.black;
-                            else
-                                RawProcessor.imgdata.image[j][c]=0;
-                }
-
-            if(autoscale)
-                {
-                    unsigned max=0,scale;
-                    for(int j=0; j<S.iheight*S.iwidth; j++)
-                        for(int c = 0; c< 4; c++)
-                            if(max < RawProcessor.imgdata.image[j][c])
-                                max = RawProcessor.imgdata.image[j][c]; 
-                    if (max >0 && max< 1<<15)
-                        {
-                            scale = (1<<16)/max;
-                            printf("Scaling with multiplier=%d (max=%d)\n",scale,max);
-                            for(int j=0; j<S.iheight*S.iwidth; j++)
-                                for(c=0;c<4;c++)
-                                    RawProcessor.imgdata.image[j][c] *= scale;
-                        }
-		   printf("Black level (scaled)=%d\n",C.black*scale);
-                }
-		else
-		   printf("Black level (unscaled)=%d\n",C.black);
-
-
-            // hack to make dcraw tiff writer happy
-	    int isrgb=(P1.colors==4?0:1);
-            P1.colors = 1;
-            S.width = S.iwidth;
-            S.height = S.iheight;
-
-            for (int layer=0;layer<4;layer++)
-                {
-                    if(layer>0)
-                        {
-                            for (int rc = 0; rc < S.iheight*S.iwidth; rc++)
-                                RawProcessor.imgdata.image[rc][0] = RawProcessor.imgdata.image[rc][layer];
-                        }
-		    char lname[8];
-		    if(isrgb)
-		      {
-			snprintf(lname,7,"%c",(char*)("RGBG")[layer]);
-			if(layer==3)
-			  strcat(lname,"2");
-		      }
-		    else
-		      snprintf(lname,7,"%c",(char*)("GCMY")[layer]);
-
-                    if(OUT.shot_select)
-                        snprintf(outfn,sizeof(outfn),"%s-%d.%s.tiff",av[i],OUT.shot_select,lname);
-                    else
-                        snprintf(outfn,sizeof(outfn),"%s.%s.tiff",av[i],lname);
-                    
-                    printf("Writing file %s\n",outfn);
-                    if( LIBRAW_SUCCESS != (ret = RawProcessor.dcraw_ppm_tiff_writer(outfn)))
-                        fprintf(stderr,"Cannot write %s: %s\n",outfn,libraw_strerror(ret));
-                }
-
-        }
-    return 0;
+  OUT.output_bps = 16;
+  OUT.output_tiff = 1;
+  OUT.user_flip = 0;
+  OUT.no_auto_bright = 1;
+  OUT.half_size = 1;
+
+  for (i = 1; i < ac; i++)
+  {
+    if (av[i][0] == '-')
+    {
+      if (av[i][1] == 's' && av[i][2] == 0)
+      {
+        i++;
+        OUT.shot_select = av[i] ? atoi(av[i]) : 0;
+      }
+      else if (av[i][1] == 'g' && av[i][2] == 0)
+        use_gamma = 1;
+      else if (av[i][1] == 'A' && av[i][2] == 0)
+        autoscale = 1;
+      else if (av[i][1] == 'B' && av[i][2] == 0)
+      {
+        black_subtraction = 0;
+      }
+      else
+        goto usage;
+      continue;
+    }
+    if (!use_gamma)
+      OUT.gamm[0] = OUT.gamm[1] = 1;
+
+    int c;
+    printf("Processing file %s\n", av[i]);
+    if ((ret = RawProcessor.open_file(av[i])) != LIBRAW_SUCCESS)
+    {
+      fprintf(stderr, "Cannot open %s: %s\n", av[i], libraw_strerror(ret));
+      continue; // no recycle b/c open file will recycle itself
+    }
+    if (P1.is_foveon)
+    {
+      printf("Cannot process Foveon image %s\n", av[i]);
+      continue;
+    }
+    if ((ret = RawProcessor.unpack()) != LIBRAW_SUCCESS)
+    {
+      fprintf(stderr, "Cannot unpack %s: %s\n", av[i], libraw_strerror(ret));
+      continue;
+    }
+    RawProcessor.raw2image();
+    if (black_subtraction)
+    {
+      RawProcessor.subtract_black();
+    }
+
+    if (autoscale)
+    {
+      unsigned max = 0, scale = 1;
+      for (int j = 0; j < S.iheight * S.iwidth; j++)
+        for (int c = 0; c < 4; c++)
+          if (max < RawProcessor.imgdata.image[j][c])
+            max = RawProcessor.imgdata.image[j][c];
+      if (max > 0 && max < 1 << 15)
+      {
+        scale = (1 << 16) / max;
+        printf("Scaling with multiplier=%d (max=%d)\n", scale, max);
+        for (int j = 0; j < S.iheight * S.iwidth; j++)
+          for (c = 0; c < 4; c++)
+            RawProcessor.imgdata.image[j][c] *= scale;
+      }
+      printf("Black level (scaled)=%d\n", C.black * scale);
+    }
+    else
+      printf("Black level (unscaled)=%d\n", C.black);
+
+    // hack to make dcraw tiff writer happy
+    int isrgb = (P1.colors == 4 ? 0 : 1);
+    P1.colors = 1;
+    S.width = S.iwidth;
+    S.height = S.iheight;
+
+    for (int layer = 0; layer < 4; layer++)
+    {
+      if (layer > 0)
+      {
+        for (int rc = 0; rc < S.iheight * S.iwidth; rc++)
+          RawProcessor.imgdata.image[rc][0] =
+              RawProcessor.imgdata.image[rc][layer];
+      }
+      char lname[8];
+      if (isrgb)
+      {
+        snprintf(lname, 7, "%c", ((char *)("RGBG"))[layer]);
+        if (layer == 3)
+          strcat(lname, "2");
+      }
+      else
+        snprintf(lname, 7, "%c", ((char *)("GCMY"))[layer]);
+
+      if (OUT.shot_select)
+        snprintf(outfn, sizeof(outfn), "%s-%d.%s.tiff", av[i], OUT.shot_select,
+                 lname);
+      else
+        snprintf(outfn, sizeof(outfn), "%s.%s.tiff", av[i], lname);
+
+      printf("Writing file %s\n", outfn);
+      if (LIBRAW_SUCCESS != (ret = RawProcessor.dcraw_ppm_tiff_writer(outfn)))
+        fprintf(stderr, "Cannot write %s: %s\n", outfn, libraw_strerror(ret));
+    }
+  }
+  return 0;
 }
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/samples/Makefile libkdcraw/libkdcraw/libraw/samples/Makefile
--- libkdcraw-wrk/libkdcraw/libraw/samples/Makefile	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/samples/Makefile	2022-11-07 07:46:31.642795007 +0300
@@ -0,0 +1,2 @@
+all:
+	(cd ..; make all_samples)
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/samples/README-samples.rus libkdcraw/libkdcraw/libraw/samples/README-samples.rus
--- libkdcraw-wrk/libkdcraw/libraw/samples/README-samples.rus	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/samples/README-samples.rus	2022-11-07 07:46:31.642795007 +0300
@@ -0,0 +1,2 @@
+
+See ../doc/Samples-LibRaw-rus.html and ../doc/Samples-LibRaw-eng.html
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/samples/dcraw_emu.cpp libkdcraw/libkdcraw/libraw/samples/dcraw_emu.cpp
--- libkdcraw-wrk/libkdcraw/libraw/samples/dcraw_emu.cpp	2022-11-07 08:15:53.614821808 +0300
+++ libkdcraw/libkdcraw/libraw/samples/dcraw_emu.cpp	2022-11-07 07:46:31.730795008 +0300
@@ -1,27 +1,25 @@
-/*
+/* -*- C++ -*-
  * File: dcraw_emu.cpp
- * Copyright 2008-2009 Alex Tutubalin <lexa@lexa.ru>
+ * Copyright 2008-2020 LibRaw LLC (info@libraw.org)
  * Created: Sun Mar 23,   2008
  *
- * LibRaw simple C++ API  (almost complete dcraw emulator)
+ * LibRaw simple C++ API sample: almost complete dcraw emulator
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
+
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+
  */
-#ifdef HAVE_CONFIG_H
-#include "config.h"
+#ifdef _MSC_VER
+// suppress sprintf-related warning. sprintf() is permitted in sample code
+#define _CRT_SECURE_NO_WARNINGS
 #endif
 
 #include <stdio.h>
@@ -29,260 +27,627 @@
 #include <stdlib.h>
 #include <math.h>
 #include <ctype.h>
-#ifndef WIN32
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/stat.h>
+
+#include "libraw/libraw.h"
+
+#ifndef LIBRAW_WIN32_CALLS
 #include <sys/mman.h>
+#include <sys/time.h>
+#include <unistd.h>
+#else
+#include <io.h>
 #endif
+#include <fcntl.h>
+#include <sys/stat.h>
 
-#include "libraw/libraw.h"
-#ifdef WIN32
+#ifdef LIBRAW_WIN32_CALLS
 #define snprintf _snprintf
+#include <windows.h>
+#else
+#define O_BINARY 0
 #endif
 
-#ifdef _OPENMP
-#include <omp.h>
+#ifdef USE_DNGSDK
+#include "dng_host.h"
+#include "dng_negative.h"
+#include "dng_simple_image.h"
+#include "dng_info.h"
 #endif
 
 void usage(const char *prog)
 {
-    printf("dcraw_emu: almost complete dcraw emulator\n");
-    printf("Usage:  %s [OPTION]... [FILE]...\n", prog);
-    printf(
-"-v        Verbose: print progress messages (repeated -v will add verbosity)\n"
-"-w        Use camera white balance, if possible\n"
-"-a        Average the whole image for white balance\n"
-"-A <x y w h> Average a grey box for white balance\n"
-"-r <r g b g> Set custom white balance\n"
-"+M/-M     Use/don't use an embedded color matrix\n"
-"-C <r b>  Correct chromatic aberration\n"
-"-P <file> Fix the dead pixels listed in this file\n"
-"-K <file> Subtract dark frame (16-bit raw PGM)\n"
-"-k <num>  Set the darkness level\n"
-"-S <num>  Set the saturation level\n"
-"-n <num>  Set threshold for wavelet denoising\n"
-"-H [0-9]  Highlight mode (0=clip, 1=unclip, 2=blend, 3+=rebuild)\n"
-"-t [0-7]  Flip image (0=none, 3=180, 5=90CCW, 6=90CW)\n"
-"-o [0-5]  Output colorspace (raw,sRGB,Adobe,Wide,ProPhoto,XYZ)\n"
+  printf("dcraw_emu: almost complete dcraw emulator\n");
+  printf("Usage:  %s [OPTION]... [FILE]...\n", prog);
+  printf("-c float-num       Set adjust maximum threshold (default 0.75)\n"
+         "-v        Verbose: print progress messages (repeated -v will add "
+         "verbosity)\n"
+         "-w        Use camera white balance, if possible\n"
+         "-a        Average the whole image for white balance\n"
+         "-A <x y w h> Average a grey box for white balance\n"
+         "-r <r g b g> Set custom white balance\n"
+         "+M/-M     Use/don't use an embedded color matrix\n"
+         "-C <r b>  Correct chromatic aberration\n"
+         "-P <file> Fix the dead pixels listed in this file\n"
+         "-K <file> Subtract dark frame (16-bit raw PGM)\n"
+         "-k <num>  Set the darkness level\n"
+         "-S <num>  Set the saturation level\n"
+         "-R <num>  Set raw processing options to num\n"
+         "-n <num>  Set threshold for wavelet denoising\n"
+         "-H [0-9]  Highlight mode (0=clip, 1=unclip, 2=blend, 3+=rebuild)\n"
+         "-t [0-7]  Flip image (0=none, 3=180, 5=90CCW, 6=90CW)\n"
+         "-o [0-6]  Output colorspace (raw,sRGB,Adobe,Wide,ProPhoto,XYZ,ACES)\n"
 #ifndef NO_LCMS
-"-o file   Output ICC profile\n"
-"-p file   Camera input profile (use \'embed\' for embedded profile)\n"
+         "-o file   Output ICC profile\n"
+         "-p file   Camera input profile (use \'embed\' for embedded profile)\n"
 #endif
-"-j        Don't stretch or rotate raw pixels\n"
-"-W        Don't automatically brighten the image\n"
-"-b <num>  Adjust brightness (default = 1.0)\n"
-"-q [0-3]  Set the interpolation quality\n"
-"-h        Half-size color image (twice as fast as \"-q 0\")\n"
-"-f        Interpolate RGGB as four colors\n"
-"-m <num>  Apply a 3x3 median filter to R-G and B-G\n"
-"-s [0..N-1] Select one raw image from input file\n"
-"-4        Write 16-bit linear instead of 8-bit with gamma\n"
-"-g pow ts Set gamma curve to gamma pow and toe slope ts (default = 2.222 4.5)\n"
-"-T        Write TIFF instead of PPM\n"
-#ifndef WIN32
-"-B        Use mmap()-ed buffer instead of plain FILE I/O\n"
+         "-j        Don't stretch or rotate raw pixels\n"
+         "-W        Don't automatically brighten the image\n"
+         "-b <num>  Adjust brightness (default = 1.0)\n"
+         "-q N      Set the interpolation quality:\n"
+         "          0 - linear, 1 - VNG, 2 - PPG, 3 - AHD, 4 - DCB\n"
+         "          11 - DHT, 12 - AAHD\n"
+         "-h        Half-size color image (twice as fast as \"-q 0\")\n"
+         "-f        Interpolate RGGB as four colors\n"
+         "-m <num>  Apply a 3x3 median filter to R-G and B-G\n"
+         "-s [0..N-1] Select one raw image from input file\n"
+         "-4        Linear 16-bit, same as \"-6 -W -g 1 1\n"
+         "-6        Write 16-bit output\n"
+         "-g pow ts Set gamma curve to gamma pow and toe slope ts (default = "
+         "2.222 4.5)\n"
+         "-T        Write TIFF instead of PPM\n"
+         "-G        Use green_matching() filter\n"
+         "-B <x y w h> use cropbox\n"
+         "-F        Use FILE I/O instead of streambuf API\n"
+         "-Z <suf>  Output filename generation rules\n"
+         "          .suf => append .suf to input name, keeping existing suffix "
+         "too\n"
+         "           suf => replace input filename last extension\n"
+         "          - => output to stdout\n"
+         "          filename.suf => output to filename.suf\n"
+         "-timing   Detailed timing report\n"
+         "-fbdd N   0 - disable FBDD noise reduction (default), 1 - light "
+         "FBDD, 2 - full\n"
+         "-dcbi N   Number of extra DCD iterations (default - 0)\n"
+         "-dcbe     DCB color enhance\n"
+         "-aexpo <e p> exposure correction\n"
+         "-apentax4shot enables merge of 4-shot pentax files\n"
+         "-apentax4shotorder 3102 sets pentax 4-shot alignment order\n"
+         "-mmap     Use memory mmaped buffer instead of plain FILE I/O\n"
+         "-mem	   Use memory buffer instead of FILE I/O\n"
+         "-disars   Do not use RawSpeed library\n"
+         "-disinterp Do not run interpolation step\n"
+         "-dsrawrgb1 Disable YCbCr to RGB conversion for sRAW (Cb/Cr "
+         "interpolation enabled)\n"
+         "-dsrawrgb2 Disable YCbCr to RGB conversion for sRAW (Cb/Cr "
+         "interpolation disabled)\n"
+#ifdef USE_DNGSDK
+         "-dngsdk   Use Adobe DNG SDK for DNG decode\n"
+         "-dngflags N set DNG decoding options to value N\n"
 #endif
-        );
-    exit(1);
+  );
+  exit(1);
 }
 
-static int verbosity=0;
+static int verbosity = 0;
+int cnt = 0;
+int my_progress_callback(void *d, enum LibRaw_progress p, int iteration,
+                         int expected)
+{
+  char *passed = (char *)(d ? d : "default string"); // data passed to callback
+                                                     // at set_callback stage
+
+  if (verbosity > 2) // verbosity set by repeat -v switches
+  {
+    printf("CB: %s  pass %d of %d (data passed=%s)\n", libraw_strprogress(p),
+           iteration, expected, passed);
+  }
+  else if (iteration == 0) // 1st iteration of each step
+    printf("Starting %s (expecting %d iterations)\n", libraw_strprogress(p),
+           expected);
+  else if (iteration == expected - 1)
+    printf("%s finished\n", libraw_strprogress(p));
 
-int cnt=0;
-int my_progress_callback(void *d,enum LibRaw_progress p,int iteration, int expected)
+  ///    if(++cnt>10) return 1; // emulate user termination on 10-th callback
+  ///    call
+
+  return 0; // always return 0 to continue processing
+}
+
+// timer
+#ifndef LIBRAW_WIN32_CALLS
+static struct timeval start, end;
+void timerstart(void) { gettimeofday(&start, NULL); }
+void timerprint(const char *msg, const char *filename)
+{
+  gettimeofday(&end, NULL);
+  float msec = (end.tv_sec - start.tv_sec) * 1000.0f +
+               (end.tv_usec - start.tv_usec) / 1000.0f;
+  printf("Timing: %s/%s: %6.3f msec\n", filename, msg, msec);
+}
+#else
+LARGE_INTEGER start;
+void timerstart(void) { QueryPerformanceCounter(&start); }
+void timerprint(const char *msg, const char *filename)
 {
-    char *passed  = (char*)(d?d:"default string"); // data passed to callback at set_callback stage
+  LARGE_INTEGER unit, end;
+  QueryPerformanceCounter(&end);
+  QueryPerformanceFrequency(&unit);
+  float msec = (float)(end.QuadPart - start.QuadPart);
+  msec /= (float)unit.QuadPart / 1000.0f;
+  printf("Timing: %s/%s: %6.3f msec\n", filename, msg, msec);
+}
 
-    if(verbosity>2) // verbosity set by repeat -v switches
-        {
-            printf("CB: %s  pass %d of %d (data passed=%s)\n",libraw_strprogress(p),iteration,expected,passed);
-        }
-    else if (iteration == 0) // 1st iteration of each step
-        printf("Starting %s (expecting %d iterations)\n", libraw_strprogress(p),expected);
-    else if (iteration == expected-1)
-        printf("%s finished\n",libraw_strprogress(p));
+#endif
+
+struct file_mapping
+{
+	void *map;
+	INT64 fsize;
+#ifdef LIBRAW_WIN32_CALLS
+	HANDLE fd, fd_map;
+	file_mapping() : map(0), fsize(0), fd(INVALID_HANDLE_VALUE), fd_map(INVALID_HANDLE_VALUE){}
+#else
+	int  fd;
+	file_mapping() : map(0), fsize(0), fd(-1){}
+#endif
+};
 
-///    if(++cnt>10) return 1; // emulate user termination on 10-th callback call
+void create_mapping(struct file_mapping& data, const std::string& fn)
+{
+#ifdef LIBRAW_WIN32_CALLS
+	std::wstring fpath(fn.begin(), fn.end());
+	if ((data.fd = CreateFileW(fpath.c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)) == INVALID_HANDLE_VALUE) return;
+	LARGE_INTEGER fs;
+	if (!GetFileSizeEx(data.fd, &fs)) return;
+	data.fsize = fs.QuadPart;
+	if ((data.fd_map = ::CreateFileMapping(data.fd, 0, PAGE_READONLY, fs.HighPart, fs.LowPart, 0)) == INVALID_HANDLE_VALUE) return;
+	data.map = MapViewOfFile(data.fd_map, FILE_MAP_READ, 0, 0, data.fsize);
+#else
+	struct stat stt;
+	if ((data.fd = open(fn.c_str(), O_RDONLY)) < 0) return;
+	if (fstat(data.fd, &stt) != 0) return;
+	data.fsize = stt.st_size;
+	data.map = mmap(0, data.fsize, PROT_READ | PROT_WRITE, MAP_PRIVATE, data.fd, 0);
+	return;
+#endif
+}
 
-    return 0; // always return 0 to continue processing
+void close_mapping(struct file_mapping& data)
+{
+#ifdef LIBRAW_WIN32_CALLS
+	if (data.map) UnmapViewOfFile(data.map);
+	if (data.fd_map != INVALID_HANDLE_VALUE) CloseHandle(data.fd_map);
+	if (data.fd != INVALID_HANDLE_VALUE) CloseHandle(data.fd);
+	data.map = 0;
+	data.fsize = 0;
+	data.fd = data.fd_map = INVALID_HANDLE_VALUE;
+#else
+	if (data.map)
+		munmap(data.map, data.fsize);
+	if (data.fd >= 0)
+		close(data.fd);
+	data.map = 0;
+	data.fsize = 0;
+	data.fd = -1;
+#endif
 }
 
 
 int main(int argc, char *argv[])
 {
-    if(argc==1) usage(argv[0]);
-
-    LibRaw RawProcessor;
-    int i,arg,c,ret;
-    char opm,opt,*cp,*sp;
-    int use_mmap=0, msize;
-    void *iobuffer;
+  if (argc == 1)
+    usage(argv[0]);
 
+  LibRaw RawProcessor;
+  int i, arg, c, ret;
+  char opm, opt, *cp, *sp;
+  int use_bigfile = 0, use_timing = 0, use_mem = 0, use_mmap = 0;
+  char *outext = NULL;
+#ifdef USE_DNGSDK
+  dng_host *dnghost = NULL;
+#endif
+  struct file_mapping mapping;
+  void *iobuffer = 0;
+#ifdef OUT
+#undef OUT
+#endif
 #define OUT RawProcessor.imgdata.params
-    
-  argv[argc] = "";
-  for (arg=1; (((opm = argv[arg][0]) - 2) | 2) == '+'; ) 
-      {
-          opt = argv[arg++][1];
-          if ((cp = strchr (sp="nbrkStqmHACgU", opt)))
-              for (i=0; i < "11411111142"[cp-sp]-'0'; i++)
-                  if (!isdigit(argv[arg+i][0])) 
-                      {
-                          fprintf (stderr,"Non-numeric argument to \"-%c\"\n", opt);
-                          return 1;
-                      }
-          switch (opt) 
-              {
-              case 'v':  verbosity++;  break;
-                  
-              case 'U':  OUT.auto_bright_thr   = atof(argv[arg++]);  break;
-              case 'n':  OUT.threshold   = atof(argv[arg++]);  break;
-              case 'b':  OUT.bright      = atof(argv[arg++]);  break;
-              case 'P':  OUT.bad_pixels  = argv[arg++];        break;
-              case 'K':  OUT.dark_frame  = argv[arg++];        break;
-              case 'r':
-                  for(c=0;c<4;c++) 
-                      OUT.user_mul[c] = atof(argv[arg++]);  
-                  break;
-              case 'C':  
-                  OUT.aber[0] = 1 / atof(argv[arg++]);
-                  OUT.aber[2] = 1 / atof(argv[arg++]);  
-                  break;
-              case 'g':  
-                  OUT.gamm[0] = 1 / atof(argv[arg++]);
-                  OUT.gamm[1] =     atof(argv[arg++]);  
-                  break;
-              case 'k':  OUT.user_black  = atoi(argv[arg++]);  break;
-              case 'S':  OUT.user_sat    = atoi(argv[arg++]);  break;
-              case 't':  OUT.user_flip   = atoi(argv[arg++]);  break;
-              case 'q':  OUT.user_qual   = atoi(argv[arg++]);  break;
-              case 'm':  OUT.med_passes  = atoi(argv[arg++]);  break;
-              case 'H':  OUT.highlight   = atoi(argv[arg++]);  break;
-              case 's':  OUT.shot_select = abs(atoi(argv[arg++])); break;
-              case 'o':  
-                  if(isdigit(argv[arg+1][0]) && !isdigit(argv[arg+1][1]))
-                          OUT.output_color = atoi(argv[arg++]);
+
+  argv[argc] = (char *)"";
+  for (arg = 1; (((opm = argv[arg][0]) - 2) | 2) == '+';)
+  {
+    char *optstr = argv[arg];
+    opt = argv[arg++][1];
+    if ((cp = strchr(sp = (char *)"cnbrkStqmHABCgU", opt)) != 0)
+      for (i = 0; i < "111411111144221"[cp - sp] - '0'; i++)
+        if (!isdigit(argv[arg + i][0]) && !optstr[2])
+        {
+          fprintf(stderr, "Non-numeric argument to \"-%c\"\n", opt);
+          return 1;
+        }
+    if (!strchr("ftdeam", opt) && argv[arg - 1][2])
+      fprintf(stderr, "Unknown option \"%s\".\n", argv[arg - 1]);
+    switch (opt)
+    {
+    case 'v':
+      verbosity++;
+      break;
+    case 'G':
+      OUT.green_matching = 1;
+      break;
+    case 'c':
+      OUT.adjust_maximum_thr = (float)atof(argv[arg++]);
+      break;
+    case 'U':
+      OUT.auto_bright_thr = (float)atof(argv[arg++]);
+      break;
+    case 'n':
+      OUT.threshold = (float)atof(argv[arg++]);
+      break;
+    case 'b':
+      OUT.bright = (float)atof(argv[arg++]);
+      break;
+    case 'P':
+      OUT.bad_pixels = argv[arg++];
+      break;
+    case 'K':
+      OUT.dark_frame = argv[arg++];
+      break;
+    case 'r':
+      for (c = 0; c < 4; c++)
+        OUT.user_mul[c] = (float)atof(argv[arg++]);
+      break;
+    case 'C':
+      OUT.aber[0] = 1 / atof(argv[arg++]);
+      OUT.aber[2] = 1 / atof(argv[arg++]);
+      break;
+    case 'g':
+      OUT.gamm[0] = 1 / atof(argv[arg++]);
+      OUT.gamm[1] = atof(argv[arg++]);
+      break;
+    case 'k':
+      OUT.user_black = atoi(argv[arg++]);
+      break;
+    case 'S':
+      OUT.user_sat = atoi(argv[arg++]);
+      break;
+    case 'R':
+      OUT.raw_processing_options = atoi(argv[arg++]);
+      break;
+    case 't':
+      if (!strcmp(optstr, "-timing"))
+        use_timing = 1;
+      else if (!argv[arg - 1][2])
+        OUT.user_flip = atoi(argv[arg++]);
+      else
+        fprintf(stderr, "Unknown option \"%s\".\n", argv[arg - 1]);
+      break;
+    case 'q':
+      OUT.user_qual = atoi(argv[arg++]);
+      break;
+    case 'm':
+      if (!strcmp(optstr, "-mmap"))
+        use_mmap = 1;
+      else
+          if (!strcmp(optstr, "-mem"))
+        use_mem = 1;
+      else
+      {
+        if (!argv[arg - 1][2])
+          OUT.med_passes = atoi(argv[arg++]);
+        else
+          fprintf(stderr, "Unknown option \"%s\".\n", argv[arg - 1]);
+      }
+      break;
+    case 'H':
+      OUT.highlight = atoi(argv[arg++]);
+      break;
+    case 's':
+      OUT.shot_select = abs(atoi(argv[arg++]));
+      break;
+    case 'o':
+      if (isdigit(argv[arg][0]) && !isdigit(argv[arg][1]))
+        OUT.output_color = atoi(argv[arg++]);
 #ifndef NO_LCMS
-                   else
-                         OUT.output_profile = argv[arg++];
-                  break;
-              case 'p':  OUT.camera_profile = argv[arg++];
-#endif
-                  break;
-              case 'h':  OUT.half_size         = 1;		
-                  // no break:  "-h" implies "-f" 
-              case 'f':  
-                  OUT.four_color_rgb    = 1;  
-                  break;
-              case 'A':  for(c=0; c<4;c++) OUT.greybox[c]  = atoi(argv[arg++]);
-              case 'a':  OUT.use_auto_wb       = 1;  break;
-              case 'w':  OUT.use_camera_wb     = 1;  break;
-              case 'M':  OUT.use_camera_matrix = (opm == '+');  break;
-              case 'j':  OUT.use_fuji_rotate   = 0;  break;
-              case 'W':  OUT.no_auto_bright    = 1;  break;
-              case 'T':  OUT.output_tiff       = 1;  break;
-              case '4':  OUT.output_bps       = 16;  break;
-              case '1':  OUT.gamma_16bit       = 1;  break;
-#ifndef WIN32
-              case 'B':  use_mmap              = 1;  break;
-#endif
-              default:
-                  fprintf (stderr,"Unknown option \"-%c\".\n", opt);
-                  return 1;
-              }
+      else
+        OUT.output_profile = argv[arg++];
+      break;
+    case 'p':
+      OUT.camera_profile = argv[arg++];
+#endif
+      break;
+    case 'h':
+      OUT.half_size = 1;
+      break;
+    case 'f':
+      if (!strcmp(optstr, "-fbdd"))
+        OUT.fbdd_noiserd = atoi(argv[arg++]);
+      else
+      {
+        if (!argv[arg - 1][2])
+          OUT.four_color_rgb = 1;
+        else
+          fprintf(stderr, "Unknown option \"%s\".\n", argv[arg - 1]);
+      }
+      break;
+    case 'A':
+      for (c = 0; c < 4; c++)
+        OUT.greybox[c] = atoi(argv[arg++]);
+      break;
+    case 'B':
+      for (c = 0; c < 4; c++)
+        OUT.cropbox[c] = atoi(argv[arg++]);
+      break;
+    case 'a':
+      if (!strcmp(optstr, "-aexpo"))
+      {
+        OUT.exp_correc = 1;
+        OUT.exp_shift = (float)atof(argv[arg++]);
+        OUT.exp_preser = (float)atof(argv[arg++]);
+      }
+      else if (!strcmp(optstr, "-apentax4shot"))
+      {
+        OUT.raw_processing_options |= LIBRAW_PROCESSING_PENTAX_PS_ALLFRAMES;
+      }
+      else if (!strcmp(optstr, "-apentax4shotorder"))
+      {
+        strncpy(OUT.p4shot_order, argv[arg++], 5);
+      }
+      else if (!argv[arg - 1][2])
+        OUT.use_auto_wb = 1;
+      else
+        fprintf(stderr, "Unknown option \"%s\".\n", argv[arg - 1]);
+      break;
+    case 'w':
+      OUT.use_camera_wb = 1;
+      break;
+    case 'M':
+      OUT.use_camera_matrix = (opm == '+')?3:0;
+      break;
+    case 'j':
+      OUT.use_fuji_rotate = 0;
+      break;
+    case 'W':
+      OUT.no_auto_bright = 1;
+      break;
+    case 'T':
+      OUT.output_tiff = 1;
+      break;
+    case '4':
+      OUT.gamm[0] = OUT.gamm[1] = OUT.no_auto_bright = 1; /* no break here! */
+    case '6':
+      OUT.output_bps = 16;
+      break;
+    case 'F':
+      use_bigfile = 1;
+      break;
+    case 'Z':
+      outext = strdup(argv[arg++]);
+      break;
+    case 'd':
+      if (!strcmp(optstr, "-dcbi"))
+        OUT.dcb_iterations = atoi(argv[arg++]);
+      else if (!strcmp(optstr, "-disars"))
+        OUT.use_rawspeed = 0;
+      else if (!strcmp(optstr, "-disinterp"))
+        OUT.no_interpolation = 1;
+      else if (!strcmp(optstr, "-dcbe"))
+        OUT.dcb_enhance_fl = 1;
+      else if (!strcmp(optstr, "-dsrawrgb1"))
+      {
+        OUT.raw_processing_options |= LIBRAW_PROCESSING_SRAW_NO_RGB;
+        OUT.raw_processing_options &= ~LIBRAW_PROCESSING_SRAW_NO_INTERPOLATE;
       }
-  putenv ((char*)"TZ=UTC"); // dcraw compatibility, affects TIFF datestamp field
-  OUT.filtering_mode = LIBRAW_FILTERING_AUTOMATIC;
+      else if (!strcmp(optstr, "-dsrawrgb2"))
+      {
+        OUT.raw_processing_options &= ~LIBRAW_PROCESSING_SRAW_NO_RGB;
+        OUT.raw_processing_options |= LIBRAW_PROCESSING_SRAW_NO_INTERPOLATE;
+      }
+#ifdef USE_DNGSDK
+      else if (!strcmp(optstr, "-dngsdk"))
+      {
+        dnghost = new dng_host;
+        RawProcessor.set_dng_host(dnghost);
+      }
+      else if (!strcmp(optstr, "-dngflags"))
+      {
+        OUT.use_dngsdk = atoi(argv[arg++]);
+      }
+#endif
+      else
+        fprintf(stderr, "Unknown option \"%s\".\n", argv[arg - 1]);
+      break;
+    default:
+      fprintf(stderr, "Unknown option \"-%c\".\n", opt);
+      return 1;
+    }
+  }
+#ifndef LIBRAW_WIN32_CALLS
+  putenv((char *)"TZ=UTC"); // dcraw compatibility, affects TIFF datestamp field
+#else
+  _putenv(
+      (char *)"TZ=UTC"); // dcraw compatibility, affects TIFF datestamp field
+#endif
 #define P1 RawProcessor.imgdata.idata
 #define S RawProcessor.imgdata.sizes
 #define C RawProcessor.imgdata.color
 #define T RawProcessor.imgdata.thumbnail
 #define P2 RawProcessor.imgdata.other
 
-  if(verbosity>1)
-          RawProcessor.set_progress_handler(my_progress_callback,(void*)"Sample data passed");
-#ifdef _OPENMP
-  if(verbosity)
-          printf ("Using %d threads\n", omp_get_max_threads());
-#endif
+  if (outext && !strcmp(outext, "-"))
+    use_timing = verbosity = 0;
 
-  for ( ; arg < argc; arg++)
+  if (verbosity > 1)
+    RawProcessor.set_progress_handler(my_progress_callback,
+                                      (void *)"Sample data passed");
+#ifdef LIBRAW_USE_OPENMP
+  if (verbosity)
+    printf("Using %d threads\n", omp_get_max_threads());
+#endif
+
+  for (; arg < argc; arg++)
+  {
+    char outfn[1024];
+
+    if (verbosity)
+      printf("Processing file %s\n", argv[arg]);
+
+    timerstart();
+
+	if (use_mmap)
+	{
+		create_mapping(mapping, argv[arg]);
+		if (!mapping.map)
+		{
+			fprintf(stderr, "Cannot map %s\n", argv[arg]);
+			close_mapping(mapping);
+			continue;
+		}
+      if ((ret = RawProcessor.open_buffer(mapping.map,mapping.fsize) !=
+                 LIBRAW_SUCCESS))
+      {
+        fprintf(stderr, "Cannot open_buffer %s: %s\n", argv[arg], libraw_strerror(ret));
+		close_mapping(mapping);
+        continue; // no recycle b/c open file will recycle itself
+      }
+    }
+    else  if (use_mem)
+    {
+      int file = open(argv[arg], O_RDONLY | O_BINARY);
+      struct stat st;
+      if (file < 0)
+      {
+        fprintf(stderr, "Cannot open %s: %s\n", argv[arg], strerror(errno));
+        continue;
+      }
+      if (fstat(file, &st))
+      {
+        fprintf(stderr, "Cannot stat %s: %s\n", argv[arg], strerror(errno));
+        close(file);
+        continue;
+      }
+      if (!(iobuffer = malloc(st.st_size)))
+      {
+        fprintf(stderr, "Cannot allocate %d kbytes for memory buffer\n",
+                (int)(st.st_size / 1024));
+        close(file);
+        continue;
+      }
+      int rd;
+      if (st.st_size != (rd = read(file, iobuffer, st.st_size)))
+      {
+        fprintf(stderr,
+                "Cannot read %d bytes instead of  %d to memory buffer\n",
+                (int)rd, (int)st.st_size);
+        close(file);
+        free(iobuffer);
+        continue;
+      }
+      close(file);
+      if ((ret = RawProcessor.open_buffer(iobuffer, st.st_size)) !=
+          LIBRAW_SUCCESS)
+      {
+        fprintf(stderr, "Cannot open_buffer %s: %s\n", argv[arg],
+                libraw_strerror(ret));
+        free(iobuffer);
+        continue; // no recycle b/c open file will recycle itself
+      }
+    }
+    else
+    {
+      if (use_bigfile)
+        // force open_file switch to bigfile processing
+        ret = RawProcessor.open_file(argv[arg], 1);
+      else
+        ret = RawProcessor.open_file(argv[arg]);
+
+      if (ret != LIBRAW_SUCCESS)
+      {
+        fprintf(stderr, "Cannot open %s: %s\n", argv[arg],
+                libraw_strerror(ret));
+        continue; // no recycle b/c open_file will recycle itself
+      }
+    }
+
+    if (use_timing)
+      timerprint("LibRaw::open_file()", argv[arg]);
+
+    timerstart();
+    if ((ret = RawProcessor.unpack()) != LIBRAW_SUCCESS)
+    {
+      fprintf(stderr, "Cannot unpack %s: %s\n", argv[arg],
+              libraw_strerror(ret));
+      continue;
+    }
+
+    if (use_timing)
+      timerprint("LibRaw::unpack()", argv[arg]);
+
+    timerstart();
+    if (LIBRAW_SUCCESS != (ret = RawProcessor.dcraw_process()))
+    {
+      fprintf(stderr, "Cannot do postpocessing on %s: %s\n", argv[arg],
+              libraw_strerror(ret));
+      if (LIBRAW_FATAL_ERROR(ret))
+        continue;
+    }
+    if (use_timing)
+      timerprint("LibRaw::dcraw_process()", argv[arg]);
+
+    if (!outext)
+      snprintf(outfn, sizeof(outfn), "%s.%s", argv[arg],
+               OUT.output_tiff ? "tiff" : (P1.colors > 1 ? "ppm" : "pgm"));
+    else if (!strcmp(outext, "-"))
+      snprintf(outfn, sizeof(outfn), "-");
+    else
+    {
+      if (*outext == '.') // append
+        snprintf(outfn, sizeof(outfn), "%s%s", argv[arg], outext);
+      else if (strchr(outext, '.') && *outext != '.') // dot is not 1st char
+        strncpy(outfn, outext, sizeof(outfn));
+      else
+      {
+        strncpy(outfn, argv[arg], sizeof(outfn));
+        if (strlen(outfn) > 0)
         {
-            char outfn[1024];
+          char *lastchar = outfn + strlen(outfn); // points to term 0
+          while (--lastchar > outfn)
+          {
+            if (*lastchar == '/' || *lastchar == '\\')
+              break;
+            if (*lastchar == '.')
+            {
+              *lastchar = 0;
+              break;
+            };
+          }
+        }
+        strncat(outfn, ".", sizeof(outfn) - strlen(outfn) - 1);
+        strncat(outfn, outext, sizeof(outfn) - strlen(outfn) - 1);
+      }
+    }
 
-            if(verbosity) printf("Processing file %s\n",argv[arg]);
-#ifndef WIN32
-            if(use_mmap)
-                {
-                    int file = open(argv[arg],O_RDONLY);
-                    struct stat st;
-                    if(file<0)
-                        {
-                            fprintf(stderr,"Cannot open %s: %s\n",argv[arg],strerror(errno));
-                            continue;
-                        }
-                    if(fstat(file,&st))
-                        {
-                            fprintf(stderr,"Cannot stat %s: %s\n",argv[arg],strerror(errno));
-                            close(file);
-                            continue;
-                        }
-                    int pgsz = getpagesize();
-                    msize = ((st.st_size+pgsz-1)/pgsz)*pgsz;
-                    iobuffer = mmap(NULL,msize,PROT_READ,MAP_PRIVATE,file,0);
-                    if(!iobuffer)
-                        {
-                            fprintf(stderr,"Cannot mmap %s: %s\n",argv[arg],strerror(errno));
-                            close(file);
-                            continue;
-                        }
-                    close(file);
-                    if( (ret = RawProcessor.open_buffer(iobuffer,st.st_size) != LIBRAW_SUCCESS))
-                        {
-                            fprintf(stderr,"Cannot open_buffer %s: %s\n",argv[arg],libraw_strerror(ret));
-                            continue; // no recycle b/c open file will recycle itself
-                        }
-
-                }
-            else
-#endif
-                {
-                    if( (ret = RawProcessor.open_file(argv[arg])) != LIBRAW_SUCCESS)
-                        {
-                            fprintf(stderr,"Cannot open %s: %s\n",argv[arg],libraw_strerror(ret));
-                            continue; // no recycle b/c open_file will recycle itself
-                        }
-                }
-            if( (ret = RawProcessor.unpack() ) != LIBRAW_SUCCESS)
-                {
-                    fprintf(stderr,"Cannot unpack %s: %s\n",argv[arg],libraw_strerror(ret));
-                    continue;
-                }
-            if (LIBRAW_SUCCESS != (ret = RawProcessor.dcraw_process()))
-                {
-                    fprintf(stderr,"Cannot do postpocessing on %s: %s\n",argv[arg],libraw_strerror(ret));
-                    if(LIBRAW_FATAL_ERROR(ret))
-                        continue; 
-                }
-            snprintf(outfn,sizeof(outfn),
-                     "%s.%s",
-                     argv[arg], OUT.output_tiff ? "tiff" : (P1.colors>1?"ppm":"pgm"));
-
-            if(verbosity) printf("Writing file %s\n",outfn);
-
-            if( LIBRAW_SUCCESS != (ret = RawProcessor.dcraw_ppm_tiff_writer(outfn)))
-                fprintf(stderr,"Cannot write %s: %s\n",outfn,libraw_strerror(ret));
-
-#ifndef WIN32
-            if(use_mmap && iobuffer)
-                {
-                    munmap(iobuffer,msize);
-                    iobuffer=0;
-                }
+    if (verbosity)
+    {
+      printf("Writing file %s\n", outfn);
+    }
+
+    if (LIBRAW_SUCCESS != (ret = RawProcessor.dcraw_ppm_tiff_writer(outfn)))
+      fprintf(stderr, "Cannot write %s: %s\n", outfn, libraw_strerror(ret));
+
+	RawProcessor.recycle(); // just for show this call
+
+	if (use_mmap && mapping.map)
+		close_mapping(mapping);
+    else if (use_mem && iobuffer)
+    {
+      free(iobuffer);
+      iobuffer = 0;
+    }
+  }
+#ifdef USE_DNGSDK
+  if (dnghost)
+    delete dnghost;
 #endif
-            
-            RawProcessor.recycle(); // just for show this call
-        }
-    return 0;
+  return 0;
 }
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/samples/dcraw_half.c libkdcraw/libkdcraw/libraw/samples/dcraw_half.c
--- libkdcraw-wrk/libkdcraw/libraw/samples/dcraw_half.c	2022-11-07 08:15:53.614821808 +0300
+++ libkdcraw/libkdcraw/libraw/samples/dcraw_half.c	2022-11-07 07:46:31.730795008 +0300
@@ -1,24 +1,20 @@
-/*
+/* -*- C++ -*-
  * File: dcraw_half.c
- * Copyright 2008-2009 Alex Tutubalin <lexa@lexa.ru>
+ * Copyright 2008-2020 LibRaw LLC (info@libraw.org)
  * Created: Sat Mar  8 , 2008
  *
- * LibRaw  C API sample  (emulates call to "dcraw  -h")
+ * LibRaw  C API sample:  emulates "dcraw  -h"
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+
  */
 #include <stdio.h>
 #include <string.h>
@@ -27,57 +23,56 @@
 
 #include "libraw/libraw.h"
 
-
-#define HANDLE_FATAL_ERROR(ret)\
-    if(ret)\
-        {\
-            fprintf(stderr,"%s: libraw  %s\n",av[i],libraw_strerror(ret));\
-            if(LIBRAW_FATAL_ERROR(ret))\
-                exit(1);               \
-        }\
-
-#define HANDLE_ALL_ERRORS(ret)\
-    if(ret)\
-        {\
-            fprintf(stderr,"%s: libraw  %s\n",av[i],libraw_strerror(ret));\
-            continue;                                                   \
-        }\
-
+#define HANDLE_FATAL_ERROR(ret)                                                \
+  if (ret)                                                                     \
+  {                                                                            \
+    fprintf(stderr, "%s: libraw  %s\n", av[i], libraw_strerror(ret));          \
+    if (LIBRAW_FATAL_ERROR(ret))                                               \
+      exit(1);                                                                 \
+  }
+
+#define HANDLE_ALL_ERRORS(ret)                                                 \
+  if (ret)                                                                     \
+  {                                                                            \
+    fprintf(stderr, "%s: libraw  %s\n", av[i], libraw_strerror(ret));          \
+    continue;                                                                  \
+  }
 
 int main(int ac, char *av[])
 {
-    int i;
-    libraw_data_t *iprc = libraw_init(0);
-    
-    if(!iprc)
-        {
-            fprintf(stderr,"Cannot create libraw handle\n");
-            exit(1);
-        }
-
-    iprc->params.half_size = 1; /* dcraw -h */
-	
-    for (i=1;i<ac;i++)
-        {
-            char outfn[1024];
-			int ret = libraw_open_file(iprc,av[i]);
-            HANDLE_ALL_ERRORS(ret);
-            
-            printf("Processing %s (%s %s)\n",av[i],iprc->idata.make,iprc->idata.model);
-
-            ret = libraw_unpack(iprc);
-            HANDLE_ALL_ERRORS(ret);
-
-            ret = libraw_dcraw_process(iprc);
-            HANDLE_ALL_ERRORS(ret);
-            
-            strcpy(outfn,av[i]);
-            strcat(outfn,".ppm");
-            printf("Writing to %s\n",outfn);
-
-            ret = libraw_dcraw_ppm_tiff_writer(iprc,outfn);
-            HANDLE_FATAL_ERROR(ret);
-        }
-    libraw_close(iprc);
-    return 0;
+  int i;
+  libraw_data_t *iprc = libraw_init(0);
+
+  if (!iprc)
+  {
+    fprintf(stderr, "Cannot create libraw handle\n");
+    exit(1);
+  }
+
+  iprc->params.half_size = 1; /* dcraw -h */
+
+  for (i = 1; i < ac; i++)
+  {
+    char outfn[1024];
+    int ret = libraw_open_file(iprc, av[i]);
+    HANDLE_ALL_ERRORS(ret);
+
+    printf("Processing %s (%s %s)\n", av[i], iprc->idata.make,
+           iprc->idata.model);
+
+    ret = libraw_unpack(iprc);
+    HANDLE_ALL_ERRORS(ret);
+
+    ret = libraw_dcraw_process(iprc);
+    HANDLE_ALL_ERRORS(ret);
+
+    strcpy(outfn, av[i]);
+    strcat(outfn, ".ppm");
+    printf("Writing to %s\n", outfn);
+
+    ret = libraw_dcraw_ppm_tiff_writer(iprc, outfn);
+    HANDLE_FATAL_ERROR(ret);
+  }
+  libraw_close(iprc);
+  return 0;
 }
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/samples/half_mt.c libkdcraw/libkdcraw/libraw/samples/half_mt.c
--- libkdcraw-wrk/libkdcraw/libraw/samples/half_mt.c	2022-11-07 08:15:53.614821808 +0300
+++ libkdcraw/libkdcraw/libraw/samples/half_mt.c	2022-11-07 07:46:31.730795008 +0300
@@ -1,24 +1,22 @@
-/*
+/* -*- C++ -*-
  * File: halt_mt.c
- * Copyright 2008-2009 Alex Tutubalin <lexa@lexa.ru>
- * Created: Sat Mar  8 , 2008
+ * Copyright 2008-2020 LibRaw LLC (info@libraw.org)
+ * Created: Sat Mar  8, 2008
  *
- * LibRaw  C API mutithreaded sample  (emulates call to "dcraw  -h [-w] [-a] [-v]")
+ * LibRaw  C API mutithreaded sample: emulates call to "dcraw  -h [-w] [-a]
+[-v]"
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
+
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+
  */
 #include <stdio.h>
 #include <string.h>
@@ -28,151 +26,153 @@
 
 #include "libraw/libraw.h"
 
-#define HANDLE_ERRORS(ret) do {                                 \
-    if(ret)                                                     \
-        {                                                       \
-            fprintf(stderr,"%s: %s\n",fn,libraw_strerror(ret)); \
-            if(LIBRAW_FATAL_ERROR(ret))                         \
-                {                                               \
-                    libraw_close(iprc);                         \
-                    return -1;                                   \
-                }                                               \
-        }                                                       \
-    }while(0)
-
+#define HANDLE_ERRORS(ret)                                                     \
+  do                                                                           \
+  {                                                                            \
+    if (ret)                                                                   \
+    {                                                                          \
+      fprintf(stderr, "%s: %s\n", fn, libraw_strerror(ret));                   \
+      if (LIBRAW_FATAL_ERROR(ret))                                             \
+      {                                                                        \
+        libraw_close(iprc);                                                    \
+        return NULL;                                                           \
+      }                                                                        \
+    }                                                                          \
+  } while (0)
 
-// global settings
-int verbose=0,use_camera_wb=0,use_auto_wb=0,tiff_mode=0;
+int verbose = 0, use_camera_wb = 0, use_auto_wb = 0, tiff_mode = 0;
 
-// global file queue
 pthread_mutex_t qm;
-char **queue=NULL;
-size_t qsize=0,qptr=0;
+char **queue = NULL;
+size_t qsize = 0, qptr = 0;
 
 char *get_next_file()
 {
-    char *ret;
-    if(!queue) return NULL;
-    if(qptr>=qsize) return NULL;
-    pthread_mutex_lock(&qm);
-    ret = queue[qptr++];
-    pthread_mutex_unlock(&qm);
-    return ret;
+  char *ret;
+  if (!queue)
+    return NULL;
+  if (qptr >= qsize)
+    return NULL;
+  pthread_mutex_lock(&qm);
+  ret = queue[qptr++];
+  pthread_mutex_unlock(&qm);
+  return ret;
 }
 
-
-// thread routine
-int process_files(void *q)
+void *process_files(void *q)
 {
-    int ret;
-    int count=0;
-    char outfn[1024], *fn;
-    libraw_data_t *iprc = libraw_init(0);
-
-    if(!iprc)
-        {
-            fprintf(stderr,"Cannot create libraw handle\n");
-            return -1;
-        }
-
-    while((fn = get_next_file()))
-        {
-
-            iprc->params.half_size = 1; /* dcraw -h */
-            iprc->params.use_camera_wb = use_camera_wb;
-            iprc->params.use_auto_wb = use_auto_wb;
-            iprc->params.output_tiff = tiff_mode;
-            
-            ret = libraw_open_file(iprc,fn);
-            if(verbose) fprintf(stderr,"%s: %s/%s\n",fn,iprc->idata.make,iprc->idata.model);
-            HANDLE_ERRORS(ret);
-
-            ret = libraw_unpack(iprc);
-            HANDLE_ERRORS(ret);
-            
-            ret = libraw_dcraw_process(iprc);
-            HANDLE_ERRORS(ret);
-            
-            snprintf(outfn,1023,"%s.ppm",fn);
-
-            if(verbose) fprintf(stderr,"Writing file %s\n",outfn);
-            ret = libraw_dcraw_ppm_tiff_writer(iprc,outfn);
-            HANDLE_ERRORS(ret);
-            count++;
-        }
-    libraw_close(iprc);
-    return count;
+  int ret;
+  int count = 0;
+  char outfn[1024], *fn;
+  libraw_data_t *iprc = libraw_init(0);
+
+  if (!iprc)
+  {
+    fprintf(stderr, "Cannot create libraw handle\n");
+    return NULL;
+  }
+
+  while ((fn = get_next_file()))
+  {
+
+    iprc->params.half_size = 1; /* dcraw -h */
+    iprc->params.use_camera_wb = use_camera_wb;
+    iprc->params.use_auto_wb = use_auto_wb;
+    iprc->params.output_tiff = tiff_mode;
+
+    ret = libraw_open_file(iprc, fn);
+    if (verbose)
+      fprintf(stderr, "%s: %s/%s\n", fn, iprc->idata.make, iprc->idata.model);
+    HANDLE_ERRORS(ret);
+
+    ret = libraw_unpack(iprc);
+    HANDLE_ERRORS(ret);
+
+    ret = libraw_dcraw_process(iprc);
+    HANDLE_ERRORS(ret);
+
+    snprintf(outfn, 1023, "%s.%s", fn, tiff_mode ? "tiff" : "ppm");
+
+    if (verbose)
+      fprintf(stderr, "Writing file %s\n", outfn);
+    ret = libraw_dcraw_ppm_tiff_writer(iprc, outfn);
+    HANDLE_ERRORS(ret);
+    count++;
+  }
+  libraw_close(iprc);
+  return NULL;
 }
 
-void usage(const char*p)
+void usage(const char *p)
 {
-    printf("%s: Multi-threaded LibRaw sample app. Emulates dcraw -h [-w] [-a]\n",p);
-    printf(
-        "Options:\n"
-        "-J n  - set parrallel job coun (default 2)\n"
-        "-v    - verbose\n"
-        "-w    - use camera white balance\n"
-        "-a    - average image for white balance\n");
-    exit(1);
+  printf("%s: Multi-threaded LibRaw sample app. Emulates dcraw -h [-w] [-a]\n",
+         p);
+  printf("Options:\n"
+         "-J n  - set parallel job count (default 2)\n"
+         "-v    - verbose\n"
+         "-w    - use camera white balance\n"
+         "-a    - average image for white balance\n");
+  exit(1);
 }
 
 int show_files(void *q)
 {
-    char *p;
-    int cnt = 0;
-    while(p = get_next_file())
-        {
-            printf("%s\n",p);
-            cnt++;
-        }
-    return cnt;
-
+  char *p;
+  int cnt = 0;
+  while ((p = get_next_file()))
+  {
+    printf("%s\n", p);
+    cnt++;
+  }
+  return cnt;
 }
 
 int main(int ac, char *av[])
 {
-    int i, thread_count,max_threads = 2;
-    pthread_t *threads;
-    if(ac<2)
-        usage(av[0]);
-
-    queue = calloc(ac-1,sizeof(queue[0]));
-
-    for (i=1;i<ac;i++)
+  int i, max_threads = 2;
+  pthread_t *threads;
+  if (ac < 2)
+    usage(av[0]);
+
+  queue = calloc(ac - 1, sizeof(queue[0]));
+
+  for (i = 1; i < ac; i++)
+  {
+    if (av[i][0] == '-')
+    {
+      if (av[i][1] == 'w')
+        use_camera_wb = 1;
+      if (av[i][1] == 'a')
+        use_auto_wb = 1;
+      if (av[i][1] == 'v')
+        verbose = 1;
+      if (av[i][1] == 'T')
+        tiff_mode = 1;
+      if (av[i][1] == 'J')
+      {
+        max_threads = atoi(av[++i]);
+        if (max_threads < 1)
         {
-            if(av[i][0]=='-')
-                {
-                    if(av[i][1]=='w') use_camera_wb = 1;
-                    if(av[i][1]=='a') use_auto_wb = 1;
-                    if(av[i][1]=='v') verbose = 1;
-                    if(av[i][1]=='T') tiff_mode = 1;
-                    if(av[i][1]=='J')
-                        {
-                            max_threads=atoi(av[++i]);
-                            if(max_threads<1)
-                                {
-                                    fprintf(stderr,"Job count should be at least 1\n");
-                                    exit(1);
-                                }
-                        }
-                }
-            else
-                queue[qsize++] = av[i];
+          fprintf(stderr, "Job count should be at least 1\n");
+          exit(1);
         }
-    pthread_mutex_init(&qm,NULL);
-    threads = calloc(max_threads,sizeof(threads[0]));
-    for(i=0;i<max_threads;i++)
-        pthread_create(&threads[i],NULL,process_files,NULL);
-    for(i=0;i<max_threads;i++)
-        {
-            int *iptr;
-            if(threads[i])
-                {
-                    pthread_join(threads[i],&iptr);
-                    if(iptr)
-                        printf("Thread %d : %d files\n",i,(int)iptr);
-                }
-        }
-            
-    return 0;
+      }
+    }
+    else
+      queue[qsize++] = av[i];
+  }
+  pthread_mutex_init(&qm, NULL);
+  threads = calloc(max_threads, sizeof(threads[0]));
+  for (i = 0; i < max_threads; i++)
+    pthread_create(&threads[i], NULL, process_files, NULL);
+  for (i = 0; i < max_threads; i++)
+  {
+    int *iptr;
+    if (threads[i])
+    {
+      pthread_join(threads[i], (void *)&iptr);
+    }
+  }
+
+  return 0;
 }
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/samples/half_mt_win32.c libkdcraw/libkdcraw/libraw/samples/half_mt_win32.c
--- libkdcraw-wrk/libkdcraw/libraw/samples/half_mt_win32.c	2022-11-07 08:15:53.614821808 +0300
+++ libkdcraw/libkdcraw/libraw/samples/half_mt_win32.c	2022-11-07 07:46:31.730795008 +0300
@@ -1,25 +1,22 @@
-/*
+/* -*- C++ -*-
  * File: halt_mt_win32.c
- * Copyright 2008-2009 Alex Tutubalin <lexa@lexa.ru>
- * Created: Sat Mar  8 , 2008
+ * Copyright 2008-2020 LibRaw LLC (info@libraw.org)
+ * Created: Sat Mar  8, 2008
  *
- * LibRaw  C API mutithreaded sample  (emulates call to "dcraw  -h [-w] [-a] [-v]")
+ * LibRaw  C API mutithreaded sample:  emulates call to "dcraw  -h [-w] [-a]
+[-v]"
  * Win32 version
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
+
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+
  */
 #include <stdio.h>
 #include <string.h>
@@ -28,185 +25,188 @@
 #include <windows.h>
 #include "libraw/libraw.h"
 
-#ifdef WIN32
+#ifdef LIBRAW_WIN32_CALLS
 #define snprintf _snprintf
 #endif
 
-
-#define HANDLE_ERRORS(ret) do {                                 \
-    if(ret)                                                     \
-        {                                                       \
-            fprintf(stderr,"%s: %s\n",fn,libraw_strerror(ret)); \
-            if(LIBRAW_FATAL_ERROR(ret))                         \
-                {                                               \
-                    libraw_close(iprc);                         \
-                    return -1;                                   \
-                }                                               \
-        }                                                       \
-    }while(0)
-
+#define HANDLE_ERRORS(ret)                                                     \
+  do                                                                           \
+  {                                                                            \
+    if (ret)                                                                   \
+    {                                                                          \
+      fprintf(stderr, "%s: %s\n", fn, libraw_strerror(ret));                   \
+      if (LIBRAW_FATAL_ERROR(ret))                                             \
+      {                                                                        \
+        libraw_close(iprc);                                                    \
+        return -1;                                                             \
+      }                                                                        \
+    }                                                                          \
+  } while (0)
 
 // global settings
-int verbose=0,use_camera_wb=0,use_auto_wb=0,tiff_mode=0;
+int verbose = 0, use_camera_wb = 0, use_auto_wb = 0, tiff_mode = 0;
 
 // global file queue
 HANDLE qmutex;
-char **queue=NULL;
-size_t qsize=0,qptr=0;
+char **queue = NULL;
+size_t qsize = 0, qptr = 0;
 
 char *get_next_file()
 {
-    char *ret;
-	DWORD dwWaitResult;
-    if(!queue) return NULL;
-    if(qptr>=qsize) return NULL;
-
-	dwWaitResult = WaitForSingleObject( 
-            qmutex,    // handle to mutex
-            INFINITE);  // no time-out interval
-	switch (dwWaitResult) 
-        {
-            // The thread got ownership of the mutex
-            case WAIT_OBJECT_0:     
-				ret = queue[qptr++];
-				ReleaseMutex(qmutex);
-				break;
-			case WAIT_ABANDONED: 
-                return NULL; // cannot obtain the lock
-	};
-    return ret;
+  char *ret;
+  DWORD dwWaitResult;
+  if (!queue)
+    return NULL;
+  if (qptr >= qsize)
+    return NULL;
+
+  dwWaitResult = WaitForSingleObject(qmutex,    // handle to mutex
+                                     INFINITE); // no time-out interval
+  switch (dwWaitResult)
+  {
+  // The thread got ownership of the mutex
+  case WAIT_OBJECT_0:
+    ret = queue[qptr++];
+    ReleaseMutex(qmutex);
+    break;
+  case WAIT_ABANDONED:
+    return NULL; // cannot obtain the lock
+  };
+  return ret;
 }
 
-
 // thread routine
 int process_files(void *q)
 {
-    int ret;
-    int count=0;
-    char outfn[1024], *fn;
-    libraw_data_t *iprc = libraw_init(0);
-
-    if(!iprc)
-        {
-            fprintf(stderr,"Cannot create libraw handle\n");
-            return -1;
-        }
-
-    while((fn = get_next_file()))
-        {
-
-            iprc->params.half_size = 1; /* dcraw -h */
-            iprc->params.use_camera_wb = use_camera_wb;
-            iprc->params.use_auto_wb = use_auto_wb;
-            iprc->params.output_tiff = tiff_mode;
-            
-            ret = libraw_open_file(iprc,fn);
-            if(verbose) fprintf(stderr,"%s: %s/%s\n",fn,iprc->idata.make,iprc->idata.model);
-            HANDLE_ERRORS(ret);
-
-            ret = libraw_unpack(iprc);
-            HANDLE_ERRORS(ret);
-            
-            ret = libraw_dcraw_process(iprc);
-            HANDLE_ERRORS(ret);
-            
-            snprintf(outfn,1023,"%s.%s",fn,tiff_mode?"tif":"ppm");
-
-            if(verbose) fprintf(stderr,"Writing file %s\n",outfn);
-            ret = libraw_dcraw_ppm_tiff_writer(iprc,outfn);
-            HANDLE_ERRORS(ret);
-            count++;
-        }
-    libraw_close(iprc);
-	printf("Processed %d files\n",count);
-    return 0;
+  int ret;
+  int count = 0;
+  char outfn[1024], *fn;
+  libraw_data_t *iprc = libraw_init(0);
+
+  if (!iprc)
+  {
+    fprintf(stderr, "Cannot create libraw handle\n");
+    return -1;
+  }
+
+  while ((fn = get_next_file()))
+  {
+
+    iprc->params.half_size = 1; /* dcraw -h */
+    iprc->params.use_camera_wb = use_camera_wb;
+    iprc->params.use_auto_wb = use_auto_wb;
+    iprc->params.output_tiff = tiff_mode;
+
+    ret = libraw_open_file(iprc, fn);
+    if (verbose)
+      fprintf(stderr, "%s: %s/%s\n", fn, iprc->idata.make, iprc->idata.model);
+    HANDLE_ERRORS(ret);
+
+    ret = libraw_unpack(iprc);
+    HANDLE_ERRORS(ret);
+
+    ret = libraw_dcraw_process(iprc);
+    HANDLE_ERRORS(ret);
+
+    snprintf(outfn, 1023, "%s.%s", fn, tiff_mode ? "tif" : "ppm");
+
+    if (verbose)
+      fprintf(stderr, "Writing file %s\n", outfn);
+    ret = libraw_dcraw_ppm_tiff_writer(iprc, outfn);
+    HANDLE_ERRORS(ret);
+    count++;
+  }
+  libraw_close(iprc);
+  printf("Processed %d files\n", count);
+  return 0;
 }
 
-void usage(const char*p)
+void usage(const char *p)
 {
-    printf(
-        "Options:\n"
-        "-J n  - set parrallel job coun (default 2)\n"
-        "-v    - verbose\n"
-        "-w    - use camera white balance\n"
-        "-T    - output TIFF instead of PPM\n"
-        "-a    - average image for white balance\n");
-    exit(1);
+  printf("Options:\n"
+         "-J n  - set parallel job count (default 2)\n"
+         "-v    - verbose\n"
+         "-w    - use camera white balance\n"
+         "-T    - output TIFF instead of PPM\n"
+         "-a    - average image for white balance\n");
+  exit(1);
 }
 
 int show_files(void *q)
 {
-    char *p;
-    int cnt = 0;
-    while(p = get_next_file())
-        {
-            printf("%s\n",p);
-            cnt++;
-        }
-    return cnt;
-
+  char *p;
+  int cnt = 0;
+  while (p = get_next_file())
+  {
+    printf("%s\n", p);
+    cnt++;
+  }
+  return cnt;
 }
 
 int main(int ac, char *av[])
 {
-    int i,max_threads = 2;
-    HANDLE *threads;
-	DWORD   ThreadID;
+  int i, max_threads = 2;
+  HANDLE *threads;
+  DWORD ThreadID;
+
+  if (ac < 2)
+    usage(av[0]);
+
+  queue = calloc(ac - 1, sizeof(queue[0]));
+
+  for (i = 1; i < ac; i++)
+  {
+    if (av[i][0] == '-')
+    {
+      if (av[i][1] == 'w')
+        use_camera_wb = 1;
+      if (av[i][1] == 'a')
+        use_auto_wb = 1;
+      if (av[i][1] == 'v')
+        verbose = 1;
+      if (av[i][1] == 'T')
+        tiff_mode = 1;
+      if (av[i][1] == 'J')
+      {
+        max_threads = atoi(av[++i]);
+        if (max_threads < 1)
+        {
+          fprintf(stderr, "Job count should be at least 1\n");
+          exit(1);
+        }
+      }
+    }
+    else
+      queue[qsize++] = av[i];
+  }
+  qmutex = CreateMutex(NULL, FALSE, NULL);
+  threads = calloc(max_threads, sizeof(threads[0]));
+  for (i = 0; i < max_threads; i++)
+  {
+
+    if (NULL ==
+        (threads[i] = CreateThread(NULL, // default security attributes
+                                   0,    // default stack size
+                                   (LPTHREAD_START_ROUTINE)process_files,
+                                   NULL,      // no thread function arguments
+                                   0,         // default creation flags
+                                   &ThreadID) // receive thread identifier
+         ))
+    {
+      printf("CreateThread error: %d\n", GetLastError());
+      return 1;
+    }
+  }
+
+  WaitForMultipleObjects(max_threads, threads, TRUE, INFINITE);
 
-    if(ac<2)
-        usage(av[0]);
+  // Close thread and mutex handles
 
-    queue = calloc(ac-1,sizeof(queue[0]));
+  for (i = 0; i < max_threads; i++)
+    CloseHandle(threads[i]);
 
-    for (i=1;i<ac;i++)
-        {
-            if(av[i][0]=='-')
-                {
-                    if(av[i][1]=='w') use_camera_wb = 1;
-                    if(av[i][1]=='a') use_auto_wb = 1;
-                    if(av[i][1]=='v') verbose = 1;
-                    if(av[i][1]=='T') tiff_mode = 1;
-                    if(av[i][1]=='J')
-                        {
-                            max_threads=atoi(av[++i]);
-                            if(max_threads<1)
-                                {
-                                    fprintf(stderr,"Job count should be at least 1\n");
-                                    exit(1);
-                                }
-                        }
-                }
-            else
-                queue[qsize++] = av[i];
-        }
-    qmutex = CreateMutex(NULL,FALSE,NULL);	
-    threads = calloc(max_threads,sizeof(threads[0]));
-    for(i=0;i<max_threads;i++)
-	{
-
-		if (NULL == (threads[i] = CreateThread( 
-                     NULL,       // default security attributes
-                     0,          // default stack size
-                     (LPTHREAD_START_ROUTINE) process_files, 
-                     NULL,       // no thread function arguments
-                     0,          // default creation flags
-                     &ThreadID) // receive thread identifier
-					 )
-			)
-        {
-            printf("CreateThread error: %d\n", GetLastError());
-            return 1;
-        }
-	}
-        
-	WaitForMultipleObjects(max_threads, threads, TRUE, INFINITE);
-
-    // Close thread and mutex handles
-
-    for( i=0; i < max_threads; i++ )
-        CloseHandle(threads[i]);
-
-    CloseHandle(qmutex);
-           
-    return 0;
+  CloseHandle(qmutex);
+
+  return 0;
 }
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/samples/identify.cpp libkdcraw/libkdcraw/libraw/samples/identify.cpp
--- libkdcraw-wrk/libkdcraw/libraw/samples/identify.cpp	2022-11-07 08:15:53.614821808 +0300
+++ libkdcraw/libkdcraw/libraw/samples/identify.cpp	1970-01-01 03:00:00.000000000 +0300
@@ -1,147 +0,0 @@
-/*
- * File: identify.cpp
- * Copyright 2008-2009 Alex Tutubalin <lexa@lexa.ru>
- * Created: Sat Mar  8 , 2008
- *
- * LibRaw C++ demo (emulates dcraw -i [-v])
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <math.h>
-#include <time.h>
-
-#include "libraw/libraw.h"
-
-#ifdef WIN32
-#define snprintf _snprintf
-#endif
-
-
-int main(int ac, char *av[])
-{
-    int verbose = 0, ret,print_unpack=0,print_frame=0;
-    LibRaw MyCoolRawProcessor;
-
-    for (int i=1;i<ac;i++) {
-        if(av[i][0]=='-')
-            {
-                if(av[i][1]=='v' && av[i][2]==0) verbose++;
-                if(av[i][1]=='u' && av[i][2]==0) print_unpack++;
-                if(av[i][1]=='f' && av[i][2]==0) print_frame++;
-                continue;
-            }
-        if( (ret = MyCoolRawProcessor.open_file(av[i])) != LIBRAW_SUCCESS)
-            {
-                printf("Cannot decode %s: %s\n",av[i],libraw_strerror(ret));
-                continue; // no recycle, open_file will recycle
-            }
-        if(verbose) {
-
-#define P1 MyCoolRawProcessor.imgdata.idata
-#define P2 MyCoolRawProcessor.imgdata.other
-
-#define S MyCoolRawProcessor.imgdata.sizes
-#define O MyCoolRawProcessor.imgdata.params
-#define C MyCoolRawProcessor.imgdata.color
-#define T MyCoolRawProcessor.imgdata.thumbnail
-
-
-            if( (ret =  MyCoolRawProcessor.adjust_sizes_info_only()))
-            {
-                printf("Cannot decode %s: %s\n",av[i],libraw_strerror(ret));
-                continue; // no recycle, open_file will recycle
-            }
-            
-            printf ("\nFilename: %s\n", av[i]);
-            printf ("Timestamp: %s", ctime(&(P2.timestamp)));
-            printf ("Camera: %s %s\n", P1.make, P1.model);
-            if (P2.artist[0])
-                printf ("Owner: %s\n", P2.artist);
-            if (P1.dng_version) {
-                printf ("DNG Version: ");
-                for (int i=24; i >= 0; i -= 8)
-                    printf ("%d%c", P1.dng_version >> i & 255, i ? '.':'\n');
-            }
-            printf ("ISO speed: %d\n", (int) P2.iso_speed);
-            printf ("Shutter: ");
-            if (P2.shutter > 0 && P2.shutter < 1)
-                P2.shutter = (printf ("1/"), 1 / P2.shutter);
-            printf ("%0.1f sec\n", P2.shutter);
-            printf ("Aperture: f/%0.1f\n", P2.aperture);
-            printf ("Focal length: %0.1f mm\n", P2.focal_len);
-            if(C.profile)
-                printf ("Embedded ICC profile: yes, %d bytes\n", C.profile_length);
-            else
-                printf ("Embedded ICC profile: no\n", C.profile_length);
-                
-            printf ("Number of raw images: %d\n", P1.raw_count);
-            if (S.pixel_aspect != 1)
-                printf ("Pixel Aspect Ratio: %0.6f\n", S.pixel_aspect);
-            if (T.tlength)
-                printf ("Thumb size:  %4d x %d\n", T.twidth, T.theight);
-            printf ("Full size:   %4d x %d\n", S.raw_width, S.raw_height);
-            
-            printf ("Image size:  %4d x %d\n", S.width, S.height);
-            printf ("Output size: %4d x %d\n", S.iwidth, S.iheight);
-            printf ("Raw colors: %d", P1.colors);
-            if (P1.filters) 
-                {
-                    printf ("\nFilter pattern: ");
-                    if (!P1.cdesc[3]) P1.cdesc[3] = 'G';
-                    for (int i=0; i < 16; i++)
-                        putchar (P1.cdesc[MyCoolRawProcessor.fc(i >> 1,i & 1)]);
-                }
-            printf ("\nDaylight multipliers:");
-            for(int c=0;c<P1.colors;c++) printf (" %f", C.pre_mul[c]);
-            if (C.cam_mul[0] > 0) 
-                {
-                    printf ("\nCamera multipliers:");
-                    for(int c=0;c<4;c++) printf (" %f", C.cam_mul[c]);
-                }
-            char *csl[] = {"U","I","CO","L","CA"};
-            printf("\nColor sources /Legend: (U)nknown, (I)nit, (CO)nstant, (L)oaded, (CA)lculated/:\n");
-            printf("\tcurve=%s; rgb_cam=%s; cmatrix=%s, pre_mul=%s, cam_mul=%s",
-                   csl[C.color_flags.curve_state],csl[C.color_flags.rgb_cam_state],
-                   csl[C.color_flags.cmatrix_state],csl[C.color_flags.pre_mul_state],
-                   csl[C.color_flags.cam_mul_state]);
-            putchar ('\n');
-            printf("Cam->XYZ matrix:\n");
-            for(int i=0; i< 4; i++)
-                printf("%6.4f\t%6.4f\t%6.4f\n",C.cam_xyz[i][0],C.cam_xyz[i][1],C.cam_xyz[i][2]);
-        }
-        else 
-            {
-                if(print_unpack)
-                    {
-                        char frame[32]="";
-                        if(print_frame)
-                            snprintf(frame,32,"%dx%dx%dx%d",S.left_margin,S.top_margin,S.right_margin,S.bottom_margin);
-                        printf ("%s\t%s\t%s\t%s/%s\n", 
-                                av[i],
-                                MyCoolRawProcessor.unpack_function_name(),
-                                frame,
-                                P1.make, P1.model);
-                    }
-                else
-                    printf ("%s is a %s %s image.\n", av[i],P1.make, P1.model);
-            }
-        MyCoolRawProcessor.recycle();
-    }// endfor
-    return 0;
-}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/samples/mem_image.cpp libkdcraw/libkdcraw/libraw/samples/mem_image.cpp
--- libkdcraw-wrk/libkdcraw/libraw/samples/mem_image.cpp	2022-11-07 08:15:53.614821808 +0300
+++ libkdcraw/libkdcraw/libraw/samples/mem_image.cpp	2022-11-07 07:46:31.642795007 +0300
@@ -1,25 +1,24 @@
-/*
+/* -*- C++ -*-
  * File: mem_image.cpp
- * Copyright 2008-2009 Alex Tutubalin <lexa@lexa.ru>
+ * Copyright 2008-2010 LibRaw LLC (info@libraw.org)
  * Created: Sat Mar  8 , 2008
  *
- * LibRaw mem_image/mem_thumb API test. Resuls should be same (bitwise) as in dcraw [-4] [-e]
- *   Testing note: for ppm-thumbnails you should use dcraw -w -e for thumbnail extraction
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
+ * LibRaw mem_image/mem_thumb API test. Results should be same (bitwise) to dcraw [-4] [-6] [-e]
+ * Testing note: for ppm-thumbnails you should use dcraw -w -e for thumbnail extraction
+
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of three licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+3. LibRaw Software License 27032010
+   (See file LICENSE.LibRaw.pdf provided in LibRaw distribution archive for details).
+
+
  */
 #include <stdio.h>
 #include <string.h>
@@ -58,7 +57,7 @@
 */
 #define SWAP(a,b) { a ^= b; a ^= (b ^= a); }
     if (img->bits == 16 && htons(0x55aa) != 0x55aa)
-        for(int i=0; i< img->data_size; i+=2)
+        for(unsigned i=0; i< img->data_size; i+=2)
             SWAP(img->data[i],img->data[i+1]);
 #undef SWAP
 
@@ -91,10 +90,9 @@
 
 int main(int ac, char *av[])
 {
-    int  i, ret, verbose=0, output_thumbs=0;
+    int  i, ret, output_thumbs=0;
 
     // don't use fixed size buffers in real apps!
-    char outfn[1024],thumbfn[1024]; 
 
     LibRaw RawProcessor;
     
@@ -103,8 +101,8 @@
             printf(
                 "mem_image - LibRaw sample, to illustrate work for memory buffers. Emulates dcraw [-4] [-1] [-e]\n"
                 "Usage: %s [-D] [-T] [-v] [-e] raw-files....\n"
-                "\t-4 - output 16-bit PPM\n"
-                "\t-1 - gamma-correct 16-bit data\n"
+                "\t-6 - output 16-bit PPM\n"
+                "\t-4 - linear 16-bit data\n"
                 "\t-e - extract thumbnails (same as dcraw -e in separate run)\n",
                 av[0]);
             return 0;
@@ -124,10 +122,13 @@
         {
             if(av[i][0]=='-')
                 {
-                    if(av[i][1]=='4' && av[i][2]==0)
+                    if(av[i][1]=='6' && av[i][2]==0)
                         OUT.output_bps = 16;
-                    if(av[i][1]=='1' && av[i][2]==0)
-                        OUT.gamma_16bit = 1;
+                    if(av[i][1]=='4' && av[i][2]==0)
+                        {
+                            OUT.output_bps = 16;
+                            OUT.gamm[0] = OUT.gamm[1] =  OUT.no_auto_bright    = 1;
+                        }
                     if(av[i][1]=='e' && av[i][2]==0)
                         output_thumbs++;
                     continue;
@@ -139,7 +140,6 @@
                     continue; // no recycle b/c open file will recycle itself
                 }
 
-
             if( (ret = RawProcessor.unpack() ) != LIBRAW_SUCCESS)
                 {
                     fprintf(stderr,"Cannot unpack %s: %s\n",av[i],libraw_strerror(ret));
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/samples/raw-identify.cpp libkdcraw/libkdcraw/libraw/samples/raw-identify.cpp
--- libkdcraw-wrk/libkdcraw/libraw/samples/raw-identify.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/samples/raw-identify.cpp	2022-11-07 07:46:31.730795008 +0300
@@ -0,0 +1,1684 @@
+/* -*- C++ -*-
+ * File: identify.cpp
+ * Copyright 2008-2020 LibRaw LLC (info@libraw.org)
+ * Created: Sat Mar  8, 2008
+ *
+ * LibRaw C++ demo: emulates dcraw -i [-v]
+ *
+
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <time.h>
+#include <string>
+#include <list>
+
+#include "libraw/libraw.h"
+
+#ifdef LIBRAW_WIN32_CALLS
+#define snprintf _snprintf
+#define strcasecmp stricmp
+#define strncasecmp strnicmp
+#endif
+
+#ifndef LIBRAW_WIN32_CALLS
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#ifndef MAX_PATH
+#define MAX_PATH PATH_MAX
+#endif
+#endif
+
+#ifdef _MSC_VER
+#if _MSC_VER < 1800 /* below MSVC 2013 */
+float roundf(float f)
+{
+ return floorf(f + 0.5);
+}
+
+#endif
+#endif
+
+struct starttime_t
+{
+#ifdef LIBRAW_WIN32_CALLS
+	LARGE_INTEGER started;
+#else
+	struct timeval started;
+#endif
+};
+
+// clang-format off
+
+#define P1 MyCoolRawProcessor.imgdata.idata
+#define P2 MyCoolRawProcessor.imgdata.other
+#define P3 MyCoolRawProcessor.imgdata.makernotes.common
+
+#define mnLens MyCoolRawProcessor.imgdata.lens.makernotes
+#define exifLens MyCoolRawProcessor.imgdata.lens
+#define ShootingInfo MyCoolRawProcessor.imgdata.shootinginfo
+
+#define S MyCoolRawProcessor.imgdata.sizes
+#define O MyCoolRawProcessor.imgdata.params
+#define C MyCoolRawProcessor.imgdata.color
+#define T MyCoolRawProcessor.imgdata.thumbnail
+
+#define Canon MyCoolRawProcessor.imgdata.makernotes.canon
+#define Hasselblad MyCoolRawProcessor.imgdata.makernotes.hasselblad
+#define Fuji MyCoolRawProcessor.imgdata.makernotes.fuji
+#define Nikon MyCoolRawProcessor.imgdata.makernotes.nikon
+#define Oly MyCoolRawProcessor.imgdata.makernotes.olympus
+#define Sony MyCoolRawProcessor.imgdata.makernotes.sony
+
+void print_verbose(FILE*, LibRaw& MyCoolRawProcessor, std::string& fn);
+void print_wbfun(FILE*, LibRaw& MyCoolRawProcessor, std::string& fn);
+void print_jsonfun(FILE*, LibRaw& MyCoolRawProcessor,
+                   std::string& fn, int fnum, int nfiles);
+void print_compactfun(FILE*, LibRaw& MyCoolRawProcessor, std::string& fn);
+void print_normfun(FILE*, LibRaw& MyCoolRawProcessor, std::string& fn);
+void print_szfun(FILE*, LibRaw& MyCoolRawProcessor, std::string& fn);
+void print_0fun(FILE*, LibRaw& MyCoolRawProcessor, std::string& fn);
+void print_1fun(FILE*, LibRaw& MyCoolRawProcessor, std::string& fn);
+void print_2fun(FILE*, LibRaw& MyCoolRawProcessor, std::string& fn);
+void print_unpackfun(FILE*, LibRaw& MyCoolRawProcessor, int print_frame, std::string& fn);
+void print_timer(FILE*, const starttime_t&, int c);
+
+/*
+table of fluorescents:
+12 = FL-D; Daylight fluorescent (D 5700K – 7100K) (F1,F5)
+13 = FL-N; Day white fluorescent (N 4600K – 5400K) (F7,F8)
+14 = FL-W; Cool white fluorescent (W 3900K – 4500K) (F2,F6, office, store,warehouse)
+15 = FL-WW; White fluorescent (WW 3200K – 3700K) (F3, residential)
+16 = FL-L; Soft/Warm white fluorescent (L 2600K - 3250K) (F4, kitchen, bath)
+*/
+
+static const struct {
+    const int NumId;
+    const char *StrId;
+    const char *hrStrId; // human-readable
+    const int aux_setting;
+} WBToStr[] = {
+    {LIBRAW_WBI_Unknown,         "WBI_Unknown",         "Unknown",                 0},
+    {LIBRAW_WBI_Daylight,        "WBI_Daylight",        "Daylight",                0},
+    {LIBRAW_WBI_Fluorescent,     "WBI_Fluorescent",     "Fluorescent",             0},
+    {LIBRAW_WBI_Tungsten,        "WBI_Tungsten",        "Tungsten (Incandescent)", 0},
+    {LIBRAW_WBI_Flash,           "WBI_Flash",           "Flash",                   0},
+    {LIBRAW_WBI_FineWeather,     "WBI_FineWeather",     "Fine Weather",            0},
+    {LIBRAW_WBI_Cloudy,          "WBI_Cloudy",          "Cloudy",                  0},
+    {LIBRAW_WBI_Shade,           "WBI_Shade",           "Shade",                   0},
+    {LIBRAW_WBI_FL_D,            "WBI_FL_D",            "Daylight Fluorescent",    0},
+    {LIBRAW_WBI_FL_N,            "WBI_FL_N",            "Day White Fluorescent",   0},
+    {LIBRAW_WBI_FL_W,            "WBI_FL_W",            "Cool White Fluorescent",  0},
+    {LIBRAW_WBI_FL_WW,           "WBI_FL_WW",           "White Fluorescent",       0},
+    {LIBRAW_WBI_FL_L,            "WBI_FL_L",            "Warm White Fluorescent",  0},
+    {LIBRAW_WBI_Ill_A,           "WBI_Ill_A",           "Illuminant A",            0},
+    {LIBRAW_WBI_Ill_B,           "WBI_Ill_B",           "Illuminant B",            0},
+    {LIBRAW_WBI_Ill_C,           "WBI_Ill_C",           "Illuminant C",            0},
+    {LIBRAW_WBI_D55,             "WBI_D55",             "D55",                     0},
+    {LIBRAW_WBI_D65,             "WBI_D65",             "D65",                     0},
+    {LIBRAW_WBI_D75,             "WBI_D75",             "D75",                     0},
+    {LIBRAW_WBI_D50,             "WBI_D50",             "D50",                     0},
+    {LIBRAW_WBI_StudioTungsten,  "WBI_StudioTungsten",  "ISO Studio Tungsten",     0},
+    {LIBRAW_WBI_BW,              "WBI_BW",              "BW",                      0},
+    {LIBRAW_WBI_Other,           "WBI_Other",           "Other",                   0},
+    {LIBRAW_WBI_Sunset,          "WBI_Sunset",          "Sunset",                  1},
+    {LIBRAW_WBI_Underwater,      "WBI_Underwater",      "Underwater",              1},
+    {LIBRAW_WBI_FluorescentHigh, "WBI_FluorescentHigh", "Fluorescent High",        1},
+    {LIBRAW_WBI_HT_Mercury,      "WBI_HT_Mercury",      "HT Mercury",              1},
+    {LIBRAW_WBI_AsShot,          "WBI_AsShot",          "As Shot",                 1},
+    {LIBRAW_WBI_Measured,        "WBI_Measured",        "Camera Measured",         1},
+    {LIBRAW_WBI_Auto,            "WBI_Auto",            "Camera Auto",             1},
+    {LIBRAW_WBI_Auto1,           "WBI_Auto1",           "Camera Auto 1",           1},
+    {LIBRAW_WBI_Auto2,           "WBI_Auto2",           "Camera Auto 2",           1},
+    {LIBRAW_WBI_Auto3,           "WBI_Auto3",           "Camera Auto 3",           1},
+    {LIBRAW_WBI_Auto4,           "WBI_Auto4",           "Camera Auto 4",           1},
+    {LIBRAW_WBI_Custom,          "WBI_Custom",          "Custom",                  1},
+    {LIBRAW_WBI_Custom1,         "WBI_Custom1",         "Custom 1",                1},
+    {LIBRAW_WBI_Custom2,         "WBI_Custom2",         "Custom 2",                1},
+    {LIBRAW_WBI_Custom3,         "WBI_Custom3",         "Custom 3",                1},
+    {LIBRAW_WBI_Custom4,         "WBI_Custom4",         "Custom 4",                1},
+    {LIBRAW_WBI_Custom5,         "WBI_Custom5",         "Custom 5",                1},
+    {LIBRAW_WBI_Custom6,         "WBI_Custom6",         "Custom 6",                1},
+    {LIBRAW_WBI_PC_Set1,         "WBI_PC_Set1",         "PC Set 1",                1},
+    {LIBRAW_WBI_PC_Set2,         "WBI_PC_Set2",         "PC Set 2",                1},
+    {LIBRAW_WBI_PC_Set3,         "WBI_PC_Set3",         "PC Set 3",                1},
+    {LIBRAW_WBI_PC_Set4,         "WBI_PC_Set4",         "PC Set 4",                1},
+    {LIBRAW_WBI_PC_Set5,         "WBI_PC_Set5",         "PC Set 5",                1},
+    {LIBRAW_WBI_Kelvin,          "WBI_Kelvin",          "Kelvin",                  1},
+};
+
+typedef struct
+{
+  unsigned long long id;
+  char const *name;
+} id2hr_t; // id to human readable
+
+static id2hr_t MountNames[] = {
+  {LIBRAW_MOUNT_Alpa,           "Alpa"},
+  {LIBRAW_MOUNT_C,              "C-mount"},
+  {LIBRAW_MOUNT_Canon_EF_M,     "Canon EF-M"},
+  {LIBRAW_MOUNT_Canon_EF_S,     "Canon EF-S"},
+  {LIBRAW_MOUNT_Canon_EF,       "Canon EF"},
+  {LIBRAW_MOUNT_Canon_RF,       "Canon RF"},
+  {LIBRAW_MOUNT_Contax_N,       "Contax N"},
+  {LIBRAW_MOUNT_Contax645,      "Contax 645"},
+  {LIBRAW_MOUNT_FT,             "4/3"},
+  {LIBRAW_MOUNT_mFT,            "m4/3"},
+  {LIBRAW_MOUNT_Fuji_GF,        "Fuji G"},  // Fujifilm G lenses, GFX cameras
+  {LIBRAW_MOUNT_Fuji_GX,        "Fuji GX"}, // GX680
+  {LIBRAW_MOUNT_Fuji_X,         "Fuji X"},
+  {LIBRAW_MOUNT_Hasselblad_H,   "Hasselblad H"},    // Hn cameras, HC & HCD lenses
+  {LIBRAW_MOUNT_Hasselblad_V,   "Hasselblad V"},
+  {LIBRAW_MOUNT_Hasselblad_XCD, "Hasselblad XCD"}, // Xn cameras, XCD lenses
+  {LIBRAW_MOUNT_Leica_M,        "Leica M"},
+  {LIBRAW_MOUNT_Leica_R,        "Leica R"},
+  {LIBRAW_MOUNT_Leica_S,        "Leica S"},
+  {LIBRAW_MOUNT_Leica_SL,       "Leica SL"},     // mounts on "L" throat
+  {LIBRAW_MOUNT_Leica_TL,       "Leica TL"},     // mounts on "L" throat
+  {LIBRAW_MOUNT_LPS_L,          "LPS L-mount"},  // throat, Leica / Panasonic / Sigma
+  {LIBRAW_MOUNT_Mamiya67,       "Mamiya RZ/RB"}, // Mamiya RB67, RZ67
+  {LIBRAW_MOUNT_Mamiya645,      "Mamiya 645"},
+  {LIBRAW_MOUNT_Minolta_A,      "Sony/Minolta A"},
+  {LIBRAW_MOUNT_Nikon_CX,       "Nikkor 1"},
+  {LIBRAW_MOUNT_Nikon_F,        "Nikkor F"},
+  {LIBRAW_MOUNT_Nikon_Z,        "Nikkor Z"},
+  {LIBRAW_MOUNT_Pentax_645,     "Pentax 645"},
+  {LIBRAW_MOUNT_Pentax_K,       "Pentax K"},
+  {LIBRAW_MOUNT_Pentax_Q,       "Pentax Q"},
+  {LIBRAW_MOUNT_RicohModule,    "Ricoh module"},
+  {LIBRAW_MOUNT_Rollei_bayonet, "Rollei bayonet"}, // Rollei Hy-6: Leaf AFi, Sinar Hy6- models
+  {LIBRAW_MOUNT_Samsung_NX_M,   "Samsung NX-M"},
+  {LIBRAW_MOUNT_Samsung_NX,     "Samsung NX"},
+  {LIBRAW_MOUNT_Sigma_X3F,      "Sigma SA/X3F"},
+  {LIBRAW_MOUNT_Sony_E,         "Sony E"},
+// generic formats:
+  {LIBRAW_MOUNT_LF,             "Large format"},
+  {LIBRAW_MOUNT_DigitalBack,    "Digital Back"},
+  {LIBRAW_MOUNT_FixedLens,      "Fixed Lens"},
+  {LIBRAW_MOUNT_IL_UM,          "Interchangeable lens, mount unknown"},
+  {LIBRAW_MOUNT_Unknown,        "Undefined Mount or Fixed Lens"},
+  {LIBRAW_MOUNT_TheLastOne,     "The Last One"},
+};
+
+static id2hr_t FormatNames[] = {
+  {LIBRAW_FORMAT_1div2p3INCH,  "1/2.3\""},
+  {LIBRAW_FORMAT_1div1p7INCH,  "1/1.7\""},
+  {LIBRAW_FORMAT_1INCH,        "1\""},
+  {LIBRAW_FORMAT_FT,           "4/3"},
+  {LIBRAW_FORMAT_APSC,         "APS-C"},     // Canon: 22.3x14.9mm; Sony et al: 23.6-23.7x15.6mm
+  {LIBRAW_FORMAT_Leica_DMR,    "Leica DMR"}, // 26.4x 17.6mm
+  {LIBRAW_FORMAT_APSH,         "APS-H"},     // Canon: 27.9x18.6mm
+  {LIBRAW_FORMAT_FF,           "FF 35mm"},
+  {LIBRAW_FORMAT_CROP645,      "645 crop 44x33mm"},
+  {LIBRAW_FORMAT_LeicaS,       "Leica S 45x30mm"},
+  {LIBRAW_FORMAT_3648,         "48x36mm"},
+  {LIBRAW_FORMAT_645,          "6x4.5"},
+  {LIBRAW_FORMAT_66,           "6x6"},
+  {LIBRAW_FORMAT_67,           "6x7"},
+  {LIBRAW_FORMAT_68,           "6x8"},
+  {LIBRAW_FORMAT_69,           "6x9"},
+  {LIBRAW_FORMAT_SigmaAPSC,    "Sigma APS-C"}, //  Sigma Foveon X3 orig: 20.7x13.8mm
+  {LIBRAW_FORMAT_SigmaMerrill, "Sigma Merrill"},
+  {LIBRAW_FORMAT_SigmaAPSH,    "Sigma APS-H"}, // Sigma "H" 26.7 x 17.9mm
+  {LIBRAW_FORMAT_MF,           "Medium Format"},
+  {LIBRAW_FORMAT_LF,           "Large format"},
+  {LIBRAW_FORMAT_Unknown,      "Unknown"},
+  {LIBRAW_FORMAT_TheLastOne,   "The Last One"},
+};
+
+static id2hr_t NikonCrops[] = {
+    {0, "Uncropped"},     {1, "1.3x"},          {2, "DX"},
+    {3, "5:4"},           {4, "3:2"},           {6, "16:9"},
+    {8, "2.7x"},          {9, "DX Movie"},      {10, "1.3x Movie"},
+    {11, "FX Uncropped"}, {12, "DX Uncropped"}, {15, "1.5x Movie"},
+    {17, "1:1"},
+};
+#define nNikonCrops (sizeof(NikonCrops) / sizeof(id2hr_t))
+
+static id2hr_t FujiCrops[] = {
+    {0, "Uncropped"},
+    {1, "GFX FF"},
+    {2, "Sports Finder Mode"},
+    {4, "Electronic Shutter 1.25x Crop"},
+};
+#define nFujiCrops (sizeof(FujiCrops) / sizeof(id2hr_t))
+
+static id2hr_t FujiDriveModes[] = {
+    {0, "Single Frame"},
+    {1, "Continuous Low"},
+    {2, "Continuous High"},
+};
+#define nFujiDriveModes (sizeof(FujiDriveModes) / sizeof(id2hr_t))
+
+static id2hr_t AspectRatios[] = {
+    {LIBRAW_IMAGE_ASPECT_UNKNOWN, "Unknown"}, {LIBRAW_IMAGE_ASPECT_3to2, "3:2"},
+    {LIBRAW_IMAGE_ASPECT_1to1, "1:1"},        {LIBRAW_IMAGE_ASPECT_4to3, "4:3"},
+    {LIBRAW_IMAGE_ASPECT_16to9, "16:9"},      {LIBRAW_IMAGE_ASPECT_5to4, "5:4"},
+    {LIBRAW_IMAGE_ASPECT_OTHER, "Other"},
+};
+#define nAspectRatios (sizeof(AspectRatios) / sizeof(id2hr_t))
+
+static id2hr_t CanonRecordModes[] = {
+    {LIBRAW_Canon_RecordMode_JPEG, "JPEG"},
+    {LIBRAW_Canon_RecordMode_CRW_THM, "CRW+THM"},
+    {LIBRAW_Canon_RecordMode_AVI_THM, "AVI+THM"},
+    {LIBRAW_Canon_RecordMode_TIF, "TIF"},
+    {LIBRAW_Canon_RecordMode_TIF_JPEG, "TIF+JPEG"},
+    {LIBRAW_Canon_RecordMode_CR2, "CR2"},
+    {LIBRAW_Canon_RecordMode_CR2_JPEG, "CR2+JPEG"},
+    {LIBRAW_Canon_RecordMode_UNKNOWN, "Unknown"},
+    {LIBRAW_Canon_RecordMode_MOV, "MOV"},
+    {LIBRAW_Canon_RecordMode_MP4, "MP4"},
+    {LIBRAW_Canon_RecordMode_CRM, "CRM"},
+    {LIBRAW_Canon_RecordMode_CR3, "CR3"},
+    {LIBRAW_Canon_RecordMode_CR3_JPEG, "CR3+JPEG"},
+    {LIBRAW_Canon_RecordMode_HEIF, "HEIF"},
+    {LIBRAW_Canon_RecordMode_CR3_HEIF, "CR3+HEIF"},
+};
+#define nCanonRecordModes LIBRAW_Canon_RecordMode_TheLastOne
+
+static const struct {
+    const int NumId;
+    const char *StrId;
+} CorpToStr[] = {
+    {LIBRAW_CAMERAMAKER_Agfa,           "LIBRAW_CAMERAMAKER_Agfa"},
+    {LIBRAW_CAMERAMAKER_Alcatel,        "LIBRAW_CAMERAMAKER_Alcatel"},
+    {LIBRAW_CAMERAMAKER_Apple,          "LIBRAW_CAMERAMAKER_Apple"},
+    {LIBRAW_CAMERAMAKER_Aptina,         "LIBRAW_CAMERAMAKER_Aptina"},
+    {LIBRAW_CAMERAMAKER_AVT,            "LIBRAW_CAMERAMAKER_AVT"},
+    {LIBRAW_CAMERAMAKER_Baumer,         "LIBRAW_CAMERAMAKER_Baumer"},
+    {LIBRAW_CAMERAMAKER_Broadcom,       "LIBRAW_CAMERAMAKER_Broadcom"},
+    {LIBRAW_CAMERAMAKER_Canon,          "LIBRAW_CAMERAMAKER_Canon"},
+    {LIBRAW_CAMERAMAKER_Casio,          "LIBRAW_CAMERAMAKER_Casio"},
+    {LIBRAW_CAMERAMAKER_CINE,           "LIBRAW_CAMERAMAKER_CINE"},
+    {LIBRAW_CAMERAMAKER_Clauss,         "LIBRAW_CAMERAMAKER_Clauss"},
+    {LIBRAW_CAMERAMAKER_Contax,         "LIBRAW_CAMERAMAKER_Contax"},
+    {LIBRAW_CAMERAMAKER_Creative,       "LIBRAW_CAMERAMAKER_Creative"},
+    {LIBRAW_CAMERAMAKER_DJI,            "LIBRAW_CAMERAMAKER_DJI"},
+    {LIBRAW_CAMERAMAKER_DXO,            "LIBRAW_CAMERAMAKER_DXO"},
+    {LIBRAW_CAMERAMAKER_Epson,          "LIBRAW_CAMERAMAKER_Epson"},
+    {LIBRAW_CAMERAMAKER_Foculus,        "LIBRAW_CAMERAMAKER_Foculus"},
+    {LIBRAW_CAMERAMAKER_Fujifilm,       "LIBRAW_CAMERAMAKER_Fujifilm"},
+    {LIBRAW_CAMERAMAKER_Generic,        "LIBRAW_CAMERAMAKER_Generic"},
+    {LIBRAW_CAMERAMAKER_Gione,          "LIBRAW_CAMERAMAKER_Gione"},
+    {LIBRAW_CAMERAMAKER_GITUP,          "LIBRAW_CAMERAMAKER_GITUP"},
+    {LIBRAW_CAMERAMAKER_Google,         "LIBRAW_CAMERAMAKER_Google"},
+    {LIBRAW_CAMERAMAKER_GoPro,          "LIBRAW_CAMERAMAKER_GoPro"},
+    {LIBRAW_CAMERAMAKER_Hasselblad,     "LIBRAW_CAMERAMAKER_Hasselblad"},
+    {LIBRAW_CAMERAMAKER_HTC,            "LIBRAW_CAMERAMAKER_HTC"},
+    {LIBRAW_CAMERAMAKER_I_Mobile,       "LIBRAW_CAMERAMAKER_I_Mobile"},
+    {LIBRAW_CAMERAMAKER_Imacon,         "LIBRAW_CAMERAMAKER_Imacon"},
+    {LIBRAW_CAMERAMAKER_Kodak,          "LIBRAW_CAMERAMAKER_Kodak"},
+    {LIBRAW_CAMERAMAKER_Konica,         "LIBRAW_CAMERAMAKER_Konica"},
+    {LIBRAW_CAMERAMAKER_Leaf,           "LIBRAW_CAMERAMAKER_Leaf"},
+    {LIBRAW_CAMERAMAKER_Leica,          "LIBRAW_CAMERAMAKER_Leica"},
+    {LIBRAW_CAMERAMAKER_Lenovo,         "LIBRAW_CAMERAMAKER_Lenovo"},
+    {LIBRAW_CAMERAMAKER_LG,             "LIBRAW_CAMERAMAKER_LG"},
+    {LIBRAW_CAMERAMAKER_Logitech,       "LIBRAW_CAMERAMAKER_Logitech"},
+    {LIBRAW_CAMERAMAKER_Mamiya,         "LIBRAW_CAMERAMAKER_Mamiya"},
+    {LIBRAW_CAMERAMAKER_Matrix,         "LIBRAW_CAMERAMAKER_Matrix"},
+    {LIBRAW_CAMERAMAKER_Meizu,          "LIBRAW_CAMERAMAKER_Meizu"},
+    {LIBRAW_CAMERAMAKER_Micron,         "LIBRAW_CAMERAMAKER_Micron"},
+    {LIBRAW_CAMERAMAKER_Minolta,        "LIBRAW_CAMERAMAKER_Minolta"},
+    {LIBRAW_CAMERAMAKER_Motorola,       "LIBRAW_CAMERAMAKER_Motorola"},
+    {LIBRAW_CAMERAMAKER_NGM,            "LIBRAW_CAMERAMAKER_NGM"},
+    {LIBRAW_CAMERAMAKER_Nikon,          "LIBRAW_CAMERAMAKER_Nikon"},
+    {LIBRAW_CAMERAMAKER_Nokia,          "LIBRAW_CAMERAMAKER_Nokia"},
+    {LIBRAW_CAMERAMAKER_Olympus,        "LIBRAW_CAMERAMAKER_Olympus"},
+    {LIBRAW_CAMERAMAKER_OmniVison,      "LIBRAW_CAMERAMAKER_OmniVison"},
+    {LIBRAW_CAMERAMAKER_Panasonic,      "LIBRAW_CAMERAMAKER_Panasonic"},
+    {LIBRAW_CAMERAMAKER_Parrot,         "LIBRAW_CAMERAMAKER_Parrot"},
+    {LIBRAW_CAMERAMAKER_Pentax,         "LIBRAW_CAMERAMAKER_Pentax"},
+    {LIBRAW_CAMERAMAKER_PhaseOne,       "LIBRAW_CAMERAMAKER_PhaseOne"},
+    {LIBRAW_CAMERAMAKER_PhotoControl,   "LIBRAW_CAMERAMAKER_PhotoControl"},
+    {LIBRAW_CAMERAMAKER_Photron,        "LIBRAW_CAMERAMAKER_Photron"},
+    {LIBRAW_CAMERAMAKER_Pixelink,       "LIBRAW_CAMERAMAKER_Pixelink"},
+    {LIBRAW_CAMERAMAKER_Polaroid,       "LIBRAW_CAMERAMAKER_Polaroid"},
+    {LIBRAW_CAMERAMAKER_RED,            "LIBRAW_CAMERAMAKER_RED"},
+    {LIBRAW_CAMERAMAKER_Ricoh,          "LIBRAW_CAMERAMAKER_Ricoh"},
+    {LIBRAW_CAMERAMAKER_Rollei,         "LIBRAW_CAMERAMAKER_Rollei"},
+    {LIBRAW_CAMERAMAKER_RoverShot,      "LIBRAW_CAMERAMAKER_RoverShot"},
+    {LIBRAW_CAMERAMAKER_Samsung,        "LIBRAW_CAMERAMAKER_Samsung"},
+    {LIBRAW_CAMERAMAKER_Sigma,          "LIBRAW_CAMERAMAKER_Sigma"},
+    {LIBRAW_CAMERAMAKER_Sinar,          "LIBRAW_CAMERAMAKER_Sinar"},
+    {LIBRAW_CAMERAMAKER_SMaL,           "LIBRAW_CAMERAMAKER_SMaL"},
+    {LIBRAW_CAMERAMAKER_Sony,           "LIBRAW_CAMERAMAKER_Sony"},
+    {LIBRAW_CAMERAMAKER_ST_Micro,       "LIBRAW_CAMERAMAKER_ST_Micro"},
+    {LIBRAW_CAMERAMAKER_THL,            "LIBRAW_CAMERAMAKER_THL"},
+    {LIBRAW_CAMERAMAKER_Xiaomi,         "LIBRAW_CAMERAMAKER_Xiaomi"},
+    {LIBRAW_CAMERAMAKER_XIAOYI,         "LIBRAW_CAMERAMAKER_XIAOYI"},
+    {LIBRAW_CAMERAMAKER_YI,             "LIBRAW_CAMERAMAKER_YI"},
+    {LIBRAW_CAMERAMAKER_Yuneec,         "LIBRAW_CAMERAMAKER_Yuneec"},
+    {LIBRAW_CAMERAMAKER_Zeiss,          "LIBRAW_CAMERAMAKER_Zeiss"},
+};
+
+static const struct {
+    const int NumId;
+    const char *StrId;
+} ColorSpaceToStr[] = {
+    {LIBRAW_COLORSPACE_NotFound, "Not Found"},
+    {LIBRAW_COLORSPACE_sRGB, "sRGB"},
+    {LIBRAW_COLORSPACE_AdobeRGB, "Adobe RGB"},
+    {LIBRAW_COLORSPACE_WideGamutRGB, "Wide Gamut RGB"},
+    {LIBRAW_COLORSPACE_ProPhotoRGB, "ProPhoto RGB"},
+    {LIBRAW_COLORSPACE_ICC, "ICC profile (embedded)"},
+    {LIBRAW_COLORSPACE_Uncalibrated, "Uncalibrated"},
+    {LIBRAW_COLORSPACE_CameraLinearUniWB, "Camera Linear, no WB"},
+    {LIBRAW_COLORSPACE_CameraLinear, "Camera Linear"},
+    {LIBRAW_COLORSPACE_CameraGammaUniWB, "Camera non-Linear, no WB"},
+    {LIBRAW_COLORSPACE_CameraGamma, "Camera non-Linear"},
+    {LIBRAW_COLORSPACE_MonochromeLinear, "Monochrome Linear"},
+    {LIBRAW_COLORSPACE_MonochromeGamma, "Monochrome non-Linear"},
+    {LIBRAW_COLORSPACE_Unknown, "Unknown"},
+};
+
+static const struct {
+    const int NumId;
+    const int LibRawId;
+    const char *StrId;
+} Fujifilm_WhiteBalance2Str[] = {
+    {0x000, LIBRAW_WBI_Auto,       "Auto"},
+    {0x100, LIBRAW_WBI_Daylight,   "Daylight"},
+    {0x200, LIBRAW_WBI_Cloudy,     "Cloudy"},
+    {0x300, LIBRAW_WBI_FL_D,       "Daylight Fluorescent"},
+    {0x301, LIBRAW_WBI_FL_N,       "Day White Fluorescent"},
+    {0x302, LIBRAW_WBI_FL_W,       "White Fluorescent"},
+    {0x303, LIBRAW_WBI_FL_WW,      "Warm White Fluorescent"},
+    {0x304, LIBRAW_WBI_FL_L,       "Living Room Warm White Fluorescent"},
+    {0x400, LIBRAW_WBI_Tungsten,   "Incandescent"},
+    {0x500, LIBRAW_WBI_Flash,      "Flash"},
+    {0x600, LIBRAW_WBI_Underwater, "Underwater"},
+    {0xf00, LIBRAW_WBI_Custom,     "Custom"},
+    {0xf01, LIBRAW_WBI_Custom2,    "Custom2"},
+    {0xf02, LIBRAW_WBI_Custom3,    "Custom3"},
+    {0xf03, LIBRAW_WBI_Custom4,    "Custom4"},
+    {0xf04, LIBRAW_WBI_Custom5,    "Custom5"},
+    {0xff0, LIBRAW_WBI_Kelvin,     "Kelvin"},
+};
+
+static const struct {
+    const int NumId;
+    const char *StrId;
+} Fujifilm_FilmModeToStr[] = {
+    {0x000, "F0/Standard (Provia)"},
+    {0x100, "F1/Studio Portrait"},
+    {0x110, "F1a/Studio Portrait Enhanced Saturation"},
+    {0x120, "F1b/Studio Portrait Smooth Skin Tone (Astia)"},
+    {0x130, "F1c/Studio Portrait Increased Sharpness"},
+    {0x200, "F2/Fujichrome (Velvia)"},
+    {0x300, "F3/Studio Portrait Ex"},
+    {0x400, "F4/Velvia"},
+    {0x500, "Pro Neg. Std"},
+    {0x501, "Pro Neg. Hi"},
+    {0x600, "Classic Chrome"},
+    {0x700, "Eterna"},
+    {0x800, "Classic Negative"},
+};
+
+static const struct {
+    const int NumId;
+    const char *StrId;
+} Fujifilm_DynamicRangeSettingToStr[] = {
+    {0x0000, "Auto (100-400%)"},
+    {0x0001, "Manual"},
+    {0x0100, "Standard (100%)"},
+    {0x0200, "Wide1 (230%)"},
+    {0x0201, "Wide2 (400%)"},
+    {0x8000, "Film Simulation"},
+};
+
+//clang-format on
+
+id2hr_t *lookup_id2hr(unsigned long long id, id2hr_t *table, ushort nEntries)
+{
+  for (int k = 0; k < nEntries; k++)
+    if (id == table[k].id)
+      return &table[k];
+  return 0;
+}
+
+const char *ColorSpace_idx2str(ushort ColorSpace) {
+  for (unsigned i = 0; i < (sizeof ColorSpaceToStr / sizeof *ColorSpaceToStr); i++)
+    if(ColorSpaceToStr[i].NumId == ColorSpace)
+      return ColorSpaceToStr[i].StrId;
+  return 0;
+}
+
+const char *CameraMaker_idx2str(unsigned maker) {
+  for (unsigned i = 0; i < (sizeof CorpToStr / sizeof *CorpToStr); i++)
+    if(CorpToStr[i].NumId == (int)maker)
+      return CorpToStr[i].StrId;
+  return 0;
+}
+
+const char *WB_idx2str(unsigned WBi) {
+  for (int i = 0; i < int(sizeof WBToStr / sizeof *WBToStr); i++)
+    if(WBToStr[i].NumId == (int)WBi)
+      return WBToStr[i].StrId;
+  return 0;
+}
+
+const char *WB_idx2hrstr(unsigned WBi) {
+  for (int i = 0; i < int(sizeof WBToStr / sizeof *WBToStr); i++)
+    if(WBToStr[i].NumId == (int)WBi)
+      return WBToStr[i].hrStrId;
+  return 0;
+}
+
+const char *Fujifilm_WhiteBalance_idx2str(ushort WB) {
+  for (int i = 0; i < int(sizeof Fujifilm_WhiteBalance2Str / sizeof *Fujifilm_WhiteBalance2Str); i++)
+    if(Fujifilm_WhiteBalance2Str[i].NumId == WB)
+      return Fujifilm_WhiteBalance2Str[i].StrId;
+  return 0;
+}
+
+const char *Fujifilm_FilmMode_idx2str(ushort FilmMode) {
+  for (int i = 0; i < int(sizeof Fujifilm_FilmModeToStr / sizeof *Fujifilm_FilmModeToStr); i++)
+    if(Fujifilm_FilmModeToStr[i].NumId == FilmMode)
+      return Fujifilm_FilmModeToStr[i].StrId;
+  return 0;
+}
+
+const char *Fujifilm_DynamicRangeSetting_idx2str(ushort DynamicRangeSetting) {
+  for (int i = 0; i < int(sizeof Fujifilm_DynamicRangeSettingToStr / sizeof *Fujifilm_DynamicRangeSettingToStr); i++)
+    if(Fujifilm_DynamicRangeSettingToStr[i].NumId == DynamicRangeSetting)
+      return Fujifilm_DynamicRangeSettingToStr[i].StrId;
+  return 0;
+}
+
+void trimSpaces(char *s)
+{
+  char *p = s;
+  if (!strncasecmp(p, "NO=", 3))
+    p = p + 3; /* fix for Nikon D70, D70s */
+  int l = strlen(p);
+  if (!l)
+    return;
+  while (isspace(p[l - 1]))
+    p[--l] = 0; /* trim trailing spaces */
+  while (*p && isspace(*p))
+    ++p, --l; /* trim leading spaces */
+  memmove(s, p, l + 1);
+}
+
+struct file_mapping
+{
+	void *map;
+	INT64 fsize;
+#ifdef LIBRAW_WIN32_CALLS
+	HANDLE fd,fd_map;
+	file_mapping() : map(0), fsize(0), fd(INVALID_HANDLE_VALUE), fd_map(INVALID_HANDLE_VALUE){}
+#else
+	int  fd;
+	file_mapping() : map(0), fsize(0), fd(-1){}
+#endif
+};
+
+
+
+void timer_start(starttime_t& r)
+{
+#ifdef LIBRAW_WIN32_CALLS
+	QueryPerformanceCounter(&r.started);
+#else
+	gettimeofday(&r.started,NULL);
+#endif
+}
+
+double timer_elapsed(const starttime_t& start)
+{
+#ifdef LIBRAW_WIN32_CALLS
+	LARGE_INTEGER ended, freq;
+	QueryPerformanceCounter(&ended);
+	QueryPerformanceFrequency(&freq);
+	return double(ended.QuadPart - start.started.QuadPart) / double(freq.QuadPart);
+#else
+	struct timeval ended;
+	gettimeofday(&ended, NULL);
+	return double(ended.tv_sec - start.started.tv_sec) + double(ended.tv_usec - start.started.tv_usec) / 1000000.0;
+#endif
+}
+
+
+void create_mapping(struct file_mapping& data, const std::string& fn)
+{
+#ifdef LIBRAW_WIN32_CALLS
+	std::wstring fpath(fn.begin(), fn.end());
+	if ((data.fd = CreateFileW(fpath.c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)) == INVALID_HANDLE_VALUE) return;
+	LARGE_INTEGER fs;
+	if(!GetFileSizeEx(data.fd,&fs)) return;
+	data.fsize = fs.QuadPart;
+	if((data.fd_map = ::CreateFileMapping(data.fd, 0, PAGE_READONLY, fs.HighPart, fs.LowPart, 0)) == INVALID_HANDLE_VALUE) return;
+	data.map = MapViewOfFile(data.fd_map,FILE_MAP_READ,0,0,data.fsize);
+#else
+	struct stat stt;
+	if ((data.fd = open(fn.c_str(), O_RDONLY)) < 0) return;
+	if (fstat(data.fd, &stt) != 0) return;
+	data.fsize = stt.st_size;
+	data.map = mmap(0, data.fsize, PROT_READ | PROT_WRITE, MAP_PRIVATE, data.fd, 0);
+	return;
+#endif
+}
+
+void close_mapping(struct file_mapping& data)
+{
+#ifdef LIBRAW_WIN32_CALLS
+	if (data.map) UnmapViewOfFile(data.map);
+	if (data.fd_map != INVALID_HANDLE_VALUE) CloseHandle(data.fd_map);
+	if (data.fd != INVALID_HANDLE_VALUE) CloseHandle(data.fd);
+	data.map = 0;
+	data.fsize = 0;
+	data.fd = data.fd_map = INVALID_HANDLE_VALUE;
+#else
+	if (data.map)
+		munmap(data.map, data.fsize);
+	if (data.fd>=0)
+		close(data.fd);
+	data.map = 0;
+	data.fsize = 0;
+	data.fd = -1;
+#endif
+}
+
+void print_usage(const char *pname)
+{
+	printf("Usage: %s [options] inputfiles\n", pname);
+	printf("Options:\n"
+		"\t-c\tcompact output\n"
+		"\t-n\tprint make/model and norm. make/model\n"
+		"\t-v\tverbose output\n"
+		"\t-w\tprint white balance\n"
+		"\t-j\tprint JSON\n"
+		"\t-u\tprint unpack function\n"
+		"\t-f\tprint frame size (only w/ -u)\n"
+		"\t-s\tprint output image size\n"
+		"\t-h\tforce half-size mode (only for -s)\n"
+		"\t-M\tdisable use of raw-embedded color data\n"
+		"\t+M\tforce use of raw-embedded color data\n"
+		"\t-L filename\tread input files list from filename\n"
+		"\t-o filename\toutput to filename\n");
+}
+
+int main(int ac, char *av[])
+{
+  int ret;
+  int verbose = 0, print_sz = 0, print_unpack = 0, print_frame = 0,
+	  print_wb = 0, print_json = 0, use_map = 0, use_timing = 0;
+  struct file_mapping mapping;
+  int compact = 0, normalized = 0, print_0 = 0, print_1 = 0, print_2 = 0;
+  LibRaw MyCoolRawProcessor;
+  char *filelistfile = NULL;
+  char *outputfilename = NULL;
+  FILE *outfile = stdout;
+  std::vector <std::string> filelist;
+  starttime_t started;
+
+  filelist.reserve(ac - 1);
+
+  for (int i = 1; i < ac; i++)
+  {
+	  if (av[i][0] == '-')
+	  {
+		  if (!strcmp(av[i], "-c"))	compact++;
+		  if (!strcmp(av[i], "-n"))	normalized++;
+		  if (!strcmp(av[i], "-v"))	verbose++;
+		  if (!strcmp(av[i], "-w")) print_wb++;
+		  if (!strcmp(av[i], "-j")) print_json++;
+		  if (!strcmp(av[i], "-u")) print_unpack++;
+		  if (!strcmp(av[i], "-m")) use_map++;
+		  if (!strcmp(av[i], "-t")) use_timing++;
+		  if (!strcmp(av[i], "-s")) print_sz++;
+		  if (!strcmp(av[i], "-h"))	O.half_size = 1;
+		  if (!strcmp(av[i], "-f"))	print_frame++;
+		  if (!strcmp(av[i], "-0")) print_0++;
+		  if (!strcmp(av[i], "-1")) print_1++;
+		  if (!strcmp(av[i], "-2")) print_2++;
+		  if (!strcmp(av[i], "-M")) MyCoolRawProcessor.imgdata.params.use_camera_matrix = 0;
+		  if (!strcmp(av[i], "-L") && i < ac - 1)
+		  {
+			  filelistfile = av[i + 1];
+			  i++;
+		  }
+		  if (!strcmp(av[i], "-o") && i < ac - 1)
+		  {
+			  outputfilename = av[i + 1];
+			  i++;
+		  }
+		  continue;
+	  }
+	  else if (!strcmp(av[i], "+M"))
+	  {
+		  MyCoolRawProcessor.imgdata.params.use_camera_matrix = 3;
+		  continue;
+	  }
+	  filelist.push_back(av[i]);
+  }
+  if(filelistfile)
+  {
+	  char *p;
+	  char path[MAX_PATH + 1];
+	  FILE *f = fopen(filelistfile, "r");
+	  if (f)
+	  {
+		  while (fgets(path,MAX_PATH,f))
+		  {
+			  if ((p = strchr(path, '\n'))) *p = 0;
+			  if ((p = strchr(path, '\r'))) *p = 0;
+			  filelist.push_back(path);
+		  }
+		  fclose(f);
+	  }
+  }
+  if (filelist.size()<1)
+  {
+	  print_usage(av[0]);
+	  return 1;
+  }
+  if (outputfilename)
+	  outfile = fopen(outputfilename, "wt");
+
+  timer_start(started);
+  for (int i = 0; i < (int)filelist.size(); i++)
+  {
+	  if (use_map)
+	  {
+		  create_mapping(mapping, filelist[i]);
+		  if (!mapping.map)
+		  {
+			  fprintf(stderr, "Cannot map %s\n", filelist[i].c_str());
+			  close_mapping(mapping);
+			  continue;
+		  }
+
+		  if ((ret = MyCoolRawProcessor.open_buffer(mapping.map, mapping.fsize)) != LIBRAW_SUCCESS)
+		  {
+			  fprintf(stderr, "Cannot decode %s: %s\n", filelist[i].c_str(), libraw_strerror(ret));
+			  close_mapping(mapping);
+			  continue;
+		  }
+	  }
+    else
+        if ((ret = MyCoolRawProcessor.open_file(filelist[i].c_str())) != LIBRAW_SUCCESS)
+		{
+			fprintf(stderr, "Cannot decode %s: %s\n", filelist[i].c_str(), libraw_strerror(ret));
+			continue; // no recycle, open_file will recycle
+		}
+
+	  if (use_timing)
+	  {
+		  /* nothing!*/
+	  }
+	  else if (print_sz)
+		  print_szfun(outfile, MyCoolRawProcessor, filelist[i]);
+	  else if (print_0)
+		  print_0fun(outfile, MyCoolRawProcessor, filelist[i]);
+	  else if (print_1)
+		  print_1fun(outfile, MyCoolRawProcessor, filelist[i]);
+	  else if (print_2)
+		  print_2fun(outfile, MyCoolRawProcessor, filelist[i]);
+	  else if (verbose)
+		  print_verbose(outfile, MyCoolRawProcessor, filelist[i]);
+	  else if (print_unpack)
+		  print_unpackfun(outfile, MyCoolRawProcessor, print_frame, filelist[i]);
+	  else if (print_wb)
+		  print_wbfun(outfile, MyCoolRawProcessor, filelist[i]);
+	  else if (compact)
+		  print_compactfun(outfile, MyCoolRawProcessor, filelist[i]);
+	  else if (normalized)
+		  print_normfun(outfile, MyCoolRawProcessor, filelist[i]);
+	  else if (print_json)
+	    print_jsonfun(outfile, MyCoolRawProcessor, filelist[i], i, filelist.size());
+	  else
+		  fprintf(outfile, "%s is a %s %s image.\n", filelist[i].c_str(), P1.make, P1.model);
+
+    MyCoolRawProcessor.recycle();
+	if (use_map)
+		close_mapping(mapping);
+  } // endfor
+
+  if (use_timing && filelist.size() > 0)
+	  print_timer(outfile, started, filelist.size());
+  return 0;
+}
+
+void print_timer(FILE* outfile, const starttime_t& started, int files)
+{
+	double elapsed = timer_elapsed(started);
+	if (elapsed > 1.0)
+		fprintf(outfile, "%d files processed in %5.3f sec, %5.3g files/sec\n", files, elapsed, files / elapsed);
+	else if (elapsed > 0.001) // 1msec
+	{
+		double msec = elapsed * 1000.0;
+		fprintf(outfile, "%d files processed in %5.3f msec, %5.3g files/sec\n", files, msec, files / elapsed);
+	}
+	else if (elapsed > 0.000001)
+	{
+		double usec = elapsed * 1000000.0;
+		fprintf(outfile, "%d files processed in %5.3f usec, %5.3g files/sec\n", files, usec, files / elapsed);
+	}
+	else
+		fprintf(outfile, "%d files processed, time too small to estimate\n", files);
+}
+
+void print_jsonfun(FILE* outfile, LibRaw& MyCoolRawProcessor,
+                   std::string& fn, int fnum, int nfiles) {
+
+  const int tab_width = 4;
+  int n_tabs;
+  const char tab_char = ' ';
+  const char *CamMakerName = CameraMaker_idx2str(P1.maker_index);
+  int WBi;
+  int data_present = 0;
+
+  if (fnum == 0) fprintf (outfile, "{\n");
+  n_tabs = 1;
+  fprintf (outfile, "%*c\"file_%05d\":{\n",
+    n_tabs*tab_width, tab_char, fnum);
+  n_tabs++;
+
+  fprintf (outfile, "%*c\"file_name\":", n_tabs*tab_width, tab_char);
+  if (fn.c_str()[0]) fprintf (outfile, "\"%s\",\n", fn.c_str());
+  else fprintf (outfile, "null,\n");
+
+  fprintf (outfile, "%*c\"cam_maker\":", n_tabs*tab_width, tab_char);
+  if (CamMakerName[0]) fprintf (outfile, "\"%s\",\n", CamMakerName);
+  else fprintf (outfile, "null,\n");
+
+  fprintf (outfile, "%*c\"norm_model\":", n_tabs*tab_width, tab_char);
+  if (P1.normalized_model[0]) fprintf (outfile, "\"%s\",\n", P1.normalized_model);
+  else fprintf (outfile, "null,\n");
+
+  fprintf (outfile, "%*c\"body_serial\":", n_tabs*tab_width, tab_char);
+  if (ShootingInfo.BodySerial[0] &&
+      strcmp(ShootingInfo.BodySerial, "0")) {
+    trimSpaces(ShootingInfo.BodySerial);
+    fprintf (outfile, "\"%s\",\n", ShootingInfo.BodySerial);
+  } else if (C.model2[0] &&
+             (!strncasecmp(P1.normalized_make, "Kodak", 5))) {
+    trimSpaces(C.model2);
+    fprintf (outfile, "\"%s\",\n", C.model2);
+  } else fprintf (outfile, "null,\n");
+
+  fprintf (outfile, "%*c\"int_serial\":", n_tabs*tab_width, tab_char);
+  if (ShootingInfo.InternalBodySerial[0]) {
+    trimSpaces(ShootingInfo.InternalBodySerial);
+    fprintf (outfile, "\"%s\",\n", ShootingInfo.InternalBodySerial);
+  } else fprintf (outfile, "null,\n");
+
+  fprintf (outfile, "%*c\"dng\":%s,\n",
+    n_tabs*tab_width, tab_char, P1.dng_version?"true":"false");
+
+  fprintf (outfile, "%*c\"ISO\":", n_tabs*tab_width, tab_char);
+  if (P2.iso_speed > 0.1f) fprintf (outfile, "%d,\n", int(P2.iso_speed));
+  else fprintf (outfile, "null,\n");
+
+  fprintf (outfile, "%*c\"BLE\":", n_tabs*tab_width, tab_char);
+  if (int(C.dng_levels.baseline_exposure) != -999)
+    fprintf (outfile, "%g,\n", C.dng_levels.baseline_exposure);
+  else fprintf (outfile, "null,\n");
+
+  fprintf (outfile, "%*c\"CameraCalibration\":", n_tabs*tab_width, tab_char);
+	for (int n = 0; n < 2; n++) {
+		if (fabsf(C.dng_color[n].calibration[0][0]) > 0) {
+			int numel = 3;
+			if (fabsf(C.dng_color[n].calibration[3][3]) > 0) numel = 4;
+			if (!data_present) {
+			  fprintf (outfile, "{");
+			  n_tabs++;
+			} else fprintf (outfile, ",");
+			fprintf (outfile, "\n%*c\"%s\":",
+			  n_tabs*tab_width, tab_char, WB_idx2str(C.dng_color[n].illuminant));
+			for (int cnt = 0; cnt < numel; cnt++) {
+				fprintf (outfile, "%s%g%s", (!cnt)?"[":"",
+				  C.dng_color[n].calibration[cnt][cnt], (cnt < (numel - 1))?",":"]");
+			}
+			data_present++;
+		}
+	}
+	if (data_present) {
+	  data_present = 0;
+	  n_tabs--;
+	  fprintf (outfile, "\n%*c},\n", n_tabs*tab_width, tab_char);
+	} else fprintf (outfile, "null,\n");
+
+  fprintf (outfile, "%*c\"ColorMatrix\":", n_tabs*tab_width, tab_char);
+	for (int n = 0; n < 2; n++) {
+		if (fabsf(C.dng_color[n].colormatrix[0][0]) > 0) {
+			int numel = 3;
+			if (!data_present) {
+			  fprintf (outfile, "{");
+			  n_tabs++;
+			} else fprintf (outfile, ",");
+			fprintf (outfile, "\n%*c\"%s\":",
+			  n_tabs*tab_width, tab_char, WB_idx2str(C.dng_color[n].illuminant));
+			for (int i = 0; i < P1.colors; i++) {
+				for (int cnt = 0; cnt < numel; cnt++) {
+					fprintf (outfile, "%s%g%s", (!i&&!cnt)?"[":"",
+					  C.dng_color[n].colormatrix[i][cnt],
+					  ((i<(P1.colors-1)) || (cnt < (numel - 1)))?",":"]");
+				}
+			}
+			data_present++;
+		}
+	}
+	if (data_present) {
+	  data_present = 0;
+	  n_tabs--;
+	  fprintf (outfile, "\n%*c},\n", n_tabs*tab_width, tab_char);
+	} else fprintf (outfile, "null,\n");
+
+  fprintf (outfile, "%*c\"WB data\":",
+    n_tabs*tab_width, tab_char);
+
+  if (C.cam_mul[0]) {
+    if (!data_present) {
+      fprintf (outfile, "{");
+      n_tabs++;
+    } else fprintf (outfile, ",");
+    fprintf (outfile, "\n%*c\"%s\":",
+      n_tabs*tab_width, tab_char, WB_idx2str(LIBRAW_WBI_AsShot));
+    for (int i = 0; i < 4; i++) {
+      fprintf (outfile, "%s%g%s", (!i)?"[":"",
+        C.cam_mul[i], (i < 3)?",":"]");
+    }
+    data_present++;
+  }
+
+  for (int cnt = 0; cnt < int(sizeof WBToStr / sizeof *WBToStr); cnt++) {
+    WBi = WBToStr[cnt].NumId;
+    if (C.WB_Coeffs[WBi][0]) {
+			if (!data_present) {
+			  fprintf (outfile, "{");
+			  n_tabs++;
+			} else fprintf (outfile, ",");
+      fprintf (outfile, "\n%*c\"%s\":",
+        n_tabs*tab_width, tab_char, WBToStr[cnt].StrId);
+      for (int i = 0; i < 4; i++) {
+				fprintf (outfile, "%s%d%s", (!i)?"[":"",
+				  C.WB_Coeffs[WBi][i], (i < 3)?",":"]");
+      }
+      data_present++;
+    }
+  }
+
+  for (int cnt = 0; cnt < 64; cnt++) {
+    if (C.WBCT_Coeffs[cnt][0]) {
+			if (!data_present) {
+			  fprintf (outfile, "{");
+			  n_tabs++;
+			} else fprintf (outfile, ",");
+      fprintf (outfile, "\n%*c\"%g\":",
+        n_tabs*tab_width, tab_char, C.WBCT_Coeffs[cnt][0]);
+      for (int i = 1; i < 5; i++) {
+				fprintf (outfile, "%s%g%s", (i == 1)?"[":"",
+				  C.WBCT_Coeffs[cnt][i], (i < 4)?",":"]");
+      }
+      data_present++;
+    }
+  }
+
+	if (data_present) {
+	  data_present = 0;
+	  n_tabs--;
+	  fprintf (outfile, "\n%*c}\n", n_tabs*tab_width, tab_char);
+	} else fprintf (outfile, "null\n");
+
+  n_tabs--;
+  fprintf (outfile, "%*c}\n", n_tabs*tab_width, tab_char);
+
+  if ((fnum+1) == nfiles) fprintf (outfile, "}\n");
+  else fprintf (outfile, ",");
+}
+
+#define PRINTMATRIX3x4(of,mat,clrs)																		\
+	do{																								    \
+		for(int r = 0; r < 3; r++)																		\
+			if(clrs==4) 																				\
+				fprintf(of, "%6.4f\t%6.4f\t%6.4f\t%6.4f\n", mat[r][0],mat[r][1],mat[r][2],mat[r][3]); 	\
+			else																						\
+				fprintf(of, "%6.4f\t%6.4f\t%6.4f\n", mat[r][0],mat[r][1],mat[r][2]);					\
+	}while(0)
+
+#define PRINTMATRIX4x3(of,mat,clrs)																		\
+	do{																									\
+		for(int r = 0; r < clrs && r < 4; r++)																	\
+				fprintf(of, "%6.4f\t%6.4f\t%6.4f\n", mat[r][0],mat[r][1],mat[r][2]);					\
+	}while(0)
+
+
+void print_verbose(FILE* outfile, LibRaw& MyCoolRawProcessor, std::string& fn)
+{
+	id2hr_t *MountName, *FormatName, *Aspect, *Crop, *DriveMode;
+	const char *CamMakerName = LibRaw::cameramakeridx2maker(P1.maker_index);
+	const char *ColorSpaceName = ColorSpace_idx2str(P3.ColorSpace);
+	int WBi;
+	float denom;
+	int ret;
+
+	if ((ret = MyCoolRawProcessor.adjust_sizes_info_only()))
+	{
+		fprintf(outfile, "Cannot decode %s: %s\n", fn.c_str(), libraw_strerror(ret));
+		return; // no recycle, open_file will recycle
+	}
+
+	fprintf(outfile, "\nFilename: %s\n", fn.c_str());
+	if (C.OriginalRawFileName[0])
+		fprintf(outfile, "OriginalRawFileName: =%s=\n", C.OriginalRawFileName);
+	fprintf(outfile, "Timestamp: %s", ctime(&(P2.timestamp)));
+	fprintf(outfile, "Camera: %s %s ID: 0x%llx\n", P1.make, P1.model, mnLens.CamID);
+	fprintf(outfile, "Normalized Make/Model: =%s/%s= ", P1.normalized_make,
+		P1.normalized_model);
+	fprintf(outfile, "CamMaker ID: %d, ", P1.maker_index);
+	if (CamMakerName)
+		fprintf(outfile, "%s\n", CamMakerName);
+	else
+		fprintf(outfile, "Undefined\n");
+	{
+		int i = 0;
+		char sep[] = ", ";
+		if (C.UniqueCameraModel[0]) {
+			i++;
+			fprintf(outfile, "UniqueCameraModel: =%s=", C.UniqueCameraModel);
+		}
+		if (C.LocalizedCameraModel[0]) {
+			if (i) {
+				fprintf(outfile, "%s", sep);
+				i++;
+			}
+			fprintf(outfile, "LocalizedCameraModel: =%s=", C.LocalizedCameraModel);
+		}
+		if (i) {
+			fprintf(outfile, "\n");
+			i = 0;
+		}
+		if (C.ImageUniqueID[0]) {
+			if (i) fprintf(outfile, "%s", sep);
+			i++;
+			fprintf(outfile, "ImageUniqueID: =%s=", C.ImageUniqueID);
+		}
+		if (C.RawDataUniqueID[0]) {
+			if (i) fprintf(outfile, "%s", sep);
+			i++;
+			fprintf(outfile, "RawDataUniqueID: =%s=", C.RawDataUniqueID);
+		}
+		if (i) fprintf(outfile, "\n");
+	}
+
+	if (ShootingInfo.BodySerial[0] && strcmp(ShootingInfo.BodySerial, "0"))
+	{
+		trimSpaces(ShootingInfo.BodySerial);
+		fprintf(outfile, "Body#: %s", ShootingInfo.BodySerial);
+	}
+	else if (C.model2[0] && (!strncasecmp(P1.normalized_make, "Kodak", 5)))
+	{
+		trimSpaces(C.model2);
+		fprintf(outfile, "Body#: %s", C.model2);
+	}
+	if (ShootingInfo.InternalBodySerial[0])
+	{
+		trimSpaces(ShootingInfo.InternalBodySerial);
+		fprintf(outfile, " BodyAssy#: %s", ShootingInfo.InternalBodySerial);
+	}
+	if (exifLens.LensSerial[0])
+	{
+		trimSpaces(exifLens.LensSerial);
+		fprintf(outfile, " Lens#: %s", exifLens.LensSerial);
+	}
+	if (exifLens.InternalLensSerial[0])
+	{
+		trimSpaces(exifLens.InternalLensSerial);
+		fprintf(outfile, " LensAssy#: %s", exifLens.InternalLensSerial);
+	}
+	if (P2.artist[0])
+		fprintf(outfile, " Owner: %s\n", P2.artist);
+	if (P1.dng_version)
+	{
+		fprintf(outfile, " DNG Version: ");
+		for (int i = 24; i >= 0; i -= 8)
+			fprintf(outfile, "%d%c", P1.dng_version >> i & 255, i ? '.' : '\n');
+	}
+	fprintf(outfile, "\nEXIF:\n");
+	fprintf(outfile, "\tMinFocal: %0.1f mm\n", exifLens.MinFocal);
+	fprintf(outfile, "\tMaxFocal: %0.1f mm\n", exifLens.MaxFocal);
+	fprintf(outfile, "\tMaxAp @MinFocal: f/%0.1f\n", exifLens.MaxAp4MinFocal);
+	fprintf(outfile, "\tMaxAp @MaxFocal: f/%0.1f\n", exifLens.MaxAp4MaxFocal);
+	fprintf(outfile, "\tCurFocal: %0.1f mm\n", P2.focal_len);
+	fprintf(outfile, "\tMaxAperture @CurFocal: f/%0.1f\n", exifLens.EXIF_MaxAp);
+	fprintf(outfile, "\tFocalLengthIn35mmFormat: %d mm\n",
+		exifLens.FocalLengthIn35mmFormat);
+	fprintf(outfile, "\tLensMake: %s\n", exifLens.LensMake);
+	fprintf(outfile, "\tLens: %s\n", exifLens.Lens);
+	fprintf(outfile, "\n");
+
+	fprintf(outfile, "\nMakernotes:\n");
+	fprintf(outfile, "\tDriveMode: %d\n", ShootingInfo.DriveMode);
+	fprintf(outfile, "\tFocusMode: %d\n", ShootingInfo.FocusMode);
+	fprintf(outfile, "\tMeteringMode: %d\n", ShootingInfo.MeteringMode);
+	fprintf(outfile, "\tAFPoint: %d\n", ShootingInfo.AFPoint);
+	fprintf(outfile, "\tExposureMode: %d\n", ShootingInfo.ExposureMode);
+	fprintf(outfile, "\tExposureProgram: %d\n", ShootingInfo.ExposureProgram);
+	fprintf(outfile, "\tImageStabilization: %d\n", ShootingInfo.ImageStabilization);
+	if (mnLens.body[0])
+	{
+		fprintf(outfile, "\tHost Body: %s\n", mnLens.body);
+	}
+	if (Hasselblad.CaptureSequenceInitiator[0])
+	{
+		fprintf(outfile, "\tInitiator: %s\n", Hasselblad.CaptureSequenceInitiator);
+	}
+	if (Hasselblad.SensorUnitConnector[0])
+	{
+		fprintf(outfile, "\tSU Connector: %s\n", Hasselblad.SensorUnitConnector);
+	}
+	fprintf(outfile, "\tCameraFormat: %d, ", mnLens.CameraFormat);
+	FormatName = lookup_id2hr(mnLens.CameraFormat, FormatNames, LIBRAW_FORMAT_TheLastOne);
+	if (FormatName)
+		fprintf(outfile, "%s\n", FormatName->name);
+	else
+		fprintf(outfile, "Unknown\n");
+
+	if (!strncasecmp(P1.make, "Nikon", 5) && Nikon.SensorHighSpeedCrop.cwidth)
+	{
+		fprintf(outfile, "\tNikon crop: %d, ", Nikon.HighSpeedCropFormat);
+		Crop = lookup_id2hr(Nikon.HighSpeedCropFormat, NikonCrops, nNikonCrops);
+		if (Crop)
+			fprintf(outfile, "%s\n", Crop->name);
+		else
+			fprintf(outfile, "Unknown\n");
+		fprintf(outfile, "\tSensor used area %d x %d; crop from: %d x %d at top left "
+			"pixel: (%d, %d)\n",
+			Nikon.SensorWidth, Nikon.SensorHeight,
+			Nikon.SensorHighSpeedCrop.cwidth,
+			Nikon.SensorHighSpeedCrop.cheight,
+			Nikon.SensorHighSpeedCrop.cleft, Nikon.SensorHighSpeedCrop.ctop);
+	}
+
+	fprintf(outfile, "\tCameraMount: %d, ", mnLens.CameraMount);
+	MountName = lookup_id2hr(mnLens.CameraMount, MountNames, LIBRAW_MOUNT_TheLastOne);
+	if (MountName)
+		fprintf(outfile, "%s\n", MountName->name);
+	else
+		fprintf(outfile, "Unknown\n");
+
+	if (mnLens.LensID == 0xffffffff)
+		fprintf(outfile, "\tLensID: n/a\n");
+	else
+		fprintf(outfile, "\tLensID: %llu 0x%0llx\n", mnLens.LensID, mnLens.LensID);
+
+	fprintf(outfile, "\tLens: %s\n", mnLens.Lens);
+	fprintf(outfile, "\tLensFormat: %d, ", mnLens.LensFormat);
+	FormatName = lookup_id2hr(mnLens.LensFormat, FormatNames, LIBRAW_FORMAT_TheLastOne);
+	if (FormatName)
+		fprintf(outfile, "%s\n", FormatName->name);
+	else
+		fprintf(outfile, "Unknown\n");
+
+	fprintf(outfile, "\tLensMount: %d, ", mnLens.LensMount);
+	MountName = lookup_id2hr(mnLens.LensMount, MountNames, LIBRAW_MOUNT_TheLastOne);
+	if (MountName)
+		fprintf(outfile, "%s\n", MountName->name);
+	else
+		fprintf(outfile, "Unknown\n");
+
+	fprintf(outfile, "\tFocalType: %d, ", mnLens.FocalType);
+	switch (mnLens.FocalType)
+	{
+	case LIBRAW_FT_UNDEFINED:
+		fprintf(outfile, "Undefined\n");
+		break;
+	case LIBRAW_FT_PRIME_LENS:
+		fprintf(outfile, "Prime lens\n");
+		break;
+	case LIBRAW_FT_ZOOM_LENS:
+		fprintf(outfile, "Zoom lens\n");
+		break;
+	default:
+		fprintf(outfile, "Unknown\n");
+		break;
+	}
+	fprintf(outfile, "\tLensFeatures_pre: %s\n", mnLens.LensFeatures_pre);
+	fprintf(outfile, "\tLensFeatures_suf: %s\n", mnLens.LensFeatures_suf);
+	fprintf(outfile, "\tMinFocal: %0.1f mm\n", mnLens.MinFocal);
+	fprintf(outfile, "\tMaxFocal: %0.1f mm\n", mnLens.MaxFocal);
+	fprintf(outfile, "\tMaxAp @MinFocal: f/%0.1f\n", mnLens.MaxAp4MinFocal);
+	fprintf(outfile, "\tMaxAp @MaxFocal: f/%0.1f\n", mnLens.MaxAp4MaxFocal);
+	fprintf(outfile, "\tMinAp @MinFocal: f/%0.1f\n", mnLens.MinAp4MinFocal);
+	fprintf(outfile, "\tMinAp @MaxFocal: f/%0.1f\n", mnLens.MinAp4MaxFocal);
+	fprintf(outfile, "\tMaxAp: f/%0.1f\n", mnLens.MaxAp);
+	fprintf(outfile, "\tMinAp: f/%0.1f\n", mnLens.MinAp);
+	fprintf(outfile, "\tCurFocal: %0.1f mm\n", mnLens.CurFocal);
+	fprintf(outfile, "\tCurAp: f/%0.1f\n", mnLens.CurAp);
+	fprintf(outfile, "\tMaxAp @CurFocal: f/%0.1f\n", mnLens.MaxAp4CurFocal);
+	fprintf(outfile, "\tMinAp @CurFocal: f/%0.1f\n", mnLens.MinAp4CurFocal);
+
+	if (exifLens.makernotes.FocalLengthIn35mmFormat > 1.0f)
+		fprintf(outfile, "\tFocalLengthIn35mmFormat: %0.1f mm\n",
+		exifLens.makernotes.FocalLengthIn35mmFormat);
+
+	if (exifLens.nikon.EffectiveMaxAp > 0.1f)
+		fprintf(outfile, "\tEffectiveMaxAp: f/%0.1f\n",
+		exifLens.nikon.EffectiveMaxAp);
+
+	if (exifLens.makernotes.LensFStops > 0.1f)
+		fprintf(outfile, "\tLensFStops @CurFocal: %0.2f\n",
+		exifLens.makernotes.LensFStops);
+
+	fprintf(outfile, "\tTeleconverterID: %lld\n", mnLens.TeleconverterID);
+	fprintf(outfile, "\tTeleconverter: %s\n", mnLens.Teleconverter);
+	fprintf(outfile, "\tAdapterID: %lld\n", mnLens.AdapterID);
+	fprintf(outfile, "\tAdapter: %s\n", mnLens.Adapter);
+	fprintf(outfile, "\tAttachmentID: %lld\n", mnLens.AttachmentID);
+	fprintf(outfile, "\tAttachment: %s\n", mnLens.Attachment);
+	fprintf(outfile, "\n");
+
+	fprintf(outfile, "ISO speed: %d\n", (int)P2.iso_speed);
+	if (P3.real_ISO > 0.1f)
+		fprintf(outfile, "real ISO speed: %d\n", (int)P3.real_ISO);
+	fprintf(outfile, "Shutter: ");
+	if (P2.shutter > 0 && P2.shutter < 1)
+		P2.shutter = fprintf(outfile, "1/%0.1f\n", 1.0f / P2.shutter);
+	else if (P2.shutter >= 1)
+		fprintf(outfile, "%0.1f sec\n", P2.shutter);
+	else /* negative*/
+		fprintf(outfile, " negative value\n");
+	fprintf(outfile, "Aperture: f/%0.1f\n", P2.aperture);
+	fprintf(outfile, "Focal length: %0.1f mm\n", P2.focal_len);
+	if (P3.exifAmbientTemperature > -273.15f)
+		fprintf(outfile, "Ambient temperature (exif data): %6.2f° C\n",
+		P3.exifAmbientTemperature);
+	if (P3.CameraTemperature > -273.15f)
+		fprintf(outfile, "Camera temperature: %6.2f° C\n", P3.CameraTemperature);
+	if (P3.SensorTemperature > -273.15f)
+		fprintf(outfile, "Sensor temperature: %6.2f° C\n", P3.SensorTemperature);
+	if (P3.SensorTemperature2 > -273.15f)
+		fprintf(outfile, "Sensor temperature2: %6.2f° C\n", P3.SensorTemperature2);
+	if (P3.LensTemperature > -273.15f)
+		fprintf(outfile, "Lens temperature: %6.2f° C\n", P3.LensTemperature);
+	if (P3.AmbientTemperature > -273.15f)
+		fprintf(outfile, "Ambient temperature: %6.2f° C\n", P3.AmbientTemperature);
+	if (P3.BatteryTemperature > -273.15f)
+		fprintf(outfile, "Battery temperature: %6.2f° C\n", P3.BatteryTemperature);
+	if (P3.FlashGN > 1.0f)
+		fprintf(outfile, "Flash Guide Number: %6.2f\n", P3.FlashGN);
+	fprintf(outfile, "Flash exposure compensation: %0.2f EV\n", P3.FlashEC);
+	if (C.profile)
+		fprintf(outfile, "Embedded ICC profile: yes, %d bytes\n", C.profile_length);
+	else
+		fprintf(outfile, "Embedded ICC profile: no\n");
+
+	if (C.dng_levels.baseline_exposure > -999.f)
+		fprintf(outfile, "Baseline exposure: %04.3f\n", C.dng_levels.baseline_exposure);
+
+	fprintf(outfile, "Number of raw images: %d\n", P1.raw_count);
+
+	if (Fuji.DriveMode != -1)
+	{
+		fprintf(outfile, "Fuji Drive Mode: %d, ", Fuji.DriveMode);
+		DriveMode =
+			lookup_id2hr(Fuji.DriveMode, FujiDriveModes, nFujiDriveModes);
+		if (DriveMode)
+			fprintf(outfile, "%s\n", DriveMode->name);
+		else
+			fprintf(outfile, "Unknown\n");
+	}
+
+	if (Fuji.CropMode)
+	{
+		fprintf(outfile, "Fuji Crop Mode: %d, ", Fuji.CropMode);
+		Crop = lookup_id2hr(Fuji.CropMode, FujiCrops, nFujiCrops);
+		if (Crop)
+			fprintf(outfile, "%s\n", Crop->name);
+		else
+			fprintf(outfile, "Unknown\n");
+	}
+
+  if (Fuji.WB_Preset != 0xffff)
+    fprintf(outfile, "Fuji WB preset: 0x%03x, %s\n", Fuji.WB_Preset, Fujifilm_WhiteBalance_idx2str(Fuji.WB_Preset));
+	if (Fuji.ExpoMidPointShift > -999.f) // tag 0x9650
+		fprintf(outfile, "Fuji Exposure shift: %04.3f\n", Fuji.ExpoMidPointShift);
+	if (Fuji.DynamicRange != 0xffff)
+		fprintf(outfile, "Fuji Dynamic Range (0x1400): %d, %s\n",
+		  Fuji.DynamicRange, Fuji.DynamicRange==1?"Standard":"Wide");
+	if (Fuji.FilmMode != 0xffff)
+		fprintf(outfile, "Fuji Film Mode (0x1401): 0x%03x, %s\n", Fuji.FilmMode, Fujifilm_FilmMode_idx2str(Fuji.FilmMode));
+	if (Fuji.DynamicRangeSetting != 0xffff)
+		fprintf(outfile, "Fuji Dynamic Range Setting (0x1402): 0x%04x, %s\n",
+		Fuji.DynamicRangeSetting, Fujifilm_DynamicRangeSetting_idx2str(Fuji.DynamicRangeSetting));
+	if (Fuji.DevelopmentDynamicRange != 0xffff)
+		fprintf(outfile, "Fuji Development Dynamic Range (0x1403): %d\n",
+		Fuji.DevelopmentDynamicRange);
+	if (Fuji.AutoDynamicRange != 0xffff)
+		fprintf(outfile, "Fuji Auto Dynamic Range (0x140b): %d\n", Fuji.AutoDynamicRange);
+	if (Fuji.DRangePriority != 0xffff)
+		fprintf(outfile, "Fuji Dynamic Range priority (0x1443): %d, %s\n",
+		   Fuji.DRangePriority, Fuji.DRangePriority?"Fixed":"Auto");
+	if (Fuji.DRangePriorityAuto)
+		fprintf(outfile, "Fuji Dynamic Range priority Auto (0x1444): %d, %s\n",
+		   Fuji.DRangePriorityAuto, Fuji.DRangePriorityAuto==1?"Weak":"Strong");
+	if (Fuji.DRangePriorityFixed)
+		fprintf(outfile, "Fuji Dynamic Range priority Fixed (0x1445): %d, %s\n",
+		   Fuji.DRangePriorityFixed, Fuji.DRangePriorityFixed==1?"Weak":"Strong");
+
+	if (S.pixel_aspect != 1)
+		fprintf(outfile, "Pixel Aspect Ratio: %0.6f\n", S.pixel_aspect);
+	if (T.tlength)
+		fprintf(outfile, "Thumb size:  %4d x %d\n", T.twidth, T.theight);
+	fprintf(outfile, "Full size:   %4d x %d\n", S.raw_width, S.raw_height);
+
+	if (S.raw_inset_crop.cwidth)
+	{
+		fprintf(outfile, "Raw inset, width x height: %4d x %d ", S.raw_inset_crop.cwidth,
+			S.raw_inset_crop.cheight);
+		if (S.raw_inset_crop.cleft != 0xffff)
+			fprintf(outfile, "left: %d ", S.raw_inset_crop.cleft);
+		if (S.raw_inset_crop.ctop != 0xffff)
+			fprintf(outfile, "top: %d", S.raw_inset_crop.ctop);
+		fprintf(outfile, "\n");
+	}
+
+	fprintf(outfile, "Aspect: ");
+	Aspect =
+		lookup_id2hr(S.raw_inset_crop.aspect, AspectRatios, nAspectRatios);
+	if (Aspect)
+		fprintf(outfile, "%s\n", Aspect->name);
+	else
+		fprintf(outfile, "Other %d\n", S.raw_inset_crop.aspect);
+
+	fprintf(outfile, "Image size:  %4d x %d\n", S.width, S.height);
+	fprintf(outfile, "Output size: %4d x %d\n", S.iwidth, S.iheight);
+	fprintf(outfile, "Image flip: %d\n", S.flip);
+
+	if (Canon.RecordMode) {
+	  id2hr_t *RecordMode =
+	    lookup_id2hr(Canon.RecordMode, CanonRecordModes, nCanonRecordModes);
+	  fprintf(outfile, "Canon record mode: %d, %s\n", Canon.RecordMode, RecordMode->name);
+	}
+	if (Canon.SensorWidth)
+		fprintf(outfile, "SensorWidth          = %d\n", Canon.SensorWidth);
+	if (Canon.SensorHeight)
+		fprintf(outfile, "SensorHeight         = %d\n", Canon.SensorHeight);
+	if (Canon.SensorLeftBorder != -1)
+		fprintf(outfile, "SensorLeftBorder     = %d\n", Canon.SensorLeftBorder);
+	if (Canon.SensorTopBorder != -1)
+		fprintf(outfile, "SensorTopBorder      = %d\n", Canon.SensorTopBorder);
+	if (Canon.SensorRightBorder)
+		fprintf(outfile, "SensorRightBorder    = %d\n", Canon.SensorRightBorder);
+	if (Canon.SensorBottomBorder)
+		fprintf(outfile, "SensorBottomBorder   = %d\n", Canon.SensorBottomBorder);
+	if (Canon.BlackMaskLeftBorder)
+		fprintf(outfile, "BlackMaskLeftBorder  = %d\n", Canon.BlackMaskLeftBorder);
+	if (Canon.BlackMaskTopBorder)
+		fprintf(outfile, "BlackMaskTopBorder   = %d\n", Canon.BlackMaskTopBorder);
+	if (Canon.BlackMaskRightBorder)
+		fprintf(outfile, "BlackMaskRightBorder = %d\n", Canon.BlackMaskRightBorder);
+	if (Canon.BlackMaskBottomBorder)
+		fprintf(outfile, "BlackMaskBottomBorder= %d\n", Canon.BlackMaskBottomBorder);
+
+	if (Hasselblad.BaseISO)
+		fprintf(outfile, "Hasselblad base ISO: %d\n", Hasselblad.BaseISO);
+	if (Hasselblad.Gain)
+		fprintf(outfile, "Hasselblad gain: %g\n", Hasselblad.Gain);
+
+	fprintf(outfile, "Raw colors: %d", P1.colors);
+	if (P1.filters)
+	{
+		fprintf(outfile, "\nFilter pattern: ");
+		if (!P1.cdesc[3])
+			P1.cdesc[3] = 'G';
+		for (int i = 0; i < 16; i++)
+			putchar(P1.cdesc[MyCoolRawProcessor.fcol(i >> 1, i & 1)]);
+	}
+
+	if (Canon.ChannelBlackLevel[0])
+		fprintf(outfile, "\nCanon makernotes, ChannelBlackLevel: %d %d %d %d",
+		Canon.ChannelBlackLevel[0], Canon.ChannelBlackLevel[1],
+		Canon.ChannelBlackLevel[2], Canon.ChannelBlackLevel[3]);
+
+	if (C.black)
+	{
+		fprintf(outfile, "\nblack: %d", C.black);
+	}
+	if (C.cblack[0] != 0)
+	{
+		fprintf(outfile, "\ncblack[0 .. 3]:");
+		for (int c = 0; c < 4; c++)
+			fprintf(outfile, " %d", C.cblack[c]);
+	}
+	if ((C.cblack[4] * C.cblack[5]) > 0)
+	{
+		fprintf(outfile, "\nBlackLevelRepeatDim: %d x %d\n", C.cblack[4], C.cblack[5]);
+		int n = C.cblack[4] * C.cblack[5];
+		fprintf(outfile, "cblack[6 .. %d]:", 6 + n - 1);
+		for (int c = 6; c < 6 + n; c++)
+			fprintf(outfile, " %d", C.cblack[c]);
+	}
+
+	if (C.linear_max[0] != 0)
+	{
+		fprintf(outfile, "\nHighlight linearity limits:");
+		for (int c = 0; c < 4; c++)
+			fprintf(outfile, " %ld", C.linear_max[c]);
+	}
+
+	if (P1.colors > 1)
+	{
+		fprintf(outfile, "\nMakernotes WB data:               coeffs                  EVs");
+		if ((C.cam_mul[0] > 0) && (C.cam_mul[1] > 0)) {
+      fprintf(outfile, "\n  %-23s   %g %g %g %g   %5.2f %5.2f %5.2f %5.2f",
+		          "As shot",
+		          C.cam_mul[0], C.cam_mul[1], C.cam_mul[2], C.cam_mul[3],
+		          roundf(log2(C.cam_mul[0] / C.cam_mul[1])*100.0f)/100.0f,
+		          0.0f,
+		          roundf(log2(C.cam_mul[2] / C.cam_mul[1])*100.0f)/100.0f,
+		          C.cam_mul[3]?roundf(log2(C.cam_mul[3] / C.cam_mul[1])*100.0f)/100.0f:0.0f);
+		}
+
+		for (int cnt = 0; cnt < int(sizeof WBToStr / sizeof *WBToStr); cnt++) {
+			WBi = WBToStr[cnt].NumId;
+			if ((C.WB_Coeffs[WBi][0] > 0) && (C.WB_Coeffs[WBi][1] > 0)) {
+        denom = (float)C.WB_Coeffs[WBi][1];
+        fprintf(outfile, "\n  %-23s   %4d %4d %4d %4d   %5.2f %5.2f %5.2f %5.2f",
+		            WBToStr[cnt].hrStrId,
+		            C.WB_Coeffs[WBi][0], C.WB_Coeffs[WBi][1], C.WB_Coeffs[WBi][2], C.WB_Coeffs[WBi][3],
+		            roundf(log2((float)C.WB_Coeffs[WBi][0] / denom)*100.0f)/100.0f,
+		            0.0f,
+		            roundf(log2((float)C.WB_Coeffs[WBi][2] / denom)*100.0f)/100.0f,
+		            C.WB_Coeffs[3]?roundf(log2((float)C.WB_Coeffs[WBi][3] / denom)*100.0f)/100.0f:0.0f);
+			}
+		}
+
+		if ((Nikon.ME_WB[0] != 0.0f) && (Nikon.ME_WB[0] != 1.0f))
+		{
+			fprintf(outfile, "\nNikon multi-exposure WB multipliers:");
+			for (int c = 0; c < 4; c++)
+				fprintf(outfile, " %g", Nikon.ME_WB[c]);
+		}
+
+		if (C.rgb_cam[0][0] > 0.0001)
+		{
+			fprintf(outfile, "\n\nCamera2RGB matrix (mode: %d):\n", MyCoolRawProcessor.imgdata.params.use_camera_matrix);
+            PRINTMATRIX3x4(outfile,C.rgb_cam,P1.colors);
+		}
+
+		fprintf(outfile, "\nXYZ->CamRGB matrix:\n");
+        PRINTMATRIX4x3(outfile,C.cam_xyz,P1.colors);
+
+    for (int cnt = 0; cnt < 2; cnt++) {
+      if (fabsf(C.P1_color[cnt].romm_cam[0]) > 0)
+      {
+        fprintf(outfile, "\nPhaseOne Matrix %d:\n", cnt+1);
+        for (int i = 0; i < 3; i++)
+          fprintf(outfile, "%6.4f\t%6.4f\t%6.4f\n", C.P1_color[cnt].romm_cam[i * 3],
+          C.P1_color[cnt].romm_cam[i * 3 + 1],
+          C.P1_color[cnt].romm_cam[i * 3 + 2]);
+      }
+    }
+
+		if (fabsf(C.cmatrix[0][0]) > 0)
+		{
+			fprintf(outfile, "\ncamRGB -> sRGB Matrix:\n");
+            PRINTMATRIX3x4(outfile,C.cmatrix,P1.colors);
+		}
+
+		if (fabsf(C.ccm[0][0]) > 0)
+		{
+			fprintf(outfile, "\nColor Correction Matrix:\n");
+            PRINTMATRIX3x4(outfile,C.ccm,P1.colors);
+		}
+
+    for (int cnt = 0; cnt < 2; cnt++) {
+      if (C.dng_color[cnt].illuminant != LIBRAW_WBI_None) {
+        if (C.dng_color[cnt].illuminant <= LIBRAW_WBI_StudioTungsten) {
+          fprintf(outfile, "\nDNG Illuminant %d: %s",
+            cnt + 1, WB_idx2hrstr(C.dng_color[cnt].illuminant));
+        }
+        else if (C.dng_color[cnt].illuminant == LIBRAW_WBI_Other) {
+          fprintf(outfile, "\nDNG Illuminant %d: Other", cnt + 1);
+        }
+        else {
+          fprintf(outfile, "\nDNG Illuminant %d is out of EXIF LightSources range [0:24, 255]: %d",
+            cnt + 1, C.dng_color[cnt].illuminant);
+        }
+      }
+    }
+
+    for (int n=0; n<2; n++) {
+      if (fabsf(C.dng_color[n].colormatrix[0][0]) > 0)
+      {
+        fprintf(outfile, "\nDNG color matrix %d:\n", n+1);
+        PRINTMATRIX4x3(outfile,C.dng_color[n].colormatrix,P1.colors);
+      }
+    }
+
+    for (int n=0; n<2; n++) {
+      if (fabsf(C.dng_color[n].calibration[0][0]) > 0)
+      {
+        fprintf(outfile, "\nDNG calibration matrix %d:\n", n+1);
+        for (int i = 0; i < P1.colors && i < 4; i++)
+        {
+          for (int j = 0; j < P1.colors && j < 4; j++)
+            fprintf(outfile, "%6.4f\t", C.dng_color[n].calibration[j][i]);
+          fprintf(outfile, "\n");
+        }
+      }
+    }
+
+    for (int n=0; n<2; n++) {
+      if (fabsf(C.dng_color[n].forwardmatrix[0][0]) > 0)
+      {
+        fprintf(outfile, "\nDNG forward matrix %d:\n", n+1);
+        PRINTMATRIX3x4(outfile,C.dng_color[n].forwardmatrix,P1.colors);
+      }
+    }
+
+		fprintf(outfile, "\nDerived D65 multipliers:");
+		for (int c = 0; c < P1.colors; c++)
+			fprintf(outfile, " %f", C.pre_mul[c]);
+	}
+	fprintf(outfile, "\nColor space (makernotes) : %d, %s", P3.ColorSpace, ColorSpaceName);
+	fprintf(outfile, "\n");
+
+	if (Sony.PixelShiftGroupID)
+	{
+		fprintf(outfile, "\nSony PixelShiftGroupPrefix 0x%x PixelShiftGroupID %d, ",
+			Sony.PixelShiftGroupPrefix, Sony.PixelShiftGroupID);
+		if (Sony.numInPixelShiftGroup)
+		{
+			fprintf(outfile, "shot# %d (starts at 1) of total %d\n",
+				Sony.numInPixelShiftGroup, Sony.nShotsInPixelShiftGroup);
+		}
+		else
+		{
+			fprintf(outfile, "shots in PixelShiftGroup %d, already ARQ\n",
+				Sony.nShotsInPixelShiftGroup);
+		}
+	}
+
+	if (Sony.Sony0x9400_version)
+		fprintf(outfile, "\nSONY Sequence data, tag 0x9400 version '%x'\n"
+		"\tReleaseMode2: %d\n"
+		"\tSequenceImageNumber: %d (starts at zero)\n"
+		"\tSequenceLength1: %d shot(s)\n"
+		"\tSequenceFileNumber: %d (starts at zero, exiftool starts at 1)\n"
+		"\tSequenceLength2: %d file(s)\n",
+		Sony.Sony0x9400_version, Sony.Sony0x9400_ReleaseMode2,
+		Sony.Sony0x9400_SequenceImageNumber,
+		Sony.Sony0x9400_SequenceLength1,
+		Sony.Sony0x9400_SequenceFileNumber,
+		Sony.Sony0x9400_SequenceLength2);
+}
+
+void print_wbfun(FILE* outfile, LibRaw& MyCoolRawProcessor, std::string& fn)
+{
+	int WBi;
+	float denom;
+	const char *CamMakerName = CameraMaker_idx2str(P1.maker_index);
+	fprintf(outfile, "// %s %s\n", P1.make, P1.model);
+	for (int cnt = 0; cnt < int(sizeof WBToStr / sizeof *WBToStr); cnt++) {
+		WBi = WBToStr[cnt].NumId;
+		if (C.WB_Coeffs[WBi][0] && C.WB_Coeffs[WBi][1] && !WBToStr[cnt].aux_setting) {
+      denom = (float) C.WB_Coeffs[WBi][1];
+      fprintf(outfile, "{%s, \"%s\", %s, {%6.5ff, 1.0f, %6.5ff, ", CamMakerName,
+        P1.normalized_model, WBToStr[cnt].StrId,
+        C.WB_Coeffs[WBi][0] / denom,
+        C.WB_Coeffs[WBi][2] / denom);
+      if (C.WB_Coeffs[WBi][1] == C.WB_Coeffs[WBi][3])
+        fprintf(outfile, "1.0f}},\n");
+      else
+        fprintf(outfile, "%6.5ff}},\n",
+        C.WB_Coeffs[WBi][3] / denom);
+    }
+  }
+
+	for (int cnt = 0; cnt < 64; cnt++)
+		if (C.WBCT_Coeffs[cnt][0])
+		{
+			fprintf(outfile, "{%s, \"%s\", %d, {%6.5ff, 1.0f, %6.5ff, ", CamMakerName,
+				P1.normalized_model, (int)C.WBCT_Coeffs[cnt][0],
+				C.WBCT_Coeffs[cnt][1] / C.WBCT_Coeffs[cnt][2],
+				C.WBCT_Coeffs[cnt][3] / C.WBCT_Coeffs[cnt][2]);
+			if (C.WBCT_Coeffs[cnt][2] == C.WBCT_Coeffs[cnt][4])
+				fprintf(outfile, "1.0f}},\n");
+			else
+				fprintf(outfile, "%6.5ff}},\n",
+				C.WBCT_Coeffs[cnt][4] / C.WBCT_Coeffs[cnt][2]);
+		}
+		else
+			break;
+	fprintf(outfile, "\n");
+}
+
+void print_szfun(FILE* outfile, LibRaw& MyCoolRawProcessor, std::string& fn)
+{
+	fprintf(outfile, "%s\t%s\t%s\t%d\t%d\n", fn.c_str(), P1.make, P1.model, S.width,
+		S.height);
+	/*
+	fprintf(outfile, "\n%s\t%s\t%s\n", filelist[i].c_str(), P1.make, P1.model);
+	fprintf(outfile, "\tCoffin:\traw %dx%d mrg %d %d img %dx%d\n",
+	  S.raw_width, S.raw_height, S.left_margin, S.top_margin, S.width, S.height);
+	fprintf (outfile, "\tmnote: \t");
+	if (Canon.SensorWidth)
+	  fprintf (outfile, "sensor %dx%d ", Canon.SensorWidth, Canon.SensorHeight);
+	if (Nikon.SensorWidth)
+	  fprintf (outfile, "sensor %dx%d ", Nikon.SensorWidth, Nikon.SensorHeight);
+	fprintf(outfile, "inset: start %d %d img %dx%d aspect calc: %f Aspect in file:",
+	  S.raw_inset_crop.cleft, S.raw_inset_crop.ctop,
+	  S.raw_inset_crop.cwidth, S.raw_inset_crop.cheight,
+	  ((float)S.raw_inset_crop.cwidth /
+	  (float)S.raw_inset_crop.cheight));
+	Aspect =
+	  lookup_id2hr(S.raw_inset_crop.aspect, AspectRatios, nAspectRatios);
+	if (Aspect) fprintf (outfile, "%s\n", Aspect->name);
+	else fprintf (outfile, "Other %d\n", S.raw_inset_crop.aspect);
+
+	if (Nikon.SensorHighSpeedCrop.cwidth) {
+	  fprintf(outfile, "\tHighSpeed crop from: %d x %d at top left pixel: (%d, %d)\n",
+	    Nikon.SensorHighSpeedCrop.cwidth, Nikon.SensorHighSpeedCrop.cheight,
+	    Nikon.SensorHighSpeedCrop.cleft, Nikon.SensorHighSpeedCrop.ctop);
+	}
+	*/
+}
+void print_0fun(FILE* outfile, LibRaw& MyCoolRawProcessor, std::string& fn)
+{
+	/*
+	printf ("filename\t%s\tmodel\t%s\tisTSNERDTS\t%d\tRAF
+	version\t%s\tRAF data version\t0x%x\tis4K\t%d\n", av[i], P1.model,
+	Fuji.isTSNERDTS, Fuji.RAFVersion, Fuji.RAFDataVersion,
+	P2.is_4K_RAFdata);
+	*/
+
+}
+
+void print_1fun(FILE* outfile, LibRaw& MyCoolRawProcessor, std::string& fn)
+{
+	char frame[64] = "";
+	snprintf(frame, 64, "rw %d rh %d lm %d tm %d", S.raw_width, S.raw_height,
+		S.left_margin, S.top_margin);
+	fprintf(outfile, "%s=%s=nFms %02d=%s=bps %02d=%s", P1.normalized_make, P1.normalized_model, P1.raw_count,
+		MyCoolRawProcessor.unpack_function_name(), C.raw_bps, frame);
+	fprintf(outfile, "\n");
+}
+
+void print_2fun(FILE* outfile, LibRaw& MyCoolRawProcessor, std::string& fn)
+{
+	fprintf(outfile, "// %s %s", P1.make, P1.model);
+	if (C.cam_mul[0] > 0)
+	{
+		fprintf(outfile, "\n'As shot' WB:");
+		for (int c = 0; c < 4; c++)
+			fprintf(outfile, " %.3f", C.cam_mul[c]);
+	}
+	if (C.WB_Coeffs[LIBRAW_WBI_Auto][0] > 0)
+	{
+		fprintf(outfile, "\n'Camera Auto' WB:");
+		for (int c = 0; c < 4; c++)
+			fprintf(outfile, " %d", C.WB_Coeffs[LIBRAW_WBI_Auto][c]);
+	}
+	if (C.WB_Coeffs[LIBRAW_WBI_Measured][0] > 0)
+	{
+		fprintf(outfile, "\n'Camera Measured' WB:");
+		for (int c = 0; c < 4; c++)
+			fprintf(outfile, " %d", C.WB_Coeffs[LIBRAW_WBI_Measured][c]);
+	}
+	fprintf(outfile, "\n\n");
+}
+
+void print_compactfun(FILE* outfile, LibRaw& MyCoolRawProcessor, std::string& fn)
+{
+	trimSpaces(P1.make);
+	trimSpaces(P1.model);
+	trimSpaces(C.model2);
+	trimSpaces(ShootingInfo.BodySerial);
+	trimSpaces(ShootingInfo.InternalBodySerial);
+	fprintf(outfile, "%s=%s", P1.make, P1.model);
+	if (ShootingInfo.BodySerial[0] &&
+		!(ShootingInfo.BodySerial[0] == 48 && !ShootingInfo.BodySerial[1]))
+		fprintf(outfile, "=Body#: %s", ShootingInfo.BodySerial);
+	else if (C.model2[0] && (!strncasecmp(P1.normalized_make, "Kodak", 5)))
+		fprintf(outfile, "=Body#: %s", C.model2);
+	if (ShootingInfo.InternalBodySerial[0])
+		fprintf(outfile, "=Assy#: %s", ShootingInfo.InternalBodySerial);
+	if (exifLens.LensSerial[0])
+		fprintf(outfile, "=Lens#: %s", exifLens.LensSerial);
+	if (exifLens.InternalLensSerial[0])
+		fprintf(outfile, "=LensAssy#: %s", exifLens.InternalLensSerial);
+	fprintf(outfile, "=\n");
+}
+
+void print_normfun(FILE* outfile, LibRaw& MyCoolRawProcessor, std::string& fn)
+{
+	trimSpaces(P1.make);
+	trimSpaces(P1.model);
+	fprintf(outfile, "\nFilename: %s\n", fn.c_str());
+	fprintf(outfile, "make/model: =%s/%s= ID: 0x%llx   norm. make/model: =%s/%s=\n",
+	        P1.make, P1.model, mnLens.CamID, P1.normalized_make, P1.normalized_model);
+}
+
+void print_unpackfun(FILE* outfile, LibRaw& MyCoolRawProcessor, int print_frame, std::string& fn)
+{
+	char frame[48] = "";
+	if (print_frame)
+	{
+		ushort right_margin = S.raw_width - S.width - S.left_margin;
+		ushort bottom_margin = S.raw_height - S.height - S.top_margin;
+		snprintf(frame, 48, "F=%dx%dx%dx%d RS=%dx%d", S.left_margin,
+			S.top_margin, right_margin, bottom_margin, S.raw_width,
+			S.raw_height);
+	}
+	fprintf(outfile, "%s\t%s\t%s\t%s/%s\n", fn.c_str(),MyCoolRawProcessor.unpack_function_name(), frame, P1.make,	P1.model);
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/samples/rawtextdump.cpp libkdcraw/libkdcraw/libraw/samples/rawtextdump.cpp
--- libkdcraw-wrk/libkdcraw/libraw/samples/rawtextdump.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/samples/rawtextdump.cpp	2022-11-07 07:46:31.730795008 +0300
@@ -0,0 +1,144 @@
+/* -*- C++ -*-
+ * File: raw2text.cpp
+ * Copyright 2008-2020 LibRaw LLC (info@libraw.org)
+ * Created: Sun Sept 01, 2020
+ *
+ * LibRaw sample
+ * Dumps (small) selection of RAW data to text file
+ *
+
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+
+ */
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#ifndef WIN32
+#include <netinet/in.h>
+#else
+#include <winsock2.h>
+#endif
+
+#include "libraw/libraw.h"
+
+void usage(const char *av)
+{
+	printf(
+		"Dump (small) selecton of RAW file as tab-separated text file\n"
+		"Usage: %s inputfile COL ROW [CHANNEL] [width] [height]\n"
+		"  COL - start column\n"
+		"  ROW - start row\n"
+		"  CHANNEL - raw channel to dump, default is 0 (red for rggb)\n"
+		"  width - area width to dump, default is 16\n"
+		"  height - area height to dump, default is 4\n"
+		, av);
+}
+
+unsigned subtract_bl(unsigned int val, int bl)
+{
+	return val > (unsigned)bl ? val - (unsigned)bl : 0;
+}
+
+class LibRaw_bl : public LibRaw
+{
+	public:
+		void adjust_blacklevel() { LibRaw::adjust_bl(); }
+};
+
+int main(int ac, char *av[])
+{
+	if (ac < 4)
+	{
+		usage(av[0]);
+		exit(1);
+	}
+	int colstart = atoi(av[2]);
+	int rowstart = atoi(av[3]);
+	int channel = 0;
+	if (ac > 4) channel = atoi(av[4]);
+	int width = 16;
+	if (ac > 5) width = atoi(av[5]);
+	int height = 4;
+	if (ac > 6) height = atoi(av[6]);
+	if (width <1 || height<1)
+	{
+		usage(av[0]);
+		exit(1);
+	}
+
+	LibRaw_bl lr;
+
+	if (lr.open_file(av[1]) != LIBRAW_SUCCESS)
+	{
+		fprintf(stderr, "Unable to open file %s\n", av[1]);
+		exit(1);
+	}
+	if ((lr.imgdata.idata.colors == 1 && channel>0) || (channel >3))
+	{
+		fprintf(stderr, "Incorrect CHANNEL specified: %d\n", channel);
+		exit(1);
+	}
+	if (lr.unpack() != LIBRAW_SUCCESS)
+	{
+		fprintf(stderr, "Unable to unpack raw data from %s\n", av[1]);
+		exit(1);
+	}
+	lr.adjust_blacklevel();
+	printf("%s\t%d-%d-%dx%d\tchannel: %d\n", av[1], colstart, rowstart, width, height, channel);
+
+	printf("%6s", "R\\C");
+	for (int col = colstart; col < colstart + width && col < lr.imgdata.sizes.raw_width; col++)
+		printf("%6u", col);
+	printf("\n");
+
+	if (lr.imgdata.rawdata.raw_image)
+	{
+		for (int row = rowstart; row < rowstart + height && row < lr.imgdata.sizes.raw_height; row++)
+		{
+			unsigned rcolors[48];
+			if (lr.imgdata.idata.colors > 1)
+				for (int c = 0; c < 48; c++)
+					rcolors[c] = lr.COLOR(row, c);
+			else
+				memset(rcolors, 0, sizeof(rcolors));
+			unsigned short *rowdata = &lr.imgdata.rawdata.raw_image[row * lr.imgdata.sizes.raw_pitch / 2];
+			printf("%6u", row);
+			for (int col = colstart; col < colstart + width && col < lr.imgdata.sizes.raw_width; col++)
+				if (rcolors[col % 48] == (unsigned)channel) printf("%6u", subtract_bl(rowdata[col],lr.imgdata.color.cblack[channel]));
+				else printf("     -");
+			printf("\n");
+		}
+	}
+	else if (lr.imgdata.rawdata.color4_image && channel < 4)
+	{
+		for (int row = rowstart; row < rowstart + height && row < lr.imgdata.sizes.raw_height; row++)
+		{
+			unsigned short(*rowdata)[4] = &lr.imgdata.rawdata.color4_image[row * lr.imgdata.sizes.raw_pitch / 8];
+			printf("%6u", row);
+			for (int col = colstart; col < colstart + width && col < lr.imgdata.sizes.raw_width; col++)
+				printf("%6u", subtract_bl(rowdata[col][channel],lr.imgdata.color.cblack[channel]));
+			printf("\n");
+		}
+	}
+	else if (lr.imgdata.rawdata.color3_image && channel < 3)
+	{
+		for (int row = rowstart; row < rowstart + height && row < lr.imgdata.sizes.raw_height; row++)
+		{
+			unsigned short(*rowdata)[3] = &lr.imgdata.rawdata.color3_image[row * lr.imgdata.sizes.raw_pitch / 6];
+			printf("%6u", row);
+			for (int col = colstart; col < colstart + width && col < lr.imgdata.sizes.raw_width; col++)
+				printf("%6u", subtract_bl(rowdata[col][channel],lr.imgdata.color.cblack[channel]));
+			printf("\n");
+		}
+	}
+	else
+		printf("Unsupported file data (e.g. floating point format), or incorrect channel specified\n");
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/samples/simple_dcraw.cpp libkdcraw/libkdcraw/libraw/samples/simple_dcraw.cpp
--- libkdcraw-wrk/libkdcraw/libraw/samples/simple_dcraw.cpp	2022-11-07 08:15:53.614821808 +0300
+++ libkdcraw/libkdcraw/libraw/samples/simple_dcraw.cpp	2022-11-07 07:46:31.730795008 +0300
@@ -1,213 +1,189 @@
-/*
+/* -*- C++ -*-
  * File: simple_dcraw.cpp
- * Copyright 2008-2009 Alex Tutubalin <lexa@lexa.ru>
- * Created: Sat Mar  8 , 2008
+ * Copyright 2008-2020 LibRaw LLC (info@libraw.org)
+ * Created: Sat Mar  8, 2008
  *
- * LibRaw simple C++ API  (emulates call to "dcraw  [-D]  [-T] [-v] [-e] [-4]")
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
+ * LibRaw simple C++ API:  emulates call to "dcraw  [-D]  [-T] [-v] [-e] [-4]"
+
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+
  */
 #include <stdio.h>
 #include <string.h>
 #include <math.h>
 
-#ifndef WIN32
+#include "libraw/libraw.h"
+
+#ifndef LIBRAW_WIN32_CALLS
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/stat.h>
 #include <sys/mman.h>
 #endif
 
-#include "libraw/libraw.h"
-
-#ifdef WIN32
+#ifdef LIBRAW_WIN32_CALLS
 #define snprintf _snprintf
 #endif
 
-int my_progress_callback(void *unused_data,enum LibRaw_progress state,int iter, int expected)
+int my_progress_callback(void *unused_data, enum LibRaw_progress state,
+                         int iter, int expected)
 {
-    if(iter==0)
-        printf("CB: state=%x, expected %d iterations\n",state,expected);
-    return 0;
+  if (iter == 0)
+    printf("CB: state=%x, expected %d iterations\n", state, expected);
+  return 0;
 }
 
+char *customCameras[] = {
+    (char *)"43704960,4080,5356, 0, 0, 0, 0,0,148,0,0, Dalsa, FTF4052C Full,0",
+    (char *)"42837504,4008,5344, 0, 0, 0, 0,0,148,0,0,Dalsa, FTF4052C 3:4",
+    (char *)"32128128,4008,4008, 0, 0, 0, 0,0,148,0,0,Dalsa, FTF4052C 1:1",
+    (char *)"24096096,4008,3006, 0, 0, 0, 0,0,148,0,0,Dalsa, FTF4052C 4:3",
+    (char *)"18068064,4008,2254, 0, 0, 0, 0,0,148,0,0,Dalsa, FTF4052C 16:9",
+    (char *)"67686894,5049,6703, 0, 0, 0, 0,0,148,0,0,Dalsa, FTF5066C Full",
+    (char *)"66573312,4992,6668, 0, 0, 0, 0,0,148,0,0,Dalsa, FTF5066C 3:4",
+    (char *)"49840128,4992,4992, 0, 0, 0, 0,0,148,0,0,Dalsa, FTF5066C 1:1",
+    (char *)"37400064,4992,3746, 0, 0, 0, 0,0,148,0,0,Dalsa, FTF5066C 4:3",
+    (char *)"28035072,4992,2808, 0, 0, 0, 0,0,148,0,0,Dalsa, FTF5066C 16:9",
+    NULL};
 
 int main(int ac, char *av[])
 {
-    int  i, ret, verbose=0, output_thumbs=0,use_mmap=0,msize;
-    void *file_buffer;
+  int i, ret, verbose = 0, output_thumbs = 0;
+
+  // don't use fixed size buffers in real apps!
+  char outfn[1024], thumbfn[1024];
+
+  LibRaw* RawProcessor = new LibRaw;
+  RawProcessor->imgdata.params.custom_camera_strings = customCameras;
+  if (ac < 2)
+  {
+    printf("simple_dcraw - LibRaw %s sample. Emulates dcraw [-D] [-T] [-v] "
+           "[-e] [-E]\n"
+           " %d cameras supported\n"
+           "Usage: %s [-D] [-T] [-v] [-e] raw-files....\n"
+           "\t-4 - 16-bit mode\n"
+           "\t-L - list supported cameras and exit\n"
+           "\t-v - verbose output\n"
+           "\t-T - output TIFF files instead of .pgm/ppm\n"
+           "\t-e - extract thumbnails (same as dcraw -e in separate run)\n",
+           LibRaw::version(), LibRaw::cameraCount(), av[0]);
+    delete RawProcessor;
+    return 0;
+  }
 
-    // don't use fixed size buffers in real apps!
-    char outfn[1024],thumbfn[1024]; 
+  putenv((char *)"TZ=UTC"); // dcraw compatibility, affects TIFF datestamp field
 
-    LibRaw RawProcessor;
-    if(ac<2) 
+#define P1 RawProcessor->imgdata.idata
+#define S RawProcessor->imgdata.sizes
+#define C RawProcessor->imgdata.color
+#define T RawProcessor->imgdata.thumbnail
+#define P2 RawProcessor->imgdata.other
+#define OUT RawProcessor->imgdata.params
+
+  for (i = 1; i < ac; i++)
+  {
+    if (av[i][0] == '-')
+    {
+      if (av[i][1] == 'T' && av[i][2] == 0)
+        OUT.output_tiff = 1;
+      if (av[i][1] == 'v' && av[i][2] == 0)
+        verbose++;
+      if (av[i][1] == 'e' && av[i][2] == 0)
+        output_thumbs++;
+      if (av[i][1] == '4' && av[i][2] == 0)
+        OUT.output_bps = 16;
+      if (av[i][1] == 'C' && av[i][2] == 0)
+        RawProcessor->set_progress_handler(my_progress_callback, NULL);
+      if (av[i][1] == 'L' && av[i][2] == 0)
+      {
+        const char **clist = LibRaw::cameraList();
+        const char **cc = clist;
+        while (*cc)
         {
-            printf(
-                "simple_dcraw - LibRaw %s sample. Emulates dcraw [-D] [-T] [-v] [-e] [-B]\n"
-                " %d cameras supported\n"
-                "Usage: %s [-D] [-T] [-v] [-e] raw-files....\n"
-                "\t-D - document mode emulation\n"
-                "\t-4 - 16-bit mode\n"
-                "\t-v - verbose output\n"
-                "\t-T - output TIFF files instead of .pgm/ppm\n"
-#ifndef WIN32
-                "\t-B - use mmap()-ed I/O (Unix only)\n"
-#endif
-                "\t-e - extract thumbnails (same as dcraw -e in separate run)\n",LibRaw::version(),
-                LibRaw::cameraCount(),
-                av[0]);
-            return 0;
+          printf("%s\n", *cc);
+          cc++;
+        }
+        delete RawProcessor;
+        exit(0);
+      }
+      continue;
+    }
+
+    if (verbose)
+      printf("Processing file %s\n", av[i]);
+
+    if ((ret = RawProcessor->open_file(av[i])) != LIBRAW_SUCCESS)
+    {
+      fprintf(stderr, "Cannot open_file %s: %s\n", av[i], libraw_strerror(ret));
+      continue; // no recycle b/c open file will recycle itself
+    }
+
+    if (!output_thumbs) // No unpack for thumb extraction
+      if ((ret = RawProcessor->unpack()) != LIBRAW_SUCCESS)
+      {
+        fprintf(stderr, "Cannot unpack %s: %s\n", av[i], libraw_strerror(ret));
+        continue;
+      }
+
+    // thumbnail unpacking and output in the middle of main
+    // image processing - for test purposes!
+    if (output_thumbs)
+    {
+      if ((ret = RawProcessor->unpack_thumb()) != LIBRAW_SUCCESS)
+      {
+        fprintf(stderr, "Cannot unpack_thumb %s: %s\n", av[i],
+                libraw_strerror(ret));
+        if (LIBRAW_FATAL_ERROR(ret))
+          continue; // skip to next file
+      }
+      else
+      {
+        snprintf(thumbfn, sizeof(thumbfn), "%s.%s", av[i],
+                 T.tformat == LIBRAW_THUMBNAIL_JPEG ? "thumb.jpg"
+                                                    : "thumb.ppm");
+
+        if (verbose)
+          printf("Writing thumbnail file %s\n", thumbfn);
+        if (LIBRAW_SUCCESS != (ret = RawProcessor->dcraw_thumb_writer(thumbfn)))
+        {
+          fprintf(stderr, "Cannot write %s: %s\n", thumbfn,
+                  libraw_strerror(ret));
+          if (LIBRAW_FATAL_ERROR(ret))
+            continue;
         }
-    
-    putenv ((char*)"TZ=UTC"); // dcraw compatibility, affects TIFF datestamp field
+      }
+      continue;
+    }
+
+    ret = RawProcessor->dcraw_process();
+
+    if (LIBRAW_SUCCESS != ret)
+    {
+      fprintf(stderr, "Cannot do postpocessing on %s: %s\n", av[i],
+              libraw_strerror(ret));
+      if (LIBRAW_FATAL_ERROR(ret))
+        continue;
+    }
+    snprintf(outfn, sizeof(outfn), "%s.%s", av[i],
+             OUT.output_tiff ? "tiff" : (P1.colors > 1 ? "ppm" : "pgm"));
+
+    if (verbose)
+      printf("Writing file %s\n", outfn);
 
-#define P1 RawProcessor.imgdata.idata
-#define S RawProcessor.imgdata.sizes
-#define C RawProcessor.imgdata.color
-#define T RawProcessor.imgdata.thumbnail
-#define P2 RawProcessor.imgdata.other
-#define OUT RawProcessor.imgdata.params
+    if (LIBRAW_SUCCESS != (ret = RawProcessor->dcraw_ppm_tiff_writer(outfn)))
+      fprintf(stderr, "Cannot write %s: %s\n", outfn, libraw_strerror(ret));
 
+    RawProcessor->recycle(); // just for show this call
+  }
 
-    for (i=1;i<ac;i++)
-        {
-            if(av[i][0]=='-')
-                {
-                    if(av[i][1]=='T' && av[i][2]==0)
-                        OUT.output_tiff=1;
-                    if(av[i][1]=='v' && av[i][2]==0)
-                        verbose++;
-                    if(av[i][1]=='e' && av[i][2]==0)
-                        output_thumbs++;
-                    if(av[i][1]=='D' && av[i][2]==0)
-                        OUT.document_mode=2;
-                    if(av[i][1]=='B' && av[i][2]==0)
-                        use_mmap=1;
-                    if(av[i][1]=='4' && av[i][2]==0)
-                        OUT.output_bps=16;
-                    if(av[i][1]=='C' && av[i][2]==0)
-                        RawProcessor.set_progress_handler(my_progress_callback,NULL);
-                    continue;
-                }
-
-            if(verbose) printf("Processing file %s\n",av[i]);
-
-#ifndef WIN32
-            if(use_mmap)
-                {
-                    int file = open(av[i],O_RDONLY);
-                    struct stat st;
-                    if(file<0)
-                        {
-                            fprintf(stderr,"Cannot open %s: %s\n",av[i],strerror(errno));
-                            continue;
-                        }
-                    if(fstat(file,&st))
-                        {
-                            fprintf(stderr,"Cannot stat %s: %s\n",av[i],strerror(errno));
-                            close(file);
-                            continue;
-                        }
-                    int pgsz = getpagesize();
-                    msize = ((st.st_size+pgsz-1)/pgsz)*pgsz;
-                    file_buffer = mmap(NULL,msize,PROT_READ,MAP_PRIVATE,file,0);
-                    if(!file_buffer)
-                        {
-                            fprintf(stderr,"Cannot mmap %s: %s\n",av[i],strerror(errno));
-                            close(file);
-                            continue;
-                        }
-                    close(file);
-                    if( (ret = RawProcessor.open_buffer(file_buffer,st.st_size) != LIBRAW_SUCCESS))
-                        {
-                            fprintf(stderr,"Cannot open_buffer %s: %s\n",av[i],libraw_strerror(ret));
-                            continue; // no recycle b/c open file will recycle itself
-                        }
-                }
-            else
-#endif
-                {
-                    if( (ret = RawProcessor.open_file(av[i])) != LIBRAW_SUCCESS)
-                        {
-                            fprintf(stderr,"Cannot open_file %s: %s\n",av[i],libraw_strerror(ret));
-                            continue; // no recycle b/c open file will recycle itself
-                        }
-                }
-
-            if( (ret = RawProcessor.unpack() ) != LIBRAW_SUCCESS)
-                {
-                    fprintf(stderr,"Cannot unpack %s: %s\n",av[i],libraw_strerror(ret));
-                    continue;
-                }
-
-            // thumbnail unpacking and output in the middle of main
-            // image processing - for test purposes!
-            if(output_thumbs)
-                {
-                    if( (ret = RawProcessor.unpack_thumb() ) != LIBRAW_SUCCESS)
-                        {
-                            fprintf(stderr,"Cannot unpack_thumb %s: %s\n",av[i],libraw_strerror(ret));
-                            if(LIBRAW_FATAL_ERROR(ret))
-                                continue; // skip to next file
-                        }
-                    else
-                        {
-                            snprintf(thumbfn,sizeof(thumbfn),"%s.%s",
-                                     av[i],T.tformat == LIBRAW_THUMBNAIL_JPEG ? "thumb.jpg" : "thumb.ppm");
-
-                            if(verbose) printf("Writing thumbnail file %s\n",thumbfn);
-                            if( LIBRAW_SUCCESS != (ret = RawProcessor.dcraw_thumb_writer(thumbfn)))
-                                {
-                                    fprintf(stderr,"Cannot write %s: %s\n",thumbfn,libraw_strerror(ret));
-                                    if(LIBRAW_FATAL_ERROR(ret))
-                                        continue; 
-                                }
-                        }
-                }
-            
-            if(OUT.document_mode)
-                ret = RawProcessor.dcraw_document_mode_processing();
-            else
-                ret = RawProcessor.dcraw_process();
-                
-            if(LIBRAW_SUCCESS !=ret)
-                {
-                    fprintf(stderr,"Cannot do postpocessing on %s: %s\n",
-                            av[i],libraw_strerror(ret));
-                    if(LIBRAW_FATAL_ERROR(ret))
-                        continue; 
-                }
-            snprintf(outfn,sizeof(outfn),
-                     "%s.%s",
-                     av[i], OUT.output_tiff ? "tiff" : (P1.colors>1?"ppm":"pgm"));
-
-            if(verbose) printf("Writing file %s\n",outfn);
-
-            if( LIBRAW_SUCCESS != (ret = RawProcessor.dcraw_ppm_tiff_writer(outfn)))
-                fprintf(stderr,"Cannot write %s: %s\n",outfn,libraw_strerror(ret));
-
-#ifndef WIN32            
-            if(use_mmap && file_buffer)
-                {
-                    munmap(file_buffer,msize);
-                    file_buffer=0;
-                }
-#endif
-            RawProcessor.recycle(); // just for show this call
-        }
-    return 0;
+  delete RawProcessor;
+  return 0;
 }
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/samples/unprocessed_raw.cpp libkdcraw/libkdcraw/libraw/samples/unprocessed_raw.cpp
--- libkdcraw-wrk/libkdcraw/libraw/samples/unprocessed_raw.cpp	2022-11-07 08:15:53.614821808 +0300
+++ libkdcraw/libkdcraw/libraw/samples/unprocessed_raw.cpp	2022-11-07 07:46:31.730795008 +0300
@@ -1,156 +1,318 @@
-/*
+/* -*- C++ -*-
  * File: unprocessed_raw.cpp
- * Copyright 2009 Alex Tutubalin <lexa@lexa.ru>
+ * Copyright 2009-2020 LibRaw LLC (info@libraw.org)
  * Created: Fri Jan 02, 2009
  *
  * LibRaw sample
- * Generates unprocessed raw image: with masked pixels and without black subtraction
+ * Generates unprocessed raw image: with masked pixels and without black
+subtraction
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
+
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
  */
 #include <stdio.h>
 #include <string.h>
 #include <math.h>
-#ifndef WIN32
+#include <time.h>
+
+#include "libraw/libraw.h"
+
+#ifndef LIBRAW_WIN32_CALLS
 #include <netinet/in.h>
 #else
+#include <sys/utime.h>
 #include <winsock2.h>
 #endif
 
-#include "libraw/libraw.h"
-
-#ifdef WIN32
+#ifdef LIBRAW_WIN32_CALLS
 #define snprintf _snprintf
 #endif
 
+#if !(LIBRAW_COMPILE_CHECK_VERSION_NOTLESS(0, 14))
+#error This code is for LibRaw 0.14+ only
+#endif
+
+void gamma_curve(unsigned short curve[]);
+void write_ppm(unsigned width, unsigned height, unsigned short *bitmap,
+               const char *basename);
+void write_tiff(int width, int height, unsigned short *bitmap,
+                const char *basename);
+
 int main(int ac, char *av[])
 {
-    int  i, ret;
-    int verbose=1,autoscale=0;
-    char outfn[1024],thumbfn[1024]; 
-
-    LibRaw RawProcessor;
-    if(ac<2) 
-        {
-          usage:
-            printf(
-                "unprocessed_raw - LibRaw %s sample. %d cameras supported\n"
-                "Usage: %s [-q] [-A] [-g] [-s N] [-N] raw-files....\n"
-                "\t-q - be quiet\n"
-                "\t-s N - select Nth image in file (default=0)\n"
-                "\t-g - use gamma correction with gamma 2.2 (not precise,use for visual inspection only)\n"
-                "\t-A - autoscaling (by integer factor)\n"
-                "\t-N - no raw curve\n"
-                ,LibRaw::version(),
-                LibRaw::cameraCount(),
-                av[0]);
-            return 0;
-        }
-    
-#define P1 RawProcessor.imgdata.idata
+  int i, ret;
+  int verbose = 1, autoscale = 0, use_gamma = 0, out_tiff = 0;
+  char outfn[1024];
+
+  LibRaw RawProcessor;
+  if (ac < 2)
+  {
+  usage:
+    printf("unprocessed_raw - LibRaw %s sample. %d cameras supported\n"
+           "Usage: %s [-q] [-A] [-g] [-s N] raw-files....\n"
+           "\t-q - be quiet\n"
+           "\t-s N - select Nth image in file (default=0)\n"
+           "\t-g - use gamma correction with gamma 2.2 (not precise,use for "
+           "visual inspection only)\n"
+           "\t-A - autoscaling (by integer factor)\n"
+           "\t-T - write tiff instead of pgm\n",
+           LibRaw::version(), LibRaw::cameraCount(), av[0]);
+    return 0;
+  }
+
 #define S RawProcessor.imgdata.sizes
-#define C RawProcessor.imgdata.color
-#define T RawProcessor.imgdata.thumbnail
-#define P2 RawProcessor.imgdata.other
 #define OUT RawProcessor.imgdata.params
 
-    OUT.document_mode=2;
-    OUT.output_bps=16;
-    OUT.output_tiff=1;
-    OUT.user_flip=0;
-    OUT.no_auto_bright = 1;
-    OUT.filtering_mode=(LibRaw_filtering)( LIBRAW_FILTERING_NOBLACKS|LIBRAW_FILTERING_NOZEROES);
-    for (i=1;i<ac;i++)
-        {
-            if(av[i][0]=='-')
-                {
-                    if(av[i][1]=='q' && av[i][2]==0)
-                        verbose=0;
-                    else if(av[i][1]=='A' && av[i][2]==0)
-                        autoscale=1;
-                    else if(av[i][1]=='g' && av[i][2]==0)
-                        OUT.gamma_16bit=1;
-                    else if(av[i][1]=='N' && av[i][2]==0)
-                        OUT.filtering_mode=LIBRAW_FILTERING_NONE;
-                    else if(av[i][1]=='s' && av[i][2]==0)
-                        {
-                            i++;
-                            OUT.shot_select=atoi(av[i]);
-                        }
-                    else
-                        goto usage;
-                    continue;
-                }
-            int r,c;
-            if(verbose) printf("Processing file %s\n",av[i]);
-            if( (ret = RawProcessor.open_file(av[i])) != LIBRAW_SUCCESS)
-                {
-                    fprintf(stderr,"Cannot open %s: %s\n",av[i],libraw_strerror(ret));
-                    continue; // no recycle b/c open file will recycle itself
-                }
-            if(verbose)
-                {
-                    printf("Image size: %dx%d\nRaw size: %dx%d\n",S.width,S.height,S.raw_width,S.raw_height);
-                    printf("Margins: top=%d, left=%d, right=%d, bottom=%d\n",
-                           S.top_margin,S.left_margin,S.right_margin,S.bottom_margin);
-                }
-
-            if( (ret = RawProcessor.unpack() ) != LIBRAW_SUCCESS)
-                {
-                    fprintf(stderr,"Cannot unpack %s: %s\n",av[i],libraw_strerror(ret));
-                    continue;
-                }
-            if(verbose)
-                printf("Unpacked....\n");
-
-            if( (ret = RawProcessor.add_masked_borders_to_bitmap() ) != LIBRAW_SUCCESS)
-                {
-                    fprintf(stderr,"Cannot add mask data to bitmap %s\n",av[i]);
-                }
-            for(int r=0;r<S.iheight;r++)
-                for(c=0;c<S.iwidth;c++)
-                    RawProcessor.imgdata.image[r*S.iwidth+c][0] 
-                        = RawProcessor.imgdata.image[r*S.iwidth+c][RawProcessor.FC(r,c)];
-
-            P1.colors=1;
-            if(autoscale)
-                {
-                    unsigned max=0,scale;
-                    for(int j=0; j<S.iheight*S.iwidth; j++)
-                        if(max < RawProcessor.imgdata.image[j][0])
-                            max = RawProcessor.imgdata.image[j][0]; 
-                    if (max >0 && max< 1<<15)
-                        {
-                            scale = (1<<16)/max;
-                            if(verbose)
-                                printf("Scaling with multiplier=%d (max=%d)\n",scale,max);
-                            
-                            for(int j=0; j<S.iheight*S.iwidth; j++)
-                                RawProcessor.imgdata.image[j][0] *= scale;
-                        }
-                }
-            
-            if(OUT.shot_select)
-                snprintf(outfn,sizeof(outfn),"%s-%d.tiff",av[i],OUT.shot_select);
-            else
-                snprintf(outfn,sizeof(outfn),"%s.tiff",av[i]);
-
-            if(verbose) printf("Writing file %s\n",outfn);
-            if( LIBRAW_SUCCESS != (ret = RawProcessor.dcraw_ppm_tiff_writer(outfn)))
-                fprintf(stderr,"Cannot write %s: %s\n",outfn,libraw_strerror(ret));
-        }
-    return 0;
+  for (i = 1; i < ac; i++)
+  {
+    if (av[i][0] == '-')
+    {
+      if (av[i][1] == 'q' && av[i][2] == 0)
+        verbose = 0;
+      else if (av[i][1] == 'A' && av[i][2] == 0)
+        autoscale = 1;
+      else if (av[i][1] == 'g' && av[i][2] == 0)
+        use_gamma = 1;
+      else if (av[i][1] == 'T' && av[i][2] == 0)
+        out_tiff = 1;
+      else if (av[i][1] == 's' && av[i][2] == 0)
+      {
+        i++;
+        OUT.shot_select = av[i] ? atoi(av[i]) : 0;
+      }
+      else
+        goto usage;
+      continue;
+    }
+
+    if (verbose)
+      printf("Processing file %s\n", av[i]);
+    if ((ret = RawProcessor.open_file(av[i])) != LIBRAW_SUCCESS)
+    {
+      fprintf(stderr, "Cannot open %s: %s\n", av[i], libraw_strerror(ret));
+      continue; // no recycle b/c open file will recycle itself
+    }
+    if (verbose)
+    {
+      printf("Image size: %dx%d\nRaw size: %dx%d\n", S.width, S.height,
+             S.raw_width, S.raw_height);
+      printf("Margins: top=%d, left=%d\n", S.top_margin, S.left_margin);
+    }
+
+    if ((ret = RawProcessor.unpack()) != LIBRAW_SUCCESS)
+    {
+      fprintf(stderr, "Cannot unpack %s: %s\n", av[i], libraw_strerror(ret));
+      continue;
+    }
+
+    if (verbose)
+      printf("Unpacked....\n");
+
+    if (!(RawProcessor.imgdata.idata.filters ||
+          RawProcessor.imgdata.idata.colors == 1))
+    {
+      printf("Only Bayer-pattern RAW files supported, sorry....\n");
+      continue;
+    }
+
+    if (autoscale)
+    {
+      unsigned max = 0, scale;
+      for (int j = 0; j < S.raw_height * S.raw_width; j++)
+        if (max < RawProcessor.imgdata.rawdata.raw_image[j])
+          max = RawProcessor.imgdata.rawdata.raw_image[j];
+      if (max > 0 && max < 1 << 15)
+      {
+        scale = (1 << 16) / max;
+        if (verbose)
+          printf("Scaling with multiplier=%d (max=%d)\n", scale, max);
+
+        for (int j = 0; j < S.raw_height * S.raw_width; j++)
+          RawProcessor.imgdata.rawdata.raw_image[j] *= scale;
+      }
+    }
+    if (use_gamma)
+    {
+      unsigned short curve[0x10000];
+      gamma_curve(curve);
+      for (int j = 0; j < S.raw_height * S.raw_width; j++)
+        RawProcessor.imgdata.rawdata.raw_image[j] =
+            curve[RawProcessor.imgdata.rawdata.raw_image[j]];
+      if (verbose)
+        printf("Gamma-corrected....\n");
+    }
+
+    if (OUT.shot_select)
+      snprintf(outfn, sizeof(outfn), "%s-%d.%s", av[i], OUT.shot_select,
+               out_tiff ? "tiff" : "pgm");
+    else
+      snprintf(outfn, sizeof(outfn), "%s.%s", av[i], out_tiff ? "tiff" : "pgm");
+
+    if (out_tiff)
+      write_tiff(S.raw_width, S.raw_height,
+                 RawProcessor.imgdata.rawdata.raw_image, outfn);
+    else
+      write_ppm(S.raw_width, S.raw_height,
+                RawProcessor.imgdata.rawdata.raw_image, outfn);
+
+    if (verbose)
+      printf("Stored to file %s\n", outfn);
+  }
+  return 0;
+}
+
+void write_ppm(unsigned width, unsigned height, unsigned short *bitmap,
+               const char *fname)
+{
+  if (!bitmap)
+    return;
+
+  FILE *f = fopen(fname, "wb");
+  if (!f)
+    return;
+  int bits = 16;
+  fprintf(f, "P5\n%d %d\n%d\n", width, height, (1 << bits) - 1);
+  unsigned char *data = (unsigned char *)bitmap;
+  unsigned data_size = width * height * 2;
+#define SWAP(a, b)                                                             \
+  {                                                                            \
+    a ^= b;                                                                    \
+    a ^= (b ^= a);                                                             \
+  }
+  for (unsigned i = 0; i < data_size; i += 2)
+    SWAP(data[i], data[i + 1]);
+#undef SWAP
+  fwrite(data, data_size, 1, f);
+  fclose(f);
+}
+
+/*  == gamma curve and tiff writer - simplified cut'n'paste from dcraw.c */
+
+#define SQR(x) ((x) * (x))
+
+void gamma_curve(unsigned short *curve)
+{
+
+  double pwr = 1.0 / 2.2;
+  double ts = 0.0;
+  int imax = 0xffff;
+  int mode = 2;
+  int i;
+  double g[6], bnd[2] = {0, 0}, r;
+
+  g[0] = pwr;
+  g[1] = ts;
+  g[2] = g[3] = g[4] = 0;
+  bnd[g[1] >= 1] = 1;
+  if (g[1] && (g[1] - 1) * (g[0] - 1) <= 0)
+  {
+    for (i = 0; i < 48; i++)
+    {
+      g[2] = (bnd[0] + bnd[1]) / 2;
+      if (g[0])
+        bnd[(pow(g[2] / g[1], -g[0]) - 1) / g[0] - 1 / g[2] > -1] = g[2];
+      else
+        bnd[g[2] / exp(1 - 1 / g[2]) < g[1]] = g[2];
+    }
+    g[3] = g[2] / g[1];
+    if (g[0])
+      g[4] = g[2] * (1 / g[0] - 1);
+  }
+  if (g[0])
+    g[5] = 1 / (g[1] * SQR(g[3]) / 2 - g[4] * (1 - g[3]) +
+                (1 - pow(g[3], 1 + g[0])) * (1 + g[4]) / (1 + g[0])) -
+           1;
+  else
+    g[5] = 1 / (g[1] * SQR(g[3]) / 2 + 1 - g[2] - g[3] -
+                g[2] * g[3] * (log(g[3]) - 1)) -
+           1;
+  for (i = 0; i < 0x10000; i++)
+  {
+    curve[i] = 0xffff;
+    if ((r = (double)i / imax) < 1)
+      curve[i] =
+          0x10000 *
+          (mode ? (r < g[3] ? r * g[1]
+                            : (g[0] ? pow(r, g[0]) * (1 + g[4]) - g[4]
+                                    : log(r) * g[2] + 1))
+                : (r < g[2] ? r / g[1]
+                            : (g[0] ? pow((r + g[4]) / (1 + g[4]), 1 / g[0])
+                                    : exp((r - 1) / g[2]))));
+  }
+}
+
+void tiff_set(ushort *ntag, ushort tag, ushort type, int count, int val)
+{
+  struct libraw_tiff_tag *tt;
+  int c;
+
+  tt = (struct libraw_tiff_tag *)(ntag + 1) + (*ntag)++;
+  tt->tag = tag;
+  tt->type = type;
+  tt->count = count;
+  if ((type < LIBRAW_EXIFTAG_TYPE_SHORT) && (count <= 4))
+    for (c = 0; c < 4; c++)
+      tt->val.c[c] = val >> (c << 3);
+  else if (tagtypeIs(LIBRAW_EXIFTAG_TYPE_SHORT) && (count <= 2))
+    for (c = 0; c < 2; c++)
+      tt->val.s[c] = val >> (c << 4);
+  else
+    tt->val.i = val;
+}
+#define TOFF(ptr) ((char *)(&(ptr)) - (char *)th)
+
+void tiff_head(int width, int height, struct tiff_hdr *th)
+{
+  int c;
+  time_t timestamp = time(NULL);
+  struct tm *t;
+
+  memset(th, 0, sizeof *th);
+  th->t_order = htonl(0x4d4d4949) >> 16;
+  th->magic = 42;
+  th->ifd = 10;
+  tiff_set(&th->ntag, 254, 4, 1, 0);
+  tiff_set(&th->ntag, 256, 4, 1, width);
+  tiff_set(&th->ntag, 257, 4, 1, height);
+  tiff_set(&th->ntag, 258, 3, 1, 16);
+  for (c = 0; c < 4; c++)
+    th->bps[c] = 16;
+  tiff_set(&th->ntag, 259, 3, 1, 1);
+  tiff_set(&th->ntag, 262, 3, 1, 1);
+  tiff_set(&th->ntag, 273, 4, 1, sizeof *th);
+  tiff_set(&th->ntag, 277, 3, 1, 1);
+  tiff_set(&th->ntag, 278, 4, 1, height);
+  tiff_set(&th->ntag, 279, 4, 1, height * width * 2);
+  tiff_set(&th->ntag, 282, 5, 1, TOFF(th->rat[0]));
+  tiff_set(&th->ntag, 283, 5, 1, TOFF(th->rat[2]));
+  tiff_set(&th->ntag, 284, 3, 1, 1);
+  tiff_set(&th->ntag, 296, 3, 1, 2);
+  tiff_set(&th->ntag, 306, 2, 20, TOFF(th->date));
+  th->rat[0] = th->rat[2] = 300;
+  th->rat[1] = th->rat[3] = 1;
+  t = localtime(&timestamp);
+  if (t)
+    sprintf(th->date, "%04d:%02d:%02d %02d:%02d:%02d", t->tm_year + 1900,
+            t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
+}
+
+void write_tiff(int width, int height, unsigned short *bitmap, const char *fn)
+{
+  struct tiff_hdr th;
+
+  FILE *ofp = fopen(fn, "wb");
+  if (!ofp)
+    return;
+  tiff_head(width, height, &th);
+  fwrite(&th, sizeof th, 1, ofp);
+  fwrite(bitmap, 2, width * height, ofp);
+  fclose(ofp);
 }
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/Makefile libkdcraw/libkdcraw/libraw/src/Makefile
--- libkdcraw-wrk/libkdcraw/libraw/src/Makefile	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/Makefile	2022-11-07 07:46:31.642795007 +0300
@@ -0,0 +1,2 @@
+all:
+	(cd ..; make library)
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/decoders/canon_600.cpp libkdcraw/libkdcraw/libraw/src/decoders/canon_600.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/decoders/canon_600.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/decoders/canon_600.cpp	2022-11-07 07:46:31.730795008 +0300
@@ -0,0 +1,224 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+void LibRaw::canon_600_fixed_wb(int temp)
+{
+  static const short mul[4][5] = {{667, 358, 397, 565, 452},
+                                  {731, 390, 367, 499, 517},
+                                  {1119, 396, 348, 448, 537},
+                                  {1399, 485, 431, 508, 688}};
+  int lo, hi, i;
+  float frac = 0;
+
+  for (lo = 4; --lo;)
+    if (*mul[lo] <= temp)
+      break;
+  for (hi = 0; hi < 3; hi++)
+    if (*mul[hi] >= temp)
+      break;
+  if (lo != hi)
+    frac = (float)(temp - *mul[lo]) / (*mul[hi] - *mul[lo]);
+  for (i = 1; i < 5; i++)
+    pre_mul[i - 1] = 1 / (frac * mul[hi][i] + (1 - frac) * mul[lo][i]);
+}
+
+/* Return values:  0 = white  1 = near white  2 = not white */
+int LibRaw::canon_600_color(int ratio[2], int mar)
+{
+  int clipped = 0, target, miss;
+
+  if (flash_used)
+  {
+    if (ratio[1] < -104)
+    {
+      ratio[1] = -104;
+      clipped = 1;
+    }
+    if (ratio[1] > 12)
+    {
+      ratio[1] = 12;
+      clipped = 1;
+    }
+  }
+  else
+  {
+    if (ratio[1] < -264 || ratio[1] > 461)
+      return 2;
+    if (ratio[1] < -50)
+    {
+      ratio[1] = -50;
+      clipped = 1;
+    }
+    if (ratio[1] > 307)
+    {
+      ratio[1] = 307;
+      clipped = 1;
+    }
+  }
+  target = flash_used || ratio[1] < 197 ? -38 - (398 * ratio[1] >> 10)
+                                        : -123 + (48 * ratio[1] >> 10);
+  if (target - mar <= ratio[0] && target + 20 >= ratio[0] && !clipped)
+    return 0;
+  miss = target - ratio[0];
+  if (abs(miss) >= mar * 4)
+    return 2;
+  if (miss < -20)
+    miss = -20;
+  if (miss > mar)
+    miss = mar;
+  ratio[0] = target - miss;
+  return 1;
+}
+
+void LibRaw::canon_600_auto_wb()
+{
+  int mar, row, col, i, j, st, count[] = {0, 0};
+  int test[8], total[2][8], ratio[2][2], stat[2];
+
+  memset(&total, 0, sizeof total);
+  i = canon_ev + 0.5;
+  if (i < 10)
+    mar = 150;
+  else if (i > 12)
+    mar = 20;
+  else
+    mar = 280 - 20 * i;
+  if (flash_used)
+    mar = 80;
+  for (row = 14; row < height - 14; row += 4)
+    for (col = 10; col < width; col += 2)
+    {
+      for (i = 0; i < 8; i++)
+        test[(i & 4) + FC(row + (i >> 1), col + (i & 1))] =
+            BAYER(row + (i >> 1), col + (i & 1));
+      for (i = 0; i < 8; i++)
+        if (test[i] < 150 || test[i] > 1500)
+          goto next;
+      for (i = 0; i < 4; i++)
+        if (abs(test[i] - test[i + 4]) > 50)
+          goto next;
+      for (i = 0; i < 2; i++)
+      {
+        for (j = 0; j < 4; j += 2)
+          ratio[i][j >> 1] =
+              ((test[i * 4 + j + 1] - test[i * 4 + j]) << 10) / test[i * 4 + j];
+        stat[i] = canon_600_color(ratio[i], mar);
+      }
+      if ((st = stat[0] | stat[1]) > 1)
+        goto next;
+      for (i = 0; i < 2; i++)
+        if (stat[i])
+          for (j = 0; j < 2; j++)
+            test[i * 4 + j * 2 + 1] =
+                test[i * 4 + j * 2] * (0x400 + ratio[i][j]) >> 10;
+      for (i = 0; i < 8; i++)
+        total[st][i] += test[i];
+      count[st]++;
+    next:;
+    }
+  if (count[0] | count[1])
+  {
+    st = count[0] * 200 < count[1];
+    for (i = 0; i < 4; i++)
+      pre_mul[i] = 1.0 / (total[st][i] + total[st][i + 4]);
+  }
+}
+
+void LibRaw::canon_600_coeff()
+{
+  static const short table[6][12] = {
+      {-190, 702, -1878, 2390, 1861, -1349, 905, -393, -432, 944, 2617, -2105},
+      {-1203, 1715, -1136, 1648, 1388, -876, 267, 245, -1641, 2153, 3921,
+       -3409},
+      {-615, 1127, -1563, 2075, 1437, -925, 509, 3, -756, 1268, 2519, -2007},
+      {-190, 702, -1886, 2398, 2153, -1641, 763, -251, -452, 964, 3040, -2528},
+      {-190, 702, -1878, 2390, 1861, -1349, 905, -393, -432, 944, 2617, -2105},
+      {-807, 1319, -1785, 2297, 1388, -876, 769, -257, -230, 742, 2067, -1555}};
+  int t = 0, i, c;
+  float mc, yc;
+
+  mc = pre_mul[1] / pre_mul[2];
+  yc = pre_mul[3] / pre_mul[2];
+  if (mc > 1 && mc <= 1.28 && yc < 0.8789)
+    t = 1;
+  if (mc > 1.28 && mc <= 2)
+  {
+    if (yc < 0.8789)
+      t = 3;
+    else if (yc <= 2)
+      t = 4;
+  }
+  if (flash_used)
+    t = 5;
+  for (raw_color = i = 0; i < 3; i++)
+    FORCC rgb_cam[i][c] = table[t][i * 4 + c] / 1024.0;
+}
+
+void LibRaw::canon_600_load_raw()
+{
+  uchar data[1120], *dp;
+  ushort *pix;
+  int irow, row;
+
+  for (irow = row = 0; irow < height; irow++)
+  {
+    checkCancel();
+    if (fread(data, 1, 1120, ifp) < 1120)
+      derror();
+    pix = raw_image + row * raw_width;
+    for (dp = data; 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);
+    }
+    if ((row += 2) > height)
+      row = 1;
+  }
+}
+
+void LibRaw::canon_600_correct()
+{
+  int row, col, val;
+  static const short mul[4][2] = {
+      {1141, 1145}, {1128, 1109}, {1178, 1149}, {1128, 1109}};
+
+  for (row = 0; row < height; row++)
+  {
+    checkCancel();
+    for (col = 0; col < width; col++)
+    {
+      if ((val = BAYER(row, col) - black) < 0)
+        val = 0;
+      val = val * mul[row & 3][col & 1] >> 9;
+      BAYER(row, col) = val;
+    }
+  }
+  canon_600_fixed_wb(1311);
+  canon_600_auto_wb();
+  canon_600_coeff();
+  maximum = (0x3ff - black) * 1109 >> 9;
+  black = 0;
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/decoders/crx.cpp libkdcraw/libkdcraw/libraw/src/decoders/crx.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/decoders/crx.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/decoders/crx.cpp	2022-11-07 07:46:31.730795008 +0300
@@ -0,0 +1,2544 @@
+/* -*- C++ -*-
+ * File: libraw_crxdec.cpp
+ * Copyright (C) 2018-2019 Alexey Danilchenko
+ * Copyright (C) 2019 Alex Tutubalin, LibRaw LLC
+ *
+   Canon CR3 file decoder
+
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/libraw_cxx_defs.h"
+
+#ifdef _abs
+#undef _abs
+#undef _min
+#undef _constrain
+#endif
+#define _abs(x) (((x) ^ ((int32_t)(x) >> 31)) - ((int32_t)(x) >> 31))
+#define _min(a, b) ((a) < (b) ? (a) : (b))
+#define _constrain(x, l, u) ((x) < (l) ? (l) : ((x) > (u) ? (u) : (x)))
+
+#if defined(__clang__) || defined(__GNUG__)
+#define libraw_inline inline __attribute__((always_inline))
+#elif defined(_MSC_VER) && _MSC_VER > 1400
+#define libraw_inline __forceinline
+#else
+#define libraw_inline inline
+#endif
+
+// this should be divisible by 4
+#define CRX_BUF_SIZE 0x10000
+#if !defined(_WIN32) || (defined (__GNUC__) && !defined(__INTRINSIC_SPECIAL__BitScanReverse))
+/* __INTRINSIC_SPECIAL__BitScanReverse found in MinGW32-W64 v7.30 headers, may be there is a better solution? */
+typedef uint32_t DWORD;
+libraw_inline void _BitScanReverse(DWORD *Index, unsigned long Mask)
+{
+  *Index = sizeof(unsigned long) * 8 - 1 - __builtin_clzl(Mask);
+}
+#if LibRawBigEndian
+#define _byteswap_ulong(x) (x)
+#else
+#define _byteswap_ulong(x) __builtin_bswap32(x)
+#endif
+#endif
+
+struct CrxBitstream
+{
+  uint8_t mdatBuf[CRX_BUF_SIZE];
+  uint64_t mdatSize;
+  uint64_t curBufOffset;
+  uint32_t curPos;
+  uint32_t curBufSize;
+  uint32_t bitData;
+  int32_t bitsLeft;
+  LibRaw_abstract_datastream *input;
+};
+
+struct CrxBandParam
+{
+  CrxBitstream bitStream;
+  int16_t subbandWidth;
+  int16_t subbandHeight;
+  int32_t roundedBitsMask;
+  int32_t roundedBits;
+  int16_t curLine;
+  int32_t *lineBuf0;
+  int32_t *lineBuf1;
+  int32_t *lineBuf2;
+  int32_t sParam;
+  int32_t kParam;
+  int32_t *paramData;
+  int32_t *nonProgrData;
+  int8_t supportsPartial;
+};
+
+struct CrxWaveletTransform
+{
+  int32_t *subband0Buf;
+  int32_t *subband1Buf;
+  int32_t *subband2Buf;
+  int32_t *subband3Buf;
+  int32_t *lineBuf[8];
+  int16_t curLine;
+  int16_t curH;
+  int8_t fltTapH;
+  int16_t height;
+  int16_t width;
+};
+
+struct CrxSubband
+{
+  CrxBandParam *bandParam;
+  uint64_t mdatOffset;
+  uint8_t *bandBuf;
+  int32_t bandSize;
+  uint64_t dataSize;
+  int8_t supportsPartial;
+  int32_t quantValue;
+  uint16_t width;
+  uint16_t height;
+  int32_t paramK;
+  int64_t dataOffset;
+};
+
+struct CrxPlaneComp
+{
+  uint8_t *compBuf;
+  CrxSubband *subBands;
+  CrxWaveletTransform *waveletTransform;
+  int8_t compNumber;
+  int64_t dataOffset;
+  int32_t compSize;
+  int8_t supportsPartial;
+  int32_t roundedBitsMask;
+  int8_t tileFlag;
+};
+
+struct CrxTile
+{
+  CrxPlaneComp *comps;
+  int8_t tileFlag;
+  int8_t tileNumber;
+  int64_t dataOffset;
+  int32_t tileSize;
+  uint16_t width;
+  uint16_t height;
+};
+
+struct CrxImage
+{
+  uint8_t nPlanes;
+  uint16_t planeWidth;
+  uint16_t planeHeight;
+  uint8_t samplePrecision;
+  uint8_t subbandCount;
+  uint8_t levels;
+  uint8_t nBits;
+  uint8_t encType;
+  uint8_t tileCols;
+  uint8_t tileRows;
+  CrxTile *tiles;
+  uint64_t mdatOffset;
+  uint64_t mdatSize;
+  int16_t *outBufs[4]; // one per plane
+  int16_t *planeBuf;
+  LibRaw_abstract_datastream *input;
+#ifdef LIBRAW_CR3_MEMPOOL
+  libraw_memmgr memmgr;
+  CrxImage() : memmgr(0){}
+#endif
+};
+
+enum TileFlags
+{
+  E_HAS_TILES_ON_THE_RIGHT = 1,
+  E_HAS_TILES_ON_THE_LEFT = 2,
+  E_HAS_TILES_ON_THE_BOTTOM = 4,
+  E_HAS_TILES_ON_THE_TOP = 8
+};
+
+int32_t exCoefNumTbl[0x120] = {
+    // level 1
+    1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+    1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+    1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
+    1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+    // level 2
+    1, 1, 3, 3, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 3, 2, 1, 0, 1, 0, 0, 0, 0, 0, 1,
+    2, 4, 4, 2, 1, 2, 1, 0, 0, 0, 0, 1, 1, 4, 3, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1,
+    3, 3, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 3, 2, 1, 0, 1, 0, 0, 0, 0, 0, 1, 2, 4,
+    4, 2, 1, 2, 1, 0, 0, 0, 0, 1, 1, 4, 3, 1, 1, 1, 1, 0, 0, 0, 0,
+
+    // level 3
+    1, 1, 7, 7, 1, 1, 3, 3, 1, 1, 1, 1, 1, 0, 7, 6, 1, 0, 3, 2, 1, 0, 1, 0, 1,
+    2, 10, 10, 2, 2, 5, 4, 2, 1, 2, 1, 1, 1, 10, 9, 1, 2, 4, 4, 2, 1, 2, 1, 1,
+    1, 9, 9, 1, 2, 4, 4, 2, 1, 2, 1, 1, 0, 9, 8, 1, 1, 4, 3, 1, 1, 1, 1, 1, 2,
+    8, 8, 2, 1, 4, 3, 1, 1, 1, 1, 1, 1, 8, 7, 1, 1, 3, 3, 1, 1, 1, 1};
+
+uint32_t JS[32] = {1,     1,     1,     1,     2,      2,      2,      2,
+                   4,     4,     4,     4,     8,      8,      8,      8,
+                   0x10,  0x10,  0x20,  0x20,  0x40,   0x40,   0x80,   0x80,
+                   0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000};
+
+uint32_t J[32] = {0, 0, 0, 0, 1,    1,    1,    1,    2,    2,   2,
+                  2, 3, 3, 3, 3,    4,    4,    5,    5,    6,   6,
+                  7, 7, 8, 9, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
+
+static inline void crxFillBuffer(CrxBitstream *bitStrm)
+{
+  if (bitStrm->curPos >= bitStrm->curBufSize && bitStrm->mdatSize)
+  {
+    bitStrm->curPos = 0;
+    bitStrm->curBufOffset += bitStrm->curBufSize;
+#ifdef LIBRAW_USE_OPENMP
+#pragma omp critical
+#endif
+    {
+#ifndef LIBRAW_USE_OPENMP
+      bitStrm->input->lock();
+#endif
+      bitStrm->input->seek(bitStrm->curBufOffset, SEEK_SET);
+      bitStrm->curBufSize = bitStrm->input->read(
+          bitStrm->mdatBuf, 1, _min(bitStrm->mdatSize, CRX_BUF_SIZE));
+#ifndef LIBRAW_USE_OPENMP
+      bitStrm->input->unlock();
+#endif
+      if (bitStrm->curBufSize < 1) // nothing read
+        throw LIBRAW_EXCEPTION_IO_EOF;
+      bitStrm->mdatSize -= bitStrm->curBufSize;
+    }
+  }
+}
+
+libraw_inline int crxBitstreamGetZeros(CrxBitstream *bitStrm)
+{
+  uint32_t nonZeroBit = 0;
+  uint64_t nextData = 0;
+  int32_t result = 0;
+
+  if (bitStrm->bitData)
+  {
+    _BitScanReverse((DWORD *)&nonZeroBit, (DWORD)bitStrm->bitData);
+    result = 31 - nonZeroBit;
+    bitStrm->bitData <<= 32 - nonZeroBit;
+    bitStrm->bitsLeft -= 32 - nonZeroBit;
+  }
+  else
+  {
+    uint32_t bitsLeft = bitStrm->bitsLeft;
+    while (1)
+    {
+      while (bitStrm->curPos + 4 <= bitStrm->curBufSize)
+      {
+        nextData =
+            _byteswap_ulong(*(uint32_t *)(bitStrm->mdatBuf + bitStrm->curPos));
+        bitStrm->curPos += 4;
+        crxFillBuffer(bitStrm);
+        if (nextData)
+        {
+          _BitScanReverse((DWORD *)&nonZeroBit, (DWORD)nextData);
+          result = bitsLeft + 31 - nonZeroBit;
+          bitStrm->bitData = nextData << (32 - nonZeroBit);
+          bitStrm->bitsLeft = nonZeroBit;
+          return result;
+        }
+        bitsLeft += 32;
+      }
+      if (bitStrm->curBufSize < bitStrm->curPos + 1)
+        break; // error
+      nextData = bitStrm->mdatBuf[bitStrm->curPos++];
+      crxFillBuffer(bitStrm);
+      if (nextData)
+        break;
+      bitsLeft += 8;
+    }
+    _BitScanReverse((DWORD *)&nonZeroBit, (DWORD)nextData);
+    result = (uint32_t)(bitsLeft + 7 - nonZeroBit);
+    bitStrm->bitData = nextData << (32 - nonZeroBit);
+    bitStrm->bitsLeft = nonZeroBit;
+  }
+  return result;
+}
+
+libraw_inline uint32_t crxBitstreamGetBits(CrxBitstream *bitStrm, int bits)
+{
+  int bitsLeft = bitStrm->bitsLeft;
+  uint32_t bitData = bitStrm->bitData;
+  uint32_t nextWord;
+  uint8_t nextByte;
+  uint32_t result;
+
+  if (bitsLeft < bits)
+  {
+    // get them from stream
+    if (bitStrm->curPos + 4 <= bitStrm->curBufSize)
+    {
+      nextWord =
+          _byteswap_ulong(*(uint32_t *)(bitStrm->mdatBuf + bitStrm->curPos));
+      bitStrm->curPos += 4;
+      crxFillBuffer(bitStrm);
+      bitStrm->bitsLeft = 32 - (bits - bitsLeft);
+      result = ((nextWord >> bitsLeft) | bitData) >> (32 - bits);
+      bitStrm->bitData = nextWord << (bits - bitsLeft);
+      return result;
+    }
+    // less than a word left - read byte at a time
+    do
+    {
+      if (bitStrm->curPos >= bitStrm->curBufSize)
+        break; // error
+      bitsLeft += 8;
+      nextByte = bitStrm->mdatBuf[bitStrm->curPos++];
+      crxFillBuffer(bitStrm);
+      bitData |= nextByte << (32 - bitsLeft);
+    } while (bitsLeft < bits);
+  }
+  result = bitData >> (32 - bits); // 32-bits
+  bitStrm->bitData = bitData << bits;
+  bitStrm->bitsLeft = bitsLeft - bits;
+  return result;
+}
+
+libraw_inline int crxPredictKParameter(int32_t prevK, int32_t bitCode,
+                                       int32_t maxVal = 0)
+{
+  int32_t newKParam = prevK - (bitCode < (1 << prevK >> 1)) +
+                      ((bitCode >> prevK) > 2) + ((bitCode >> prevK) > 5);
+
+  return !maxVal || newKParam < maxVal ? newKParam : maxVal;
+}
+
+libraw_inline void crxDecodeSymbolL1(CrxBandParam *param,
+                                     int32_t doMedianPrediction,
+                                     int32_t notEOL = 0)
+{
+  if (doMedianPrediction)
+  {
+    int32_t symb[4];
+
+    int32_t delta = param->lineBuf0[1] - param->lineBuf0[0];
+    symb[2] = param->lineBuf1[0];
+    symb[0] = symb[1] = delta + symb[2];
+    symb[3] = param->lineBuf0[1];
+
+    param->lineBuf1[1] =
+        symb[(((param->lineBuf0[0] < param->lineBuf1[0]) ^ (delta < 0)) << 1) +
+             ((param->lineBuf1[0] < param->lineBuf0[1]) ^ (delta < 0))];
+  }
+  else
+    param->lineBuf1[1] = param->lineBuf0[1];
+
+  // get next error symbol
+  uint32_t bitCode = crxBitstreamGetZeros(&param->bitStream);
+  if (bitCode >= 41)
+    bitCode = crxBitstreamGetBits(&param->bitStream, 21);
+  else if (param->kParam)
+    bitCode = crxBitstreamGetBits(&param->bitStream, param->kParam) |
+              (bitCode << param->kParam);
+
+  // add converted (+/-) error code to predicted value
+  param->lineBuf1[1] += -(bitCode & 1) ^ (bitCode >> 1);
+
+  // for not end of the line - use one symbol ahead to estimate next K
+  if (notEOL)
+  {
+    int32_t nextDelta = (param->lineBuf0[2] - param->lineBuf0[1]) << 1;
+    bitCode = (bitCode + _abs(nextDelta)) >> 1;
+    ++param->lineBuf0;
+  }
+
+  // update K parameter
+  param->kParam = crxPredictKParameter(param->kParam, bitCode, 15);
+
+  ++param->lineBuf1;
+}
+
+int crxDecodeLine(CrxBandParam *param)
+{
+  int length = param->subbandWidth;
+
+  param->lineBuf1[0] = param->lineBuf0[1];
+  for (; length > 1; --length)
+  {
+    if (param->lineBuf1[0] != param->lineBuf0[1] ||
+        param->lineBuf1[0] != param->lineBuf0[2])
+    {
+      crxDecodeSymbolL1(param, 1, 1);
+    }
+    else
+    {
+      int nSyms = 0;
+      if (crxBitstreamGetBits(&param->bitStream, 1))
+      {
+        nSyms = 1;
+        while (crxBitstreamGetBits(&param->bitStream, 1))
+        {
+          nSyms += JS[param->sParam];
+          if (nSyms > length)
+          {
+            nSyms = length;
+            break;
+          }
+          if (param->sParam < 31)
+            ++param->sParam;
+          if (nSyms == length)
+            break;
+        }
+
+        if (nSyms < length)
+        {
+          if (J[param->sParam])
+            nSyms += crxBitstreamGetBits(&param->bitStream, J[param->sParam]);
+          if (param->sParam > 0)
+            --param->sParam;
+          if (nSyms > length)
+            return -1;
+        }
+
+        length -= nSyms;
+
+        // copy symbol nSyms times
+        param->lineBuf0 += nSyms;
+
+        // copy symbol nSyms times
+        while (nSyms-- > 0)
+        {
+          param->lineBuf1[1] = param->lineBuf1[0];
+          ++param->lineBuf1;
+        }
+      }
+
+      if (length > 0)
+        crxDecodeSymbolL1(param, 0, (length > 1));
+    }
+  }
+
+  if (length == 1)
+    crxDecodeSymbolL1(param, 1, 0);
+
+  param->lineBuf1[1] = param->lineBuf1[0] + 1;
+
+  return 0;
+}
+
+libraw_inline void crxDecodeSymbolL1Rounded(CrxBandParam *param,
+                                            int32_t doSym = 1,
+                                            int32_t doCode = 1)
+{
+  int32_t sym = param->lineBuf0[1];
+
+  if (doSym)
+  {
+    // calculate the next symbol gradient
+    int32_t symb[4];
+    int32_t deltaH = param->lineBuf0[1] - param->lineBuf0[0];
+    symb[2] = param->lineBuf1[0];
+    symb[0] = symb[1] = deltaH + symb[2];
+    symb[3] = param->lineBuf0[1];
+    sym =
+        symb[(((param->lineBuf0[0] < param->lineBuf1[0]) ^ (deltaH < 0)) << 1) +
+             ((param->lineBuf1[0] < param->lineBuf0[1]) ^ (deltaH < 0))];
+  }
+
+  uint32_t bitCode = crxBitstreamGetZeros(&param->bitStream);
+  if (bitCode >= 41)
+    bitCode = crxBitstreamGetBits(&param->bitStream, 21);
+  else if (param->kParam)
+    bitCode = crxBitstreamGetBits(&param->bitStream, param->kParam) |
+              (bitCode << param->kParam);
+  int32_t code = -(bitCode & 1) ^ (bitCode >> 1);
+  param->lineBuf1[1] = param->roundedBitsMask * 2 * code + (code >> 31) + sym;
+
+  if (doCode)
+  {
+    if (param->lineBuf0[2] > param->lineBuf0[1])
+      code = (param->lineBuf0[2] - param->lineBuf0[1] + param->roundedBitsMask -
+              1) >>
+             param->roundedBits;
+    else
+      code = -(
+          (param->lineBuf0[1] - param->lineBuf0[2] + param->roundedBitsMask) >>
+          param->roundedBits);
+
+    param->kParam = crxPredictKParameter(param->kParam,
+                                         (bitCode + 2 * _abs(code)) >> 1, 15);
+  }
+  else
+    param->kParam = crxPredictKParameter(param->kParam, bitCode, 15);
+
+  ++param->lineBuf1;
+}
+
+int crxDecodeLineRounded(CrxBandParam *param)
+{
+  int32_t valueReached = 0;
+
+  param->lineBuf0[0] = param->lineBuf0[1];
+  param->lineBuf1[0] = param->lineBuf0[1];
+  int32_t length = param->subbandWidth;
+
+  for (; length > 1; --length)
+  {
+    if (_abs(param->lineBuf0[2] - param->lineBuf0[1]) > param->roundedBitsMask)
+    {
+      crxDecodeSymbolL1Rounded(param);
+      ++param->lineBuf0;
+      valueReached = 1;
+    }
+    else if (valueReached || _abs(param->lineBuf0[0] - param->lineBuf1[0]) >
+                                 param->roundedBitsMask)
+    {
+      crxDecodeSymbolL1Rounded(param);
+      ++param->lineBuf0;
+      valueReached = 0;
+    }
+    else
+    {
+      int nSyms = 0;
+      if (crxBitstreamGetBits(&param->bitStream, 1))
+      {
+        nSyms = 1;
+        while (crxBitstreamGetBits(&param->bitStream, 1))
+        {
+          nSyms += JS[param->sParam];
+          if (nSyms > length)
+          {
+            nSyms = length;
+            break;
+          }
+          if (param->sParam < 31)
+            ++param->sParam;
+          if (nSyms == length)
+            break;
+        }
+        if (nSyms < length)
+        {
+          if (J[param->sParam])
+            nSyms += crxBitstreamGetBits(&param->bitStream, J[param->sParam]);
+          if (param->sParam > 0)
+            --param->sParam;
+        }
+        if (nSyms > length)
+          return -1;
+      }
+      length -= nSyms;
+
+      // copy symbol nSyms times
+      param->lineBuf0 += nSyms;
+
+      // copy symbol nSyms times
+      while (nSyms-- > 0)
+      {
+        param->lineBuf1[1] = param->lineBuf1[0];
+        ++param->lineBuf1;
+      }
+
+      if (length > 1)
+      {
+        crxDecodeSymbolL1Rounded(param, 0);
+        ++param->lineBuf0;
+        valueReached = _abs(param->lineBuf0[1] - param->lineBuf0[0]) >
+                       param->roundedBitsMask;
+      }
+      else if (length == 1)
+        crxDecodeSymbolL1Rounded(param, 0, 0);
+    }
+  }
+  if (length == 1)
+    crxDecodeSymbolL1Rounded(param, 1, 0);
+
+  param->lineBuf1[1] = param->lineBuf1[0] + 1;
+
+  return 0;
+}
+
+int crxDecodeLineNoRefPrevLine(CrxBandParam *param)
+{
+  int32_t i = 0;
+
+  for (; i < param->subbandWidth - 1; i++)
+  {
+    if (param->lineBuf0[i + 2] | param->lineBuf0[i + 1] | param->lineBuf1[i])
+    {
+      uint32_t bitCode = crxBitstreamGetZeros(&param->bitStream);
+      if (bitCode >= 41)
+        bitCode = crxBitstreamGetBits(&param->bitStream, 21);
+      else if (param->kParam)
+        bitCode = crxBitstreamGetBits(&param->bitStream, param->kParam) |
+                  (bitCode << param->kParam);
+      param->lineBuf1[i + 1] = -(bitCode & 1) ^ (bitCode >> 1);
+      param->kParam = crxPredictKParameter(param->kParam, bitCode);
+      if (param->lineBuf2[i + 1] - param->kParam <= 1)
+      {
+        if (param->kParam >= 15)
+          param->kParam = 15;
+      }
+      else
+        ++param->kParam;
+    }
+    else
+    {
+      int nSyms = 0;
+      if (crxBitstreamGetBits(&param->bitStream, 1))
+      {
+        nSyms = 1;
+        if (i != param->subbandWidth - 1)
+        {
+          while (crxBitstreamGetBits(&param->bitStream, 1))
+          {
+            nSyms += JS[param->sParam];
+            if (i + nSyms > param->subbandWidth)
+            {
+              nSyms = param->subbandWidth - i;
+              break;
+            }
+            if (param->sParam < 31)
+              ++param->sParam;
+            if (i + nSyms == param->subbandWidth)
+              break;
+          }
+          if (i + nSyms < param->subbandWidth)
+          {
+            if (J[param->sParam])
+              nSyms += crxBitstreamGetBits(&param->bitStream, J[param->sParam]);
+            if (param->sParam > 0)
+              --param->sParam;
+          }
+          if (i + nSyms > param->subbandWidth)
+            return -1;
+        }
+      }
+      else if (i > param->subbandWidth)
+        return -1;
+
+      if (nSyms > 0)
+      {
+        memset(param->lineBuf1 + i + 1, 0, nSyms * sizeof(int32_t));
+        memset(param->lineBuf2 + i, 0, nSyms * sizeof(int32_t));
+        i += nSyms;
+      }
+
+      if (i >= param->subbandWidth - 1)
+      {
+        if (i == param->subbandWidth - 1)
+        {
+          uint32_t bitCode = crxBitstreamGetZeros(&param->bitStream);
+          if (bitCode >= 41)
+            bitCode = crxBitstreamGetBits(&param->bitStream, 21);
+          else if (param->kParam)
+            bitCode = crxBitstreamGetBits(&param->bitStream, param->kParam) |
+                      (bitCode << param->kParam);
+          param->lineBuf1[i + 1] = -((bitCode + 1) & 1) ^ ((bitCode + 1) >> 1);
+          param->kParam = crxPredictKParameter(param->kParam, bitCode, 15);
+          param->lineBuf2[i] = param->kParam;
+        }
+        continue;
+      }
+      else
+      {
+        uint32_t bitCode = crxBitstreamGetZeros(&param->bitStream);
+        if (bitCode >= 41)
+          bitCode = crxBitstreamGetBits(&param->bitStream, 21);
+        else if (param->kParam)
+          bitCode = crxBitstreamGetBits(&param->bitStream, param->kParam) |
+                    (bitCode << param->kParam);
+        param->lineBuf1[i + 1] = -((bitCode + 1) & 1) ^ ((bitCode + 1) >> 1);
+        param->kParam = crxPredictKParameter(param->kParam, bitCode);
+        if (param->lineBuf2[i + 1] - param->kParam <= 1)
+        {
+          if (param->kParam >= 15)
+            param->kParam = 15;
+        }
+        else
+          ++param->kParam;
+      }
+    }
+    param->lineBuf2[i] = param->kParam;
+  }
+  if (i == param->subbandWidth - 1)
+  {
+    int32_t bitCode = crxBitstreamGetZeros(&param->bitStream);
+    if (bitCode >= 41)
+      bitCode = crxBitstreamGetBits(&param->bitStream, 21);
+    else if (param->kParam)
+      bitCode = crxBitstreamGetBits(&param->bitStream, param->kParam) |
+                (bitCode << param->kParam);
+    param->lineBuf1[i + 1] = -(bitCode & 1) ^ (bitCode >> 1);
+    param->kParam = crxPredictKParameter(param->kParam, bitCode, 15);
+    param->lineBuf2[i] = param->kParam;
+  }
+
+  return 0;
+}
+
+int crxDecodeTopLine(CrxBandParam *param)
+{
+  param->lineBuf1[0] = 0;
+
+  int32_t length = param->subbandWidth;
+
+  // read the line from bitstream
+  for (; length > 1; --length)
+  {
+    if (param->lineBuf1[0])
+      param->lineBuf1[1] = param->lineBuf1[0];
+    else
+    {
+      int nSyms = 0;
+      if (crxBitstreamGetBits(&param->bitStream, 1))
+      {
+        nSyms = 1;
+        while (crxBitstreamGetBits(&param->bitStream, 1))
+        {
+          nSyms += JS[param->sParam];
+          if (nSyms > length)
+          {
+            nSyms = length;
+            break;
+          }
+          if (param->sParam < 31)
+            ++param->sParam;
+          if (nSyms == length)
+            break;
+        }
+        if (nSyms < length)
+        {
+          if (J[param->sParam])
+            nSyms += crxBitstreamGetBits(&param->bitStream, J[param->sParam]);
+          if (param->sParam > 0)
+            --param->sParam;
+          if (nSyms > length)
+            return -1;
+        }
+
+        length -= nSyms;
+
+        // copy symbol nSyms times
+        while (nSyms-- > 0)
+        {
+          param->lineBuf1[1] = param->lineBuf1[0];
+          ++param->lineBuf1;
+        }
+
+        if (length <= 0)
+          break;
+      }
+
+      param->lineBuf1[1] = 0;
+    }
+
+    uint32_t bitCode = crxBitstreamGetZeros(&param->bitStream);
+    if (bitCode >= 41)
+      bitCode = crxBitstreamGetBits(&param->bitStream, 21);
+    else if (param->kParam)
+      bitCode = crxBitstreamGetBits(&param->bitStream, param->kParam) |
+                (bitCode << param->kParam);
+    param->lineBuf1[1] += -(bitCode & 1) ^ (bitCode >> 1);
+    param->kParam = crxPredictKParameter(param->kParam, bitCode, 15);
+    ++param->lineBuf1;
+  }
+
+  if (length == 1)
+  {
+    param->lineBuf1[1] = param->lineBuf1[0];
+    uint32_t bitCode = crxBitstreamGetZeros(&param->bitStream);
+    if (bitCode >= 41)
+      bitCode = crxBitstreamGetBits(&param->bitStream, 21);
+    else if (param->kParam)
+      bitCode = crxBitstreamGetBits(&param->bitStream, param->kParam) |
+                (bitCode << param->kParam);
+    param->lineBuf1[1] += -(bitCode & 1) ^ (bitCode >> 1);
+    param->kParam = crxPredictKParameter(param->kParam, bitCode, 15);
+    ++param->lineBuf1;
+  }
+
+  param->lineBuf1[1] = param->lineBuf1[0] + 1;
+
+  return 0;
+}
+
+int crxDecodeTopLineRounded(CrxBandParam *param)
+{
+  param->lineBuf1[0] = 0;
+
+  int32_t length = param->subbandWidth;
+
+  // read the line from bitstream
+  for (; length > 1; --length)
+  {
+    if (_abs(param->lineBuf1[0]) > param->roundedBitsMask)
+      param->lineBuf1[1] = param->lineBuf1[0];
+    else
+    {
+      int nSyms = 0;
+      if (crxBitstreamGetBits(&param->bitStream, 1))
+      {
+        nSyms = 1;
+        while (crxBitstreamGetBits(&param->bitStream, 1))
+        {
+          nSyms += JS[param->sParam];
+          if (nSyms > length)
+          {
+            nSyms = length;
+            break;
+          }
+          if (param->sParam < 31)
+            ++param->sParam;
+          if (nSyms == length)
+            break;
+        }
+        if (nSyms < length)
+        {
+          if (J[param->sParam])
+            nSyms += crxBitstreamGetBits(&param->bitStream, J[param->sParam]);
+          if (param->sParam > 0)
+            --param->sParam;
+          if (nSyms > length)
+            return -1;
+        }
+      }
+
+      length -= nSyms;
+
+      // copy symbol nSyms times
+      while (nSyms-- > 0)
+      {
+        param->lineBuf1[1] = param->lineBuf1[0];
+        ++param->lineBuf1;
+      }
+
+      if (length <= 0)
+        break;
+
+      param->lineBuf1[1] = 0;
+    }
+
+    uint32_t bitCode = crxBitstreamGetZeros(&param->bitStream);
+    if (bitCode >= 41)
+      bitCode = crxBitstreamGetBits(&param->bitStream, 21);
+    else if (param->kParam)
+      bitCode = crxBitstreamGetBits(&param->bitStream, param->kParam) |
+                (bitCode << param->kParam);
+
+    int32_t sVal = -(bitCode & 1) ^ (bitCode >> 1);
+    param->lineBuf1[1] += param->roundedBitsMask * 2 * sVal + (sVal >> 31);
+    param->kParam = crxPredictKParameter(param->kParam, bitCode, 15);
+    ++param->lineBuf1;
+  }
+
+  if (length == 1)
+  {
+    uint32_t bitCode = crxBitstreamGetZeros(&param->bitStream);
+    if (bitCode >= 41)
+      bitCode = crxBitstreamGetBits(&param->bitStream, 21);
+    else if (param->kParam)
+      bitCode = crxBitstreamGetBits(&param->bitStream, param->kParam) |
+                (bitCode << param->kParam);
+    int32_t sVal = -(bitCode & 1) ^ (bitCode >> 1);
+    param->lineBuf1[1] += param->roundedBitsMask * 2 * sVal + (sVal >> 31);
+    param->kParam = crxPredictKParameter(param->kParam, bitCode, 15);
+    ++param->lineBuf1;
+  }
+
+  param->lineBuf1[1] = param->lineBuf1[0] + 1;
+
+  return 0;
+}
+
+int crxDecodeTopLineNoRefPrevLine(CrxBandParam *param)
+{
+  param->lineBuf0[0] = 0;
+  param->lineBuf1[0] = 0;
+  int32_t length = param->subbandWidth;
+  for (; length > 1; --length)
+  {
+    if (param->lineBuf1[0])
+    {
+      uint32_t bitCode = crxBitstreamGetZeros(&param->bitStream);
+      if (bitCode >= 41)
+        bitCode = crxBitstreamGetBits(&param->bitStream, 21);
+      else if (param->kParam)
+        bitCode = crxBitstreamGetBits(&param->bitStream, param->kParam) |
+                  (bitCode << param->kParam);
+      param->lineBuf1[1] = -(bitCode & 1) ^ (bitCode >> 1);
+      param->kParam = crxPredictKParameter(param->kParam, bitCode, 15);
+    }
+    else
+    {
+      int nSyms = 0;
+      if (crxBitstreamGetBits(&param->bitStream, 1))
+      {
+        nSyms = 1;
+        while (crxBitstreamGetBits(&param->bitStream, 1))
+        {
+          nSyms += JS[param->sParam];
+          if (nSyms > length)
+          {
+            nSyms = length;
+            break;
+          }
+          if (param->sParam < 31)
+            ++param->sParam;
+          if (nSyms == length)
+            break;
+        }
+        if (nSyms < length)
+        {
+          if (J[param->sParam])
+            nSyms += crxBitstreamGetBits(&param->bitStream, J[param->sParam]);
+          if (param->sParam > 0)
+            --param->sParam;
+          if (nSyms > length)
+            return -1;
+        }
+      }
+
+      length -= nSyms;
+
+      // copy symbol nSyms times
+      while (nSyms-- > 0)
+      {
+        param->lineBuf2[0] = 0;
+        param->lineBuf1[1] = 0;
+        ++param->lineBuf1;
+        ++param->lineBuf2;
+      }
+
+      if (length <= 0)
+        break;
+      uint32_t bitCode = crxBitstreamGetZeros(&param->bitStream);
+      if (bitCode >= 41)
+        bitCode = crxBitstreamGetBits(&param->bitStream, 21);
+      else if (param->kParam)
+        bitCode = crxBitstreamGetBits(&param->bitStream, param->kParam) |
+                  (bitCode << param->kParam);
+      param->lineBuf1[1] = -((bitCode + 1) & 1) ^ ((bitCode + 1) >> 1);
+      param->kParam = crxPredictKParameter(param->kParam, bitCode, 15);
+    }
+    param->lineBuf2[0] = param->kParam;
+    ++param->lineBuf2;
+    ++param->lineBuf1;
+  }
+
+  if (length == 1)
+  {
+    uint32_t bitCode = crxBitstreamGetZeros(&param->bitStream);
+    if (bitCode >= 41)
+      bitCode = crxBitstreamGetBits(&param->bitStream, 21);
+    else if (param->kParam)
+      bitCode = crxBitstreamGetBits(&param->bitStream, param->kParam) |
+                (bitCode << param->kParam);
+    param->lineBuf1[1] = -(bitCode & 1) ^ (bitCode >> 1);
+    param->kParam = crxPredictKParameter(param->kParam, bitCode, 15);
+    param->lineBuf2[0] = param->kParam;
+    ++param->lineBuf1;
+  }
+
+  param->lineBuf1[1] = 0;
+
+  return 0;
+}
+
+int crxDecodeLine(CrxBandParam *param, uint8_t *bandBuf)
+{
+  if (!param || !bandBuf)
+    return -1;
+  if (param->curLine >= param->subbandHeight)
+    return -1;
+
+  if (param->curLine == 0)
+  {
+    int32_t lineLength = param->subbandWidth + 2;
+
+    param->sParam = 0;
+    param->kParam = 0;
+    if (param->supportsPartial)
+    {
+      if (param->roundedBitsMask <= 0)
+      {
+        param->lineBuf0 = (int32_t *)param->paramData;
+        param->lineBuf1 = param->lineBuf0 + lineLength;
+        int32_t *lineBuf = param->lineBuf1 + 1;
+        if (crxDecodeTopLine(param))
+          return -1;
+        memcpy(bandBuf, lineBuf, param->subbandWidth * sizeof(int32_t));
+        ++param->curLine;
+      }
+      else
+      {
+        param->roundedBits = 1;
+        if (param->roundedBitsMask & ~1)
+        {
+          while (param->roundedBitsMask >> param->roundedBits)
+            ++param->roundedBits;
+        }
+        param->lineBuf0 = (int32_t *)param->paramData;
+        param->lineBuf1 = param->lineBuf0 + lineLength;
+        int32_t *lineBuf = param->lineBuf1 + 1;
+        if (crxDecodeTopLineRounded(param))
+          return -1;
+        memcpy(bandBuf, lineBuf, param->subbandWidth * sizeof(int32_t));
+        ++param->curLine;
+      }
+    }
+    else
+    {
+      param->lineBuf2 = (int32_t *)param->nonProgrData;
+      param->lineBuf0 = (int32_t *)param->paramData;
+      param->lineBuf1 = param->lineBuf0 + lineLength;
+      int32_t *lineBuf = param->lineBuf1 + 1;
+      if (crxDecodeTopLineNoRefPrevLine(param))
+        return -1;
+      memcpy(bandBuf, lineBuf, param->subbandWidth * sizeof(int32_t));
+      ++param->curLine;
+    }
+  }
+  else if (!param->supportsPartial)
+  {
+    int32_t lineLength = param->subbandWidth + 2;
+    param->lineBuf2 = (int32_t *)param->nonProgrData;
+    if (param->curLine & 1)
+    {
+      param->lineBuf1 = (int32_t *)param->paramData;
+      param->lineBuf0 = param->lineBuf1 + lineLength;
+    }
+    else
+    {
+      param->lineBuf0 = (int32_t *)param->paramData;
+      param->lineBuf1 = param->lineBuf0 + lineLength;
+    }
+    int32_t *lineBuf = param->lineBuf1 + 1;
+    if (crxDecodeLineNoRefPrevLine(param))
+      return -1;
+    memcpy(bandBuf, lineBuf, param->subbandWidth * sizeof(int32_t));
+    ++param->curLine;
+  }
+  else if (param->roundedBitsMask <= 0)
+  {
+    int32_t lineLength = param->subbandWidth + 2;
+    if (param->curLine & 1)
+    {
+      param->lineBuf1 = (int32_t *)param->paramData;
+      param->lineBuf0 = param->lineBuf1 + lineLength;
+    }
+    else
+    {
+      param->lineBuf0 = (int32_t *)param->paramData;
+      param->lineBuf1 = param->lineBuf0 + lineLength;
+    }
+    int32_t *lineBuf = param->lineBuf1 + 1;
+    if (crxDecodeLine(param))
+      return -1;
+    memcpy(bandBuf, lineBuf, param->subbandWidth * sizeof(int32_t));
+    ++param->curLine;
+  }
+  else
+  {
+    int32_t lineLength = param->subbandWidth + 2;
+    if (param->curLine & 1)
+    {
+      param->lineBuf1 = (int32_t *)param->paramData;
+      param->lineBuf0 = param->lineBuf1 + lineLength;
+    }
+    else
+    {
+      param->lineBuf0 = (int32_t *)param->paramData;
+      param->lineBuf1 = param->lineBuf0 + lineLength;
+    }
+    int32_t *lineBuf = param->lineBuf1 + 1;
+    if (crxDecodeLineRounded(param))
+      return -1;
+    memcpy(bandBuf, lineBuf, param->subbandWidth * sizeof(int32_t));
+    ++param->curLine;
+  }
+  return 0;
+}
+
+int crxDecodeLineWithIQuantization(CrxSubband *subband)
+{
+  int32_t q_step_tbl[6] = {0x28, 0x2D, 0x33, 0x39, 0x40, 0x48};
+
+  if (!subband->dataSize)
+  {
+    memset(subband->bandBuf, 0, subband->bandSize);
+    return 0;
+  }
+
+  if (subband->supportsPartial)
+  {
+    uint32_t bitCode = crxBitstreamGetZeros(&subband->bandParam->bitStream);
+    if (bitCode >= 23)
+      bitCode = crxBitstreamGetBits(&subband->bandParam->bitStream, 8);
+    else if (subband->paramK)
+      bitCode =
+          crxBitstreamGetBits(&subband->bandParam->bitStream, subband->paramK) |
+          (bitCode << subband->paramK);
+
+    subband->quantValue +=
+        -(bitCode & 1) ^ (bitCode >> 1); // converting encoded to signed integer
+    subband->paramK = crxPredictKParameter(subband->paramK, bitCode);
+    if (subband->paramK > 7)
+      return -1;
+  }
+  if (crxDecodeLine(subband->bandParam, subband->bandBuf))
+    return -1;
+
+  if (subband->width <= 0)
+    return 0LL;
+
+  // update subband buffers
+  int32_t *bandBuf = (int32_t *)subband->bandBuf;
+  int32_t qScale =
+      q_step_tbl[subband->quantValue % 6] >> (6 - subband->quantValue / 6);
+  if (subband->quantValue / 6 >= 6)
+    qScale = q_step_tbl[subband->quantValue % 6] *
+             (1 << (subband->quantValue / 6 + 26));
+
+  if (qScale != 1)
+    for (int32_t i = 0; i < subband->width; i++)
+      bandBuf[i] *= qScale;
+
+  return 0;
+}
+
+void crxHorizontal53(int32_t *lineBufLA, int32_t *lineBufLB,
+                     CrxWaveletTransform *wavelet, uint32_t tileFlag)
+{
+  int32_t *band0Buf = wavelet->subband0Buf;
+  int32_t *band1Buf = wavelet->subband1Buf;
+  int32_t *band2Buf = wavelet->subband2Buf;
+  int32_t *band3Buf = wavelet->subband3Buf;
+
+  if (wavelet->width <= 1)
+  {
+    lineBufLA[0] = band0Buf[0];
+    lineBufLB[0] = band2Buf[0];
+  }
+  else
+  {
+    if (tileFlag & E_HAS_TILES_ON_THE_LEFT)
+    {
+      lineBufLA[0] = band0Buf[0] - ((band1Buf[0] + band1Buf[1] + 2) >> 2);
+      lineBufLB[0] = band2Buf[0] - ((band3Buf[0] + band3Buf[1] + 2) >> 2);
+      ++band1Buf;
+      ++band3Buf;
+    }
+    else
+    {
+      lineBufLA[0] = band0Buf[0] - ((band1Buf[0] + 1) >> 1);
+      lineBufLB[0] = band2Buf[0] - ((band3Buf[0] + 1) >> 1);
+    }
+    ++band0Buf;
+    ++band2Buf;
+
+    for (int i = 0; i < wavelet->width - 3; i += 2)
+    {
+      int32_t delta = band0Buf[0] - ((band1Buf[0] + band1Buf[1] + 2) >> 2);
+      lineBufLA[1] = band1Buf[0] + ((delta + lineBufLA[0]) >> 1);
+      lineBufLA[2] = delta;
+
+      delta = band2Buf[0] - ((band3Buf[0] + band3Buf[1] + 2) >> 2);
+      lineBufLB[1] = band3Buf[0] + ((delta + lineBufLB[0]) >> 1);
+      lineBufLB[2] = delta;
+
+      ++band0Buf;
+      ++band1Buf;
+      ++band2Buf;
+      ++band3Buf;
+      lineBufLA += 2;
+      lineBufLB += 2;
+    }
+    if (tileFlag & E_HAS_TILES_ON_THE_RIGHT)
+    {
+      int32_t deltaA = band0Buf[0] - ((band1Buf[0] + band1Buf[1] + 2) >> 2);
+      lineBufLA[1] = band1Buf[0] + ((deltaA + lineBufLA[0]) >> 1);
+
+      int32_t deltaB = band2Buf[0] - ((band3Buf[0] + band3Buf[1] + 2) >> 2);
+      lineBufLB[1] = band3Buf[0] + ((deltaB + lineBufLB[0]) >> 1);
+
+      if (wavelet->width & 1)
+      {
+        lineBufLA[2] = deltaA;
+        lineBufLB[2] = deltaB;
+      }
+    }
+    else if (wavelet->width & 1)
+    {
+      lineBufLA[1] =
+          band1Buf[0] +
+          ((lineBufLA[0] + band0Buf[0] - ((band1Buf[0] + 1) >> 1)) >> 1);
+      lineBufLA[2] = band0Buf[0] - ((band1Buf[0] + 1) >> 1);
+
+      lineBufLB[1] =
+          band3Buf[0] +
+          ((lineBufLB[0] + band2Buf[0] - ((band3Buf[0] + 1) >> 1)) >> 1);
+      lineBufLB[2] = band2Buf[0] - ((band3Buf[0] + 1) >> 1);
+    }
+    else
+    {
+      lineBufLA[1] = lineBufLA[0] + band1Buf[0];
+      lineBufLB[1] = lineBufLB[0] + band3Buf[0];
+    }
+  }
+}
+
+int32_t *crxIdwt53FilterGetLine(CrxPlaneComp *comp, int32_t level)
+{
+  int32_t *result = comp->waveletTransform[level]
+                        .lineBuf[(comp->waveletTransform[level].fltTapH -
+                                  comp->waveletTransform[level].curH + 5) %
+                                     5 +
+                                 3];
+  comp->waveletTransform[level].curH--;
+  return result;
+}
+
+int crxIdwt53FilterDecode(CrxPlaneComp *comp, int32_t level)
+{
+  if (comp->waveletTransform[level].curH)
+    return 0;
+
+  CrxSubband *sband = comp->subBands + 3 * level;
+
+  if (comp->waveletTransform[level].height - 3 <=
+          comp->waveletTransform[level].curLine &&
+      !(comp->tileFlag & E_HAS_TILES_ON_THE_BOTTOM))
+  {
+    if (comp->waveletTransform[level].height & 1)
+    {
+      if (level)
+      {
+        if (crxIdwt53FilterDecode(comp, level - 1))
+          return -1;
+      }
+      else if (crxDecodeLineWithIQuantization(sband))
+        return -1;
+
+      if (crxDecodeLineWithIQuantization(sband + 1))
+        return -1;
+    }
+  }
+  else
+  {
+    if (level)
+    {
+      if (crxIdwt53FilterDecode(comp, level - 1))
+        return -1;
+    }
+    else if (crxDecodeLineWithIQuantization(sband)) // LL band
+      return -1;
+
+    if (crxDecodeLineWithIQuantization(sband + 1) || // HL band
+        crxDecodeLineWithIQuantization(sband + 2) || // LH band
+        crxDecodeLineWithIQuantization(sband + 3))   // HH band
+      return -1;
+  }
+
+  return 0;
+}
+
+int crxIdwt53FilterTransform(CrxPlaneComp *comp, uint32_t level)
+{
+  CrxWaveletTransform *wavelet = comp->waveletTransform + level;
+
+  if (wavelet->curH)
+    return 0;
+
+  if (wavelet->curLine >= wavelet->height - 3)
+  {
+    if (!(comp->tileFlag & E_HAS_TILES_ON_THE_BOTTOM))
+    {
+      if (wavelet->height & 1)
+      {
+        if (level)
+        {
+          if (!wavelet[-1].curH)
+            if (crxIdwt53FilterTransform(comp, level - 1))
+              return -1;
+          wavelet->subband0Buf = crxIdwt53FilterGetLine(comp, level - 1);
+        }
+        int32_t *band0Buf = wavelet->subband0Buf;
+        int32_t *band1Buf = wavelet->subband1Buf;
+        int32_t *lineBufH0 = wavelet->lineBuf[wavelet->fltTapH + 3];
+        int32_t *lineBufH1 = wavelet->lineBuf[(wavelet->fltTapH + 1) % 5 + 3];
+        int32_t *lineBufH2 = wavelet->lineBuf[(wavelet->fltTapH + 2) % 5 + 3];
+
+        int32_t *lineBufL0 = wavelet->lineBuf[0];
+        int32_t *lineBufL1 = wavelet->lineBuf[1];
+        wavelet->lineBuf[1] = wavelet->lineBuf[2];
+        wavelet->lineBuf[2] = lineBufL1;
+
+        // process L bands
+        if (wavelet->width <= 1)
+        {
+          lineBufL0[0] = band0Buf[0];
+        }
+        else
+        {
+          if (comp->tileFlag & E_HAS_TILES_ON_THE_LEFT)
+          {
+            lineBufL0[0] = band0Buf[0] - ((band1Buf[0] + band1Buf[1] + 2) >> 2);
+            ++band1Buf;
+          }
+          else
+          {
+            lineBufL0[0] = band0Buf[0] - ((band1Buf[0] + 1) >> 1);
+          }
+          ++band0Buf;
+          for (int i = 0; i < wavelet->width - 3; i += 2)
+          {
+            int32_t delta =
+                band0Buf[0] - ((band1Buf[0] + band1Buf[1] + 2) >> 2);
+            lineBufL0[1] = band1Buf[0] + ((lineBufL0[0] + delta) >> 1);
+            lineBufL0[2] = delta;
+            ++band0Buf;
+            ++band1Buf;
+            lineBufL0 += 2;
+          }
+          if (comp->tileFlag & E_HAS_TILES_ON_THE_RIGHT)
+          {
+            int32_t delta =
+                band0Buf[0] - ((band1Buf[0] + band1Buf[1] + 2) >> 2);
+            lineBufL0[1] = band1Buf[0] + ((lineBufL0[0] + delta) >> 1);
+            if (wavelet->width & 1)
+              lineBufL0[2] = delta;
+          }
+          else if (wavelet->width & 1)
+          {
+            int32_t delta = band0Buf[0] - ((band1Buf[0] + 1) >> 1);
+            lineBufL0[1] = band1Buf[0] + ((lineBufL0[0] + delta) >> 1);
+            lineBufL0[2] = delta;
+          }
+          else
+            lineBufL0[1] = band1Buf[0] + lineBufL0[0];
+        }
+
+        // process H bands
+        lineBufL0 = wavelet->lineBuf[0];
+        lineBufL1 = wavelet->lineBuf[1];
+        for (int32_t i = 0; i < wavelet->width; i++)
+        {
+          int32_t delta = lineBufL0[i] - ((lineBufL1[i] + 1) >> 1);
+          lineBufH1[i] = lineBufL1[i] + ((delta + lineBufH0[i]) >> 1);
+          lineBufH2[i] = delta;
+        }
+        wavelet->curH += 3;
+        wavelet->curLine += 3;
+        wavelet->fltTapH = (wavelet->fltTapH + 3) % 5;
+      }
+      else
+      {
+        int32_t *lineBufL2 = wavelet->lineBuf[2];
+        int32_t *lineBufH0 = wavelet->lineBuf[wavelet->fltTapH + 3];
+        int32_t *lineBufH1 = wavelet->lineBuf[(wavelet->fltTapH + 1) % 5 + 3];
+        wavelet->lineBuf[1] = lineBufL2;
+        wavelet->lineBuf[2] = wavelet->lineBuf[1];
+
+        for (int32_t i = 0; i < wavelet->width; i++)
+          lineBufH1[i] = lineBufH0[i] + lineBufL2[i];
+
+        wavelet->curH += 2;
+        wavelet->curLine += 2;
+        wavelet->fltTapH = (wavelet->fltTapH + 2) % 5;
+      }
+    }
+  }
+  else
+  {
+    if (level)
+    {
+      if (!wavelet[-1].curH && crxIdwt53FilterTransform(comp, level - 1))
+        return -1;
+      wavelet->subband0Buf = crxIdwt53FilterGetLine(comp, level - 1);
+    }
+
+    int32_t *band0Buf = wavelet->subband0Buf;
+    int32_t *band1Buf = wavelet->subband1Buf;
+    int32_t *band2Buf = wavelet->subband2Buf;
+    int32_t *band3Buf = wavelet->subband3Buf;
+
+    int32_t *lineBufL0 = wavelet->lineBuf[0];
+    int32_t *lineBufL1 = wavelet->lineBuf[1];
+    int32_t *lineBufL2 = wavelet->lineBuf[2];
+    int32_t *lineBufH0 = wavelet->lineBuf[wavelet->fltTapH + 3];
+    int32_t *lineBufH1 = wavelet->lineBuf[(wavelet->fltTapH + 1) % 5 + 3];
+    int32_t *lineBufH2 = wavelet->lineBuf[(wavelet->fltTapH + 2) % 5 + 3];
+
+    wavelet->lineBuf[1] = wavelet->lineBuf[2];
+    wavelet->lineBuf[2] = lineBufL1;
+
+    // process L bands
+    if (wavelet->width <= 1)
+    {
+      lineBufL0[0] = band0Buf[0];
+      lineBufL1[0] = band2Buf[0];
+    }
+    else
+    {
+      if (comp->tileFlag & E_HAS_TILES_ON_THE_LEFT)
+      {
+        lineBufL0[0] = band0Buf[0] - ((band1Buf[0] + band1Buf[1] + 2) >> 2);
+        lineBufL1[0] = band2Buf[0] - ((band3Buf[0] + band3Buf[1] + 2) >> 2);
+        ++band1Buf;
+        ++band3Buf;
+      }
+      else
+      {
+        lineBufL0[0] = band0Buf[0] - ((band1Buf[0] + 1) >> 1);
+        lineBufL1[0] = band2Buf[0] - ((band3Buf[0] + 1) >> 1);
+      }
+      ++band0Buf;
+      ++band2Buf;
+      for (int i = 0; i < wavelet->width - 3; i += 2)
+      {
+        int32_t delta = band0Buf[0] - ((band1Buf[0] + band1Buf[1] + 2) >> 2);
+        lineBufL0[1] = band1Buf[0] + ((delta + lineBufL0[0]) >> 1);
+        lineBufL0[2] = delta;
+
+        delta = band2Buf[0] - ((band3Buf[0] + band3Buf[1] + 2) >> 2);
+        lineBufL1[1] = band3Buf[0] + ((delta + lineBufL1[0]) >> 1);
+        lineBufL1[2] = delta;
+
+        ++band0Buf;
+        ++band1Buf;
+        ++band2Buf;
+        ++band3Buf;
+        lineBufL0 += 2;
+        lineBufL1 += 2;
+      }
+      if (comp->tileFlag & E_HAS_TILES_ON_THE_RIGHT)
+      {
+        int32_t deltaA = band0Buf[0] - ((band1Buf[0] + band1Buf[1] + 2) >> 2);
+        lineBufL0[1] = band1Buf[0] + ((deltaA + lineBufL0[0]) >> 1);
+
+        int32_t deltaB = band2Buf[0] - ((band3Buf[0] + band3Buf[1] + 2) >> 2);
+        lineBufL1[1] = band3Buf[0] + ((deltaB + lineBufL1[0]) >> 1);
+
+        if (wavelet->width & 1)
+        {
+          lineBufL0[2] = deltaA;
+          lineBufL1[2] = deltaB;
+        }
+      }
+      else if (wavelet->width & 1)
+      {
+        int32_t delta = band0Buf[0] - ((band1Buf[0] + 1) >> 1);
+        lineBufL0[1] = band1Buf[0] + ((delta + lineBufL0[0]) >> 1);
+        lineBufL0[2] = delta;
+
+        delta = band2Buf[0] - ((band3Buf[0] + 1) >> 1);
+        lineBufL1[1] = band3Buf[0] + ((delta + lineBufL1[0]) >> 1);
+        lineBufL1[2] = delta;
+      }
+      else
+      {
+        lineBufL0[1] = lineBufL0[0] + band1Buf[0];
+        lineBufL1[1] = lineBufL1[0] + band3Buf[0];
+      }
+    }
+
+    // process H bands
+    lineBufL0 = wavelet->lineBuf[0];
+    lineBufL1 = wavelet->lineBuf[1];
+    lineBufL2 = wavelet->lineBuf[2];
+    for (int32_t i = 0; i < wavelet->width; i++)
+    {
+      int32_t delta = lineBufL0[i] - ((lineBufL2[i] + lineBufL1[i] + 2) >> 2);
+      lineBufH1[i] = lineBufL1[i] + ((delta + lineBufH0[i]) >> 1);
+      lineBufH2[i] = delta;
+    }
+    if (wavelet->curLine >= wavelet->height - 3 && wavelet->height & 1)
+    {
+      wavelet->curH += 3;
+      wavelet->curLine += 3;
+      wavelet->fltTapH = (wavelet->fltTapH + 3) % 5;
+    }
+    else
+    {
+      wavelet->curH += 2;
+      wavelet->curLine += 2;
+      wavelet->fltTapH = (wavelet->fltTapH + 2) % 5;
+    }
+  }
+
+  return 0;
+}
+
+int crxIdwt53FilterInitialize(CrxPlaneComp *comp, int32_t prevLevel)
+{
+  if (prevLevel < 0)
+    return 0;
+
+  for (int curLevel = 0, curBand = 0; curLevel < prevLevel + 1;
+       curLevel++, curBand += 3)
+  {
+    CrxWaveletTransform *wavelet = comp->waveletTransform + curLevel;
+    if (curLevel)
+      wavelet[0].subband0Buf = crxIdwt53FilterGetLine(comp, curLevel - 1);
+    else if (crxDecodeLineWithIQuantization(comp->subBands + curBand))
+      return -1;
+
+    int32_t *lineBufH0 = wavelet->lineBuf[wavelet->fltTapH + 3];
+    if (wavelet->height > 1)
+    {
+      if (crxDecodeLineWithIQuantization(comp->subBands + curBand + 1) ||
+          crxDecodeLineWithIQuantization(comp->subBands + curBand + 2) ||
+          crxDecodeLineWithIQuantization(comp->subBands + curBand + 3))
+        return -1;
+
+      int32_t *lineBufL0 = wavelet->lineBuf[0];
+      int32_t *lineBufL1 = wavelet->lineBuf[1];
+      int32_t *lineBufL2 = wavelet->lineBuf[2];
+
+      if (comp->tileFlag & E_HAS_TILES_ON_THE_TOP)
+      {
+        crxHorizontal53(lineBufL0, wavelet->lineBuf[1], wavelet,
+                        comp->tileFlag);
+        if (crxDecodeLineWithIQuantization(comp->subBands + curBand + 3) ||
+            crxDecodeLineWithIQuantization(comp->subBands + curBand + 2))
+          return -1;
+
+        int32_t *band2Buf = wavelet->subband2Buf;
+        int32_t *band3Buf = wavelet->subband3Buf;
+
+        // process L band
+        if (wavelet->width <= 1)
+          lineBufL2[0] = band2Buf[0];
+        else
+        {
+          if (comp->tileFlag & E_HAS_TILES_ON_THE_LEFT)
+          {
+            lineBufL2[0] = band2Buf[0] - ((band3Buf[0] + band3Buf[1] + 2) >> 2);
+            ++band3Buf;
+          }
+          else
+            lineBufL2[0] = band2Buf[0] - ((band3Buf[0] + 1) >> 1);
+
+          ++band2Buf;
+
+          for (int i = 0; i < wavelet->width - 3; i += 2)
+          {
+            int32_t delta =
+                band2Buf[0] - ((band3Buf[0] + band3Buf[1] + 2) >> 2);
+            lineBufL2[1] = band3Buf[0] + ((lineBufL2[0] + delta) >> 1);
+            lineBufL2[2] = delta;
+
+            ++band2Buf;
+            ++band3Buf;
+            lineBufL2 += 2;
+          }
+          if (comp->tileFlag & E_HAS_TILES_ON_THE_RIGHT)
+          {
+            int32_t delta =
+                band2Buf[0] - ((band3Buf[0] + band3Buf[1] + 2) >> 2);
+            lineBufL2[1] = band3Buf[0] + ((lineBufL2[0] + delta) >> 1);
+            if (wavelet->width & 1)
+              lineBufL2[2] = delta;
+          }
+          else if (wavelet->width & 1)
+          {
+            int32_t delta = band2Buf[0] - ((band3Buf[0] + 1) >> 1);
+
+            lineBufL2[1] = band3Buf[0] + ((lineBufL2[0] + delta) >> 1);
+            lineBufL2[2] = delta;
+          }
+          else
+          {
+            lineBufL2[1] = band3Buf[0] + lineBufL2[0];
+          }
+        }
+
+        // process H band
+        for (int32_t i = 0; i < wavelet->width; i++)
+          lineBufH0[i] =
+              lineBufL0[i] - ((lineBufL1[i] + lineBufL2[i] + 2) >> 2);
+      }
+      else
+      {
+        crxHorizontal53(lineBufL0, wavelet->lineBuf[2], wavelet,
+                        comp->tileFlag);
+        for (int i = 0; i < wavelet->width; i++)
+          lineBufH0[i] = lineBufL0[i] - ((lineBufL2[i] + 1) >> 1);
+      }
+
+      if (crxIdwt53FilterDecode(comp, curLevel) ||
+          crxIdwt53FilterTransform(comp, curLevel))
+        return -1;
+    }
+    else
+    {
+      if (crxDecodeLineWithIQuantization(comp->subBands + curBand + 1))
+        return -1;
+
+      int32_t *band0Buf = wavelet->subband0Buf;
+      int32_t *band1Buf = wavelet->subband1Buf;
+
+      // process H band
+      if (wavelet->width <= 1)
+        lineBufH0[0] = band0Buf[0];
+      else
+      {
+        if (comp->tileFlag & E_HAS_TILES_ON_THE_LEFT)
+        {
+          lineBufH0[0] = band0Buf[0] - ((band1Buf[0] + band1Buf[1] + 2) >> 2);
+          ++band1Buf;
+        }
+        else
+          lineBufH0[0] = band0Buf[0] - ((band1Buf[0] + 1) >> 1);
+
+        ++band0Buf;
+
+        for (int i = 0; i < wavelet->width - 3; i += 2)
+        {
+          int32_t delta = band0Buf[0] - ((band1Buf[0] + band1Buf[1] + 2) >> 2);
+          lineBufH0[1] = band1Buf[0] + ((lineBufH0[0] + delta) >> 1);
+          lineBufH0[2] = delta;
+
+          ++band0Buf;
+          ++band1Buf;
+          lineBufH0 += 2;
+        }
+
+        if (comp->tileFlag & E_HAS_TILES_ON_THE_RIGHT)
+        {
+          int32_t delta = band0Buf[0] - ((band1Buf[0] + band1Buf[1] + 2) >> 2);
+          lineBufH0[1] = band1Buf[0] + ((lineBufH0[0] + delta) >> 1);
+          lineBufH0[2] = delta;
+        }
+        else if (wavelet->width & 1)
+        {
+          int32_t delta = band0Buf[0] - ((band1Buf[0] + 1) >> 1);
+          lineBufH0[1] = band1Buf[0] + ((lineBufH0[0] + delta) >> 1);
+          lineBufH0[2] = delta;
+        }
+        else
+        {
+          lineBufH0[1] = band1Buf[0] + lineBufH0[0];
+        }
+      }
+      ++wavelet->curLine;
+      ++wavelet->curH;
+      wavelet->fltTapH = (wavelet->fltTapH + 1) % 5;
+    }
+  }
+
+  return 0;
+}
+
+void crxFreeSubbandData(CrxImage *image, CrxPlaneComp *comp)
+{
+  if (comp->compBuf)
+  {
+    free(comp->compBuf);
+    comp->compBuf = 0;
+  }
+
+  if (!comp->subBands)
+    return;
+
+  for (int32_t i = 0; i < image->subbandCount; i++)
+  {
+    if (comp->subBands[i].bandParam)
+    {
+      free(comp->subBands[i].bandParam);
+      comp->subBands[i].bandParam = 0LL;
+    }
+    comp->subBands[i].bandBuf = 0;
+    comp->subBands[i].bandSize = 0;
+  }
+}
+
+void crxConvertPlaneLine(CrxImage *img, int imageRow, int imageCol = 0,
+                         int plane = 0, int32_t *lineData = 0,
+                         int lineLength = 0)
+{
+  if (lineData)
+  {
+    uint64_t rawOffset = 4 * img->planeWidth * imageRow + 2 * imageCol;
+    if (img->encType == 1)
+    {
+      int32_t maxVal = 1 << (img->nBits - 1);
+      int32_t minVal = -maxVal;
+      --maxVal;
+      for (int i = 0; i < lineLength; i++)
+        img->outBufs[plane][rawOffset + 2 * i] =
+            _constrain(lineData[i], minVal, maxVal);
+    }
+    else if (img->encType == 3)
+    {
+      // copy to intermediate planeBuf
+      rawOffset = plane * img->planeWidth * img->planeHeight +
+                  img->planeWidth * imageRow + imageCol;
+      for (int i = 0; i < lineLength; i++)
+        img->planeBuf[rawOffset + i] = lineData[i];
+    }
+    else if (img->nPlanes == 4)
+    {
+      int32_t median = 1 << (img->nBits - 1);
+      int32_t maxVal = (1 << img->nBits) - 1;
+      for (int i = 0; i < lineLength; i++)
+        img->outBufs[plane][rawOffset + 2 * i] =
+            _constrain(median + lineData[i], 0, maxVal);
+    }
+    else if (img->nPlanes == 1)
+    {
+      int32_t maxVal = (1 << img->nBits) - 1;
+      int32_t median = 1 << (img->nBits - 1);
+      rawOffset = img->planeWidth * imageRow + imageCol;
+      for (int i = 0; i < lineLength; i++)
+        img->outBufs[0][rawOffset + i] =
+            _constrain(median + lineData[i], 0, maxVal);
+    }
+  }
+  else if (img->encType == 3 && img->planeBuf)
+  {
+    int32_t planeSize = img->planeWidth * img->planeHeight;
+    int16_t *plane0 = img->planeBuf + imageRow * img->planeWidth;
+    int16_t *plane1 = plane0 + planeSize;
+    int16_t *plane2 = plane1 + planeSize;
+    int16_t *plane3 = plane2 + planeSize;
+
+    int32_t median = 1 << (img->nBits - 1) << 10;
+    int32_t maxVal = (1 << img->nBits) - 1;
+    uint32_t rawLineOffset = 4 * img->planeWidth * imageRow;
+
+    // for this stage - all except imageRow is ignored
+    for (int i = 0; i < img->planeWidth; i++)
+    {
+      int32_t gr =
+          median + (plane0[i] << 10) - 168 * plane1[i] - 585 * plane3[i];
+      int32_t val = 0;
+      if (gr < 0)
+        gr = -(((_abs(gr) + 512) >> 9) & ~1);
+      else
+        gr = ((_abs(gr) + 512) >> 9) & ~1;
+
+      // Essentially R = round(median + P0 + 1.474*P3)
+      val = (median + (plane0[i] << 10) + 1510 * plane3[i] + 512) >> 10;
+      img->outBufs[0][rawLineOffset + 2 * i] = _constrain(val, 0, maxVal);
+      // Essentially G1 = round(median + P0 + P2 - 0.164*P1 - 0.571*P3)
+      val = (plane2[i] + gr + 1) >> 1;
+      img->outBufs[1][rawLineOffset + 2 * i] = _constrain(val, 0, maxVal);
+      // Essentially G1 = round(median + P0 - P2 - 0.164*P1 - 0.571*P3)
+      val = (gr - plane2[i] + 1) >> 1;
+      img->outBufs[2][rawLineOffset + 2 * i] = _constrain(val, 0, maxVal);
+      // Essentially B = round(median + P0 + 1.881*P1)
+      val = (median + (plane0[i] << 10) + 1927 * plane1[i] + 512) >> 10;
+      img->outBufs[3][rawLineOffset + 2 * i] = _constrain(val, 0, maxVal);
+    }
+  }
+}
+
+int crxParamInit(
+#ifdef LIBRAW_CR3_MEMPOOL
+	libraw_memmgr&  mm,
+#endif
+	CrxBandParam **param, uint64_t subbandMdatOffset,
+                 uint64_t subbandDataSize, uint32_t subbandWidth,
+                 uint32_t subbandHeight, int32_t supportsPartial,
+                 uint32_t roundedBitsMask, LibRaw_abstract_datastream *input)
+{
+  int32_t progrDataSize = supportsPartial ? 0 : sizeof(int32_t) * subbandWidth;
+  int32_t paramLength = 2 * subbandWidth + 4;
+  uint8_t *paramBuf = (uint8_t *)
+#ifdef LIBRAW_CR3_MEMPOOL
+	  mm.
+#endif
+	  calloc(
+      1, sizeof(CrxBandParam) + sizeof(int32_t) * paramLength + progrDataSize);
+
+  if (!paramBuf)
+    return -1;
+
+  *param = (CrxBandParam *)paramBuf;
+
+  paramBuf += sizeof(CrxBandParam);
+
+  (*param)->paramData = (int32_t *)paramBuf;
+  (*param)->nonProgrData =
+      progrDataSize ? (*param)->paramData + paramLength : 0;
+  (*param)->subbandWidth = subbandWidth;
+  (*param)->subbandHeight = subbandHeight;
+  (*param)->roundedBits = 0;
+  (*param)->curLine = 0;
+  (*param)->roundedBitsMask = roundedBitsMask;
+  (*param)->supportsPartial = supportsPartial;
+  (*param)->bitStream.bitData = 0;
+  (*param)->bitStream.bitsLeft = 0;
+  (*param)->bitStream.mdatSize = subbandDataSize;
+  (*param)->bitStream.curPos = 0;
+  (*param)->bitStream.curBufSize = 0;
+  (*param)->bitStream.curBufOffset = subbandMdatOffset;
+  (*param)->bitStream.input = input;
+
+  crxFillBuffer(&(*param)->bitStream);
+
+  return 0;
+}
+
+int crxSetupSubbandData(CrxImage *img, CrxPlaneComp *planeComp,
+                        const CrxTile *tile, uint32_t mdatOffset)
+{
+  long compDataSize = 0;
+  long waveletDataOffset = 0;
+  long compCoeffDataOffset = 0;
+  int32_t toSubbands = 3 * img->levels + 1;
+  int32_t transformWidth = 0;
+
+  CrxSubband *subbands = planeComp->subBands;
+
+  // calculate sizes
+  for (int32_t subbandNum = 0; subbandNum < toSubbands; subbandNum++)
+  {
+    subbands[subbandNum].bandSize =
+        subbands[subbandNum].width * sizeof(int32_t); // 4bytes
+    compDataSize += subbands[subbandNum].bandSize;
+  }
+
+  if (img->levels)
+  {
+    int32_t encLevels = img->levels ? img->levels : 1;
+    waveletDataOffset = (compDataSize + 7) & ~7;
+    compDataSize =
+        (sizeof(CrxWaveletTransform) * encLevels + waveletDataOffset + 7) & ~7;
+    compCoeffDataOffset = compDataSize;
+
+    // calc wavelet line buffer sizes (always at one level up from current)
+    for (int level = 0; level < img->levels; ++level)
+      if (level < img->levels - 1)
+        compDataSize += 8 * sizeof(int32_t) *
+                        planeComp->subBands[3 * (level + 1) + 2].width;
+      else
+        compDataSize += 8 * sizeof(int32_t) * tile->width;
+  }
+
+  // buffer allocation
+  planeComp->compBuf = (uint8_t *)
+#ifdef LIBRAW_CR3_MEMPOOL
+	  img->memmgr.
+#endif
+	  malloc(compDataSize);
+  if (!planeComp->compBuf)
+    return -1;
+
+  // subbands buffer and sizes initialisation
+  uint64_t subbandMdatOffset = img->mdatOffset + mdatOffset;
+  uint8_t *subbandBuf = planeComp->compBuf;
+
+  for (int32_t subbandNum = 0; subbandNum < toSubbands; subbandNum++)
+  {
+    subbands[subbandNum].bandBuf = subbandBuf;
+    subbandBuf += subbands[subbandNum].bandSize;
+    subbands[subbandNum].mdatOffset =
+        subbandMdatOffset + subbands[subbandNum].dataOffset;
+  }
+
+  // wavelet data initialisation
+  if (img->levels)
+  {
+    CrxWaveletTransform *waveletTransforms =
+        (CrxWaveletTransform *)(planeComp->compBuf + waveletDataOffset);
+    int32_t *paramData = (int32_t *)(planeComp->compBuf + compCoeffDataOffset);
+
+    planeComp->waveletTransform = waveletTransforms;
+    waveletTransforms[0].subband0Buf = (int32_t *)subbands->bandBuf;
+
+    for (int level = 0; level < img->levels; ++level)
+    {
+      int32_t band = 3 * level + 1;
+
+      if (level >= img->levels - 1)
+      {
+        waveletTransforms[level].height = tile->height;
+        transformWidth = tile->width;
+      }
+      else
+      {
+        waveletTransforms[level].height = subbands[band + 3].height;
+        transformWidth = subbands[band + 4].width;
+      }
+      waveletTransforms[level].width = transformWidth;
+      waveletTransforms[level].lineBuf[0] = paramData;
+      waveletTransforms[level].lineBuf[1] =
+          waveletTransforms[level].lineBuf[0] + transformWidth;
+      waveletTransforms[level].lineBuf[2] =
+          waveletTransforms[level].lineBuf[1] + transformWidth;
+      waveletTransforms[level].lineBuf[3] =
+          waveletTransforms[level].lineBuf[2] + transformWidth;
+      waveletTransforms[level].lineBuf[4] =
+          waveletTransforms[level].lineBuf[3] + transformWidth;
+      waveletTransforms[level].lineBuf[5] =
+          waveletTransforms[level].lineBuf[4] + transformWidth;
+      waveletTransforms[level].lineBuf[6] =
+          waveletTransforms[level].lineBuf[5] + transformWidth;
+      waveletTransforms[level].lineBuf[7] =
+          waveletTransforms[level].lineBuf[6] + transformWidth;
+      waveletTransforms[level].curLine = 0;
+      waveletTransforms[level].curH = 0;
+      waveletTransforms[level].fltTapH = 0;
+      waveletTransforms[level].subband1Buf = (int32_t *)subbands[band].bandBuf;
+      waveletTransforms[level].subband2Buf =
+          (int32_t *)subbands[band + 1].bandBuf;
+      waveletTransforms[level].subband3Buf =
+          (int32_t *)subbands[band + 2].bandBuf;
+
+      paramData = waveletTransforms[level].lineBuf[7] + transformWidth;
+    }
+  }
+
+  // decoding params and bitstream initialisation
+  for (int32_t subbandNum = 0; subbandNum < toSubbands; subbandNum++)
+  {
+    if (subbands[subbandNum].dataSize)
+    {
+      int32_t supportsPartial = 0;
+      uint32_t roundedBitsMask = 0;
+
+      if (planeComp->supportsPartial && subbandNum == 0)
+      {
+        roundedBitsMask = planeComp->roundedBitsMask;
+        supportsPartial = 1;
+      }
+      if (crxParamInit(
+#ifdef LIBRAW_CR3_MEMPOOL
+		  img->memmgr,
+#endif
+		  &subbands[subbandNum].bandParam,
+                       subbands[subbandNum].mdatOffset,
+                       subbands[subbandNum].dataSize,
+                       subbands[subbandNum].width, subbands[subbandNum].height,
+                       supportsPartial, roundedBitsMask, img->input))
+        return -1;
+    }
+  }
+
+  return 0;
+}
+
+int LibRaw::crxDecodePlane(void *p, uint32_t planeNumber)
+{
+  CrxImage *img = (CrxImage *)p;
+  int imageRow = 0;
+  for (int tRow = 0; tRow < img->tileRows; tRow++)
+  {
+    int imageCol = 0;
+    for (int tCol = 0; tCol < img->tileCols; tCol++)
+    {
+      CrxTile *tile = img->tiles + tRow * img->tileCols + tCol;
+      CrxPlaneComp *planeComp = tile->comps + planeNumber;
+      uint64_t tileMdatOffset = tile->dataOffset + planeComp->dataOffset;
+
+      // decode single tile
+      if (crxSetupSubbandData(img, planeComp, tile, tileMdatOffset))
+        return -1;
+
+      if (img->levels)
+      {
+        if (crxIdwt53FilterInitialize(planeComp, img->levels - 1))
+          return -1;
+        for (int i = 0; i < tile->height; ++i)
+        {
+          if (crxIdwt53FilterDecode(planeComp, img->levels - 1) ||
+              crxIdwt53FilterTransform(planeComp, img->levels - 1))
+            return -1;
+          int32_t *lineData =
+              crxIdwt53FilterGetLine(planeComp, img->levels - 1);
+          crxConvertPlaneLine(img, imageRow + i, imageCol, planeNumber,
+                              lineData, tile->width);
+        }
+      }
+      else
+      {
+        // we have the only subband in this case
+        if (!planeComp->subBands->dataSize)
+        {
+          memset(planeComp->subBands->bandBuf, 0,
+                 planeComp->subBands->bandSize);
+          return 0;
+        }
+
+        for (int i = 0; i < tile->height; ++i)
+        {
+          if (crxDecodeLine(planeComp->subBands->bandParam,
+                            planeComp->subBands->bandBuf))
+            return -1;
+          int32_t *lineData = (int32_t *)planeComp->subBands->bandBuf;
+          crxConvertPlaneLine(img, imageRow + i, imageCol, planeNumber,
+                              lineData, tile->width);
+        }
+      }
+      imageCol += tile->width;
+    }
+    imageRow += img->tiles[tRow * img->tileCols].height;
+  }
+
+  return 0;
+}
+
+int crxReadSubbandHeaders(crx_data_header_t *hdr, CrxImage *img, CrxTile *tile,
+                          CrxPlaneComp *comp, uint8_t **subbandMdatPtr,
+                          int32_t *hdrSize)
+{
+  CrxSubband *band = comp->subBands + img->subbandCount - 1; // set to last band
+  uint32_t bandHeight = tile->height;
+  uint32_t bandWidth = tile->width;
+  int32_t bandWidthExCoef = 0;
+  int32_t bandHeightExCoef = 0;
+  if (img->levels)
+  {
+    // Build up subband sequences to crxDecode to a level in a header
+
+    // Coefficient structure is a bit unclear and convoluted:
+    //   3 levels max - 8 groups (for tile width rounded to 8 bytes)
+    //                  of 3 band per level 4 sets of coefficients for each
+    int32_t *rowExCoef =
+        exCoefNumTbl + 0x60 * (img->levels - 1) + 12 * (tile->width & 7);
+    int32_t *colExCoef =
+        exCoefNumTbl + 0x60 * (img->levels - 1) + 12 * (tile->height & 7);
+    for (int level = 0; level < img->levels; ++level)
+    {
+      int32_t widthOddPixel = bandWidth & 1;
+      int32_t heightOddPixel = bandHeight & 1;
+      bandWidth = (widthOddPixel + bandWidth) >> 1;
+      bandHeight = (heightOddPixel + bandHeight) >> 1;
+
+      int32_t bandWidthExCoef0 = 0;
+      int32_t bandWidthExCoef1 = 0;
+      int32_t bandHeightExCoef0 = 0;
+      int32_t bandHeightExCoef1 = 0;
+      if (tile->tileFlag & E_HAS_TILES_ON_THE_RIGHT)
+      {
+        bandWidthExCoef0 = rowExCoef[0];
+        bandWidthExCoef1 = rowExCoef[1];
+      }
+      if (tile->tileFlag & E_HAS_TILES_ON_THE_LEFT)
+        ++bandWidthExCoef0;
+      if (tile->tileFlag & E_HAS_TILES_ON_THE_BOTTOM)
+      {
+        bandHeightExCoef0 = colExCoef[0];
+        bandHeightExCoef1 = colExCoef[1];
+      }
+      if (tile->tileFlag & E_HAS_TILES_ON_THE_TOP)
+        ++bandHeightExCoef0;
+
+      band[0].width = bandWidth + bandWidthExCoef0 - widthOddPixel;
+      band[0].height = bandHeight + bandHeightExCoef0 - heightOddPixel;
+
+      band[-1].width = bandWidth + bandWidthExCoef1;
+      band[-1].height = bandHeight + bandHeightExCoef0 - heightOddPixel;
+
+      band[-2].width = bandWidth + bandWidthExCoef0 - widthOddPixel;
+      band[-2].height = bandHeight + bandHeightExCoef1;
+
+      rowExCoef += 4;
+      colExCoef += 4;
+      band -= 3;
+    }
+    bandWidthExCoef = bandHeightExCoef = 0;
+    if (tile->tileFlag & E_HAS_TILES_ON_THE_RIGHT)
+      bandWidthExCoef =
+          exCoefNumTbl[0x60 * (img->levels - 1) + 12 * (tile->width & 7) +
+                       4 * (img->levels - 1) + 1];
+    if (tile->tileFlag & E_HAS_TILES_ON_THE_BOTTOM)
+      bandHeightExCoef =
+          exCoefNumTbl[0x60 * (img->levels - 1) + 12 * (tile->height & 7) +
+                       4 * (img->levels - 1) + 1];
+  }
+  band->width = bandWidthExCoef + bandWidth;
+  band->height = bandHeightExCoef + bandHeight;
+
+  if (!img->subbandCount)
+    return 0;
+  int32_t subbandOffset = 0;
+  band = comp->subBands;
+  for (int curSubband = 0; curSubband < img->subbandCount; curSubband++, band++)
+  {
+    if (*hdrSize < 0xC)
+      return -1;
+
+    if (LibRaw::sgetn(2, *subbandMdatPtr) != 0xFF03)
+      return -1;
+
+    uint32_t bitData = LibRaw::sgetn(4, *subbandMdatPtr + 8);
+    uint32_t subbandSize = LibRaw::sgetn(4, *subbandMdatPtr + 4);
+
+    if ((unsigned)curSubband != bitData >> 28)
+    {
+      band->dataSize = subbandSize;
+      return -1;
+    }
+    band->dataSize = subbandSize - (bitData & 0x7FF);
+    band->supportsPartial = bitData & 0x8000 ? 1 : 0;
+    band->dataOffset = subbandOffset;
+    band->quantValue = (bitData >> 19) & 0xFF;
+    band->paramK = 0;
+    band->bandParam = 0;
+    band->bandBuf = 0;
+    band->bandSize = 0;
+
+    subbandOffset += subbandSize;
+
+    *subbandMdatPtr += 0xC;
+    *hdrSize -= 0xC;
+  }
+  return 0;
+}
+
+int crxReadImageHeaders(crx_data_header_t *hdr, CrxImage *img, uint8_t *mdatPtr,
+                        int32_t hdrBufSize)
+{
+  int nTiles = img->tileRows * img->tileCols;
+
+  if (!nTiles)
+    return -1;
+
+  if (!img->tiles)
+  {
+    img->tiles = (CrxTile *)
+#ifdef LIBRAW_CR3_MEMPOOL
+		img->memmgr.
+#endif
+		calloc(
+        sizeof(CrxTile) * nTiles +
+        sizeof(CrxPlaneComp) * nTiles * img->nPlanes +
+        sizeof(CrxSubband) * nTiles * img->nPlanes * img->subbandCount,1);
+    if (!img->tiles)
+      return -1;
+
+    // memory areas in allocated chunk
+    CrxTile *tile = img->tiles;
+    CrxPlaneComp *comps = (CrxPlaneComp *)(tile + nTiles);
+    CrxSubband *bands = (CrxSubband *)(comps + img->nPlanes * nTiles);
+
+    for (int curTile = 0; curTile < nTiles; curTile++, tile++)
+    {
+      tile->tileFlag = 0; // tile neighbouring flags
+      tile->tileNumber = curTile;
+      tile->tileSize = 0;
+      tile->comps = comps + curTile * img->nPlanes;
+
+      if ((curTile + 1) % img->tileCols)
+      {
+        // not the last tile in a tile row
+        tile->width = hdr->tileWidth;
+        if (img->tileCols > 1)
+        {
+          tile->tileFlag = E_HAS_TILES_ON_THE_RIGHT;
+          if (curTile % img->tileCols)
+            // not the first tile in tile row
+            tile->tileFlag |= E_HAS_TILES_ON_THE_LEFT;
+        }
+      }
+      else
+      {
+        // last tile in a tile row
+        tile->width = img->planeWidth - hdr->tileWidth * (img->tileCols - 1);
+        if (img->tileCols > 1)
+          tile->tileFlag = E_HAS_TILES_ON_THE_LEFT;
+      }
+      if (curTile < nTiles - img->tileCols)
+      {
+        // in first tile row
+        tile->height = hdr->tileHeight;
+        if (img->tileRows > 1)
+        {
+          tile->tileFlag |= E_HAS_TILES_ON_THE_BOTTOM;
+          if (curTile >= img->tileCols)
+            tile->tileFlag |= E_HAS_TILES_ON_THE_TOP;
+        }
+      }
+      else
+      {
+        // non first tile row
+        tile->height = img->planeHeight - hdr->tileHeight * (img->tileRows - 1);
+        if (img->tileRows > 1)
+          tile->tileFlag |= E_HAS_TILES_ON_THE_TOP;
+      }
+      if (img->nPlanes)
+      {
+        CrxPlaneComp *comp = tile->comps;
+        CrxSubband *band = bands + curTile * img->nPlanes * img->subbandCount;
+
+        for (int curComp = 0; curComp < img->nPlanes; curComp++, comp++)
+        {
+          comp->compNumber = curComp;
+          comp->supportsPartial = 1;
+          comp->tileFlag = tile->tileFlag;
+          comp->subBands = band;
+          comp->compBuf = 0;
+          comp->waveletTransform = 0;
+          if (img->subbandCount)
+          {
+            for (int curBand = 0; curBand < img->subbandCount;
+                 curBand++, band++)
+            {
+              band->supportsPartial = 0;
+              band->quantValue = 4;
+              band->bandParam = 0;
+              band->dataSize = 0;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  uint32_t tileOffset = 0;
+  int32_t dataSize = hdrBufSize;
+  uint8_t *dataPtr = mdatPtr;
+  CrxTile *tile = img->tiles;
+
+  for (int curTile = 0; curTile < nTiles; curTile++, tile++)
+  {
+    if (dataSize < 0xC)
+      return -1;
+
+    if (LibRaw::sgetn(2, dataPtr) != 0xFF01)
+      return -1;
+    if (LibRaw::sgetn(2, dataPtr + 8) != (unsigned)curTile)
+      return -1;
+
+    dataSize -= 0xC;
+
+    tile->tileSize = LibRaw::sgetn(4, dataPtr + 4);
+    tile->dataOffset = tileOffset;
+
+    int32_t hdrExtraBytes = LibRaw::sgetn(2, dataPtr + 2) - 8;
+    tileOffset += tile->tileSize;
+    dataPtr += hdrExtraBytes + 0xC;
+    dataSize -= hdrExtraBytes;
+
+    uint32_t compOffset = 0;
+    CrxPlaneComp *comp = tile->comps;
+
+    for (int compNum = 0; compNum < img->nPlanes; compNum++, comp++)
+    {
+      if (dataSize < 0xC)
+        return -1;
+
+      if (LibRaw::sgetn(2, dataPtr) != 0xFF02)
+        return -1;
+      if (compNum != dataPtr[8] >> 4)
+        return -1;
+
+      comp->compSize = LibRaw::sgetn(4, dataPtr + 4);
+
+      int32_t compHdrRoundedBits = (dataPtr[8] >> 1) & 3;
+      comp->supportsPartial = (dataPtr[8] & 8) != 0;
+
+      comp->dataOffset = compOffset;
+      comp->tileFlag = tile->tileFlag;
+
+      compOffset += comp->compSize;
+	  dataSize -= 0xC;
+	  dataPtr += 0xC;
+
+      comp->roundedBitsMask = 0;
+
+      if (compHdrRoundedBits)
+      {
+        if (img->levels || !comp->supportsPartial)
+          return -1;
+
+        comp->roundedBitsMask = 1 << (compHdrRoundedBits - 1);
+      }
+
+      if (crxReadSubbandHeaders(hdr, img, tile, comp, &dataPtr, &dataSize))
+        return -1;
+    }
+  }
+  return 0;
+}
+
+int crxSetupImageData(crx_data_header_t *hdr, CrxImage *img, int16_t *outBuf,
+                      uint64_t mdatOffset, uint32_t mdatSize, int32_t hdrBufSize,
+                      uint8_t *mdatHdrPtr)
+{
+  int IncrBitTable[32] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0,
+                          0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0};
+
+  img->planeWidth = hdr->f_width;
+  img->planeHeight = hdr->f_height;
+
+  if (hdr->tileWidth < 0x16 || hdr->tileHeight < 0x16 ||
+      img->planeWidth > 0x7FFF || img->planeHeight > 0x7FFF)
+    return -1;
+
+  img->tileCols = (img->planeWidth + hdr->tileWidth - 1) / hdr->tileWidth;
+  img->tileRows = (img->planeHeight + hdr->tileHeight - 1) / hdr->tileHeight;
+
+  if (img->tileCols > 0xFF || img->tileRows > 0xFF ||
+      img->planeWidth - hdr->tileWidth * (img->tileCols - 1) < 0x16 ||
+      img->planeHeight - hdr->tileHeight * (img->tileRows - 1) < 0x16)
+    return -1;
+
+  img->tiles = 0;
+  img->levels = hdr->imageLevels;
+  img->subbandCount = 3 * img->levels + 1; // 3 bands per level + one last LL
+  img->nPlanes = hdr->nPlanes;
+  img->nBits = hdr->nBits;
+  img->encType = hdr->encType;
+  img->samplePrecision = hdr->nBits + IncrBitTable[4 * hdr->encType + 2] + 1;
+  img->mdatOffset = mdatOffset + hdr->mdatHdrSize;
+  img->mdatSize = mdatSize;
+  img->planeBuf = 0;
+  img->outBufs[0] = img->outBufs[1] = img->outBufs[2] = img->outBufs[3] = 0;
+
+  // The encoding type 3 needs all 4 planes to be decoded to generate row of
+  // RGGB values. It seems to be using some other colour space for raw encoding
+  // It is a massive buffer so ideallly it will need a different approach:
+  // decode planes line by line and convert single line then without
+  // intermediate plane buffer. At the moment though it's too many changes so
+  // left as is.
+  if (img->encType == 3 && img->nPlanes == 4 && img->nBits > 8)
+  {
+    img->planeBuf =
+        (int16_t *)
+#ifdef LIBRAW_CR3_MEMPOOL
+		img->memmgr.
+#endif
+		malloc(img->planeHeight * img->planeWidth * img->nPlanes *
+                          ((img->samplePrecision + 7) >> 3));
+    if (!img->planeBuf)
+      return -1;
+  }
+
+  int32_t rowSize = 2 * img->planeWidth;
+
+  if (img->nPlanes == 1)
+    img->outBufs[0] = outBuf;
+  else
+    switch (hdr->cfaLayout)
+    {
+    case 0:
+      // R G
+      // G B
+      img->outBufs[0] = outBuf;
+      img->outBufs[1] = outBuf + 1;
+      img->outBufs[2] = outBuf + rowSize;
+      img->outBufs[3] = img->outBufs[2] + 1;
+      break;
+    case 1:
+      // G R
+      // B G
+      img->outBufs[1] = outBuf;
+      img->outBufs[0] = outBuf + 1;
+      img->outBufs[3] = outBuf + rowSize;
+      img->outBufs[2] = img->outBufs[3] + 1;
+      break;
+    case 2:
+      // G B
+      // R G
+      img->outBufs[2] = outBuf;
+      img->outBufs[3] = outBuf + 1;
+      img->outBufs[0] = outBuf + rowSize;
+      img->outBufs[1] = img->outBufs[0] + 1;
+      break;
+    case 3:
+      // B G
+      // G R
+      img->outBufs[3] = outBuf;
+      img->outBufs[2] = outBuf + 1;
+      img->outBufs[1] = outBuf + rowSize;
+      img->outBufs[0] = img->outBufs[1] + 1;
+      break;
+    }
+
+  // read header
+  return crxReadImageHeaders(hdr, img, mdatHdrPtr, hdrBufSize);
+}
+
+int crxFreeImageData(CrxImage *img)
+{
+#ifdef LIBRAW_CR3_MEMPOOL
+	img->memmgr.cleanup();
+#else
+  CrxTile *tile = img->tiles;
+  int nTiles = img->tileRows * img->tileCols;
+
+  if (img->tiles)
+  {
+    for (int32_t curTile = 0; curTile < nTiles; curTile++)
+      if (tile[curTile].comps)
+        for (int32_t curPlane = 0; curPlane < img->nPlanes; curPlane++)
+          crxFreeSubbandData(img, tile[curTile].comps + curPlane);
+    free(img->tiles);
+    img->tiles = 0;
+  }
+
+  if (img->planeBuf)
+  {
+    free(img->planeBuf);
+    img->planeBuf = 0;
+  }
+#endif
+  return 0;
+}
+
+void LibRaw::crxLoadDecodeLoop(void *img, int nPlanes)
+{
+#ifdef LIBRAW_USE_OPENMP
+  int results[4]; // nPlanes is always <= 4
+#pragma omp parallel for
+  for (int32_t plane = 0; plane < nPlanes; ++plane)
+    results[plane] = crxDecodePlane(img, plane);
+
+  for (int32_t plane = 0; plane < nPlanes; ++plane)
+    if (results[plane])
+      derror();
+#else
+  for (int32_t plane = 0; plane < nPlanes; ++plane)
+    if (crxDecodePlane(img, plane))
+      derror();
+#endif
+}
+
+void LibRaw::crxConvertPlaneLineDf(void *p, int imageRow)
+{
+  crxConvertPlaneLine((CrxImage *)p, imageRow);
+}
+
+void LibRaw::crxLoadFinalizeLoopE3(void *p, int planeHeight)
+{
+#ifdef LIBRAW_USE_OPENMP
+#pragma omp parallel for
+#endif
+  for (int i = 0; i < planeHeight; ++i)
+    crxConvertPlaneLineDf(p, i);
+}
+
+void LibRaw::crxLoadRaw()
+{
+	if (libraw_internal_data.unpacker_data.CR3_Version != 0x100)
+		throw LIBRAW_EXCEPTION_DECODE_RAW;
+  CrxImage img;
+  if (libraw_internal_data.unpacker_data.crx_track_selected < 0 ||
+      libraw_internal_data.unpacker_data.crx_track_selected >=
+          LIBRAW_CRXTRACKS_MAXCOUNT)
+    derror();
+  crx_data_header_t hdr =
+      libraw_internal_data.unpacker_data
+          .crx_header[libraw_internal_data.unpacker_data.crx_track_selected];
+
+  img.input = libraw_internal_data.internal_data.input;
+
+  // update sizes for the planes
+  if (hdr.nPlanes == 4)
+  {
+    hdr.f_width >>= 1;
+    hdr.f_height >>= 1;
+    hdr.tileWidth >>= 1;
+    hdr.tileHeight >>= 1;
+  }
+
+  imgdata.color.maximum = (1 << hdr.nBits) - 1;
+
+  uint8_t *hdrBuf = (uint8_t *)malloc(hdr.mdatHdrSize * 2);
+
+  // read image header
+#ifdef LIBRAW_USE_OPENMP
+#pragma omp critical
+#endif
+  {
+#ifndef LIBRAW_USE_OPENMP
+    libraw_internal_data.internal_data.input->lock();
+#endif
+    libraw_internal_data.internal_data.input->seek(
+        libraw_internal_data.unpacker_data.data_offset, SEEK_SET);
+    libraw_internal_data.internal_data.input->read(hdrBuf, 1, hdr.mdatHdrSize);
+#ifndef LIBRAW_USE_OPENMP
+    libraw_internal_data.internal_data.input->unlock();
+#endif
+  }
+
+  // parse and setup the image data
+  if (crxSetupImageData(&hdr, &img, (int16_t *)imgdata.rawdata.raw_image,
+                        libraw_internal_data.unpacker_data.data_offset,
+                        libraw_internal_data.unpacker_data.data_size, hdr.mdatHdrSize*2, hdrBuf))
+    derror();
+  free(hdrBuf);
+
+  crxLoadDecodeLoop(&img, hdr.nPlanes);
+
+  if (img.encType == 3)
+    crxLoadFinalizeLoopE3(&img, img.planeHeight);
+
+  crxFreeImageData(&img);
+}
+
+int LibRaw::crxParseImageHeader(uchar *cmp1TagData, int nTrack)
+{
+  if (nTrack < 0 || nTrack >= LIBRAW_CRXTRACKS_MAXCOUNT)
+    return -1;
+  if (!cmp1TagData)
+    return -1;
+
+  crx_data_header_t *hdr =
+      &libraw_internal_data.unpacker_data.crx_header[nTrack];
+
+  hdr->version = sgetn(2, cmp1TagData + 4);
+  hdr->f_width = sgetn(4, cmp1TagData + 8);
+  hdr->f_height = sgetn(4, cmp1TagData + 12);
+  hdr->tileWidth = sgetn(4, cmp1TagData + 16);
+  hdr->tileHeight = sgetn(4, cmp1TagData + 20);
+  hdr->nBits = cmp1TagData[24];
+  hdr->nPlanes = cmp1TagData[25] >> 4;
+  hdr->cfaLayout = cmp1TagData[25] & 0xF;
+  hdr->encType = cmp1TagData[26] >> 4;
+  hdr->imageLevels = cmp1TagData[26] & 0xF;
+  hdr->hasTileCols = cmp1TagData[27] >> 7;
+  hdr->hasTileRows = (cmp1TagData[27] >> 6) & 1;
+  hdr->mdatHdrSize = sgetn(4, cmp1TagData + 28);
+
+  // validation
+  if ((hdr->version != 0x100 && hdr->version != 0x200) || !hdr->mdatHdrSize)
+    return -1;
+  libraw_internal_data.unpacker_data.CR3_Version = hdr->version;
+  if (hdr->encType == 1)
+  {
+    if (hdr->nBits > 15)
+      return -1;
+  }
+  else
+  {
+    if (hdr->encType && hdr->encType != 3)
+      return -1;
+    if (hdr->nBits > 14)
+      return -1;
+  }
+
+  if (hdr->nPlanes == 1)
+  {
+    if (hdr->cfaLayout || hdr->encType)
+      return -1;
+    if (hdr->nBits != 8)
+      return -1;
+  }
+  else if (hdr->nPlanes != 4 || hdr->f_width & 1 || hdr->f_height & 1 ||
+           hdr->tileWidth & 1 || hdr->tileHeight & 1 || hdr->cfaLayout > 3 ||
+           (hdr->encType && hdr->encType != 1 && hdr->encType != 3) ||
+           hdr->nBits == 8)
+    return -1;
+
+  if (hdr->tileWidth > hdr->f_width || hdr->tileHeight > hdr->f_height)
+    return -1;
+
+  if (hdr->imageLevels > 3 || hdr->hasTileCols > 1 || hdr->hasTileRows > 1)
+    return -1;
+  return 0;
+}
+
+#undef _abs
+#undef _min
+#undef _constrain
+#undef libraw_inline
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/decoders/decoders_dcraw.cpp libkdcraw/libkdcraw/libraw/src/decoders/decoders_dcraw.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/decoders/decoders_dcraw.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/decoders/decoders_dcraw.cpp	2022-11-07 07:46:31.730795008 +0300
@@ -0,0 +1,1815 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+#include "../../internal/libraw_cameraids.h"
+
+unsigned LibRaw::getbithuff(int nbits, ushort *huff)
+{
+#ifdef LIBRAW_NOTHREADS
+  static unsigned bitbuf = 0;
+  static int vbits = 0, reset = 0;
+#else
+#define bitbuf tls->getbits.bitbuf
+#define vbits tls->getbits.vbits
+#define reset tls->getbits.reset
+#endif
+  unsigned c;
+
+  if (nbits > 25)
+    return 0;
+  if (nbits < 0)
+    return bitbuf = vbits = reset = 0;
+  if (nbits == 0 || vbits < 0)
+    return 0;
+  while (!reset && vbits < nbits && (c = fgetc(ifp)) != (unsigned)EOF &&
+         !(reset = zero_after_ff && c == 0xff && fgetc(ifp)))
+  {
+    bitbuf = (bitbuf << 8) + (uchar)c;
+    vbits += 8;
+  }
+  c = vbits == 0 ? 0 : bitbuf << (32 - vbits) >> (32 - nbits);
+  if (huff)
+  {
+    vbits -= huff[c] >> 8;
+    c = (uchar)huff[c];
+  }
+  else
+    vbits -= nbits;
+  if (vbits < 0)
+    derror();
+  return c;
+#ifndef LIBRAW_NOTHREADS
+#undef bitbuf
+#undef vbits
+#undef reset
+#endif
+}
+
+/*
+   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
+ */
+ushort *LibRaw::make_decoder_ref(const uchar **source)
+{
+  int max, len, h, i, j;
+  const uchar *count;
+  ushort *huff;
+
+  count = (*source += 16) - 17;
+  for (max = 16; max && !count[max]; max--)
+    ;
+  huff = (ushort *)calloc(1 + (1 << max), sizeof *huff);
+  merror(huff, "make_decoder()");
+  huff[0] = max;
+  for (h = len = 1; len <= max; len++)
+    for (i = 0; i < count[len]; i++, ++*source)
+      for (j = 0; j < 1 << (max - len); j++)
+        if (h <= 1 << max)
+          huff[h++] = len << 8 | **source;
+  return huff;
+}
+
+ushort *LibRaw::make_decoder(const uchar *source)
+{
+  return make_decoder_ref(&source);
+}
+
+void LibRaw::crw_init_tables(unsigned table, ushort *huff[2])
+{
+  static const uchar 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 uchar 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}};
+  if (table > 2)
+    table = 2;
+  huff[0] = make_decoder(first_tree[table]);
+  huff[1] = make_decoder(second_tree[table]);
+}
+
+/*
+   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.
+ */
+int LibRaw::canon_has_lowbits()
+{
+  uchar test[0x4000];
+  int ret = 1, i;
+
+  fseek(ifp, 0, SEEK_SET);
+  fread(test, 1, sizeof test, ifp);
+  for (i = 540; i < int(sizeof test - 1); i++)
+    if (test[i] == 0xff)
+    {
+      if (test[i + 1])
+        return 1;
+      ret = 0;
+    }
+  return ret;
+}
+
+void LibRaw::canon_load_raw()
+{
+  ushort *pixel, *prow, *huff[2];
+  int nblocks, lowbits, i, c, row, r, save, val;
+  int block, diffbuf[64], leaf, len, diff, carry = 0, pnum = 0, base[2];
+
+  crw_init_tables(tiff_compress, huff);
+  lowbits = canon_has_lowbits();
+  if (!lowbits)
+    maximum = 0x3ff;
+  fseek(ifp, 540 + lowbits * raw_height * raw_width / 4, SEEK_SET);
+  zero_after_ff = 1;
+  getbits(-1);
+  try
+  {
+    for (row = 0; row < raw_height; row += 8)
+    {
+      checkCancel();
+      pixel = raw_image + row * raw_width;
+      nblocks = MIN(8, raw_height - row) * raw_width >> 6;
+      for (block = 0; block < nblocks; block++)
+      {
+        memset(diffbuf, 0, sizeof diffbuf);
+        for (i = 0; i < 64; i++)
+        {
+          leaf = gethuff(huff[i > 0]);
+          if (leaf == 0 && i)
+            break;
+          if (leaf == 0xff)
+            continue;
+          i += leaf >> 4;
+          len = leaf & 15;
+          if (len == 0)
+            continue;
+          diff = getbits(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;
+          if ((pixel[(block << 6) + i] = base[i & 1] += diffbuf[i]) >> 10)
+            derror();
+        }
+      }
+      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);
+      }
+    }
+  }
+  catch (...)
+  {
+    FORC(2) free(huff[c]);
+    throw;
+  }
+  FORC(2) free(huff[c]);
+}
+
+int LibRaw::ljpeg_start(struct jhead *jh, int info_only)
+{
+  ushort c, tag, len;
+  int cnt = 0;
+  std::vector<uchar> data_buffer(0x10000);
+  uchar* data = &data_buffer[0];
+  const uchar *dp;
+
+  memset(jh, 0, sizeof *jh);
+  jh->restart = INT_MAX;
+  if (fread(data, 2, 1, ifp) != 1 || data[1] != 0xd8)
+    return 0;
+  do
+  {
+    if (feof(ifp))
+      return 0;
+    if (cnt++ > 1024)
+      return 0; // 1024 tags limit
+    if (fread(data, 2, 2, ifp) != 2)
+      return 0;
+    tag = data[0] << 8 | data[1];
+    len = (data[2] << 8 | data[3]) - 2;
+    if (tag <= 0xff00)
+      return 0;
+    if (fread(data, 1, len, ifp) != len)
+      return 0;
+    switch (tag)
+    {
+    case 0xffc3: // start of frame; lossless, Huffman
+      jh->sraw = ((data[7] >> 4) * (data[7] & 15) - 1) & 3;
+    case 0xffc1:
+    case 0xffc0:
+      jh->algo = tag & 0xff;
+      jh->bits = data[0];
+      jh->high = data[1] << 8 | data[2];
+      jh->wide = data[3] << 8 | data[4];
+      jh->clrs = data[5] + jh->sraw;
+      if (len == 9 && !dng_version)
+        getc(ifp);
+      break;
+    case 0xffc4: // define Huffman tables
+      if (info_only)
+        break;
+      for (dp = data; dp < data + len && !((c = *dp++) & -20);)
+        jh->free[c] = jh->huff[c] = make_decoder_ref(&dp);
+      break;
+    case 0xffda: // start of scan
+      jh->psv = data[1 + data[0] * 2];
+      jh->bits -= data[3 + data[0] * 2] & 15;
+      break;
+    case 0xffdb:
+      FORC(64) jh->quant[c] = data[c * 2 + 1] << 8 | data[c * 2 + 2];
+      break;
+    case 0xffdd:
+      jh->restart = data[0] << 8 | data[1];
+    }
+  } while (tag != 0xffda);
+  if (jh->bits > 16 || jh->clrs > 6 || !jh->bits || !jh->high || !jh->wide ||
+      !jh->clrs)
+    return 0;
+  if (info_only)
+    return 1;
+  if (!jh->huff[0])
+    return 0;
+  FORC(19) if (!jh->huff[c + 1]) jh->huff[c + 1] = jh->huff[c];
+  if (jh->sraw)
+  {
+    FORC(4) jh->huff[2 + c] = jh->huff[1];
+    FORC(jh->sraw) jh->huff[1 + c] = jh->huff[0];
+  }
+  jh->row = (ushort *)calloc(jh->wide * jh->clrs, 16);
+  merror(jh->row, "ljpeg_start()");
+  return zero_after_ff = 1;
+}
+
+void LibRaw::ljpeg_end(struct jhead *jh)
+{
+  int c;
+  FORC4 if (jh->free[c]) free(jh->free[c]);
+  free(jh->row);
+}
+
+int LibRaw::ljpeg_diff(ushort *huff)
+{
+  int len, diff;
+  if (!huff)
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+
+  len = gethuff(huff);
+  if (len == 16 && (!dng_version || dng_version >= 0x1010000))
+    return -32768;
+  diff = getbits(len);
+
+
+  if ((diff & (1 << (len - 1))) == 0)
+    diff -= (1 << len) - 1;
+  return diff;
+}
+
+ushort *LibRaw::ljpeg_row(int jrow, struct jhead *jh)
+{
+  int col, c, diff, pred, spred = 0;
+  ushort mark = 0, *row[3];
+
+  // Use the optimized, unrolled version if possible.
+  if (!jh->sraw)
+    return ljpeg_row_unrolled(jrow, jh);
+
+  if (jh->restart != 0 && jrow * jh->wide % jh->restart == 0)
+  {
+    FORC(6) jh->vpred[c] = 1 << (jh->bits - 1);
+    if (jrow)
+    {
+      fseek(ifp, -2, SEEK_CUR);
+      do
+        mark = (mark << 8) + (c = fgetc(ifp));
+      while (c != EOF && mark >> 4 != 0xffd);
+    }
+    getbits(-1);
+  }
+  FORC3 row[c] = jh->row + jh->wide * jh->clrs * ((jrow + c) & 1);
+  for (col = 0; col < jh->wide; col++)
+    FORC(jh->clrs)
+    {
+      diff = ljpeg_diff(jh->huff[c]);
+      if (jh->sraw && c <= jh->sraw && (col | c))
+        pred = spred;
+      else if (col)
+        pred = row[0][-jh->clrs];
+      else
+        pred = (jh->vpred[c] += diff) - diff;
+      if (jrow && col)
+        switch (jh->psv)
+        {
+        case 1:
+          break;
+        case 2:
+          pred = row[1][0];
+          break;
+        case 3:
+          pred = row[1][-jh->clrs];
+          break;
+        case 4:
+          pred = pred + row[1][0] - row[1][-jh->clrs];
+          break;
+        case 5:
+          pred = pred + ((row[1][0] - row[1][-jh->clrs]) >> 1);
+          break;
+        case 6:
+          pred = row[1][0] + ((pred - row[1][-jh->clrs]) >> 1);
+          break;
+        case 7:
+          pred = (pred + row[1][0]) >> 1;
+          break;
+        default:
+          pred = 0;
+        }
+      if ((**row = pred + diff) >> jh->bits)
+		  if(!(load_flags & 512))
+			derror();
+      if (c <= jh->sraw)
+        spred = **row;
+      row[0]++;
+      row[1]++;
+    }
+  return row[2];
+}
+
+ushort *LibRaw::ljpeg_row_unrolled(int jrow, struct jhead *jh)
+{
+  int col, c, diff, pred;
+  ushort mark = 0, *row[3];
+
+  if (jh->restart != 0 && jrow * jh->wide % jh->restart == 0)
+  {
+    FORC(6) jh->vpred[c] = 1 << (jh->bits - 1);
+    if (jrow)
+    {
+      fseek(ifp, -2, SEEK_CUR);
+      do
+        mark = (mark << 8) + (c = fgetc(ifp));
+      while (c != EOF && mark >> 4 != 0xffd);
+    }
+    getbits(-1);
+  }
+  FORC3 row[c] = jh->row + jh->wide * jh->clrs * ((jrow + c) & 1);
+
+  // The first column uses one particular predictor.
+  FORC(jh->clrs)
+  {
+    diff = ljpeg_diff(jh->huff[c]);
+    pred = (jh->vpred[c] += diff) - diff;
+    if ((**row = pred + diff) >> jh->bits)
+      derror();
+    row[0]++;
+    row[1]++;
+  }
+
+  if (!jrow)
+  {
+    for (col = 1; col < jh->wide; col++)
+      FORC(jh->clrs)
+      {
+        diff = ljpeg_diff(jh->huff[c]);
+        pred = row[0][-jh->clrs];
+        if ((**row = pred + diff) >> jh->bits)
+          derror();
+        row[0]++;
+        row[1]++;
+      }
+  }
+  else if (jh->psv == 1)
+  {
+    for (col = 1; col < jh->wide; col++)
+      FORC(jh->clrs)
+      {
+        diff = ljpeg_diff(jh->huff[c]);
+        pred = row[0][-jh->clrs];
+        if ((**row = pred + diff) >> jh->bits)
+          derror();
+        row[0]++;
+      }
+  }
+  else
+  {
+    for (col = 1; col < jh->wide; col++)
+      FORC(jh->clrs)
+      {
+        diff = ljpeg_diff(jh->huff[c]);
+        pred = row[0][-jh->clrs];
+        switch (jh->psv)
+        {
+        case 1:
+          break;
+        case 2:
+          pred = row[1][0];
+          break;
+        case 3:
+          pred = row[1][-jh->clrs];
+          break;
+        case 4:
+          pred = pred + row[1][0] - row[1][-jh->clrs];
+          break;
+        case 5:
+          pred = pred + ((row[1][0] - row[1][-jh->clrs]) >> 1);
+          break;
+        case 6:
+          pred = row[1][0] + ((pred - row[1][-jh->clrs]) >> 1);
+          break;
+        case 7:
+          pred = (pred + row[1][0]) >> 1;
+          break;
+        default:
+          pred = 0;
+        }
+        if ((**row = pred + diff) >> jh->bits)
+          derror();
+        row[0]++;
+        row[1]++;
+      }
+  }
+  return row[2];
+}
+
+void LibRaw::lossless_jpeg_load_raw()
+{
+  int jwide, jhigh, jrow, jcol, val, jidx, i, j, row = 0, col = 0;
+  struct jhead jh;
+  ushort *rp;
+
+  if (!ljpeg_start(&jh, 0))
+    return;
+
+  if (jh.wide < 1 || jh.high < 1 || jh.clrs < 1 || jh.bits < 1)
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+  jwide = jh.wide * jh.clrs;
+  jhigh = jh.high;
+  if (jh.clrs == 4 && jwide >= raw_width * 2)
+    jhigh *= 2;
+
+  try
+  {
+    for (jrow = 0; jrow < jh.high; jrow++)
+    {
+      checkCancel();
+      rp = ljpeg_row(jrow, &jh);
+      if (load_flags & 1)
+        row = jrow & 1 ? height - 1 - jrow / 2 : jrow / 2;
+      for (jcol = 0; jcol < jwide; jcol++)
+      {
+        val = curve[*rp++];
+        if (cr2_slice[0])
+        {
+          jidx = jrow * jwide + jcol;
+          i = jidx / (cr2_slice[1] * raw_height);
+          if ((j = i >= cr2_slice[0]))
+            i = cr2_slice[0];
+          jidx -= i * (cr2_slice[1] * raw_height);
+          row = jidx / cr2_slice[1 + j];
+          col = jidx % cr2_slice[1 + j] + i * cr2_slice[1];
+        }
+        if (raw_width == 3984 && (col -= 2) < 0)
+          col += (row--, raw_width);
+        if (row > raw_height)
+          throw LIBRAW_EXCEPTION_IO_CORRUPT;
+        if ((unsigned)row < raw_height)
+          RAW(row, col) = val;
+        if (++col >= raw_width)
+          col = (row++, 0);
+      }
+    }
+  }
+  catch (...)
+  {
+    ljpeg_end(&jh);
+    throw;
+  }
+  ljpeg_end(&jh);
+}
+
+void LibRaw::canon_sraw_load_raw()
+{
+  struct jhead jh;
+  short *rp = 0, (*ip)[4];
+  int jwide, slice, scol, ecol, row, col, jrow = 0, jcol = 0, pix[3], c;
+  int v[3] = {0, 0, 0}, ver, hue;
+  int saved_w = width, saved_h = height;
+  char *cp;
+
+  if (!ljpeg_start(&jh, 0) || jh.clrs < 4)
+    return;
+  jwide = (jh.wide >>= 1) * jh.clrs;
+
+  if (load_flags & 256)
+  {
+    width = raw_width;
+    height = raw_height;
+  }
+
+  try
+  {
+    for (ecol = slice = 0; slice <= cr2_slice[0]; slice++)
+    {
+      scol = ecol;
+      ecol += cr2_slice[1] * 2 / jh.clrs;
+      if (!cr2_slice[0] || ecol > raw_width - 1)
+        ecol = raw_width & -2;
+      for (row = 0; row < height; row += (jh.clrs >> 1) - 1)
+      {
+        checkCancel();
+        ip = (short(*)[4])image + row * width;
+        for (col = scol; col < ecol; col += 2, jcol += jh.clrs)
+        {
+          if ((jcol %= jwide) == 0)
+            rp = (short *)ljpeg_row(jrow++, &jh);
+          if (col >= width)
+            continue;
+          if (imgdata.params.raw_processing_options &
+              LIBRAW_PROCESSING_SRAW_NO_INTERPOLATE)
+          {
+            FORC(jh.clrs - 2)
+            {
+              ip[col + (c >> 1) * width + (c & 1)][0] = rp[jcol + c];
+              ip[col + (c >> 1) * width + (c & 1)][1] =
+                  ip[col + (c >> 1) * width + (c & 1)][2] = 8192;
+            }
+            ip[col][1] = rp[jcol + jh.clrs - 2] - 8192;
+            ip[col][2] = rp[jcol + jh.clrs - 1] - 8192;
+          }
+          else if (imgdata.params.raw_processing_options &
+                   LIBRAW_PROCESSING_SRAW_NO_RGB)
+          {
+            FORC(jh.clrs - 2)
+            ip[col + (c >> 1) * width + (c & 1)][0] = rp[jcol + c];
+            ip[col][1] = rp[jcol + jh.clrs - 2] - 8192;
+            ip[col][2] = rp[jcol + jh.clrs - 1] - 8192;
+          }
+          else
+          {
+            FORC(jh.clrs - 2)
+            ip[col + (c >> 1) * width + (c & 1)][0] = rp[jcol + c];
+            ip[col][1] = rp[jcol + jh.clrs - 2] - 16384;
+            ip[col][2] = rp[jcol + jh.clrs - 1] - 16384;
+          }
+        }
+      }
+    }
+  }
+  catch (...)
+  {
+    ljpeg_end(&jh);
+    throw;
+  }
+
+  if (imgdata.params.raw_processing_options &
+      LIBRAW_PROCESSING_SRAW_NO_INTERPOLATE)
+  {
+    ljpeg_end(&jh);
+    maximum = 0x3fff;
+    height = saved_h;
+    width = saved_w;
+    return;
+  }
+
+  try
+  {
+    for (cp = model2; *cp && !isdigit(*cp); cp++)
+      ;
+    sscanf(cp, "%d.%d.%d", v, v + 1, v + 2);
+    ver = (v[0] * 1000 + v[1]) * 1000 + v[2];
+    hue = (jh.sraw + 1) << 2;
+    if (unique_id >= 0x80000281ULL ||
+        (unique_id == 0x80000218ULL && ver > 1000006))
+      hue = jh.sraw << 1;
+    ip = (short(*)[4])image;
+    rp = ip[0];
+    for (row = 0; row < height; row++, ip += width)
+    {
+      checkCancel();
+      if (row & (jh.sraw >> 1))
+      {
+        for (col = 0; col < width; col += 2)
+          for (c = 1; c < 3; c++)
+            if (row == height - 1)
+            {
+              ip[col][c] = ip[col - width][c];
+            }
+            else
+            {
+              ip[col][c] = (ip[col - width][c] + ip[col + width][c] + 1) >> 1;
+            }
+      }
+      for (col = 1; col < width; col += 2)
+        for (c = 1; c < 3; c++)
+          if (col == width - 1)
+            ip[col][c] = ip[col - 1][c];
+          else
+            ip[col][c] = (ip[col - 1][c] + ip[col + 1][c] + 1) >> 1;
+    }
+    if (!(imgdata.params.raw_processing_options &
+          LIBRAW_PROCESSING_SRAW_NO_RGB))
+      for (; rp < ip[0]; rp += 4)
+      {
+        checkCancel();
+        if ((unique_id == CanonID_EOS_5D_Mark_II) ||
+            (unique_id == CanonID_EOS_7D)         ||
+            (unique_id == CanonID_EOS_50D)        ||
+            (unique_id == CanonID_EOS_1D_Mark_IV) ||
+            (unique_id == CanonID_EOS_60D))
+        {
+          rp[1] = (rp[1] << 2) + hue;
+          rp[2] = (rp[2] << 2) + hue;
+          pix[0] = rp[0] + ((50 * rp[1] + 22929 * rp[2]) >> 14);
+          pix[1] = rp[0] + ((-5640 * rp[1] - 11751 * rp[2]) >> 14);
+          pix[2] = rp[0] + ((29040 * rp[1] - 101 * rp[2]) >> 14);
+        }
+        else
+        {
+          if (unique_id < CanonID_EOS_5D_Mark_II)
+            rp[0] -= 512;
+          pix[0] = rp[0] + rp[2];
+          pix[2] = rp[0] + rp[1];
+          pix[1] = rp[0] + ((-778 * rp[1] - (rp[2] << 11)) >> 12);
+        }
+        FORC3 rp[c] = CLIP15(pix[c] * sraw_mul[c] >> 10);
+      }
+  }
+  catch (...)
+  {
+    ljpeg_end(&jh);
+    throw;
+  }
+  height = saved_h;
+  width = saved_w;
+  ljpeg_end(&jh);
+  maximum = 0x3fff;
+}
+
+void LibRaw::ljpeg_idct(struct jhead *jh)
+{
+  int c, i, j, len, skip, coef;
+  float work[3][8][8];
+  static float cs[106] = {0};
+  static const uchar zigzag[80] = {
+      0,  1,  8,  16, 9,  2,  3,  10, 17, 24, 32, 25, 18, 11, 4,  5,
+      12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6,  7,  14, 21, 28,
+      35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51,
+      58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63,
+      63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63};
+
+  if (!cs[0])
+    FORC(106) cs[c] = cos((c & 31) * M_PI / 16) / 2;
+  memset(work, 0, sizeof work);
+  work[0][0][0] = jh->vpred[0] += ljpeg_diff(jh->huff[0]) * jh->quant[0];
+  for (i = 1; i < 64; i++)
+  {
+    len = gethuff(jh->huff[16]);
+    i += skip = len >> 4;
+    if (!(len &= 15) && skip < 15)
+      break;
+    coef = getbits(len);
+    if ((coef & (1 << (len - 1))) == 0)
+      coef -= (1 << len) - 1;
+    ((float *)work)[zigzag[i]] = coef * jh->quant[i];
+  }
+  FORC(8) work[0][0][c] *= M_SQRT1_2;
+  FORC(8) work[0][c][0] *= M_SQRT1_2;
+  for (i = 0; i < 8; i++)
+    for (j = 0; j < 8; j++)
+      FORC(8) work[1][i][j] += work[0][i][c] * cs[(j * 2 + 1) * c];
+  for (i = 0; i < 8; i++)
+    for (j = 0; j < 8; j++)
+      FORC(8) work[2][i][j] += work[1][c][j] * cs[(i * 2 + 1) * c];
+
+  FORC(64) jh->idct[c] = CLIP(((float *)work[2])[c] + 0.5);
+}
+
+void LibRaw::pentax_load_raw()
+{
+  ushort bit[2][15], huff[4097];
+  int dep, row, col, diff, c, i;
+  ushort vpred[2][2] = {{0, 0}, {0, 0}}, hpred[2];
+
+  fseek(ifp, meta_offset, SEEK_SET);
+  dep = (get2() + 12) & 15;
+  fseek(ifp, 12, SEEK_CUR);
+  FORC(dep) bit[0][c] = get2();
+  FORC(dep) bit[1][c] = fgetc(ifp);
+  FORC(dep)
+  for (i = bit[0][c]; i <= ((bit[0][c] + (4096 >> bit[1][c]) - 1) & 4095);)
+    huff[++i] = bit[1][c] << 8 | c;
+  huff[0] = 12;
+  fseek(ifp, data_offset, SEEK_SET);
+  getbits(-1);
+  for (row = 0; row < raw_height; row++)
+  {
+    checkCancel();
+    for (col = 0; col < raw_width; col++)
+    {
+      diff = ljpeg_diff(huff);
+      if (col < 2)
+        hpred[col] = vpred[row & 1][col] += diff;
+      else
+        hpred[col & 1] += diff;
+      RAW(row, col) = hpred[col & 1];
+      if (hpred[col & 1] >> tiff_bps)
+        derror();
+    }
+  }
+}
+void LibRaw::nikon_read_curve()
+{
+  ushort ver0, ver1, vpred[2][2], csize;
+  int i, step, max;
+
+  fseek(ifp, meta_offset, SEEK_SET);
+  ver0 = fgetc(ifp);
+  ver1 = fgetc(ifp);
+  if (ver0 == 0x49 || ver1 == 0x58)
+    fseek(ifp, 2110, SEEK_CUR);
+  read_shorts(vpred[0], 4);
+  max = 1 << tiff_bps & 0x7fff;
+  if ((csize = get2()) > 1)
+    step = max / (csize - 1);
+  if (ver0 == 0x44 && (ver1 == 0x20 || (ver1 == 0x40 && step > 3)) && step > 0)
+  {
+    if (ver1 == 0x40)
+    {
+      step /= 4;
+      max /= 4;
+    }
+    for (i = 0; i < csize; i++)
+      curve[i * step] = get2();
+    for (i = 0; i < max; i++)
+      curve[i] = (curve[i - i % step] * (step - i % step) +
+                  curve[i - i % step + step] * (i % step)) /
+                 step;
+  }
+  else if (ver0 != 0x46 && csize <= 0x4001)
+    read_shorts(curve, max = csize);
+}
+
+void LibRaw::nikon_load_raw()
+{
+  static const uchar nikon_tree[][32] = {
+      {0, 1, 5, 1, 1, 1, 1, 1, 1, 2, 0,  0,  0, 0, 0, 0, /* 12-bit lossy */
+       5, 4, 3, 6, 2, 7, 1, 0, 8, 9, 11, 10, 12},
+      {0,    1,    5,    1,    1,    1, 1, 1, 1, 2, 0, 0,  0,  0,
+       0,    0, /* 12-bit lossy after split */
+       0x39, 0x5a, 0x38, 0x27, 0x16, 5, 4, 3, 2, 1, 0, 11, 12, 12},
+
+      {0, 1, 4, 2, 3, 1, 2, 0, 0, 0, 0,  0,  0, 0, 0, 0, /* 12-bit lossless */
+       5, 4, 6, 3, 7, 2, 8, 1, 9, 0, 10, 11, 12},
+      {0, 1, 4, 3, 1, 1, 1, 1, 1, 2, 0,  0,  0,  0,  0, 0, /* 14-bit lossy */
+       5, 6, 4, 7, 8, 3, 9, 2, 1, 0, 10, 11, 12, 13, 14},
+      {0, 1,    5,    1,    1,    1, 1, 1, 1, 1, 2, 0, 0, 0,  0,
+       0, /* 14-bit lossy after split */
+       8, 0x5c, 0x4b, 0x3a, 0x29, 7, 6, 5, 4, 3, 2, 1, 0, 13, 14},
+      {0, 1, 4, 2, 2, 3, 1,  2, 0,  0,  0, 0, 0, 0,  0, 0, /* 14-bit lossless */
+       7, 6, 8, 5, 9, 4, 10, 3, 11, 12, 2, 0, 1, 13, 14}};
+  ushort *huff, ver0, ver1, vpred[2][2], hpred[2];
+  int i, min, max, tree = 0, split = 0, row, col, len, shl, diff;
+
+  fseek(ifp, meta_offset, SEEK_SET);
+  ver0 = fgetc(ifp);
+  ver1 = fgetc(ifp);
+  if (ver0 == 0x49 || ver1 == 0x58)
+    fseek(ifp, 2110, SEEK_CUR);
+  if (ver0 == 0x46)
+    tree = 2;
+  if (tiff_bps == 14)
+    tree += 3;
+  read_shorts(vpred[0], 4);
+  max = 1 << tiff_bps & 0x7fff;
+  if (ver0 == 0x44 && (ver1 == 0x20 || ver1 == 0x40))
+  {
+    if (ver1 == 0x40)
+      max /= 4;
+    fseek(ifp, meta_offset + 562, SEEK_SET);
+    split = get2();
+  }
+
+  while (max > 2 && (curve[max - 2] == curve[max - 1]))
+    max--;
+  huff = make_decoder(nikon_tree[tree]);
+  fseek(ifp, data_offset, SEEK_SET);
+  getbits(-1);
+  try
+  {
+    for (min = row = 0; row < height; row++)
+    {
+      checkCancel();
+      if (split && row == split)
+      {
+        free(huff);
+        huff = make_decoder(nikon_tree[tree + 1]);
+        max += (min = 16) << 1;
+      }
+      for (col = 0; col < raw_width; col++)
+      {
+        i = gethuff(huff);
+        len = i & 15;
+        shl = i >> 4;
+        diff = ((getbits(len - shl) << 1) + 1) << shl >> 1;
+        if (len > 0 && (diff & (1 << (len - 1))) == 0)
+          diff -= (1 << len) - !shl;
+        if (col < 2)
+          hpred[col] = vpred[row & 1][col] += diff;
+        else
+          hpred[col & 1] += diff;
+        if ((ushort)(hpred[col & 1] + min) >= max)
+          derror();
+        RAW(row, col) = curve[LIM((short)hpred[col & 1], 0, 0x3fff)];
+      }
+    }
+  }
+  catch (...)
+  {
+    free(huff);
+    throw;
+  }
+  free(huff);
+}
+
+void LibRaw::nikon_yuv_load_raw()
+{
+  if (!image)
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+  int row, col, yuv[4], rgb[3], b, c;
+  UINT64 bitbuf = 0;
+  float cmul[4];
+  FORC4 { cmul[c] = cam_mul[c] > 0.001f ? cam_mul[c] : 1.f; }
+  for (row = 0; row < raw_height; row++)
+  {
+    checkCancel();
+
+    for (col = 0; col < raw_width; col++)
+    {
+      if (!(b = col & 1))
+      {
+        bitbuf = 0;
+        FORC(6) bitbuf |= (UINT64)fgetc(ifp) << c * 8;
+        FORC(4) yuv[c] = (bitbuf >> c * 12 & 0xfff) - (c >> 1 << 11);
+      }
+      rgb[0] = yuv[b] + 1.370705 * yuv[3];
+      rgb[1] = yuv[b] - 0.337633 * yuv[2] - 0.698001 * yuv[3];
+      rgb[2] = yuv[b] + 1.732446 * yuv[2];
+      FORC3 image[row * width + col][c] =
+          curve[LIM(rgb[c], 0, 0xfff)] / cmul[c];
+    }
+  }
+}
+
+void LibRaw::rollei_load_raw()
+{
+  uchar pixel[10];
+  unsigned iten = 0, isix, i, buffer = 0, todo[16];
+  if (raw_width > 32767 || raw_height > 32767)
+    throw LIBRAW_EXCEPTION_IO_BADFILE;
+  unsigned maxpixel = raw_width * (raw_height + 7);
+
+  isix = raw_width * raw_height * 5 / 8;
+  while (fread(pixel, 1, 10, ifp) == 10)
+  {
+    checkCancel();
+    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)
+      if (todo[i] < maxpixel)
+        raw_image[todo[i]] = (todo[i + 1] & 0x3ff);
+      else
+        derror();
+  }
+  maximum = 0x3ff;
+}
+
+void LibRaw::nokia_load_raw()
+{
+  uchar *data, *dp;
+  int rev, dwide, row, col, c;
+  double sum[] = {0, 0};
+
+  rev = 3 * (order == 0x4949);
+  dwide = (raw_width * 5 + 1) / 4;
+#ifdef USE_6BY9RPI
+  if (raw_stride)
+	  dwide = raw_stride;
+#endif
+  data = (uchar *)malloc(dwide * 2);
+  merror(data, "nokia_load_raw()");
+  try
+  {
+    for (row = 0; row < raw_height; row++)
+    {
+      checkCancel();
+      if (fread(data + dwide, 1, dwide, ifp) < dwide)
+        derror();
+      FORC(dwide) data[c] = data[dwide + (c ^ rev)];
+      for (dp = data, col = 0; col < raw_width; dp += 5, col += 4)
+        FORC4 RAW(row, col + c) = (dp[c] << 2) | (dp[4] >> (c << 1) & 3);
+    }
+  }
+  catch (...)
+  {
+    free(data);
+    throw;
+  }
+  free(data);
+  maximum = 0x3ff;
+#ifdef USE_6BY9RPI
+  if (!strcmp(make, "OmniVision") ||
+	  !strcmp(make, "Sony") ||
+	  !strcmp(make, "RaspberryPi")) return;
+#else
+  if (strncmp(make, "OmniVision", 10))
+	  return;
+#endif
+  row = raw_height / 2;
+  FORC(width - 1)
+  {
+    sum[c & 1] += SQR(RAW(row, c) - RAW(row + 1, c + 1));
+    sum[~c & 1] += SQR(RAW(row + 1, c) - RAW(row, c + 1));
+  }
+  if (sum[1] > sum[0])
+    filters = 0x4b4b4b4b;
+}
+
+void LibRaw::canon_rmf_load_raw()
+{
+  int row, col, bits, orow, ocol, c;
+
+  int *words = (int *)malloc(sizeof(int) * (raw_width / 3 + 1));
+  merror(words, "canon_rmf_load_raw");
+  for (row = 0; row < raw_height; row++)
+  {
+    checkCancel();
+    fread(words, sizeof(int), raw_width / 3, ifp);
+    for (col = 0; col < raw_width - 2; col += 3)
+    {
+      bits = words[col / 3];
+      FORC3
+      {
+        orow = row;
+        if ((ocol = col + c - 4) < 0)
+        {
+          ocol += raw_width;
+          if ((orow -= 2) < 0)
+            orow += raw_height;
+        }
+        RAW(orow, ocol) = curve[bits >> (10 * c + 2) & 0x3ff];
+      }
+    }
+  }
+  free(words);
+  maximum = curve[0x3ff];
+}
+
+unsigned LibRaw::pana_data(int nb, unsigned *bytes)
+{
+#ifndef LIBRAW_NOTHREADS
+#define vpos tls->pana_data.vpos
+#define buf tls->pana_data.buf
+#else
+  static uchar buf[0x4002];
+  static int vpos;
+#endif
+  int byte;
+
+  if (!nb && !bytes)
+    return vpos = 0;
+
+  if (!vpos)
+  {
+    fread(buf + load_flags, 1, 0x4000 - load_flags, ifp);
+    fread(buf, 1, load_flags, ifp);
+  }
+
+  if (pana_encoding == 5)
+  {
+    for (byte = 0; byte < 16; byte++)
+    {
+      bytes[byte] = buf[vpos++];
+      vpos &= 0x3FFF;
+    }
+  }
+  else
+  {
+    vpos = (vpos - nb) & 0x1ffff;
+    byte = vpos >> 3 ^ 0x3ff0;
+    return (buf[byte] | buf[byte + 1] << 8) >> (vpos & 7) & ~((~0u) << nb);
+  }
+  return 0;
+#ifndef LIBRAW_NOTHREADS
+#undef vpos
+#undef buf
+#endif
+}
+
+void LibRaw::panasonic_load_raw()
+{
+  int row, col, i, j, sh = 0, pred[2], nonz[2];
+  unsigned bytes[16];
+  ushort *raw_block_data;
+
+  pana_data(0, 0);
+
+  int enc_blck_size = pana_bpp == 12 ? 10 : 9;
+  if (pana_encoding == 5)
+  {
+    for (row = 0; row < raw_height; row++)
+    {
+      raw_block_data = raw_image + row * raw_width;
+      checkCancel();
+      for (col = 0; col < raw_width; col += enc_blck_size)
+      {
+        pana_data(0, bytes);
+
+        if (pana_bpp == 12)
+        {
+          raw_block_data[col] = ((bytes[1] & 0xF) << 8) + bytes[0];
+          raw_block_data[col + 1] = 16 * bytes[2] + (bytes[1] >> 4);
+          raw_block_data[col + 2] = ((bytes[4] & 0xF) << 8) + bytes[3];
+          raw_block_data[col + 3] = 16 * bytes[5] + (bytes[4] >> 4);
+          raw_block_data[col + 4] = ((bytes[7] & 0xF) << 8) + bytes[6];
+          raw_block_data[col + 5] = 16 * bytes[8] + (bytes[7] >> 4);
+          raw_block_data[col + 6] = ((bytes[10] & 0xF) << 8) + bytes[9];
+          raw_block_data[col + 7] = 16 * bytes[11] + (bytes[10] >> 4);
+          raw_block_data[col + 8] = ((bytes[13] & 0xF) << 8) + bytes[12];
+          raw_block_data[col + 9] = 16 * bytes[14] + (bytes[13] >> 4);
+        }
+        else if (pana_bpp == 14)
+        {
+          raw_block_data[col] = bytes[0] + ((bytes[1] & 0x3F) << 8);
+          raw_block_data[col + 1] =
+              (bytes[1] >> 6) + 4 * (bytes[2]) + ((bytes[3] & 0xF) << 10);
+          raw_block_data[col + 2] =
+              (bytes[3] >> 4) + 16 * (bytes[4]) + ((bytes[5] & 3) << 12);
+          raw_block_data[col + 3] = ((bytes[5] & 0xFC) >> 2) + (bytes[6] << 6);
+          raw_block_data[col + 4] = bytes[7] + ((bytes[8] & 0x3F) << 8);
+          raw_block_data[col + 5] =
+              (bytes[8] >> 6) + 4 * bytes[9] + ((bytes[10] & 0xF) << 10);
+          raw_block_data[col + 6] =
+              (bytes[10] >> 4) + 16 * bytes[11] + ((bytes[12] & 3) << 12);
+          raw_block_data[col + 7] =
+              ((bytes[12] & 0xFC) >> 2) + (bytes[13] << 6);
+          raw_block_data[col + 8] = bytes[14] + ((bytes[15] & 0x3F) << 8);
+        }
+      }
+    }
+  }
+  else
+  {
+    for (row = 0; row < raw_height; row++)
+    {
+      checkCancel();
+      for (col = 0; col < raw_width; col++)
+      {
+        if ((i = col % 14) == 0)
+          pred[0] = pred[1] = nonz[0] = nonz[1] = 0;
+        if (i % 3 == 2)
+          sh = 4 >> (3 - pana_data(2, 0));
+        if (nonz[i & 1])
+        {
+          if ((j = pana_data(8, 0)))
+          {
+            if ((pred[i & 1] -= 0x80 << sh) < 0 || sh == 4)
+              pred[i & 1] &= ~((~0u) << sh);
+            pred[i & 1] += j << sh;
+          }
+        }
+        else if ((nonz[i & 1] = pana_data(8, 0)) || i > 11)
+          pred[i & 1] = nonz[i & 1] << 4 | pana_data(4, 0);
+        if ((RAW(row, col) = pred[col & 1]) > 4098 && col < width &&
+            row < height)
+          derror();
+      }
+    }
+  }
+}
+
+void LibRaw::olympus_load_raw()
+{
+  ushort huff[4096];
+  int row, col, nbits, sign, low, high, i, c, w, n, nw;
+  int acarry[2][3], *carry, pred, diff;
+
+  huff[n = 0] = 0xc0c;
+  for (i = 12; i--;)
+    FORC(2048 >> i) huff[++n] = (i + 1) << 8 | i;
+  fseek(ifp, 7, SEEK_CUR);
+  getbits(-1);
+  for (row = 0; row < height; row++)
+  {
+    checkCancel();
+    memset(acarry, 0, sizeof acarry);
+    for (col = 0; col < raw_width; col++)
+    {
+      carry = acarry[col & 1];
+      i = 2 * (carry[2] < 3);
+      for (nbits = 2 + i; (ushort)carry[0] >> (nbits + i); nbits++)
+        ;
+      low = (sign = getbits(3)) & 3;
+      sign = sign << 29 >> 31;
+      if ((high = getbithuff(12, huff)) == 12)
+        high = getbits(16 - nbits) >> 1;
+      carry[0] = (high << nbits) | getbits(nbits);
+      diff = (carry[0] ^ sign) + carry[1];
+      carry[1] = (diff * 3 + carry[1]) >> 5;
+      carry[2] = carry[0] > 16 ? 0 : carry[2] + 1;
+      if (col >= width)
+        continue;
+      if (row < 2 && col < 2)
+        pred = 0;
+      else if (row < 2)
+        pred = RAW(row, col - 2);
+      else if (col < 2)
+        pred = RAW(row - 2, col);
+      else
+      {
+        w = RAW(row, col - 2);
+        n = RAW(row - 2, col);
+        nw = RAW(row - 2, col - 2);
+        if ((w < nw && nw < n) || (n < nw && nw < w))
+        {
+          if (ABS(w - nw) > 32 || ABS(n - nw) > 32)
+            pred = w + n - nw;
+          else
+            pred = (w + n) >> 1;
+        }
+        else
+          pred = ABS(w - nw) > ABS(n - nw) ? w : n;
+      }
+      if ((RAW(row, col) = pred + ((diff << 2) | low)) >> 12)
+        derror();
+    }
+  }
+}
+
+void LibRaw::minolta_rd175_load_raw()
+{
+  uchar pixel[768];
+  unsigned irow, box, row, col;
+
+  for (irow = 0; irow < 1481; irow++)
+  {
+    checkCancel();
+    if (fread(pixel, 1, 768, ifp) < 768)
+      derror();
+    box = irow / 82;
+    row = irow % 82 * 12 + ((box < 12) ? box | 1 : (box - 12) * 2);
+    switch (irow)
+    {
+    case 1477:
+    case 1479:
+      continue;
+    case 1476:
+      row = 984;
+      break;
+    case 1480:
+      row = 985;
+      break;
+    case 1478:
+      row = 985;
+      box = 1;
+    }
+    if ((box < 12) && (box & 1))
+    {
+      for (col = 0; col < 1533; col++, row ^= 1)
+        if (col != 1)
+          RAW(row, col) = (col + 1) & 2
+                              ? pixel[col / 2 - 1] + pixel[col / 2 + 1]
+                              : pixel[col / 2] << 1;
+      RAW(row, 1) = pixel[1] << 1;
+      RAW(row, 1533) = pixel[765] << 1;
+    }
+    else
+      for (col = row & 1; col < 1534; col += 2)
+        RAW(row, col) = pixel[col / 2] << 1;
+  }
+  maximum = 0xff << 1;
+}
+
+void LibRaw::quicktake_100_load_raw()
+{
+  std::vector<uchar> pixel_buffer(484 * 644, 0x80);
+  uchar* pixel = &pixel_buffer[0];
+  static const short gstep[16] = {-89, -60, -44, -32, -22, -15, -8, -2,
+                                  2,   8,   15,  22,  32,  44,  60, 89};
+  static const short rstep[6][4] = {{-3, -1, 1, 3},   {-5, -1, 1, 5},
+                                    {-8, -2, 2, 8},   {-13, -3, 3, 13},
+                                    {-19, -4, 4, 19}, {-28, -6, 6, 28}};
+  static const short t_curve[256] = {
+      0,   1,    2,    3,   4,   5,   6,   7,   8,   9,   11,  12,  13,  14,
+      15,  16,   17,   18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,
+      29,  30,   32,   33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,
+      44,  45,   46,   47,  48,  49,  50,  51,  53,  54,  55,  56,  57,  58,
+      59,  60,   61,   62,  63,  64,  65,  66,  67,  68,  69,  70,  71,  72,
+      74,  75,   76,   77,  78,  79,  80,  81,  82,  83,  84,  86,  88,  90,
+      92,  94,   97,   99,  101, 103, 105, 107, 110, 112, 114, 116, 118, 120,
+      123, 125,  127,  129, 131, 134, 136, 138, 140, 142, 144, 147, 149, 151,
+      153, 155,  158,  160, 162, 164, 166, 168, 171, 173, 175, 177, 179, 181,
+      184, 186,  188,  190, 192, 195, 197, 199, 201, 203, 205, 208, 210, 212,
+      214, 216,  218,  221, 223, 226, 230, 235, 239, 244, 248, 252, 257, 261,
+      265, 270,  274,  278, 283, 287, 291, 296, 300, 305, 309, 313, 318, 322,
+      326, 331,  335,  339, 344, 348, 352, 357, 361, 365, 370, 374, 379, 383,
+      387, 392,  396,  400, 405, 409, 413, 418, 422, 426, 431, 435, 440, 444,
+      448, 453,  457,  461, 466, 470, 474, 479, 483, 487, 492, 496, 500, 508,
+      519, 531,  542,  553, 564, 575, 587, 598, 609, 620, 631, 643, 654, 665,
+      676, 687,  698,  710, 721, 732, 743, 754, 766, 777, 788, 799, 810, 822,
+      833, 844,  855,  866, 878, 889, 900, 911, 922, 933, 945, 956, 967, 978,
+      989, 1001, 1012, 1023};
+  int rb, row, col, sharp, val = 0;
+
+  if (width > 640 || height > 480)
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+
+  getbits(-1);
+  for (row = 2; row < height + 2; row++)
+  {
+    checkCancel();
+    for (col = 2 + (row & 1); col < width + 2; col += 2)
+    {
+      val = ((pixel[(row - 1) * 644 + col - 1] + 2 * pixel[(row - 1) * 644 + col + 1] + pixel[row * 644 + col - 2]) >> 2) + gstep[getbits(4)];
+      pixel[row * 644 + col] = val = LIM(val, 0, 255);
+      if (col < 4)
+        pixel[row * 644 + col - 2] = pixel[(row + 1) * 644 + (~row & 1)] = val;
+      if (row == 2)
+        pixel[(row - 1) * 644 + col + 1] = pixel[(row - 1) * 644 + col + 3] = val;
+    }
+    pixel[row * 644 + col] = val;
+  }
+  for (rb = 0; rb < 2; rb++)
+    for (row = 2 + rb; row < height + 2; row += 2)
+    {
+      checkCancel();
+      for (col = 3 - (row & 1); col < width + 2; col += 2)
+      {
+        if (row < 4 || col < 4)
+          sharp = 2;
+        else
+        {
+          val = ABS(pixel[(row - 2) * 644 + col] - pixel[row * 644 + col - 2]) + ABS(pixel[(row - 2) * 644 + col] - pixel[(row - 2) * 644 + col - 2]) +
+                ABS(pixel[row * 644 + col - 2] - pixel[(row - 2) * 644 + col - 2]);
+          sharp = val < 4
+                      ? 0
+                      : val < 8
+                            ? 1
+                            : val < 16 ? 2 : val < 32 ? 3 : val < 48 ? 4 : 5;
+        }
+        val = ((pixel[(row - 2) * 644 + col] + pixel[row * 644 + col - 2]) >> 1) + rstep[sharp][getbits(2)];
+        pixel[row * 644 + col] = val = LIM(val, 0, 255);
+        if (row < 4)
+          pixel[(row - 2) * 644 + col + 2] = val;
+        if (col < 4)
+          pixel[(row + 2) * 644 + col - 2] = val;
+      }
+    }
+  for (row = 2; row < height + 2; row++)
+  {
+    checkCancel();
+    for (col = 3 - (row & 1); col < width + 2; col += 2)
+    {
+      val = ((pixel[row * 644 + col - 1] + (pixel[row * 644 + col] << 2) + pixel[row * 644 + col + 1]) >> 1) - 0x100;
+      pixel[row * 644 + col] = LIM(val, 0, 255);
+    }
+  }
+  for (row = 0; row < height; row++)
+  {
+    checkCancel();
+    for (col = 0; col < width; col++)
+      RAW(row, col) = t_curve[pixel[(row + 2) * 644 + col + 2]];
+  }
+  maximum = 0x3ff;
+}
+
+void LibRaw::sony_load_raw()
+{
+  uchar head[40];
+  ushort *pixel;
+  unsigned i, key, row, col;
+
+  fseek(ifp, 200896, SEEK_SET);
+  fseek(ifp, (unsigned)fgetc(ifp) * 4 - 1, SEEK_CUR);
+  order = 0x4d4d;
+  key = get4();
+
+  fseek(ifp, 164600, SEEK_SET);
+  fread(head, 1, 40, ifp);
+  sony_decrypt((unsigned *)head, 10, 1, key);
+  for (i = 26; i-- > 22;)
+    key = key << 8 | head[i];
+
+  fseek(ifp, data_offset, SEEK_SET);
+  for (row = 0; row < raw_height; row++)
+  {
+    checkCancel();
+    pixel = raw_image + row * raw_width;
+    if (fread(pixel, 2, raw_width, ifp) < raw_width)
+      derror();
+    sony_decrypt((unsigned *)pixel, raw_width / 2, !row, key);
+    for (col = 0; col < raw_width; col++)
+      if ((pixel[col] = ntohs(pixel[col])) >> 14)
+        derror();
+  }
+  maximum = 0x3ff0;
+}
+
+void LibRaw::sony_arw_load_raw()
+{
+  std::vector<ushort> huff_buffer(32770);
+  ushort* huff = &huff_buffer[0];
+  static const ushort tab[18] = {0xf11, 0xf10, 0xe0f, 0xd0e, 0xc0d, 0xb0c,
+                                 0xa0b, 0x90a, 0x809, 0x708, 0x607, 0x506,
+                                 0x405, 0x304, 0x303, 0x300, 0x202, 0x201};
+  int i, c, n, col, row, sum = 0;
+
+  huff[0] = 15;
+  for (n = i = 0; i < 18; i++)
+    FORC(32768 >> (tab[i] >> 8)) huff[++n] = tab[i];
+  getbits(-1);
+  for (col = raw_width; col--;)
+  {
+    checkCancel();
+    for (row = 0; row < raw_height + 1; row += 2)
+    {
+      if (row == raw_height)
+        row = 1;
+      if ((sum += ljpeg_diff(huff)) >> 12)
+        derror();
+      if (row < height)
+        RAW(row, col) = sum;
+    }
+  }
+}
+
+void LibRaw::sony_arw2_load_raw()
+{
+  uchar *data, *dp;
+  ushort pix[16];
+  int row, col, val, max, min, imax, imin, sh, bit, i;
+
+  data = (uchar *)malloc(raw_width + 1);
+  merror(data, "sony_arw2_load_raw()");
+  try
+  {
+    for (row = 0; row < height; row++)
+    {
+      checkCancel();
+      fread(data, 1, raw_width, ifp);
+      for (dp = data, col = 0; col < raw_width - 30; dp += 16)
+      {
+        max = 0x7ff & (val = sget4(dp));
+        min = 0x7ff & val >> 11;
+        imax = 0x0f & val >> 22;
+        imin = 0x0f & val >> 26;
+        for (sh = 0; sh < 4 && 0x80 << sh <= max - min; sh++)
+          ;
+        /* flag checks if outside of loop */
+        if (!(imgdata.params.raw_processing_options &
+              LIBRAW_PROCESSING_SONYARW2_ALLFLAGS) // no flag set
+            || (imgdata.params.raw_processing_options &
+                LIBRAW_PROCESSING_SONYARW2_DELTATOVALUE))
+        {
+          for (bit = 30, i = 0; i < 16; i++)
+            if (i == imax)
+              pix[i] = max;
+            else if (i == imin)
+              pix[i] = min;
+            else
+            {
+              pix[i] =
+                  ((sget2(dp + (bit >> 3)) >> (bit & 7) & 0x7f) << sh) + min;
+              if (pix[i] > 0x7ff)
+                pix[i] = 0x7ff;
+              bit += 7;
+            }
+        }
+        else if (imgdata.params.raw_processing_options &
+                 LIBRAW_PROCESSING_SONYARW2_BASEONLY)
+        {
+          for (bit = 30, i = 0; i < 16; i++)
+            if (i == imax)
+              pix[i] = max;
+            else if (i == imin)
+              pix[i] = min;
+            else
+              pix[i] = 0;
+        }
+        else if (imgdata.params.raw_processing_options &
+                 LIBRAW_PROCESSING_SONYARW2_DELTAONLY)
+        {
+          for (bit = 30, i = 0; i < 16; i++)
+            if (i == imax)
+              pix[i] = 0;
+            else if (i == imin)
+              pix[i] = 0;
+            else
+            {
+              pix[i] =
+                  ((sget2(dp + (bit >> 3)) >> (bit & 7) & 0x7f) << sh) + min;
+              if (pix[i] > 0x7ff)
+                pix[i] = 0x7ff;
+              bit += 7;
+            }
+        }
+        else if (imgdata.params.raw_processing_options &
+                 LIBRAW_PROCESSING_SONYARW2_DELTAZEROBASE)
+        {
+          for (bit = 30, i = 0; i < 16; i++)
+            if (i == imax)
+              pix[i] = 0;
+            else if (i == imin)
+              pix[i] = 0;
+            else
+            {
+              pix[i] = ((sget2(dp + (bit >> 3)) >> (bit & 7) & 0x7f) << sh);
+              if (pix[i] > 0x7ff)
+                pix[i] = 0x7ff;
+              bit += 7;
+            }
+        }
+
+        if (imgdata.params.raw_processing_options &
+            LIBRAW_PROCESSING_SONYARW2_DELTATOVALUE)
+        {
+          for (i = 0; i < 16; i++, col += 2)
+          {
+            unsigned slope =
+                pix[i] < 1001 ? 2
+                              : curve[pix[i] << 1] - curve[(pix[i] << 1) - 2];
+            unsigned step = 1 << sh;
+            RAW(row, col) =
+                curve[pix[i] << 1] >
+                        black + imgdata.params.sony_arw2_posterization_thr
+                    ? LIM(((slope * step * 1000) /
+                           (curve[pix[i] << 1] - black)),
+                          0, 10000)
+                    : 0;
+          }
+        }
+        else
+          for (i = 0; i < 16; i++, col += 2)
+            RAW(row, col) = curve[pix[i] << 1];
+        col -= col & 1 ? 1 : 31;
+      }
+    }
+  }
+  catch (...)
+  {
+    free(data);
+    throw;
+  }
+  if (imgdata.params.raw_processing_options &
+      LIBRAW_PROCESSING_SONYARW2_DELTATOVALUE)
+    maximum = 10000;
+  free(data);
+}
+
+void LibRaw::samsung_load_raw()
+{
+  int row, col, c, i, dir, op[4], len[4];
+  if (raw_width > 32768 ||
+      raw_height > 32768) // definitely too much for old samsung
+    throw LIBRAW_EXCEPTION_IO_BADFILE;
+  unsigned maxpixels = raw_width * (raw_height + 7);
+
+  order = 0x4949;
+  for (row = 0; row < raw_height; row++)
+  {
+    checkCancel();
+    fseek(ifp, strip_offset + row * 4, SEEK_SET);
+    fseek(ifp, data_offset + get4(), SEEK_SET);
+    ph1_bits(-1);
+    FORC4 len[c] = row < 2 ? 7 : 4;
+    for (col = 0; col < raw_width; col += 16)
+    {
+      dir = ph1_bits(1);
+      FORC4 op[c] = ph1_bits(2);
+      FORC4 switch (op[c])
+      {
+      case 3:
+        len[c] = ph1_bits(4);
+        break;
+      case 2:
+        len[c]--;
+        break;
+      case 1:
+        len[c]++;
+      }
+      for (c = 0; c < 16; c += 2)
+      {
+        i = len[((c & 1) << 1) | (c >> 3)];
+        unsigned idest = RAWINDEX(row, col + c);
+        unsigned isrc = (dir ? RAWINDEX(row + (~c | -2), col + c)
+                             : col ? RAWINDEX(row, col + (c | -2)) : 0);
+        if (idest < maxpixels &&
+            isrc <
+                maxpixels) // less than zero is handled by unsigned conversion
+          RAW(row, col + c) = (i > 0 ? ((signed)ph1_bits(i) << (32 - i) >> (32 - i)) : 0) +
+            (dir ? RAW(row + (~c | -2), col + c) : col ? RAW(row, col + (c | -2)) : 128);
+        else
+          derror();
+        if (c == 14)
+          c = -1;
+      }
+    }
+  }
+  for (row = 0; row < raw_height - 1; row += 2)
+    for (col = 0; col < raw_width - 1; col += 2)
+      SWAP(RAW(row, col + 1), RAW(row + 1, col));
+}
+
+void LibRaw::samsung2_load_raw()
+{
+  static const ushort tab[14] = {0x304, 0x307, 0x206, 0x205, 0x403,
+                                 0x600, 0x709, 0x80a, 0x90b, 0xa0c,
+                                 0xa0d, 0x501, 0x408, 0x402};
+  ushort huff[1026], vpred[2][2] = {{0, 0}, {0, 0}}, hpred[2];
+  int i, c, n, row, col, diff;
+
+  huff[0] = 10;
+  for (n = i = 0; i < 14; i++)
+    FORC(1024 >> (tab[i] >> 8)) huff[++n] = tab[i];
+  getbits(-1);
+  for (row = 0; row < raw_height; row++)
+  {
+    checkCancel();
+    for (col = 0; col < raw_width; col++)
+    {
+      diff = ljpeg_diff(huff);
+      if (col < 2)
+        hpred[col] = vpred[row & 1][col] += diff;
+      else
+        hpred[col & 1] += diff;
+      RAW(row, col) = hpred[col & 1];
+      if (hpred[col & 1] >> tiff_bps)
+        derror();
+    }
+  }
+}
+
+void LibRaw::samsung3_load_raw()
+{
+  int opt, init, mag, pmode, row, tab, col, pred, diff, i, c;
+  ushort lent[3][2], len[4], *prow[2];
+  order = 0x4949;
+  fseek(ifp, 9, SEEK_CUR);
+  opt = fgetc(ifp);
+  init = (get2(), get2());
+  for (row = 0; row < raw_height; row++)
+  {
+    checkCancel();
+    fseek(ifp, (data_offset - ftell(ifp)) & 15, SEEK_CUR);
+    ph1_bits(-1);
+    mag = 0;
+    pmode = 7;
+    FORC(6)((ushort *)lent)[c] = row < 2 ? 7 : 4;
+    prow[row & 1] = &RAW(row - 1, 1 - ((row & 1) << 1)); // green
+    prow[~row & 1] = &RAW(row - 2, 0);                   // red and blue
+    for (tab = 0; tab + 15 < raw_width; tab += 16)
+    {
+      if (~opt & 4 && !(tab & 63))
+      {
+        i = ph1_bits(2);
+        mag = i < 3 ? mag - '2' + "204"[i] : ph1_bits(12);
+      }
+      if (opt & 2)
+        pmode = 7 - 4 * ph1_bits(1);
+      else if (!ph1_bits(1))
+        pmode = ph1_bits(3);
+      if (opt & 1 || !ph1_bits(1))
+      {
+        FORC4 len[c] = ph1_bits(2);
+        FORC4
+        {
+          i = ((row & 1) << 1 | (c & 1)) % 3;
+          if (i < 0)
+            throw LIBRAW_EXCEPTION_IO_CORRUPT;
+          len[c] = len[c] < 3 ? lent[i][0] - '1' + "120"[len[c]] : ph1_bits(4);
+          lent[i][0] = lent[i][1];
+          lent[i][1] = len[c];
+        }
+      }
+      FORC(16)
+      {
+        col = tab + (((c & 7) << 1) ^ (c >> 3) ^ (row & 1));
+        if (col < 0)
+          throw LIBRAW_EXCEPTION_IO_CORRUPT;
+        if (pmode < 0)
+          throw LIBRAW_EXCEPTION_IO_CORRUPT;
+        if (pmode != 7 && row >= 2 && (col - '4' + "0224468"[pmode]) < 0)
+          throw LIBRAW_EXCEPTION_IO_CORRUPT;
+        pred = (pmode == 7 || row < 2)
+                   ? (tab ? RAW(row, tab - 2 + (col & 1)) : init)
+                   : (prow[col & 1][col - '4' + "0224468"[pmode]] +
+                      prow[col & 1][col - '4' + "0244668"[pmode]] + 1) >>
+                         1;
+        diff = ph1_bits(i = len[c >> 2]);
+        if (i > 0 && diff >> (i - 1))
+          diff -= 1 << i;
+        diff = diff * (mag * 2 + 1) + mag;
+        RAW(row, col) = pred + diff;
+      }
+    }
+  }
+}
+
+void LibRaw::redcine_load_raw()
+{
+#ifndef NO_JASPER
+  int c, row, col;
+  jas_stream_t *in;
+  jas_image_t *jimg;
+  jas_matrix_t *jmat;
+  jas_seqent_t *data;
+  ushort *img, *pix;
+
+  jas_init();
+  in = (jas_stream_t *)ifp->make_jas_stream();
+  if (!in)
+    throw LIBRAW_EXCEPTION_DECODE_JPEG2000;
+  jas_stream_seek(in, data_offset + 20, SEEK_SET);
+  jimg = jas_image_decode(in, -1, 0);
+  if (!jimg)
+  {
+    jas_stream_close(in);
+    throw LIBRAW_EXCEPTION_DECODE_JPEG2000;
+  }
+  jmat = jas_matrix_create(height / 2, width / 2);
+  merror(jmat, "redcine_load_raw()");
+  img = (ushort *)calloc((height + 2), (width + 2) * 2);
+  merror(img, "redcine_load_raw()");
+  bool fastexitflag = false;
+  try
+  {
+    FORC4
+    {
+      checkCancel();
+      jas_image_readcmpt(jimg, c, 0, 0, width / 2, height / 2, jmat);
+      data = jas_matrix_getref(jmat, 0, 0);
+      for (row = c >> 1; row < height; row += 2)
+        for (col = c & 1; col < width; col += 2)
+          img[(row + 1) * (width + 2) + col + 1] =
+              data[(row / 2) * (width / 2) + col / 2];
+    }
+    for (col = 1; col <= width; col++)
+    {
+      img[col] = img[2 * (width + 2) + col];
+      img[(height + 1) * (width + 2) + col] =
+          img[(height - 1) * (width + 2) + col];
+    }
+    for (row = 0; row < height + 2; row++)
+    {
+      img[row * (width + 2)] = img[row * (width + 2) + 2];
+      img[(row + 1) * (width + 2) - 1] = img[(row + 1) * (width + 2) - 3];
+    }
+    for (row = 1; row <= height; row++)
+    {
+      checkCancel();
+      pix = img + row * (width + 2) + (col = 1 + (FC(row, 1) & 1));
+      for (; col <= width; col += 2, pix += 2)
+      {
+        c = (((pix[0] - 0x800) << 3) + pix[-(width + 2)] + pix[width + 2] +
+             pix[-1] + pix[1]) >>
+            2;
+        pix[0] = LIM(c, 0, 4095);
+      }
+    }
+    for (row = 0; row < height; row++)
+    {
+      checkCancel();
+      for (col = 0; col < width; col++)
+        RAW(row, col) = curve[img[(row + 1) * (width + 2) + col + 1]];
+    }
+  }
+  catch (...)
+  {
+    fastexitflag = true;
+  }
+  free(img);
+  jas_matrix_destroy(jmat);
+  jas_image_destroy(jimg);
+  jas_stream_close(in);
+  if (fastexitflag)
+    throw LIBRAW_EXCEPTION_CANCELLED_BY_CALLBACK;
+#endif
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/decoders/decoders_libraw.cpp libkdcraw/libkdcraw/libraw/src/decoders/decoders_libraw.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/decoders/decoders_libraw.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/decoders/decoders_libraw.cpp	2022-11-07 07:46:31.730795008 +0300
@@ -0,0 +1,609 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/libraw_cxx_defs.h"
+
+void LibRaw::sony_arq_load_raw()
+{
+  int row, col;
+  read_shorts(imgdata.rawdata.raw_image,
+              imgdata.sizes.raw_width * imgdata.sizes.raw_height * 4);
+  libraw_internal_data.internal_data.input->seek(
+      -2, SEEK_CUR); // avoid wrong eof error
+
+  if(imgdata.params.raw_processing_options & LIBRAW_PROCESSING_ARQ_SKIP_CHANNEL_SWAP)
+    return;
+
+  for (row = 0; row < imgdata.sizes.raw_height; row++)
+  {
+    unsigned short(*rowp)[4] =
+        (unsigned short(*)[4]) &
+        imgdata.rawdata.raw_image[row * imgdata.sizes.raw_width * 4];
+    for (col = 0; col < imgdata.sizes.raw_width; col++)
+    {
+      unsigned short g2 = rowp[col][2];
+      rowp[col][2] = rowp[col][3];
+      rowp[col][3] = g2;
+      if (((unsigned)(row - imgdata.sizes.top_margin) < imgdata.sizes.height) &&
+          ((unsigned)(col - imgdata.sizes.left_margin) < imgdata.sizes.width) &&
+          (MAX(MAX(rowp[col][0], rowp[col][1]),
+               MAX(rowp[col][2], rowp[col][3])) > imgdata.color.maximum))
+        derror();
+    }
+  }
+}
+
+void LibRaw::pentax_4shot_load_raw()
+{
+  ushort *plane = (ushort *)malloc(imgdata.sizes.raw_width *
+                                   imgdata.sizes.raw_height * sizeof(ushort));
+  int alloc_sz = imgdata.sizes.raw_width * (imgdata.sizes.raw_height + 16) * 4 *
+                 sizeof(ushort);
+  ushort(*result)[4] = (ushort(*)[4])malloc(alloc_sz);
+  struct movement_t
+  {
+    int row, col;
+  } _move[4] = {
+      {1, 1},
+      {0, 1},
+      {0, 0},
+      {1, 0},
+  };
+
+  int tidx = 0;
+  for (int i = 0; i < 4; i++)
+  {
+    int move_row, move_col;
+    if (imgdata.params.p4shot_order[i] >= '0' &&
+        imgdata.params.p4shot_order[i] <= '3')
+    {
+      move_row = ((imgdata.params.p4shot_order[i] - '0') & 2) ? 1 : 0;
+      move_col = ((imgdata.params.p4shot_order[i] - '0') & 1) ? 1 : 0;
+    }
+    else
+    {
+      move_row = _move[i].row;
+      move_col = _move[i].col;
+    }
+    for (; tidx < 16; tidx++)
+      if (tiff_ifd[tidx].t_width == imgdata.sizes.raw_width &&
+          tiff_ifd[tidx].t_height == imgdata.sizes.raw_height &&
+          tiff_ifd[tidx].bps > 8 && tiff_ifd[tidx].samples == 1)
+        break;
+    if (tidx >= 16)
+      break;
+    imgdata.rawdata.raw_image = plane;
+    ID.input->seek(tiff_ifd[tidx].offset, SEEK_SET);
+    imgdata.idata.filters = 0xb4b4b4b4;
+    libraw_internal_data.unpacker_data.data_offset = tiff_ifd[tidx].offset;
+    (this->*pentax_component_load_raw)();
+    for (int row = 0; row < imgdata.sizes.raw_height - move_row; row++)
+    {
+      int colors[2];
+      for (int c = 0; c < 2; c++)
+        colors[c] = COLOR(row, c);
+      ushort *srcrow = &plane[imgdata.sizes.raw_width * row];
+      ushort(*dstrow)[4] =
+          &result[(imgdata.sizes.raw_width) * (row + move_row) + move_col];
+      for (int col = 0; col < imgdata.sizes.raw_width - move_col; col++)
+        dstrow[col][colors[col % 2]] = srcrow[col];
+    }
+    tidx++;
+  }
+
+  if (imgdata.color.cblack[4] == 2 && imgdata.color.cblack[5] == 2)
+    for (int c = 0; c < 4; c++)
+      imgdata.color.cblack[FC(c / 2, c % 2)] +=
+          imgdata.color.cblack[6 +
+                               c / 2 % imgdata.color.cblack[4] *
+                                   imgdata.color.cblack[5] +
+                               c % 2 % imgdata.color.cblack[5]];
+  imgdata.color.cblack[4] = imgdata.color.cblack[5] = 0;
+
+  // assign things back:
+  imgdata.sizes.raw_pitch = imgdata.sizes.raw_width * 8;
+  imgdata.idata.filters = 0;
+  imgdata.rawdata.raw_alloc = imgdata.rawdata.color4_image = result;
+  free(plane);
+  imgdata.rawdata.raw_image = 0;
+}
+
+void LibRaw::hasselblad_full_load_raw()
+{
+  int row, col;
+
+  for (row = 0; row < S.height; row++)
+    for (col = 0; col < S.width; col++)
+    {
+      read_shorts(&imgdata.image[row * S.width + col][2], 1); // B
+      read_shorts(&imgdata.image[row * S.width + col][1], 1); // G
+      read_shorts(&imgdata.image[row * S.width + col][0], 1); // R
+    }
+}
+
+static inline void unpack7bytesto4x16(unsigned char *src, unsigned short *dest)
+{
+  dest[0] = (src[0] << 6) | (src[1] >> 2);
+  dest[1] = ((src[1] & 0x3) << 12) | (src[2] << 4) | (src[3] >> 4);
+  dest[2] = (src[3] & 0xf) << 10 | (src[4] << 2) | (src[5] >> 6);
+  dest[3] = ((src[5] & 0x3f) << 8) | src[6];
+}
+
+static inline void unpack28bytesto16x16ns(unsigned char *src,
+                                          unsigned short *dest)
+{
+  dest[0] = (src[3] << 6) | (src[2] >> 2);
+  dest[1] = ((src[2] & 0x3) << 12) | (src[1] << 4) | (src[0] >> 4);
+  dest[2] = (src[0] & 0xf) << 10 | (src[7] << 2) | (src[6] >> 6);
+  dest[3] = ((src[6] & 0x3f) << 8) | src[5];
+  dest[4] = (src[4] << 6) | (src[11] >> 2);
+  dest[5] = ((src[11] & 0x3) << 12) | (src[10] << 4) | (src[9] >> 4);
+  dest[6] = (src[9] & 0xf) << 10 | (src[8] << 2) | (src[15] >> 6);
+  dest[7] = ((src[15] & 0x3f) << 8) | src[14];
+  dest[8] = (src[13] << 6) | (src[12] >> 2);
+  dest[9] = ((src[12] & 0x3) << 12) | (src[19] << 4) | (src[18] >> 4);
+  dest[10] = (src[18] & 0xf) << 10 | (src[17] << 2) | (src[16] >> 6);
+  dest[11] = ((src[16] & 0x3f) << 8) | src[23];
+  dest[12] = (src[22] << 6) | (src[21] >> 2);
+  dest[13] = ((src[21] & 0x3) << 12) | (src[20] << 4) | (src[27] >> 4);
+  dest[14] = (src[27] & 0xf) << 10 | (src[26] << 2) | (src[25] >> 6);
+  dest[15] = ((src[25] & 0x3f) << 8) | src[24];
+}
+
+#define swab32(x)                                                              \
+  ((unsigned int)((((unsigned int)(x) & (unsigned int)0x000000ffUL) << 24) |   \
+                  (((unsigned int)(x) & (unsigned int)0x0000ff00UL) << 8) |    \
+                  (((unsigned int)(x) & (unsigned int)0x00ff0000UL) >> 8) |    \
+                  (((unsigned int)(x) & (unsigned int)0xff000000UL) >> 24)))
+
+static inline void swab32arr(unsigned *arr, unsigned len)
+{
+  for (unsigned i = 0; i < len; i++)
+    arr[i] = swab32(arr[i]);
+}
+#undef swab32
+
+static inline void unpack7bytesto4x16_nikon(unsigned char *src,
+                                            unsigned short *dest)
+{
+  dest[3] = (src[6] << 6) | (src[5] >> 2);
+  dest[2] = ((src[5] & 0x3) << 12) | (src[4] << 4) | (src[3] >> 4);
+  dest[1] = (src[3] & 0xf) << 10 | (src[2] << 2) | (src[1] >> 6);
+  dest[0] = ((src[1] & 0x3f) << 8) | src[0];
+}
+
+void LibRaw::nikon_14bit_load_raw()
+{
+  const unsigned linelen =
+      (unsigned)(ceilf((float)(S.raw_width * 7 / 4) / 16.0)) *
+      16; // 14512; // S.raw_width * 7 / 4;
+  const unsigned pitch = S.raw_pitch ? S.raw_pitch / 2 : S.raw_width;
+  unsigned char *buf = (unsigned char *)malloc(linelen);
+  merror(buf, "nikon_14bit_load_raw()");
+  for (int row = 0; row < S.raw_height; row++)
+  {
+    unsigned bytesread =
+        libraw_internal_data.internal_data.input->read(buf, 1, linelen);
+    unsigned short *dest = &imgdata.rawdata.raw_image[pitch * row];
+    // swab32arr((unsigned *)buf, bytesread / 4);
+    for (unsigned int sp = 0, dp = 0;
+         dp < pitch - 3 && sp < linelen - 6 && sp < bytesread - 6;
+         sp += 7, dp += 4)
+      unpack7bytesto4x16_nikon(buf + sp, dest + dp);
+  }
+  free(buf);
+}
+
+void LibRaw::fuji_14bit_load_raw()
+{
+  const unsigned linelen = S.raw_width * 7 / 4;
+  const unsigned pitch = S.raw_pitch ? S.raw_pitch / 2 : S.raw_width;
+  unsigned char *buf = (unsigned char *)malloc(linelen);
+  merror(buf, "fuji_14bit_load_raw()");
+
+  for (int row = 0; row < S.raw_height; row++)
+  {
+    unsigned bytesread =
+        libraw_internal_data.internal_data.input->read(buf, 1, linelen);
+    unsigned short *dest = &imgdata.rawdata.raw_image[pitch * row];
+    if (bytesread % 28)
+    {
+      swab32arr((unsigned *)buf, bytesread / 4);
+      for (unsigned int sp = 0, dp = 0;
+           dp < pitch - 3 && sp < linelen - 6 && sp < bytesread - 6;
+           sp += 7, dp += 4)
+        unpack7bytesto4x16(buf + sp, dest + dp);
+    }
+    else
+      for (unsigned int sp = 0, dp = 0;
+           dp < pitch - 15 && sp < linelen - 27 && sp < bytesread - 27;
+           sp += 28, dp += 16)
+        unpack28bytesto16x16ns(buf + sp, dest + dp);
+  }
+  free(buf);
+}
+void LibRaw::nikon_load_padded_packed_raw() // 12 bit per pixel, padded to 16
+                                            // bytes
+{
+  // libraw_internal_data.unpacker_data.load_flags -> row byte count
+  if (libraw_internal_data.unpacker_data.load_flags < 2000 ||
+      libraw_internal_data.unpacker_data.load_flags > 64000)
+    return;
+  unsigned char *buf =
+      (unsigned char *)malloc(libraw_internal_data.unpacker_data.load_flags);
+  for (int row = 0; row < S.raw_height; row++)
+  {
+    checkCancel();
+    libraw_internal_data.internal_data.input->read(
+        buf, libraw_internal_data.unpacker_data.load_flags, 1);
+    for (int icol = 0; icol < S.raw_width / 2; icol++)
+    {
+      imgdata.rawdata.raw_image[(row)*S.raw_width + (icol * 2)] =
+          ((buf[icol * 3 + 1] & 0xf) << 8) | buf[icol * 3];
+      imgdata.rawdata.raw_image[(row)*S.raw_width + (icol * 2 + 1)] =
+          buf[icol * 3 + 2] << 4 | ((buf[icol * 3 + 1] & 0xf0) >> 4);
+    }
+  }
+  free(buf);
+}
+
+void LibRaw::nikon_load_striped_packed_raw()
+{
+  int vbits = 0, bwide, rbits, bite, row, col, i;
+
+  UINT64 bitbuf = 0;
+  unsigned load_flags = 24; // libraw_internal_data.unpacker_data.load_flags;
+  unsigned tiff_bps = libraw_internal_data.unpacker_data.tiff_bps;
+
+  struct tiff_ifd_t *ifd = &tiff_ifd[0];
+  while (ifd < &tiff_ifd[libraw_internal_data.identify_data.tiff_nifds] &&
+         ifd->offset != libraw_internal_data.unpacker_data.data_offset)
+    ++ifd;
+  if (ifd == &tiff_ifd[libraw_internal_data.identify_data.tiff_nifds])
+    throw LIBRAW_EXCEPTION_DECODE_RAW;
+
+  if (!ifd->rows_per_strip || !ifd->strip_offsets_count)
+    return; // not unpacked
+  int stripcnt = 0;
+
+  bwide = S.raw_width * tiff_bps / 8;
+  bwide += bwide & load_flags >> 7;
+  rbits = bwide * 8 - S.raw_width * tiff_bps;
+  if (load_flags & 1)
+    bwide = bwide * 16 / 15;
+  bite = 8 + (load_flags & 24);
+  for (row = 0; row < S.raw_height; row++)
+  {
+    checkCancel();
+    if (!(row % ifd->rows_per_strip))
+    {
+      if (stripcnt >= ifd->strip_offsets_count)
+        return; // run out of data
+      libraw_internal_data.internal_data.input->seek(
+          ifd->strip_offsets[stripcnt], SEEK_SET);
+      stripcnt++;
+    }
+    for (col = 0; col < S.raw_width; col++)
+    {
+      for (vbits -= tiff_bps; vbits < 0; vbits += bite)
+      {
+        bitbuf <<= bite;
+        for (i = 0; i < bite; i += 8)
+          bitbuf |=
+              (unsigned)(libraw_internal_data.internal_data.input->get_char()
+                         << i);
+      }
+      imgdata.rawdata.raw_image[(row)*S.raw_width + (col)] =
+          bitbuf << (64 - tiff_bps - vbits) >> (64 - tiff_bps);
+    }
+    vbits -= rbits;
+  }
+}
+
+struct pana_cs6_page_decoder
+{
+  unsigned int pixelbuffer[14], lastoffset, maxoffset;
+  unsigned char current, *buffer;
+  pana_cs6_page_decoder(unsigned char *_buffer, unsigned int bsize)
+      : lastoffset(0), maxoffset(bsize), current(0), buffer(_buffer)
+  {
+  }
+  void read_page(); // will throw IO error if not enough space in buffer
+  unsigned int nextpixel() { return current < 14 ? pixelbuffer[current++] : 0; }
+};
+
+void pana_cs6_page_decoder::read_page()
+{
+  if (!buffer || (maxoffset - lastoffset < 16))
+    throw LIBRAW_EXCEPTION_IO_EOF;
+#define wbuffer(i) ((unsigned short)buffer[lastoffset + 15 - i])
+  pixelbuffer[0] = (wbuffer(0) << 6) | (wbuffer(1) >> 2); // 14 bit
+  pixelbuffer[1] =
+      (((wbuffer(1) & 0x3) << 12) | (wbuffer(2) << 4) | (wbuffer(3) >> 4)) &
+      0x3fff;
+  pixelbuffer[2] = (wbuffer(3) >> 2) & 0x3;
+  pixelbuffer[3] = ((wbuffer(3) & 0x3) << 8) | wbuffer(4);
+  pixelbuffer[4] = (wbuffer(5) << 2) | (wbuffer(6) >> 6);
+  pixelbuffer[5] = ((wbuffer(6) & 0x3f) << 4) | (wbuffer(7) >> 4);
+  pixelbuffer[6] = (wbuffer(7) >> 2) & 0x3;
+  pixelbuffer[7] = ((wbuffer(7) & 0x3) << 8) | wbuffer(8);
+  pixelbuffer[8] = ((wbuffer(9) << 2) & 0x3fc) | (wbuffer(10) >> 6);
+  pixelbuffer[9] = ((wbuffer(10) << 4) | (wbuffer(11) >> 4)) & 0x3ff;
+  pixelbuffer[10] = (wbuffer(11) >> 2) & 0x3;
+  pixelbuffer[11] = ((wbuffer(11) & 0x3) << 8) | wbuffer(12);
+  pixelbuffer[12] = (((wbuffer(13) << 2) & 0x3fc) | wbuffer(14) >> 6) & 0x3ff;
+  pixelbuffer[13] = ((wbuffer(14) << 4) | (wbuffer(15) >> 4)) & 0x3ff;
+#undef wbuffer
+  current = 0;
+  lastoffset += 16;
+}
+
+void LibRaw::panasonicC6_load_raw()
+{
+  const int rowstep = 16;
+  const int blocksperrow = imgdata.sizes.raw_width / 11;
+  const int rowbytes = blocksperrow * 16;
+  unsigned char *iobuf = (unsigned char *)malloc(rowbytes * rowstep);
+  merror(iobuf, "panasonicC6_load_raw()");
+
+  for (int row = 0; row < imgdata.sizes.raw_height - rowstep + 1;
+       row += rowstep)
+  {
+    int rowstoread = MIN(rowstep, imgdata.sizes.raw_height - row);
+    if (libraw_internal_data.internal_data.input->read(
+            iobuf, rowbytes, rowstoread) != rowstoread)
+      throw LIBRAW_EXCEPTION_IO_EOF;
+    pana_cs6_page_decoder page(iobuf, rowbytes * rowstoread);
+    for (int crow = 0, col = 0; crow < rowstoread; crow++, col = 0)
+    {
+      unsigned short *rowptr =
+          &imgdata.rawdata
+               .raw_image[(row + crow) * imgdata.sizes.raw_pitch / 2];
+      for (int rblock = 0; rblock < blocksperrow; rblock++)
+      {
+        page.read_page();
+        unsigned oddeven[2] = {0, 0}, nonzero[2] = {0, 0};
+        unsigned pmul = 0, pixel_base = 0;
+        for (int pix = 0; pix < 11; pix++)
+        {
+          if (pix % 3 == 2)
+          {
+            unsigned base = page.nextpixel();
+            if (base > 3)
+              throw LIBRAW_EXCEPTION_IO_CORRUPT; // not possible b/c of 2-bit
+                                                 // field, but....
+            if (base == 3)
+              base = 4;
+            pixel_base = 0x200 << base;
+            pmul = 1 << base;
+          }
+          unsigned epixel = page.nextpixel();
+          if (oddeven[pix % 2])
+          {
+            epixel *= pmul;
+            if (pixel_base < 0x2000 && nonzero[pix % 2] > pixel_base)
+              epixel += nonzero[pix % 2] - pixel_base;
+            nonzero[pix % 2] = epixel;
+          }
+          else
+          {
+            oddeven[pix % 2] = epixel;
+            if (epixel)
+              nonzero[pix % 2] = epixel;
+            else
+              epixel = nonzero[pix % 2];
+          }
+          unsigned spix = epixel - 0xf;
+          if (spix <= 0xffff)
+            rowptr[col++] = spix & 0xffff;
+          else
+          {
+            epixel = (((signed int)(epixel + 0x7ffffff1)) >> 0x1f);
+            rowptr[col++] = epixel & 0x3fff;
+          }
+        }
+      }
+    }
+  }
+  free(iobuf);
+}
+
+void LibRaw::panasonicC7_load_raw()
+{
+  const int rowstep = 16;
+  int pixperblock = libraw_internal_data.unpacker_data.pana_bpp == 14 ? 9 : 10;
+  int rowbytes = imgdata.sizes.raw_width / pixperblock * 16;
+  unsigned char *iobuf = (unsigned char *)malloc(rowbytes * rowstep);
+  merror(iobuf, "panasonicC7_load_raw()");
+  for (int row = 0; row < imgdata.sizes.raw_height - rowstep + 1;
+       row += rowstep)
+  {
+    int rowstoread = MIN(rowstep, imgdata.sizes.raw_height - row);
+    if (libraw_internal_data.internal_data.input->read(
+            iobuf, rowbytes, rowstoread) != rowstoread)
+      throw LIBRAW_EXCEPTION_IO_EOF;
+    unsigned char *bytes = iobuf;
+    for (int crow = 0; crow < rowstoread; crow++)
+    {
+      unsigned short *rowptr =
+          &imgdata.rawdata
+               .raw_image[(row + crow) * imgdata.sizes.raw_pitch / 2];
+      for (int col = 0; col < imgdata.sizes.raw_width - pixperblock + 1;
+           col += pixperblock, bytes += 16)
+      {
+        if (libraw_internal_data.unpacker_data.pana_bpp == 14)
+        {
+          rowptr[col] = bytes[0] + ((bytes[1] & 0x3F) << 8);
+          rowptr[col + 1] =
+              (bytes[1] >> 6) + 4 * (bytes[2]) + ((bytes[3] & 0xF) << 10);
+          rowptr[col + 2] =
+              (bytes[3] >> 4) + 16 * (bytes[4]) + ((bytes[5] & 3) << 12);
+          rowptr[col + 3] = ((bytes[5] & 0xFC) >> 2) + (bytes[6] << 6);
+          rowptr[col + 4] = bytes[7] + ((bytes[8] & 0x3F) << 8);
+          rowptr[col + 5] =
+              (bytes[8] >> 6) + 4 * bytes[9] + ((bytes[10] & 0xF) << 10);
+          rowptr[col + 6] =
+              (bytes[10] >> 4) + 16 * bytes[11] + ((bytes[12] & 3) << 12);
+          rowptr[col + 7] = ((bytes[12] & 0xFC) >> 2) + (bytes[13] << 6);
+          rowptr[col + 8] = bytes[14] + ((bytes[15] & 0x3F) << 8);
+        }
+        else if (libraw_internal_data.unpacker_data.pana_bpp ==
+                 12) // have not seen in the wild yet
+        {
+          rowptr[col] = ((bytes[1] & 0xF) << 8) + bytes[0];
+          rowptr[col + 1] = 16 * bytes[2] + (bytes[1] >> 4);
+          rowptr[col + 2] = ((bytes[4] & 0xF) << 8) + bytes[3];
+          rowptr[col + 3] = 16 * bytes[5] + (bytes[4] >> 4);
+          rowptr[col + 4] = ((bytes[7] & 0xF) << 8) + bytes[6];
+          rowptr[col + 5] = 16 * bytes[8] + (bytes[7] >> 4);
+          rowptr[col + 6] = ((bytes[10] & 0xF) << 8) + bytes[9];
+          rowptr[col + 7] = 16 * bytes[11] + (bytes[10] >> 4);
+          rowptr[col + 8] = ((bytes[13] & 0xF) << 8) + bytes[12];
+          rowptr[col + 9] = 16 * bytes[14] + (bytes[13] >> 4);
+        }
+      }
+    }
+  }
+  free(iobuf);
+}
+
+void LibRaw::unpacked_load_raw_fuji_f700s20()
+{
+  int base_offset = 0;
+  int row_size = imgdata.sizes.raw_width * 2; // in bytes
+  if (imgdata.idata.raw_count == 2 && imgdata.params.shot_select)
+  {
+    libraw_internal_data.internal_data.input->seek(-row_size, SEEK_CUR);
+    base_offset = row_size; // in bytes
+  }
+  unsigned char *buffer = (unsigned char *)malloc(row_size * 2);
+  for (int row = 0; row < imgdata.sizes.raw_height; row++)
+  {
+    read_shorts((ushort *)buffer, imgdata.sizes.raw_width * 2);
+    memmove(&imgdata.rawdata.raw_image[row * imgdata.sizes.raw_pitch / 2],
+            buffer + base_offset, row_size);
+  }
+  free(buffer);
+}
+
+void LibRaw::nikon_load_sraw()
+{
+  // We're already seeked to data!
+  unsigned char *rd =
+      (unsigned char *)malloc(3 * (imgdata.sizes.raw_width + 2));
+  if (!rd)
+    throw LIBRAW_EXCEPTION_ALLOC;
+  try
+  {
+    int row, col;
+    for (row = 0; row < imgdata.sizes.raw_height; row++)
+    {
+      checkCancel();
+      libraw_internal_data.internal_data.input->read(rd, 3,
+                                                     imgdata.sizes.raw_width);
+      for (col = 0; col < imgdata.sizes.raw_width - 1; col += 2)
+      {
+        int bi = col * 3;
+        ushort bits1 = (rd[bi + 1] & 0xf) << 8 | rd[bi];            // 3,0,1
+        ushort bits2 = rd[bi + 2] << 4 | ((rd[bi + 1] >> 4) & 0xf); // 452
+        ushort bits3 = ((rd[bi + 4] & 0xf) << 8) | rd[bi + 3];      // 967
+        ushort bits4 = rd[bi + 5] << 4 | ((rd[bi + 4] >> 4) & 0xf); // ab8
+        imgdata.image[row * imgdata.sizes.raw_width + col][0] = bits1;
+        imgdata.image[row * imgdata.sizes.raw_width + col][1] = bits3;
+        imgdata.image[row * imgdata.sizes.raw_width + col][2] = bits4;
+        imgdata.image[row * imgdata.sizes.raw_width + col + 1][0] = bits2;
+        imgdata.image[row * imgdata.sizes.raw_width + col + 1][1] = 2048;
+        imgdata.image[row * imgdata.sizes.raw_width + col + 1][2] = 2048;
+      }
+    }
+  }
+  catch (...)
+  {
+    free(rd);
+    throw;
+  }
+  free(rd);
+  C.maximum = 0xfff; // 12 bit?
+  if (imgdata.params.raw_processing_options &
+      LIBRAW_PROCESSING_SRAW_NO_INTERPOLATE)
+  {
+    return; // no CbCr interpolation
+  }
+  // Interpolate CC channels
+  int row, col;
+  for (row = 0; row < imgdata.sizes.raw_height; row++)
+  {
+    checkCancel(); // will throw out
+    for (col = 0; col < imgdata.sizes.raw_width; col += 2)
+    {
+      int col2 = col < imgdata.sizes.raw_width - 2 ? col + 2 : col;
+      imgdata.image[row * imgdata.sizes.raw_width + col + 1][1] =
+          (unsigned short)(int(imgdata.image[row * imgdata.sizes.raw_width +
+                                             col][1] +
+                               imgdata.image[row * imgdata.sizes.raw_width +
+                                             col2][1]) /
+                           2);
+      imgdata.image[row * imgdata.sizes.raw_width + col + 1][2] =
+          (unsigned short)(int(imgdata.image[row * imgdata.sizes.raw_width +
+                                             col][2] +
+                               imgdata.image[row * imgdata.sizes.raw_width +
+                                             col2][2]) /
+                           2);
+    }
+  }
+  if (imgdata.params.raw_processing_options & LIBRAW_PROCESSING_SRAW_NO_RGB)
+    return;
+
+  for (row = 0; row < imgdata.sizes.raw_height; row++)
+  {
+    checkCancel(); // will throw out
+    for (col = 0; col < imgdata.sizes.raw_width; col++)
+    {
+      float Y =
+          float(imgdata.image[row * imgdata.sizes.raw_width + col][0]) / 2549.f;
+      float Ch2 =
+          float(imgdata.image[row * imgdata.sizes.raw_width + col][1] - 1280) /
+          1536.f;
+      float Ch3 =
+          float(imgdata.image[row * imgdata.sizes.raw_width + col][2] - 1280) /
+          1536.f;
+      if (Y > 1.f)
+        Y = 1.f;
+      if (Y > 0.803f)
+        Ch2 = Ch3 = 0.5f;
+      float r = Y + 1.40200f * (Ch3 - 0.5f);
+      if (r < 0.f)
+        r = 0.f;
+      if (r > 1.f)
+        r = 1.f;
+      float g = Y - 0.34414f * (Ch2 - 0.5f) - 0.71414 * (Ch3 - 0.5f);
+      if (g > 1.f)
+        g = 1.f;
+      if (g < 0.f)
+        g = 0.f;
+      float b = Y + 1.77200 * (Ch2 - 0.5f);
+      if (b > 1.f)
+        b = 1.f;
+      if (b < 0.f)
+        b = 0.f;
+      imgdata.image[row * imgdata.sizes.raw_width + col][0] =
+          imgdata.color.curve[int(r * 3072.f)];
+      imgdata.image[row * imgdata.sizes.raw_width + col][1] =
+          imgdata.color.curve[int(g * 3072.f)];
+      imgdata.image[row * imgdata.sizes.raw_width + col][2] =
+          imgdata.color.curve[int(b * 3072.f)];
+    }
+  }
+  C.maximum = 16383;
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/decoders/decoders_libraw_dcrdefs.cpp libkdcraw/libkdcraw/libraw/src/decoders/decoders_libraw_dcrdefs.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/decoders/decoders_libraw_dcrdefs.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/decoders/decoders_libraw_dcrdefs.cpp	2022-11-07 07:46:31.730795008 +0300
@@ -0,0 +1,285 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+void LibRaw::nikon_coolscan_load_raw()
+{
+  if (!image)
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+
+  int bypp = tiff_bps <= 8 ? 1 : 2;
+  int bufsize = width * 3 * bypp;
+  unsigned char *buf = (unsigned char *)malloc(bufsize);
+  unsigned short *ubuf = (unsigned short *)buf;
+
+  if (tiff_bps <= 8)
+    gamma_curve(1.0 / imgdata.params.coolscan_nef_gamma, 0., 1, 255);
+  else
+    gamma_curve(1.0 / imgdata.params.coolscan_nef_gamma, 0., 1, 65535);
+  fseek(ifp, data_offset, SEEK_SET);
+  for (int row = 0; row < raw_height; row++)
+  {
+      if(tiff_bps <=8)
+        fread(buf, 1, bufsize, ifp);
+      else
+          read_shorts(ubuf,width*3);
+    unsigned short(*ip)[4] = (unsigned short(*)[4])image + row * width;
+    if (is_NikonTransfer == 2)
+    { // it is also (tiff_bps == 8)
+      for (int col = 0; col < width; col++)
+      {
+        ip[col][0] = ((float)curve[buf[col * 3]]) / 255.0f;
+        ip[col][1] = ((float)curve[buf[col * 3 + 1]]) / 255.0f;
+        ip[col][2] = ((float)curve[buf[col * 3 + 2]]) / 255.0f;
+        ip[col][3] = 0;
+      }
+    }
+    else if (tiff_bps <= 8)
+    {
+      for (int col = 0; col < width; col++)
+      {
+        ip[col][0] = curve[buf[col * 3]];
+        ip[col][1] = curve[buf[col * 3 + 1]];
+        ip[col][2] = curve[buf[col * 3 + 2]];
+        ip[col][3] = 0;
+      }
+    }
+    else
+    {
+      for (int col = 0; col < width; col++)
+      {
+        ip[col][0] = curve[ubuf[col * 3]];
+        ip[col][1] = curve[ubuf[col * 3 + 1]];
+        ip[col][2] = curve[ubuf[col * 3 + 2]];
+        ip[col][3] = 0;
+      }
+    }
+  }
+  free(buf);
+}
+
+void LibRaw::broadcom_load_raw()
+{
+
+  uchar *data, *dp;
+  int rev, row, col, c;
+  ushort _raw_stride = (ushort)load_flags;
+  rev = 3 * (order == 0x4949);
+  data = (uchar *)malloc(raw_stride * 2);
+  merror(data, "broadcom_load_raw()");
+
+  for (row = 0; row < raw_height; row++)
+  {
+    if (fread(data + _raw_stride, 1, _raw_stride, ifp) < _raw_stride)
+      derror();
+    FORC(_raw_stride) data[c] = data[_raw_stride + (c ^ rev)];
+    for (dp = data, col = 0; col < raw_width; dp += 5, col += 4)
+      FORC4 RAW(row, col + c) = (dp[c] << 2) | (dp[4] >> (c << 1) & 3);
+  }
+  free(data);
+}
+
+void LibRaw::android_tight_load_raw()
+{
+  uchar *data, *dp;
+  int bwide, row, col, c;
+
+  bwide = -(-5 * raw_width >> 5) << 3;
+  data = (uchar *)malloc(bwide);
+  merror(data, "android_tight_load_raw()");
+  for (row = 0; row < raw_height; row++)
+  {
+    if (fread(data, 1, bwide, ifp) < bwide)
+      derror();
+    for (dp = data, col = 0; col < raw_width; dp += 5, col += 4)
+      FORC4 RAW(row, col + c) = (dp[c] << 2) | (dp[4] >> (c << 1) & 3);
+  }
+  free(data);
+}
+
+void LibRaw::android_loose_load_raw()
+{
+  uchar *data, *dp;
+  int bwide, row, col, c;
+  UINT64 bitbuf = 0;
+
+  bwide = (raw_width + 5) / 6 << 3;
+  data = (uchar *)malloc(bwide);
+  merror(data, "android_loose_load_raw()");
+  for (row = 0; row < raw_height; row++)
+  {
+    if (fread(data, 1, bwide, ifp) < bwide)
+      derror();
+    for (dp = data, col = 0; col < raw_width; dp += 8, col += 6)
+    {
+      FORC(8) bitbuf = (bitbuf << 8) | dp[c ^ 7];
+      FORC(6) RAW(row, col + c) = (bitbuf >> c * 10) & 0x3ff;
+    }
+  }
+  free(data);
+}
+
+void LibRaw::unpacked_load_raw_reversed()
+{
+  int row, col, bits = 0;
+  while (1 << ++bits < (int)maximum)
+    ;
+  for (row = raw_height - 1; row >= 0; row--)
+  {
+    checkCancel();
+    read_shorts(&raw_image[row * raw_width], raw_width);
+    for (col = 0; col < raw_width; col++)
+      if ((RAW(row, col) >>= load_flags) >> bits &&
+          (unsigned)(row - top_margin) < height &&
+          (unsigned)(col - left_margin) < width)
+        derror();
+  }
+}
+
+#ifdef USE_6BY9RPI
+
+void LibRaw::rpi_load_raw8()
+{
+	uchar  *data, *dp;
+	int rev, dwide, row, col, c;
+	double sum[] = { 0,0 };
+	rev = 3 * (order == 0x4949);
+	if (raw_stride == 0)
+		dwide = raw_width;
+	else
+		dwide = raw_stride;
+	data = (uchar *)malloc(dwide * 2);
+	merror(data, "rpi_load_raw8()");
+	for (row = 0; row < raw_height; row++) {
+		if (fread(data + dwide, 1, dwide, ifp) < dwide) derror();
+		FORC(dwide) data[c] = data[dwide + (c ^ rev)];
+		for (dp = data, col = 0; col < raw_width; dp++, col++)
+			RAW(row, col + c) = dp[c];
+	}
+	free(data);
+	maximum = 0xff;
+	if (!strcmp(make, "OmniVision") ||
+		!strcmp(make, "Sony") ||
+		!strcmp(make, "RaspberryPi")) return;
+
+	row = raw_height / 2;
+	FORC(width - 1) {
+		sum[c & 1] += SQR(RAW(row, c) - RAW(row + 1, c + 1));
+		sum[~c & 1] += SQR(RAW(row + 1, c) - RAW(row, c + 1));
+	}
+	if (sum[1] > sum[0]) filters = 0x4b4b4b4b;
+}
+
+void LibRaw::rpi_load_raw12()
+{
+	uchar  *data, *dp;
+	int rev, dwide, row, col, c;
+	double sum[] = { 0,0 };
+	rev = 3 * (order == 0x4949);
+	if (raw_stride == 0)
+		dwide = (raw_width * 3 + 1) / 2;
+	else
+		dwide = raw_stride;
+	data = (uchar *)malloc(dwide * 2);
+	merror(data, "rpi_load_raw12()");
+	for (row = 0; row < raw_height; row++) {
+		if (fread(data + dwide, 1, dwide, ifp) < dwide) derror();
+		FORC(dwide) data[c] = data[dwide + (c ^ rev)];
+		for (dp = data, col = 0; col < raw_width; dp += 3, col += 2)
+			FORC(2) RAW(row, col + c) = (dp[c] << 4) | (dp[2] >> (c << 2) & 0xF);
+	}
+	free(data);
+	maximum = 0xfff;
+	if (!strcmp(make, "OmniVision") ||
+		!strcmp(make, "Sony") ||
+		!strcmp(make, "RaspberryPi")) return;
+
+	row = raw_height / 2;
+	FORC(width - 1) {
+		sum[c & 1] += SQR(RAW(row, c) - RAW(row + 1, c + 1));
+		sum[~c & 1] += SQR(RAW(row + 1, c) - RAW(row, c + 1));
+	}
+	if (sum[1] > sum[0]) filters = 0x4b4b4b4b;
+}
+
+void LibRaw::rpi_load_raw14()
+{
+	uchar  *data, *dp;
+	int rev, dwide, row, col, c;
+	double sum[] = { 0,0 };
+	rev = 3 * (order == 0x4949);
+	if (raw_stride == 0)
+		dwide = ((raw_width * 7) + 3) >> 2;
+	else
+		dwide = raw_stride;
+	data = (uchar *)malloc(dwide * 2);
+	merror(data, "rpi_load_raw14()");
+	for (row = 0; row < raw_height; row++) {
+		if (fread(data + dwide, 1, dwide, ifp) < dwide) derror();
+		FORC(dwide) data[c] = data[dwide + (c ^ rev)];
+		for (dp = data, col = 0; col < raw_width; dp += 7, col += 4) {
+			RAW(row, col + 0) = (dp[0] << 6) | (dp[4] >> 2);
+			RAW(row, col + 1) = (dp[1] << 6) | ((dp[4] & 0x3) << 4) | ((dp[5] & 0xf0) >> 4);
+			RAW(row, col + 2) = (dp[2] << 6) | ((dp[5] & 0xf) << 2) | ((dp[6] & 0xc0) >> 6);
+			RAW(row, col + 3) = (dp[3] << 6) | ((dp[6] & 0x3f) << 2);
+		}
+	}
+	free(data);
+	maximum = 0x3fff;
+	if (!strcmp(make, "OmniVision") ||
+		!strcmp(make, "Sony") ||
+		!strcmp(make, "RaspberryPi")) return;
+
+	row = raw_height / 2;
+	FORC(width - 1) {
+		sum[c & 1] += SQR(RAW(row, c) - RAW(row + 1, c + 1));
+		sum[~c & 1] += SQR(RAW(row + 1, c) - RAW(row, c + 1));
+	}
+	if (sum[1] > sum[0]) filters = 0x4b4b4b4b;
+}
+
+void LibRaw::rpi_load_raw16()
+{
+	uchar  *data, *dp;
+	int rev, dwide, row, col, c;
+	double sum[] = { 0,0 };
+	rev = 3 * (order == 0x4949);
+	if (raw_stride == 0)
+		dwide = (raw_width * 2);
+	else
+		dwide = raw_stride;
+	data = (uchar *)malloc(dwide * 2);
+	merror(data, "rpi_load_raw16()");
+	for (row = 0; row < raw_height; row++) {
+		if (fread(data + dwide, 1, dwide, ifp) < dwide) derror();
+		FORC(dwide) data[c] = data[dwide + (c ^ rev)];
+		for (dp = data, col = 0; col < raw_width; dp += 2, col++)
+			RAW(row, col + c) = (dp[1] << 8) | dp[0];
+	}
+	free(data);
+	maximum = 0xffff;
+	if (!strcmp(make, "OmniVision") ||
+		!strcmp(make, "Sony") ||
+		!strcmp(make, "RaspberryPi")) return;
+
+	row = raw_height / 2;
+	FORC(width - 1) {
+		sum[c & 1] += SQR(RAW(row, c) - RAW(row + 1, c + 1));
+		sum[~c & 1] += SQR(RAW(row + 1, c) - RAW(row, c + 1));
+	}
+	if (sum[1] > sum[0]) filters = 0x4b4b4b4b;
+}
+
+#endif
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/decoders/dng.cpp libkdcraw/libkdcraw/libraw/src/decoders/dng.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/decoders/dng.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/decoders/dng.cpp	2022-11-07 07:46:31.730795008 +0300
@@ -0,0 +1,271 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+void LibRaw::vc5_dng_load_raw_placeholder()
+{
+    // placeholder only, real decoding implemented in GPR SDK
+    throw LIBRAW_EXCEPTION_DECODE_RAW;
+}
+
+void LibRaw::adobe_copy_pixel(unsigned row, unsigned col, ushort **rp)
+{
+  int c;
+
+  if (tiff_samples == 2 && shot_select)
+    (*rp)++;
+  if (raw_image)
+  {
+    if (row < raw_height && col < raw_width)
+      RAW(row, col) = curve[**rp];
+    *rp += tiff_samples;
+  }
+  else
+  {
+    if (row < raw_height && col < raw_width)
+      FORC(int(tiff_samples))
+    image[row * raw_width + col][c] = curve[(*rp)[c]];
+    *rp += tiff_samples;
+  }
+  if (tiff_samples == 2 && shot_select)
+    (*rp)--;
+}
+void LibRaw::lossless_dng_load_raw()
+{
+  unsigned save, trow = 0, tcol = 0, jwide, jrow, jcol, row, col, i, j;
+  struct jhead jh;
+  ushort *rp;
+
+  int ss = shot_select;
+  shot_select = libraw_internal_data.unpacker_data.dng_frames[LIM(ss,0,(LIBRAW_IFD_MAXCOUNT*2-1))] & 0xff;
+
+  while (trow < raw_height)
+  {
+    checkCancel();
+    save = ftell(ifp);
+    if (tile_length < INT_MAX)
+      fseek(ifp, get4(), SEEK_SET);
+    if (!ljpeg_start(&jh, 0))
+      break;
+    jwide = jh.wide;
+    if (filters)
+      jwide *= jh.clrs;
+
+    if(filters && (tiff_samples == 2)) // Fuji Super CCD
+        jwide /= 2;
+    try
+    {
+      switch (jh.algo)
+      {
+      case 0xc1:
+        jh.vpred[0] = 16384;
+        getbits(-1);
+        for (jrow = 0; jrow + 7 < (unsigned)jh.high; jrow += 8)
+        {
+          checkCancel();
+          for (jcol = 0; jcol + 7 < (unsigned)jh.wide; jcol += 8)
+          {
+            ljpeg_idct(&jh);
+            rp = jh.idct;
+            row = trow + jcol / tile_width + jrow * 2;
+            col = tcol + jcol % tile_width;
+            for (i = 0; i < 16; i += 2)
+              for (j = 0; j < 8; j++)
+                adobe_copy_pixel(row + i, col + j, &rp);
+          }
+        }
+        break;
+      case 0xc3:
+        for (row = col = jrow = 0; jrow < (unsigned)jh.high; jrow++)
+        {
+          checkCancel();
+          rp = ljpeg_row(jrow, &jh);
+          if (tiff_samples == 1 && jh.clrs > 1 && jh.clrs * jwide == raw_width)
+            for (jcol = 0; jcol < jwide * jh.clrs; jcol++)
+            {
+              adobe_copy_pixel(trow + row, tcol + col, &rp);
+              if (++col >= tile_width || col >= raw_width)
+                row += 1 + (col = 0);
+            }
+          else
+            for (jcol = 0; jcol < jwide; jcol++)
+            {
+              adobe_copy_pixel(trow + row, tcol + col, &rp);
+              if (++col >= tile_width || col >= raw_width)
+                row += 1 + (col = 0);
+            }
+        }
+      }
+    }
+    catch (...)
+    {
+      ljpeg_end(&jh);
+      shot_select = ss;
+      throw;
+    }
+    fseek(ifp, save + 4, SEEK_SET);
+    if ((tcol += tile_width) >= raw_width)
+      trow += tile_length + (tcol = 0);
+    ljpeg_end(&jh);
+  }
+  shot_select = ss;
+}
+
+void LibRaw::packed_dng_load_raw()
+{
+  ushort *pixel, *rp;
+  unsigned row, col;
+
+  int ss = shot_select;
+  shot_select = libraw_internal_data.unpacker_data.dng_frames[LIM(ss,0,(LIBRAW_IFD_MAXCOUNT*2-1))] & 0xff;
+
+  pixel = (ushort *)calloc(raw_width, tiff_samples * sizeof *pixel);
+  merror(pixel, "packed_dng_load_raw()");
+  try
+  {
+    for (row = 0; row < raw_height; row++)
+    {
+      checkCancel();
+      if (tiff_bps == 16)
+        read_shorts(pixel, raw_width * tiff_samples);
+      else
+      {
+        getbits(-1);
+        for (col = 0; col < raw_width * tiff_samples; col++)
+          pixel[col] = getbits(tiff_bps);
+      }
+      for (rp = pixel, col = 0; col < raw_width; col++)
+        adobe_copy_pixel(row, col, &rp);
+    }
+  }
+  catch (...)
+  {
+    free(pixel);
+    shot_select = ss;
+    throw;
+  }
+  free(pixel);
+  shot_select = ss;
+}
+#ifdef NO_JPEG
+void LibRaw::lossy_dng_load_raw() {}
+#else
+
+static void jpegErrorExit_d(j_common_ptr cinfo)
+{
+  throw LIBRAW_EXCEPTION_DECODE_JPEG;
+}
+
+void LibRaw::lossy_dng_load_raw()
+{
+  if (!image)
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+  struct jpeg_decompress_struct cinfo;
+  JSAMPARRAY buf;
+  JSAMPLE(*pixel)[3];
+  unsigned sorder = order, ntags, opcode, deg, i, j, c;
+  unsigned save = data_offset - 4, trow = 0, tcol = 0, row, col;
+  ushort cur[3][256];
+  double coeff[9], tot;
+
+  if (meta_offset)
+  {
+    fseek(ifp, meta_offset, SEEK_SET);
+    order = 0x4d4d;
+    ntags = get4();
+    while (ntags--)
+    {
+      opcode = get4();
+      get4();
+      get4();
+      if (opcode != 8)
+      {
+        fseek(ifp, get4(), SEEK_CUR);
+        continue;
+      }
+      fseek(ifp, 20, SEEK_CUR);
+      if ((c = get4()) > 2)
+        break;
+      fseek(ifp, 12, SEEK_CUR);
+      if ((deg = get4()) > 8)
+        break;
+      for (i = 0; i <= deg && i < 9; i++)
+        coeff[i] = getreal(LIBRAW_EXIFTAG_TYPE_DOUBLE);
+      for (i = 0; i < 256; i++)
+      {
+        for (tot = j = 0; j <= deg; j++)
+          tot += coeff[j] * pow(i / 255.0, (int)j);
+        cur[c][i] = tot * 0xffff;
+      }
+    }
+    order = sorder;
+  }
+  else
+  {
+    gamma_curve(1 / 2.4, 12.92, 1, 255);
+    FORC3 memcpy(cur[c], curve, sizeof cur[0]);
+  }
+
+  struct jpeg_error_mgr pub;
+  cinfo.err = jpeg_std_error(&pub);
+  pub.error_exit = jpegErrorExit_d;
+
+  jpeg_create_decompress(&cinfo);
+
+  while (trow < raw_height)
+  {
+    fseek(ifp, save += 4, SEEK_SET);
+    if (tile_length < INT_MAX)
+      fseek(ifp, get4(), SEEK_SET);
+    if (libraw_internal_data.internal_data.input->jpeg_src(&cinfo) == -1)
+    {
+      jpeg_destroy_decompress(&cinfo);
+      throw LIBRAW_EXCEPTION_DECODE_JPEG;
+    }
+    jpeg_read_header(&cinfo, TRUE);
+    jpeg_start_decompress(&cinfo);
+    buf = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE,
+                                     cinfo.output_width * 3, 1);
+    try
+    {
+      while (cinfo.output_scanline < cinfo.output_height &&
+             (row = trow + cinfo.output_scanline) < height)
+      {
+        checkCancel();
+        jpeg_read_scanlines(&cinfo, buf, 1);
+        pixel = (JSAMPLE(*)[3])buf[0];
+        for (col = 0; col < cinfo.output_width && tcol + col < width; col++)
+        {
+          FORC3 image[row * width + tcol + col][c] = cur[c][pixel[col][c]];
+        }
+      }
+    }
+    catch (...)
+    {
+      jpeg_destroy_decompress(&cinfo);
+      throw;
+    }
+    jpeg_abort_decompress(&cinfo);
+    if ((tcol += tile_width) >= raw_width)
+      trow += tile_length + (tcol = 0);
+  }
+  jpeg_destroy_decompress(&cinfo);
+  maximum = 0xffff;
+}
+#endif
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/decoders/fp_dng.cpp libkdcraw/libkdcraw/libraw/src/decoders/fp_dng.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/decoders/fp_dng.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/decoders/fp_dng.cpp	2022-11-07 07:46:31.730795008 +0300
@@ -0,0 +1,538 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/libraw_cxx_defs.h"
+
+#ifdef USE_ZLIB
+inline unsigned int __DNG_HalfToFloat(ushort halfValue)
+{
+  int sign = (halfValue >> 15) & 0x00000001;
+  int exponent = (halfValue >> 10) & 0x0000001f;
+  int mantissa = halfValue & 0x000003ff;
+  if (exponent == 0)
+  {
+    if (mantissa == 0)
+    {
+      return (unsigned int)(sign << 31);
+    }
+    else
+    {
+      while (!(mantissa & 0x00000400))
+      {
+        mantissa <<= 1;
+        exponent -= 1;
+      }
+      exponent += 1;
+      mantissa &= ~0x00000400;
+    }
+  }
+  else if (exponent == 31)
+  {
+    if (mantissa == 0)
+    {
+      return (unsigned int)((sign << 31) | ((0x1eL + 127 - 15) << 23) |
+                            (0x3ffL << 13));
+    }
+    else
+    {
+      return 0;
+    }
+  }
+  exponent += (127 - 15);
+  mantissa <<= 13;
+  return (unsigned int)((sign << 31) | (exponent << 23) | mantissa);
+}
+
+inline unsigned int __DNG_FP24ToFloat(const unsigned char *input)
+{
+  int sign = (input[0] >> 7) & 0x01;
+  int exponent = (input[0]) & 0x7F;
+  int mantissa = (((int)input[1]) << 8) | input[2];
+  if (exponent == 0)
+  {
+    if (mantissa == 0)
+    {
+      return (unsigned int)(sign << 31);
+    }
+    else
+    {
+      while (!(mantissa & 0x00010000))
+      {
+        mantissa <<= 1;
+        exponent -= 1;
+      }
+      exponent += 1;
+      mantissa &= ~0x00010000;
+    }
+  }
+  else if (exponent == 127)
+  {
+    if (mantissa == 0)
+    {
+      return (unsigned int)((sign << 31) | ((0x7eL + 128 - 64) << 23) |
+                            (0xffffL << 7));
+    }
+    else
+    {
+      // Nan -- Just set to zero.
+      return 0;
+    }
+  }
+  exponent += (128 - 64);
+  mantissa <<= 7;
+  return (uint32_t)((sign << 31) | (exponent << 23) | mantissa);
+}
+
+inline void DecodeDeltaBytes(unsigned char *bytePtr, int cols, int channels)
+{
+  if (channels == 1)
+  {
+    unsigned char b0 = bytePtr[0];
+    bytePtr += 1;
+    for (uint32_t col = 1; col < cols; ++col)
+    {
+      b0 += bytePtr[0];
+      bytePtr[0] = b0;
+      bytePtr += 1;
+    }
+  }
+  else if (channels == 3)
+  {
+    unsigned char b0 = bytePtr[0];
+    unsigned char b1 = bytePtr[1];
+    unsigned char b2 = bytePtr[2];
+    bytePtr += 3;
+    for (int col = 1; col < cols; ++col)
+    {
+      b0 += bytePtr[0];
+      b1 += bytePtr[1];
+      b2 += bytePtr[2];
+      bytePtr[0] = b0;
+      bytePtr[1] = b1;
+      bytePtr[2] = b2;
+      bytePtr += 3;
+    }
+  }
+  else if (channels == 4)
+  {
+    unsigned char b0 = bytePtr[0];
+    unsigned char b1 = bytePtr[1];
+    unsigned char b2 = bytePtr[2];
+    unsigned char b3 = bytePtr[3];
+    bytePtr += 4;
+    for (uint32_t col = 1; col < cols; ++col)
+    {
+      b0 += bytePtr[0];
+      b1 += bytePtr[1];
+      b2 += bytePtr[2];
+      b3 += bytePtr[3];
+      bytePtr[0] = b0;
+      bytePtr[1] = b1;
+      bytePtr[2] = b2;
+      bytePtr[3] = b3;
+      bytePtr += 4;
+    }
+  }
+  else
+  {
+    for (int col = 1; col < cols; ++col)
+    {
+      for (int chan = 0; chan < channels; ++chan)
+      {
+        bytePtr[chan + channels] += bytePtr[chan];
+      }
+      bytePtr += channels;
+    }
+  }
+}
+
+static void DecodeFPDelta(unsigned char *input, unsigned char *output, int cols,
+                          int channels, int bytesPerSample)
+{
+  DecodeDeltaBytes(input, cols * bytesPerSample, channels);
+  int32_t rowIncrement = cols * channels;
+
+  if (bytesPerSample == 2)
+  {
+
+#if LibRawBigEndian
+    const unsigned char *input0 = input;
+    const unsigned char *input1 = input + rowIncrement;
+#else
+    const unsigned char *input1 = input;
+    const unsigned char *input0 = input + rowIncrement;
+#endif
+    for (int col = 0; col < rowIncrement; ++col)
+    {
+      output[0] = input0[col];
+      output[1] = input1[col];
+      output += 2;
+    }
+  }
+  else if (bytesPerSample == 3)
+  {
+    const unsigned char *input0 = input;
+    const unsigned char *input1 = input + rowIncrement;
+    const unsigned char *input2 = input + rowIncrement * 2;
+    for (int col = 0; col < rowIncrement; ++col)
+    {
+      output[0] = input0[col];
+      output[1] = input1[col];
+      output[2] = input2[col];
+      output += 3;
+    }
+  }
+  else
+  {
+#if LibRawBigEndian
+    const unsigned char *input0 = input;
+    const unsigned char *input1 = input + rowIncrement;
+    const unsigned char *input2 = input + rowIncrement * 2;
+    const unsigned char *input3 = input + rowIncrement * 3;
+#else
+    const unsigned char *input3 = input;
+    const unsigned char *input2 = input + rowIncrement;
+    const unsigned char *input1 = input + rowIncrement * 2;
+    const unsigned char *input0 = input + rowIncrement * 3;
+#endif
+    for (int col = 0; col < rowIncrement; ++col)
+    {
+      output[0] = input0[col];
+      output[1] = input1[col];
+      output[2] = input2[col];
+      output[3] = input3[col];
+      output += 4;
+    }
+  }
+}
+
+static float expandFloats(unsigned char *dst, int tileWidth, int bytesps)
+{
+  float max = 0.f;
+  if (bytesps == 2)
+  {
+    uint16_t *dst16 = (ushort *)dst;
+    uint32_t *dst32 = (unsigned int *)dst;
+    float *f32 = (float *)dst;
+    for (int index = tileWidth - 1; index >= 0; --index)
+    {
+      dst32[index] = __DNG_HalfToFloat(dst16[index]);
+      max = MAX(max, f32[index]);
+    }
+  }
+  else if (bytesps == 3)
+  {
+    uint8_t *dst8 = ((unsigned char *)dst) + (tileWidth - 1) * 3;
+    uint32_t *dst32 = (unsigned int *)dst;
+    float *f32 = (float *)dst;
+    for (int index = tileWidth - 1; index >= 0; --index, dst8 -= 3)
+    {
+      dst32[index] = __DNG_FP24ToFloat(dst8);
+      max = MAX(max, f32[index]);
+    }
+  }
+  else if (bytesps == 4)
+  {
+    float *f32 = (float *)dst;
+    for (int index = 0; index < tileWidth; index++)
+      max = MAX(max, f32[index]);
+  }
+  return max;
+}
+
+void LibRaw::deflate_dng_load_raw()
+{
+  int iifd = find_ifd_by_offset(libraw_internal_data.unpacker_data.data_offset);
+  if(iifd < 0 || iifd > libraw_internal_data.identify_data.tiff_nifds)
+      throw LIBRAW_EXCEPTION_DECODE_RAW;
+  struct tiff_ifd_t *ifd = &tiff_ifd[iifd];
+
+  float *float_raw_image = 0;
+  float max = 0.f;
+
+  if (ifd->samples != 1 && ifd->samples != 3 && ifd->samples != 4)
+    throw LIBRAW_EXCEPTION_DECODE_RAW; // Only float deflated supported
+
+  if (libraw_internal_data.unpacker_data.tiff_samples != ifd->samples)
+    throw LIBRAW_EXCEPTION_DECODE_RAW; // Wrong IFD
+
+  size_t tilesH = (imgdata.sizes.raw_width +
+                   libraw_internal_data.unpacker_data.tile_width - 1) /
+                  libraw_internal_data.unpacker_data.tile_width;
+  size_t tilesV = (imgdata.sizes.raw_height +
+                   libraw_internal_data.unpacker_data.tile_length - 1) /
+                  libraw_internal_data.unpacker_data.tile_length;
+  size_t tileCnt = tilesH * tilesV;
+
+  if (ifd->sample_format == 3)
+  { // Floating point data
+    float_raw_image = (float *)calloc(
+        tileCnt * libraw_internal_data.unpacker_data.tile_length *
+            libraw_internal_data.unpacker_data.tile_width * ifd->samples,
+        sizeof(float));
+    // imgdata.color.maximum = 65535;
+    // imgdata.color.black = 0;
+    // memset(imgdata.color.cblack,0,sizeof(imgdata.color.cblack));
+  }
+  else
+    throw LIBRAW_EXCEPTION_DECODE_RAW; // Only float deflated supported
+
+  int xFactor;
+  switch (ifd->predictor)
+  {
+  case 3:
+  default:
+    xFactor = 1;
+    break;
+  case 34894:
+    xFactor = 2;
+    break;
+  case 34895:
+    xFactor = 4;
+    break;
+  }
+
+  if (libraw_internal_data.unpacker_data.tile_length < INT_MAX)
+  {
+    if (tileCnt < 1 || tileCnt > 1000000)
+      throw LIBRAW_EXCEPTION_DECODE_RAW;
+
+    size_t *tOffsets = (size_t *)malloc(tileCnt * sizeof(size_t));
+    for (int t = 0; t < tileCnt; ++t)
+      tOffsets[t] = get4();
+
+    size_t *tBytes = (size_t *)malloc(tileCnt * sizeof(size_t));
+    unsigned long maxBytesInTile = 0;
+    if (tileCnt == 1)
+      tBytes[0] = maxBytesInTile = ifd->bytes;
+    else
+    {
+      libraw_internal_data.internal_data.input->seek(ifd->bytes, SEEK_SET);
+      for (size_t t = 0; t < tileCnt; ++t)
+      {
+        tBytes[t] = get4();
+        maxBytesInTile = MAX(maxBytesInTile, tBytes[t]);
+      }
+    }
+    unsigned tilePixels = libraw_internal_data.unpacker_data.tile_width *
+                          libraw_internal_data.unpacker_data.tile_length;
+    unsigned pixelSize = sizeof(float) * ifd->samples;
+    unsigned tileBytes = tilePixels * pixelSize;
+    unsigned tileRowBytes =
+        libraw_internal_data.unpacker_data.tile_width * pixelSize;
+
+    unsigned char *cBuffer = (unsigned char *)malloc(maxBytesInTile);
+    unsigned char *uBuffer = (unsigned char *)malloc(
+        tileBytes + tileRowBytes); // extra row for decoding
+
+    for (size_t y = 0, t = 0; y < imgdata.sizes.raw_height;
+         y += libraw_internal_data.unpacker_data.tile_length)
+    {
+      for (size_t x = 0; x < imgdata.sizes.raw_width;
+           x += libraw_internal_data.unpacker_data.tile_width, ++t)
+      {
+        libraw_internal_data.internal_data.input->seek(tOffsets[t], SEEK_SET);
+        libraw_internal_data.internal_data.input->read(cBuffer, 1, tBytes[t]);
+        unsigned long dstLen = tileBytes;
+        int err =
+            uncompress(uBuffer + tileRowBytes, &dstLen, cBuffer, tBytes[t]);
+        if (err != Z_OK)
+        {
+          free(tOffsets);
+          free(tBytes);
+          free(cBuffer);
+          free(uBuffer);
+          throw LIBRAW_EXCEPTION_DECODE_RAW;
+          return;
+        }
+        else
+        {
+          int bytesps = ifd->bps >> 3;
+          size_t rowsInTile =
+              y + libraw_internal_data.unpacker_data.tile_length >
+                      imgdata.sizes.raw_height
+                  ? imgdata.sizes.raw_height - y
+                  : libraw_internal_data.unpacker_data.tile_length;
+          size_t colsInTile =
+              x + libraw_internal_data.unpacker_data.tile_width >
+                      imgdata.sizes.raw_width
+                  ? imgdata.sizes.raw_width - x
+                  : libraw_internal_data.unpacker_data.tile_width;
+
+          for (size_t row = 0; row < rowsInTile;
+               ++row) // do not process full tile if not needed
+          {
+            unsigned char *dst =
+                uBuffer + row * libraw_internal_data.unpacker_data.tile_width *
+                              bytesps * ifd->samples;
+            unsigned char *src = dst + tileRowBytes;
+            DecodeFPDelta(src, dst,
+                          libraw_internal_data.unpacker_data.tile_width /
+                              xFactor,
+                          ifd->samples * xFactor, bytesps);
+            float lmax = expandFloats(
+                dst,
+                libraw_internal_data.unpacker_data.tile_width * ifd->samples,
+                bytesps);
+            max = MAX(max, lmax);
+            unsigned char *dst2 = (unsigned char *)&float_raw_image
+                [((y + row) * imgdata.sizes.raw_width + x) * ifd->samples];
+            memmove(dst2, dst, colsInTile * ifd->samples * sizeof(float));
+          }
+        }
+      }
+    }
+    free(tOffsets);
+    free(tBytes);
+    free(cBuffer);
+    free(uBuffer);
+  }
+  imgdata.color.fmaximum = max;
+
+  // Set fields according to data format
+
+  imgdata.rawdata.raw_alloc = float_raw_image;
+  if (ifd->samples == 1)
+  {
+    imgdata.rawdata.float_image = float_raw_image;
+    imgdata.rawdata.sizes.raw_pitch = imgdata.sizes.raw_pitch =
+        imgdata.sizes.raw_width * 4;
+  }
+  else if (ifd->samples == 3)
+  {
+    imgdata.rawdata.float3_image = (float(*)[3])float_raw_image;
+    imgdata.rawdata.sizes.raw_pitch = imgdata.sizes.raw_pitch =
+        imgdata.sizes.raw_width * 12;
+  }
+  else if (ifd->samples == 4)
+  {
+    imgdata.rawdata.float4_image = (float(*)[4])float_raw_image;
+    imgdata.rawdata.sizes.raw_pitch = imgdata.sizes.raw_pitch =
+        imgdata.sizes.raw_width * 16;
+  }
+
+  if (imgdata.params.raw_processing_options &
+      LIBRAW_PROCESSING_CONVERTFLOAT_TO_INT)
+    convertFloatToInt(); // with default settings
+}
+#else
+void LibRaw::deflate_dng_load_raw() { throw LIBRAW_EXCEPTION_DECODE_RAW; }
+#endif
+void LibRaw::float_dng_load_raw_placeholder()
+{
+  // placeholder only, real decoding implemented in DNG SDK
+  throw LIBRAW_EXCEPTION_DECODE_RAW;
+}
+
+int LibRaw::is_floating_point()
+{
+  struct tiff_ifd_t *ifd = &tiff_ifd[0];
+  while (ifd < &tiff_ifd[libraw_internal_data.identify_data.tiff_nifds] &&
+         ifd->offset != libraw_internal_data.unpacker_data.data_offset)
+    ++ifd;
+  if (ifd == &tiff_ifd[libraw_internal_data.identify_data.tiff_nifds])
+    return 0;
+
+  return ifd->sample_format == 3;
+}
+
+int LibRaw::have_fpdata()
+{
+  return imgdata.rawdata.float_image || imgdata.rawdata.float3_image ||
+         imgdata.rawdata.float4_image;
+}
+
+void LibRaw::convertFloatToInt(float dmin /* =4096.f */,
+                               float dmax /* =32767.f */,
+                               float dtarget /*= 16383.f */)
+{
+  int samples = 0;
+  float *data = 0;
+  if (imgdata.rawdata.float_image)
+  {
+    samples = 1;
+    data = imgdata.rawdata.float_image;
+  }
+  else if (imgdata.rawdata.float3_image)
+  {
+    samples = 3;
+    data = (float *)imgdata.rawdata.float3_image;
+  }
+  else if (imgdata.rawdata.float4_image)
+  {
+    samples = 4;
+    data = (float *)imgdata.rawdata.float4_image;
+  }
+  else
+    return;
+
+  ushort *raw_alloc = (ushort *)malloc(
+      imgdata.sizes.raw_height * imgdata.sizes.raw_width *
+      libraw_internal_data.unpacker_data.tiff_samples * sizeof(ushort));
+  float tmax = MAX(imgdata.color.maximum, 1);
+  float datamax = imgdata.color.fmaximum;
+
+  tmax = MAX(tmax, datamax);
+  tmax = MAX(tmax, 1.f);
+
+  float multip = 1.f;
+  if (tmax < dmin || tmax > dmax)
+  {
+    imgdata.rawdata.color.fnorm = imgdata.color.fnorm = multip = dtarget / tmax;
+    imgdata.rawdata.color.maximum = imgdata.color.maximum = dtarget;
+    imgdata.rawdata.color.black = imgdata.color.black =
+        (float)imgdata.color.black * multip;
+    for (int i = 0;
+         i < int(sizeof(imgdata.color.cblack)/sizeof(imgdata.color.cblack[0]));
+         i++)
+      if (i != 4 && i != 5)
+        imgdata.rawdata.color.cblack[i] = imgdata.color.cblack[i] =
+            (float)imgdata.color.cblack[i] * multip;
+  }
+  else
+    imgdata.rawdata.color.fnorm = imgdata.color.fnorm = 0.f;
+
+  for (size_t i = 0; i < imgdata.sizes.raw_height * imgdata.sizes.raw_width *
+                             libraw_internal_data.unpacker_data.tiff_samples;
+       ++i)
+  {
+    float val = MAX(data[i], 0.f);
+    raw_alloc[i] = (ushort)(val * multip);
+  }
+
+  if (samples == 1)
+  {
+    imgdata.rawdata.raw_alloc = imgdata.rawdata.raw_image = raw_alloc;
+    imgdata.rawdata.sizes.raw_pitch = imgdata.sizes.raw_pitch =
+        imgdata.sizes.raw_width * 2;
+  }
+  else if (samples == 3)
+  {
+    imgdata.rawdata.raw_alloc = imgdata.rawdata.color3_image =
+        (ushort(*)[3])raw_alloc;
+    imgdata.rawdata.sizes.raw_pitch = imgdata.sizes.raw_pitch =
+        imgdata.sizes.raw_width * 6;
+  }
+  else if (samples == 4)
+  {
+    imgdata.rawdata.raw_alloc = imgdata.rawdata.color4_image =
+        (ushort(*)[4])raw_alloc;
+    imgdata.rawdata.sizes.raw_pitch = imgdata.sizes.raw_pitch =
+        imgdata.sizes.raw_width * 8;
+  }
+  free(data); // remove old allocation
+  imgdata.rawdata.float_image = 0;
+  imgdata.rawdata.float3_image = 0;
+  imgdata.rawdata.float4_image = 0;
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/decoders/fuji_compressed.cpp libkdcraw/libkdcraw/libraw/src/decoders/fuji_compressed.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/decoders/fuji_compressed.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/decoders/fuji_compressed.cpp	2022-11-07 07:46:31.730795008 +0300
@@ -0,0 +1,1132 @@
+/* -*- C++ -*-
+ * File: libraw_fuji_compressed.cpp
+ * Copyright (C) 2016-2019 Alexey Danilchenko
+ *
+ * Adopted to LibRaw by Alex Tutubalin, lexa@lexa.ru
+ * LibRaw Fujifilm/compressed decoder
+
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/libraw_cxx_defs.h"
+
+#ifdef _abs
+#undef _abs
+#undef _min
+#undef _max
+#endif
+#define _abs(x) (((int)(x) ^ ((int)(x) >> 31)) - ((int)(x) >> 31))
+#define _min(a, b) ((a) < (b) ? (a) : (b))
+#define _max(a, b) ((a) > (b) ? (a) : (b))
+
+struct int_pair
+{
+  int value1;
+  int value2;
+};
+
+enum _xt_lines
+{
+  _R0 = 0,
+  _R1,
+  _R2,
+  _R3,
+  _R4,
+  _G0,
+  _G1,
+  _G2,
+  _G3,
+  _G4,
+  _G5,
+  _G6,
+  _G7,
+  _B0,
+  _B1,
+  _B2,
+  _B3,
+  _B4,
+  _ltotal
+};
+
+struct fuji_compressed_block
+{
+  int cur_bit;            // current bit being read (from left to right)
+  int cur_pos;            // current position in a buffer
+  INT64 cur_buf_offset;   // offset of this buffer in a file
+  unsigned max_read_size; // Amount of data to be read
+  int cur_buf_size;       // buffer size
+  uchar *cur_buf;         // currently read block
+  int fillbytes;          // Counter to add extra byte for block size N*16
+  LibRaw_abstract_datastream *input;
+  struct int_pair grad_even[3][41]; // tables of gradients
+  struct int_pair grad_odd[3][41];
+  ushort *linealloc;
+  ushort *linebuf[_ltotal];
+};
+
+void LibRaw::init_fuji_compr(struct fuji_compressed_params *info)
+{
+  int cur_val;
+  int8_t *qt;
+
+  if ((libraw_internal_data.unpacker_data.fuji_block_width % 3 &&
+       libraw_internal_data.unpacker_data.fuji_raw_type == 16) ||
+      (libraw_internal_data.unpacker_data.fuji_block_width & 1 &&
+       libraw_internal_data.unpacker_data.fuji_raw_type == 0))
+    derror();
+
+  info->q_table =
+      (int8_t *)malloc(2 << libraw_internal_data.unpacker_data.fuji_bits);
+  merror(info->q_table, "init_fuji_compr()");
+
+  if (libraw_internal_data.unpacker_data.fuji_raw_type == 16)
+    info->line_width =
+        (libraw_internal_data.unpacker_data.fuji_block_width * 2) / 3;
+  else
+    info->line_width = libraw_internal_data.unpacker_data.fuji_block_width >> 1;
+
+  info->q_point[0] = 0;
+  info->q_point[1] = 0x12;
+  info->q_point[2] = 0x43;
+  info->q_point[3] = 0x114;
+  info->q_point[4] = (1 << libraw_internal_data.unpacker_data.fuji_bits) - 1;
+  info->min_value = 0x40;
+
+  cur_val = -info->q_point[4];
+  for (qt = info->q_table; cur_val <= info->q_point[4]; ++qt, ++cur_val)
+  {
+    if (cur_val <= -info->q_point[3])
+      *qt = -4;
+    else if (cur_val <= -info->q_point[2])
+      *qt = -3;
+    else if (cur_val <= -info->q_point[1])
+      *qt = -2;
+    else if (cur_val < 0)
+      *qt = -1;
+    else if (cur_val == 0)
+      *qt = 0;
+    else if (cur_val < info->q_point[1])
+      *qt = 1;
+    else if (cur_val < info->q_point[2])
+      *qt = 2;
+    else if (cur_val < info->q_point[3])
+      *qt = 3;
+    else
+      *qt = 4;
+  }
+
+  // populting gradients
+  info->total_values = (1 << libraw_internal_data.unpacker_data.fuji_bits);
+  info->raw_bits = libraw_internal_data.unpacker_data.fuji_bits;
+  info->max_bits = 4 * info->raw_bits;
+  info->maxDiff = info->total_values >> 6;
+}
+
+#define XTRANS_BUF_SIZE 0x10000
+
+static inline void fuji_fill_buffer(struct fuji_compressed_block *info)
+{
+  if (info->cur_pos >= info->cur_buf_size)
+  {
+    info->cur_pos = 0;
+    info->cur_buf_offset += info->cur_buf_size;
+#ifdef LIBRAW_USE_OPENMP
+#pragma omp critical
+#endif
+    {
+#ifndef LIBRAW_USE_OPENMP
+      info->input->lock();
+#endif
+      info->input->seek(info->cur_buf_offset, SEEK_SET);
+      info->cur_buf_size = info->input->read(
+          info->cur_buf, 1, _min(info->max_read_size, XTRANS_BUF_SIZE));
+#ifndef LIBRAW_USE_OPENMP
+      info->input->unlock();
+#endif
+      if (info->cur_buf_size < 1) // nothing read
+      {
+        if (info->fillbytes > 0)
+        {
+          int ls = _max(1, _min(info->fillbytes, XTRANS_BUF_SIZE));
+          memset(info->cur_buf, 0, ls);
+          info->fillbytes -= ls;
+        }
+        else
+          throw LIBRAW_EXCEPTION_IO_EOF;
+      }
+      info->max_read_size -= info->cur_buf_size;
+    }
+  }
+}
+
+void LibRaw::init_fuji_block(struct fuji_compressed_block *info,
+                             const struct fuji_compressed_params *params,
+                             INT64 raw_offset, unsigned dsize)
+{
+  info->linealloc =
+      (ushort *)calloc(sizeof(ushort), _ltotal * (params->line_width + 2));
+  merror(info->linealloc, "init_fuji_block()");
+
+  INT64 fsize = libraw_internal_data.internal_data.input->size();
+  info->max_read_size =
+      _min(unsigned(fsize - raw_offset), dsize); // Data size may be incorrect?
+  info->fillbytes = 1;
+
+  info->input = libraw_internal_data.internal_data.input;
+  info->linebuf[_R0] = info->linealloc;
+  for (int i = _R1; i <= _B4; i++)
+    info->linebuf[i] = info->linebuf[i - 1] + params->line_width + 2;
+
+  // init buffer
+  info->cur_buf = (uchar *)malloc(XTRANS_BUF_SIZE);
+  merror(info->cur_buf, "init_fuji_block()");
+  info->cur_bit = 0;
+  info->cur_pos = 0;
+  info->cur_buf_offset = raw_offset;
+  for (int j = 0; j < 3; j++)
+    for (int i = 0; i < 41; i++)
+    {
+      info->grad_even[j][i].value1 = params->maxDiff;
+      info->grad_even[j][i].value2 = 1;
+      info->grad_odd[j][i].value1 = params->maxDiff;
+      info->grad_odd[j][i].value2 = 1;
+    }
+
+  info->cur_buf_size = 0;
+  fuji_fill_buffer(info);
+}
+
+void LibRaw::copy_line_to_xtrans(struct fuji_compressed_block *info,
+                                 int cur_line, int cur_block,
+                                 int cur_block_width)
+{
+  ushort *lineBufB[3];
+  ushort *lineBufG[6];
+  ushort *lineBufR[3];
+  unsigned pixel_count;
+  ushort *line_buf;
+  int index;
+
+  int offset = libraw_internal_data.unpacker_data.fuji_block_width * cur_block +
+               6 * imgdata.sizes.raw_width * cur_line;
+  ushort *raw_block_data = imgdata.rawdata.raw_image + offset;
+  int row_count = 0;
+
+  for (int i = 0; i < 3; i++)
+  {
+    lineBufR[i] = info->linebuf[_R2 + i] + 1;
+    lineBufB[i] = info->linebuf[_B2 + i] + 1;
+  }
+  for (int i = 0; i < 6; i++)
+    lineBufG[i] = info->linebuf[_G2 + i] + 1;
+
+  while (row_count < 6)
+  {
+    pixel_count = 0;
+    while (pixel_count < (unsigned)cur_block_width)
+    {
+      switch (imgdata.idata.xtrans_abs[row_count][(pixel_count % 6)])
+      {
+      case 0: // red
+        line_buf = lineBufR[row_count >> 1];
+        break;
+      case 1:  // green
+      default: // to make static analyzer happy
+        line_buf = lineBufG[row_count];
+        break;
+      case 2: // blue
+        line_buf = lineBufB[row_count >> 1];
+        break;
+      }
+
+      index = (((pixel_count * 2 / 3) & 0x7FFFFFFE) | ((pixel_count % 3) & 1)) +
+              ((pixel_count % 3) >> 1);
+      raw_block_data[pixel_count] = line_buf[index];
+
+      ++pixel_count;
+    }
+    ++row_count;
+    raw_block_data += imgdata.sizes.raw_width;
+  }
+}
+
+void LibRaw::copy_line_to_bayer(struct fuji_compressed_block *info,
+                                int cur_line, int cur_block,
+                                int cur_block_width)
+{
+  ushort *lineBufB[3];
+  ushort *lineBufG[6];
+  ushort *lineBufR[3];
+  unsigned pixel_count;
+  ushort *line_buf;
+
+  int fuji_bayer[2][2];
+  for (int r = 0; r < 2; r++)
+    for (int c = 0; c < 2; c++)
+      fuji_bayer[r][c] = FC(r, c); // We'll downgrade G2 to G below
+
+  int offset = libraw_internal_data.unpacker_data.fuji_block_width * cur_block +
+               6 * imgdata.sizes.raw_width * cur_line;
+  ushort *raw_block_data = imgdata.rawdata.raw_image + offset;
+  int row_count = 0;
+
+  for (int i = 0; i < 3; i++)
+  {
+    lineBufR[i] = info->linebuf[_R2 + i] + 1;
+    lineBufB[i] = info->linebuf[_B2 + i] + 1;
+  }
+  for (int i = 0; i < 6; i++)
+    lineBufG[i] = info->linebuf[_G2 + i] + 1;
+
+  while (row_count < 6)
+  {
+    pixel_count = 0;
+    while (pixel_count < (unsigned)cur_block_width)
+    {
+      switch (fuji_bayer[row_count & 1][pixel_count & 1])
+      {
+      case 0: // red
+        line_buf = lineBufR[row_count >> 1];
+        break;
+      case 1:  // green
+      case 3:  // second green
+      default: // to make static analyzer happy
+        line_buf = lineBufG[row_count];
+        break;
+      case 2: // blue
+        line_buf = lineBufB[row_count >> 1];
+        break;
+      }
+
+      raw_block_data[pixel_count] = line_buf[pixel_count >> 1];
+      ++pixel_count;
+    }
+    ++row_count;
+    raw_block_data += imgdata.sizes.raw_width;
+  }
+}
+
+#define fuji_quant_gradient(i, v1, v2)                                         \
+  (9 * i->q_table[i->q_point[4] + (v1)] + i->q_table[i->q_point[4] + (v2)])
+
+static inline void fuji_zerobits(struct fuji_compressed_block *info, int *count)
+{
+  uchar zero = 0;
+  *count = 0;
+  while (zero == 0)
+  {
+    zero = (info->cur_buf[info->cur_pos] >> (7 - info->cur_bit)) & 1;
+    info->cur_bit++;
+    info->cur_bit &= 7;
+    if (!info->cur_bit)
+    {
+      ++info->cur_pos;
+      fuji_fill_buffer(info);
+    }
+    if (zero)
+      break;
+    ++*count;
+  }
+}
+
+static inline void fuji_read_code(struct fuji_compressed_block *info, int *data,
+                                  int bits_to_read)
+{
+  uchar bits_left = bits_to_read;
+  uchar bits_left_in_byte = 8 - (info->cur_bit & 7);
+  *data = 0;
+  if (!bits_to_read)
+    return;
+  if (bits_to_read >= bits_left_in_byte)
+  {
+    do
+    {
+      *data <<= bits_left_in_byte;
+      bits_left -= bits_left_in_byte;
+      *data |= info->cur_buf[info->cur_pos] & ((1 << bits_left_in_byte) - 1);
+      ++info->cur_pos;
+      fuji_fill_buffer(info);
+      bits_left_in_byte = 8;
+    } while (bits_left >= 8);
+  }
+  if (!bits_left)
+  {
+    info->cur_bit = (8 - (bits_left_in_byte & 7)) & 7;
+    return;
+  }
+  *data <<= bits_left;
+  bits_left_in_byte -= bits_left;
+  *data |= ((1 << bits_left) - 1) &
+           ((unsigned)info->cur_buf[info->cur_pos] >> bits_left_in_byte);
+  info->cur_bit = (8 - (bits_left_in_byte & 7)) & 7;
+}
+
+static inline int bitDiff(int value1, int value2)
+{
+  int decBits = 0;
+  if (value2 < value1)
+    while (decBits <= 14 && (value2 << ++decBits) < value1)
+      ;
+  return decBits;
+}
+
+static inline int
+fuji_decode_sample_even(struct fuji_compressed_block *info,
+                        const struct fuji_compressed_params *params,
+                        ushort *line_buf, int pos, struct int_pair *grads)
+{
+  int interp_val = 0;
+  // ushort decBits;
+  int errcnt = 0;
+
+  int sample = 0, code = 0;
+  ushort *line_buf_cur = line_buf + pos;
+  int Rb = line_buf_cur[-2 - params->line_width];
+  int Rc = line_buf_cur[-3 - params->line_width];
+  int Rd = line_buf_cur[-1 - params->line_width];
+  int Rf = line_buf_cur[-4 - 2 * params->line_width];
+
+  int grad, gradient, diffRcRb, diffRfRb, diffRdRb;
+
+  grad = fuji_quant_gradient(params, Rb - Rf, Rc - Rb);
+  gradient = _abs(grad);
+  diffRcRb = _abs(Rc - Rb);
+  diffRfRb = _abs(Rf - Rb);
+  diffRdRb = _abs(Rd - Rb);
+
+  if (diffRcRb > diffRfRb && diffRcRb > diffRdRb)
+    interp_val = Rf + Rd + 2 * Rb;
+  else if (diffRdRb > diffRcRb && diffRdRb > diffRfRb)
+    interp_val = Rf + Rc + 2 * Rb;
+  else
+    interp_val = Rd + Rc + 2 * Rb;
+
+  fuji_zerobits(info, &sample);
+
+  if (sample < params->max_bits - params->raw_bits - 1)
+  {
+    int decBits = bitDiff(grads[gradient].value1, grads[gradient].value2);
+    fuji_read_code(info, &code, decBits);
+    code += sample << decBits;
+  }
+  else
+  {
+    fuji_read_code(info, &code, params->raw_bits);
+    code++;
+  }
+
+  if (code < 0 || code >= params->total_values)
+    errcnt++;
+
+  if (code & 1)
+    code = -1 - code / 2;
+  else
+    code /= 2;
+
+  grads[gradient].value1 += _abs(code);
+  if (grads[gradient].value2 == params->min_value)
+  {
+    grads[gradient].value1 >>= 1;
+    grads[gradient].value2 >>= 1;
+  }
+  grads[gradient].value2++;
+  if (grad < 0)
+    interp_val = (interp_val >> 2) - code;
+  else
+    interp_val = (interp_val >> 2) + code;
+  if (interp_val < 0)
+    interp_val += params->total_values;
+  else if (interp_val > params->q_point[4])
+    interp_val -= params->total_values;
+
+  if (interp_val >= 0)
+    line_buf_cur[0] = _min(interp_val, params->q_point[4]);
+  else
+    line_buf_cur[0] = 0;
+  return errcnt;
+}
+
+static inline int
+fuji_decode_sample_odd(struct fuji_compressed_block *info,
+                       const struct fuji_compressed_params *params,
+                       ushort *line_buf, int pos, struct int_pair *grads)
+{
+  int interp_val = 0;
+  int errcnt = 0;
+
+  int sample = 0, code = 0;
+  ushort *line_buf_cur = line_buf + pos;
+  int Ra = line_buf_cur[-1];
+  int Rb = line_buf_cur[-2 - params->line_width];
+  int Rc = line_buf_cur[-3 - params->line_width];
+  int Rd = line_buf_cur[-1 - params->line_width];
+  int Rg = line_buf_cur[1];
+
+  int grad, gradient;
+
+  grad = fuji_quant_gradient(params, Rb - Rc, Rc - Ra);
+  gradient = _abs(grad);
+
+  if ((Rb > Rc && Rb > Rd) || (Rb < Rc && Rb < Rd))
+    interp_val = (Rg + Ra + 2 * Rb) >> 2;
+  else
+    interp_val = (Ra + Rg) >> 1;
+
+  fuji_zerobits(info, &sample);
+
+  if (sample < params->max_bits - params->raw_bits - 1)
+  {
+    int decBits = bitDiff(grads[gradient].value1, grads[gradient].value2);
+    fuji_read_code(info, &code, decBits);
+    code += sample << decBits;
+  }
+  else
+  {
+    fuji_read_code(info, &code, params->raw_bits);
+    code++;
+  }
+
+  if (code < 0 || code >= params->total_values)
+    errcnt++;
+
+  if (code & 1)
+    code = -1 - code / 2;
+  else
+    code /= 2;
+
+  grads[gradient].value1 += _abs(code);
+  if (grads[gradient].value2 == params->min_value)
+  {
+    grads[gradient].value1 >>= 1;
+    grads[gradient].value2 >>= 1;
+  }
+  grads[gradient].value2++;
+  if (grad < 0)
+    interp_val -= code;
+  else
+    interp_val += code;
+  if (interp_val < 0)
+    interp_val += params->total_values;
+  else if (interp_val > params->q_point[4])
+    interp_val -= params->total_values;
+
+  if (interp_val >= 0)
+    line_buf_cur[0] = _min(interp_val, params->q_point[4]);
+  else
+    line_buf_cur[0] = 0;
+  return errcnt;
+}
+
+static void fuji_decode_interpolation_even(int line_width, ushort *line_buf,
+                                           int pos)
+{
+  ushort *line_buf_cur = line_buf + pos;
+  int Rb = line_buf_cur[-2 - line_width];
+  int Rc = line_buf_cur[-3 - line_width];
+  int Rd = line_buf_cur[-1 - line_width];
+  int Rf = line_buf_cur[-4 - 2 * line_width];
+  int diffRcRb = _abs(Rc - Rb);
+  int diffRfRb = _abs(Rf - Rb);
+  int diffRdRb = _abs(Rd - Rb);
+  if (diffRcRb > diffRfRb && diffRcRb > diffRdRb)
+    *line_buf_cur = (Rf + Rd + 2 * Rb) >> 2;
+  else if (diffRdRb > diffRcRb && diffRdRb > diffRfRb)
+    *line_buf_cur = (Rf + Rc + 2 * Rb) >> 2;
+  else
+    *line_buf_cur = (Rd + Rc + 2 * Rb) >> 2;
+}
+
+static void fuji_extend_generic(ushort *linebuf[_ltotal], int line_width,
+                                int start, int end)
+{
+  for (int i = start; i <= end; i++)
+  {
+    linebuf[i][0] = linebuf[i - 1][1];
+    linebuf[i][line_width + 1] = linebuf[i - 1][line_width];
+  }
+}
+
+static void fuji_extend_red(ushort *linebuf[_ltotal], int line_width)
+{
+  fuji_extend_generic(linebuf, line_width, _R2, _R4);
+}
+
+static void fuji_extend_green(ushort *linebuf[_ltotal], int line_width)
+{
+  fuji_extend_generic(linebuf, line_width, _G2, _G7);
+}
+
+static void fuji_extend_blue(ushort *linebuf[_ltotal], int line_width)
+{
+  fuji_extend_generic(linebuf, line_width, _B2, _B4);
+}
+
+void LibRaw::xtrans_decode_block(struct fuji_compressed_block *info,
+                                 const struct fuji_compressed_params *params,
+                                 int cur_line)
+{
+  int r_even_pos = 0, r_odd_pos = 1;
+  int g_even_pos = 0, g_odd_pos = 1;
+  int b_even_pos = 0, b_odd_pos = 1;
+
+  int errcnt = 0;
+
+  const int line_width = params->line_width;
+
+  while (g_even_pos < line_width || g_odd_pos < line_width)
+  {
+    if (g_even_pos < line_width)
+    {
+      fuji_decode_interpolation_even(line_width, info->linebuf[_R2] + 1,
+                                     r_even_pos);
+      r_even_pos += 2;
+      errcnt += fuji_decode_sample_even(info, params, info->linebuf[_G2] + 1,
+                                        g_even_pos, info->grad_even[0]);
+      g_even_pos += 2;
+    }
+    if (g_even_pos > 8)
+    {
+      errcnt += fuji_decode_sample_odd(info, params, info->linebuf[_R2] + 1,
+                                       r_odd_pos, info->grad_odd[0]);
+      r_odd_pos += 2;
+      errcnt += fuji_decode_sample_odd(info, params, info->linebuf[_G2] + 1,
+                                       g_odd_pos, info->grad_odd[0]);
+      g_odd_pos += 2;
+    }
+  }
+
+  fuji_extend_red(info->linebuf, line_width);
+  fuji_extend_green(info->linebuf, line_width);
+
+  g_even_pos = 0, g_odd_pos = 1;
+
+  while (g_even_pos < line_width || g_odd_pos < line_width)
+  {
+    if (g_even_pos < line_width)
+    {
+      errcnt += fuji_decode_sample_even(info, params, info->linebuf[_G3] + 1,
+                                        g_even_pos, info->grad_even[1]);
+      g_even_pos += 2;
+      fuji_decode_interpolation_even(line_width, info->linebuf[_B2] + 1,
+                                     b_even_pos);
+      b_even_pos += 2;
+    }
+    if (g_even_pos > 8)
+    {
+      errcnt += fuji_decode_sample_odd(info, params, info->linebuf[_G3] + 1,
+                                       g_odd_pos, info->grad_odd[1]);
+      g_odd_pos += 2;
+      errcnt += fuji_decode_sample_odd(info, params, info->linebuf[_B2] + 1,
+                                       b_odd_pos, info->grad_odd[1]);
+      b_odd_pos += 2;
+    }
+  }
+
+  fuji_extend_green(info->linebuf, line_width);
+  fuji_extend_blue(info->linebuf, line_width);
+
+  r_even_pos = 0, r_odd_pos = 1;
+  g_even_pos = 0, g_odd_pos = 1;
+
+  while (g_even_pos < line_width || g_odd_pos < line_width)
+  {
+    if (g_even_pos < line_width)
+    {
+      if (r_even_pos & 3)
+        errcnt += fuji_decode_sample_even(info, params, info->linebuf[_R3] + 1,
+                                          r_even_pos, info->grad_even[2]);
+      else
+        fuji_decode_interpolation_even(line_width, info->linebuf[_R3] + 1,
+                                       r_even_pos);
+      r_even_pos += 2;
+      fuji_decode_interpolation_even(line_width, info->linebuf[_G4] + 1,
+                                     g_even_pos);
+      g_even_pos += 2;
+    }
+    if (g_even_pos > 8)
+    {
+      errcnt += fuji_decode_sample_odd(info, params, info->linebuf[_R3] + 1,
+                                       r_odd_pos, info->grad_odd[2]);
+      r_odd_pos += 2;
+      errcnt += fuji_decode_sample_odd(info, params, info->linebuf[_G4] + 1,
+                                       g_odd_pos, info->grad_odd[2]);
+      g_odd_pos += 2;
+    }
+  }
+
+  fuji_extend_red(info->linebuf, line_width);
+  fuji_extend_green(info->linebuf, line_width);
+
+  g_even_pos = 0, g_odd_pos = 1;
+  b_even_pos = 0, b_odd_pos = 1;
+
+  while (g_even_pos < line_width || g_odd_pos < line_width)
+  {
+    if (g_even_pos < line_width)
+    {
+      errcnt += fuji_decode_sample_even(info, params, info->linebuf[_G5] + 1,
+                                        g_even_pos, info->grad_even[0]);
+      g_even_pos += 2;
+      if ((b_even_pos & 3) == 2)
+        fuji_decode_interpolation_even(line_width, info->linebuf[_B3] + 1,
+                                       b_even_pos);
+      else
+        errcnt += fuji_decode_sample_even(info, params, info->linebuf[_B3] + 1,
+                                          b_even_pos, info->grad_even[0]);
+      b_even_pos += 2;
+    }
+    if (g_even_pos > 8)
+    {
+      errcnt += fuji_decode_sample_odd(info, params, info->linebuf[_G5] + 1,
+                                       g_odd_pos, info->grad_odd[0]);
+      g_odd_pos += 2;
+      errcnt += fuji_decode_sample_odd(info, params, info->linebuf[_B3] + 1,
+                                       b_odd_pos, info->grad_odd[0]);
+      b_odd_pos += 2;
+    }
+  }
+
+  fuji_extend_green(info->linebuf, line_width);
+  fuji_extend_blue(info->linebuf, line_width);
+
+  r_even_pos = 0, r_odd_pos = 1;
+  g_even_pos = 0, g_odd_pos = 1;
+
+  while (g_even_pos < line_width || g_odd_pos < line_width)
+  {
+    if (g_even_pos < line_width)
+    {
+      if ((r_even_pos & 3) == 2)
+        fuji_decode_interpolation_even(line_width, info->linebuf[_R4] + 1,
+                                       r_even_pos);
+      else
+        errcnt += fuji_decode_sample_even(info, params, info->linebuf[_R4] + 1,
+                                          r_even_pos, info->grad_even[1]);
+      r_even_pos += 2;
+      errcnt += fuji_decode_sample_even(info, params, info->linebuf[_G6] + 1,
+                                        g_even_pos, info->grad_even[1]);
+      g_even_pos += 2;
+    }
+    if (g_even_pos > 8)
+    {
+      errcnt += fuji_decode_sample_odd(info, params, info->linebuf[_R4] + 1,
+                                       r_odd_pos, info->grad_odd[1]);
+      r_odd_pos += 2;
+      errcnt += fuji_decode_sample_odd(info, params, info->linebuf[_G6] + 1,
+                                       g_odd_pos, info->grad_odd[1]);
+      g_odd_pos += 2;
+    }
+  }
+
+  fuji_extend_red(info->linebuf, line_width);
+  fuji_extend_green(info->linebuf, line_width);
+
+  g_even_pos = 0, g_odd_pos = 1;
+  b_even_pos = 0, b_odd_pos = 1;
+
+  while (g_even_pos < line_width || g_odd_pos < line_width)
+  {
+    if (g_even_pos < line_width)
+    {
+      fuji_decode_interpolation_even(line_width, info->linebuf[_G7] + 1,
+                                     g_even_pos);
+      g_even_pos += 2;
+      if (b_even_pos & 3)
+        errcnt += fuji_decode_sample_even(info, params, info->linebuf[_B4] + 1,
+                                          b_even_pos, info->grad_even[2]);
+      else
+        fuji_decode_interpolation_even(line_width, info->linebuf[_B4] + 1,
+                                       b_even_pos);
+      b_even_pos += 2;
+    }
+    if (g_even_pos > 8)
+    {
+      errcnt += fuji_decode_sample_odd(info, params, info->linebuf[_G7] + 1,
+                                       g_odd_pos, info->grad_odd[2]);
+      g_odd_pos += 2;
+      errcnt += fuji_decode_sample_odd(info, params, info->linebuf[_B4] + 1,
+                                       b_odd_pos, info->grad_odd[2]);
+      b_odd_pos += 2;
+    }
+  }
+
+  fuji_extend_green(info->linebuf, line_width);
+  fuji_extend_blue(info->linebuf, line_width);
+
+  if (errcnt)
+    derror();
+}
+
+void LibRaw::fuji_bayer_decode_block(
+    struct fuji_compressed_block *info,
+    const struct fuji_compressed_params *params, int cur_line)
+{
+  int r_even_pos = 0, r_odd_pos = 1;
+  int g_even_pos = 0, g_odd_pos = 1;
+  int b_even_pos = 0, b_odd_pos = 1;
+
+  int errcnt = 0;
+
+  const int line_width = params->line_width;
+
+  while (g_even_pos < line_width || g_odd_pos < line_width)
+  {
+    if (g_even_pos < line_width)
+    {
+      errcnt += fuji_decode_sample_even(info, params, info->linebuf[_R2] + 1,
+                                        r_even_pos, info->grad_even[0]);
+      r_even_pos += 2;
+      errcnt += fuji_decode_sample_even(info, params, info->linebuf[_G2] + 1,
+                                        g_even_pos, info->grad_even[0]);
+      g_even_pos += 2;
+    }
+    if (g_even_pos > 8)
+    {
+      errcnt += fuji_decode_sample_odd(info, params, info->linebuf[_R2] + 1,
+                                       r_odd_pos, info->grad_odd[0]);
+      r_odd_pos += 2;
+      errcnt += fuji_decode_sample_odd(info, params, info->linebuf[_G2] + 1,
+                                       g_odd_pos, info->grad_odd[0]);
+      g_odd_pos += 2;
+    }
+  }
+
+  fuji_extend_red(info->linebuf, line_width);
+  fuji_extend_green(info->linebuf, line_width);
+
+  g_even_pos = 0, g_odd_pos = 1;
+
+  while (g_even_pos < line_width || g_odd_pos < line_width)
+  {
+    if (g_even_pos < line_width)
+    {
+      errcnt += fuji_decode_sample_even(info, params, info->linebuf[_G3] + 1,
+                                        g_even_pos, info->grad_even[1]);
+      g_even_pos += 2;
+      errcnt += fuji_decode_sample_even(info, params, info->linebuf[_B2] + 1,
+                                        b_even_pos, info->grad_even[1]);
+      b_even_pos += 2;
+    }
+    if (g_even_pos > 8)
+    {
+      errcnt += fuji_decode_sample_odd(info, params, info->linebuf[_G3] + 1,
+                                       g_odd_pos, info->grad_odd[1]);
+      g_odd_pos += 2;
+      errcnt += fuji_decode_sample_odd(info, params, info->linebuf[_B2] + 1,
+                                       b_odd_pos, info->grad_odd[1]);
+      b_odd_pos += 2;
+    }
+  }
+
+  fuji_extend_green(info->linebuf, line_width);
+  fuji_extend_blue(info->linebuf, line_width);
+
+  r_even_pos = 0, r_odd_pos = 1;
+  g_even_pos = 0, g_odd_pos = 1;
+
+  while (g_even_pos < line_width || g_odd_pos < line_width)
+  {
+    if (g_even_pos < line_width)
+    {
+      errcnt += fuji_decode_sample_even(info, params, info->linebuf[_R3] + 1,
+                                        r_even_pos, info->grad_even[2]);
+      r_even_pos += 2;
+      errcnt += fuji_decode_sample_even(info, params, info->linebuf[_G4] + 1,
+                                        g_even_pos, info->grad_even[2]);
+      g_even_pos += 2;
+    }
+    if (g_even_pos > 8)
+    {
+      errcnt += fuji_decode_sample_odd(info, params, info->linebuf[_R3] + 1,
+                                       r_odd_pos, info->grad_odd[2]);
+      r_odd_pos += 2;
+      errcnt += fuji_decode_sample_odd(info, params, info->linebuf[_G4] + 1,
+                                       g_odd_pos, info->grad_odd[2]);
+      g_odd_pos += 2;
+    }
+  }
+
+  fuji_extend_red(info->linebuf, line_width);
+  fuji_extend_green(info->linebuf, line_width);
+
+  g_even_pos = 0, g_odd_pos = 1;
+  b_even_pos = 0, b_odd_pos = 1;
+
+  while (g_even_pos < line_width || g_odd_pos < line_width)
+  {
+    if (g_even_pos < line_width)
+    {
+      errcnt += fuji_decode_sample_even(info, params, info->linebuf[_G5] + 1,
+                                        g_even_pos, info->grad_even[0]);
+      g_even_pos += 2;
+      errcnt += fuji_decode_sample_even(info, params, info->linebuf[_B3] + 1,
+                                        b_even_pos, info->grad_even[0]);
+      b_even_pos += 2;
+    }
+    if (g_even_pos > 8)
+    {
+      errcnt += fuji_decode_sample_odd(info, params, info->linebuf[_G5] + 1,
+                                       g_odd_pos, info->grad_odd[0]);
+      g_odd_pos += 2;
+      errcnt += fuji_decode_sample_odd(info, params, info->linebuf[_B3] + 1,
+                                       b_odd_pos, info->grad_odd[0]);
+      b_odd_pos += 2;
+    }
+  }
+
+  fuji_extend_green(info->linebuf, line_width);
+  fuji_extend_blue(info->linebuf, line_width);
+
+  r_even_pos = 0, r_odd_pos = 1;
+  g_even_pos = 0, g_odd_pos = 1;
+
+  while (g_even_pos < line_width || g_odd_pos < line_width)
+  {
+    if (g_even_pos < line_width)
+    {
+      errcnt += fuji_decode_sample_even(info, params, info->linebuf[_R4] + 1,
+                                        r_even_pos, info->grad_even[1]);
+      r_even_pos += 2;
+      errcnt += fuji_decode_sample_even(info, params, info->linebuf[_G6] + 1,
+                                        g_even_pos, info->grad_even[1]);
+      g_even_pos += 2;
+    }
+    if (g_even_pos > 8)
+    {
+      errcnt += fuji_decode_sample_odd(info, params, info->linebuf[_R4] + 1,
+                                       r_odd_pos, info->grad_odd[1]);
+      r_odd_pos += 2;
+      errcnt += fuji_decode_sample_odd(info, params, info->linebuf[_G6] + 1,
+                                       g_odd_pos, info->grad_odd[1]);
+      g_odd_pos += 2;
+    }
+  }
+
+  fuji_extend_red(info->linebuf, line_width);
+  fuji_extend_green(info->linebuf, line_width);
+
+  g_even_pos = 0, g_odd_pos = 1;
+  b_even_pos = 0, b_odd_pos = 1;
+
+  while (g_even_pos < line_width || g_odd_pos < line_width)
+  {
+    if (g_even_pos < line_width)
+    {
+      errcnt += fuji_decode_sample_even(info, params, info->linebuf[_G7] + 1,
+                                        g_even_pos, info->grad_even[2]);
+      g_even_pos += 2;
+      errcnt += fuji_decode_sample_even(info, params, info->linebuf[_B4] + 1,
+                                        b_even_pos, info->grad_even[2]);
+      b_even_pos += 2;
+    }
+    if (g_even_pos > 8)
+    {
+      errcnt += fuji_decode_sample_odd(info, params, info->linebuf[_G7] + 1,
+                                       g_odd_pos, info->grad_odd[2]);
+      g_odd_pos += 2;
+      errcnt += fuji_decode_sample_odd(info, params, info->linebuf[_B4] + 1,
+                                       b_odd_pos, info->grad_odd[2]);
+      b_odd_pos += 2;
+    }
+  }
+
+  fuji_extend_green(info->linebuf, line_width);
+  fuji_extend_blue(info->linebuf, line_width);
+
+  if (errcnt)
+    derror();
+}
+
+void LibRaw::fuji_decode_strip(const struct fuji_compressed_params *info_common,
+                               int cur_block, INT64 raw_offset, unsigned dsize)
+{
+  int cur_block_width, cur_line;
+  unsigned line_size;
+  struct fuji_compressed_block info;
+
+  init_fuji_block(&info, info_common, raw_offset, dsize);
+  line_size = sizeof(ushort) * (info_common->line_width + 2);
+
+  cur_block_width = libraw_internal_data.unpacker_data.fuji_block_width;
+  if (cur_block + 1 == libraw_internal_data.unpacker_data.fuji_total_blocks)
+  {
+    cur_block_width =
+        imgdata.sizes.raw_width -
+        (libraw_internal_data.unpacker_data.fuji_block_width * cur_block);
+    /* Old code, may get incorrect results on GFX50, but luckily large optical
+    black cur_block_width = imgdata.sizes.raw_width %
+    libraw_internal_data.unpacker_data.fuji_block_width;
+    */
+  }
+
+  struct i_pair
+  {
+    int a, b;
+  };
+  const i_pair mtable[6] = {{_R0, _R3}, {_R1, _R4}, {_G0, _G6},
+                            {_G1, _G7}, {_B0, _B3}, {_B1, _B4}},
+               ztable[3] = {{_R2, 3}, {_G2, 6}, {_B2, 3}};
+  for (cur_line = 0;
+       cur_line < libraw_internal_data.unpacker_data.fuji_total_lines;
+       cur_line++)
+  {
+    if (libraw_internal_data.unpacker_data.fuji_raw_type == 16)
+      xtrans_decode_block(&info, info_common, cur_line);
+    else
+      fuji_bayer_decode_block(&info, info_common, cur_line);
+
+    // copy data from line buffers and advance
+    for (int i = 0; i < 6; i++)
+      memcpy(info.linebuf[mtable[i].a], info.linebuf[mtable[i].b], line_size);
+
+    if (libraw_internal_data.unpacker_data.fuji_raw_type == 16)
+      copy_line_to_xtrans(&info, cur_line, cur_block, cur_block_width);
+    else
+      copy_line_to_bayer(&info, cur_line, cur_block, cur_block_width);
+
+    for (int i = 0; i < 3; i++)
+    {
+      memset(info.linebuf[ztable[i].a], 0, ztable[i].b * line_size);
+      info.linebuf[ztable[i].a][0] = info.linebuf[ztable[i].a - 1][1];
+      info.linebuf[ztable[i].a][info_common->line_width + 1] =
+          info.linebuf[ztable[i].a - 1][info_common->line_width];
+    }
+  }
+
+  // release data
+  free(info.linealloc);
+  free(info.cur_buf);
+}
+
+void LibRaw::fuji_compressed_load_raw()
+{
+  struct fuji_compressed_params common_info;
+  int cur_block;
+  unsigned *block_sizes;
+  INT64 raw_offset, *raw_block_offsets;
+  // struct fuji_compressed_block info;
+
+  init_fuji_compr(&common_info);
+
+  // read block sizes
+  block_sizes = (unsigned *)malloc(
+      sizeof(unsigned) * libraw_internal_data.unpacker_data.fuji_total_blocks);
+  merror(block_sizes, "fuji_compressed_load_raw()");
+  raw_block_offsets = (INT64 *)malloc(
+      sizeof(INT64) * libraw_internal_data.unpacker_data.fuji_total_blocks);
+  merror(raw_block_offsets, "fuji_compressed_load_raw()");
+
+  raw_offset =
+      sizeof(unsigned) * libraw_internal_data.unpacker_data.fuji_total_blocks;
+  if (raw_offset & 0xC)
+    raw_offset += 0x10 - (raw_offset & 0xC);
+
+  raw_offset += libraw_internal_data.unpacker_data.data_offset;
+
+  libraw_internal_data.internal_data.input->seek(
+      libraw_internal_data.unpacker_data.data_offset, SEEK_SET);
+  libraw_internal_data.internal_data.input->read(
+      block_sizes, 1,
+      sizeof(unsigned) * libraw_internal_data.unpacker_data.fuji_total_blocks);
+
+  raw_block_offsets[0] = raw_offset;
+  // calculating raw block offsets
+  for (cur_block = 0;
+       cur_block < libraw_internal_data.unpacker_data.fuji_total_blocks;
+       cur_block++)
+  {
+    unsigned bsize = sgetn(4, (uchar *)(block_sizes + cur_block));
+    block_sizes[cur_block] = bsize;
+  }
+
+  for (cur_block = 1;
+       cur_block < libraw_internal_data.unpacker_data.fuji_total_blocks;
+       cur_block++)
+    raw_block_offsets[cur_block] =
+        raw_block_offsets[cur_block - 1] + block_sizes[cur_block - 1];
+
+  fuji_decode_loop(&common_info,
+                   libraw_internal_data.unpacker_data.fuji_total_blocks,
+                   raw_block_offsets, block_sizes);
+
+  free(block_sizes);
+  free(raw_block_offsets);
+  free(common_info.q_table);
+}
+
+void LibRaw::fuji_decode_loop(const struct fuji_compressed_params *common_info,
+                              int count, INT64 *raw_block_offsets,
+                              unsigned *block_sizes)
+{
+  int cur_block;
+#ifdef LIBRAW_USE_OPENMP
+#pragma omp parallel for private(cur_block)
+#endif
+  for (cur_block = 0; cur_block < count; cur_block++)
+  {
+    fuji_decode_strip(common_info, cur_block, raw_block_offsets[cur_block],
+                      block_sizes[cur_block]);
+  }
+}
+
+void LibRaw::parse_fuji_compressed_header()
+{
+  unsigned signature, version, h_raw_type, h_raw_bits, h_raw_height,
+      h_raw_rounded_width, h_raw_width, h_block_size, h_blocks_in_row,
+      h_total_lines;
+
+  uchar header[16];
+
+  libraw_internal_data.internal_data.input->seek(
+      libraw_internal_data.unpacker_data.data_offset, SEEK_SET);
+  libraw_internal_data.internal_data.input->read(header, 1, sizeof(header));
+
+  // read all header
+  signature = sgetn(2, header);
+  version = header[2];
+  h_raw_type = header[3];
+  h_raw_bits = header[4];
+  h_raw_height = sgetn(2, header + 5);
+  h_raw_rounded_width = sgetn(2, header + 7);
+  h_raw_width = sgetn(2, header + 9);
+  h_block_size = sgetn(2, header + 11);
+  h_blocks_in_row = header[13];
+  h_total_lines = sgetn(2, header + 14);
+
+  // general validation
+  if (signature != 0x4953 || version != 1 || h_raw_height > 0x3000 ||
+      h_raw_height < 6 || h_raw_height % 6 || h_block_size < 1 ||
+      h_raw_width > 0x3000 || h_raw_width < 0x300 || h_raw_width % 24 ||
+      h_raw_rounded_width > 0x3000 || h_raw_rounded_width < h_block_size ||
+      h_raw_rounded_width % h_block_size ||
+      h_raw_rounded_width - h_raw_width >= h_block_size ||
+      h_block_size != 0x300 || h_blocks_in_row > 0x10 || h_blocks_in_row == 0 ||
+      h_blocks_in_row != h_raw_rounded_width / h_block_size ||
+      h_total_lines > 0x800 || h_total_lines == 0 ||
+      h_total_lines != h_raw_height / 6 ||
+      (h_raw_bits != 12 && h_raw_bits != 14 && h_raw_bits != 16) ||
+      (h_raw_type != 16 && h_raw_type != 0))
+    return;
+
+  // modify data
+  libraw_internal_data.unpacker_data.fuji_total_lines = h_total_lines;
+  libraw_internal_data.unpacker_data.fuji_total_blocks = h_blocks_in_row;
+  libraw_internal_data.unpacker_data.fuji_block_width = h_block_size;
+  libraw_internal_data.unpacker_data.fuji_bits = h_raw_bits;
+  libraw_internal_data.unpacker_data.fuji_raw_type = h_raw_type;
+  imgdata.sizes.raw_width = h_raw_width;
+  imgdata.sizes.raw_height = h_raw_height;
+  libraw_internal_data.unpacker_data.data_offset += 16;
+  load_raw = &LibRaw::fuji_compressed_load_raw;
+}
+
+#undef _abs
+#undef _min
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/decoders/generic.cpp libkdcraw/libkdcraw/libraw/src/decoders/generic.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/decoders/generic.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/decoders/generic.cpp	2022-11-07 07:46:31.730795008 +0300
@@ -0,0 +1,112 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+void LibRaw::unpacked_load_raw()
+{
+  int row, col, bits = 0;
+  while (1 << ++bits < (int)maximum)
+    ;
+  read_shorts(raw_image, raw_width * raw_height);
+  fseek(ifp, -2, SEEK_CUR); // avoid EOF error
+  if (maximum < 0xffff || load_flags)
+    for (row = 0; row < raw_height; row++)
+    {
+      checkCancel();
+      for (col = 0; col < raw_width; col++)
+        if ((RAW(row, col) >>= load_flags) >> bits &&
+            (unsigned)(row - top_margin) < height &&
+            (unsigned)(col - left_margin) < width)
+          derror();
+    }
+}
+
+void LibRaw::packed_load_raw()
+{
+  int vbits = 0, bwide, rbits, bite, half, irow, row, col, val, i;
+  UINT64 bitbuf = 0;
+
+  bwide = raw_width * tiff_bps / 8;
+  bwide += bwide & load_flags >> 7;
+  rbits = bwide * 8 - raw_width * tiff_bps;
+  if (load_flags & 1)
+    bwide = bwide * 16 / 15;
+  bite = 8 + (load_flags & 24);
+  half = (raw_height + 1) >> 1;
+  for (irow = 0; irow < raw_height; irow++)
+  {
+    checkCancel();
+    row = irow;
+    if (load_flags & 2 && (row = irow % half * 2 + irow / half) == 1 &&
+        load_flags & 4)
+    {
+      if (vbits = 0, tiff_compress)
+        fseek(ifp, data_offset - (-half * bwide & -2048), SEEK_SET);
+      else
+      {
+        fseek(ifp, 0, SEEK_END);
+        fseek(ifp, ftell(ifp) >> 3 << 2, SEEK_SET);
+      }
+    }
+    if (feof(ifp))
+      throw LIBRAW_EXCEPTION_IO_EOF;
+    for (col = 0; col < raw_width; col++)
+    {
+      for (vbits -= tiff_bps; vbits < 0; vbits += bite)
+      {
+        bitbuf <<= bite;
+        for (i = 0; i < bite; i += 8)
+          bitbuf |= (unsigned(fgetc(ifp)) << i);
+      }
+      val = bitbuf << (64 - tiff_bps - vbits) >> (64 - tiff_bps);
+      RAW(row, col ^ (load_flags >> 6 & 1)) = val;
+      if (load_flags & 1 && (col % 10) == 9 && fgetc(ifp) &&
+          row < height + top_margin && col < width + left_margin)
+        derror();
+    }
+    vbits -= rbits;
+  }
+}
+
+void LibRaw::eight_bit_load_raw()
+{
+  uchar *pixel;
+  unsigned row, col;
+
+  pixel = (uchar *)calloc(raw_width, sizeof *pixel);
+  merror(pixel, "eight_bit_load_raw()");
+  try
+  {
+    for (row = 0; row < raw_height; row++)
+    {
+      checkCancel();
+      if (fread(pixel, 1, raw_width, ifp) < raw_width)
+        derror();
+      for (col = 0; col < raw_width; col++)
+        RAW(row, col) = curve[pixel[col]];
+    }
+  }
+  catch (...)
+  {
+    free(pixel);
+    throw;
+  }
+  free(pixel);
+  maximum = curve[0xff];
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/decoders/kodak_decoders.cpp libkdcraw/libkdcraw/libraw/src/decoders/kodak_decoders.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/decoders/kodak_decoders.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/decoders/kodak_decoders.cpp	2022-11-07 07:46:31.730795008 +0300
@@ -0,0 +1,543 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+#define radc_token(tree) ((signed char)getbithuff(8, huff + (tree) * 256))
+
+#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)
+
+#ifdef __GNUC__
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
+#pragma GCC optimize("no-aggressive-loop-optimizations")
+#endif
+#endif
+
+void LibRaw::kodak_radc_load_raw()
+{
+  // All kodak radc images are 768x512
+  if (width > 768 || raw_width > 768 || height > 512 || raw_height > 512)
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+  static const signed char src[] = {
+      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};
+  std::vector<ushort> huff_buffer(19 * 256);
+  ushort* huff = &huff_buffer[0];
+  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];
+  static const ushort pt[] = {0,    0,    1280, 1344,  2320,  3616,
+                              3328, 8000, 4095, 16383, 65535, 16383};
+
+  for (i = 2; i < 12; i += 2)
+    for (c = pt[i - 2]; c <= pt[i]; c++)
+      curve[c] = (float)(c - pt[i - 2]) / (pt[i] - pt[i - 2]) *
+                     (pt[i + 1] - pt[i - 1]) +
+                 pt[i - 1] + 0.5;
+  for (s = i = 0; i < int(sizeof src); i += 2)
+    FORC(256 >> src[i])
+  ((ushort *)huff)[s++] = src[i] << 8 | (uchar)src[i + 1];
+  s = kodak_cbpp == 243 ? 2 : 3;
+  FORC(256) huff[18 * 256 + c] = (8 - s) << 8 | c >> s << s | 1 << (s - 1);
+  getbits(-1);
+  for (i = 0; i < int(sizeof(buf) / sizeof(short)); i++)
+    ((short *)buf)[i] = 2048;
+  for (row = 0; row < height; row += 4)
+  {
+    checkCancel();
+    FORC3 mul[c] = getbits(6);
+    if (!mul[0] || !mul[1] || !mul[2])
+      throw LIBRAW_EXCEPTION_IO_CORRUPT;
+    FORC3
+    {
+      val = ((0x1000000 / last[c] + 0x7ff) >> 12) * mul[c];
+      s = val > 65564 ? 10 : 12;
+      x = ~((~0u) << (s - 1));
+      val <<= 12 - s;
+      for (i = 0; i < int(sizeof(buf[0]) / sizeof(short)); i++)
+        ((short *)buf[c])[i] = MIN(0x7FFFFFFF, (((short *)buf[c])[i] * static_cast<long long>(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 (col >= 0)
+            {
+              if (tree == 8)
+                FORYX buf[c][y][x] = (uchar)radc_token(18) * 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;
+                if (col >= 0)
+                  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)
+              RAW(row + y * 2 + c - 1, x * 2 + 2 - c) = val;
+            else
+              RAW(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)
+        {
+          r = x ? x - 1 : x + 1;
+          s = x + 1 < width ? x + 1 : x - 1;
+          val = (RAW(y, x) - 2048) * 2 + (RAW(y, r) + RAW(y, s)) / 2;
+          if (val < 0)
+            val = 0;
+          RAW(y, x) = val;
+        }
+  }
+  for (i = 0; i < height * width; i++)
+    raw_image[i] = curve[raw_image[i]];
+  maximum = 0x3fff;
+}
+
+#undef FORYX
+#undef PREDICTOR
+
+#ifdef NO_JPEG
+void LibRaw::kodak_jpeg_load_raw() {}
+#else
+static void jpegErrorExit_k(j_common_ptr cinfo)
+{
+  throw LIBRAW_EXCEPTION_DECODE_JPEG;
+}
+
+// LibRaw's Kodak_jpeg_load_raw
+void LibRaw::kodak_jpeg_load_raw()
+{
+  if (data_size < 1)
+    throw LIBRAW_EXCEPTION_DECODE_JPEG;
+
+  int row, col;
+  struct jpeg_decompress_struct cinfo;
+  struct jpeg_error_mgr pub;
+  cinfo.err = jpeg_std_error(&pub);
+  pub.error_exit = jpegErrorExit_k;
+
+  unsigned char *jpg_buf = (unsigned char *)malloc(data_size);
+  merror(jpg_buf, "kodak_jpeg_load_raw");
+  unsigned char *pixel_buf = (unsigned char *)malloc(width * 3);
+  jpeg_create_decompress(&cinfo);
+  merror(pixel_buf, "kodak_jpeg_load_raw");
+
+  fread(jpg_buf, data_size, 1, ifp);
+  swab((char *)jpg_buf, (char *)jpg_buf, data_size);
+  try
+  {
+    jpeg_mem_src(&cinfo, jpg_buf, data_size);
+    int rc = jpeg_read_header(&cinfo, TRUE);
+    if (rc != 1)
+      throw LIBRAW_EXCEPTION_DECODE_JPEG;
+
+    jpeg_start_decompress(&cinfo);
+    if ((cinfo.output_width != width) || (cinfo.output_height * 2 != height) ||
+        (cinfo.output_components != 3))
+    {
+      throw LIBRAW_EXCEPTION_DECODE_JPEG;
+    }
+
+    unsigned char *buf[1];
+    buf[0] = pixel_buf;
+
+    while (cinfo.output_scanline < cinfo.output_height)
+    {
+      checkCancel();
+      row = cinfo.output_scanline * 2;
+      jpeg_read_scanlines(&cinfo, buf, 1);
+      unsigned char(*pixel)[3] = (unsigned char(*)[3])buf[0];
+      for (col = 0; col < width; col += 2)
+      {
+        RAW(row + 0, col + 0) = pixel[col + 0][1] << 1;
+        RAW(row + 1, col + 1) = pixel[col + 1][1] << 1;
+        RAW(row + 0, col + 1) = pixel[col][0] + pixel[col + 1][0];
+        RAW(row + 1, col + 0) = pixel[col][2] + pixel[col + 1][2];
+      }
+    }
+  }
+  catch (...)
+  {
+    jpeg_finish_decompress(&cinfo);
+    jpeg_destroy_decompress(&cinfo);
+    free(jpg_buf);
+    free(pixel_buf);
+    throw;
+  }
+  jpeg_finish_decompress(&cinfo);
+  jpeg_destroy_decompress(&cinfo);
+  free(jpg_buf);
+  free(pixel_buf);
+  maximum = 0xff << 1;
+}
+#endif
+
+void LibRaw::kodak_dc120_load_raw()
+{
+  static const int mul[4] = {162, 192, 187, 92};
+  static const int add[4] = {0, 636, 424, 212};
+  uchar pixel[848];
+  int row, shift, col;
+
+  for (row = 0; row < height; row++)
+  {
+    checkCancel();
+    if (fread(pixel, 1, 848, ifp) < 848)
+      derror();
+    shift = row * mul[row & 3] + add[row & 3];
+    for (col = 0; col < width; col++)
+      RAW(row, col) = (ushort)pixel[(col + shift) % 848];
+  }
+  maximum = 0xff;
+}
+void LibRaw::kodak_c330_load_raw()
+{
+  if (!image)
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+  uchar *pixel;
+  int row, col, y, cb, cr, rgb[3], c;
+
+  pixel = (uchar *)calloc(raw_width, 2 * sizeof *pixel);
+  merror(pixel, "kodak_c330_load_raw()");
+  try
+  {
+    for (row = 0; row < height; row++)
+    {
+      checkCancel();
+      if (fread(pixel, raw_width, 2, ifp) < 2)
+        derror();
+      if (load_flags && (row & 31) == 31)
+        fseek(ifp, raw_width * 32, SEEK_CUR);
+      for (col = 0; col < width; col++)
+      {
+        y = pixel[col * 2];
+        cb = pixel[(col * 2 & -4) | 1] - 128;
+        cr = pixel[(col * 2 & -4) | 3] - 128;
+        rgb[1] = y - ((cb + cr + 2) >> 2);
+        rgb[2] = rgb[1] + cb;
+        rgb[0] = rgb[1] + cr;
+        FORC3 image[row * width + col][c] = curve[LIM(rgb[c], 0, 255)];
+      }
+    }
+  }
+  catch (...)
+  {
+    free(pixel);
+    throw;
+  }
+  free(pixel);
+  maximum = curve[0xff];
+}
+
+void LibRaw::kodak_c603_load_raw()
+{
+  if (!image)
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+  uchar *pixel;
+  int row, col, y, cb, cr, rgb[3], c;
+
+  pixel = (uchar *)calloc(raw_width, 3 * sizeof *pixel);
+  merror(pixel, "kodak_c603_load_raw()");
+  try
+  {
+    for (row = 0; row < height; row++)
+    {
+      checkCancel();
+      if (~row & 1)
+        if (fread(pixel, raw_width, 3, ifp) < 3)
+          derror();
+      for (col = 0; col < width; col++)
+      {
+        y = pixel[width * 2 * (row & 1) + col];
+        cb = pixel[width + (col & -2)] - 128;
+        cr = pixel[width + (col & -2) + 1] - 128;
+        rgb[1] = y - ((cb + cr + 2) >> 2);
+        rgb[2] = rgb[1] + cb;
+        rgb[0] = rgb[1] + cr;
+        FORC3 image[row * width + col][c] = curve[LIM(rgb[c], 0, 255)];
+      }
+    }
+  }
+  catch (...)
+  {
+    free(pixel);
+    throw;
+  }
+  free(pixel);
+  maximum = curve[0xff];
+}
+
+void LibRaw::kodak_262_load_raw()
+{
+  static const uchar kodak_tree[2][26] = {
+      {0, 1, 5, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+      {0, 3, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}};
+  ushort *huff[2];
+  uchar *pixel;
+  int *strip, ns, c, row, col, chess, pi = 0, pi1, pi2, pred, val;
+
+  FORC(2) huff[c] = make_decoder(kodak_tree[c]);
+  ns = (raw_height + 63) >> 5;
+  pixel = (uchar *)malloc(raw_width * 32 + ns * 4);
+  merror(pixel, "kodak_262_load_raw()");
+  strip = (int *)(pixel + raw_width * 32);
+  order = 0x4d4d;
+  FORC(ns) strip[c] = get4();
+  try
+  {
+    for (row = 0; row < raw_height; row++)
+    {
+      checkCancel();
+      if ((row & 31) == 0)
+      {
+        fseek(ifp, strip[row >> 5], SEEK_SET);
+        getbits(-1);
+        pi = 0;
+      }
+      for (col = 0; col < raw_width; col++)
+      {
+        chess = (row + col) & 1;
+        pi1 = chess ? pi - 2 : pi - raw_width - 1;
+        pi2 = chess ? pi - 2 * raw_width : pi - raw_width + 1;
+        if (col <= chess)
+          pi1 = -1;
+        if (pi1 < 0)
+          pi1 = pi2;
+        if (pi2 < 0)
+          pi2 = pi1;
+        if (pi1 < 0 && col > 1)
+          pi1 = pi2 = pi - 2;
+        pred = (pi1 < 0) ? 0 : (pixel[pi1] + pixel[pi2]) >> 1;
+        pixel[pi] = val = pred + ljpeg_diff(huff[chess]);
+        if (val >> 8)
+          derror();
+        val = curve[pixel[pi++]];
+        RAW(row, col) = val;
+      }
+    }
+  }
+  catch (...)
+  {
+    free(pixel);
+    throw;
+  }
+  free(pixel);
+  FORC(2) free(huff[c]);
+}
+
+int LibRaw::kodak_65000_decode(short *out, int bsize)
+{
+  uchar c, blen[768];
+  ushort raw[6];
+  INT64 bitbuf = 0;
+  int save, bits = 0, i, j, len, diff;
+
+  save = ftell(ifp);
+  bsize = (bsize + 3) & -4;
+  for (i = 0; i < bsize; i += 2)
+  {
+    c = fgetc(ifp);
+    if ((blen[i] = c & 15) > 12 || (blen[i + 1] = c >> 4) > 12)
+    {
+      fseek(ifp, save, SEEK_SET);
+      for (i = 0; i < bsize; i += 8)
+      {
+        read_shorts(raw, 6);
+        out[i] = raw[0] >> 12 << 8 | raw[2] >> 12 << 4 | raw[4] >> 12;
+        out[i + 1] = raw[1] >> 12 << 8 | raw[3] >> 12 << 4 | raw[5] >> 12;
+        for (j = 0; j < 6; j++)
+          out[i + 2 + j] = raw[j] & 0xfff;
+      }
+      return 1;
+    }
+  }
+  if ((bsize & 7) == 4)
+  {
+    bitbuf = fgetc(ifp) << 8;
+    bitbuf += fgetc(ifp);
+    bits = 16;
+  }
+  for (i = 0; i < bsize; i++)
+  {
+    len = blen[i];
+    if (bits < len)
+    {
+      for (j = 0; j < 32; j += 8)
+        bitbuf += (INT64)fgetc(ifp) << (bits + (j ^ 8));
+      bits += 32;
+    }
+    diff = bitbuf & (0xffff >> (16 - len));
+    bitbuf >>= len;
+    bits -= len;
+    if (len > 0 && (diff & (1 << (len - 1))) == 0)
+      diff -= (1 << len) - 1;
+    out[i] = diff;
+  }
+  return 0;
+}
+
+void LibRaw::kodak_65000_load_raw()
+{
+  short buf[272]; /* 264 looks enough */
+  int row, col, len, pred[2], ret, i;
+
+  for (row = 0; row < height; row++)
+  {
+    checkCancel();
+    for (col = 0; col < width; col += 256)
+    {
+      pred[0] = pred[1] = 0;
+      len = MIN(256, width - col);
+      ret = kodak_65000_decode(buf, len);
+      for (i = 0; i < len; i++)
+      {
+        int idx = ret ? buf[i] : (pred[i & 1] += buf[i]);
+        if (idx >= 0 && idx < 0xffff)
+        {
+          if ((RAW(row, col + i) = curve[idx]) >> 12)
+            derror();
+        }
+        else
+          derror();
+      }
+    }
+  }
+}
+
+void LibRaw::kodak_ycbcr_load_raw()
+{
+  if (!image)
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+  short buf[384], *bp;
+  int row, col, len, c, i, j, k, y[2][2], cb, cr, rgb[3];
+  ushort *ip;
+
+  unsigned int bits =
+      (load_flags && load_flags > 9 && load_flags < 17) ? load_flags : 10;
+  for (row = 0; row < height; row += 2)
+  {
+    checkCancel();
+    for (col = 0; col < width; col += 128)
+    {
+      len = MIN(128, width - col);
+      kodak_65000_decode(buf, len * 3);
+      y[0][1] = y[1][1] = cb = cr = 0;
+      for (bp = buf, i = 0; i < len; i += 2, bp += 2)
+      {
+        cb += bp[4];
+        cr += bp[5];
+        rgb[1] = -((cb + cr + 2) >> 2);
+        rgb[2] = rgb[1] + cb;
+        rgb[0] = rgb[1] + cr;
+        for (j = 0; j < 2; j++)
+          for (k = 0; k < 2; k++)
+          {
+            if ((y[j][k] = y[j][k ^ 1] + *bp++) >> bits)
+              derror();
+            ip = image[(row + j) * width + col + i + k];
+            FORC3 ip[c] = curve[LIM(y[j][k] + rgb[c], 0, 0xfff)];
+          }
+      }
+    }
+  }
+}
+
+void LibRaw::kodak_rgb_load_raw()
+{
+  if (!image)
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+  short buf[768], *bp;
+  int row, col, len, c, i, rgb[3], ret;
+  ushort *ip = image[0];
+
+  for (row = 0; row < height; row++)
+  {
+    checkCancel();
+    for (col = 0; col < width; col += 256)
+    {
+      len = MIN(256, width - col);
+      ret = kodak_65000_decode(buf, len * 3);
+      memset(rgb, 0, sizeof rgb);
+      for (bp = buf, i = 0; i < len; i++, ip += 4)
+        if (load_flags == 12)
+          FORC3 ip[c] = ret ? (*bp++) : (rgb[c] += *bp++);
+        else
+          FORC3 if ((ip[c] = ret ? (*bp++) : (rgb[c] += *bp++)) >> 12) derror();
+    }
+  }
+}
+
+void LibRaw::kodak_thumb_load_raw()
+{
+  if (!image)
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+  int row, col;
+  colors = thumb_misc >> 5;
+  for (row = 0; row < height; row++)
+    for (col = 0; col < width; col++)
+      read_shorts(image[row * width + col], colors);
+  maximum = (1 << (thumb_misc & 31)) - 1;
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/decoders/load_mfbacks.cpp libkdcraw/libkdcraw/libraw/src/decoders/load_mfbacks.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/decoders/load_mfbacks.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/decoders/load_mfbacks.cpp	2022-11-07 07:46:31.730795008 +0300
@@ -0,0 +1,770 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+int LibRaw::p1raw(unsigned row, unsigned col)
+{
+  return (row < raw_height && col < raw_width) ? RAW(row, col) : 0;
+}
+
+void LibRaw::phase_one_flat_field(int is_float, int nc)
+{
+  ushort head[8];
+  unsigned wide, high, y, x, c, rend, cend, row, col;
+  float *mrow, num, mult[4];
+
+  read_shorts(head, 8);
+  if (head[2] == 0 || head[3] == 0 || head[4] == 0 || head[5] == 0)
+    return;
+  wide = head[2] / head[4] + (head[2] % head[4] != 0);
+  high = head[3] / head[5] + (head[3] % head[5] != 0);
+  mrow = (float *)calloc(nc * wide, sizeof *mrow);
+  merror(mrow, "phase_one_flat_field()");
+  for (y = 0; y < high; y++)
+  {
+    checkCancel();
+    for (x = 0; x < wide; x++)
+      for (c = 0; c < (unsigned)nc; c += 2)
+      {
+        num = is_float ? getreal(LIBRAW_EXIFTAG_TYPE_FLOAT) : get2() / 32768.0;
+        if (y == 0)
+          mrow[c * wide + x] = num;
+        else
+          mrow[(c + 1) * wide + x] = (num - mrow[c * wide + x]) / head[5];
+      }
+    if (y == 0)
+      continue;
+    rend = head[1] + y * head[5];
+    for (row = rend - head[5];
+         row < raw_height && row < rend && row < unsigned(head[1] + head[3] - head[5]);
+         row++)
+    {
+      for (x = 1; x < wide; x++)
+      {
+        for (c = 0; c < (unsigned)nc; c += 2)
+        {
+          mult[c] = mrow[c * wide + x - 1];
+          mult[c + 1] = (mrow[c * wide + x] - mult[c]) / head[4];
+        }
+        cend = head[0] + x * head[4];
+        for (col = cend - head[4];
+             col < raw_width && col < cend && col < unsigned(head[0] + head[2] - head[4]);
+             col++)
+        {
+          c = nc > 2 ? FC(row - top_margin, col - left_margin) : 0;
+          if (!(c & 1))
+          {
+            c = RAW(row, col) * mult[c];
+            RAW(row, col) = LIM(c, 0, 65535);
+          }
+          for (c = 0; c < (unsigned)nc; c += 2)
+            mult[c] += mult[c + 1];
+        }
+      }
+      for (x = 0; x < wide; x++)
+        for (c = 0; c < (unsigned)nc; c += 2)
+          mrow[c * wide + x] += mrow[(c + 1) * wide + x];
+    }
+  }
+  free(mrow);
+}
+
+int LibRaw::phase_one_correct()
+{
+  unsigned entries, tag, data, save, col, row, type;
+  int len, i, j, k, cip, val[4], dev[4], sum, max;
+  int head[9], diff, mindiff = INT_MAX, off_412 = 0;
+  /* static */ const signed char dir[12][2] = {
+      {-1, -1}, {-1, 1}, {1, -1},  {1, 1},  {-2, 0}, {0, -2},
+      {0, 2},   {2, 0},  {-2, -2}, {-2, 2}, {2, -2}, {2, 2}};
+  float poly[8], num, cfrac, frac, mult[2], *yval[2] = {NULL, NULL};
+  ushort *xval[2];
+  int qmult_applied = 0, qlin_applied = 0;
+
+  if (!meta_length)
+    return 0;
+  fseek(ifp, meta_offset, SEEK_SET);
+  order = get2();
+  fseek(ifp, 6, SEEK_CUR);
+  fseek(ifp, meta_offset + get4(), SEEK_SET);
+  entries = get4();
+  get4();
+
+  try
+  {
+    while (entries--)
+    {
+      checkCancel();
+      tag = get4();
+      len = get4();
+      data = get4();
+      save = ftell(ifp);
+      fseek(ifp, meta_offset + data, SEEK_SET);
+      if (tag == 0x0419)
+      { /* Polynomial curve */
+        for (get4(), i = 0; i < 8; i++)
+          poly[i] = getreal(LIBRAW_EXIFTAG_TYPE_FLOAT);
+        poly[3] += (ph1.tag_210 - poly[7]) * poly[6] + 1;
+        for (i = 0; i < 0x10000; i++)
+        {
+          num = (poly[5] * i + poly[3]) * i + poly[1];
+          curve[i] = LIM(num, 0, 65535);
+        }
+        goto apply; /* apply to right half */
+      }
+      else if (tag == 0x041a)
+      { /* Polynomial curve */
+        for (i = 0; i < 4; i++)
+          poly[i] = getreal(LIBRAW_EXIFTAG_TYPE_FLOAT);
+        for (i = 0; i < 0x10000; i++)
+        {
+          for (num = 0, j = 4; j--;)
+            num = num * i + poly[j];
+          curve[i] = LIM(num + i, 0, 65535);
+        }
+      apply: /* apply to whole image */
+        for (row = 0; row < raw_height; row++)
+        {
+          checkCancel();
+          for (col = (tag & 1) * ph1.split_col; col < raw_width; col++)
+            RAW(row, col) = curve[RAW(row, col)];
+        }
+      }
+      else if (tag == 0x0400)
+      { /* Sensor defects */
+        while ((len -= 8) >= 0)
+        {
+          col = get2();
+          row = get2();
+          type = get2();
+          get2();
+          if (col >= raw_width)
+            continue;
+          if (type == 131 || type == 137) /* Bad column */
+            for (row = 0; row < raw_height; row++)
+              if (FC(row - top_margin, col - left_margin) == 1)
+              {
+                for (sum = i = 0; i < 4; i++)
+                  sum += val[i] = p1raw(row + dir[i][0], col + dir[i][1]);
+                for (max = i = 0; i < 4; i++)
+                {
+                  dev[i] = abs((val[i] << 2) - sum);
+                  if (dev[max] < dev[i])
+                    max = i;
+                }
+                RAW(row, col) = (sum - val[max]) / 3.0 + 0.5;
+              }
+              else
+              {
+                for (sum = 0, i = 8; i < 12; i++)
+                  sum += p1raw(row + dir[i][0], col + dir[i][1]);
+                RAW(row, col) =
+                    0.5 + sum * 0.0732233 +
+                    (p1raw(row, col - 2) + p1raw(row, col + 2)) * 0.3535534;
+              }
+          else if (type == 129)
+          { /* Bad pixel */
+            if (row >= raw_height)
+              continue;
+            j = (FC(row - top_margin, col - left_margin) != 1) * 4;
+            for (sum = 0, i = j; i < j + 8; i++)
+              sum += p1raw(row + dir[i][0], col + dir[i][1]);
+            RAW(row, col) = (sum + 4) >> 3;
+          }
+        }
+      }
+      else if (tag == 0x0401)
+      { /* All-color flat fields */
+        phase_one_flat_field(1, 2);
+      }
+      else if (tag == 0x0416 || tag == 0x0410)
+      {
+        phase_one_flat_field(0, 2);
+      }
+      else if (tag == 0x040b)
+      { /* Red+blue flat field */
+        phase_one_flat_field(0, 4);
+      }
+      else if (tag == 0x0412)
+      {
+        fseek(ifp, 36, SEEK_CUR);
+        diff = abs(get2() - ph1.tag_21a);
+        if (mindiff > diff)
+        {
+          mindiff = diff;
+          off_412 = ftell(ifp) - 38;
+        }
+      }
+      else if (tag == 0x041f && !qlin_applied)
+      { /* Quadrant linearization */
+        ushort lc[2][2][16], ref[16];
+        int qr, qc;
+        for (qr = 0; qr < 2; qr++)
+          for (qc = 0; qc < 2; qc++)
+            for (i = 0; i < 16; i++)
+              lc[qr][qc][i] = get4();
+        for (i = 0; i < 16; i++)
+        {
+          int v = 0;
+          for (qr = 0; qr < 2; qr++)
+            for (qc = 0; qc < 2; qc++)
+              v += lc[qr][qc][i];
+          ref[i] = (v + 2) >> 2;
+        }
+        for (qr = 0; qr < 2; qr++)
+        {
+          for (qc = 0; qc < 2; qc++)
+          {
+            int cx[19], cf[19];
+            for (i = 0; i < 16; i++)
+            {
+              cx[1 + i] = lc[qr][qc][i];
+              cf[1 + i] = ref[i];
+            }
+            cx[0] = cf[0] = 0;
+            cx[17] = cf[17] = ((unsigned int)ref[15] * 65535) / lc[qr][qc][15];
+            cf[18] = cx[18] = 65535;
+            cubic_spline(cx, cf, 19);
+
+            for (row = (qr ? ph1.split_row : 0);
+                 row < unsigned(qr ? raw_height : ph1.split_row); row++)
+            {
+              checkCancel();
+              for (col = (qc ? ph1.split_col : 0);
+                   col < unsigned(qc ? raw_width : ph1.split_col); col++)
+                RAW(row, col) = curve[RAW(row, col)];
+            }
+          }
+        }
+        qlin_applied = 1;
+      }
+      else if (tag == 0x041e && !qmult_applied)
+      { /* Quadrant multipliers */
+        float qmult[2][2] = {{1, 1}, {1, 1}};
+        get4();
+        get4();
+        get4();
+        get4();
+        qmult[0][0] = 1.0 + getreal(LIBRAW_EXIFTAG_TYPE_FLOAT);
+        get4();
+        get4();
+        get4();
+        get4();
+        get4();
+        qmult[0][1] = 1.0 + getreal(LIBRAW_EXIFTAG_TYPE_FLOAT);
+        get4();
+        get4();
+        get4();
+        qmult[1][0] = 1.0 + getreal(LIBRAW_EXIFTAG_TYPE_FLOAT);
+        get4();
+        get4();
+        get4();
+        qmult[1][1] = 1.0 + getreal(LIBRAW_EXIFTAG_TYPE_FLOAT);
+        for (row = 0; row < raw_height; row++)
+        {
+          checkCancel();
+          for (col = 0; col < raw_width; col++)
+          {
+            i = qmult[row >= (unsigned)ph1.split_row][col >= (unsigned)ph1.split_col] *
+                RAW(row, col);
+            RAW(row, col) = LIM(i, 0, 65535);
+          }
+        }
+        qmult_applied = 1;
+      }
+      else if (tag == 0x0431 && !qmult_applied)
+      { /* Quadrant combined */
+        ushort lc[2][2][7], ref[7];
+        int qr, qc;
+        for (i = 0; i < 7; i++)
+          ref[i] = get4();
+        for (qr = 0; qr < 2; qr++)
+          for (qc = 0; qc < 2; qc++)
+            for (i = 0; i < 7; i++)
+              lc[qr][qc][i] = get4();
+        for (qr = 0; qr < 2; qr++)
+        {
+          for (qc = 0; qc < 2; qc++)
+          {
+            int cx[9], cf[9];
+            for (i = 0; i < 7; i++)
+            {
+              cx[1 + i] = ref[i];
+              cf[1 + i] = ((unsigned)ref[i] * lc[qr][qc][i]) / 10000;
+            }
+            cx[0] = cf[0] = 0;
+            cx[8] = cf[8] = 65535;
+            cubic_spline(cx, cf, 9);
+            for (row = (qr ? ph1.split_row : 0);
+                 row < unsigned(qr ? raw_height : ph1.split_row); row++)
+            {
+              checkCancel();
+              for (col = (qc ? ph1.split_col : 0);
+                   col < unsigned(qc ? raw_width : ph1.split_col); col++)
+                RAW(row, col) = curve[RAW(row, col)];
+            }
+          }
+        }
+        qmult_applied = 1;
+        qlin_applied = 1;
+      }
+      fseek(ifp, save, SEEK_SET);
+    }
+    if (off_412)
+    {
+      fseek(ifp, off_412, SEEK_SET);
+      for (i = 0; i < 9; i++)
+        head[i] = get4() & 0x7fff;
+      yval[0] = (float *)calloc(head[1] * head[3] + head[2] * head[4], 6);
+      merror(yval[0], "phase_one_correct()");
+      yval[1] = (float *)(yval[0] + head[1] * head[3]);
+      xval[0] = (ushort *)(yval[1] + head[2] * head[4]);
+      xval[1] = (ushort *)(xval[0] + head[1] * head[3]);
+      get2();
+      for (i = 0; i < 2; i++)
+        for (j = 0; j < head[i + 1] * head[i + 3]; j++)
+          yval[i][j] = getreal(LIBRAW_EXIFTAG_TYPE_FLOAT);
+      for (i = 0; i < 2; i++)
+        for (j = 0; j < head[i + 1] * head[i + 3]; j++)
+          xval[i][j] = get2();
+      for (row = 0; row < raw_height; row++)
+      {
+        checkCancel();
+        for (col = 0; col < raw_width; col++)
+        {
+          cfrac = (float)col * head[3] / raw_width;
+          cfrac -= cip = cfrac;
+          num = RAW(row, col) * 0.5;
+          for (i = cip; i < cip + 2; i++)
+          {
+            for (k = j = 0; j < head[1]; j++)
+              if (num < xval[0][k = head[1] * i + j])
+                break;
+            frac = (j == 0 || j == head[1])
+                       ? 0
+                       : (xval[0][k] - num) / (xval[0][k] - xval[0][k - 1]);
+            mult[i - cip] = yval[0][k - 1] * frac + yval[0][k] * (1 - frac);
+          }
+          i = ((mult[0] * (1 - cfrac) + mult[1] * cfrac) * row + num) * 2;
+          RAW(row, col) = LIM(i, 0, 65535);
+        }
+      }
+      free(yval[0]);
+    }
+  }
+  catch (...)
+  {
+    if (yval[0])
+      free(yval[0]);
+    return LIBRAW_CANCELLED_BY_CALLBACK;
+  }
+  return 0;
+}
+
+void LibRaw::phase_one_load_raw()
+{
+  int a, b, i;
+  ushort akey, bkey, t_mask;
+
+  fseek(ifp, ph1.key_off, SEEK_SET);
+  akey = get2();
+  bkey = get2();
+  t_mask = ph1.format == 1 ? 0x5555 : 0x1354;
+  if (ph1.black_col || ph1.black_row)
+  {
+    imgdata.rawdata.ph1_cblack =
+        (short(*)[2])calloc(raw_height * 2, sizeof(ushort));
+    merror(imgdata.rawdata.ph1_cblack, "phase_one_load_raw()");
+    imgdata.rawdata.ph1_rblack =
+        (short(*)[2])calloc(raw_width * 2, sizeof(ushort));
+    merror(imgdata.rawdata.ph1_rblack, "phase_one_load_raw()");
+    if (ph1.black_col)
+    {
+      fseek(ifp, ph1.black_col, SEEK_SET);
+      read_shorts((ushort *)imgdata.rawdata.ph1_cblack[0], raw_height * 2);
+    }
+    if (ph1.black_row)
+    {
+      fseek(ifp, ph1.black_row, SEEK_SET);
+      read_shorts((ushort *)imgdata.rawdata.ph1_rblack[0], raw_width * 2);
+    }
+  }
+  fseek(ifp, data_offset, SEEK_SET);
+  read_shorts(raw_image, raw_width * raw_height);
+  if (ph1.format)
+    for (i = 0; i < raw_width * raw_height; i += 2)
+    {
+      a = raw_image[i + 0] ^ akey;
+      b = raw_image[i + 1] ^ bkey;
+      raw_image[i + 0] = (a & t_mask) | (b & ~t_mask);
+      raw_image[i + 1] = (b & t_mask) | (a & ~t_mask);
+    }
+}
+
+unsigned LibRaw::ph1_bithuff(int nbits, ushort *huff)
+{
+#ifndef LIBRAW_NOTHREADS
+#define bitbuf tls->ph1_bits.bitbuf
+#define vbits tls->ph1_bits.vbits
+#else
+  static UINT64 bitbuf = 0;
+  static int vbits = 0;
+#endif
+  unsigned c;
+
+  if (nbits == -1)
+    return bitbuf = vbits = 0;
+  if (nbits == 0)
+    return 0;
+  if (vbits < nbits)
+  {
+    bitbuf = bitbuf << 32 | get4();
+    vbits += 32;
+  }
+  c = bitbuf << (64 - vbits) >> (64 - nbits);
+  if (huff)
+  {
+    vbits -= huff[c] >> 8;
+    return (uchar)huff[c];
+  }
+  vbits -= nbits;
+  return c;
+#ifndef LIBRAW_NOTHREADS
+#undef bitbuf
+#undef vbits
+#endif
+}
+
+void LibRaw::phase_one_load_raw_c()
+{
+  static const int length[] = {8, 7, 6, 9, 11, 10, 5, 12, 14, 13};
+  int *offset, len[2], pred[2], row, col, i, j;
+  ushort *pixel;
+  short(*c_black)[2], (*r_black)[2];
+  if (ph1.format == 6)
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+
+  pixel = (ushort *)calloc(raw_width * 3 + raw_height * 4, 2);
+  merror(pixel, "phase_one_load_raw_c()");
+  offset = (int *)(pixel + raw_width);
+  fseek(ifp, strip_offset, SEEK_SET);
+  for (row = 0; row < raw_height; row++)
+    offset[row] = get4();
+  c_black = (short(*)[2])(offset + raw_height);
+  fseek(ifp, ph1.black_col, SEEK_SET);
+  if (ph1.black_col)
+    read_shorts((ushort *)c_black[0], raw_height * 2);
+  r_black = c_black + raw_height;
+  fseek(ifp, ph1.black_row, SEEK_SET);
+  if (ph1.black_row)
+    read_shorts((ushort *)r_black[0], raw_width * 2);
+
+  // Copy data to internal copy (ever if not read)
+  if (ph1.black_col || ph1.black_row)
+  {
+    imgdata.rawdata.ph1_cblack =
+        (short(*)[2])calloc(raw_height * 2, sizeof(ushort));
+    merror(imgdata.rawdata.ph1_cblack, "phase_one_load_raw_c()");
+    memmove(imgdata.rawdata.ph1_cblack, (ushort *)c_black[0],
+            raw_height * 2 * sizeof(ushort));
+    imgdata.rawdata.ph1_rblack =
+        (short(*)[2])calloc(raw_width * 2, sizeof(ushort));
+    merror(imgdata.rawdata.ph1_rblack, "phase_one_load_raw_c()");
+    memmove(imgdata.rawdata.ph1_rblack, (ushort *)r_black[0],
+            raw_width * 2 * sizeof(ushort));
+  }
+
+  for (i = 0; i < 256; i++)
+    curve[i] = i * i / 3.969 + 0.5;
+  try
+  {
+    for (row = 0; row < raw_height; row++)
+    {
+      checkCancel();
+      fseek(ifp, data_offset + offset[row], SEEK_SET);
+      ph1_bits(-1);
+      pred[0] = pred[1] = 0;
+      for (col = 0; col < raw_width; col++)
+      {
+        if (col >= (raw_width & -8))
+          len[0] = len[1] = 14;
+        else if ((col & 7) == 0)
+          for (i = 0; i < 2; i++)
+          {
+            for (j = 0; j < 5 && !ph1_bits(1); j++)
+              ;
+            if (j--)
+              len[i] = length[j * 2 + ph1_bits(1)];
+          }
+        if ((i = len[col & 1]) == 14)
+          pixel[col] = pred[col & 1] = ph1_bits(16);
+        else
+          pixel[col] = pred[col & 1] += ph1_bits(i) + 1 - (1 << (i - 1));
+        if (pred[col & 1] >> 16)
+          derror();
+        if (ph1.format == 5 && pixel[col] < 256)
+          pixel[col] = curve[pixel[col]];
+      }
+      if (ph1.format == 8)
+        memmove(&RAW(row, 0), &pixel[0], raw_width * 2);
+      else
+        for (col = 0; col < raw_width; col++)
+          RAW(row, col) = pixel[col] << 2;
+    }
+  }
+  catch (...)
+  {
+    free(pixel);
+    throw;
+  }
+  free(pixel);
+  maximum = 0xfffc - ph1.t_black;
+}
+
+void LibRaw::hasselblad_load_raw()
+{
+  struct jhead jh;
+  int shot, row, col, *back[5], len[2], diff[12], pred, sh, f, c;
+  unsigned s;
+  unsigned upix, urow, ucol;
+  ushort *ip;
+
+  if (!ljpeg_start(&jh, 0))
+    return;
+  order = 0x4949;
+  ph1_bits(-1);
+  try
+  {
+    back[4] = (int *)calloc(raw_width, 3 * sizeof **back);
+    merror(back[4], "hasselblad_load_raw()");
+    FORC3 back[c] = back[4] + c * raw_width;
+    cblack[6] >>= sh = tiff_samples > 1;
+    shot = LIM(shot_select, 1, tiff_samples) - 1;
+    for (row = 0; row < raw_height; row++)
+    {
+      checkCancel();
+      FORC4 back[(c + 3) & 3] = back[c];
+      for (col = 0; col < raw_width; col += 2)
+      {
+        for (s = 0; s < tiff_samples * 2; s += 2)
+        {
+          FORC(2) len[c] = ph1_huff(jh.huff[0]);
+          FORC(2)
+          {
+            diff[s + c] = ph1_bits(len[c]);
+            if (len[c] > 0 && (diff[s + c] & (1 << (len[c] - 1))) == 0)
+              diff[s + c] -= (1 << len[c]) - 1;
+            if (diff[s + c] == 65535)
+              diff[s + c] = -32768;
+          }
+        }
+        for (s = col; s < unsigned(col + 2); s++)
+        {
+          pred = 0x8000 + load_flags;
+          if (col)
+            pred = back[2][s - 2];
+          if (col && row > 1)
+            switch (jh.psv)
+            {
+            case 11:
+              pred += back[0][s] / 2 - back[0][s - 2] / 2;
+              break;
+            }
+          f = (row & 1) * 3 ^ ((col + s) & 1);
+          FORC(int(tiff_samples))
+          {
+            pred += diff[(s & 1) * tiff_samples + c];
+            upix = pred >> sh & 0xffff;
+            if (raw_image && c == shot)
+              RAW(row, s) = upix;
+            if (image)
+            {
+              urow = row - top_margin + (c & 1);
+              ucol = col - left_margin - ((c >> 1) & 1);
+              ip = &image[urow * width + ucol][f];
+              if (urow < height && ucol < width)
+                *ip = c < 4 ? upix : (*ip + upix) >> 1;
+            }
+          }
+          back[2][s] = pred;
+        }
+      }
+    }
+  }
+  catch (...)
+  {
+    free(back[4]);
+    ljpeg_end(&jh);
+    throw;
+  }
+  free(back[4]);
+  ljpeg_end(&jh);
+  if (image)
+    mix_green = 1;
+}
+
+void LibRaw::leaf_hdr_load_raw()
+{
+  ushort *pixel = 0;
+  unsigned tile = 0, r, c, row, col;
+
+  if (!filters || !raw_image)
+  {
+    if (!image)
+      throw LIBRAW_EXCEPTION_IO_CORRUPT;
+    pixel = (ushort *)calloc(raw_width, sizeof *pixel);
+    merror(pixel, "leaf_hdr_load_raw()");
+  }
+  try
+  {
+    FORC(tiff_samples)
+    for (r = 0; r < raw_height; r++)
+    {
+      checkCancel();
+      if (r % tile_length == 0)
+      {
+        fseek(ifp, data_offset + 4 * tile++, SEEK_SET);
+        fseek(ifp, get4(), SEEK_SET);
+      }
+      if (filters && c != shot_select)
+        continue;
+      if (filters && raw_image)
+        pixel = raw_image + r * raw_width;
+      read_shorts(pixel, raw_width);
+      if (!filters && image && (row = r - top_margin) < height)
+        for (col = 0; col < width && col + left_margin < raw_width; col++)
+          image[row * width + col][c] = pixel[col + left_margin];
+    }
+  }
+  catch (...)
+  {
+    if (!filters)
+      free(pixel);
+    throw;
+  }
+  if (!filters)
+  {
+    maximum = 0xffff;
+    raw_color = 1;
+    free(pixel);
+  }
+}
+
+void LibRaw::unpacked_load_raw_FujiDBP()
+/*
+for Fuji DBP for GX680, aka DX-2000
+  DBP_tile_width = 688;
+  DBP_tile_height = 3856;
+  DBP_n_tiles = 8;
+*/
+{
+  int scan_line, tile_n;
+  int nTiles;
+
+  nTiles = 8;
+  tile_width = raw_width / nTiles;
+
+  ushort *tile;
+  tile = (ushort *)calloc(raw_height, tile_width * 2);
+
+  for (tile_n = 0; tile_n < nTiles; tile_n++)
+  {
+    read_shorts(tile, tile_width * raw_height);
+    for (scan_line = 0; scan_line < raw_height; scan_line++)
+    {
+      memcpy(&raw_image[scan_line * raw_width + tile_n * tile_width],
+             &tile[scan_line * tile_width], tile_width * 2);
+    }
+  }
+  free(tile);
+  fseek(ifp, -2, SEEK_CUR); // avoid EOF error
+}
+
+void LibRaw::sinar_4shot_load_raw()
+{
+  ushort *pixel;
+  unsigned shot, row, col, r, c;
+
+  if (raw_image)
+  {
+    shot = LIM(shot_select, 1, 4) - 1;
+    fseek(ifp, data_offset + shot * 4, SEEK_SET);
+    fseek(ifp, get4(), SEEK_SET);
+    unpacked_load_raw();
+    return;
+  }
+  if (!image)
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+  pixel = (ushort *)calloc(raw_width, sizeof *pixel);
+  merror(pixel, "sinar_4shot_load_raw()");
+  try
+  {
+    for (shot = 0; shot < 4; shot++)
+    {
+      checkCancel();
+      fseek(ifp, data_offset + shot * 4, SEEK_SET);
+      fseek(ifp, get4(), SEEK_SET);
+      for (row = 0; row < raw_height; row++)
+      {
+        read_shorts(pixel, raw_width);
+        if ((r = row - top_margin - (shot >> 1 & 1)) >= height)
+          continue;
+        for (col = 0; col < raw_width; col++)
+        {
+          if ((c = col - left_margin - (shot & 1)) >= width)
+            continue;
+          image[r * width + c][(row & 1) * 3 ^ (~col & 1)] = pixel[col];
+        }
+      }
+    }
+  }
+  catch (...)
+  {
+    free(pixel);
+    throw;
+  }
+  free(pixel);
+  mix_green = 1;
+}
+
+void LibRaw::imacon_full_load_raw()
+{
+  if (!image)
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+  int row, col;
+
+  unsigned short *buf =
+      (unsigned short *)malloc(width * 3 * sizeof(unsigned short));
+  merror(buf, "imacon_full_load_raw");
+
+  for (row = 0; row < height; row++)
+  {
+    checkCancel();
+    read_shorts(buf, width * 3);
+    unsigned short(*rowp)[4] = &image[row * width];
+    for (col = 0; col < width; col++)
+    {
+      rowp[col][0] = buf[col * 3];
+      rowp[col][1] = buf[col * 3 + 1];
+      rowp[col][2] = buf[col * 3 + 2];
+      rowp[col][3] = 0;
+    }
+  }
+  free(buf);
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/decoders/smal.cpp libkdcraw/libkdcraw/libraw/src/decoders/smal.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/decoders/smal.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/decoders/smal.cpp	2022-11-07 07:46:31.730795008 +0300
@@ -0,0 +1,181 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+#define HOLE(row) ((holes >> (((row)-raw_height) & 7)) & 1)
+
+/* Kudos to Rich Taylor for figuring out SMaL's compression algorithm. */
+void LibRaw::smal_decode_segment(unsigned seg[2][2], int holes)
+{
+  uchar hist[3][13] = {{7, 7, 0, 0, 63, 55, 47, 39, 31, 23, 15, 7, 0},
+                       {7, 7, 0, 0, 63, 55, 47, 39, 31, 23, 15, 7, 0},
+                       {3, 3, 0, 0, 63, 47, 31, 15, 0}};
+  int low, high = 0xff, carry = 0, nbits = 8;
+  int s, count, bin, next, i, sym[3];
+  unsigned pix;
+  uchar diff, pred[] = {0, 0};
+  ushort data = 0, range = 0;
+
+  fseek(ifp, seg[0][1] + 1, SEEK_SET);
+  getbits(-1);
+  if (seg[1][0] > raw_width * raw_height)
+    seg[1][0] = raw_width * raw_height;
+  for (pix = seg[0][0]; pix < seg[1][0]; pix++)
+  {
+    for (s = 0; s < 3; s++)
+    {
+      data = data << nbits | getbits(nbits);
+      if (carry < 0)
+        carry = (nbits += carry + 1) < 1 ? nbits - 1 : 0;
+      while (--nbits >= 0)
+        if ((data >> nbits & 0xff) == 0xff)
+          break;
+      if (nbits > 0)
+        data =
+            ((data & ((1 << (nbits - 1)) - 1)) << 1) |
+            ((data + (((data & (1 << (nbits - 1)))) << 1)) & ((~0u) << nbits));
+      if (nbits >= 0)
+      {
+        data += getbits(1);
+        carry = nbits - 8;
+      }
+      count = ((((data - range + 1) & 0xffff) << 2) - 1) / (high >> 4);
+      for (bin = 0; hist[s][bin + 5] > count; bin++)
+        ;
+      low = hist[s][bin + 5] * (high >> 4) >> 2;
+      if (bin)
+        high = hist[s][bin + 4] * (high >> 4) >> 2;
+      high -= low;
+      for (nbits = 0; high << nbits < 128; nbits++)
+        ;
+      range = (range + low) << nbits;
+      high <<= nbits;
+      next = hist[s][1];
+      if (++hist[s][2] > hist[s][3])
+      {
+        next = (next + 1) & hist[s][0];
+        hist[s][3] = (hist[s][next + 4] - hist[s][next + 5]) >> 2;
+        hist[s][2] = 1;
+      }
+      if (hist[s][hist[s][1] + 4] - hist[s][hist[s][1] + 5] > 1)
+      {
+        if (bin < hist[s][1])
+          for (i = bin; i < hist[s][1]; i++)
+            hist[s][i + 5]--;
+        else if (next <= bin)
+          for (i = hist[s][1]; i < bin; i++)
+            hist[s][i + 5]++;
+      }
+      hist[s][1] = next;
+      sym[s] = bin;
+    }
+    diff = sym[2] << 5 | sym[1] << 2 | (sym[0] & 3);
+    if (sym[0] & 4)
+      diff = diff ? -diff : 0x80;
+    if (ftell(ifp) + 12 >= seg[1][1])
+      diff = 0;
+    if (pix >= raw_width * raw_height)
+      throw LIBRAW_EXCEPTION_IO_CORRUPT;
+    raw_image[pix] = pred[pix & 1] += diff;
+    if (!(pix & 1) && HOLE(pix / raw_width))
+      pix += 2;
+  }
+  maximum = 0xff;
+}
+
+void LibRaw::smal_v6_load_raw()
+{
+  unsigned seg[2][2];
+
+  fseek(ifp, 16, SEEK_SET);
+  seg[0][0] = 0;
+  seg[0][1] = get2();
+  seg[1][0] = raw_width * raw_height;
+  seg[1][1] = INT_MAX;
+  smal_decode_segment(seg, 0);
+}
+
+int LibRaw::median4(int *p)
+{
+  int min, max, sum, i;
+
+  min = max = sum = p[0];
+  for (i = 1; i < 4; i++)
+  {
+    sum += p[i];
+    if (min > p[i])
+      min = p[i];
+    if (max < p[i])
+      max = p[i];
+  }
+  return (sum - min - max) >> 1;
+}
+
+void LibRaw::fill_holes(int holes)
+{
+  int row, col, val[4];
+
+  for (row = 2; row < height - 2; row++)
+  {
+    if (!HOLE(row))
+      continue;
+    for (col = 1; col < width - 1; col += 4)
+    {
+      val[0] = RAW(row - 1, col - 1);
+      val[1] = RAW(row - 1, col + 1);
+      val[2] = RAW(row + 1, col - 1);
+      val[3] = RAW(row + 1, col + 1);
+      RAW(row, col) = median4(val);
+    }
+    for (col = 2; col < width - 2; col += 4)
+      if (HOLE(row - 2) || HOLE(row + 2))
+        RAW(row, col) = (RAW(row, col - 2) + RAW(row, col + 2)) >> 1;
+      else
+      {
+        val[0] = RAW(row, col - 2);
+        val[1] = RAW(row, col + 2);
+        val[2] = RAW(row - 2, col);
+        val[3] = RAW(row + 2, col);
+        RAW(row, col) = median4(val);
+      }
+  }
+}
+
+void LibRaw::smal_v9_load_raw()
+{
+  unsigned seg[256][2], offset, nseg, holes, i;
+
+  fseek(ifp, 67, SEEK_SET);
+  offset = get4();
+  nseg = (uchar)fgetc(ifp);
+  fseek(ifp, offset, SEEK_SET);
+  for (i = 0; i < nseg * 2; i++)
+    ((unsigned *)seg)[i] = get4() + data_offset * (i & 1);
+  fseek(ifp, 78, SEEK_SET);
+  holes = fgetc(ifp);
+  fseek(ifp, 88, SEEK_SET);
+  seg[nseg][0] = raw_height * raw_width;
+  seg[nseg][1] = get4() + data_offset;
+  for (i = 0; i < nseg; i++)
+    smal_decode_segment(seg + i, holes);
+  if (holes)
+    fill_holes(holes);
+}
+
+#undef HOLE
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/decoders/unpack.cpp libkdcraw/libkdcraw/libraw/src/decoders/unpack.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/decoders/unpack.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/decoders/unpack.cpp	2022-11-07 07:46:31.730795008 +0300
@@ -0,0 +1,365 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+#include "../../internal/libraw_cameraids.h"
+#include "../../internal/libraw_cxx_defs.h"
+
+int LibRaw::unpack(void)
+{
+  CHECK_ORDER_HIGH(LIBRAW_PROGRESS_LOAD_RAW);
+  CHECK_ORDER_LOW(LIBRAW_PROGRESS_IDENTIFY);
+  try
+  {
+
+    if (!libraw_internal_data.internal_data.input)
+      return LIBRAW_INPUT_CLOSED;
+
+    RUN_CALLBACK(LIBRAW_PROGRESS_LOAD_RAW, 0, 2);
+    if (O.shot_select >= P1.raw_count)
+      return LIBRAW_REQUEST_FOR_NONEXISTENT_IMAGE;
+
+    if (!load_raw)
+      return LIBRAW_UNSPECIFIED_ERROR;
+
+    // already allocated ?
+    if (imgdata.image)
+    {
+      free(imgdata.image);
+      imgdata.image = 0;
+    }
+    if (imgdata.rawdata.raw_alloc)
+    {
+      free(imgdata.rawdata.raw_alloc);
+      imgdata.rawdata.raw_alloc = 0;
+    }
+    if (libraw_internal_data.unpacker_data.meta_length)
+    {
+      libraw_internal_data.internal_data.meta_data =
+          (char *)malloc(libraw_internal_data.unpacker_data.meta_length);
+      merror(libraw_internal_data.internal_data.meta_data, "LibRaw::unpack()");
+    }
+
+    libraw_decoder_info_t decoder_info;
+    get_decoder_info(&decoder_info);
+
+    int save_iwidth = S.iwidth, save_iheight = S.iheight,
+        save_shrink = IO.shrink;
+
+    int rwidth = S.raw_width, rheight = S.raw_height;
+    if (!IO.fuji_width)
+    {
+      // adjust non-Fuji allocation
+      if (rwidth < S.width + S.left_margin)
+        rwidth = S.width + S.left_margin;
+      if (rheight < S.height + S.top_margin)
+        rheight = S.height + S.top_margin;
+    }
+    if (rwidth > 65535 ||
+        rheight > 65535) // No way to make image larger than 64k pix
+      throw LIBRAW_EXCEPTION_IO_CORRUPT;
+
+    imgdata.rawdata.raw_image = 0;
+    imgdata.rawdata.color4_image = 0;
+    imgdata.rawdata.color3_image = 0;
+    imgdata.rawdata.float_image = 0;
+    imgdata.rawdata.float3_image = 0;
+
+#ifdef USE_DNGSDK
+    if (imgdata.idata.dng_version && dnghost
+        && libraw_internal_data.unpacker_data.tiff_samples != 2  // Fuji SuperCCD; it is better to detect is more rigid way
+        && valid_for_dngsdk() && load_raw != &LibRaw::pentax_4shot_load_raw)
+    {
+      // Data size check
+      INT64 pixcount =
+          INT64(MAX(S.width, S.raw_width)) * INT64(MAX(S.height, S.raw_height));
+      INT64 planecount =
+          (imgdata.idata.filters || P1.colors == 1) ? 1 : LIM(P1.colors, 3, 4);
+      INT64 samplesize = is_floating_point() ? 4 : 2;
+      INT64 bytes = pixcount * planecount * samplesize;
+      if (bytes > INT64(imgdata.params.max_raw_memory_mb) * INT64(1024 * 1024))
+        throw LIBRAW_EXCEPTION_TOOBIG;
+
+      // find ifd to check sample
+      int rr = try_dngsdk();
+      if (raw_was_read())
+        imgdata.process_warnings |= LIBRAW_WARN_DNGSDK_PROCESSED;
+    }
+#endif
+
+#ifdef USE_RAWSPEED
+    if (!raw_was_read())
+    {
+      int rawspeed_enabled = 1;
+
+      if (imgdata.idata.dng_version && (libraw_internal_data.unpacker_data.tiff_samples == 2 || imgdata.idata.raw_count > 1))
+        rawspeed_enabled = 0;
+
+      if (libraw_internal_data.unpacker_data.is_NikonTransfer)
+        rawspeed_enabled = 0;
+
+      if (libraw_internal_data.unpacker_data.pana_encoding == 5)
+        rawspeed_enabled = 0;
+
+      if (imgdata.idata.raw_count > 1)
+        rawspeed_enabled = 0;
+      if (!strncasecmp(imgdata.idata.software, "Magic", 5))
+        rawspeed_enabled = 0;
+      // Disable rawspeed for double-sized Oly files
+      if (makeIs(LIBRAW_CAMERAMAKER_Olympus) &&
+          ((imgdata.sizes.raw_width > 6000) ||
+           !strncasecmp(imgdata.idata.model, "SH-", 3) ||
+           !strncasecmp(imgdata.idata.model, "TG-", 3) ))
+        rawspeed_enabled = 0;
+
+      if (makeIs(LIBRAW_CAMERAMAKER_Canon) &&
+          (libraw_internal_data.identify_data.unique_id == CanonID_EOS_6D_Mark_II))
+        rawspeed_enabled = 0;
+
+      if (imgdata.idata.dng_version && imgdata.idata.filters == 0 &&
+          libraw_internal_data.unpacker_data.tiff_bps == 8) // Disable for 8 bit
+        rawspeed_enabled = 0;
+
+      if (load_raw == &LibRaw::packed_load_raw &&
+        makeIs(LIBRAW_CAMERAMAKER_Nikon) &&
+          (!strncasecmp(imgdata.idata.model, "E", 1) ||
+           !strncasecmp(imgdata.idata.model, "COOLPIX B", 9) ||
+		   !strncasecmp(imgdata.idata.model, "COOLPIX P9", 10) ||
+           !strncasecmp(imgdata.idata.model, "COOLPIX P1000", 13)))
+        rawspeed_enabled = 0;
+
+	if (load_raw == &LibRaw::lossless_jpeg_load_raw &&
+		imgdata.makernotes.canon.RecordMode && makeIs(LIBRAW_CAMERAMAKER_Kodak) &&
+		/* Not normalized models here, it is intentional */
+		(!strncasecmp(imgdata.idata.model, "EOS D2000", 9) ||
+		 !strncasecmp(imgdata.idata.model, "EOS D6000", 9)))
+	  rawspeed_enabled = 0;
+
+      if (load_raw == &LibRaw::nikon_load_raw &&
+        makeIs(LIBRAW_CAMERAMAKER_Nikon) &&
+          (!strncasecmp(imgdata.idata.model, "Z", 1) || !strncasecmp(imgdata.idata.model,"D780",4)))
+        rawspeed_enabled = 0;
+
+      if (load_raw == &LibRaw::panasonic_load_raw &&
+          libraw_internal_data.unpacker_data.pana_encoding > 4)
+        rawspeed_enabled = 0;
+
+      // RawSpeed Supported,
+      if (O.use_rawspeed && rawspeed_enabled &&
+          !(is_sraw() && (O.raw_processing_options &
+                          (LIBRAW_PROCESSING_SRAW_NO_RGB |
+                           LIBRAW_PROCESSING_SRAW_NO_INTERPOLATE))) &&
+          (decoder_info.decoder_flags & LIBRAW_DECODER_TRYRAWSPEED) &&
+          _rawspeed_camerameta)
+      {
+        INT64 pixcount = INT64(MAX(S.width, S.raw_width)) *
+                         INT64(MAX(S.height, S.raw_height));
+        INT64 planecount = (imgdata.idata.filters || P1.colors == 1)
+                               ? 1
+                               : LIM(P1.colors, 3, 4);
+        INT64 bytes =
+            pixcount * planecount * 2; // sample size is always 2 for rawspeed
+        if (bytes >
+            INT64(imgdata.params.max_raw_memory_mb) * INT64(1024 * 1024))
+          throw LIBRAW_EXCEPTION_TOOBIG;
+
+        int rr = try_rawspeed();
+      }
+    }
+#endif
+    if (!raw_was_read()) // RawSpeed failed or not run
+    {
+      // Not allocated on RawSpeed call, try call LibRaow
+      int zero_rawimage = 0;
+      if (decoder_info.decoder_flags & LIBRAW_DECODER_OWNALLOC)
+      {
+        // x3f foveon decoder and DNG float
+        // Do nothing! Decoder will allocate data internally
+      }
+      if (decoder_info.decoder_flags & LIBRAW_DECODER_SINAR4SHOT)
+      {
+        if (imgdata.params.shot_select) // single image extract
+        {
+          if (INT64(rwidth) * INT64(rheight + 8) *
+                  sizeof(imgdata.rawdata.raw_image[0]) >
+              INT64(imgdata.params.max_raw_memory_mb) * INT64(1024 * 1024))
+            throw LIBRAW_EXCEPTION_TOOBIG;
+          imgdata.rawdata.raw_alloc = malloc(
+              rwidth * (rheight + 8) * sizeof(imgdata.rawdata.raw_image[0]));
+          imgdata.rawdata.raw_image = (ushort *)imgdata.rawdata.raw_alloc;
+          if (!S.raw_pitch)
+            S.raw_pitch = S.raw_width * 2; // Bayer case, not set before
+        }
+        else // Full image extract
+        {
+          if (INT64(rwidth) * INT64(rheight + 8) *
+                  sizeof(imgdata.rawdata.raw_image[0]) * 4 >
+              INT64(imgdata.params.max_raw_memory_mb) * INT64(1024 * 1024))
+            throw LIBRAW_EXCEPTION_TOOBIG;
+          S.raw_pitch = S.raw_width * 8;
+          imgdata.rawdata.raw_alloc = 0;
+          imgdata.image = (ushort(*)[4])calloc(
+              unsigned(MAX(S.width, S.raw_width)) *
+                  unsigned(MAX(S.height, S.raw_height) + 8),
+              sizeof(*imgdata.image));
+        }
+      }
+      else if (decoder_info.decoder_flags & LIBRAW_DECODER_3CHANNEL)
+      {
+        if (INT64(rwidth) * INT64(rheight + 8) *
+                sizeof(imgdata.rawdata.raw_image[0]) * 3 >
+            INT64(imgdata.params.max_raw_memory_mb) * INT64(1024 * 1024))
+          throw LIBRAW_EXCEPTION_TOOBIG;
+
+        imgdata.rawdata.raw_alloc = malloc(
+            rwidth * (rheight + 8) * sizeof(imgdata.rawdata.raw_image[0]) * 3);
+        imgdata.rawdata.color3_image = (ushort(*)[3])imgdata.rawdata.raw_alloc;
+        if (!S.raw_pitch)
+          S.raw_pitch = S.raw_width * 6;
+      }
+      else if (imgdata.idata.filters ||
+               P1.colors ==
+                   1) // Bayer image or single color -> decode to raw_image
+      {
+        if (INT64(rwidth) * INT64(rheight + 8) *
+                sizeof(imgdata.rawdata.raw_image[0]) >
+            INT64(imgdata.params.max_raw_memory_mb) * INT64(1024 * 1024))
+          throw LIBRAW_EXCEPTION_TOOBIG;
+        imgdata.rawdata.raw_alloc = malloc(
+            rwidth * (rheight + 8) * sizeof(imgdata.rawdata.raw_image[0]));
+        imgdata.rawdata.raw_image = (ushort *)imgdata.rawdata.raw_alloc;
+        if (!S.raw_pitch)
+          S.raw_pitch = S.raw_width * 2; // Bayer case, not set before
+      }
+      else // NO LEGACY FLAG if (decoder_info.decoder_flags &
+           // LIBRAW_DECODER_LEGACY)
+      {
+        if (decoder_info.decoder_flags & LIBRAW_DECODER_ADOBECOPYPIXEL)
+        {
+          S.raw_pitch = S.raw_width * 8;
+        }
+        else
+        {
+          S.iwidth = S.width;
+          S.iheight = S.height;
+          IO.shrink = 0;
+          if (!S.raw_pitch)
+            S.raw_pitch = (decoder_info.decoder_flags &
+                           LIBRAW_DECODER_LEGACY_WITH_MARGINS)
+                              ? S.raw_width * 8
+                              : S.width * 8;
+        }
+        // sRAW and old Foveon decoders only, so extra buffer size is just 1/4
+        // allocate image as temporary buffer, size
+        if (INT64(MAX(S.width, S.raw_width)) *
+                INT64(MAX(S.height, S.raw_height) + 8) *
+                sizeof(*imgdata.image) >
+            INT64(imgdata.params.max_raw_memory_mb) * INT64(1024 * 1024))
+          throw LIBRAW_EXCEPTION_TOOBIG;
+
+        imgdata.rawdata.raw_alloc = 0;
+        imgdata.image =
+            (ushort(*)[4])calloc(unsigned(MAX(S.width, S.raw_width)) *
+                                     unsigned(MAX(S.height, S.raw_height) + 8),
+                                 sizeof(*imgdata.image));
+        if (!(decoder_info.decoder_flags & LIBRAW_DECODER_ADOBECOPYPIXEL))
+        {
+          imgdata.rawdata.raw_image = (ushort *)imgdata.image;
+          zero_rawimage = 1;
+        }
+      }
+      ID.input->seek(libraw_internal_data.unpacker_data.data_offset, SEEK_SET);
+
+      unsigned m_save = C.maximum;
+      if (load_raw == &LibRaw::unpacked_load_raw &&
+          (!strcasecmp(imgdata.idata.make, "Nikon") || !strcasecmp(imgdata.idata.make, "Hasselblad"))
+          )
+        C.maximum = 65535;
+      (this->*load_raw)();
+      if (zero_rawimage)
+        imgdata.rawdata.raw_image = 0;
+      if (load_raw == &LibRaw::unpacked_load_raw &&
+          (!strcasecmp(imgdata.idata.make, "Nikon") || !strcasecmp(imgdata.idata.make, "Hasselblad"))
+          )
+        C.maximum = m_save;
+      if (decoder_info.decoder_flags & LIBRAW_DECODER_OWNALLOC)
+      {
+        // x3f foveon decoder only: do nothing
+      }
+      else if (decoder_info.decoder_flags & LIBRAW_DECODER_SINAR4SHOT &&
+               imgdata.params.shot_select == 0)
+      {
+        imgdata.rawdata.raw_alloc = imgdata.image;
+        imgdata.rawdata.color4_image = (ushort(*)[4])imgdata.rawdata.raw_alloc;
+        imgdata.image = 0;
+      }
+      else if (!(imgdata.idata.filters ||
+                 P1.colors == 1)) // legacy decoder, ownalloc handled above
+      {
+        // successfully decoded legacy image, attach image to raw_alloc
+        imgdata.rawdata.raw_alloc = imgdata.image;
+        imgdata.rawdata.color4_image = (ushort(*)[4])imgdata.rawdata.raw_alloc;
+        imgdata.image = 0;
+        // Restore saved values. Note: Foveon have masked frame
+        // Other 4-color legacy data: no borders
+        if (!(libraw_internal_data.unpacker_data.load_flags & 256) &&
+            !(decoder_info.decoder_flags & LIBRAW_DECODER_ADOBECOPYPIXEL) &&
+            !(decoder_info.decoder_flags & LIBRAW_DECODER_LEGACY_WITH_MARGINS))
+        {
+          S.raw_width = S.width;
+          S.left_margin = 0;
+          S.raw_height = S.height;
+          S.top_margin = 0;
+        }
+      }
+    }
+
+    if (imgdata.rawdata.raw_image)
+      crop_masked_pixels(); // calculate black levels
+
+    // recover image sizes
+    S.iwidth = save_iwidth;
+    S.iheight = save_iheight;
+    IO.shrink = save_shrink;
+
+    // adjust black to possible maximum
+    unsigned int i = C.cblack[3];
+    unsigned int c;
+    for (c = 0; c < 3; c++)
+      if (i > C.cblack[c])
+        i = C.cblack[c];
+    for (c = 0; c < 4; c++)
+      C.cblack[c] -= i;
+    C.black += i;
+
+    // Save color,sizes and internal data into raw_image fields
+    memmove(&imgdata.rawdata.color, &imgdata.color, sizeof(imgdata.color));
+    memmove(&imgdata.rawdata.sizes, &imgdata.sizes, sizeof(imgdata.sizes));
+    memmove(&imgdata.rawdata.iparams, &imgdata.idata, sizeof(imgdata.idata));
+    memmove(&imgdata.rawdata.ioparams,
+            &libraw_internal_data.internal_output_params,
+            sizeof(libraw_internal_data.internal_output_params));
+
+    SET_PROC_FLAG(LIBRAW_PROGRESS_LOAD_RAW);
+    RUN_CALLBACK(LIBRAW_PROGRESS_LOAD_RAW, 1, 2);
+
+    return 0;
+  }
+  catch (const LibRaw_exceptions& err)
+  {
+    EXCEPTION_HANDLER(err);
+  }
+  catch (const std::exception& ee)
+  {
+    EXCEPTION_HANDLER(LIBRAW_EXCEPTION_IO_CORRUPT);
+  }
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/decoders/unpack_thumb.cpp libkdcraw/libkdcraw/libraw/src/decoders/unpack_thumb.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/decoders/unpack_thumb.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/decoders/unpack_thumb.cpp	2022-11-07 07:46:31.730795008 +0300
@@ -0,0 +1,360 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/libraw_cxx_defs.h"
+
+#ifndef NO_JPEG
+struct jpegErrorManager
+{
+  struct jpeg_error_mgr pub;
+  jmp_buf setjmp_buffer;
+};
+
+static void jpegErrorExit(j_common_ptr cinfo)
+{
+  jpegErrorManager *myerr = (jpegErrorManager *)cinfo->err;
+  longjmp(myerr->setjmp_buffer, 1);
+}
+#endif
+
+int LibRaw::unpack_thumb(void)
+{
+  CHECK_ORDER_LOW(LIBRAW_PROGRESS_IDENTIFY);
+  CHECK_ORDER_BIT(LIBRAW_PROGRESS_THUMB_LOAD);
+
+#define THUMB_SIZE_CHECKT(A) \
+  do { \
+    if (INT64(A) > 1024ULL * 1024ULL * LIBRAW_MAX_THUMBNAIL_MB) throw LIBRAW_EXCEPTION_IO_CORRUPT; \
+    if (INT64(A) > 0 &&  INT64(A) < 64ULL)        throw LIBRAW_EXCEPTION_IO_CORRUPT; \
+  } while (0)
+
+#define THUMB_SIZE_CHECKTNZ(A) \
+  do { \
+    if (INT64(A) > 1024ULL * 1024ULL * LIBRAW_MAX_THUMBNAIL_MB) throw LIBRAW_EXCEPTION_IO_CORRUPT; \
+    if (INT64(A) < 64ULL)        throw LIBRAW_EXCEPTION_IO_CORRUPT; \
+  } while (0)
+
+
+#define THUMB_SIZE_CHECKWH(W,H) \
+  do { \
+    if (INT64(W)*INT64(H) > 1024ULL * 1024ULL * LIBRAW_MAX_THUMBNAIL_MB) throw LIBRAW_EXCEPTION_IO_CORRUPT; \
+    if (INT64(W)*INT64(H) < 64ULL)        throw LIBRAW_EXCEPTION_IO_CORRUPT; \
+  } while (0)
+
+  try
+  {
+    if (!libraw_internal_data.internal_data.input)
+      return LIBRAW_INPUT_CLOSED;
+
+    int t_colors = libraw_internal_data.unpacker_data.thumb_misc >> 5 & 7;
+    int t_bytesps = (libraw_internal_data.unpacker_data.thumb_misc & 31) / 8;
+
+    if (!ID.toffset && !(imgdata.thumbnail.tlength > 0 &&
+                         load_raw == &LibRaw::broadcom_load_raw) // RPi
+    )
+    {
+      return LIBRAW_NO_THUMBNAIL;
+    }
+    else if (thumb_load_raw)
+    {
+      kodak_thumb_loader();
+      T.tformat = LIBRAW_THUMBNAIL_BITMAP;
+      SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD);
+      return 0;
+    }
+    else
+    {
+#ifdef USE_X3FTOOLS
+	if (write_thumb == &LibRaw::x3f_thumb_loader)
+      {
+        INT64 tsize = x3f_thumb_size();
+        if (tsize < 2048 || INT64(ID.toffset) + tsize < 1)
+          throw LIBRAW_EXCEPTION_IO_CORRUPT;
+
+        if (INT64(ID.toffset) + tsize > ID.input->size() + THUMB_READ_BEYOND)
+          throw LIBRAW_EXCEPTION_IO_EOF;
+        THUMB_SIZE_CHECKT(tsize);
+      }
+#else
+	if (0) {}
+#endif
+      else
+      {
+        if (INT64(ID.toffset) + INT64(T.tlength) < 1)
+          throw LIBRAW_EXCEPTION_IO_CORRUPT;
+
+        if (INT64(ID.toffset) + INT64(T.tlength) >
+            ID.input->size() + THUMB_READ_BEYOND)
+          throw LIBRAW_EXCEPTION_IO_EOF;
+      }
+
+      ID.input->seek(ID.toffset, SEEK_SET);
+      if (write_thumb == &LibRaw::jpeg_thumb)
+      {
+        THUMB_SIZE_CHECKTNZ(T.tlength);
+
+        if (T.thumb)
+          free(T.thumb);
+        T.thumb = (char *)malloc(T.tlength);
+        merror(T.thumb, "jpeg_thumb()");
+        ID.input->read(T.thumb, 1, T.tlength);
+        unsigned char *tthumb = (unsigned char *)T.thumb;
+        tthumb[0] = 0xff;
+        tthumb[1] = 0xd8;
+#ifdef NO_JPEG
+        T.tcolors = 3;
+#else
+        {
+          jpegErrorManager jerr;
+          struct jpeg_decompress_struct cinfo;
+          cinfo.err = jpeg_std_error(&jerr.pub);
+          jerr.pub.error_exit = jpegErrorExit;
+          if (setjmp(jerr.setjmp_buffer))
+          {
+          err2:
+            // Error in original JPEG thumb, read it again because
+            // original bytes 0-1 was damaged above
+            jpeg_destroy_decompress(&cinfo);
+            T.tcolors = 3;
+            T.tformat = LIBRAW_THUMBNAIL_UNKNOWN;
+            ID.input->seek(ID.toffset, SEEK_SET);
+            ID.input->read(T.thumb, 1, T.tlength);
+            SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD);
+            return 0;
+          }
+          jpeg_create_decompress(&cinfo);
+          jpeg_mem_src(&cinfo, (unsigned char *)T.thumb, T.tlength);
+          int rc = jpeg_read_header(&cinfo, TRUE);
+          if (rc != 1)
+            goto err2;
+          T.tcolors = (cinfo.num_components > 0 && cinfo.num_components <= 3)
+                          ? cinfo.num_components
+                          : 3;
+          jpeg_destroy_decompress(&cinfo);
+        }
+#endif
+        T.tformat = LIBRAW_THUMBNAIL_JPEG;
+        SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD);
+        return 0;
+      }
+      else if (write_thumb == &LibRaw::layer_thumb)
+      {
+        int colors = libraw_internal_data.unpacker_data.thumb_misc >> 5 & 7;
+        if (colors != 1 && colors != 3)
+          return LIBRAW_UNSUPPORTED_THUMBNAIL;
+
+        THUMB_SIZE_CHECKWH(T.twidth, T.theight);
+
+        int tlength = T.twidth * T.theight;
+        if (T.thumb)
+          free(T.thumb);
+        T.thumb = (char *)calloc(colors, tlength);
+        merror(T.thumb, "layer_thumb()");
+        unsigned char *tbuf = (unsigned char *)calloc(colors, tlength);
+        merror(tbuf, "layer_thumb()");
+        ID.input->read(tbuf, colors, T.tlength);
+        if (libraw_internal_data.unpacker_data.thumb_misc >> 8 &&
+            colors == 3) // GRB order
+          for (int i = 0; i < tlength; i++)
+          {
+            T.thumb[i * 3] = tbuf[i + tlength];
+            T.thumb[i * 3 + 1] = tbuf[i];
+            T.thumb[i * 3 + 2] = tbuf[i + 2 * tlength];
+          }
+        else if (colors == 3) // RGB or 1-channel
+          for (int i = 0; i < tlength; i++)
+          {
+            T.thumb[i * 3] = tbuf[i];
+            T.thumb[i * 3 + 1] = tbuf[i + tlength];
+            T.thumb[i * 3 + 2] = tbuf[i + 2 * tlength];
+          }
+        else if (colors == 1)
+        {
+          free(T.thumb);
+          T.thumb = (char *)tbuf;
+          tbuf = 0;
+        }
+        if (tbuf)
+          free(tbuf);
+        T.tcolors = colors;
+        T.tlength = colors * tlength;
+        T.tformat = LIBRAW_THUMBNAIL_BITMAP;
+        SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD);
+        return 0;
+      }
+      else if (write_thumb == &LibRaw::rollei_thumb)
+      {
+        int i;
+        THUMB_SIZE_CHECKWH(T.twidth, T.theight);
+        int tlength = T.twidth * T.theight;
+        if (T.thumb)
+          free(T.thumb);
+        T.tcolors = 3;
+        T.thumb = (char *)calloc(T.tcolors, tlength);
+        merror(T.thumb, "layer_thumb()");
+        unsigned short *tbuf = (unsigned short *)calloc(2, tlength);
+        merror(tbuf, "layer_thumb()");
+        read_shorts(tbuf, tlength);
+        for (i = 0; i < tlength; i++)
+        {
+          T.thumb[i * 3] = (tbuf[i] << 3) & 0xff;
+          T.thumb[i * 3 + 1] = (tbuf[i] >> 5 << 2) & 0xff;
+          T.thumb[i * 3 + 2] = (tbuf[i] >> 11 << 3) & 0xff;
+        }
+        free(tbuf);
+        T.tlength = T.tcolors * tlength;
+        T.tformat = LIBRAW_THUMBNAIL_BITMAP;
+        SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD);
+        return 0;
+      }
+      else if (write_thumb == &LibRaw::ppm_thumb)
+      {
+        if (t_bytesps > 1)
+          throw LIBRAW_EXCEPTION_IO_CORRUPT; // 8-bit thumb, but parsed for more
+                                             // bits
+        THUMB_SIZE_CHECKWH(T.twidth, T.theight);
+        int t_length = T.twidth * T.theight * t_colors;
+
+        if (T.tlength &&
+            (int)T.tlength < t_length) // try to find tiff ifd with needed offset
+        {
+          int pifd = find_ifd_by_offset(libraw_internal_data.internal_data.toffset);
+          if (pifd >= 0 && tiff_ifd[pifd].strip_offsets_count &&
+              tiff_ifd[pifd].strip_byte_counts_count)
+          {
+            // We found it, calculate final size
+            unsigned total_size = 0;
+            for (int i = 0; i < tiff_ifd[pifd].strip_byte_counts_count; i++)
+              total_size += tiff_ifd[pifd].strip_byte_counts[i];
+            if (total_size != (unsigned)t_length) // recalculate colors
+            {
+              if (total_size == T.twidth * T.tlength * 3)
+                T.tcolors = 3;
+              else if (total_size == T.twidth * T.tlength)
+                T.tcolors = 1;
+            }
+            T.tlength = total_size;
+            THUMB_SIZE_CHECKTNZ(T.tlength);
+            if (T.thumb)
+              free(T.thumb);
+            T.thumb = (char *)malloc(T.tlength);
+            merror(T.thumb, "ppm_thumb()");
+
+            char *dest = T.thumb;
+            INT64 pos = ID.input->tell();
+
+            for (int i = 0; i < tiff_ifd[pifd].strip_byte_counts_count &&
+                            i < tiff_ifd[pifd].strip_offsets_count;
+                 i++)
+            {
+              int remain = T.tlength;
+              int sz = tiff_ifd[pifd].strip_byte_counts[i];
+              int off = tiff_ifd[pifd].strip_offsets[i];
+              if (off >= 0 && off + sz <= ID.input->size() && sz <= remain)
+              {
+                ID.input->seek(off, SEEK_SET);
+                ID.input->read(dest, sz, 1);
+                remain -= sz;
+                dest += sz;
+              }
+            }
+            ID.input->seek(pos, SEEK_SET);
+            T.tformat = LIBRAW_THUMBNAIL_BITMAP;
+            SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD);
+            return 0;
+          }
+        }
+
+        if (!T.tlength)
+          T.tlength = t_length;
+        if (T.thumb)
+          free(T.thumb);
+
+        THUMB_SIZE_CHECKTNZ(T.tlength);
+
+        T.thumb = (char *)malloc(T.tlength);
+        if (!T.tcolors)
+          T.tcolors = t_colors;
+        merror(T.thumb, "ppm_thumb()");
+
+        ID.input->read(T.thumb, 1, T.tlength);
+
+        T.tformat = LIBRAW_THUMBNAIL_BITMAP;
+        SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD);
+        return 0;
+      }
+      else if (write_thumb == &LibRaw::ppm16_thumb)
+      {
+        if (t_bytesps > 2)
+          throw LIBRAW_EXCEPTION_IO_CORRUPT; // 16-bit thumb, but parsed for
+                                             // more bits
+        int o_bps = (imgdata.params.raw_processing_options &
+                     LIBRAW_PROCESSING_USE_PPM16_THUMBS)
+                        ? 2
+                        : 1;
+        int o_length = T.twidth * T.theight * t_colors * o_bps;
+        int i_length = T.twidth * T.theight * t_colors * 2;
+        if (!T.tlength)
+          T.tlength = o_length;
+        THUMB_SIZE_CHECKTNZ(o_length);
+        THUMB_SIZE_CHECKTNZ(i_length);
+        THUMB_SIZE_CHECKTNZ(T.tlength);
+
+        ushort *t_thumb = (ushort *)calloc(i_length, 1);
+        ID.input->read(t_thumb, 1, i_length);
+        if ((libraw_internal_data.unpacker_data.order == 0x4949) ==
+            (ntohs(0x1234) == 0x1234))
+          swab((char *)t_thumb, (char *)t_thumb, i_length);
+
+        if (T.thumb)
+          free(T.thumb);
+        if ((imgdata.params.raw_processing_options &
+             LIBRAW_PROCESSING_USE_PPM16_THUMBS))
+        {
+          T.thumb = (char *)t_thumb;
+          T.tformat = LIBRAW_THUMBNAIL_BITMAP16;
+        }
+        else
+        {
+          T.thumb = (char *)malloc(o_length);
+          merror(T.thumb, "ppm_thumb()");
+          for (int i = 0; i < o_length; i++)
+            T.thumb[i] = t_thumb[i] >> 8;
+          free(t_thumb);
+          T.tformat = LIBRAW_THUMBNAIL_BITMAP;
+        }
+        SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD);
+        return 0;
+      }
+#ifdef USE_X3FTOOLS
+	  else if (write_thumb == &LibRaw::x3f_thumb_loader)
+      {
+        x3f_thumb_loader();
+        SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD);
+        return 0;
+      }
+#endif
+      else
+      {
+        return LIBRAW_UNSUPPORTED_THUMBNAIL;
+      }
+    }
+    // last resort
+    return LIBRAW_UNSUPPORTED_THUMBNAIL;
+  }
+  catch (LibRaw_exceptions err)
+  {
+    EXCEPTION_HANDLER(err);
+  }
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/demosaic/aahd_demosaic.cpp libkdcraw/libkdcraw/libraw/src/demosaic/aahd_demosaic.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/demosaic/aahd_demosaic.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/demosaic/aahd_demosaic.cpp	2022-11-07 07:46:31.730795008 +0300
@@ -0,0 +1,787 @@
+/* -*- C++ -*-
+ * File: aahd_demosaic.cpp
+ * Copyright 2013 Anton Petrusevich
+ * Created: Wed May  15, 2013
+ *
+ * This code is licensed under one of two licenses as you choose:
+ *
+ * 1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+ *    (See file LICENSE.LGPL provided in LibRaw distribution archive for
+ * details).
+ *
+ * 2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+ *    (See file LICENSE.CDDL provided in LibRaw distribution archive for
+ * details).
+ *
+ */
+
+#include "../../internal/dmp_include.h"
+
+typedef ushort ushort3[3];
+typedef int int3[3];
+
+#ifndef Pnw
+#define Pnw (-1 - nr_width)
+#define Pn (-nr_width)
+#define Pne (+1 - nr_width)
+#define Pe (+1)
+#define Pse (+1 + nr_width)
+#define Ps (+nr_width)
+#define Psw (-1 + nr_width)
+#define Pw (-1)
+#endif
+
+struct AAHD
+{
+  int nr_height, nr_width;
+  static const int nr_margin = 4;
+  static const int Thot = 4;
+  static const int Tdead = 4;
+  static const int OverFraction = 8;
+  ushort3 *rgb_ahd[2];
+  int3 *yuv[2];
+  char *ndir, *homo[2];
+  ushort channel_maximum[3], channels_max;
+  ushort channel_minimum[3];
+  static const float yuv_coeff[3][3];
+  static float gammaLUT[0x10000];
+  float yuv_cam[3][3];
+  LibRaw &libraw;
+  enum
+  {
+    HVSH = 1,
+    HOR = 2,
+    VER = 4,
+    HORSH = HOR | HVSH,
+    VERSH = VER | HVSH,
+    HOT = 8
+  };
+
+  static inline float calc_dist(int c1, int c2) throw()
+  {
+    return c1 > c2 ? (float)c1 / c2 : (float)c2 / c1;
+  }
+  int inline Y(ushort3 &rgb) throw()
+  {
+    return yuv_cam[0][0] * rgb[0] + yuv_cam[0][1] * rgb[1] +
+           yuv_cam[0][2] * rgb[2];
+  }
+  int inline U(ushort3 &rgb) throw()
+  {
+    return yuv_cam[1][0] * rgb[0] + yuv_cam[1][1] * rgb[1] +
+           yuv_cam[1][2] * rgb[2];
+  }
+  int inline V(ushort3 &rgb) throw()
+  {
+    return yuv_cam[2][0] * rgb[0] + yuv_cam[2][1] * rgb[1] +
+           yuv_cam[2][2] * rgb[2];
+  }
+  inline int nr_offset(int row, int col) throw()
+  {
+    return (row * nr_width + col);
+  }
+  ~AAHD();
+  AAHD(LibRaw &_libraw);
+  void make_ahd_greens();
+  void make_ahd_gline(int i);
+  void make_ahd_rb();
+  void make_ahd_rb_hv(int i);
+  void make_ahd_rb_last(int i);
+  void evaluate_ahd();
+  void combine_image();
+  void hide_hots();
+  void refine_hv_dirs();
+  void refine_hv_dirs(int i, int js);
+  void refine_ihv_dirs(int i);
+  void illustrate_dirs();
+  void illustrate_dline(int i);
+};
+
+const float AAHD::yuv_coeff[3][3] = {
+    // YPbPr
+    //	{
+    //		0.299f,
+    //		0.587f,
+    //		0.114f },
+    //	{
+    //		-0.168736,
+    //		-0.331264f,
+    //		0.5f },
+    //	{
+    //		0.5f,
+    //		-0.418688f,
+    //		-0.081312f }
+    //
+    //	Rec. 2020
+    //	Y'= 0,2627R' + 0,6780G' + 0,0593B'
+    //	U = (B-Y)/1.8814 =  (-0,2627R' - 0,6780G' + 0.9407B) / 1.8814 =
+    //-0.13963R - 0.36037G + 0.5B
+    //	V = (R-Y)/1.4647 = (0.7373R - 0,6780G - 0,0593B) / 1.4647 = 0.5R -
+    //0.4629G - 0.04049B
+    {+0.2627f, +0.6780f, +0.0593f},
+    {-0.13963f, -0.36037f, +0.5f},
+    {+0.5034f, -0.4629f, -0.0405f}
+
+};
+
+float AAHD::gammaLUT[0x10000] = {-1.f};
+
+AAHD::AAHD(LibRaw &_libraw) : libraw(_libraw)
+{
+  nr_height = libraw.imgdata.sizes.iheight + nr_margin * 2;
+  nr_width = libraw.imgdata.sizes.iwidth + nr_margin * 2;
+  rgb_ahd[0] = (ushort3 *)calloc(nr_height * nr_width,
+                                 (sizeof(ushort3) * 2 + sizeof(int3) * 2 + 3));
+  if (!rgb_ahd[0])
+    throw LIBRAW_EXCEPTION_ALLOC;
+
+  rgb_ahd[1] = rgb_ahd[0] + nr_height * nr_width;
+  yuv[0] = (int3 *)(rgb_ahd[1] + nr_height * nr_width);
+  yuv[1] = yuv[0] + nr_height * nr_width;
+  ndir = (char *)(yuv[1] + nr_height * nr_width);
+  homo[0] = ndir + nr_height * nr_width;
+  homo[1] = homo[0] + nr_height * nr_width;
+  channel_maximum[0] = channel_maximum[1] = channel_maximum[2] = 0;
+  channel_minimum[0] = libraw.imgdata.image[0][0];
+  channel_minimum[1] = libraw.imgdata.image[0][1];
+  channel_minimum[2] = libraw.imgdata.image[0][2];
+  int iwidth = libraw.imgdata.sizes.iwidth;
+  for (int i = 0; i < 3; ++i)
+    for (int j = 0; j < 3; ++j)
+    {
+      yuv_cam[i][j] = 0;
+      for (int k = 0; k < 3; ++k)
+        yuv_cam[i][j] += yuv_coeff[i][k] * libraw.imgdata.color.rgb_cam[k][j];
+    }
+  if (gammaLUT[0] < -0.1f)
+  {
+    float r;
+    for (int i = 0; i < 0x10000; i++)
+    {
+      r = (float)i / 0x10000;
+      gammaLUT[i] =
+          0x10000 * (r < 0.0181 ? 4.5f * r : 1.0993f * pow(r, 0.45f) - .0993f);
+    }
+  }
+  for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
+  {
+    int col_cache[48];
+    for (int j = 0; j < 48; ++j)
+    {
+      int c = libraw.COLOR(i, j);
+      if (c == 3)
+        c = 1;
+      col_cache[j] = c;
+    }
+    int moff = nr_offset(i + nr_margin, nr_margin);
+    for (int j = 0; j < iwidth; ++j, ++moff)
+    {
+      int c = col_cache[j % 48];
+      unsigned short d = libraw.imgdata.image[i * iwidth + j][c];
+      if (d != 0)
+      {
+        if (channel_maximum[c] < d)
+          channel_maximum[c] = d;
+        if (channel_minimum[c] > d)
+          channel_minimum[c] = d;
+        rgb_ahd[1][moff][c] = rgb_ahd[0][moff][c] = d;
+      }
+    }
+  }
+  channels_max =
+      MAX(MAX(channel_maximum[0], channel_maximum[1]), channel_maximum[2]);
+}
+
+void AAHD::hide_hots()
+{
+  int iwidth = libraw.imgdata.sizes.iwidth;
+  for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
+  {
+    int js = libraw.COLOR(i, 0) & 1;
+    int kc = libraw.COLOR(i, js);
+    /*
+     * js -- Ð½Ð°Ñ‡Ð°Ð»ÑŒÐ½Ð°Ñ Ñ…-координата, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð¿Ð¾Ð¿Ð°Ð´Ð°ÐµÑ‚ мимо извеÑтного зелёного
+     * kc -- извеÑтный цвет в точке интерполированиÑ
+     */
+    int moff = nr_offset(i + nr_margin, nr_margin + js);
+    for (int j = js; j < iwidth; j += 2, moff += 2)
+    {
+      ushort3 *rgb = &rgb_ahd[0][moff];
+      int c = rgb[0][kc];
+      if ((c > rgb[2 * Pe][kc] && c > rgb[2 * Pw][kc] && c > rgb[2 * Pn][kc] &&
+           c > rgb[2 * Ps][kc] && c > rgb[Pe][1] && c > rgb[Pw][1] &&
+           c > rgb[Pn][1] && c > rgb[Ps][1]) ||
+          (c < rgb[2 * Pe][kc] && c < rgb[2 * Pw][kc] && c < rgb[2 * Pn][kc] &&
+           c < rgb[2 * Ps][kc] && c < rgb[Pe][1] && c < rgb[Pw][1] &&
+           c < rgb[Pn][1] && c < rgb[Ps][1]))
+      {
+        int chot = c >> Thot;
+        int cdead = c << Tdead;
+        int avg = 0;
+        for (int k = -2; k < 3; k += 2)
+          for (int m = -2; m < 3; m += 2)
+            if (m == 0 && k == 0)
+              continue;
+            else
+              avg += rgb[nr_offset(k, m)][kc];
+        avg /= 8;
+        if (chot > avg || cdead < avg)
+        {
+          ndir[moff] |= HOT;
+          int dh =
+              ABS(rgb[2 * Pw][kc] - rgb[2 * Pe][kc]) +
+              ABS(rgb[Pw][1] - rgb[Pe][1]) +
+              ABS(rgb[Pw][1] - rgb[Pe][1] + rgb[2 * Pe][kc] - rgb[2 * Pw][kc]);
+          int dv =
+              ABS(rgb[2 * Pn][kc] - rgb[2 * Ps][kc]) +
+              ABS(rgb[Pn][1] - rgb[Ps][1]) +
+              ABS(rgb[Pn][1] - rgb[Ps][1] + rgb[2 * Ps][kc] - rgb[2 * Pn][kc]);
+          int d;
+          if (dv > dh)
+            d = Pw;
+          else
+            d = Pn;
+          rgb_ahd[1][moff][kc] = rgb[0][kc] =
+              (rgb[+2 * d][kc] + rgb[-2 * d][kc]) / 2;
+        }
+      }
+    }
+    js ^= 1;
+    moff = nr_offset(i + nr_margin, nr_margin + js);
+    for (int j = js; j < iwidth; j += 2, moff += 2)
+    {
+      ushort3 *rgb = &rgb_ahd[0][moff];
+      int c = rgb[0][1];
+      if ((c > rgb[2 * Pe][1] && c > rgb[2 * Pw][1] && c > rgb[2 * Pn][1] &&
+           c > rgb[2 * Ps][1] && c > rgb[Pe][kc] && c > rgb[Pw][kc] &&
+           c > rgb[Pn][kc ^ 2] && c > rgb[Ps][kc ^ 2]) ||
+          (c < rgb[2 * Pe][1] && c < rgb[2 * Pw][1] && c < rgb[2 * Pn][1] &&
+           c < rgb[2 * Ps][1] && c < rgb[Pe][kc] && c < rgb[Pw][kc] &&
+           c < rgb[Pn][kc ^ 2] && c < rgb[Ps][kc ^ 2]))
+      {
+        int chot = c >> Thot;
+        int cdead = c << Tdead;
+        int avg = 0;
+        for (int k = -2; k < 3; k += 2)
+          for (int m = -2; m < 3; m += 2)
+            if (k == 0 && m == 0)
+              continue;
+            else
+              avg += rgb[nr_offset(k, m)][1];
+        avg /= 8;
+        if (chot > avg || cdead < avg)
+        {
+          ndir[moff] |= HOT;
+          int dh =
+              ABS(rgb[2 * Pw][1] - rgb[2 * Pe][1]) +
+              ABS(rgb[Pw][kc] - rgb[Pe][kc]) +
+              ABS(rgb[Pw][kc] - rgb[Pe][kc] + rgb[2 * Pe][1] - rgb[2 * Pw][1]);
+          int dv = ABS(rgb[2 * Pn][1] - rgb[2 * Ps][1]) +
+                   ABS(rgb[Pn][kc ^ 2] - rgb[Ps][kc ^ 2]) +
+                   ABS(rgb[Pn][kc ^ 2] - rgb[Ps][kc ^ 2] + rgb[2 * Ps][1] -
+                       rgb[2 * Pn][1]);
+          int d;
+          if (dv > dh)
+            d = Pw;
+          else
+            d = Pn;
+          rgb_ahd[1][moff][1] = rgb[0][1] =
+              (rgb[+2 * d][1] + rgb[-2 * d][1]) / 2;
+        }
+      }
+    }
+  }
+}
+
+const static double xyz_rgb[3][3] = {{0.412453, 0.357580, 0.180423},
+                                     {0.212671, 0.715160, 0.072169},
+                                     {0.019334, 0.119193, 0.950227}};
+
+const static float d65_white[3] = {0.950456f, 1.0f, 1.088754f};
+
+void AAHD::evaluate_ahd()
+{
+  int hvdir[4] = {Pw, Pe, Pn, Ps};
+  /*
+   * YUV
+   *
+   */
+  for (int d = 0; d < 2; ++d)
+  {
+    for (int i = 0; i < nr_width * nr_height; ++i)
+    {
+      ushort3 rgb;
+      for (int c = 0; c < 3; ++c)
+      {
+        rgb[c] = gammaLUT[rgb_ahd[d][i][c]];
+      }
+      yuv[d][i][0] = Y(rgb);
+      yuv[d][i][1] = U(rgb);
+      yuv[d][i][2] = V(rgb);
+    }
+  }
+  /* */
+  /*
+   * Lab
+   *
+   float r, cbrt[0x10000], xyz[3], xyz_cam[3][4];
+   for (int i = 0; i < 0x10000; i++) {
+   r = i / 65535.0;
+   cbrt[i] = r > 0.008856 ? pow((double) r, (double) (1 / 3.0)) : 7.787 * r + 16
+   / 116.0;
+   }
+   for (int i = 0; i < 3; i++)
+   for (int j = 0; j < 3; j++) {
+   xyz_cam[i][j] = 0;
+   for (int k = 0; k < 3; k++)
+   xyz_cam[i][j] += xyz_rgb[i][k] * libraw.imgdata.color.rgb_cam[k][j] /
+   d65_white[i];
+   }
+   for (int d = 0; d < 2; ++d)
+   for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) {
+   int moff = nr_offset(i + nr_margin, nr_margin);
+   for (int j = 0; j < libraw.imgdata.sizes.iwidth; j++, ++moff) {
+   xyz[0] = xyz[1] = xyz[2] = 0.5;
+   for (int c = 0; c < 3; c++) {
+   xyz[0] += xyz_cam[0][c] * rgb_ahd[d][moff][c];
+   xyz[1] += xyz_cam[1][c] * rgb_ahd[d][moff][c];
+   xyz[2] += xyz_cam[2][c] * rgb_ahd[d][moff][c];
+   }
+   xyz[0] = cbrt[CLIP((int) xyz[0])];
+   xyz[1] = cbrt[CLIP((int) xyz[1])];
+   xyz[2] = cbrt[CLIP((int) xyz[2])];
+   yuv[d][moff][0] = 64 * (116 * xyz[1] - 16);
+   yuv[d][moff][1] = 64 * 500 * (xyz[0] - xyz[1]);
+   yuv[d][moff][2] = 64 * 200 * (xyz[1] - xyz[2]);
+   }
+   }
+   * Lab */
+  for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
+  {
+    int moff = nr_offset(i + nr_margin, nr_margin);
+    for (int j = 0; j < libraw.imgdata.sizes.iwidth; j++, ++moff)
+    {
+      int3 *ynr;
+      float ydiff[2][4];
+      int uvdiff[2][4];
+      for (int d = 0; d < 2; ++d)
+      {
+        ynr = &yuv[d][moff];
+        for (int k = 0; k < 4; k++)
+        {
+          ydiff[d][k] = ABS(ynr[0][0] - ynr[hvdir[k]][0]);
+          uvdiff[d][k] = SQR(ynr[0][1] - ynr[hvdir[k]][1]) +
+                         SQR(ynr[0][2] - ynr[hvdir[k]][2]);
+        }
+      }
+      float yeps =
+          MIN(MAX(ydiff[0][0], ydiff[0][1]), MAX(ydiff[1][2], ydiff[1][3]));
+      int uveps =
+          MIN(MAX(uvdiff[0][0], uvdiff[0][1]), MAX(uvdiff[1][2], uvdiff[1][3]));
+      for (int d = 0; d < 2; d++)
+      {
+        ynr = &yuv[d][moff];
+        for (int k = 0; k < 4; k++)
+          if (ydiff[d][k] <= yeps && uvdiff[d][k] <= uveps)
+          {
+            homo[d][moff + hvdir[k]]++;
+            if (k / 2 == d)
+            {
+              // еÑли в Ñонаправленном направлении интеполÑции Ñледующие точки
+              // так же гомогенны, учтём их тоже
+              for (int m = 2; m < 4; ++m)
+              {
+                int hvd = m * hvdir[k];
+                if (ABS(ynr[0][0] - ynr[hvd][0]) < yeps &&
+                    SQR(ynr[0][1] - ynr[hvd][1]) +
+                            SQR(ynr[0][2] - ynr[hvd][2]) <
+                        uveps)
+                {
+                  homo[d][moff + hvd]++;
+                }
+                else
+                  break;
+              }
+            }
+          }
+      }
+    }
+  }
+  for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
+  {
+    int moff = nr_offset(i + nr_margin, nr_margin);
+    for (int j = 0; j < libraw.imgdata.sizes.iwidth; j++, ++moff)
+    {
+      char hm[2];
+      for (int d = 0; d < 2; d++)
+      {
+        hm[d] = 0;
+        char *hh = &homo[d][moff];
+        for (int hx = -1; hx < 2; hx++)
+          for (int hy = -1; hy < 2; hy++)
+            hm[d] += hh[nr_offset(hy, hx)];
+      }
+      char d = 0;
+      if (hm[0] != hm[1])
+      {
+        if (hm[1] > hm[0])
+        {
+          d = VERSH;
+        }
+        else
+        {
+          d = HORSH;
+        }
+      }
+      else
+      {
+        int3 *ynr = &yuv[1][moff];
+        int gv = SQR(2 * ynr[0][0] - ynr[Pn][0] - ynr[Ps][0]);
+        gv += SQR(2 * ynr[0][1] - ynr[Pn][1] - ynr[Ps][1]) +
+              SQR(2 * ynr[0][2] - ynr[Pn][2] - ynr[Ps][2]);
+        ynr = &yuv[1][moff + Pn];
+        gv += (SQR(2 * ynr[0][0] - ynr[Pn][0] - ynr[Ps][0]) +
+               SQR(2 * ynr[0][1] - ynr[Pn][1] - ynr[Ps][1]) +
+               SQR(2 * ynr[0][2] - ynr[Pn][2] - ynr[Ps][2])) /
+              2;
+        ynr = &yuv[1][moff + Ps];
+        gv += (SQR(2 * ynr[0][0] - ynr[Pn][0] - ynr[Ps][0]) +
+               SQR(2 * ynr[0][1] - ynr[Pn][1] - ynr[Ps][1]) +
+               SQR(2 * ynr[0][2] - ynr[Pn][2] - ynr[Ps][2])) /
+              2;
+        ynr = &yuv[0][moff];
+        int gh = SQR(2 * ynr[0][0] - ynr[Pw][0] - ynr[Pe][0]);
+        gh += SQR(2 * ynr[0][1] - ynr[Pw][1] - ynr[Pe][1]) +
+              SQR(2 * ynr[0][2] - ynr[Pw][2] - ynr[Pe][2]);
+        ynr = &yuv[0][moff + Pw];
+        gh += (SQR(2 * ynr[0][0] - ynr[Pw][0] - ynr[Pe][0]) +
+               SQR(2 * ynr[0][1] - ynr[Pw][1] - ynr[Pe][1]) +
+               SQR(2 * ynr[0][2] - ynr[Pw][2] - ynr[Pe][2])) /
+              2;
+        ynr = &yuv[0][moff + Pe];
+        gh += (SQR(2 * ynr[0][0] - ynr[Pw][0] - ynr[Pe][0]) +
+               SQR(2 * ynr[0][1] - ynr[Pw][1] - ynr[Pe][1]) +
+               SQR(2 * ynr[0][2] - ynr[Pw][2] - ynr[Pe][2])) /
+              2;
+        if (gv > gh)
+          d = HOR;
+        else
+          d = VER;
+      }
+      ndir[moff] |= d;
+    }
+  }
+}
+
+void AAHD::combine_image()
+{
+  for (int i = 0, i_out = 0; i < libraw.imgdata.sizes.iheight; ++i)
+  {
+    int moff = nr_offset(i + nr_margin, nr_margin);
+    for (int j = 0; j < libraw.imgdata.sizes.iwidth; j++, ++moff, ++i_out)
+    {
+      if (ndir[moff] & HOT)
+      {
+        int c = libraw.COLOR(i, j);
+        rgb_ahd[1][moff][c] = rgb_ahd[0][moff][c] =
+            libraw.imgdata.image[i_out][c];
+      }
+      if (ndir[moff] & VER)
+      {
+        libraw.imgdata.image[i_out][0] = rgb_ahd[1][moff][0];
+        libraw.imgdata.image[i_out][3] = libraw.imgdata.image[i_out][1] =
+            rgb_ahd[1][moff][1];
+        libraw.imgdata.image[i_out][2] = rgb_ahd[1][moff][2];
+      }
+      else
+      {
+        libraw.imgdata.image[i_out][0] = rgb_ahd[0][moff][0];
+        libraw.imgdata.image[i_out][3] = libraw.imgdata.image[i_out][1] =
+            rgb_ahd[0][moff][1];
+        libraw.imgdata.image[i_out][2] = rgb_ahd[0][moff][2];
+      }
+    }
+  }
+}
+
+void AAHD::refine_hv_dirs()
+{
+  for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
+  {
+    refine_hv_dirs(i, i & 1);
+  }
+  for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
+  {
+    refine_hv_dirs(i, (i & 1) ^ 1);
+  }
+  for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
+  {
+    refine_ihv_dirs(i);
+  }
+}
+
+void AAHD::refine_ihv_dirs(int i)
+{
+  int iwidth = libraw.imgdata.sizes.iwidth;
+  int moff = nr_offset(i + nr_margin, nr_margin);
+  for (int j = 0; j < iwidth; j++, ++moff)
+  {
+    if (ndir[moff] & HVSH)
+      continue;
+    int nv = (ndir[moff + Pn] & VER) + (ndir[moff + Ps] & VER) +
+             (ndir[moff + Pw] & VER) + (ndir[moff + Pe] & VER);
+    int nh = (ndir[moff + Pn] & HOR) + (ndir[moff + Ps] & HOR) +
+             (ndir[moff + Pw] & HOR) + (ndir[moff + Pe] & HOR);
+    nv /= VER;
+    nh /= HOR;
+    if ((ndir[moff] & VER) && nh > 3)
+    {
+      ndir[moff] &= ~VER;
+      ndir[moff] |= HOR;
+    }
+    if ((ndir[moff] & HOR) && nv > 3)
+    {
+      ndir[moff] &= ~HOR;
+      ndir[moff] |= VER;
+    }
+  }
+}
+
+void AAHD::refine_hv_dirs(int i, int js)
+{
+  int iwidth = libraw.imgdata.sizes.iwidth;
+  int moff = nr_offset(i + nr_margin, nr_margin + js);
+  for (int j = js; j < iwidth; j += 2, moff += 2)
+  {
+    int nv = (ndir[moff + Pn] & VER) + (ndir[moff + Ps] & VER) +
+             (ndir[moff + Pw] & VER) + (ndir[moff + Pe] & VER);
+    int nh = (ndir[moff + Pn] & HOR) + (ndir[moff + Ps] & HOR) +
+             (ndir[moff + Pw] & HOR) + (ndir[moff + Pe] & HOR);
+    bool codir = (ndir[moff] & VER)
+                     ? ((ndir[moff + Pn] & VER) || (ndir[moff + Ps] & VER))
+                     : ((ndir[moff + Pw] & HOR) || (ndir[moff + Pe] & HOR));
+    nv /= VER;
+    nh /= HOR;
+    if ((ndir[moff] & VER) && (nh > 2 && !codir))
+    {
+      ndir[moff] &= ~VER;
+      ndir[moff] |= HOR;
+    }
+    if ((ndir[moff] & HOR) && (nv > 2 && !codir))
+    {
+      ndir[moff] &= ~HOR;
+      ndir[moff] |= VER;
+    }
+  }
+}
+
+/*
+ * вычиÑление недоÑтающих зелёных точек.
+ */
+void AAHD::make_ahd_greens()
+{
+  for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
+  {
+    make_ahd_gline(i);
+  }
+}
+
+void AAHD::make_ahd_gline(int i)
+{
+  int iwidth = libraw.imgdata.sizes.iwidth;
+  int js = libraw.COLOR(i, 0) & 1;
+  int kc = libraw.COLOR(i, js);
+  /*
+   * js -- Ð½Ð°Ñ‡Ð°Ð»ÑŒÐ½Ð°Ñ Ñ…-координата, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð¿Ð¾Ð¿Ð°Ð´Ð°ÐµÑ‚ мимо извеÑтного зелёного
+   * kc -- извеÑтный цвет в точке интерполированиÑ
+   */
+  int hvdir[2] = {Pe, Ps};
+  for (int d = 0; d < 2; ++d)
+  {
+    int moff = nr_offset(i + nr_margin, nr_margin + js);
+    for (int j = js; j < iwidth; j += 2, moff += 2)
+    {
+      ushort3 *cnr;
+      cnr = &rgb_ahd[d][moff];
+      int h1 = 2 * cnr[-hvdir[d]][1] - int(cnr[-2 * hvdir[d]][kc] + cnr[0][kc]);
+      int h2 = 2 * cnr[+hvdir[d]][1] - int(cnr[+2 * hvdir[d]][kc] + cnr[0][kc]);
+      int h0 = (h1 + h2) / 4;
+      int eg = cnr[0][kc] + h0;
+      int min = MIN(cnr[-hvdir[d]][1], cnr[+hvdir[d]][1]);
+      int max = MAX(cnr[-hvdir[d]][1], cnr[+hvdir[d]][1]);
+      min -= min / OverFraction;
+      max += max / OverFraction;
+      if (eg < min)
+        eg = min - sqrt(float(min - eg));
+      else if (eg > max)
+        eg = max + sqrt(float(eg - max));
+      if (eg > channel_maximum[1])
+        eg = channel_maximum[1];
+      else if (eg < channel_minimum[1])
+        eg = channel_minimum[1];
+      cnr[0][1] = eg;
+    }
+  }
+}
+
+/*
+ * Ð¾Ñ‚Ð»Ð°Ð´Ð¾Ñ‡Ð½Ð°Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ
+ */
+
+void AAHD::illustrate_dirs()
+{
+  for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
+  {
+    illustrate_dline(i);
+  }
+}
+
+void AAHD::illustrate_dline(int i)
+{
+  int iwidth = libraw.imgdata.sizes.iwidth;
+  for (int j = 0; j < iwidth; j++)
+  {
+    int x = j + nr_margin;
+    int y = i + nr_margin;
+    rgb_ahd[1][nr_offset(y, x)][0] = rgb_ahd[1][nr_offset(y, x)][1] =
+        rgb_ahd[1][nr_offset(y, x)][2] = rgb_ahd[0][nr_offset(y, x)][0] =
+            rgb_ahd[0][nr_offset(y, x)][1] = rgb_ahd[0][nr_offset(y, x)][2] = 0;
+    int l = ndir[nr_offset(y, x)] & HVSH;
+    l /= HVSH;
+    if (ndir[nr_offset(y, x)] & VER)
+      rgb_ahd[1][nr_offset(y, x)][0] =
+          l * channel_maximum[0] / 4 + channel_maximum[0] / 4;
+    else
+      rgb_ahd[0][nr_offset(y, x)][2] =
+          l * channel_maximum[2] / 4 + channel_maximum[2] / 4;
+  }
+}
+
+void AAHD::make_ahd_rb_hv(int i)
+{
+  int iwidth = libraw.imgdata.sizes.iwidth;
+  int js = libraw.COLOR(i, 0) & 1;
+  int kc = libraw.COLOR(i, js);
+  js ^= 1; // Ð½Ð°Ñ‡Ð°Ð»ÑŒÐ½Ð°Ñ ÐºÐ¾Ð¾Ñ€Ð´Ð¸Ð½Ð°Ñ‚Ð° зелёного
+  int hvdir[2] = {Pe, Ps};
+  // интерполÑÑ†Ð¸Ñ Ð²ÐµÑ€Ñ‚Ð¸ÐºÐ°Ð»ÑŒÐ½Ñ‹Ñ… вертикально и горизонтальных горизонтально
+  for (int j = js; j < iwidth; j += 2)
+  {
+    int x = j + nr_margin;
+    int y = i + nr_margin;
+    int moff = nr_offset(y, x);
+    for (int d = 0; d < 2; ++d)
+    {
+      ushort3 *cnr;
+      cnr = &rgb_ahd[d][moff];
+      int c = kc ^ (d << 1); // цвет ÑоответÑвенного направлениÑ, длÑ
+                             // горизонтального c = kc, Ð´Ð»Ñ Ð²ÐµÑ€Ñ‚Ð¸ÐºÐ°Ð»ÑŒÐ½Ð¾Ð³Ð¾ c=kc^2
+      int h1 = cnr[-hvdir[d]][c] - cnr[-hvdir[d]][1];
+      int h2 = cnr[+hvdir[d]][c] - cnr[+hvdir[d]][1];
+      int h0 = (h1 + h2) / 2;
+      int eg = cnr[0][1] + h0;
+      //			int min = MIN(cnr[-hvdir[d]][c], cnr[+hvdir[d]][c]);
+      //			int max = MAX(cnr[-hvdir[d]][c], cnr[+hvdir[d]][c]);
+      //			min -= min / OverFraction;
+      //			max += max / OverFraction;
+      //			if (eg < min)
+      //				eg = min - sqrt(min - eg);
+      //			else if (eg > max)
+      //				eg = max + sqrt(eg - max);
+      if (eg > channel_maximum[c])
+        eg = channel_maximum[c];
+      else if (eg < channel_minimum[c])
+        eg = channel_minimum[c];
+      cnr[0][c] = eg;
+    }
+  }
+}
+
+void AAHD::make_ahd_rb()
+{
+  for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
+  {
+    make_ahd_rb_hv(i);
+  }
+  for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
+  {
+    make_ahd_rb_last(i);
+  }
+}
+
+void AAHD::make_ahd_rb_last(int i)
+{
+  int iwidth = libraw.imgdata.sizes.iwidth;
+  int js = libraw.COLOR(i, 0) & 1;
+  int kc = libraw.COLOR(i, js);
+  /*
+   * js -- Ð½Ð°Ñ‡Ð°Ð»ÑŒÐ½Ð°Ñ Ñ…-координата, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð¿Ð¾Ð¿Ð°Ð´Ð°ÐµÑ‚ мимо извеÑтного зелёного
+   * kc -- извеÑтный цвет в точке интерполированиÑ
+   */
+  int dirs[2][3] = {{Pnw, Pn, Pne}, {Pnw, Pw, Psw}};
+  int moff = nr_offset(i + nr_margin, nr_margin);
+  for (int j = 0; j < iwidth; j++)
+  {
+    for (int d = 0; d < 2; ++d)
+    {
+      ushort3 *cnr;
+      cnr = &rgb_ahd[d][moff + j];
+      int c = kc ^ 2;
+      if ((j & 1) != js)
+      {
+        // точка зелёного, Ð´Ð»Ñ Ð²ÐµÑ€Ñ‚Ð¸ÐºÐ°Ð»ÑŒÐ½Ð¾Ð³Ð¾ Ð½Ð°Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð½ÑƒÐ¶ÐµÐ½ альтернативный
+        // Ñтрочному цвет
+        c ^= d << 1;
+      }
+      int bh, bk;
+      int bgd = 0;
+      for (int k = 0; k < 3; ++k)
+        for (int h = 0; h < 3; ++h)
+        {
+          // градиент зелёного Ð¿Ð»ÑŽÑ Ð³Ñ€Ð°Ð´Ð¸ÐµÐ½Ñ‚ {r,b}
+          int gd =
+              ABS(2 * cnr[0][1] - (cnr[+dirs[d][k]][1] + cnr[-dirs[d][h]][1])) +
+              ABS(cnr[+dirs[d][k]][c] - cnr[-dirs[d][h]][c]) / 4 +
+              ABS(cnr[+dirs[d][k]][c] - cnr[+dirs[d][k]][1] +
+                  cnr[-dirs[d][h]][1] - cnr[-dirs[d][h]][c]) /
+                  4;
+          if (bgd == 0 || gd < bgd)
+          {
+            bgd = gd;
+            bh = h;
+            bk = k;
+          }
+        }
+      int h1 = cnr[+dirs[d][bk]][c] - cnr[+dirs[d][bk]][1];
+      int h2 = cnr[-dirs[d][bh]][c] - cnr[-dirs[d][bh]][1];
+      int eg = cnr[0][1] + (h1 + h2) / 2;
+      //			int min = MIN(cnr[+dirs[d][bk]][c], cnr[-dirs[d][bh]][c]);
+      //			int max = MAX(cnr[+dirs[d][bk]][c], cnr[-dirs[d][bh]][c]);
+      //			min -= min / OverFraction;
+      //			max += max / OverFraction;
+      //			if (eg < min)
+      //				eg = min - sqrt(min - eg);
+      //			else if (eg > max)
+      //				eg = max + sqrt(eg - max);
+      if (eg > channel_maximum[c])
+        eg = channel_maximum[c];
+      else if (eg < channel_minimum[c])
+        eg = channel_minimum[c];
+      cnr[0][c] = eg;
+    }
+  }
+}
+
+AAHD::~AAHD() { free(rgb_ahd[0]); }
+
+void LibRaw::aahd_interpolate()
+{
+  AAHD aahd(*this);
+  aahd.hide_hots();
+  aahd.make_ahd_greens();
+  aahd.make_ahd_rb();
+  aahd.evaluate_ahd();
+  aahd.refine_hv_dirs();
+  //	aahd.illustrate_dirs();
+  aahd.combine_image();
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/demosaic/ahd_demosaic.cpp libkdcraw/libkdcraw/libraw/src/demosaic/ahd_demosaic.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/demosaic/ahd_demosaic.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/demosaic/ahd_demosaic.cpp	2022-11-07 07:46:31.730795008 +0300
@@ -0,0 +1,354 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+/*
+   Adaptive Homogeneity-Directed interpolation is based on
+   the work of Keigo Hirakawa, Thomas Parks, and Paul Lee.
+ */
+
+void LibRaw::cielab(ushort rgb[3], short lab[3])
+{
+  int c, i, j, k;
+  float r, xyz[3];
+#ifdef LIBRAW_NOTHREADS
+  static float cbrt[0x10000], xyz_cam[3][4];
+#else
+#define cbrt tls->ahd_data.cbrt
+#define xyz_cam tls->ahd_data.xyz_cam
+#endif
+
+  if (!rgb)
+  {
+#ifndef LIBRAW_NOTHREADS
+    if (cbrt[0] < -1.0f)
+#endif
+      for (i = 0; i < 0x10000; i++)
+      {
+        r = i / 65535.0;
+        cbrt[i] =
+            r > 0.008856 ? pow(r, 1.f / 3.0f) : 7.787f * r + 16.f / 116.0f;
+      }
+    for (i = 0; i < 3; i++)
+      for (j = 0; j < colors; j++)
+        for (xyz_cam[i][j] = k = 0; k < 3; k++)
+          xyz_cam[i][j] += LibRaw_constants::xyz_rgb[i][k] * rgb_cam[k][j] /
+                           LibRaw_constants::d65_white[i];
+    return;
+  }
+  xyz[0] = xyz[1] = xyz[2] = 0.5;
+  FORCC
+  {
+    xyz[0] += xyz_cam[0][c] * rgb[c];
+    xyz[1] += xyz_cam[1][c] * rgb[c];
+    xyz[2] += xyz_cam[2][c] * rgb[c];
+  }
+  xyz[0] = cbrt[CLIP((int)xyz[0])];
+  xyz[1] = cbrt[CLIP((int)xyz[1])];
+  xyz[2] = cbrt[CLIP((int)xyz[2])];
+  lab[0] = 64 * (116 * xyz[1] - 16);
+  lab[1] = 64 * 500 * (xyz[0] - xyz[1]);
+  lab[2] = 64 * 200 * (xyz[1] - xyz[2]);
+#ifndef LIBRAW_NOTHREADS
+#undef cbrt
+#undef xyz_cam
+#endif
+}
+
+void LibRaw::ahd_interpolate_green_h_and_v(
+    int top, int left, ushort (*out_rgb)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3])
+{
+  int row, col;
+  int c, val;
+  ushort(*pix)[4];
+  const int rowlimit = MIN(top + LIBRAW_AHD_TILE, height - 2);
+  const int collimit = MIN(left + LIBRAW_AHD_TILE, width - 2);
+
+  for (row = top; row < rowlimit; row++)
+  {
+    col = left + (FC(row, left) & 1);
+    for (c = FC(row, col); col < collimit; col += 2)
+    {
+      pix = image + row * width + col;
+      val =
+          ((pix[-1][1] + pix[0][c] + pix[1][1]) * 2 - pix[-2][c] - pix[2][c]) >>
+          2;
+      out_rgb[0][row - top][col - left][1] = ULIM(val, pix[-1][1], pix[1][1]);
+      val = ((pix[-width][1] + pix[0][c] + pix[width][1]) * 2 -
+             pix[-2 * width][c] - pix[2 * width][c]) >>
+            2;
+      out_rgb[1][row - top][col - left][1] =
+          ULIM(val, pix[-width][1], pix[width][1]);
+    }
+  }
+}
+void LibRaw::ahd_interpolate_r_and_b_in_rgb_and_convert_to_cielab(
+    int top, int left, ushort (*inout_rgb)[LIBRAW_AHD_TILE][3],
+    short (*out_lab)[LIBRAW_AHD_TILE][3])
+{
+  unsigned row, col;
+  int c, val;
+  ushort(*pix)[4];
+  ushort(*rix)[3];
+  short(*lix)[3];
+  const unsigned num_pix_per_row = 4 * width;
+  const unsigned rowlimit = MIN(top + LIBRAW_AHD_TILE - 1, height - 3);
+  const unsigned collimit = MIN(left + LIBRAW_AHD_TILE - 1, width - 3);
+  ushort *pix_above;
+  ushort *pix_below;
+  int t1, t2;
+
+  for (row = top + 1; row < rowlimit; row++)
+  {
+    pix = image + row * width + left;
+    rix = &inout_rgb[row - top][0];
+    lix = &out_lab[row - top][0];
+
+    for (col = left + 1; col < collimit; col++)
+    {
+      pix++;
+      pix_above = &pix[0][0] - num_pix_per_row;
+      pix_below = &pix[0][0] + num_pix_per_row;
+      rix++;
+      lix++;
+
+      c = 2 - FC(row, col);
+
+      if (c == 1)
+      {
+        c = FC(row + 1, col);
+        t1 = 2 - c;
+        val = pix[0][1] +
+              ((pix[-1][t1] + pix[1][t1] - rix[-1][1] - rix[1][1]) >> 1);
+        rix[0][t1] = CLIP(val);
+        val =
+            pix[0][1] + ((pix_above[c] + pix_below[c] -
+                          rix[-LIBRAW_AHD_TILE][1] - rix[LIBRAW_AHD_TILE][1]) >>
+                         1);
+      }
+      else
+      {
+        t1 = -4 + c; /* -4+c: pixel of color c to the left */
+        t2 = 4 + c;  /* 4+c: pixel of color c to the right */
+        val = rix[0][1] +
+              ((pix_above[t1] + pix_above[t2] + pix_below[t1] + pix_below[t2] -
+                rix[-LIBRAW_AHD_TILE - 1][1] - rix[-LIBRAW_AHD_TILE + 1][1] -
+                rix[+LIBRAW_AHD_TILE - 1][1] - rix[+LIBRAW_AHD_TILE + 1][1] +
+                1) >>
+               2);
+      }
+      rix[0][c] = CLIP(val);
+      c = FC(row, col);
+      rix[0][c] = pix[0][c];
+      cielab(rix[0], lix[0]);
+    }
+  }
+}
+void LibRaw::ahd_interpolate_r_and_b_and_convert_to_cielab(
+    int top, int left, ushort (*inout_rgb)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3],
+    short (*out_lab)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3])
+{
+  int direction;
+  for (direction = 0; direction < 2; direction++)
+  {
+    ahd_interpolate_r_and_b_in_rgb_and_convert_to_cielab(
+        top, left, inout_rgb[direction], out_lab[direction]);
+  }
+}
+
+void LibRaw::ahd_interpolate_build_homogeneity_map(
+    int top, int left, short (*lab)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3],
+    char (*out_homogeneity_map)[LIBRAW_AHD_TILE][2])
+{
+  int row, col;
+  int tr;
+  int direction;
+  int i;
+  short(*lix)[3];
+  short(*lixs[2])[3];
+  short *adjacent_lix;
+  unsigned ldiff[2][4], abdiff[2][4], leps, abeps;
+  static const int dir[4] = {-1, 1, -LIBRAW_AHD_TILE, LIBRAW_AHD_TILE};
+  const int rowlimit = MIN(top + LIBRAW_AHD_TILE - 2, height - 4);
+  const int collimit = MIN(left + LIBRAW_AHD_TILE - 2, width - 4);
+  int homogeneity;
+  char(*homogeneity_map_p)[2];
+
+  memset(out_homogeneity_map, 0, 2 * LIBRAW_AHD_TILE * LIBRAW_AHD_TILE);
+
+  for (row = top + 2; row < rowlimit; row++)
+  {
+    tr = row - top;
+    homogeneity_map_p = &out_homogeneity_map[tr][1];
+    for (direction = 0; direction < 2; direction++)
+    {
+      lixs[direction] = &lab[direction][tr][1];
+    }
+
+    for (col = left + 2; col < collimit; col++)
+    {
+      homogeneity_map_p++;
+
+      for (direction = 0; direction < 2; direction++)
+      {
+        lix = ++lixs[direction];
+        for (i = 0; i < 4; i++)
+        {
+          adjacent_lix = lix[dir[i]];
+          ldiff[direction][i] = ABS(lix[0][0] - adjacent_lix[0]);
+          abdiff[direction][i] = SQR(lix[0][1] - adjacent_lix[1]) +
+                                 SQR(lix[0][2] - adjacent_lix[2]);
+        }
+      }
+      leps = MIN(MAX(ldiff[0][0], ldiff[0][1]), MAX(ldiff[1][2], ldiff[1][3]));
+      abeps =
+          MIN(MAX(abdiff[0][0], abdiff[0][1]), MAX(abdiff[1][2], abdiff[1][3]));
+      for (direction = 0; direction < 2; direction++)
+      {
+        homogeneity = 0;
+        for (i = 0; i < 4; i++)
+        {
+          if (ldiff[direction][i] <= leps && abdiff[direction][i] <= abeps)
+          {
+            homogeneity++;
+          }
+        }
+        homogeneity_map_p[0][direction] = homogeneity;
+      }
+    }
+  }
+}
+void LibRaw::ahd_interpolate_combine_homogeneous_pixels(
+    int top, int left, ushort (*rgb)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3],
+    char (*homogeneity_map)[LIBRAW_AHD_TILE][2])
+{
+  int row, col;
+  int tr, tc;
+  int i, j;
+  int direction;
+  int hm[2];
+  int c;
+  const int rowlimit = MIN(top + LIBRAW_AHD_TILE - 3, height - 5);
+  const int collimit = MIN(left + LIBRAW_AHD_TILE - 3, width - 5);
+
+  ushort(*pix)[4];
+  ushort(*rix[2])[3];
+
+  for (row = top + 3; row < rowlimit; row++)
+  {
+    tr = row - top;
+    pix = &image[row * width + left + 2];
+    for (direction = 0; direction < 2; direction++)
+    {
+      rix[direction] = &rgb[direction][tr][2];
+    }
+
+    for (col = left + 3; col < collimit; col++)
+    {
+      tc = col - left;
+      pix++;
+      for (direction = 0; direction < 2; direction++)
+      {
+        rix[direction]++;
+      }
+
+      for (direction = 0; direction < 2; direction++)
+      {
+        hm[direction] = 0;
+        for (i = tr - 1; i <= tr + 1; i++)
+        {
+          for (j = tc - 1; j <= tc + 1; j++)
+          {
+            hm[direction] += homogeneity_map[i][j][direction];
+          }
+        }
+      }
+      if (hm[0] != hm[1])
+      {
+        memcpy(pix[0], rix[hm[1] > hm[0]][0], 3 * sizeof(ushort));
+      }
+      else
+      {
+        FORC3 { pix[0][c] = (rix[0][0][c] + rix[1][0][c]) >> 1; }
+      }
+    }
+  }
+}
+void LibRaw::ahd_interpolate()
+{
+  int top, left;
+  char *buffer;
+  ushort(*rgb)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3];
+  short(*lab)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3];
+  char(*homo)[LIBRAW_AHD_TILE][2];
+  int terminate_flag = 0;
+
+  cielab(0, 0);
+  border_interpolate(5);
+
+#ifdef LIBRAW_USE_OPENMP
+#pragma omp parallel private(buffer, rgb, lab, homo, top, left )       \
+    shared(terminate_flag)
+#endif
+  {
+#ifdef LIBRAW_USE_OPENMP
+#pragma omp critical
+#endif
+    buffer =
+        (char *)malloc(26 * LIBRAW_AHD_TILE * LIBRAW_AHD_TILE); /* 1664 kB */
+    merror(buffer, "ahd_interpolate()");
+    rgb = (ushort(*)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3])buffer;
+    lab = (short(*)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3])(
+        buffer + 12 * LIBRAW_AHD_TILE * LIBRAW_AHD_TILE);
+    homo = (char(*)[LIBRAW_AHD_TILE][2])(buffer + 24 * LIBRAW_AHD_TILE *
+                                                      LIBRAW_AHD_TILE);
+
+#ifdef LIBRAW_USE_OPENMP
+#pragma omp for schedule(dynamic)
+#endif
+    for (top = 2; top < height - 5; top += LIBRAW_AHD_TILE - 6)
+    {
+#ifdef LIBRAW_USE_OPENMP
+      if (0 == omp_get_thread_num())
+#endif
+        if (callbacks.progress_cb)
+        {
+          int rr = (*callbacks.progress_cb)(callbacks.progresscb_data,
+                                            LIBRAW_PROGRESS_INTERPOLATE,
+                                            top - 2, height - 7);
+          if (rr)
+            terminate_flag = 1;
+        }
+      for (left = 2; !terminate_flag && (left < width - 5);
+           left += LIBRAW_AHD_TILE - 6)
+      {
+        ahd_interpolate_green_h_and_v(top, left, rgb);
+        ahd_interpolate_r_and_b_and_convert_to_cielab(top, left, rgb, lab);
+        ahd_interpolate_build_homogeneity_map(top, left, lab, homo);
+        ahd_interpolate_combine_homogeneous_pixels(top, left, rgb, homo);
+      }
+    }
+#ifdef LIBRAW_USE_OPENMP
+#pragma omp critical
+#endif
+    free(buffer);
+  }
+  if (terminate_flag)
+    throw LIBRAW_EXCEPTION_CANCELLED_BY_CALLBACK;
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/demosaic/dcb_demosaic.cpp libkdcraw/libkdcraw/libraw/src/demosaic/dcb_demosaic.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/demosaic/dcb_demosaic.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/demosaic/dcb_demosaic.cpp	2022-11-07 07:46:31.730795008 +0300
@@ -0,0 +1,901 @@
+/*
+ *    Copyright (C) 2010,  Jacek Gozdz (cuniek@kft.umcs.lublin.pl)
+ *
+ *    This code is licensed under a (3-clause) BSD license as follows :
+ *
+ *    Redistribution and use in source and binary forms, with or without
+ *    modification, are permitted provided that the following
+ *	  conditions are met:
+ *
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *		disclaimer in the documentation and/or other materials provided
+ * 	    with the distribution.
+ *    * Neither the name of the author nor the names of its
+ *      contributors may be used to endorse or promote products
+ * 		derived from this software without specific prior written permission.
+ *
+ *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * 	  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * 	  THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * 	  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * 	  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * 	  SERVICES; LOSS OF USE,
+ *    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 	  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ *	  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * 	  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ *    OF SUCH DAMAGE.
+ */
+
+// DCB demosaicing by Jacek Gozdz (cuniek@kft.umcs.lublin.pl)
+
+// FBDD denoising by Jacek Gozdz (cuniek@kft.umcs.lublin.pl) and
+// Luis Sanz Rodríguez (luis.sanz.rodriguez@gmail.com)
+
+// last modification: 11.07.2010
+
+#include "../../internal/dcraw_defs.h"
+
+// interpolates green vertically and saves it to image3
+void LibRaw::dcb_ver(float (*image3)[3])
+{
+  int row, col, u = width, indx;
+
+  for (row = 2; row < height - 2; row++)
+    for (col = 2 + (FC(row, 2) & 1), indx = row * width + col; col < u - 2;
+         col += 2, indx += 2)
+    {
+
+      image3[indx][1] = CLIP((image[indx + u][1] + image[indx - u][1]) / 2.0);
+    }
+}
+
+// interpolates green horizontally and saves it to image2
+void LibRaw::dcb_hor(float (*image2)[3])
+{
+  int row, col, u = width, indx;
+
+  for (row = 2; row < height - 2; row++)
+    for (col = 2 + (FC(row, 2) & 1), indx = row * width + col; col < u - 2;
+         col += 2, indx += 2)
+    {
+
+      image2[indx][1] = CLIP((image[indx + 1][1] + image[indx - 1][1]) / 2.0);
+    }
+}
+
+// missing colors are interpolated
+void LibRaw::dcb_color()
+{
+  int row, col, c, d, u = width, indx;
+
+  for (row = 1; row < height - 1; row++)
+    for (col = 1 + (FC(row, 1) & 1), indx = row * width + col,
+        c = 2 - FC(row, col);
+         col < u - 1; col += 2, indx += 2)
+    {
+
+      image[indx][c] = CLIP((4 * image[indx][1] - image[indx + u + 1][1] -
+                             image[indx + u - 1][1] - image[indx - u + 1][1] -
+                             image[indx - u - 1][1] + image[indx + u + 1][c] +
+                             image[indx + u - 1][c] + image[indx - u + 1][c] +
+                             image[indx - u - 1][c]) /
+                            4.0);
+    }
+
+  for (row = 1; row < height - 1; row++)
+    for (col = 1 + (FC(row, 2) & 1), indx = row * width + col,
+        c = FC(row, col + 1), d = 2 - c;
+         col < width - 1; col += 2, indx += 2)
+    {
+
+      image[indx][c] =
+          CLIP((2 * image[indx][1] - image[indx + 1][1] - image[indx - 1][1] +
+                image[indx + 1][c] + image[indx - 1][c]) /
+               2.0);
+      image[indx][d] =
+          CLIP((2 * image[indx][1] - image[indx + u][1] - image[indx - u][1] +
+                image[indx + u][d] + image[indx - u][d]) /
+               2.0);
+    }
+}
+
+// missing R and B are interpolated horizontally and saved in image2
+void LibRaw::dcb_color2(float (*image2)[3])
+{
+  int row, col, c, d, u = width, indx;
+
+  for (row = 1; row < height - 1; row++)
+    for (col = 1 + (FC(row, 1) & 1), indx = row * width + col,
+        c = 2 - FC(row, col);
+         col < u - 1; col += 2, indx += 2)
+    {
+
+      image2[indx][c] =
+          CLIP((4 * image2[indx][1] - image2[indx + u + 1][1] -
+                image2[indx + u - 1][1] - image2[indx - u + 1][1] -
+                image2[indx - u - 1][1] + image[indx + u + 1][c] +
+                image[indx + u - 1][c] + image[indx - u + 1][c] +
+                image[indx - u - 1][c]) /
+               4.0);
+    }
+
+  for (row = 1; row < height - 1; row++)
+    for (col = 1 + (FC(row, 2) & 1), indx = row * width + col,
+        c = FC(row, col + 1), d = 2 - c;
+         col < width - 1; col += 2, indx += 2)
+    {
+
+      image2[indx][c] = CLIP((image[indx + 1][c] + image[indx - 1][c]) / 2.0);
+      image2[indx][d] =
+          CLIP((2 * image2[indx][1] - image2[indx + u][1] -
+                image2[indx - u][1] + image[indx + u][d] + image[indx - u][d]) /
+               2.0);
+    }
+}
+
+// missing R and B are interpolated vertically and saved in image3
+void LibRaw::dcb_color3(float (*image3)[3])
+{
+  int row, col, c, d, u = width, indx;
+
+  for (row = 1; row < height - 1; row++)
+    for (col = 1 + (FC(row, 1) & 1), indx = row * width + col,
+        c = 2 - FC(row, col);
+         col < u - 1; col += 2, indx += 2)
+    {
+
+      image3[indx][c] =
+          CLIP((4 * image3[indx][1] - image3[indx + u + 1][1] -
+                image3[indx + u - 1][1] - image3[indx - u + 1][1] -
+                image3[indx - u - 1][1] + image[indx + u + 1][c] +
+                image[indx + u - 1][c] + image[indx - u + 1][c] +
+                image[indx - u - 1][c]) /
+               4.0);
+    }
+
+  for (row = 1; row < height - 1; row++)
+    for (col = 1 + (FC(row, 2) & 1), indx = row * width + col,
+        c = FC(row, col + 1), d = 2 - c;
+         col < width - 1; col += 2, indx += 2)
+    {
+
+      image3[indx][c] =
+          CLIP((2 * image3[indx][1] - image3[indx + 1][1] -
+                image3[indx - 1][1] + image[indx + 1][c] + image[indx - 1][c]) /
+               2.0);
+      image3[indx][d] = CLIP((image[indx + u][d] + image[indx - u][d]) / 2.0);
+    }
+}
+
+// decides the primary green interpolation direction
+void LibRaw::dcb_decide(float (*image2)[3], float (*image3)[3])
+{
+  int row, col, c, d, u = width, v = 2 * u, indx;
+  float current, current2, current3;
+
+  for (row = 2; row < height - 2; row++)
+    for (col = 2 + (FC(row, 2) & 1), indx = row * width + col, c = FC(row, col);
+         col < u - 2; col += 2, indx += 2)
+    {
+
+      d = ABS(c - 2);
+
+      current = MAX(image[indx + v][c],
+                    MAX(image[indx - v][c],
+                        MAX(image[indx - 2][c], image[indx + 2][c]))) -
+                MIN(image[indx + v][c],
+                    MIN(image[indx - v][c],
+                        MIN(image[indx - 2][c], image[indx + 2][c]))) +
+                MAX(image[indx + 1 + u][d],
+                    MAX(image[indx + 1 - u][d],
+                        MAX(image[indx - 1 + u][d], image[indx - 1 - u][d]))) -
+                MIN(image[indx + 1 + u][d],
+                    MIN(image[indx + 1 - u][d],
+                        MIN(image[indx - 1 + u][d], image[indx - 1 - u][d])));
+
+      current2 =
+          MAX(image2[indx + v][d],
+              MAX(image2[indx - v][d],
+                  MAX(image2[indx - 2][d], image2[indx + 2][d]))) -
+          MIN(image2[indx + v][d],
+              MIN(image2[indx - v][d],
+                  MIN(image2[indx - 2][d], image2[indx + 2][d]))) +
+          MAX(image2[indx + 1 + u][c],
+              MAX(image2[indx + 1 - u][c],
+                  MAX(image2[indx - 1 + u][c], image2[indx - 1 - u][c]))) -
+          MIN(image2[indx + 1 + u][c],
+              MIN(image2[indx + 1 - u][c],
+                  MIN(image2[indx - 1 + u][c], image2[indx - 1 - u][c])));
+
+      current3 =
+          MAX(image3[indx + v][d],
+              MAX(image3[indx - v][d],
+                  MAX(image3[indx - 2][d], image3[indx + 2][d]))) -
+          MIN(image3[indx + v][d],
+              MIN(image3[indx - v][d],
+                  MIN(image3[indx - 2][d], image3[indx + 2][d]))) +
+          MAX(image3[indx + 1 + u][c],
+              MAX(image3[indx + 1 - u][c],
+                  MAX(image3[indx - 1 + u][c], image3[indx - 1 - u][c]))) -
+          MIN(image3[indx + 1 + u][c],
+              MIN(image3[indx + 1 - u][c],
+                  MIN(image3[indx - 1 + u][c], image3[indx - 1 - u][c])));
+
+      if (ABS(current - current2) < ABS(current - current3))
+        image[indx][1] = image2[indx][1];
+      else
+        image[indx][1] = image3[indx][1];
+    }
+}
+
+// saves red and blue in image2
+void LibRaw::dcb_copy_to_buffer(float (*image2)[3])
+{
+  int indx;
+
+  for (indx = 0; indx < height * width; indx++)
+  {
+    image2[indx][0] = image[indx][0]; // R
+    image2[indx][2] = image[indx][2]; // B
+  }
+}
+
+// restores red and blue from image2
+void LibRaw::dcb_restore_from_buffer(float (*image2)[3])
+{
+  int indx;
+
+  for (indx = 0; indx < height * width; indx++)
+  {
+    image[indx][0] = image2[indx][0]; // R
+    image[indx][2] = image2[indx][2]; // B
+  }
+}
+
+// R and B smoothing using green contrast, all pixels except 2 pixel wide border
+void LibRaw::dcb_pp()
+{
+  int g1, r1, b1, u = width, indx, row, col;
+
+  for (row = 2; row < height - 2; row++)
+    for (col = 2, indx = row * u + col; col < width - 2; col++, indx++)
+    {
+
+      r1 = (image[indx - 1][0] + image[indx + 1][0] + image[indx - u][0] +
+            image[indx + u][0] + image[indx - u - 1][0] +
+            image[indx + u + 1][0] + image[indx - u + 1][0] +
+            image[indx + u - 1][0]) /
+           8.0;
+      g1 = (image[indx - 1][1] + image[indx + 1][1] + image[indx - u][1] +
+            image[indx + u][1] + image[indx - u - 1][1] +
+            image[indx + u + 1][1] + image[indx - u + 1][1] +
+            image[indx + u - 1][1]) /
+           8.0;
+      b1 = (image[indx - 1][2] + image[indx + 1][2] + image[indx - u][2] +
+            image[indx + u][2] + image[indx - u - 1][2] +
+            image[indx + u + 1][2] + image[indx - u + 1][2] +
+            image[indx + u - 1][2]) /
+           8.0;
+
+      image[indx][0] = CLIP(r1 + (image[indx][1] - g1));
+      image[indx][2] = CLIP(b1 + (image[indx][1] - g1));
+    }
+}
+
+// green blurring correction, helps to get the nyquist right
+void LibRaw::dcb_nyquist()
+{
+  int row, col, c, u = width, v = 2 * u, indx;
+
+  for (row = 2; row < height - 2; row++)
+    for (col = 2 + (FC(row, 2) & 1), indx = row * width + col, c = FC(row, col);
+         col < u - 2; col += 2, indx += 2)
+    {
+
+      image[indx][1] = CLIP((image[indx + v][1] + image[indx - v][1] +
+                             image[indx - 2][1] + image[indx + 2][1]) /
+                                4.0 +
+                            image[indx][c] -
+                            (image[indx + v][c] + image[indx - v][c] +
+                             image[indx - 2][c] + image[indx + 2][c]) /
+                                4.0);
+    }
+}
+
+// missing colors are interpolated using high quality algorithm by Luis Sanz
+// Rodríguez
+void LibRaw::dcb_color_full()
+{
+  int row, col, c, d, u = width, w = 3 * u, indx, g1, g2;
+  float f[4], g[4], (*chroma)[2];
+
+  chroma = (float(*)[2])calloc(width * height, sizeof *chroma);
+  merror(chroma, "dcb_color_full()");
+
+  for (row = 1; row < height - 1; row++)
+    for (col = 1 + (FC(row, 1) & 1), indx = row * width + col, c = FC(row, col),
+        d = c / 2;
+         col < u - 1; col += 2, indx += 2)
+      chroma[indx][d] = image[indx][c] - image[indx][1];
+
+  for (row = 3; row < height - 3; row++)
+    for (col = 3 + (FC(row, 1) & 1), indx = row * width + col,
+        c = 1 - FC(row, col) / 2, d = 1 - c;
+         col < u - 3; col += 2, indx += 2)
+    {
+      f[0] = 1.0 /
+             (float)(1.0 +
+                     fabs(chroma[indx - u - 1][c] - chroma[indx + u + 1][c]) +
+                     fabs(chroma[indx - u - 1][c] - chroma[indx - w - 3][c]) +
+                     fabs(chroma[indx + u + 1][c] - chroma[indx - w - 3][c]));
+      f[1] = 1.0 /
+             (float)(1.0 +
+                     fabs(chroma[indx - u + 1][c] - chroma[indx + u - 1][c]) +
+                     fabs(chroma[indx - u + 1][c] - chroma[indx - w + 3][c]) +
+                     fabs(chroma[indx + u - 1][c] - chroma[indx - w + 3][c]));
+      f[2] = 1.0 /
+             (float)(1.0 +
+                     fabs(chroma[indx + u - 1][c] - chroma[indx - u + 1][c]) +
+                     fabs(chroma[indx + u - 1][c] - chroma[indx + w + 3][c]) +
+                     fabs(chroma[indx - u + 1][c] - chroma[indx + w - 3][c]));
+      f[3] = 1.0 /
+             (float)(1.0 +
+                     fabs(chroma[indx + u + 1][c] - chroma[indx - u - 1][c]) +
+                     fabs(chroma[indx + u + 1][c] - chroma[indx + w - 3][c]) +
+                     fabs(chroma[indx - u - 1][c] - chroma[indx + w + 3][c]));
+      g[0] = 1.325 * chroma[indx - u - 1][c] - 0.175 * chroma[indx - w - 3][c] -
+             0.075 * chroma[indx - w - 1][c] - 0.075 * chroma[indx - u - 3][c];
+      g[1] = 1.325 * chroma[indx - u + 1][c] - 0.175 * chroma[indx - w + 3][c] -
+             0.075 * chroma[indx - w + 1][c] - 0.075 * chroma[indx - u + 3][c];
+      g[2] = 1.325 * chroma[indx + u - 1][c] - 0.175 * chroma[indx + w - 3][c] -
+             0.075 * chroma[indx + w - 1][c] - 0.075 * chroma[indx + u - 3][c];
+      g[3] = 1.325 * chroma[indx + u + 1][c] - 0.175 * chroma[indx + w + 3][c] -
+             0.075 * chroma[indx + w + 1][c] - 0.075 * chroma[indx + u + 3][c];
+      chroma[indx][c] =
+          (f[0] * g[0] + f[1] * g[1] + f[2] * g[2] + f[3] * g[3]) /
+          (f[0] + f[1] + f[2] + f[3]);
+    }
+  for (row = 3; row < height - 3; row++)
+    for (col = 3 + (FC(row, 2) & 1), indx = row * width + col,
+        c = FC(row, col + 1) / 2;
+         col < u - 3; col += 2, indx += 2)
+      for (d = 0; d <= 1; c = 1 - c, d++)
+      {
+        f[0] = 1.0 /
+               (float)(1.0 + fabs(chroma[indx - u][c] - chroma[indx + u][c]) +
+                       fabs(chroma[indx - u][c] - chroma[indx - w][c]) +
+                       fabs(chroma[indx + u][c] - chroma[indx - w][c]));
+        f[1] = 1.0 /
+               (float)(1.0 + fabs(chroma[indx + 1][c] - chroma[indx - 1][c]) +
+                       fabs(chroma[indx + 1][c] - chroma[indx + 3][c]) +
+                       fabs(chroma[indx - 1][c] - chroma[indx + 3][c]));
+        f[2] = 1.0 /
+               (float)(1.0 + fabs(chroma[indx - 1][c] - chroma[indx + 1][c]) +
+                       fabs(chroma[indx - 1][c] - chroma[indx - 3][c]) +
+                       fabs(chroma[indx + 1][c] - chroma[indx - 3][c]));
+        f[3] = 1.0 /
+               (float)(1.0 + fabs(chroma[indx + u][c] - chroma[indx - u][c]) +
+                       fabs(chroma[indx + u][c] - chroma[indx + w][c]) +
+                       fabs(chroma[indx - u][c] - chroma[indx + w][c]));
+
+        g[0] = 0.875 * chroma[indx - u][c] + 0.125 * chroma[indx - w][c];
+        g[1] = 0.875 * chroma[indx + 1][c] + 0.125 * chroma[indx + 3][c];
+        g[2] = 0.875 * chroma[indx - 1][c] + 0.125 * chroma[indx - 3][c];
+        g[3] = 0.875 * chroma[indx + u][c] + 0.125 * chroma[indx + w][c];
+
+        chroma[indx][c] =
+            (f[0] * g[0] + f[1] * g[1] + f[2] * g[2] + f[3] * g[3]) /
+            (f[0] + f[1] + f[2] + f[3]);
+      }
+
+  for (row = 6; row < height - 6; row++)
+    for (col = 6, indx = row * width + col; col < width - 6; col++, indx++)
+    {
+      image[indx][0] = CLIP(chroma[indx][0] + image[indx][1]);
+      image[indx][2] = CLIP(chroma[indx][1] + image[indx][1]);
+
+      g1 = MIN(
+          image[indx + 1 + u][0],
+          MIN(image[indx + 1 - u][0],
+              MIN(image[indx - 1 + u][0],
+                  MIN(image[indx - 1 - u][0],
+                      MIN(image[indx - 1][0],
+                          MIN(image[indx + 1][0],
+                              MIN(image[indx - u][0], image[indx + u][0])))))));
+
+      g2 = MAX(
+          image[indx + 1 + u][0],
+          MAX(image[indx + 1 - u][0],
+              MAX(image[indx - 1 + u][0],
+                  MAX(image[indx - 1 - u][0],
+                      MAX(image[indx - 1][0],
+                          MAX(image[indx + 1][0],
+                              MAX(image[indx - u][0], image[indx + u][0])))))));
+
+      image[indx][0] = ULIM(image[indx][0], g2, g1);
+
+      g1 = MIN(
+          image[indx + 1 + u][2],
+          MIN(image[indx + 1 - u][2],
+              MIN(image[indx - 1 + u][2],
+                  MIN(image[indx - 1 - u][2],
+                      MIN(image[indx - 1][2],
+                          MIN(image[indx + 1][2],
+                              MIN(image[indx - u][2], image[indx + u][2])))))));
+
+      g2 = MAX(
+          image[indx + 1 + u][2],
+          MAX(image[indx + 1 - u][2],
+              MAX(image[indx - 1 + u][2],
+                  MAX(image[indx - 1 - u][2],
+                      MAX(image[indx - 1][2],
+                          MAX(image[indx + 1][2],
+                              MAX(image[indx - u][2], image[indx + u][2])))))));
+
+      image[indx][2] = ULIM(image[indx][2], g2, g1);
+    }
+
+  free(chroma);
+}
+
+// green is used to create an interpolation direction map saved in image[][3]
+// 1 = vertical
+// 0 = horizontal
+void LibRaw::dcb_map()
+{
+  int row, col, u = width, indx;
+
+  for (row = 1; row < height - 1; row++)
+  {
+    for (col = 1, indx = row * width + col; col < width - 1; col++, indx++)
+    {
+
+      if (image[indx][1] > (image[indx - 1][1] + image[indx + 1][1] +
+                            image[indx - u][1] + image[indx + u][1]) /
+                               4.0)
+        image[indx][3] = ((MIN(image[indx - 1][1], image[indx + 1][1]) +
+                           image[indx - 1][1] + image[indx + 1][1]) <
+                          (MIN(image[indx - u][1], image[indx + u][1]) +
+                           image[indx - u][1] + image[indx + u][1]));
+      else
+        image[indx][3] = ((MAX(image[indx - 1][1], image[indx + 1][1]) +
+                           image[indx - 1][1] + image[indx + 1][1]) >
+                          (MAX(image[indx - u][1], image[indx + u][1]) +
+                           image[indx - u][1] + image[indx + u][1]));
+    }
+  }
+}
+
+// interpolated green pixels are corrected using the map
+void LibRaw::dcb_correction()
+{
+  int current, row, col, u = width, v = 2 * u, indx;
+
+  for (row = 2; row < height - 2; row++)
+    for (col = 2 + (FC(row, 2) & 1), indx = row * width + col; col < u - 2;
+         col += 2, indx += 2)
+    {
+
+      current = 4 * image[indx][3] +
+                2 * (image[indx + u][3] + image[indx - u][3] +
+                     image[indx + 1][3] + image[indx - 1][3]) +
+                image[indx + v][3] + image[indx - v][3] + image[indx + 2][3] +
+                image[indx - 2][3];
+
+      image[indx][1] =
+          ((16 - current) * (image[indx - 1][1] + image[indx + 1][1]) / 2.0 +
+           current * (image[indx - u][1] + image[indx + u][1]) / 2.0) /
+          16.0;
+    }
+}
+
+// interpolated green pixels are corrected using the map
+// with contrast correction
+void LibRaw::dcb_correction2()
+{
+  int current, row, col, c, u = width, v = 2 * u, indx;
+
+  for (row = 4; row < height - 4; row++)
+    for (col = 4 + (FC(row, 2) & 1), indx = row * width + col, c = FC(row, col);
+         col < u - 4; col += 2, indx += 2)
+    {
+
+      current = 4 * image[indx][3] +
+                2 * (image[indx + u][3] + image[indx - u][3] +
+                     image[indx + 1][3] + image[indx - 1][3]) +
+                image[indx + v][3] + image[indx - v][3] + image[indx + 2][3] +
+                image[indx - 2][3];
+
+      image[indx][1] = CLIP(
+          ((16 - current) * ((image[indx - 1][1] + image[indx + 1][1]) / 2.0 +
+                             image[indx][c] -
+                             (image[indx + 2][c] + image[indx - 2][c]) / 2.0) +
+           current * ((image[indx - u][1] + image[indx + u][1]) / 2.0 +
+                      image[indx][c] -
+                      (image[indx + v][c] + image[indx - v][c]) / 2.0)) /
+          16.0);
+    }
+}
+
+void LibRaw::dcb_refinement()
+{
+  int row, col, c, u = width, v = 2 * u, w = 3 * u, indx, current;
+  float f[5], g1, g2;
+
+  for (row = 4; row < height - 4; row++)
+    for (col = 4 + (FC(row, 2) & 1), indx = row * width + col, c = FC(row, col);
+         col < u - 4; col += 2, indx += 2)
+    {
+
+      current = 4 * image[indx][3] +
+                2 * (image[indx + u][3] + image[indx - u][3] +
+                     image[indx + 1][3] + image[indx - 1][3]) +
+                image[indx + v][3] + image[indx - v][3] + image[indx - 2][3] +
+                image[indx + 2][3];
+
+      if (image[indx][c] > 1)
+      {
+
+        f[0] = (float)(image[indx - u][1] + image[indx + u][1]) /
+               (2 * image[indx][c]);
+
+        if (image[indx - v][c] > 0)
+          f[1] = 2 * (float)image[indx - u][1] /
+                 (image[indx - v][c] + image[indx][c]);
+        else
+          f[1] = f[0];
+
+        if (image[indx - v][c] > 0)
+          f[2] = (float)(image[indx - u][1] + image[indx - w][1]) /
+                 (2 * image[indx - v][c]);
+        else
+          f[2] = f[0];
+
+        if (image[indx + v][c] > 0)
+          f[3] = 2 * (float)image[indx + u][1] /
+                 (image[indx + v][c] + image[indx][c]);
+        else
+          f[3] = f[0];
+
+        if (image[indx + v][c] > 0)
+          f[4] = (float)(image[indx + u][1] + image[indx + w][1]) /
+                 (2 * image[indx + v][c]);
+        else
+          f[4] = f[0];
+
+        g1 = (5 * f[0] + 3 * f[1] + f[2] + 3 * f[3] + f[4]) / 13.0;
+
+        f[0] = (float)(image[indx - 1][1] + image[indx + 1][1]) /
+               (2 * image[indx][c]);
+
+        if (image[indx - 2][c] > 0)
+          f[1] = 2 * (float)image[indx - 1][1] /
+                 (image[indx - 2][c] + image[indx][c]);
+        else
+          f[1] = f[0];
+
+        if (image[indx - 2][c] > 0)
+          f[2] = (float)(image[indx - 1][1] + image[indx - 3][1]) /
+                 (2 * image[indx - 2][c]);
+        else
+          f[2] = f[0];
+
+        if (image[indx + 2][c] > 0)
+          f[3] = 2 * (float)image[indx + 1][1] /
+                 (image[indx + 2][c] + image[indx][c]);
+        else
+          f[3] = f[0];
+
+        if (image[indx + 2][c] > 0)
+          f[4] = (float)(image[indx + 1][1] + image[indx + 3][1]) /
+                 (2 * image[indx + 2][c]);
+        else
+          f[4] = f[0];
+
+        g2 = (5 * f[0] + 3 * f[1] + f[2] + 3 * f[3] + f[4]) / 13.0;
+
+        image[indx][1] = CLIP((image[indx][c]) *
+                              (current * g1 + (16 - current) * g2) / 16.0);
+      }
+      else
+        image[indx][1] = image[indx][c];
+
+      // get rid of overshooted pixels
+
+      g1 = MIN(
+          image[indx + 1 + u][1],
+          MIN(image[indx + 1 - u][1],
+              MIN(image[indx - 1 + u][1],
+                  MIN(image[indx - 1 - u][1],
+                      MIN(image[indx - 1][1],
+                          MIN(image[indx + 1][1],
+                              MIN(image[indx - u][1], image[indx + u][1])))))));
+
+      g2 = MAX(
+          image[indx + 1 + u][1],
+          MAX(image[indx + 1 - u][1],
+              MAX(image[indx - 1 + u][1],
+                  MAX(image[indx - 1 - u][1],
+                      MAX(image[indx - 1][1],
+                          MAX(image[indx + 1][1],
+                              MAX(image[indx - u][1], image[indx + u][1])))))));
+
+      image[indx][1] = ULIM(image[indx][1], g2, g1);
+    }
+}
+
+// converts RGB to LCH colorspace and saves it to image3
+void LibRaw::rgb_to_lch(double (*image2)[3])
+{
+  int indx;
+  for (indx = 0; indx < height * width; indx++)
+  {
+
+    image2[indx][0] = image[indx][0] + image[indx][1] + image[indx][2]; // L
+    image2[indx][1] = 1.732050808 * (image[indx][0] - image[indx][1]);  // C
+    image2[indx][2] =
+        2.0 * image[indx][2] - image[indx][0] - image[indx][1]; // H
+  }
+}
+
+// converts LCH to RGB colorspace and saves it back to image
+void LibRaw::lch_to_rgb(double (*image2)[3])
+{
+  int indx;
+  for (indx = 0; indx < height * width; indx++)
+  {
+
+    image[indx][0] = CLIP(image2[indx][0] / 3.0 - image2[indx][2] / 6.0 +
+                          image2[indx][1] / 3.464101615);
+    image[indx][1] = CLIP(image2[indx][0] / 3.0 - image2[indx][2] / 6.0 -
+                          image2[indx][1] / 3.464101615);
+    image[indx][2] = CLIP(image2[indx][0] / 3.0 + image2[indx][2] / 3.0);
+  }
+}
+
+// denoising using interpolated neighbours
+void LibRaw::fbdd_correction()
+{
+  int row, col, c, u = width, indx;
+
+  for (row = 2; row < height - 2; row++)
+  {
+    for (col = 2, indx = row * width + col; col < width - 2; col++, indx++)
+    {
+
+      c = fcol(row, col);
+
+      image[indx][c] =
+          ULIM(image[indx][c],
+               MAX(image[indx - 1][c],
+                   MAX(image[indx + 1][c],
+                       MAX(image[indx - u][c], image[indx + u][c]))),
+               MIN(image[indx - 1][c],
+                   MIN(image[indx + 1][c],
+                       MIN(image[indx - u][c], image[indx + u][c]))));
+    }
+  }
+}
+
+// corrects chroma noise
+void LibRaw::fbdd_correction2(double (*image2)[3])
+{
+  int indx, v = 2 * width;
+  int col, row;
+  double Co, Ho, ratio;
+
+  for (row = 6; row < height - 6; row++)
+  {
+    for (col = 6; col < width - 6; col++)
+    {
+      indx = row * width + col;
+
+      if (image2[indx][1] * image2[indx][2] != 0)
+      {
+        Co = (image2[indx + v][1] + image2[indx - v][1] + image2[indx - 2][1] +
+              image2[indx + 2][1] -
+              MAX(image2[indx - 2][1],
+                  MAX(image2[indx + 2][1],
+                      MAX(image2[indx - v][1], image2[indx + v][1]))) -
+              MIN(image2[indx - 2][1],
+                  MIN(image2[indx + 2][1],
+                      MIN(image2[indx - v][1], image2[indx + v][1])))) /
+             2.0;
+        Ho = (image2[indx + v][2] + image2[indx - v][2] + image2[indx - 2][2] +
+              image2[indx + 2][2] -
+              MAX(image2[indx - 2][2],
+                  MAX(image2[indx + 2][2],
+                      MAX(image2[indx - v][2], image2[indx + v][2]))) -
+              MIN(image2[indx - 2][2],
+                  MIN(image2[indx + 2][2],
+                      MIN(image2[indx - v][2], image2[indx + v][2])))) /
+             2.0;
+        ratio = sqrt((Co * Co + Ho * Ho) / (image2[indx][1] * image2[indx][1] +
+                                            image2[indx][2] * image2[indx][2]));
+
+        if (ratio < 0.85)
+        {
+          image2[indx][0] =
+              -(image2[indx][1] + image2[indx][2] - Co - Ho) + image2[indx][0];
+          image2[indx][1] = Co;
+          image2[indx][2] = Ho;
+        }
+      }
+    }
+  }
+}
+
+// Cubic Spline Interpolation by Li and Randhawa, modified by Jacek Gozdz and
+// Luis Sanz Rodríguez
+void LibRaw::fbdd_green()
+{
+  int row, col, c, u = width, v = 2 * u, w = 3 * u, x = 4 * u, y = 5 * u, indx,
+                   min, max;
+  float f[4], g[4];
+
+  for (row = 5; row < height - 5; row++)
+    for (col = 5 + (FC(row, 1) & 1), indx = row * width + col, c = FC(row, col);
+         col < u - 5; col += 2, indx += 2)
+    {
+
+      f[0] = 1.0 / (1.0 + abs(image[indx - u][1] - image[indx - w][1]) +
+                    abs(image[indx - w][1] - image[indx + y][1]));
+      f[1] = 1.0 / (1.0 + abs(image[indx + 1][1] - image[indx + 3][1]) +
+                    abs(image[indx + 3][1] - image[indx - 5][1]));
+      f[2] = 1.0 / (1.0 + abs(image[indx - 1][1] - image[indx - 3][1]) +
+                    abs(image[indx - 3][1] - image[indx + 5][1]));
+      f[3] = 1.0 / (1.0 + abs(image[indx + u][1] - image[indx + w][1]) +
+                    abs(image[indx + w][1] - image[indx - y][1]));
+
+      g[0] = CLIP((23 * image[indx - u][1] + 23 * image[indx - w][1] +
+                   2 * image[indx - y][1] +
+                   8 * (image[indx - v][c] - image[indx - x][c]) +
+                   40 * (image[indx][c] - image[indx - v][c])) /
+                  48.0);
+      g[1] = CLIP((23 * image[indx + 1][1] + 23 * image[indx + 3][1] +
+                   2 * image[indx + 5][1] +
+                   8 * (image[indx + 2][c] - image[indx + 4][c]) +
+                   40 * (image[indx][c] - image[indx + 2][c])) /
+                  48.0);
+      g[2] = CLIP((23 * image[indx - 1][1] + 23 * image[indx - 3][1] +
+                   2 * image[indx - 5][1] +
+                   8 * (image[indx - 2][c] - image[indx - 4][c]) +
+                   40 * (image[indx][c] - image[indx - 2][c])) /
+                  48.0);
+      g[3] = CLIP((23 * image[indx + u][1] + 23 * image[indx + w][1] +
+                   2 * image[indx + y][1] +
+                   8 * (image[indx + v][c] - image[indx + x][c]) +
+                   40 * (image[indx][c] - image[indx + v][c])) /
+                  48.0);
+
+      image[indx][1] =
+          CLIP((f[0] * g[0] + f[1] * g[1] + f[2] * g[2] + f[3] * g[3]) /
+               (f[0] + f[1] + f[2] + f[3]));
+
+      min = MIN(
+          image[indx + 1 + u][1],
+          MIN(image[indx + 1 - u][1],
+              MIN(image[indx - 1 + u][1],
+                  MIN(image[indx - 1 - u][1],
+                      MIN(image[indx - 1][1],
+                          MIN(image[indx + 1][1],
+                              MIN(image[indx - u][1], image[indx + u][1])))))));
+
+      max = MAX(
+          image[indx + 1 + u][1],
+          MAX(image[indx + 1 - u][1],
+              MAX(image[indx - 1 + u][1],
+                  MAX(image[indx - 1 - u][1],
+                      MAX(image[indx - 1][1],
+                          MAX(image[indx + 1][1],
+                              MAX(image[indx - u][1], image[indx + u][1])))))));
+
+      image[indx][1] = ULIM(image[indx][1], max, min);
+    }
+}
+
+// FBDD (Fake Before Demosaicing Denoising)
+void LibRaw::fbdd(int noiserd)
+{
+  double(*image2)[3];
+  // safety net: disable for 4-color bayer or full-color images
+  if (colors != 3 || !filters)
+    return;
+  image2 = (double(*)[3])calloc(width * height, sizeof *image2);
+
+  border_interpolate(4);
+
+  if (noiserd > 1)
+  {
+    fbdd_green();
+    // dcb_color_full(image2);
+    dcb_color_full();
+    fbdd_correction();
+
+    dcb_color();
+    rgb_to_lch(image2);
+    fbdd_correction2(image2);
+    fbdd_correction2(image2);
+    lch_to_rgb(image2);
+  }
+  else
+  {
+    fbdd_green();
+    // dcb_color_full(image2);
+    dcb_color_full();
+    fbdd_correction();
+  }
+
+  free(image2);
+}
+
+// DCB demosaicing main routine
+void LibRaw::dcb(int iterations, int dcb_enhance)
+{
+
+  int i = 1;
+
+  float(*image2)[3];
+  image2 = (float(*)[3])calloc(width * height, sizeof *image2);
+
+  float(*image3)[3];
+  image3 = (float(*)[3])calloc(width * height, sizeof *image3);
+
+  border_interpolate(6);
+
+  dcb_hor(image2);
+  dcb_color2(image2);
+
+  dcb_ver(image3);
+  dcb_color3(image3);
+
+  dcb_decide(image2, image3);
+
+  free(image3);
+
+  dcb_copy_to_buffer(image2);
+
+  while (i <= iterations)
+  {
+    dcb_nyquist();
+    dcb_nyquist();
+    dcb_nyquist();
+    dcb_map();
+    dcb_correction();
+    i++;
+  }
+
+  dcb_color();
+  dcb_pp();
+
+  dcb_map();
+  dcb_correction2();
+
+  dcb_map();
+  dcb_correction();
+
+  dcb_map();
+  dcb_correction();
+
+  dcb_map();
+  dcb_correction();
+
+  dcb_map();
+  dcb_restore_from_buffer(image2);
+  dcb_color();
+
+  if (dcb_enhance)
+  {
+    dcb_refinement();
+    // dcb_color_full(image2);
+    dcb_color_full();
+  }
+
+  free(image2);
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/demosaic/dht_demosaic.cpp libkdcraw/libkdcraw/libraw/src/demosaic/dht_demosaic.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/demosaic/dht_demosaic.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/demosaic/dht_demosaic.cpp	2022-11-07 07:46:31.730795008 +0300
@@ -0,0 +1,1024 @@
+/* -*- C++ -*-
+ * File: dht_demosaic.cpp
+ * Copyright 2013 Anton Petrusevich
+ * Created: Tue Apr  9, 2013
+ *
+ * This code is licensed under one of two licenses as you choose:
+ *
+ * 1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+ *    (See file LICENSE.LGPL provided in LibRaw distribution archive for
+ * details).
+ *
+ * 2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+ *    (See file LICENSE.CDDL provided in LibRaw distribution archive for
+ * details).
+ *
+ */
+
+/*
+ * Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð²Ñ‹Ñ‡Ð¸ÑлÑет ÑркоÑтную диÑтанцию.
+ * еÑли две ÑркоÑти отличаютÑÑ Ð² два раза, например 10 и 20, то они имеют такой
+ * же Ð²ÐµÑ Ð¿Ñ€Ð¸ принÑтии Ñ€ÐµÑˆÐµÐ½Ð¸Ñ Ð¾Ð± интерполировании, как и 100 и 200 --
+ * фотографичеÑÐºÐ°Ñ ÑркоÑÑ‚ÑŒ между ними 1 Ñтоп. диÑÑ‚Ð°Ð½Ñ†Ð¸Ñ Ð²Ñегда >=1
+ */
+
+#include "../../internal/dmp_include.h"
+
+static inline float calc_dist(float c1, float c2)
+{
+  return c1 > c2 ? c1 / c2 : c2 / c1;
+}
+
+struct DHT
+{
+  int nr_height, nr_width;
+  static const int nr_topmargin = 4, nr_leftmargin = 4;
+  float (*nraw)[3];
+  ushort channel_maximum[3];
+  float channel_minimum[3];
+  LibRaw &libraw;
+  enum
+  {
+    HVSH = 1,
+    HOR = 2,
+    VER = 4,
+    HORSH = HOR | HVSH,
+    VERSH = VER | HVSH,
+    DIASH = 8,
+    LURD = 16,
+    RULD = 32,
+    LURDSH = LURD | DIASH,
+    RULDSH = RULD | DIASH,
+    HOT = 64
+  };
+  static inline float Thot(void) throw() { return 64.0f; }
+  static inline float Tg(void) throw() { return 256.0f; }
+  static inline float T(void) throw() { return 1.4f; }
+  char *ndir;
+  inline int nr_offset(int row, int col) throw()
+  {
+    return (row * nr_width + col);
+  }
+  int get_hv_grb(int x, int y, int kc)
+  {
+    float hv1 = 2 * nraw[nr_offset(y - 1, x)][1] /
+                (nraw[nr_offset(y - 2, x)][kc] + nraw[nr_offset(y, x)][kc]);
+    float hv2 = 2 * nraw[nr_offset(y + 1, x)][1] /
+                (nraw[nr_offset(y + 2, x)][kc] + nraw[nr_offset(y, x)][kc]);
+    float kv = calc_dist(hv1, hv2) *
+               calc_dist(nraw[nr_offset(y, x)][kc] * nraw[nr_offset(y, x)][kc],
+                         (nraw[nr_offset(y - 2, x)][kc] *
+                          nraw[nr_offset(y + 2, x)][kc]));
+    kv *= kv;
+    kv *= kv;
+    kv *= kv;
+    float dv =
+        kv *
+        calc_dist(nraw[nr_offset(y - 3, x)][1] * nraw[nr_offset(y + 3, x)][1],
+                  nraw[nr_offset(y - 1, x)][1] * nraw[nr_offset(y + 1, x)][1]);
+    float hh1 = 2 * nraw[nr_offset(y, x - 1)][1] /
+                (nraw[nr_offset(y, x - 2)][kc] + nraw[nr_offset(y, x)][kc]);
+    float hh2 = 2 * nraw[nr_offset(y, x + 1)][1] /
+                (nraw[nr_offset(y, x + 2)][kc] + nraw[nr_offset(y, x)][kc]);
+    float kh = calc_dist(hh1, hh2) *
+               calc_dist(nraw[nr_offset(y, x)][kc] * nraw[nr_offset(y, x)][kc],
+                         (nraw[nr_offset(y, x - 2)][kc] *
+                          nraw[nr_offset(y, x + 2)][kc]));
+    kh *= kh;
+    kh *= kh;
+    kh *= kh;
+    float dh =
+        kh *
+        calc_dist(nraw[nr_offset(y, x - 3)][1] * nraw[nr_offset(y, x + 3)][1],
+                  nraw[nr_offset(y, x - 1)][1] * nraw[nr_offset(y, x + 1)][1]);
+    float e = calc_dist(dh, dv);
+    char d = dh < dv ? (e > Tg() ? HORSH : HOR) : (e > Tg() ? VERSH : VER);
+    return d;
+  }
+  int get_hv_rbg(int x, int y, int hc)
+  {
+    float hv1 = 2 * nraw[nr_offset(y - 1, x)][hc ^ 2] /
+                (nraw[nr_offset(y - 2, x)][1] + nraw[nr_offset(y, x)][1]);
+    float hv2 = 2 * nraw[nr_offset(y + 1, x)][hc ^ 2] /
+                (nraw[nr_offset(y + 2, x)][1] + nraw[nr_offset(y, x)][1]);
+    float kv = calc_dist(hv1, hv2) *
+               calc_dist(nraw[nr_offset(y, x)][1] * nraw[nr_offset(y, x)][1],
+                         (nraw[nr_offset(y - 2, x)][1] *
+                          nraw[nr_offset(y + 2, x)][1]));
+    kv *= kv;
+    kv *= kv;
+    kv *= kv;
+    float dv = kv * calc_dist(nraw[nr_offset(y - 3, x)][hc ^ 2] *
+                                  nraw[nr_offset(y + 3, x)][hc ^ 2],
+                              nraw[nr_offset(y - 1, x)][hc ^ 2] *
+                                  nraw[nr_offset(y + 1, x)][hc ^ 2]);
+    float hh1 = 2 * nraw[nr_offset(y, x - 1)][hc] /
+                (nraw[nr_offset(y, x - 2)][1] + nraw[nr_offset(y, x)][1]);
+    float hh2 = 2 * nraw[nr_offset(y, x + 1)][hc] /
+                (nraw[nr_offset(y, x + 2)][1] + nraw[nr_offset(y, x)][1]);
+    float kh = calc_dist(hh1, hh2) *
+               calc_dist(nraw[nr_offset(y, x)][1] * nraw[nr_offset(y, x)][1],
+                         (nraw[nr_offset(y, x - 2)][1] *
+                          nraw[nr_offset(y, x + 2)][1]));
+    kh *= kh;
+    kh *= kh;
+    kh *= kh;
+    float dh =
+        kh * calc_dist(
+                 nraw[nr_offset(y, x - 3)][hc] * nraw[nr_offset(y, x + 3)][hc],
+                 nraw[nr_offset(y, x - 1)][hc] * nraw[nr_offset(y, x + 1)][hc]);
+    float e = calc_dist(dh, dv);
+    char d = dh < dv ? (e > Tg() ? HORSH : HOR) : (e > Tg() ? VERSH : VER);
+    return d;
+  }
+  int get_diag_grb(int x, int y, int kc)
+  {
+    float hlu =
+        nraw[nr_offset(y - 1, x - 1)][1] / nraw[nr_offset(y - 1, x - 1)][kc];
+    float hrd =
+        nraw[nr_offset(y + 1, x + 1)][1] / nraw[nr_offset(y + 1, x + 1)][kc];
+    float dlurd =
+        calc_dist(hlu, hrd) *
+        calc_dist(nraw[nr_offset(y - 1, x - 1)][1] *
+                      nraw[nr_offset(y + 1, x + 1)][1],
+                  nraw[nr_offset(y, x)][1] * nraw[nr_offset(y, x)][1]);
+    float druld =
+        calc_dist(hlu, hrd) *
+        calc_dist(nraw[nr_offset(y - 1, x + 1)][1] *
+                      nraw[nr_offset(y + 1, x - 1)][1],
+                  nraw[nr_offset(y, x)][1] * nraw[nr_offset(y, x)][1]);
+    float e = calc_dist(dlurd, druld);
+    char d =
+        druld < dlurd ? (e > T() ? RULDSH : RULD) : (e > T() ? LURDSH : LURD);
+    return d;
+  }
+  int get_diag_rbg(int x, int y, int hc)
+  {
+    float dlurd = calc_dist(
+        nraw[nr_offset(y - 1, x - 1)][1] * nraw[nr_offset(y + 1, x + 1)][1],
+        nraw[nr_offset(y, x)][1] * nraw[nr_offset(y, x)][1]);
+    float druld = calc_dist(
+        nraw[nr_offset(y - 1, x + 1)][1] * nraw[nr_offset(y + 1, x - 1)][1],
+        nraw[nr_offset(y, x)][1] * nraw[nr_offset(y, x)][1]);
+    float e = calc_dist(dlurd, druld);
+    char d =
+        druld < dlurd ? (e > T() ? RULDSH : RULD) : (e > T() ? LURDSH : LURD);
+    return d;
+  }
+  static inline float scale_over(float ec, float base)
+  {
+    float s = base * .4;
+    float o = ec - base;
+    return base + sqrt(s * (o + s)) - s;
+  }
+  static inline float scale_under(float ec, float base)
+  {
+    float s = base * .6;
+    float o = base - ec;
+    return base - sqrt(s * (o + s)) + s;
+  }
+  ~DHT();
+  DHT(LibRaw &_libraw);
+  void copy_to_image();
+  void make_greens();
+  void make_diag_dirs();
+  void make_hv_dirs();
+  void refine_hv_dirs(int i, int js);
+  void refine_diag_dirs(int i, int js);
+  void refine_ihv_dirs(int i);
+  void refine_idiag_dirs(int i);
+  void illustrate_dirs();
+  void illustrate_dline(int i);
+  void make_hv_dline(int i);
+  void make_diag_dline(int i);
+  void make_gline(int i);
+  void make_rbdiag(int i);
+  void make_rbhv(int i);
+  void make_rb();
+  void hide_hots();
+  void restore_hots();
+};
+
+typedef float float3[3];
+
+/*
+ * Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ цветах копируетÑÑ Ð²Ð¾ float в общем то Ñ Ð¾Ð´Ð½Ð¾Ð¹ целью -- чтобы
+ * вмеÑто 0 можно было пиÑать 0.5, что больше подходит Ð´Ð»Ñ Ð²Ñ‹Ñ‡Ð¸ÑÐ»ÐµÐ½Ð¸Ñ ÑркоÑтной
+ * разницы. причина: в целых чиÑлах разница в 1 Ñтоп ÑоÑтавлÑет Ñ€Ñд 8,4,2,1,0 --
+ * поÑледнее чиÑло должно быть 0.5, которое непредÑтвамио в целых чиÑлах. так же
+ * Ñто изменение позволÑет не думать о Ñпециальных ÑлучаÑÑ… Ð´ÐµÐ»ÐµÐ½Ð¸Ñ Ð½Ð° ноль.
+ *
+ * альтернативное решение: умножить вÑе данные на 2 и в младший бит внеÑти 1.
+ * правда, вÑÑ‘ равно придётÑÑ Ñледить, чтобы при интерпретации зелёного цвета не
+ * получилÑÑ 0 при округлении, иначе проблема при интерпретации Ñиних и краÑных.
+ *
+ */
+DHT::DHT(LibRaw &_libraw) : libraw(_libraw)
+{
+  nr_height = libraw.imgdata.sizes.iheight + nr_topmargin * 2;
+  nr_width = libraw.imgdata.sizes.iwidth + nr_leftmargin * 2;
+  nraw = (float3 *)malloc(nr_height * nr_width * sizeof(float3));
+  int iwidth = libraw.imgdata.sizes.iwidth;
+  ndir = (char *)calloc(nr_height * nr_width, 1);
+  channel_maximum[0] = channel_maximum[1] = channel_maximum[2] = 0;
+  channel_minimum[0] = libraw.imgdata.image[0][0];
+  channel_minimum[1] = libraw.imgdata.image[0][1];
+  channel_minimum[2] = libraw.imgdata.image[0][2];
+  for (int i = 0; i < nr_height * nr_width; ++i)
+    nraw[i][0] = nraw[i][1] = nraw[i][2] = 0.5;
+  for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
+  {
+    int col_cache[48];
+    for (int j = 0; j < 48; ++j)
+    {
+      int l = libraw.COLOR(i, j);
+      if (l == 3)
+        l = 1;
+      col_cache[j] = l;
+    }
+    for (int j = 0; j < iwidth; ++j)
+    {
+      int l = col_cache[j % 48];
+      unsigned short c = libraw.imgdata.image[i * iwidth + j][l];
+      if (c != 0)
+      {
+        if (channel_maximum[l] < c)
+          channel_maximum[l] = c;
+        if (channel_minimum[l] > c)
+          channel_minimum[l] = c;
+        nraw[nr_offset(i + nr_topmargin, j + nr_leftmargin)][l] = (float)c;
+      }
+    }
+  }
+  channel_minimum[0] += .5;
+  channel_minimum[1] += .5;
+  channel_minimum[2] += .5;
+}
+
+void DHT::hide_hots()
+{
+  int iwidth = libraw.imgdata.sizes.iwidth;
+#if defined(LIBRAW_USE_OPENMP)
+#pragma omp parallel for schedule(guided) firstprivate(iwidth)
+#endif
+  for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
+  {
+    int js = libraw.COLOR(i, 0) & 1;
+    int kc = libraw.COLOR(i, js);
+    /*
+     * js -- Ð½Ð°Ñ‡Ð°Ð»ÑŒÐ½Ð°Ñ Ñ…-координата, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð¿Ð¾Ð¿Ð°Ð´Ð°ÐµÑ‚ мимо извеÑтного зелёного
+     * kc -- извеÑтный цвет в точке интерполированиÑ
+     */
+    for (int j = js; j < iwidth; j += 2)
+    {
+      int x = j + nr_leftmargin;
+      int y = i + nr_topmargin;
+      float c = nraw[nr_offset(y, x)][kc];
+      if ((c > nraw[nr_offset(y, x + 2)][kc] &&
+           c > nraw[nr_offset(y, x - 2)][kc] &&
+           c > nraw[nr_offset(y - 2, x)][kc] &&
+           c > nraw[nr_offset(y + 2, x)][kc] &&
+           c > nraw[nr_offset(y, x + 1)][1] &&
+           c > nraw[nr_offset(y, x - 1)][1] &&
+           c > nraw[nr_offset(y - 1, x)][1] &&
+           c > nraw[nr_offset(y + 1, x)][1]) ||
+          (c < nraw[nr_offset(y, x + 2)][kc] &&
+           c < nraw[nr_offset(y, x - 2)][kc] &&
+           c < nraw[nr_offset(y - 2, x)][kc] &&
+           c < nraw[nr_offset(y + 2, x)][kc] &&
+           c < nraw[nr_offset(y, x + 1)][1] &&
+           c < nraw[nr_offset(y, x - 1)][1] &&
+           c < nraw[nr_offset(y - 1, x)][1] &&
+           c < nraw[nr_offset(y + 1, x)][1]))
+      {
+        float avg = 0;
+        for (int k = -2; k < 3; k += 2)
+          for (int m = -2; m < 3; m += 2)
+            if (m == 0 && k == 0)
+              continue;
+            else
+              avg += nraw[nr_offset(y + k, x + m)][kc];
+        avg /= 8;
+        //				float dev = 0;
+        //				for (int k = -2; k < 3; k += 2)
+        //					for (int l = -2; l < 3; l += 2)
+        //						if (k == 0 && l == 0)
+        //							continue;
+        //						else {
+        //							float t = nraw[nr_offset(y + k, x + l)][kc] -
+        //avg; 							dev += t * t;
+        //						}
+        //				dev /= 8;
+        //				dev = sqrt(dev);
+        if (calc_dist(c, avg) > Thot())
+        {
+          ndir[nr_offset(y, x)] |= HOT;
+          float dv = calc_dist(
+              nraw[nr_offset(y - 2, x)][kc] * nraw[nr_offset(y - 1, x)][1],
+              nraw[nr_offset(y + 2, x)][kc] * nraw[nr_offset(y + 1, x)][1]);
+          float dh = calc_dist(
+              nraw[nr_offset(y, x - 2)][kc] * nraw[nr_offset(y, x - 1)][1],
+              nraw[nr_offset(y, x + 2)][kc] * nraw[nr_offset(y, x + 1)][1]);
+          if (dv > dh)
+            nraw[nr_offset(y, x)][kc] = (nraw[nr_offset(y, x + 2)][kc] +
+                                         nraw[nr_offset(y, x - 2)][kc]) /
+                                        2;
+          else
+            nraw[nr_offset(y, x)][kc] = (nraw[nr_offset(y - 2, x)][kc] +
+                                         nraw[nr_offset(y + 2, x)][kc]) /
+                                        2;
+        }
+      }
+    }
+    for (int j = js ^ 1; j < iwidth; j += 2)
+    {
+      int x = j + nr_leftmargin;
+      int y = i + nr_topmargin;
+      float c = nraw[nr_offset(y, x)][1];
+      if ((c > nraw[nr_offset(y, x + 2)][1] &&
+           c > nraw[nr_offset(y, x - 2)][1] &&
+           c > nraw[nr_offset(y - 2, x)][1] &&
+           c > nraw[nr_offset(y + 2, x)][1] &&
+           c > nraw[nr_offset(y, x + 1)][kc] &&
+           c > nraw[nr_offset(y, x - 1)][kc] &&
+           c > nraw[nr_offset(y - 1, x)][kc ^ 2] &&
+           c > nraw[nr_offset(y + 1, x)][kc ^ 2]) ||
+          (c < nraw[nr_offset(y, x + 2)][1] &&
+           c < nraw[nr_offset(y, x - 2)][1] &&
+           c < nraw[nr_offset(y - 2, x)][1] &&
+           c < nraw[nr_offset(y + 2, x)][1] &&
+           c < nraw[nr_offset(y, x + 1)][kc] &&
+           c < nraw[nr_offset(y, x - 1)][kc] &&
+           c < nraw[nr_offset(y - 1, x)][kc ^ 2] &&
+           c < nraw[nr_offset(y + 1, x)][kc ^ 2]))
+      {
+        float avg = 0;
+        for (int k = -2; k < 3; k += 2)
+          for (int m = -2; m < 3; m += 2)
+            if (k == 0 && m == 0)
+              continue;
+            else
+              avg += nraw[nr_offset(y + k, x + m)][1];
+        avg /= 8;
+        //				float dev = 0;
+        //				for (int k = -2; k < 3; k += 2)
+        //					for (int l = -2; l < 3; l += 2)
+        //						if (k == 0 && l == 0)
+        //							continue;
+        //						else {
+        //							float t = nraw[nr_offset(y + k, x + l)][1] -
+        //avg; 							dev += t * t;
+        //						}
+        //				dev /= 8;
+        //				dev = sqrt(dev);
+        if (calc_dist(c, avg) > Thot())
+        {
+          ndir[nr_offset(y, x)] |= HOT;
+          float dv = calc_dist(
+              nraw[nr_offset(y - 2, x)][1] * nraw[nr_offset(y - 1, x)][kc ^ 2],
+              nraw[nr_offset(y + 2, x)][1] * nraw[nr_offset(y + 1, x)][kc ^ 2]);
+          float dh = calc_dist(
+              nraw[nr_offset(y, x - 2)][1] * nraw[nr_offset(y, x - 1)][kc],
+              nraw[nr_offset(y, x + 2)][1] * nraw[nr_offset(y, x + 1)][kc]);
+          if (dv > dh)
+            nraw[nr_offset(y, x)][1] =
+                (nraw[nr_offset(y, x + 2)][1] + nraw[nr_offset(y, x - 2)][1]) /
+                2;
+          else
+            nraw[nr_offset(y, x)][1] =
+                (nraw[nr_offset(y - 2, x)][1] + nraw[nr_offset(y + 2, x)][1]) /
+                2;
+        }
+      }
+    }
+  }
+}
+
+void DHT::restore_hots()
+{
+  int iwidth = libraw.imgdata.sizes.iwidth;
+#if defined(LIBRAW_USE_OPENMP)
+#ifdef _MSC_VER
+#pragma omp parallel for firstprivate(iwidth)
+#else
+#pragma omp parallel for schedule(guided) firstprivate(iwidth) collapse(2)
+#endif
+#endif
+  for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
+  {
+    for (int j = 0; j < iwidth; ++j)
+    {
+      int x = j + nr_leftmargin;
+      int y = i + nr_topmargin;
+      if (ndir[nr_offset(y, x)] & HOT)
+      {
+        int l = libraw.COLOR(i, j);
+        nraw[nr_offset(i + nr_topmargin, j + nr_leftmargin)][l] =
+            libraw.imgdata.image[i * iwidth + j][l];
+      }
+    }
+  }
+}
+
+void DHT::make_diag_dirs()
+{
+#if defined(LIBRAW_USE_OPENMP)
+#pragma omp parallel for schedule(guided)
+#endif
+  for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
+  {
+    make_diag_dline(i);
+  }
+//#if defined(LIBRAW_USE_OPENMP)
+//#pragma omp parallel for schedule(guided)
+//#endif
+//	for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) {
+//		refine_diag_dirs(i, i & 1);
+//	}
+//#if defined(LIBRAW_USE_OPENMP)
+//#pragma omp parallel for schedule(guided)
+//#endif
+//	for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i) {
+//		refine_diag_dirs(i, (i & 1) ^ 1);
+//	}
+#if defined(LIBRAW_USE_OPENMP)
+#pragma omp parallel for schedule(guided)
+#endif
+  for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
+  {
+    refine_idiag_dirs(i);
+  }
+}
+
+void DHT::make_hv_dirs()
+{
+#if defined(LIBRAW_USE_OPENMP)
+#pragma omp parallel for schedule(guided)
+#endif
+  for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
+  {
+    make_hv_dline(i);
+  }
+#if defined(LIBRAW_USE_OPENMP)
+#pragma omp parallel for schedule(guided)
+#endif
+  for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
+  {
+    refine_hv_dirs(i, i & 1);
+  }
+#if defined(LIBRAW_USE_OPENMP)
+#pragma omp parallel for schedule(guided)
+#endif
+  for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
+  {
+    refine_hv_dirs(i, (i & 1) ^ 1);
+  }
+#if defined(LIBRAW_USE_OPENMP)
+#pragma omp parallel for schedule(guided)
+#endif
+  for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
+  {
+    refine_ihv_dirs(i);
+  }
+}
+
+void DHT::refine_hv_dirs(int i, int js)
+{
+  int iwidth = libraw.imgdata.sizes.iwidth;
+  for (int j = js; j < iwidth; j += 2)
+  {
+    int x = j + nr_leftmargin;
+    int y = i + nr_topmargin;
+    if (ndir[nr_offset(y, x)] & HVSH)
+      continue;
+    int nv =
+        (ndir[nr_offset(y - 1, x)] & VER) + (ndir[nr_offset(y + 1, x)] & VER) +
+        (ndir[nr_offset(y, x - 1)] & VER) + (ndir[nr_offset(y, x + 1)] & VER);
+    int nh =
+        (ndir[nr_offset(y - 1, x)] & HOR) + (ndir[nr_offset(y + 1, x)] & HOR) +
+        (ndir[nr_offset(y, x - 1)] & HOR) + (ndir[nr_offset(y, x + 1)] & HOR);
+    bool codir = (ndir[nr_offset(y, x)] & VER)
+                     ? ((ndir[nr_offset(y - 1, x)] & VER) ||
+                        (ndir[nr_offset(y + 1, x)] & VER))
+                     : ((ndir[nr_offset(y, x - 1)] & HOR) ||
+                        (ndir[nr_offset(y, x + 1)] & HOR));
+    nv /= VER;
+    nh /= HOR;
+    if ((ndir[nr_offset(y, x)] & VER) && (nh > 2 && !codir))
+    {
+      ndir[nr_offset(y, x)] &= ~VER;
+      ndir[nr_offset(y, x)] |= HOR;
+    }
+    if ((ndir[nr_offset(y, x)] & HOR) && (nv > 2 && !codir))
+    {
+      ndir[nr_offset(y, x)] &= ~HOR;
+      ndir[nr_offset(y, x)] |= VER;
+    }
+  }
+}
+
+void DHT::refine_ihv_dirs(int i)
+{
+  int iwidth = libraw.imgdata.sizes.iwidth;
+  for (int j = 0; j < iwidth; j++)
+  {
+    int x = j + nr_leftmargin;
+    int y = i + nr_topmargin;
+    if (ndir[nr_offset(y, x)] & HVSH)
+      continue;
+    int nv =
+        (ndir[nr_offset(y - 1, x)] & VER) + (ndir[nr_offset(y + 1, x)] & VER) +
+        (ndir[nr_offset(y, x - 1)] & VER) + (ndir[nr_offset(y, x + 1)] & VER);
+    int nh =
+        (ndir[nr_offset(y - 1, x)] & HOR) + (ndir[nr_offset(y + 1, x)] & HOR) +
+        (ndir[nr_offset(y, x - 1)] & HOR) + (ndir[nr_offset(y, x + 1)] & HOR);
+    nv /= VER;
+    nh /= HOR;
+    if ((ndir[nr_offset(y, x)] & VER) && nh > 3)
+    {
+      ndir[nr_offset(y, x)] &= ~VER;
+      ndir[nr_offset(y, x)] |= HOR;
+    }
+    if ((ndir[nr_offset(y, x)] & HOR) && nv > 3)
+    {
+      ndir[nr_offset(y, x)] &= ~HOR;
+      ndir[nr_offset(y, x)] |= VER;
+    }
+  }
+}
+void DHT::make_hv_dline(int i)
+{
+  int iwidth = libraw.imgdata.sizes.iwidth;
+  int js = libraw.COLOR(i, 0) & 1;
+  int kc = libraw.COLOR(i, js);
+  /*
+   * js -- Ð½Ð°Ñ‡Ð°Ð»ÑŒÐ½Ð°Ñ Ñ…-координата, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð¿Ð¾Ð¿Ð°Ð´Ð°ÐµÑ‚ мимо извеÑтного зелёного
+   * kc -- извеÑтный цвет в точке интерполированиÑ
+   */
+  for (int j = 0; j < iwidth; j++)
+  {
+    int x = j + nr_leftmargin;
+    int y = i + nr_topmargin;
+    char d = 0;
+    if ((j & 1) == js)
+    {
+      d = get_hv_grb(x, y, kc);
+    }
+    else
+    {
+      d = get_hv_rbg(x, y, kc);
+    }
+    ndir[nr_offset(y, x)] |= d;
+  }
+}
+
+void DHT::make_diag_dline(int i)
+{
+  int iwidth = libraw.imgdata.sizes.iwidth;
+  int js = libraw.COLOR(i, 0) & 1;
+  int kc = libraw.COLOR(i, js);
+  /*
+   * js -- Ð½Ð°Ñ‡Ð°Ð»ÑŒÐ½Ð°Ñ Ñ…-координата, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð¿Ð¾Ð¿Ð°Ð´Ð°ÐµÑ‚ мимо извеÑтного зелёного
+   * kc -- извеÑтный цвет в точке интерполированиÑ
+   */
+  for (int j = 0; j < iwidth; j++)
+  {
+    int x = j + nr_leftmargin;
+    int y = i + nr_topmargin;
+    char d = 0;
+    if ((j & 1) == js)
+    {
+      d = get_diag_grb(x, y, kc);
+    }
+    else
+    {
+      d = get_diag_rbg(x, y, kc);
+    }
+    ndir[nr_offset(y, x)] |= d;
+  }
+}
+
+void DHT::refine_diag_dirs(int i, int js)
+{
+  int iwidth = libraw.imgdata.sizes.iwidth;
+  for (int j = js; j < iwidth; j += 2)
+  {
+    int x = j + nr_leftmargin;
+    int y = i + nr_topmargin;
+    if (ndir[nr_offset(y, x)] & DIASH)
+      continue;
+    int nv = (ndir[nr_offset(y - 1, x)] & LURD) +
+             (ndir[nr_offset(y + 1, x)] & LURD) +
+             (ndir[nr_offset(y, x - 1)] & LURD) +
+             (ndir[nr_offset(y, x + 1)] & LURD) +
+             (ndir[nr_offset(y - 1, x - 1)] & LURD) +
+             (ndir[nr_offset(y - 1, x + 1)] & LURD) +
+             (ndir[nr_offset(y + 1, x - 1)] & LURD) +
+             (ndir[nr_offset(y + 1, x + 1)] & LURD);
+    int nh = (ndir[nr_offset(y - 1, x)] & RULD) +
+             (ndir[nr_offset(y + 1, x)] & RULD) +
+             (ndir[nr_offset(y, x - 1)] & RULD) +
+             (ndir[nr_offset(y, x + 1)] & RULD) +
+             (ndir[nr_offset(y - 1, x - 1)] & RULD) +
+             (ndir[nr_offset(y - 1, x + 1)] & RULD) +
+             (ndir[nr_offset(y + 1, x - 1)] & RULD) +
+             (ndir[nr_offset(y + 1, x + 1)] & RULD);
+    bool codir = (ndir[nr_offset(y, x)] & LURD)
+                     ? ((ndir[nr_offset(y - 1, x - 1)] & LURD) ||
+                        (ndir[nr_offset(y + 1, x + 1)] & LURD))
+                     : ((ndir[nr_offset(y - 1, x + 1)] & RULD) ||
+                        (ndir[nr_offset(y + 1, x - 1)] & RULD));
+    nv /= LURD;
+    nh /= RULD;
+    if ((ndir[nr_offset(y, x)] & LURD) && (nh > 4 && !codir))
+    {
+      ndir[nr_offset(y, x)] &= ~LURD;
+      ndir[nr_offset(y, x)] |= RULD;
+    }
+    if ((ndir[nr_offset(y, x)] & RULD) && (nv > 4 && !codir))
+    {
+      ndir[nr_offset(y, x)] &= ~RULD;
+      ndir[nr_offset(y, x)] |= LURD;
+    }
+  }
+}
+
+void DHT::refine_idiag_dirs(int i)
+{
+  int iwidth = libraw.imgdata.sizes.iwidth;
+  for (int j = 0; j < iwidth; j++)
+  {
+    int x = j + nr_leftmargin;
+    int y = i + nr_topmargin;
+    if (ndir[nr_offset(y, x)] & DIASH)
+      continue;
+    int nv = (ndir[nr_offset(y - 1, x)] & LURD) +
+             (ndir[nr_offset(y + 1, x)] & LURD) +
+             (ndir[nr_offset(y, x - 1)] & LURD) +
+             (ndir[nr_offset(y, x + 1)] & LURD) +
+             (ndir[nr_offset(y - 1, x - 1)] & LURD) +
+             (ndir[nr_offset(y - 1, x + 1)] & LURD) +
+             (ndir[nr_offset(y + 1, x - 1)] & LURD) +
+             (ndir[nr_offset(y + 1, x + 1)] & LURD);
+    int nh = (ndir[nr_offset(y - 1, x)] & RULD) +
+             (ndir[nr_offset(y + 1, x)] & RULD) +
+             (ndir[nr_offset(y, x - 1)] & RULD) +
+             (ndir[nr_offset(y, x + 1)] & RULD) +
+             (ndir[nr_offset(y - 1, x - 1)] & RULD) +
+             (ndir[nr_offset(y - 1, x + 1)] & RULD) +
+             (ndir[nr_offset(y + 1, x - 1)] & RULD) +
+             (ndir[nr_offset(y + 1, x + 1)] & RULD);
+    nv /= LURD;
+    nh /= RULD;
+    if ((ndir[nr_offset(y, x)] & LURD) && nh > 7)
+    {
+      ndir[nr_offset(y, x)] &= ~LURD;
+      ndir[nr_offset(y, x)] |= RULD;
+    }
+    if ((ndir[nr_offset(y, x)] & RULD) && nv > 7)
+    {
+      ndir[nr_offset(y, x)] &= ~RULD;
+      ndir[nr_offset(y, x)] |= LURD;
+    }
+  }
+}
+
+/*
+ * вычиÑление недоÑтающих зелёных точек.
+ */
+void DHT::make_greens()
+{
+#if defined(LIBRAW_USE_OPENMP)
+#pragma omp parallel for schedule(guided)
+#endif
+  for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
+  {
+    make_gline(i);
+  }
+}
+
+void DHT::make_gline(int i)
+{
+  int iwidth = libraw.imgdata.sizes.iwidth;
+  int js = libraw.COLOR(i, 0) & 1;
+  int kc = libraw.COLOR(i, js);
+  /*
+   * js -- Ð½Ð°Ñ‡Ð°Ð»ÑŒÐ½Ð°Ñ Ñ…-координата, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð¿Ð¾Ð¿Ð°Ð´Ð°ÐµÑ‚ мимо извеÑтного зелёного
+   * kc -- извеÑтный цвет в точке интерполированиÑ
+   */
+  for (int j = js; j < iwidth; j += 2)
+  {
+    int x = j + nr_leftmargin;
+    int y = i + nr_topmargin;
+    int dx, dy, dx2, dy2;
+    float h1, h2;
+    if (ndir[nr_offset(y, x)] & VER)
+    {
+      dx = dx2 = 0;
+      dy = -1;
+      dy2 = 1;
+      h1 = 2 * nraw[nr_offset(y - 1, x)][1] /
+           (nraw[nr_offset(y - 2, x)][kc] + nraw[nr_offset(y, x)][kc]);
+      h2 = 2 * nraw[nr_offset(y + 1, x)][1] /
+           (nraw[nr_offset(y + 2, x)][kc] + nraw[nr_offset(y, x)][kc]);
+    }
+    else
+    {
+      dy = dy2 = 0;
+      dx = 1;
+      dx2 = -1;
+      h1 = 2 * nraw[nr_offset(y, x + 1)][1] /
+           (nraw[nr_offset(y, x + 2)][kc] + nraw[nr_offset(y, x)][kc]);
+      h2 = 2 * nraw[nr_offset(y, x - 1)][1] /
+           (nraw[nr_offset(y, x - 2)][kc] + nraw[nr_offset(y, x)][kc]);
+    }
+    float b1 = 1 / calc_dist(nraw[nr_offset(y, x)][kc],
+                             nraw[nr_offset(y + dy * 2, x + dx * 2)][kc]);
+    float b2 = 1 / calc_dist(nraw[nr_offset(y, x)][kc],
+                             nraw[nr_offset(y + dy2 * 2, x + dx2 * 2)][kc]);
+    b1 *= b1;
+    b2 *= b2;
+    float eg = nraw[nr_offset(y, x)][kc] * (b1 * h1 + b2 * h2) / (b1 + b2);
+    float min, max;
+    min = MIN(nraw[nr_offset(y + dy, x + dx)][1],
+              nraw[nr_offset(y + dy2, x + dx2)][1]);
+    max = MAX(nraw[nr_offset(y + dy, x + dx)][1],
+              nraw[nr_offset(y + dy2, x + dx2)][1]);
+    min /= 1.2;
+    max *= 1.2;
+    if (eg < min)
+      eg = scale_under(eg, min);
+    else if (eg > max)
+      eg = scale_over(eg, max);
+    if (eg > channel_maximum[1])
+      eg = channel_maximum[1];
+    else if (eg < channel_minimum[1])
+      eg = channel_minimum[1];
+    nraw[nr_offset(y, x)][1] = eg;
+  }
+}
+
+/*
+ * Ð¾Ñ‚Ð»Ð°Ð´Ð¾Ñ‡Ð½Ð°Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ
+ */
+
+void DHT::illustrate_dirs()
+{
+#if defined(LIBRAW_USE_OPENMP)
+#pragma omp parallel for schedule(guided)
+#endif
+  for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
+  {
+    illustrate_dline(i);
+  }
+}
+
+void DHT::illustrate_dline(int i)
+{
+  int iwidth = libraw.imgdata.sizes.iwidth;
+  for (int j = 0; j < iwidth; j++)
+  {
+    int x = j + nr_leftmargin;
+    int y = i + nr_topmargin;
+    nraw[nr_offset(y, x)][0] = nraw[nr_offset(y, x)][1] =
+        nraw[nr_offset(y, x)][2] = 0.5;
+    int l = ndir[nr_offset(y, x)] & 8;
+    // l >>= 3; // WTF?
+    l = 1;
+    if (ndir[nr_offset(y, x)] & HOT)
+      nraw[nr_offset(y, x)][0] =
+          l * channel_maximum[0] / 4 + channel_maximum[0] / 4;
+    else
+      nraw[nr_offset(y, x)][2] =
+          l * channel_maximum[2] / 4 + channel_maximum[2] / 4;
+  }
+}
+
+/*
+ * интерполÑÑ†Ð¸Ñ ÐºÑ€Ð°Ñных и Ñиних.
+ *
+ * Ñначала интерполируютÑÑ Ð½ÐµÐ´Ð¾Ñтающие цвета, по диагональным направлениÑм от
+ * которых находÑÑ‚ÑÑ Ð¸Ð·Ð²ÐµÑтные, затем ÑÐ¸Ñ‚ÑƒÐ°Ñ†Ð¸Ñ ÑводитÑÑ Ðº тому как
+ * интерполировалиÑÑŒ зелёные.
+ */
+
+void DHT::make_rbdiag(int i)
+{
+  int iwidth = libraw.imgdata.sizes.iwidth;
+  int js = libraw.COLOR(i, 0) & 1;
+  int uc = libraw.COLOR(i, js);
+  int cl = uc ^ 2;
+  /*
+   * js -- Ð½Ð°Ñ‡Ð°Ð»ÑŒÐ½Ð°Ñ Ñ…-координата, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð¿Ð¾Ð¿Ð°Ð´Ð°ÐµÑ‚ на уже интерполированный
+   * зелёный al -- извеÑтный цвет (кроме зелёного) в точке Ð¸Ð½Ñ‚ÐµÑ€Ð¿Ð¾Ð»Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ cl
+   * -- неизвеÑтный цвет
+   */
+  for (int j = js; j < iwidth; j += 2)
+  {
+    int x = j + nr_leftmargin;
+    int y = i + nr_topmargin;
+    int dx, dy, dx2, dy2;
+    if (ndir[nr_offset(y, x)] & LURD)
+    {
+      dx = -1;
+      dx2 = 1;
+      dy = -1;
+      dy2 = 1;
+    }
+    else
+    {
+      dx = -1;
+      dx2 = 1;
+      dy = 1;
+      dy2 = -1;
+    }
+    float g1 = 1 / calc_dist(nraw[nr_offset(y, x)][1],
+                             nraw[nr_offset(y + dy, x + dx)][1]);
+    float g2 = 1 / calc_dist(nraw[nr_offset(y, x)][1],
+                             nraw[nr_offset(y + dy2, x + dx2)][1]);
+    g1 *= g1 * g1;
+    g2 *= g2 * g2;
+
+    float eg;
+    eg = nraw[nr_offset(y, x)][1] *
+         (g1 * nraw[nr_offset(y + dy, x + dx)][cl] /
+              nraw[nr_offset(y + dy, x + dx)][1] +
+          g2 * nraw[nr_offset(y + dy2, x + dx2)][cl] /
+              nraw[nr_offset(y + dy2, x + dx2)][1]) /
+         (g1 + g2);
+    float min, max;
+    min = MIN(nraw[nr_offset(y + dy, x + dx)][cl],
+              nraw[nr_offset(y + dy2, x + dx2)][cl]);
+    max = MAX(nraw[nr_offset(y + dy, x + dx)][cl],
+              nraw[nr_offset(y + dy2, x + dx2)][cl]);
+    min /= 1.2;
+    max *= 1.2;
+    if (eg < min)
+      eg = scale_under(eg, min);
+    else if (eg > max)
+      eg = scale_over(eg, max);
+    if (eg > channel_maximum[cl])
+      eg = channel_maximum[cl];
+    else if (eg < channel_minimum[cl])
+      eg = channel_minimum[cl];
+    nraw[nr_offset(y, x)][cl] = eg;
+  }
+}
+
+/*
+ * интерполÑÑ†Ð¸Ñ ÐºÑ€Ð°Ñных и Ñиних в точках где был извеÑтен только зелёный,
+ * Ð½Ð°Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð³Ð¾Ñ€Ð¸Ð·Ð¾Ð½Ñ‚Ð°Ð»ÑŒÐ½Ñ‹Ðµ или вертикальные
+ */
+
+void DHT::make_rbhv(int i)
+{
+  int iwidth = libraw.imgdata.sizes.iwidth;
+  int js = (libraw.COLOR(i, 0) & 1) ^ 1;
+  for (int j = js; j < iwidth; j += 2)
+  {
+    int x = j + nr_leftmargin;
+    int y = i + nr_topmargin;
+    /*
+     * поÑкольку Ñверху-Ñнизу и Ñправа-Ñлева уже еÑÑ‚ÑŒ вÑе необходимые краÑные и
+     * Ñиние, то можно выбрать наилучшее направление иÑÑ…Ð¾Ð´Ñ Ð¸Ð· информации по
+     * обоим цветам.
+     */
+    int dx, dy, dx2, dy2;
+    if (ndir[nr_offset(y, x)] & VER)
+    {
+      dx = dx2 = 0;
+      dy = -1;
+      dy2 = 1;
+    }
+    else
+    {
+      dy = dy2 = 0;
+      dx = 1;
+      dx2 = -1;
+    }
+    float g1 = 1 / calc_dist(nraw[nr_offset(y, x)][1],
+                             nraw[nr_offset(y + dy, x + dx)][1]);
+    float g2 = 1 / calc_dist(nraw[nr_offset(y, x)][1],
+                             nraw[nr_offset(y + dy2, x + dx2)][1]);
+    g1 *= g1;
+    g2 *= g2;
+    float eg_r, eg_b;
+    eg_r = nraw[nr_offset(y, x)][1] *
+           (g1 * nraw[nr_offset(y + dy, x + dx)][0] /
+                nraw[nr_offset(y + dy, x + dx)][1] +
+            g2 * nraw[nr_offset(y + dy2, x + dx2)][0] /
+                nraw[nr_offset(y + dy2, x + dx2)][1]) /
+           (g1 + g2);
+    eg_b = nraw[nr_offset(y, x)][1] *
+           (g1 * nraw[nr_offset(y + dy, x + dx)][2] /
+                nraw[nr_offset(y + dy, x + dx)][1] +
+            g2 * nraw[nr_offset(y + dy2, x + dx2)][2] /
+                nraw[nr_offset(y + dy2, x + dx2)][1]) /
+           (g1 + g2);
+    float min_r, max_r;
+    min_r = MIN(nraw[nr_offset(y + dy, x + dx)][0],
+                nraw[nr_offset(y + dy2, x + dx2)][0]);
+    max_r = MAX(nraw[nr_offset(y + dy, x + dx)][0],
+                nraw[nr_offset(y + dy2, x + dx2)][0]);
+    float min_b, max_b;
+    min_b = MIN(nraw[nr_offset(y + dy, x + dx)][2],
+                nraw[nr_offset(y + dy2, x + dx2)][2]);
+    max_b = MAX(nraw[nr_offset(y + dy, x + dx)][2],
+                nraw[nr_offset(y + dy2, x + dx2)][2]);
+    min_r /= 1.2;
+    max_r *= 1.2;
+    min_b /= 1.2;
+    max_b *= 1.2;
+
+    if (eg_r < min_r)
+      eg_r = scale_under(eg_r, min_r);
+    else if (eg_r > max_r)
+      eg_r = scale_over(eg_r, max_r);
+    if (eg_b < min_b)
+      eg_b = scale_under(eg_b, min_b);
+    else if (eg_b > max_b)
+      eg_b = scale_over(eg_b, max_b);
+
+    if (eg_r > channel_maximum[0])
+      eg_r = channel_maximum[0];
+    else if (eg_r < channel_minimum[0])
+      eg_r = channel_minimum[0];
+    if (eg_b > channel_maximum[2])
+      eg_b = channel_maximum[2];
+    else if (eg_b < channel_minimum[2])
+      eg_b = channel_minimum[2];
+    nraw[nr_offset(y, x)][0] = eg_r;
+    nraw[nr_offset(y, x)][2] = eg_b;
+  }
+}
+
+void DHT::make_rb()
+{
+#if defined(LIBRAW_USE_OPENMP)
+#pragma omp barrier
+#pragma omp parallel for schedule(guided)
+#endif
+  for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
+  {
+    make_rbdiag(i);
+  }
+#if defined(LIBRAW_USE_OPENMP)
+#pragma omp barrier
+#pragma omp parallel for schedule(guided)
+#endif
+  for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
+  {
+    make_rbhv(i);
+  }
+}
+
+/*
+ * Ð¿ÐµÑ€ÐµÐ½Ð¾Ñ Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð² выходной маÑÑив
+ */
+void DHT::copy_to_image()
+{
+  int iwidth = libraw.imgdata.sizes.iwidth;
+#if defined(LIBRAW_USE_OPENMP)
+#ifdef _MSC_VER
+#pragma omp parallel for
+#else
+#pragma omp parallel for schedule(guided) collapse(2)
+#endif
+#endif
+  for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
+  {
+    for (int j = 0; j < iwidth; ++j)
+    {
+      libraw.imgdata.image[i * iwidth + j][0] =
+          (unsigned short)(nraw[nr_offset(i + nr_topmargin, j + nr_leftmargin)]
+                               [0]);
+      libraw.imgdata.image[i * iwidth + j][2] =
+          (unsigned short)(nraw[nr_offset(i + nr_topmargin, j + nr_leftmargin)]
+                               [2]);
+      libraw.imgdata.image[i * iwidth + j][1] =
+          libraw.imgdata.image[i * iwidth + j][3] =
+              (unsigned short)(nraw[nr_offset(i + nr_topmargin,
+                                              j + nr_leftmargin)][1]);
+    }
+  }
+}
+
+DHT::~DHT()
+{
+  free(nraw);
+  free(ndir);
+}
+
+void LibRaw::dht_interpolate()
+{
+  DHT dht(*this);
+  dht.hide_hots();
+  dht.make_hv_dirs();
+  //	dht.illustrate_dirs();
+  dht.make_greens();
+  dht.make_diag_dirs();
+  //	dht.illustrate_dirs();
+  dht.make_rb();
+  dht.restore_hots();
+  dht.copy_to_image();
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/demosaic/misc_demosaic.cpp libkdcraw/libkdcraw/libraw/src/demosaic/misc_demosaic.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/demosaic/misc_demosaic.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/demosaic/misc_demosaic.cpp	2022-11-07 07:46:31.734795008 +0300
@@ -0,0 +1,423 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+void LibRaw::pre_interpolate()
+{
+  ushort(*img)[4];
+  int row, col, c;
+  RUN_CALLBACK(LIBRAW_PROGRESS_PRE_INTERPOLATE, 0, 2);
+  if (shrink)
+  {
+    if (half_size)
+    {
+      height = iheight;
+      width = iwidth;
+      if (filters == 9)
+      {
+        for (row = 0; row < 3; row++)
+          for (col = 1; col < 4; col++)
+            if (!(image[row * width + col][0] | image[row * width + col][2]))
+              goto break2;
+      break2:
+        for (; row < height; row += 3)
+          for (col = (col - 1) % 3 + 1; col < width - 1; col += 3)
+          {
+            img = image + row * width + col;
+            for (c = 0; c < 3; c += 2)
+              img[0][c] = (img[-1][c] + img[1][c]) >> 1;
+          }
+      }
+    }
+    else
+    {
+      img = (ushort(*)[4])calloc(height, width * sizeof *img);
+      merror(img, "pre_interpolate()");
+      for (row = 0; row < height; row++)
+        for (col = 0; col < width; col++)
+        {
+          c = fcol(row, col);
+          img[row * width + col][c] =
+              image[(row >> 1) * iwidth + (col >> 1)][c];
+        }
+      free(image);
+      image = img;
+      shrink = 0;
+    }
+  }
+  if (filters > 1000 && colors == 3)
+  {
+    mix_green = four_color_rgb ^ half_size;
+    if (four_color_rgb | half_size)
+      colors++;
+    else
+    {
+      for (row = FC(1, 0) >> 1; row < height; row += 2)
+        for (col = FC(row, 1) & 1; col < width; col += 2)
+          image[row * width + col][1] = image[row * width + col][3];
+      filters &= ~((filters & 0x55555555U) << 1);
+    }
+  }
+  if (half_size)
+    filters = 0;
+  RUN_CALLBACK(LIBRAW_PROGRESS_PRE_INTERPOLATE, 1, 2);
+}
+
+void LibRaw::border_interpolate(int border)
+{
+  unsigned row, col, y, x, f, c, sum[8];
+
+  for (row = 0; row < height; row++)
+    for (col = 0; col < width; col++)
+    {
+      if (col == (unsigned)border && row >= (unsigned)border && row < (unsigned)(height - border))
+        col = width - border;
+      memset(sum, 0, sizeof sum);
+      for (y = row - 1; y != row + 2; y++)
+        for (x = col - 1; x != col + 2; x++)
+          if (y < height && x < width)
+          {
+            f = fcol(y, x);
+            sum[f] += image[y * width + x][f];
+            sum[f + 4]++;
+          }
+      f = fcol(row, col);
+      FORC(unsigned(colors)) if (c != f && sum[c + 4]) image[row * width + col][c] =
+          sum[c] / sum[c + 4];
+    }
+}
+
+void LibRaw::lin_interpolate_loop(int *code, int size)
+{
+  int row;
+  for (row = 1; row < height - 1; row++)
+  {
+    int col, *ip;
+    ushort *pix;
+    for (col = 1; col < width - 1; col++)
+    {
+      int i;
+      int sum[4];
+      pix = image[row * width + col];
+      ip = code + ((((row % size) * 16) + (col % size)) * 32);
+      memset(sum, 0, sizeof sum);
+      for (i = *ip++; i--; ip += 3)
+        sum[ip[2]] += pix[ip[0]] << ip[1];
+      for (i = colors; --i; ip += 2)
+        pix[ip[0]] = sum[ip[0]] * ip[1] >> 8;
+    }
+  }
+}
+
+void LibRaw::lin_interpolate()
+{
+  std::vector<int> code_buffer(16 * 16 * 32);
+  int* code = &code_buffer[0], size = 16, *ip, sum[4];
+  int f, c, x, y, row, col, shift, color;
+
+  RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE, 0, 3);
+
+  if (filters == 9)
+    size = 6;
+  border_interpolate(1);
+  for (row = 0; row < size; row++)
+    for (col = 0; col < size; col++)
+    {
+      ip = code + (((row * 16) + col) * 32) + 1;
+      f = fcol(row, col);
+      memset(sum, 0, sizeof sum);
+      for (y = -1; y <= 1; y++)
+        for (x = -1; x <= 1; x++)
+        {
+          shift = (y == 0) + (x == 0);
+          color = fcol(row + y + 48, col + x + 48);
+          if (color == f)
+            continue;
+          *ip++ = (width * y + x) * 4 + color;
+          *ip++ = shift;
+          *ip++ = color;
+          sum[color] += 1 << shift;
+        }
+      code[(row * 16 + col) * 32] = (ip - (code + ((row * 16) + col) * 32)) / 3;
+      FORCC
+      if (c != f)
+      {
+        *ip++ = c;
+        *ip++ = sum[c] > 0 ? 256 / sum[c] : 0;
+      }
+    }
+  RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE, 1, 3);
+  lin_interpolate_loop(code, size);
+  RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE, 2, 3);
+}
+
+/*
+   This algorithm is officially called:
+
+   "Interpolation using a Threshold-based variable number of gradients"
+
+   described in
+   http://scien.stanford.edu/pages/labsite/1999/psych221/projects/99/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.
+ */
+void LibRaw::vng_interpolate()
+{
+  static const signed char *cp,
+      terms[] =
+          {-2, -2, +0,   -1, 0,  0x01, -2, -2, +0,   +0, 1,  0x01, -2, -1, -1,
+           +0, 0,  0x01, -2, -1, +0,   -1, 0,  0x02, -2, -1, +0,   +0, 0,  0x03,
+           -2, -1, +0,   +1, 1,  0x01, -2, +0, +0,   -1, 0,  0x06, -2, +0, +0,
+           +0, 1,  0x02, -2, +0, +0,   +1, 0,  0x03, -2, +1, -1,   +0, 0,  0x04,
+           -2, +1, +0,   -1, 1,  0x04, -2, +1, +0,   +0, 0,  0x06, -2, +1, +0,
+           +1, 0,  0x02, -2, +2, +0,   +0, 1,  0x04, -2, +2, +0,   +1, 0,  0x04,
+           -1, -2, -1,   +0, 0,  -128, -1, -2, +0,   -1, 0,  0x01, -1, -2, +1,
+           -1, 0,  0x01, -1, -2, +1,   +0, 1,  0x01, -1, -1, -1,   +1, 0,  -120,
+           -1, -1, +1,   -2, 0,  0x40, -1, -1, +1,   -1, 0,  0x22, -1, -1, +1,
+           +0, 0,  0x33, -1, -1, +1,   +1, 1,  0x11, -1, +0, -1,   +2, 0,  0x08,
+           -1, +0, +0,   -1, 0,  0x44, -1, +0, +0,   +1, 0,  0x11, -1, +0, +1,
+           -2, 1,  0x40, -1, +0, +1,   -1, 0,  0x66, -1, +0, +1,   +0, 1,  0x22,
+           -1, +0, +1,   +1, 0,  0x33, -1, +0, +1,   +2, 1,  0x10, -1, +1, +1,
+           -1, 1,  0x44, -1, +1, +1,   +0, 0,  0x66, -1, +1, +1,   +1, 0,  0x22,
+           -1, +1, +1,   +2, 0,  0x10, -1, +2, +0,   +1, 0,  0x04, -1, +2, +1,
+           +0, 1,  0x04, -1, +2, +1,   +1, 0,  0x04, +0, -2, +0,   +0, 1,  -128,
+           +0, -1, +0,   +1, 1,  -120, +0, -1, +1,   -2, 0,  0x40, +0, -1, +1,
+           +0, 0,  0x11, +0, -1, +2,   -2, 0,  0x40, +0, -1, +2,   -1, 0,  0x20,
+           +0, -1, +2,   +0, 0,  0x30, +0, -1, +2,   +1, 1,  0x10, +0, +0, +0,
+           +2, 1,  0x08, +0, +0, +2,   -2, 1,  0x40, +0, +0, +2,   -1, 0,  0x60,
+           +0, +0, +2,   +0, 1,  0x20, +0, +0, +2,   +1, 0,  0x30, +0, +0, +2,
+           +2, 1,  0x10, +0, +1, +1,   +0, 0,  0x44, +0, +1, +1,   +2, 0,  0x10,
+           +0, +1, +2,   -1, 1,  0x40, +0, +1, +2,   +0, 0,  0x60, +0, +1, +2,
+           +1, 0,  0x20, +0, +1, +2,   +2, 0,  0x10, +1, -2, +1,   +0, 0,  -128,
+           +1, -1, +1,   +1, 0,  -120, +1, +0, +1,   +2, 0,  0x08, +1, +0, +2,
+           -1, 0,  0x40, +1, +0, +2,   +1, 0,  0x10},
+      chood[] = {-1, -1, -1, 0, -1, +1, 0, +1, +1, +1, +1, 0, +1, -1, 0, -1};
+  ushort(*brow[5])[4], *pix;
+  int prow = 8, pcol = 2, *ip, *code[16][16], gval[8], gmin, gmax, sum[4];
+  int row, col, x, y, x1, x2, y1, y2, t, weight, grads, color, diag;
+  int g, diff, thold, num, c;
+
+  lin_interpolate();
+
+  if (filters == 1)
+    prow = pcol = 16;
+  if (filters == 9)
+    prow = pcol = 6;
+  ip = (int *)calloc(prow * pcol, 1280);
+  merror(ip, "vng_interpolate()");
+  for (row = 0; row < prow; row++) /* Precalculate for VNG */
+    for (col = 0; col < pcol; col++)
+    {
+      code[row][col] = ip;
+      for (cp = terms, t = 0; t < 64; t++)
+      {
+        y1 = *cp++;
+        x1 = *cp++;
+        y2 = *cp++;
+        x2 = *cp++;
+        weight = *cp++;
+        grads = *cp++;
+        color = fcol(row + y1 + 144, col + x1 + 144);
+        if (fcol(row + y2 + 144, col + x2 + 144) != color)
+          continue;
+        diag = (fcol(row, col + 1) == color && fcol(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 = fcol(row, col);
+        if (fcol(row + y + 144, col + x + 144) != color &&
+            fcol(row + y * 2 + 144, col + x * 2 + 144) == color)
+          *ip++ = (y * width + x) * 8 + color;
+        else
+          *ip++ = 0;
+      }
+    }
+  brow[4] = (ushort(*)[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 */
+    if (!((row - 2) % 256))
+      RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE, (row - 2) / 256 + 1,
+                   ((height - 3) / 256) + 1);
+    for (col = 2; col < width - 2; col++)
+    {
+      pix = image[row * width + col];
+      ip = code[row % prow][col % pcol];
+      memset(gval, 0, sizeof gval);
+      while ((g = ip[0]) != INT_MAX)
+      { /* Calculate gradients */
+        diff = ABS(pix[g] - pix[ip[1]]) << ip[2];
+        gval[ip[3]] += diff;
+        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 = fcol(row, col);
+      for (num = g = 0; g < 8; g++, ip += 2)
+      { /* Average the neighbors */
+        if (gval[g] <= thold)
+        {
+          FORCC
+          if (c == color && ip[1])
+            sum[c] += (pix[c] + pix[ip[1]]) >> 1;
+          else
+            sum[c] += pix[ip[0] + c];
+          num++;
+        }
+      }
+      FORCC
+      { /* Save to buffer */
+        t = pix[color];
+        if (c != color)
+          t += (sum[c] - sum[color]) / num;
+        brow[2][col][c] = CLIP(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]);
+  free(code[0][0]);
+}
+
+/*
+   Patterned Pixel Grouping Interpolation by Alain Desbiolles
+*/
+void LibRaw::ppg_interpolate()
+{
+  int dir[5] = {1, width, -1, -width, 1};
+  int row, col, diff[2], guess[2], c, d, i;
+  ushort(*pix)[4];
+
+  border_interpolate(3);
+
+  /*  Fill in the green layer with gradients and pattern recognition: */
+  RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE, 0, 3);
+#ifdef LIBRAW_USE_OPENMP
+#pragma omp parallel for default(shared) private(guess, diff, row, col, d, c,  \
+                                                 i, pix) schedule(static)
+#endif
+  for (row = 3; row < height - 3; row++)
+    for (col = 3 + (FC(row, 3) & 1), c = FC(row, col); col < width - 3;
+         col += 2)
+    {
+      pix = image + row * width + col;
+      for (i = 0; i < 2; i++)
+      {
+        d = dir[i];
+        guess[i] = (pix[-d][1] + pix[0][c] + pix[d][1]) * 2 - pix[-2 * d][c] -
+                   pix[2 * d][c];
+        diff[i] =
+            (ABS(pix[-2 * d][c] - pix[0][c]) + ABS(pix[2 * d][c] - pix[0][c]) +
+             ABS(pix[-d][1] - pix[d][1])) *
+                3 +
+            (ABS(pix[3 * d][1] - pix[d][1]) +
+             ABS(pix[-3 * d][1] - pix[-d][1])) *
+                2;
+      }
+      d = dir[i = diff[0] > diff[1]];
+      pix[0][1] = ULIM(guess[i] >> 2, pix[d][1], pix[-d][1]);
+    }
+  /*  Calculate red and blue for each green pixel:		*/
+  RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE, 1, 3);
+#ifdef LIBRAW_USE_OPENMP
+#pragma omp parallel for default(shared) private(guess, diff, row, col, d, c,  \
+                                                 i, pix) schedule(static)
+#endif
+  for (row = 1; row < height - 1; row++)
+    for (col = 1 + (FC(row, 2) & 1), c = FC(row, col + 1); col < width - 1;
+         col += 2)
+    {
+      pix = image + row * width + col;
+      for (i = 0; i < 2; c = 2 - c, i++)
+      {
+        d = dir[i];
+        pix[0][c] = CLIP(
+            (pix[-d][c] + pix[d][c] + 2 * pix[0][1] - pix[-d][1] - pix[d][1]) >>
+            1);
+      }
+    }
+  /*  Calculate blue for red pixels and vice versa:		*/
+  RUN_CALLBACK(LIBRAW_PROGRESS_INTERPOLATE, 2, 3);
+#ifdef LIBRAW_USE_OPENMP
+#pragma omp parallel for default(shared) private(guess, diff, row, col, d, c,  \
+                                                 i, pix) schedule(static)
+#endif
+  for (row = 1; row < height - 1; row++)
+    for (col = 1 + (FC(row, 1) & 1), c = 2 - FC(row, col); col < width - 1;
+         col += 2)
+    {
+      pix = image + row * width + col;
+      for (i = 0; i < 2; i++)
+      {
+        d = dir[i] + dir[i+1];
+        diff[i] = ABS(pix[-d][c] - pix[d][c]) + ABS(pix[-d][1] - pix[0][1]) +
+                  ABS(pix[d][1] - pix[0][1]);
+        guess[i] =
+            pix[-d][c] + pix[d][c] + 2 * pix[0][1] - pix[-d][1] - pix[d][1];
+      }
+      if (diff[0] != diff[1])
+        pix[0][c] = CLIP(guess[diff[0] > diff[1]] >> 1);
+      else
+        pix[0][c] = CLIP((guess[0] + guess[1]) >> 2);
+    }
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/demosaic/xtrans_demosaic.cpp libkdcraw/libkdcraw/libraw/src/demosaic/xtrans_demosaic.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/demosaic/xtrans_demosaic.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/demosaic/xtrans_demosaic.cpp	2022-11-07 07:46:31.734795008 +0300
@@ -0,0 +1,381 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+#define fcol(row, col) xtrans[(row + 6) % 6][(col + 6) % 6]
+/*
+   Frank Markesteijn's algorithm for Fuji X-Trans sensors
+ */
+void LibRaw::xtrans_interpolate(int passes)
+{
+  int c, d, f, g, h, i, v, ng, row, col, top, left, mrow, mcol;
+
+  int cstat[4] = {0, 0, 0, 0};
+
+  int val, ndir, pass, hm[8], avg[4], color[3][8];
+  static const short orth[12] = {1, 0, 0, 1, -1, 0, 0, -1, 1, 0, 0, 1},
+                     patt[2][16] = {{0, 1, 0, -1, 2, 0, -1, 0, 1, 1, 1, -1, 0,
+                                     0, 0, 0},
+                                    {0, 1, 0, -2, 1, 0, -2, 0, 1, 1, -2, -2, 1,
+                                     -1, -1, 1}},
+                     dir[4] = {1, LIBRAW_AHD_TILE, LIBRAW_AHD_TILE + 1,
+                               LIBRAW_AHD_TILE - 1};
+  short allhex[3][3][2][8], *hex;
+  ushort min, max, sgrow, sgcol;
+  ushort(*rgb)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3], (*rix)[3], (*pix)[4];
+  short(*lab)[LIBRAW_AHD_TILE][3], (*lix)[3];
+  float(*drv)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE], diff[6], tr;
+  char(*homo)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE], *buffer;
+
+  if (width < LIBRAW_AHD_TILE || height < LIBRAW_AHD_TILE)
+    throw LIBRAW_EXCEPTION_IO_CORRUPT; // too small image
+                                       /* Check against right pattern */
+  for (row = 0; row < 6; row++)
+    for (col = 0; col < 6; col++)
+      cstat[(unsigned)fcol(row, col)]++;
+
+  if (cstat[0] < 6 || cstat[0] > 10 || cstat[1] < 16 || cstat[1] > 24 ||
+      cstat[2] < 6 || cstat[2] > 10 || cstat[3])
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+
+  // Init allhex table to unreasonable values
+  for (int i = 0; i < 3; i++)
+    for (int j = 0; j < 3; j++)
+      for (int k = 0; k < 2; k++)
+        for (int l = 0; l < 8; l++)
+          allhex[i][j][k][l] = 32700;
+
+  cielab(0, 0);
+  ndir = 4 << (passes > 1);
+  buffer = (char *)malloc(LIBRAW_AHD_TILE * LIBRAW_AHD_TILE * (ndir * 11 + 6));
+  merror(buffer, "xtrans_interpolate()");
+  rgb = (ushort(*)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3])buffer;
+  lab = (short(*)[LIBRAW_AHD_TILE][3])(
+      buffer + LIBRAW_AHD_TILE * LIBRAW_AHD_TILE * (ndir * 6));
+  drv = (float(*)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE])(
+      buffer + LIBRAW_AHD_TILE * LIBRAW_AHD_TILE * (ndir * 6 + 6));
+  homo = (char(*)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE])(
+      buffer + LIBRAW_AHD_TILE * LIBRAW_AHD_TILE * (ndir * 10 + 6));
+
+  int minv = 0, maxv = 0, minh = 0, maxh = 0;
+  /* Map a green hexagon around each non-green pixel and vice versa:	*/
+  for (row = 0; row < 3; row++)
+    for (col = 0; col < 3; col++)
+      for (ng = d = 0; d < 10; d += 2)
+      {
+        g = fcol(row, col) == 1;
+        if (fcol(row + orth[d], col + orth[d + 2]) == 1)
+          ng = 0;
+        else
+          ng++;
+        if (ng == 4)
+        {
+          sgrow = row;
+          sgcol = col;
+        }
+        if (ng == g + 1)
+          FORC(8)
+          {
+            v = orth[d] * patt[g][c * 2] + orth[d + 1] * patt[g][c * 2 + 1];
+            h = orth[d + 2] * patt[g][c * 2] + orth[d + 3] * patt[g][c * 2 + 1];
+            minv = MIN(v, minv);
+            maxv = MAX(v, maxv);
+            minh = MIN(v, minh);
+            maxh = MAX(v, maxh);
+            allhex[row][col][0][c ^ (g * 2 & d)] = h + v * width;
+            allhex[row][col][1][c ^ (g * 2 & d)] = h + v * LIBRAW_AHD_TILE;
+          }
+      }
+
+  // Check allhex table initialization
+  for (int i = 0; i < 3; i++)
+    for (int j = 0; j < 3; j++)
+      for (int k = 0; k < 2; k++)
+        for (int l = 0; l < 8; l++)
+          if (allhex[i][j][k][l] > maxh + maxv * width + 1 ||
+              allhex[i][j][k][l] < minh + minv * width - 1)
+            throw LIBRAW_EXCEPTION_IO_CORRUPT;
+  int retrycount = 0;
+
+  /* Set green1 and green3 to the minimum and maximum allowed values:	*/
+  for (row = 2; row < height - 2; row++)
+    for (min = ~(max = 0), col = 2; col < width - 2; col++)
+    {
+      if (fcol(row, col) == 1 && (min = ~(max = 0)))
+        continue;
+      pix = image + row * width + col;
+      hex = allhex[row % 3][col % 3][0];
+      if (!max)
+        FORC(6)
+        {
+          val = pix[hex[c]][1];
+          if (min > val)
+            min = val;
+          if (max < val)
+            max = val;
+        }
+      pix[0][1] = min;
+      pix[0][3] = max;
+      switch ((row - sgrow) % 3)
+      {
+      case 1:
+        if (row < height - 3)
+        {
+          row++;
+          col--;
+        }
+        break;
+      case 2:
+        if ((min = ~(max = 0)) && (col += 2) < width - 3 && row > 2)
+        {
+          row--;
+          if (retrycount++ > width * height)
+            throw LIBRAW_EXCEPTION_IO_CORRUPT;
+        }
+      }
+    }
+
+  for (row = 3; row < 9 && row < height - 3; row++)
+	  for (col = 3; col < 9 && col < width - 3; col++)
+	  {
+		  if ((f = fcol(row, col)) == 1)
+			  continue;
+		  hex = allhex[row % 3][col % 3][0];
+		  FORC(2)
+		  {
+			  int idx3 = 3 * hex[4 + c] + row * width + col;
+			  int idx4 = -3 * hex[4 + c] + row * width + col;
+			  int maxidx = width * height;
+			  if (idx3 < 0 || idx3 >= maxidx)
+				  throw LIBRAW_EXCEPTION_IO_CORRUPT;
+			  if (idx4 < 0 || idx4 >= maxidx)
+				  throw LIBRAW_EXCEPTION_IO_CORRUPT;
+		  }
+	  }
+
+  for (top = 3; top < height - 19; top += LIBRAW_AHD_TILE - 16)
+    for (left = 3; left < width - 19; left += LIBRAW_AHD_TILE - 16)
+    {
+      mrow = MIN(top + LIBRAW_AHD_TILE, height - 3);
+      mcol = MIN(left + LIBRAW_AHD_TILE, width - 3);
+      for (row = top; row < mrow; row++)
+        for (col = left; col < mcol; col++)
+          memcpy(rgb[0][row - top][col - left], image[row * width + col], 6);
+      FORC3 memcpy(rgb[c + 1], rgb[0], sizeof *rgb);
+
+      /* Interpolate green horizontally, vertically, and along both diagonals:
+       */
+      for (row = top; row < mrow; row++)
+        for (col = left; col < mcol; col++)
+        {
+          if ((f = fcol(row, col)) == 1)
+            continue;
+          pix = image + row * width + col;
+          hex = allhex[row % 3][col % 3][0];
+          color[1][0] = 174 * (pix[hex[1]][1] + pix[hex[0]][1]) -
+                        46 * (pix[2 * hex[1]][1] + pix[2 * hex[0]][1]);
+          color[1][1] = 223 * pix[hex[3]][1] + pix[hex[2]][1] * 33 +
+                        92 * (pix[0][f] - pix[-hex[2]][f]);
+          FORC(2)
+          color[1][2 + c] = 164 * pix[hex[4 + c]][1] +
+                            92 * pix[-2 * hex[4 + c]][1] +
+                            33 * (2 * pix[0][f] - pix[3 * hex[4 + c]][f] -
+                                  pix[-3 * hex[4 + c]][f]);
+          FORC4 rgb[c ^ !((row - sgrow) % 3)][row - top][col - left][1] =
+              LIM(color[1][c] >> 8, pix[0][1], pix[0][3]);
+        }
+
+      for (pass = 0; pass < passes; pass++)
+      {
+        if (pass == 1)
+          memcpy(rgb += 4, buffer, 4 * sizeof *rgb);
+
+        /* Recalculate green from interpolated values of closer pixels:	*/
+        if (pass)
+        {
+          for (row = top + 2; row < mrow - 2; row++)
+            for (col = left + 2; col < mcol - 2; col++)
+            {
+              if ((f = fcol(row, col)) == 1)
+                continue;
+              pix = image + row * width + col;
+              hex = allhex[row % 3][col % 3][1];
+              for (d = 3; d < 6; d++)
+              {
+                rix =
+                    &rgb[(d - 2) ^ !((row - sgrow) % 3)][row - top][col - left];
+                val = rix[-2 * hex[d]][1] + 2 * rix[hex[d]][1] -
+                      rix[-2 * hex[d]][f] - 2 * rix[hex[d]][f] + 3 * rix[0][f];
+                rix[0][1] = LIM(val / 3, pix[0][1], pix[0][3]);
+              }
+            }
+        }
+
+        /* Interpolate red and blue values for solitary green pixels:	*/
+        for (row = (top - sgrow + 4) / 3 * 3 + sgrow; row < mrow - 2; row += 3)
+          for (col = (left - sgcol + 4) / 3 * 3 + sgcol; col < mcol - 2;
+               col += 3)
+          {
+            rix = &rgb[0][row - top][col - left];
+            h = fcol(row, col + 1);
+            memset(diff, 0, sizeof diff);
+            for (i = 1, d = 0; d < 6; d++, i ^= LIBRAW_AHD_TILE ^ 1, h ^= 2)
+            {
+              for (c = 0; c < 2; c++, h ^= 2)
+              {
+                g = 2 * rix[0][1] - rix[i << c][1] - rix[-i << c][1];
+                color[h][d] = g + rix[i << c][h] + rix[-i << c][h];
+                if (d > 1)
+                  diff[d] += SQR((float)rix[i << c][1] - (float)rix[-i << c][1] -
+                  (float)rix[i << c][h] + (float)rix[-i << c][h]) + SQR((float)g);
+              }
+              if (d > 1 && (d & 1))
+                if (diff[d - 1] < diff[d])
+                  FORC(2) color[c * 2][d] = color[c * 2][d - 1];
+              if (d < 2 || (d & 1))
+              {
+                FORC(2) rix[0][c * 2] = CLIP(color[c * 2][d] / 2);
+                rix += LIBRAW_AHD_TILE * LIBRAW_AHD_TILE;
+              }
+            }
+          }
+
+        /* Interpolate red for blue pixels and vice versa:		*/
+        for (row = top + 3; row < mrow - 3; row++)
+          for (col = left + 3; col < mcol - 3; col++)
+          {
+            if ((f = 2 - fcol(row, col)) == 1)
+              continue;
+            rix = &rgb[0][row - top][col - left];
+            c = (row - sgrow) % 3 ? LIBRAW_AHD_TILE : 1;
+            h = 3 * (c ^ LIBRAW_AHD_TILE ^ 1);
+            for (d = 0; d < 4; d++, rix += LIBRAW_AHD_TILE * LIBRAW_AHD_TILE)
+            {
+              i = d > 1 || ((d ^ c) & 1) ||
+                          ((ABS(rix[0][1] - rix[c][1]) +
+                            ABS(rix[0][1] - rix[-c][1])) <
+                           2 * (ABS(rix[0][1] - rix[h][1]) +
+                                ABS(rix[0][1] - rix[-h][1])))
+                      ? c
+                      : h;
+              rix[0][f] = CLIP((rix[i][f] + rix[-i][f] + 2 * rix[0][1] -
+                                rix[i][1] - rix[-i][1]) /
+                               2);
+            }
+          }
+
+        /* Fill in red and blue for 2x2 blocks of green:		*/
+        for (row = top + 2; row < mrow - 2; row++)
+          if ((row - sgrow) % 3)
+            for (col = left + 2; col < mcol - 2; col++)
+              if ((col - sgcol) % 3)
+              {
+                rix = &rgb[0][row - top][col - left];
+                hex = allhex[row % 3][col % 3][1];
+                for (d = 0; d < ndir;
+                     d += 2, rix += LIBRAW_AHD_TILE * LIBRAW_AHD_TILE)
+                  if (hex[d] + hex[d + 1])
+                  {
+                    g = 3 * rix[0][1] - 2 * rix[hex[d]][1] - rix[hex[d + 1]][1];
+                    for (c = 0; c < 4; c += 2)
+                      rix[0][c] = CLIP(
+                          (g + 2 * rix[hex[d]][c] + rix[hex[d + 1]][c]) / 3);
+                  }
+                  else
+                  {
+                    g = 2 * rix[0][1] - rix[hex[d]][1] - rix[hex[d + 1]][1];
+                    for (c = 0; c < 4; c += 2)
+                      rix[0][c] =
+                          CLIP((g + rix[hex[d]][c] + rix[hex[d + 1]][c]) / 2);
+                  }
+              }
+      }
+      rgb = (ushort(*)[LIBRAW_AHD_TILE][LIBRAW_AHD_TILE][3])buffer;
+      mrow -= top;
+      mcol -= left;
+
+      /* Convert to CIELab and differentiate in all directions:	*/
+      for (d = 0; d < ndir; d++)
+      {
+        for (row = 2; row < mrow - 2; row++)
+          for (col = 2; col < mcol - 2; col++)
+            cielab(rgb[d][row][col], lab[row][col]);
+        for (f = dir[d & 3], row = 3; row < mrow - 3; row++)
+          for (col = 3; col < mcol - 3; col++)
+          {
+            lix = &lab[row][col];
+            g = 2 * lix[0][0] - lix[f][0] - lix[-f][0];
+            drv[d][row][col] =
+                SQR(g) +
+                SQR((2 * lix[0][1] - lix[f][1] - lix[-f][1] + g * 500 / 232)) +
+                SQR((2 * lix[0][2] - lix[f][2] - lix[-f][2] - g * 500 / 580));
+          }
+      }
+
+      /* Build homogeneity maps from the derivatives:			*/
+      memset(homo, 0, ndir * LIBRAW_AHD_TILE * LIBRAW_AHD_TILE);
+      for (row = 4; row < mrow - 4; row++)
+        for (col = 4; col < mcol - 4; col++)
+        {
+          for (tr = FLT_MAX, d = 0; d < ndir; d++)
+            if (tr > drv[d][row][col])
+              tr = drv[d][row][col];
+          tr *= 8;
+          for (d = 0; d < ndir; d++)
+            for (v = -1; v <= 1; v++)
+              for (h = -1; h <= 1; h++)
+                if (drv[d][row + v][col + h] <= tr)
+                  homo[d][row][col]++;
+        }
+
+      /* Average the most homogenous pixels for the final result:	*/
+      if (height - top < LIBRAW_AHD_TILE + 4)
+        mrow = height - top + 2;
+      if (width - left < LIBRAW_AHD_TILE + 4)
+        mcol = width - left + 2;
+      for (row = MIN(top, 8); row < mrow - 8; row++)
+        for (col = MIN(left, 8); col < mcol - 8; col++)
+        {
+          for (d = 0; d < ndir; d++)
+            for (hm[d] = 0, v = -2; v <= 2; v++)
+              for (h = -2; h <= 2; h++)
+                hm[d] += homo[d][row + v][col + h];
+          for (d = 0; d < ndir - 4; d++)
+            if (hm[d] < hm[d + 4])
+              hm[d] = 0;
+            else if (hm[d] > hm[d + 4])
+              hm[d + 4] = 0;
+          for (max = hm[0], d = 1; d < ndir; d++)
+            if (max < hm[d])
+              max = hm[d];
+          max -= max >> 3;
+          memset(avg, 0, sizeof avg);
+          for (d = 0; d < ndir; d++)
+            if (hm[d] >= max)
+            {
+              FORC3 avg[c] += rgb[d][row][col][c];
+              avg[3]++;
+            }
+          FORC3 image[(row + top) * width + col + left][c] = avg[c] / avg[3];
+        }
+    }
+  free(buffer);
+  border_interpolate(8);
+}
+#undef fcol
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/integration/dngsdk_glue.cpp libkdcraw/libkdcraw/libraw/src/integration/dngsdk_glue.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/integration/dngsdk_glue.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/integration/dngsdk_glue.cpp	2022-11-07 07:46:31.734795008 +0300
@@ -0,0 +1,389 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/libraw_cxx_defs.h"
+
+#if defined (USE_GPRSDK) && !defined(USE_DNGSDK)
+#error  GPR (GoPro) SDK should be used with Adobe DNG SDK
+#endif
+#ifdef USE_DNGSDK
+#include "dng_read_image.h"
+#endif
+#ifdef USE_GPRSDK
+#include "gpr_read_image.h"
+#endif
+
+#ifdef USE_DNGSDK
+static dng_ifd* search_single_ifd(const std::vector <dng_ifd *>& v, uint64 offset, int& idx, dng_stream& stream)
+{
+    idx = -1;
+    for (int i = 0; i < v.size(); i++)
+    {
+        if (!v[i]) continue;
+        if (v[i]->fTileOffsetsOffset == offset)
+        {
+            idx = i;
+            return v[i];
+        }
+        else if (v[i]->fTileOffsetsCount == 1 && v[i]->fTileOffset[0] == offset)
+        {
+            idx = i;
+            return v[i];
+        }
+		else if (v[i]->fTileOffsetsCount > dng_ifd::kMaxTileInfo)
+		{
+			uint64 p  = stream.Position();
+			stream.SetReadPosition(v[i]->fTileOffsetsOffset);
+			int32 oo = stream.TagValue_uint32(v[i]->fTileOffsetsType);
+			stream.SetReadPosition(p);
+			if (oo == offset)
+			{
+				idx = i;
+				return v[i];
+			}
+		}
+    }
+    return NULL;
+}
+
+static dng_ifd* search_for_ifd(const dng_info& info, uint64 offset, ushort w, ushort h, int& ifdIndex, dng_stream& stream)
+{
+    dng_ifd *ret = 0;
+    ret = search_single_ifd(info.fIFD, offset, ifdIndex, stream);
+    int dummy;
+    if (!ret) ret = search_single_ifd(info.fChainedIFD, offset, dummy, stream);
+    if (!ret)
+    {
+        for (int c = 0; !ret && c < info.fChainedSubIFD.size(); c++)
+            ret = search_single_ifd(info.fChainedSubIFD[c], offset, dummy, stream);
+    }
+    if (ret && (ret->fImageLength == h) && ret->fImageWidth == w)
+        return ret;
+    ifdIndex = -1;
+    return 0;
+}
+#endif
+
+int LibRaw::valid_for_dngsdk()
+{
+#ifndef USE_DNGSDK
+  return 0;
+#else
+  if (!imgdata.idata.dng_version)
+    return 0;
+  if (libraw_internal_data.unpacker_data.tiff_compress == 34892
+	  && libraw_internal_data.unpacker_data.tiff_bps == 8
+	  && libraw_internal_data.unpacker_data.tiff_samples == 3
+	  && load_raw == &LibRaw::lossy_dng_load_raw
+	  )
+  {
+      if (!dnghost)
+          return 0;
+      dng_host *host = static_cast<dng_host *>(dnghost);
+      libraw_dng_stream stream(libraw_internal_data.internal_data.input);
+      AutoPtr<dng_negative> negative;
+      negative.Reset(host->Make_dng_negative());
+      dng_info info;
+      info.Parse(*host, stream);
+      info.PostParse(*host);
+      if (!info.IsValidDNG())
+          return 0;
+      negative->Parse(*host, stream, info);
+      negative->PostParse(*host, stream, info);
+      int ifdindex = -1;
+      dng_ifd *rawIFD = search_for_ifd(info, libraw_internal_data.unpacker_data.data_offset, imgdata.sizes.raw_width, imgdata.sizes.raw_height, ifdindex,stream);
+      if (rawIFD && ifdindex >= 0 && ifdindex == info.fMainIndex)
+          return 1;
+	  return 0;
+  }
+
+#ifdef USE_GPRSDK
+  if (load_raw == &LibRaw::vc5_dng_load_raw_placeholder) // regardless of flags or use_dngsdk value!
+      return 1;
+#endif
+  if (!imgdata.params.use_dngsdk)
+    return 0;
+  if (load_raw == &LibRaw::lossy_dng_load_raw) // WHY??
+    return 0;
+  if (load_raw ==
+      &LibRaw::float_dng_load_raw_placeholder) // regardless of flags!
+    return 1;
+  if (is_floating_point() && (imgdata.params.use_dngsdk & LIBRAW_DNG_FLOAT))
+    return 1;
+  if (!imgdata.idata.filters && (imgdata.params.use_dngsdk & LIBRAW_DNG_LINEAR))
+    return 1;
+  if (libraw_internal_data.unpacker_data.tiff_bps == 8 &&
+      (imgdata.params.use_dngsdk & LIBRAW_DNG_8BIT))
+    return 1;
+  if (libraw_internal_data.unpacker_data.tiff_compress == 8 &&
+      (imgdata.params.use_dngsdk & LIBRAW_DNG_DEFLATE))
+    return 1;
+  if (libraw_internal_data.unpacker_data.tiff_samples == 2)
+    return 0; // Always deny 2-samples (old fuji superccd)
+  if (imgdata.idata.filters == 9 &&
+      (imgdata.params.use_dngsdk & LIBRAW_DNG_XTRANS))
+    return 1;
+  if (is_fuji_rotated())
+    return 0; // refuse
+  if (imgdata.params.use_dngsdk & LIBRAW_DNG_OTHER)
+    return 1;
+  return 0;
+#endif
+}
+
+
+
+
+int LibRaw::try_dngsdk()
+{
+#ifdef USE_DNGSDK
+  if (!dnghost)
+    return LIBRAW_UNSPECIFIED_ERROR;
+
+  dng_host *host = static_cast<dng_host *>(dnghost);
+
+  try
+  {
+    libraw_dng_stream stream(libraw_internal_data.internal_data.input);
+
+    AutoPtr<dng_negative> negative;
+    negative.Reset(host->Make_dng_negative());
+
+    dng_info info;
+    info.Parse(*host, stream);
+    info.PostParse(*host);
+
+    if (!info.IsValidDNG())
+    {
+      return LIBRAW_DATA_ERROR;
+    }
+    negative->Parse(*host, stream, info);
+    negative->PostParse(*host, stream, info);
+    int ifdindex;
+    dng_ifd *rawIFD = search_for_ifd(info,libraw_internal_data.unpacker_data.data_offset,imgdata.sizes.raw_width,imgdata.sizes.raw_height,ifdindex,stream);
+    if(!rawIFD)
+        return LIBRAW_DATA_ERROR;
+
+    AutoPtr<dng_simple_image> stage2;
+    bool stage23used = false;
+	bool zerocopy = false;
+
+    //(new dng_simple_image(rawIFD->Bounds(), rawIFD->fSamplesPerPixel, rawIFD->PixelType(), host->Allocator()));
+
+    if (((libraw_internal_data.unpacker_data.tiff_compress == 34892
+        && libraw_internal_data.unpacker_data.tiff_bps == 8
+        && libraw_internal_data.unpacker_data.tiff_samples == 3
+        && load_raw == &LibRaw::lossy_dng_load_raw) ||
+		(imgdata.params.raw_processing_options & (LIBRAW_PROCESSING_DNG_STAGE2| LIBRAW_PROCESSING_DNG_STAGE3)))
+        && ifdindex >= 0)
+    {
+        if (info.fMainIndex != ifdindex)
+            info.fMainIndex = ifdindex;
+
+        negative->ReadStage1Image(*host, stream, info);
+        negative->BuildStage2Image(*host);
+		imgdata.process_warnings |= LIBRAW_WARN_DNG_STAGE2_APPLIED;
+		if (imgdata.params.raw_processing_options & LIBRAW_PROCESSING_DNG_STAGE3)
+		{
+			negative->BuildStage3Image(*host);
+			stage2.Reset((dng_simple_image*)negative->Stage3Image());
+			imgdata.process_warnings |= LIBRAW_WARN_DNG_STAGE3_APPLIED;
+		}
+		else
+			stage2.Reset((dng_simple_image*)negative->Stage2Image());
+        stage23used = true;
+    }
+    else
+    {
+        stage2.Reset(new dng_simple_image(rawIFD->Bounds(), rawIFD->fSamplesPerPixel, rawIFD->PixelType(), host->Allocator()));
+#ifdef USE_GPRSDK
+        if (libraw_internal_data.unpacker_data.tiff_compress == 9)
+        {
+            gpr_allocator allocator;
+            allocator.Alloc = ::malloc;
+            allocator.Free = ::free;
+            gpr_buffer_auto vc5_image_obj(allocator.Alloc, allocator.Free);
+
+            gpr_read_image reader(&vc5_image_obj);
+            reader.Read(*host, *rawIFD, stream, *stage2.Get(), NULL, NULL);
+        }
+        else
+#endif
+        {
+            dng_read_image reader;
+            reader.Read(*host, *rawIFD, stream, *stage2.Get(), NULL, NULL);
+        }
+    }
+
+    if (stage2->Bounds().W() != S.raw_width ||
+        stage2->Bounds().H() != S.raw_height)
+    {
+		if (imgdata.params.raw_processing_options & LIBRAW_PROCESSING_DNG_ALLOWSIZECHANGE)
+		{
+			S.raw_width = S.width = stage2->Bounds().W();
+			S.left_margin = 0;
+			S.raw_height = S.height = stage2->Bounds().H();
+			S.top_margin = 0;
+		}
+		else
+		{
+			stage2.Release(); // It holds copy to internal dngnegative
+			return LIBRAW_DATA_ERROR;
+		}
+    }
+	if (stage23used)
+	{
+		if (stage2->Planes() > 1)
+		{
+			imgdata.idata.filters = 0;
+			imgdata.idata.colors = stage2->Planes();
+		}
+		// reset BL and whitepoint
+		imgdata.color.black = 0;
+		memset(imgdata.color.cblack, 0, sizeof(imgdata.color.cblack));
+		imgdata.color.maximum = 0xffff;
+	}
+
+    int pplanes = stage2->Planes();
+    int ptype = stage2->PixelType();
+
+    dng_pixel_buffer buffer;
+    stage2->GetPixelBuffer(buffer);
+
+    int pixels = stage2->Bounds().H() * stage2->Bounds().W() * pplanes;
+
+    if (ptype == ttShort && !stage23used &&  !is_curve_linear())
+    {
+      imgdata.rawdata.raw_alloc = malloc(pixels * TagTypeSize(ptype));
+      ushort *src = (ushort *)buffer.fData;
+      ushort *dst = (ushort *)imgdata.rawdata.raw_alloc;
+      for (int i = 0; i < pixels; i++)
+        dst[i] = imgdata.color.curve[src[i]];
+      S.raw_pitch = S.raw_width * pplanes * TagTypeSize(ptype);
+
+    }
+    else if (ptype == ttByte)
+    {
+      imgdata.rawdata.raw_alloc = malloc(pixels * TagTypeSize(ttShort));
+      unsigned char *src = (unsigned char *)buffer.fData;
+      ushort *dst = (ushort *)imgdata.rawdata.raw_alloc;
+      if (is_curve_linear())
+      {
+        for (int i = 0; i < pixels; i++)
+          dst[i] = src[i];
+      }
+      else
+      {
+        for (int i = 0; i < pixels; i++)
+          dst[i] = imgdata.color.curve[src[i]];
+      }
+      S.raw_pitch = S.raw_width * pplanes * TagTypeSize(ttShort);
+    }
+    else
+    {
+      // Alloc
+      if ((imgdata.params.raw_processing_options & LIBRAW_PROCESSING_DNGSDK_ZEROCOPY) && !stage23used)
+      {
+        zerocopy = true;
+      }
+      else
+      {
+        imgdata.rawdata.raw_alloc = malloc(pixels * TagTypeSize(ptype));
+        memmove(imgdata.rawdata.raw_alloc, buffer.fData,
+                pixels * TagTypeSize(ptype));
+      }
+      S.raw_pitch = S.raw_width * pplanes * TagTypeSize(ptype);
+    }
+
+    if (stage23used)
+        stage2.Release();
+
+    if (zerocopy)
+    {
+      switch (ptype)
+      {
+      case ttFloat:
+        if (pplanes == 1)
+          imgdata.rawdata.float_image = (float *)buffer.fData;
+        else if (pplanes == 3)
+          imgdata.rawdata.float3_image = (float(*)[3])buffer.fData;
+        else if (pplanes == 4)
+          imgdata.rawdata.float4_image = (float(*)[4])buffer.fData;
+        break;
+
+      case ttShort:
+        if (pplanes == 1)
+          imgdata.rawdata.raw_image = (ushort *)buffer.fData;
+        else if (pplanes == 3)
+          imgdata.rawdata.color3_image = (ushort(*)[3])buffer.fData;
+        else if (pplanes == 4)
+          imgdata.rawdata.color4_image = (ushort(*)[4])buffer.fData;
+        break;
+      default:
+        /* do nothing */
+        break;
+      }
+    }
+    else
+    {
+      switch (ptype)
+      {
+      case ttFloat:
+        if (pplanes == 1)
+          imgdata.rawdata.float_image = (float *)imgdata.rawdata.raw_alloc;
+        else if (pplanes == 3)
+          imgdata.rawdata.float3_image = (float(*)[3])imgdata.rawdata.raw_alloc;
+        else if (pplanes == 4)
+          imgdata.rawdata.float4_image = (float(*)[4])imgdata.rawdata.raw_alloc;
+        break;
+
+      case ttByte:
+      case ttShort:
+        if (pplanes == 1)
+          imgdata.rawdata.raw_image = (ushort *)imgdata.rawdata.raw_alloc;
+        else if (pplanes == 3)
+          imgdata.rawdata.color3_image =
+              (ushort(*)[3])imgdata.rawdata.raw_alloc;
+        else if (pplanes == 4)
+          imgdata.rawdata.color4_image =
+              (ushort(*)[4])imgdata.rawdata.raw_alloc;
+        break;
+      default:
+        /* do nothing */
+        break;
+      }
+    }
+    if (zerocopy)
+    {
+      dng_negative *stolen = negative.Release();
+      dngnegative = stolen;
+      dng_simple_image *simage = stage2.Release();
+      dngimage = simage;
+    }
+  }
+  catch (...)
+  {
+    return LIBRAW_UNSPECIFIED_ERROR;
+  }
+  return imgdata.rawdata.raw_alloc ? LIBRAW_SUCCESS : LIBRAW_UNSPECIFIED_ERROR;
+#else
+  return LIBRAW_UNSPECIFIED_ERROR;
+#endif
+}
+void LibRaw::set_dng_host(void *p)
+{
+#ifdef USE_DNGSDK
+  dnghost = p;
+#endif
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/integration/rawspeed_glue.cpp libkdcraw/libkdcraw/libraw/src/integration/rawspeed_glue.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/integration/rawspeed_glue.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/integration/rawspeed_glue.cpp	2022-11-07 07:46:31.734795008 +0300
@@ -0,0 +1,273 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/libraw_cxx_defs.h"
+
+#ifdef USE_RAWSPEED
+using namespace RawSpeed;
+
+CameraMetaDataLR::CameraMetaDataLR(char *data, int sz) : CameraMetaData()
+{
+  ctxt = xmlNewParserCtxt();
+  if (ctxt == NULL)
+  {
+    ThrowCME("CameraMetaData:Could not initialize context.");
+  }
+
+  xmlResetLastError();
+  doc = xmlCtxtReadMemory(ctxt, data, sz, "", NULL, XML_PARSE_DTDVALID);
+
+  if (doc == NULL)
+  {
+    ThrowCME("CameraMetaData: XML Document could not be parsed successfully. "
+             "Error was: %s",
+             ctxt->lastError.message);
+  }
+
+  if (ctxt->valid == 0)
+  {
+    if (ctxt->lastError.code == 0x5e)
+    {
+      // ignore this error code
+    }
+    else
+    {
+      ThrowCME("CameraMetaData: XML file does not validate. DTD Error was: %s",
+               ctxt->lastError.message);
+    }
+  }
+
+  xmlNodePtr cur;
+  cur = xmlDocGetRootElement(doc);
+  if (xmlStrcmp(cur->name, (const xmlChar *)"Cameras"))
+  {
+    ThrowCME("CameraMetaData: XML document of the wrong type, root node is not "
+             "cameras.");
+    return;
+  }
+
+  cur = cur->xmlChildrenNode;
+  while (cur != NULL)
+  {
+    if ((!xmlStrcmp(cur->name, (const xmlChar *)"Camera")))
+    {
+      Camera *camera = new Camera(doc, cur);
+      addCamera(camera);
+
+      // Create cameras for aliases.
+      for (unsigned int i = 0; i < camera->aliases.size(); i++)
+      {
+        addCamera(new Camera(camera, i));
+      }
+    }
+    cur = cur->next;
+  }
+  if (doc)
+    xmlFreeDoc(doc);
+  doc = 0;
+  if (ctxt)
+    xmlFreeParserCtxt(ctxt);
+  ctxt = 0;
+}
+
+CameraMetaDataLR *make_camera_metadata()
+{
+  int len = 0, i;
+  for (i = 0; i < RAWSPEED_DATA_COUNT; i++)
+    if (_rawspeed_data_xml[i])
+    {
+      len += strlen(_rawspeed_data_xml[i]);
+    }
+  char *rawspeed_xml =
+      (char *)calloc(len + 1, sizeof(_rawspeed_data_xml[0][0]));
+  if (!rawspeed_xml)
+    return NULL;
+  int offt = 0;
+  for (i = 0; i < RAWSPEED_DATA_COUNT; i++)
+    if (_rawspeed_data_xml[i])
+    {
+      int ll = strlen(_rawspeed_data_xml[i]);
+      if (offt + ll > len)
+        break;
+      memmove(rawspeed_xml + offt, _rawspeed_data_xml[i], ll);
+      offt += ll;
+    }
+  rawspeed_xml[offt] = 0;
+  CameraMetaDataLR *ret = NULL;
+  try
+  {
+    ret = new CameraMetaDataLR(rawspeed_xml, offt);
+  }
+  catch (...)
+  {
+    // Mask all exceptions
+  }
+  free(rawspeed_xml);
+  return ret;
+}
+
+#endif
+
+int LibRaw::set_rawspeed_camerafile(char *filename)
+{
+#ifdef USE_RAWSPEED
+  try
+  {
+    CameraMetaDataLR *camerameta = new CameraMetaDataLR(filename);
+    if (_rawspeed_camerameta)
+    {
+      CameraMetaDataLR *d =
+          static_cast<CameraMetaDataLR *>(_rawspeed_camerameta);
+      delete d;
+    }
+    _rawspeed_camerameta = static_cast<void *>(camerameta);
+  }
+  catch (...)
+  {
+    // just return error code
+    return -1;
+  }
+#endif
+  return 0;
+}
+#ifdef USE_RAWSPEED
+void LibRaw::fix_after_rawspeed(int bl)
+{
+  if (load_raw == &LibRaw::lossy_dng_load_raw)
+    C.maximum = 0xffff;
+  else if (load_raw == &LibRaw::sony_load_raw)
+    C.maximum = 0x3ff0;
+}
+#else
+void LibRaw::fix_after_rawspeed(int) {}
+#endif
+
+int LibRaw::try_rawspeed()
+{
+#ifdef USE_RAWSPEED
+  int ret = LIBRAW_SUCCESS;
+
+  int rawspeed_ignore_errors = 0;
+  if (imgdata.idata.dng_version && imgdata.idata.colors == 3 &&
+      !strcasecmp(imgdata.idata.software,
+                  "Adobe Photoshop Lightroom 6.1.1 (Windows)"))
+    rawspeed_ignore_errors = 1;
+
+  // RawSpeed Supported,
+  INT64 spos = ID.input->tell();
+  void *_rawspeed_buffer = 0;
+  try
+  {
+    ID.input->seek(0, SEEK_SET);
+    INT64 _rawspeed_buffer_sz = ID.input->size() + 32;
+    _rawspeed_buffer = malloc(_rawspeed_buffer_sz);
+    if (!_rawspeed_buffer)
+      throw LIBRAW_EXCEPTION_ALLOC;
+    ID.input->read(_rawspeed_buffer, _rawspeed_buffer_sz, 1);
+    FileMap map((uchar8 *)_rawspeed_buffer, _rawspeed_buffer_sz);
+    RawParser t(&map);
+    RawDecoder *d = 0;
+    CameraMetaDataLR *meta =
+        static_cast<CameraMetaDataLR *>(_rawspeed_camerameta);
+    d = t.getDecoder();
+    if (!d)
+      throw "Unable to find decoder";
+    try
+    {
+      d->checkSupport(meta);
+    }
+    catch (const RawDecoderException &e)
+    {
+      imgdata.process_warnings |= LIBRAW_WARN_RAWSPEED_UNSUPPORTED;
+      throw e;
+    }
+    d->interpolateBadPixels = FALSE;
+    d->applyStage1DngOpcodes = FALSE;
+    _rawspeed_decoder = static_cast<void *>(d);
+    d->decodeRaw();
+    d->decodeMetaData(meta);
+    RawImage r = d->mRaw;
+    if (r->errors.size() > 0 && !rawspeed_ignore_errors)
+    {
+      delete d;
+      _rawspeed_decoder = 0;
+      throw 1;
+    }
+    if (r->isCFA)
+    {
+      imgdata.rawdata.raw_image = (ushort *)r->getDataUncropped(0, 0);
+    }
+    else if (r->getCpp() == 4)
+    {
+      imgdata.rawdata.color4_image = (ushort(*)[4])r->getDataUncropped(0, 0);
+      if (r->whitePoint > 0 && r->whitePoint < 65536)
+        C.maximum = r->whitePoint;
+    }
+    else if (r->getCpp() == 3)
+    {
+      imgdata.rawdata.color3_image = (ushort(*)[3])r->getDataUncropped(0, 0);
+      if (r->whitePoint > 0 && r->whitePoint < 65536)
+        C.maximum = r->whitePoint;
+    }
+    else
+    {
+      delete d;
+      _rawspeed_decoder = 0;
+      ret = LIBRAW_UNSPECIFIED_ERROR;
+    }
+    if (_rawspeed_decoder)
+    {
+      // set sizes
+      iPoint2D rsdim = r->getUncroppedDim();
+      S.raw_pitch = r->pitch;
+      S.raw_width = rsdim.x;
+      S.raw_height = rsdim.y;
+      // C.maximum = r->whitePoint;
+      fix_after_rawspeed(r->blackLevel);
+    }
+    free(_rawspeed_buffer);
+    _rawspeed_buffer = 0;
+    imgdata.process_warnings |= LIBRAW_WARN_RAWSPEED_PROCESSED;
+  }
+  catch (const RawDecoderException &RDE)
+  {
+    imgdata.process_warnings |= LIBRAW_WARN_RAWSPEED_PROBLEM;
+    if (_rawspeed_buffer)
+    {
+      free(_rawspeed_buffer);
+      _rawspeed_buffer = 0;
+    }
+    const char *p = RDE.what();
+    if (!strncmp(RDE.what(), "Decoder canceled", strlen("Decoder canceled")))
+      throw LIBRAW_EXCEPTION_CANCELLED_BY_CALLBACK;
+    ret = LIBRAW_UNSPECIFIED_ERROR;
+  }
+  catch (...)
+  {
+    // We may get here due to cancellation flag
+    imgdata.process_warnings |= LIBRAW_WARN_RAWSPEED_PROBLEM;
+    if (_rawspeed_buffer)
+    {
+      free(_rawspeed_buffer);
+      _rawspeed_buffer = 0;
+    }
+    ret = LIBRAW_UNSPECIFIED_ERROR;
+  }
+  ID.input->seek(spos, SEEK_SET);
+
+  return ret;
+#else
+  return LIBRAW_NOT_IMPLEMENTED;
+#endif
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/libraw_c_api.cpp libkdcraw/libkdcraw/libraw/src/libraw_c_api.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/libraw_c_api.cpp	2022-11-07 08:15:53.614821808 +0300
+++ libkdcraw/libkdcraw/libraw/src/libraw_c_api.cpp	2022-11-07 07:46:31.734795008 +0300
@@ -1,142 +1,430 @@
-/*
+/* -*- C++ -*-
  * File: libraw_c_api.cpp
- * Copyright 2008-2009 Alex Tutubalin <lexa@lexa.ru>
+ * Copyright 2008-2020 LibRaw LLC (info@libraw.org)
  * Created: Sat Mar  8 , 2008
  *
- * LibRaw C++ interface (implementation)
- */
-#include <errno.h>
-#include "libraw/libraw.h"
-
-#ifdef __cplusplus
-extern "C" 
-{
-#endif
-
-    libraw_data_t *libraw_init(unsigned int flags)
-    {
-        LibRaw *ret = new LibRaw(flags);
-        return &(ret->imgdata);
-    }
-
-    const char*   libraw_version() { return LibRaw::version();}
-    const char*   libraw_strprogress(enum LibRaw_progress p) { return LibRaw::strprogress(p);}
-    int     libraw_versionNumber() { return LibRaw::versionNumber();}
-    const char**  libraw_cameraList() { return LibRaw::cameraList();}
-    int   libraw_cameraCount() { return LibRaw::cameraCount(); }
-    const char* libraw_unpack_function_name(libraw_data_t* lr)
-    {
-        if(!lr) return "NULL parameter passed";
-        LibRaw *ip = (LibRaw*) lr->parent_class;
-        return ip->unpack_function_name();
-    }
-    int libraw_rotate_fuji_raw(libraw_data_t* lr)
-    {
-        if(!lr) return EINVAL;
-        LibRaw *ip = (LibRaw*) lr->parent_class;
-        return ip->rotate_fuji_raw();
-    }
-
-    int libraw_add_masked_borders_to_bitmap(libraw_data_t* lr)
-    {
-        if(!lr) return EINVAL;
-        LibRaw *ip = (LibRaw*) lr->parent_class;
-        return ip->add_masked_borders_to_bitmap();
-    }
-
-    int libraw_open_file(libraw_data_t* lr, const char *file)
-    {
-        if(!lr) return EINVAL;
-        LibRaw *ip = (LibRaw*) lr->parent_class;
-        return ip->open_file(file);
-    }
-    int libraw_open_buffer(libraw_data_t* lr, void *buffer, size_t size)
-    {
-        if(!lr) return EINVAL;
-        LibRaw *ip = (LibRaw*) lr->parent_class;
-        return ip->open_buffer(buffer,size);
-    }
-    int libraw_unpack(libraw_data_t* lr)
-    {
-        if(!lr) return EINVAL;
-        LibRaw *ip = (LibRaw*) lr->parent_class;
-        return ip->unpack();
-    }
-    int libraw_unpack_thumb(libraw_data_t* lr)
-    {
-        if(!lr) return EINVAL;
-        LibRaw *ip = (LibRaw*) lr->parent_class;
-        return ip->unpack_thumb();
-    }
-    void libraw_recycle(libraw_data_t* lr)
-    {
-        if(!lr) return;
-        LibRaw *ip = (LibRaw*) lr->parent_class;
-        ip->recycle();
-    }
-    void libraw_close(libraw_data_t* lr)
-    {
-        if(!lr) return;
-        LibRaw *ip = (LibRaw*) lr->parent_class;
-        delete ip;
-    }
+ * LibRaw C interface
 
-    void  libraw_set_memerror_handler(libraw_data_t* lr, memory_callback cb,void *data)
-    {
-        if(!lr) return;
-        LibRaw *ip = (LibRaw*) lr->parent_class;
-        ip->set_memerror_handler(cb,data);
 
-    }
-    void libraw_set_dataerror_handler(libraw_data_t* lr,data_callback func,void *data)
-    {
-        if(!lr) return;
-        LibRaw *ip = (LibRaw*) lr->parent_class;
-        ip->set_dataerror_handler(func,data);
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of two licenses as you choose:
 
-    }
-    void  libraw_set_progress_handler(libraw_data_t* lr, progress_callback cb,void *data)
-    {
-        if(!lr) return;
-        LibRaw *ip = (LibRaw*) lr->parent_class;
-        ip->set_progress_handler(cb,data);
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
 
-    }
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
 
-    // DCRAW
-    int  libraw_adjust_sizes_info_only(libraw_data_t* lr)
-    {
-        if(!lr) return EINVAL;
-        LibRaw *ip = (LibRaw*) lr->parent_class;
-        return ip->adjust_sizes_info_only();
-    }
+ */
 
-    int  libraw_dcraw_document_mode_processing(libraw_data_t* lr)
-    {
-        if(!lr) return EINVAL;
-        LibRaw *ip = (LibRaw*) lr->parent_class;
-        return ip->dcraw_document_mode_processing();
+#include <math.h>
+#include <errno.h>
+#include "libraw/libraw.h"
 
-    }
-    int  libraw_dcraw_ppm_tiff_writer(libraw_data_t* lr,const char *filename)
-    {
-        if(!lr) return EINVAL;
-        LibRaw *ip = (LibRaw*) lr->parent_class;
-        return ip->dcraw_ppm_tiff_writer(filename);
-    }
-    int  libraw_dcraw_thumb_writer(libraw_data_t* lr,const char *fname)
-    {
-        if(!lr) return EINVAL;
-        LibRaw *ip = (LibRaw*) lr->parent_class;
-        return ip->dcraw_thumb_writer(fname);
+#ifdef __cplusplus
+#include <new>
+extern "C"
+{
+#endif
 
-    }
-    int libraw_dcraw_process(libraw_data_t* lr)
-    {
-        if(!lr) return EINVAL;
-        LibRaw *ip = (LibRaw*) lr->parent_class;
-        return ip->dcraw_process();
-    }
+  libraw_data_t *libraw_init(unsigned int flags)
+  {
+    LibRaw *ret;
+    try
+    {
+      ret = new LibRaw(flags);
+    }
+    catch (const std::bad_alloc& )
+    {
+      return NULL;
+    }
+    return &(ret->imgdata);
+  }
+
+  unsigned libraw_capabilities() { return LibRaw::capabilities(); }
+  const char *libraw_version() { return LibRaw::version(); }
+  const char *libraw_strprogress(enum LibRaw_progress p)
+  {
+    return LibRaw::strprogress(p);
+  }
+  int libraw_versionNumber() { return LibRaw::versionNumber(); }
+  const char **libraw_cameraList() { return LibRaw::cameraList(); }
+  int libraw_cameraCount() { return LibRaw::cameraCount(); }
+  const char *libraw_unpack_function_name(libraw_data_t *lr)
+  {
+    if (!lr)
+      return "NULL parameter passed";
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    return ip->unpack_function_name();
+  }
+
+  void libraw_subtract_black(libraw_data_t *lr)
+  {
+    if (!lr)
+      return;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    ip->subtract_black();
+  }
+
+  int libraw_open_file(libraw_data_t *lr, const char *file)
+  {
+    if (!lr)
+      return EINVAL;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    return ip->open_file(file);
+  }
+
+  libraw_iparams_t *libraw_get_iparams(libraw_data_t *lr)
+  {
+    if (!lr)
+      return NULL;
+    return &(lr->idata);
+  }
+
+  libraw_lensinfo_t *libraw_get_lensinfo(libraw_data_t *lr)
+  {
+    if (!lr)
+      return NULL;
+    return &(lr->lens);
+  }
+
+  libraw_imgother_t *libraw_get_imgother(libraw_data_t *lr)
+  {
+    if (!lr)
+      return NULL;
+    return &(lr->other);
+  }
+
+  int libraw_open_file_ex(libraw_data_t *lr, const char *file, INT64 sz)
+  {
+    if (!lr)
+      return EINVAL;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    return ip->open_file(file, sz);
+  }
+#if defined(_WIN32) && !defined(__MINGW32__) && defined(_MSC_VER) &&           \
+    (_MSC_VER > 1310)
+  int libraw_open_wfile(libraw_data_t *lr, const wchar_t *file)
+  {
+    if (!lr)
+      return EINVAL;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    return ip->open_file(file);
+  }
+
+  int libraw_open_wfile_ex(libraw_data_t *lr, const wchar_t *file, INT64 sz)
+  {
+    if (!lr)
+      return EINVAL;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    return ip->open_file(file, sz);
+  }
+#endif
+  int libraw_open_buffer(libraw_data_t *lr, void *buffer, size_t size)
+  {
+    if (!lr)
+      return EINVAL;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    return ip->open_buffer(buffer, size);
+  }
+  int libraw_unpack(libraw_data_t *lr)
+  {
+    if (!lr)
+      return EINVAL;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    return ip->unpack();
+  }
+  int libraw_unpack_thumb(libraw_data_t *lr)
+  {
+    if (!lr)
+      return EINVAL;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    return ip->unpack_thumb();
+  }
+  void libraw_recycle_datastream(libraw_data_t *lr)
+  {
+    if (!lr)
+      return;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    ip->recycle_datastream();
+  }
+  void libraw_recycle(libraw_data_t *lr)
+  {
+    if (!lr)
+      return;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    ip->recycle();
+  }
+  void libraw_close(libraw_data_t *lr)
+  {
+    if (!lr)
+      return;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    delete ip;
+  }
+
+  void libraw_set_exifparser_handler(libraw_data_t *lr, exif_parser_callback cb,
+                                     void *data)
+  {
+    if (!lr)
+      return;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    ip->set_exifparser_handler(cb, data);
+  }
+
+  void libraw_set_memerror_handler(libraw_data_t *lr, memory_callback cb,
+                                   void *data)
+  {
+    if (!lr)
+      return;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    ip->set_memerror_handler(cb, data);
+  }
+  void libraw_set_dataerror_handler(libraw_data_t *lr, data_callback func,
+                                    void *data)
+  {
+    if (!lr)
+      return;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    ip->set_dataerror_handler(func, data);
+  }
+  void libraw_set_progress_handler(libraw_data_t *lr, progress_callback cb,
+                                   void *data)
+  {
+    if (!lr)
+      return;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    ip->set_progress_handler(cb, data);
+  }
+
+  // DCRAW
+  int libraw_adjust_sizes_info_only(libraw_data_t *lr)
+  {
+    if (!lr)
+      return EINVAL;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    return ip->adjust_sizes_info_only();
+  }
+  int libraw_dcraw_ppm_tiff_writer(libraw_data_t *lr, const char *filename)
+  {
+    if (!lr)
+      return EINVAL;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    return ip->dcraw_ppm_tiff_writer(filename);
+  }
+  int libraw_dcraw_thumb_writer(libraw_data_t *lr, const char *fname)
+  {
+    if (!lr)
+      return EINVAL;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    return ip->dcraw_thumb_writer(fname);
+  }
+  int libraw_dcraw_process(libraw_data_t *lr)
+  {
+    if (!lr)
+      return EINVAL;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    return ip->dcraw_process();
+  }
+  libraw_processed_image_t *libraw_dcraw_make_mem_image(libraw_data_t *lr,
+                                                        int *errc)
+  {
+    if (!lr)
+    {
+      if (errc)
+        *errc = EINVAL;
+      return NULL;
+    }
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    return ip->dcraw_make_mem_image(errc);
+  }
+  libraw_processed_image_t *libraw_dcraw_make_mem_thumb(libraw_data_t *lr,
+                                                        int *errc)
+  {
+    if (!lr)
+    {
+      if (errc)
+        *errc = EINVAL;
+      return NULL;
+    }
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    return ip->dcraw_make_mem_thumb(errc);
+  }
+
+  void libraw_dcraw_clear_mem(libraw_processed_image_t *p)
+  {
+    LibRaw::dcraw_clear_mem(p);
+  }
+
+  int libraw_raw2image(libraw_data_t *lr)
+  {
+    if (!lr)
+      return EINVAL;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    return ip->raw2image();
+  }
+  void libraw_free_image(libraw_data_t *lr)
+  {
+    if (!lr)
+      return;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    ip->free_image();
+  }
+  int libraw_get_decoder_info(libraw_data_t *lr, libraw_decoder_info_t *d)
+  {
+    if (!lr || !d)
+      return EINVAL;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    return ip->get_decoder_info(d);
+  }
+  int libraw_COLOR(libraw_data_t *lr, int row, int col)
+  {
+    if (!lr)
+      return EINVAL;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    return ip->COLOR(row, col);
+  }
+
+  /* getters/setters used by 3DLut Creator */
+  DllDef void libraw_set_demosaic(libraw_data_t *lr, int value)
+  {
+    if (!lr)
+      return;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    ip->imgdata.params.user_qual = value;
+  }
+
+  DllDef void libraw_set_output_color(libraw_data_t *lr, int value)
+  {
+    if (!lr)
+      return;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    ip->imgdata.params.output_color = value;
+  }
+
+  DllDef void libraw_set_output_bps(libraw_data_t *lr, int value)
+  {
+    if (!lr)
+      return;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    ip->imgdata.params.output_bps = value;
+  }
+
+	DllDef void libraw_set_output_tif(libraw_data_t *lr, int value)
+  {
+    if (!lr)
+      return;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    ip->imgdata.params.output_tiff = value;
+  }
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define LIM(x, min, max) MAX(min, MIN(x, max))
+
+  DllDef void libraw_set_user_mul(libraw_data_t *lr, int index, float val)
+  {
+    if (!lr)
+      return;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    ip->imgdata.params.user_mul[LIM(index, 0, 3)] = val;
+  }
+
+  DllDef void libraw_set_gamma(libraw_data_t *lr, int index, float value)
+  {
+    if (!lr)
+      return;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    ip->imgdata.params.gamm[LIM(index, 0, 5)] = value;
+  }
+
+  DllDef void libraw_set_no_auto_bright(libraw_data_t *lr, int value)
+  {
+    if (!lr)
+      return;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    ip->imgdata.params.no_auto_bright = value;
+  }
+
+  DllDef void libraw_set_bright(libraw_data_t *lr, float value)
+  {
+    if (!lr)
+      return;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    ip->imgdata.params.bright = value;
+  }
+
+  DllDef void libraw_set_highlight(libraw_data_t *lr, int value)
+  {
+    if (!lr)
+      return;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    ip->imgdata.params.highlight = value;
+  }
+
+  DllDef void libraw_set_fbdd_noiserd(libraw_data_t *lr, int value)
+  {
+    if (!lr)
+      return;
+    LibRaw *ip = (LibRaw *)lr->parent_class;
+    ip->imgdata.params.fbdd_noiserd = value;
+  }
+
+  DllDef int libraw_get_raw_height(libraw_data_t *lr)
+  {
+    if (!lr)
+      return EINVAL;
+    return lr->sizes.raw_height;
+  }
+
+  DllDef int libraw_get_raw_width(libraw_data_t *lr)
+  {
+    if (!lr)
+      return EINVAL;
+    return lr->sizes.raw_width;
+  }
+
+  DllDef int libraw_get_iheight(libraw_data_t *lr)
+  {
+    if (!lr)
+      return EINVAL;
+    return lr->sizes.iheight;
+  }
+
+  DllDef int libraw_get_iwidth(libraw_data_t *lr)
+  {
+    if (!lr)
+      return EINVAL;
+    return lr->sizes.iwidth;
+  }
+
+  DllDef float libraw_get_cam_mul(libraw_data_t *lr, int index)
+  {
+    if (!lr)
+      return EINVAL;
+    return lr->color.cam_mul[LIM(index, 0, 3)];
+  }
+
+  DllDef float libraw_get_pre_mul(libraw_data_t *lr, int index)
+  {
+    if (!lr)
+      return EINVAL;
+    return lr->color.pre_mul[LIM(index, 0, 3)];
+  }
+
+  DllDef float libraw_get_rgb_cam(libraw_data_t *lr, int index1, int index2)
+  {
+    if (!lr)
+      return EINVAL;
+    return lr->color.rgb_cam[LIM(index1, 0, 2)][LIM(index2, 0, 3)];
+  }
+
+  DllDef int libraw_get_color_maximum(libraw_data_t *lr)
+  {
+    if (!lr)
+      return EINVAL;
+    return lr->color.maximum;
+  }
 
 #ifdef __cplusplus
 }
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/libraw_cxx.cpp libkdcraw/libkdcraw/libraw/src/libraw_cxx.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/libraw_cxx.cpp	2022-11-07 08:15:53.614821808 +0300
+++ libkdcraw/libkdcraw/libraw/src/libraw_cxx.cpp	2022-11-07 07:46:31.734795008 +0300
@@ -1,1940 +1,55 @@
-/*
+/* -*- C++ -*-
  * File: libraw_cxx.cpp
- * Copyright 2008-2009 Alex Tutubalin <lexa@lexa.ru>
+ * Copyright 2008-2020 LibRaw LLC (info@libraw.org)
  * Created: Sat Mar  8 , 2008
  *
- * LibRaw C++ interface (implementation)
- */
-
-#include <errno.h>
-#include <float.h>
-#include <math.h>
-#ifndef WIN32
-#include <netinet/in.h>
-#else
-#include <winsock2.h>
-#endif
-#define LIBRAW_LIBRARY_BUILD
-#include "libraw/libraw.h"
-
-#ifdef __cplusplus
-extern "C" 
-{
-#endif
-    void default_memory_callback(void *,const char *file,const char *where)
-    {
-        fprintf (stderr,"%s: Out of memory in %s\n", file?file:"unknown file", where);
-    }
-
-    void default_data_callback(void*,const char *file, const int offset)
-    {
-        if(offset < 0)
-            fprintf (stderr,"%s: Unexpected end of file\n", file?file:"unknown file");
-        else
-            fprintf (stderr,"%s: data corrupted at %d\n",file?file:"unknown file",offset); 
-    }
-    const char *libraw_strerror(int e)
-    {
-        enum LibRaw_errors errorcode = (LibRaw_errors)e;
-        switch(errorcode)
-            {
-            case        LIBRAW_SUCCESS:
-                return "No error";
-            case        LIBRAW_UNSPECIFIED_ERROR:
-                return "Unspecified error";
-            case        LIBRAW_FILE_UNSUPPORTED:
-                return "Unsupported file format or not RAW file";
-            case        LIBRAW_REQUEST_FOR_NONEXISTENT_IMAGE:
-                return "Request for nonexisting image number";
-            case        LIBRAW_OUT_OF_ORDER_CALL:
-                return "Out of order call of libraw function";
-            case    LIBRAW_NO_THUMBNAIL:
-                return "No thumbnail in file";
-            case    LIBRAW_UNSUPPORTED_THUMBNAIL:
-                return "Unsupported thumbnail format";
-            case LIBRAW_CANNOT_ADDMASK:
-                return "Cannot add masked pixels to resized image";
-            case    LIBRAW_UNSUFFICIENT_MEMORY:
-                return "Unsufficient memory";
-            case    LIBRAW_DATA_ERROR:
-                return "Corrupted data or unexpected EOF";
-            case    LIBRAW_IO_ERROR:
-                return "Input/output error";
-            case LIBRAW_CANCELLED_BY_CALLBACK:
-                return "Cancelled by user callback";
-            default:
-                return "Unknown error code";
-        }
-    }
-
-#ifdef __cplusplus
-}
-#endif
-
-
-const double LibRaw_constants::xyz_rgb[3][3] = 
-{
-    { 0.412453, 0.357580, 0.180423 },
-    { 0.212671, 0.715160, 0.072169 },
-    { 0.019334, 0.119193, 0.950227 } 
-};
-
-const float LibRaw_constants::d65_white[3] =  { 0.950456, 1, 1.088754 };
-
-#define P1 imgdata.idata
-#define S imgdata.sizes
-#define O imgdata.params
-#define C imgdata.color
-#define M imgdata.masked_pixels
-#define T imgdata.thumbnail
-#define IO libraw_internal_data.internal_output_params
-#define ID libraw_internal_data.internal_data
-
-#define EXCEPTION_HANDLER(e) do{                        \
-        fprintf(stderr,"Exception %d caught\n",e);      \
-        switch(e)                                       \
-            {                                           \
-            case LIBRAW_EXCEPTION_ALLOC:                \
-                recycle();                              \
-                return LIBRAW_UNSUFFICIENT_MEMORY;      \
-            case LIBRAW_EXCEPTION_DECODE_RAW:           \
-            case LIBRAW_EXCEPTION_DECODE_JPEG:          \
-                recycle();                              \
-                return LIBRAW_DATA_ERROR;               \
-            case LIBRAW_EXCEPTION_IO_EOF:               \
-            case LIBRAW_EXCEPTION_IO_CORRUPT:           \
-                recycle();                              \
-                return LIBRAW_IO_ERROR;                 \
-            case LIBRAW_EXCEPTION_CANCELLED_BY_CALLBACK:\
-                recycle();                              \
-                return LIBRAW_CANCELLED_BY_CALLBACK;    \
-            default:                                    \
-                return LIBRAW_UNSPECIFIED_ERROR;        \
-            } \
-    }while(0)
-
-void LibRaw::derror()
-{
-    if (!libraw_internal_data.unpacker_data.data_error && libraw_internal_data.internal_data.input) 
-        {
-            if (libraw_internal_data.internal_data.input->eof())
-                {
-                    if(callbacks.data_cb)(*callbacks.data_cb)(callbacks.datacb_data,
-                                                              libraw_internal_data.internal_data.input->fname(),-1);
-                    throw LIBRAW_EXCEPTION_IO_EOF;
-                }
-            else
-                {
-                    if(callbacks.data_cb)(*callbacks.data_cb)(callbacks.datacb_data,
-                                                              libraw_internal_data.internal_data.input->fname(),
-                                                              libraw_internal_data.internal_data.input->tell());
-                    throw LIBRAW_EXCEPTION_IO_CORRUPT;
-                }
-        }
-    libraw_internal_data.unpacker_data.data_error = 1;
-}
-LibRaw:: LibRaw(unsigned int flags)
-{
-    double aber[4] = {1,1,1,1};
-    double gamm[5] = { 0.45,4.5,0,0,0 };
-    unsigned greybox[4] =  { 0, 0, UINT_MAX, UINT_MAX };
-#ifdef DCRAW_VERBOSE
-    verbose = 1;
-#else
-    verbose = 0;
-#endif
-    bzero(&imgdata,sizeof(imgdata));
-    bzero(&libraw_internal_data,sizeof(libraw_internal_data));
-    bzero(&callbacks,sizeof(callbacks));
-    callbacks.mem_cb = (flags & LIBRAW_OPIONS_NO_MEMERR_CALLBACK) ? NULL:  &default_memory_callback;
-    callbacks.data_cb = (flags & LIBRAW_OPIONS_NO_DATAERR_CALLBACK)? NULL : &default_data_callback;
-    memmove(&imgdata.params.aber,&aber,sizeof(aber));
-    memmove(&imgdata.params.gamm,&gamm,sizeof(gamm));
-    memmove(&imgdata.params.greybox,&greybox,sizeof(greybox));
-    
-    imgdata.params.bright=1;
-    imgdata.params.use_camera_matrix=-1;
-    imgdata.params.user_flip=-1;
-    imgdata.params.user_black=-1;
-    imgdata.params.user_sat=-1;
-    imgdata.params.user_qual=-1;
-    imgdata.params.output_color=1;
-    imgdata.params.output_bps=8;
-    imgdata.params.use_fuji_rotate=1;
-    imgdata.params.auto_bright_thr = 0.01;
-    imgdata.parent_class = this;
-    imgdata.progress_flags = 0;
-    tls = new LibRaw_TLS;
-    tls->init();
-}
-
-
-void* LibRaw:: malloc(size_t t)
-{
-    void *p = memmgr.malloc(t);
-    return p;
-}
-void* LibRaw::       calloc(size_t n,size_t t)
-{
-    void *p = memmgr.calloc(n,t);
-    return p;
-}
-void  LibRaw::      free(void *p)
-{
-    memmgr.free(p);
-}
-
-
-int LibRaw:: fc (int row, int col)
-{
-    static const char filter[16][16] =
-        { { 2,1,1,3,2,3,2,0,3,2,3,0,1,2,1,0 },
-          { 0,3,0,2,0,1,3,1,0,1,1,2,0,3,3,2 },
-          { 2,3,3,2,3,1,1,3,3,1,2,1,2,0,0,3 },
-          { 0,1,0,1,0,2,0,2,2,0,3,0,1,3,2,1 },
-          { 3,1,1,2,0,1,0,2,1,3,1,3,0,1,3,0 },
-          { 2,0,0,3,3,2,3,1,2,0,2,0,3,2,2,1 },
-          { 2,3,3,1,2,1,2,1,2,1,1,2,3,0,0,1 },
-          { 1,0,0,2,3,0,0,3,0,3,0,3,2,1,2,3 },
-          { 2,3,3,1,1,2,1,0,3,2,3,0,2,3,1,3 },
-          { 1,0,2,0,3,0,3,2,0,1,1,2,0,1,0,2 },
-          { 0,1,1,3,3,2,2,1,1,3,3,0,2,1,3,2 },
-          { 2,3,2,0,0,1,3,0,2,0,1,2,3,0,1,0 },
-          { 1,3,1,2,3,2,3,2,0,2,0,1,1,0,3,0 },
-          { 0,2,0,3,1,0,0,1,1,3,3,2,3,2,2,1 },
-          { 2,1,3,2,3,1,2,1,0,3,0,2,0,2,0,2 },
-          { 0,3,1,0,0,2,0,3,2,1,3,1,1,3,1,3 } };
-    
-    if (imgdata.idata.filters != 1) return FC(row,col);
-    return filter[(row+imgdata.sizes.top_margin) & 15][(col+imgdata.sizes.left_margin) & 15];
-}
-
-void LibRaw:: recycle() 
-{
-    if(libraw_internal_data.internal_data.input && libraw_internal_data.internal_data.input_internal) 
-        { 
-            delete libraw_internal_data.internal_data.input; 
-            libraw_internal_data.internal_data.input = NULL;
-        }
-    libraw_internal_data.internal_data.input_internal = 0;
-#define FREE(a) do { if(a) { free(a); a = NULL;} }while(0)
-            
-    FREE(imgdata.image); 
-    FREE(imgdata.thumbnail.thumb);
-    FREE(libraw_internal_data.internal_data.meta_data);
-    FREE(libraw_internal_data.output_data.histogram);
-    FREE(libraw_internal_data.output_data.oprof);
-    FREE(imgdata.color.profile);
-    FREE(imgdata.masked_pixels.buffer);
-    FREE(imgdata.masked_pixels.ph1_black);
-#undef FREE
-#define ZERO(a) bzero(&a,sizeof(a))
-    ZERO(imgdata.masked_pixels);
-    ZERO(imgdata.sizes);
-    ZERO(libraw_internal_data.internal_output_params);
-#undef ZERO
-    memmgr.cleanup();
-    imgdata.thumbnail.tformat = LIBRAW_THUMBNAIL_UNKNOWN;
-    imgdata.progress_flags = 0;
-    
-    tls->init();
-}
-
-const char * LibRaw::unpack_function_name()
-{
-    if(!load_raw) return "Function not set";
-
-    // sorted names order
-    if (load_raw == &LibRaw::adobe_dng_load_raw_lj)     return "adobe_dng_load_raw_lj()";
-    if (load_raw == &LibRaw::adobe_dng_load_raw_nc)     return "adobe_dng_load_raw_nc()";
-    if (load_raw == &LibRaw::canon_600_load_raw)        return "canon_600_load_raw()";
-
-    if (load_raw == &LibRaw::canon_a5_load_raw)         return "canon_a5_load_raw()";
-    if (load_raw == &LibRaw::canon_compressed_load_raw) return "canon_compressed_load_raw()";
-    if (load_raw == &LibRaw::canon_sraw_load_raw)       return "canon_sraw_load_raw()";
-
-    if (load_raw == &LibRaw::casio_qv5700_load_raw )    return "casio_qv5700_load_raw()";
-    if (load_raw == &LibRaw::eight_bit_load_raw )       return "eight_bit_load_raw()";
-    if (load_raw == &LibRaw::foveon_load_raw )          return "foveon_load_raw()";
-    if (load_raw == &LibRaw::fuji_load_raw )            return "fuji_load_raw()";
-    // 10
-    if (load_raw == &LibRaw::hasselblad_load_raw )      return "hasselblad_load_raw()";
-    if (load_raw == &LibRaw::imacon_full_load_raw )     return "imacon_full_load_raw()";
-    if (load_raw == &LibRaw::kodak_262_load_raw )       return "kodak_262_load_raw()";
-
-    if (load_raw == &LibRaw::kodak_65000_load_raw )     return "kodak_65000_load_raw()";
-    if (load_raw == &LibRaw::kodak_dc120_load_raw )     return "kodak_dc120_load_raw()";
-    if (load_raw == &LibRaw::kodak_jpeg_load_raw )      return "kodak_jpeg_load_raw()";
-
-    if (load_raw == &LibRaw::kodak_radc_load_raw )      return "kodak_radc_load_raw()";
-    if (load_raw == &LibRaw::kodak_rgb_load_raw )       return "kodak_rgb_load_raw()";
-    if (load_raw == &LibRaw::kodak_yrgb_load_raw )      return "kodak_yrgb_load_raw()";
-    if (load_raw == &LibRaw::kodak_ycbcr_load_raw )     return "kodak_ycbcr_load_raw()";
-    // 20
-    if (load_raw == &LibRaw::leaf_hdr_load_raw )        return "leaf_hdr_load_raw()";
-    if (load_raw == &LibRaw::lossless_jpeg_load_raw)    return "lossless_jpeg_load_raw()";
-    if (load_raw == &LibRaw::minolta_rd175_load_raw )   return "minolta_rd175_load_raw()";
-
-    if (load_raw == &LibRaw::nikon_compressed_load_raw) return "nikon_compressed_load_raw()";
-    if (load_raw == &LibRaw::nikon_e900_load_raw )      return "nikon_e900_load_raw()";
-    if (load_raw == &LibRaw::nokia_load_raw )           return "nokia_load_raw()";
-
-    if (load_raw == &LibRaw::olympus_e300_load_raw )    return "olympus_e300_load_raw()";
-    if (load_raw == &LibRaw::olympus_e410_load_raw )    return "olympus_e410_load_raw()";
-    if (load_raw == &LibRaw::packed_12_load_raw )       return "packed_12_load_raw()";
-    if (load_raw == &LibRaw::panasonic_load_raw )       return "panasonic_load_raw()";
-    // 30
-    if (load_raw == &LibRaw::pentax_k10_load_raw )      return "pentax_k10_load_raw()";
-    if (load_raw == &LibRaw::phase_one_load_raw )       return "phase_one_load_raw()";
-    if (load_raw == &LibRaw::phase_one_load_raw_c )     return "phase_one_load_raw_c()";
-
-    if (load_raw == &LibRaw::quicktake_100_load_raw )   return "quicktake_100_load_raw()";
-    if (load_raw == &LibRaw::rollei_load_raw )          return "rollei_load_raw()";
-    if (load_raw == &LibRaw::sinar_4shot_load_raw )     return "sinar_4shot_load_raw()";
-
-    if (load_raw == &LibRaw::smal_v6_load_raw )         return "smal_v6_load_raw()";
-    if (load_raw == &LibRaw::smal_v9_load_raw )         return "smal_v9_load_raw()";
-    if (load_raw == &LibRaw::sony_load_raw )            return "sony_load_raw()";
-    if (load_raw == &LibRaw::sony_arw_load_raw )        return "sony_arw_load_raw()";
-    // 40
-    if (load_raw == &LibRaw::sony_arw2_load_raw )       return "sony_arw2_load_raw()";
-    if (load_raw == &LibRaw::unpacked_load_raw )        return "unpacked_load_raw()";
-    // 42 total
-        
-    return "Unknown unpack function";
-}
-
-
-void LibRaw:: merror (void *ptr, const char *where)
-{
-    if (ptr) return;
-    if(callbacks.mem_cb)(*callbacks.mem_cb)(callbacks.memcb_data,
-                                            libraw_internal_data.internal_data.input
-                                            ?libraw_internal_data.internal_data.input->fname()
-                                            :NULL,
-                                            where);
-    throw LIBRAW_EXCEPTION_ALLOC;
-}
-
-ushort * LibRaw::get_masked_pointer(int row, int col) 
-{ 
-    if(row<0 || col < 0) return NULL;
-    if(!M.buffer) return NULL; 
-    if(row < S.top_margin)
-        {
-            // top band
-            if(col < S.left_margin)
-                {
-                    return &(M.tl[row*S.left_margin+col]);
-                }
-            else if (col < S.left_margin + S.width)
-                {
-                    int icol = col - S.left_margin;
-                    return &(M.top[row*S.width+icol]);
-                }
-            else if (col < S.raw_width)
-                {
-                    int icol = col - S.left_margin - S.width;
-                    return &(M.tr[row*S.right_margin+icol]);
-                }
-            else
-                return NULL; // out of bounds
-        }
-    else if (row < S.top_margin + S.height)
-        {
-            //normal image height
-            int irow = row - S.top_margin;
-            if(col < S.left_margin)
-                {
-                    return &M.left[irow*S.left_margin + col];
-                }
-            else if (col < S.left_margin + S.width)
-                {
-                    // central image
-                    return NULL;
-                }
-            else if (col < S.raw_width)
-                {
-                    int icol = col - S.left_margin - S.width;
-                    return &M.right[irow*S.right_margin+icol];
-                }
-            else
-                return NULL; // out of bounds
-        }
-    else if (row < S.raw_height)
-        {
-            int irow = row - S.top_margin - S.height;
-            // bottom band
-            if(col < S.left_margin)
-                {
-                    return &M.bl[irow*S.left_margin+col];
-                }
-            else if (col < S.left_margin + S.width)
-                {
-                    int icol = col - S.left_margin;
-                    return &M.bottom[irow*S.width + icol];
-                }
-            else if (col < S.raw_width)
-                {
-                    int icol = col - S.left_margin - S.width;
-                    return &M.br[irow*S.right_margin + icol];
-                }
-            else
-                return NULL; // out of bounds
-        }
-    else
-        {
-            // out of bounds
-            return NULL;
-        }
-    // fallback
-    return NULL;
-}
-
-void LibRaw:: init_masked_ptrs()
-{
-    if(!M.buffer) return;
-    
-    // top band
-    M.tl = M.buffer;
-    M.top = M.tl +(S.top_margin*S.left_margin);
-    M.tr =  M.top + (S.top_margin*S.width);
-    
-    // left-right
-    M.left = M.tr + (S.top_margin * S.right_margin);
-    M.right = M.left + (S.left_margin * S.height);
-
-    // bottom band
-    M.bl = M.right + (S.right_margin * S.height);
-    M.bottom = M.bl + (S.left_margin * S.bottom_margin);
-    M.br = M.bottom + (S.width * S.bottom_margin);
-
-}
-
-int LibRaw::add_masked_borders_to_bitmap()
-{
-    CHECK_ORDER_HIGH(LIBRAW_PROGRESS_PRE_INTERPOLATE);
-    CHECK_ORDER_LOW(LIBRAW_PROGRESS_LOAD_RAW);
-
-    if(S.width != S.iwidth || S.height!=S.iheight)
-        return LIBRAW_CANNOT_ADDMASK;
-
-    if(P1.is_foveon || !P1.filters)
-        return LIBRAW_CANNOT_ADDMASK;
-        
-    if(!imgdata.image)
-        return LIBRAW_OUT_OF_ORDER_CALL;
-
-    if(S.raw_width < S.width || S.raw_height < S.height)
-        return LIBRAW_SUCCESS; // nothing to do or already called
-
-    if(S.width == S.raw_width && S.height == S.raw_height)
-        return LIBRAW_SUCCESS; // nothing to do or already called
-
-    ushort (*newimage)[4];
-
-    newimage = (ushort (*)[4]) calloc (S.raw_height*S.raw_width, sizeof (*newimage));
-    merror (newimage, "add_masked_borders_to_bitmap()");
-
-    int r,c;
-    // top rows
-    for (r=0; r<S.top_margin;r++)
-        for(c=0;c<S.raw_width;c++)
-            {
-                ushort *p = get_masked_pointer(r,c);
-                if(p)
-                    newimage[r*S.raw_width+c][FC(r,c)] = *p;
-            }
-    // middle rows
-    for (r=S.top_margin; r<S.top_margin+S.height;r++)
-        {
-            int row = r-S.top_margin;
-            for(c=0;c<S.left_margin;c++)
-                {
-                    ushort *p = get_masked_pointer(r,c);
-                    if(p)
-                        newimage[r*S.raw_width+c][FC(r,c)] =  *p;
-                }
-            for(c=S.left_margin; c<S.left_margin+S.iwidth;c++)
-                {
-                    int col = c - S.left_margin;
-                    newimage[r*S.raw_width+c][FC(r,c)] = imgdata.image[row*S.iwidth+col][FC(row,col)];
-                }
-            for(c=S.left_margin+S.iwidth;c<S.raw_width;c++)
-                {
-                    ushort *p = get_masked_pointer(r,c);
-                    if(p)
-                        newimage[r*S.raw_width+c][FC(r,c)] =  *p;
-                }
-        }
-    // bottom rows
-    for (r=S.top_margin+S.height; r<S.raw_height;r++)
-        for(c=0;c<S.raw_width;c++)
-            {
-                ushort *p = get_masked_pointer(r,c);
-                if(p)
-                    newimage[r*S.raw_width+c][FC(r,c)] = *p;
-            }
-    free(imgdata.image);
-    imgdata.image=newimage;
-    S.iwidth = S.width = S.raw_width;
-    S.iheight = S.height = S.raw_height;
-    return LIBRAW_SUCCESS;
-}
-
-int LibRaw::open_file(const char *fname)
-{
-    // this stream will close on recycle()
-    LibRaw_file_datastream *stream = new LibRaw_file_datastream(fname);
-    if(!stream->valid())
-        {
-            delete stream;
-            return LIBRAW_IO_ERROR;
-        }
-    ID.input_internal = 0; // preserve from deletion on error
-    int ret = open_datastream(stream);
-    if (ret == LIBRAW_SUCCESS)
-        {
-            ID.input_internal =1 ; // flag to delete datastream on recycle
-        }
-    else
-        {
-            delete stream;
-            ID.input_internal = 0;
-        }
-    return ret;
-}
-
-int LibRaw::open_buffer(void *buffer, size_t size)
-{
-    // this stream will close on recycle()
-    if(!buffer  || buffer==(void*)-1)
-        return LIBRAW_IO_ERROR;
-
-    LibRaw_buffer_datastream *stream = new LibRaw_buffer_datastream(buffer,size);
-    if(!stream->valid())
-        {
-            delete stream;
-            return LIBRAW_IO_ERROR;
-        }
-    ID.input_internal = 0; // preserve from deletion on error
-    int ret = open_datastream(stream);
-    if (ret == LIBRAW_SUCCESS)
-        {
-            ID.input_internal =1 ; // flag to delete datastream on recycle
-        }
-    else
-        {
-            delete stream;
-            ID.input_internal = 0;
-        }
-    return ret;
-}
-
-
-int LibRaw::open_datastream(LibRaw_abstract_datastream *stream)
-{
-
-    if(!stream)
-        return ENOENT;
-    if(!stream->valid())
-        return LIBRAW_IO_ERROR;
-    recycle();
-
-    try {
-        ID.input = stream;
-        SET_PROC_FLAG(LIBRAW_PROGRESS_OPEN);
-
-        if (O.use_camera_matrix < 0)
-            O.use_camera_matrix = O.use_camera_wb;
-
-        identify();
-
-        if(IO.fuji_width)
-            {
-                IO.fwidth = S.width;
-                IO.fheight = S.height;
-                S.iwidth = S.width = IO.fuji_width << !libraw_internal_data.unpacker_data.fuji_layout;
-                S.iheight = S.height = S.raw_height;
-                S.raw_height += 2*S.top_margin;
-            }
-
-        int saved_raw_width = S.raw_width;
-        int saved_width = S.width;
-        // from packed_12_load_raw
-        if ((load_raw == &LibRaw:: packed_12_load_raw) && (S.raw_width * 2 >= S.width * 3))
-            {	
-                // raw_width is in bytes!
-                S.raw_width = S.raw_width * 2 / 3;	
-            }
-        else if (S.pixel_aspect < 0.95 || S.pixel_aspect > 1.05)
-            {
-                S.width*=S.pixel_aspect;
-            }
-
-        if(S.raw_width>S.width + S.left_margin)
-            S.right_margin = S.raw_width - S.width - S.left_margin;
-
-        if(S.raw_height > S.height + S.top_margin)
-            S.bottom_margin = S.raw_height - S.height - S.top_margin;
-
-        S.raw_width = saved_raw_width;
-        S.width = saved_width;
-
-        if(C.profile_length)
-            {
-                if(C.profile) free(C.profile);
-                C.profile = malloc(C.profile_length);
-                merror(C.profile,"LibRaw::open_file()");
-                ID.input->seek(ID.profile_offset,SEEK_SET);
-                ID.input->read(C.profile,C.profile_length,1);
-            }
-        
-        SET_PROC_FLAG(LIBRAW_PROGRESS_IDENTIFY);
-    }
-    catch ( LibRaw_exceptions err) {
-        EXCEPTION_HANDLER(err);
-    }
-
-    if(P1.raw_count < 1) 
-        return LIBRAW_FILE_UNSUPPORTED;
-
-    if (O.user_flip >= 0)
-        S.flip = O.user_flip;
-    
-    switch ((S.flip+3600) % 360) 
-        {
-        case 270:  S.flip = 5;  break;
-        case 180:  S.flip = 3;  break;
-        case  90:  S.flip = 6;  break;
-        }
-    
-    write_fun = &LibRaw::write_ppm_tiff;
-    
-    if (load_raw == &LibRaw::kodak_ycbcr_load_raw) 
-        {
-            S.height += S.height & 1;
-            S.width  += S.width  & 1;
-        }
-
-    IO.shrink = P1.filters && (O.half_size || O.threshold || O.aber[0] != 1 || O.aber[2] != 1);
-    S.iheight = (S.height + IO.shrink) >> IO.shrink;
-    S.iwidth  = (S.width  + IO.shrink) >> IO.shrink;
-    
-    SET_PROC_FLAG(LIBRAW_PROGRESS_SIZE_ADJUST);
-
-
-    return LIBRAW_SUCCESS;
-}
-
-int LibRaw::unpack(void)
-{
-    CHECK_ORDER_HIGH(LIBRAW_PROGRESS_LOAD_RAW);
-    CHECK_ORDER_LOW(LIBRAW_PROGRESS_IDENTIFY);
-    try {
-
-        RUN_CALLBACK(LIBRAW_PROGRESS_LOAD_RAW,0,2);
-        if (O.shot_select >= P1.raw_count)
-            return LIBRAW_REQUEST_FOR_NONEXISTENT_IMAGE;
-        
-        if(!load_raw)
-            return LIBRAW_UNSPECIFIED_ERROR;
-        
-        if (O.use_camera_matrix && C.cmatrix[0][0] > 0.25) 
-            {
-                memcpy (C.rgb_cam, C.cmatrix, sizeof (C.cmatrix));
-                IO.raw_color = 0;
-            }
-        // already allocated ?
-        if(imgdata.image) free(imgdata.image);
-        
-        imgdata.image = (ushort (*)[4]) calloc (S.iheight*S.iwidth, sizeof (*imgdata.image));
-        merror (imgdata.image, "unpack()");
-
-
-        if(S.top_margin || S.left_margin || S.right_margin || S.bottom_margin)
-            {
-                unsigned sz = S.raw_height*(S.left_margin+S.right_margin) 
-                    + S.width*(S.top_margin+S.bottom_margin);
-                imgdata.masked_pixels.buffer = (ushort*) calloc(sz, sizeof(ushort)); 
-                merror (imgdata.masked_pixels.buffer, "unpack()");
-                init_masked_ptrs();
-            }
-        if (libraw_internal_data.unpacker_data.meta_length) 
-            {
-                libraw_internal_data.internal_data.meta_data = 
-                    (char *) malloc (libraw_internal_data.unpacker_data.meta_length);
-                merror (libraw_internal_data.internal_data.meta_data, "LibRaw::unpack()");
-            }
-        ID.input->seek(libraw_internal_data.unpacker_data.data_offset, SEEK_SET);
-        // foveon_load_raw produces different data for document_mode, we'll
-        // deal with it in dcraw_document_mode_processing
-        int save_document_mode = O.document_mode;
-        O.document_mode = 0;
-
-        if(!own_filtering_supported() && (O.filtering_mode & LIBRAW_FILTERING_AUTOMATIC_BIT))
-            O.filtering_mode = LIBRAW_FILTERING_AUTOMATIC_BIT; // turn on black and zeroes filtering
-        
-        (this->*load_raw)();
-        
-        O.document_mode = save_document_mode;
-
-        if (O.filtering_mode & LIBRAW_FILTERING_AUTOMATIC_BIT)
-            O.filtering_mode = LIBRAW_FILTERING_AUTOMATIC; // restore automated mode
-        
-        SET_PROC_FLAG(LIBRAW_PROGRESS_LOAD_RAW);
-        RUN_CALLBACK(LIBRAW_PROGRESS_LOAD_RAW,1,2);
-        
-        return 0;
-    }
-    catch ( LibRaw_exceptions err) {
-        EXCEPTION_HANDLER(err);
-    }
-}
-
-int LibRaw::dcraw_document_mode_processing(void)
-{
-    CHECK_ORDER_HIGH(LIBRAW_PROGRESS_PRE_INTERPOLATE);
-    CHECK_ORDER_LOW(LIBRAW_PROGRESS_LOAD_RAW);
-
-    try {
-
-        if(IO.fwidth) 
-            rotate_fuji_raw();
-
-        if(!own_filtering_supported() && (O.filtering_mode & LIBRAW_FILTERING_AUTOMATIC_BIT))
-            O.filtering_mode = LIBRAW_FILTERING_AUTOMATIC_BIT; // turn on black and zeroes filtering
-
-        O.document_mode = 2;
-        if(P1.is_foveon)
-            {
-                // filter image data for foveon document mode
-                short *iptr = (short *)imgdata.image;
-                for (int i=0; i < S.height*S.width*4; i++)
-                    {
-                        if ((short) iptr[i] < 0) 
-                            iptr[i] = 0;
-                    }
-                SET_PROC_FLAG(LIBRAW_PROGRESS_FOVEON_INTERPOLATE);
-            }
-
-        O.use_fuji_rotate = 0;
-        if (!(O.filtering_mode & LIBRAW_FILTERING_NOZEROES) && IO.zero_is_bad)
-            {
-                remove_zeroes();
-                SET_PROC_FLAG(LIBRAW_PROGRESS_REMOVE_ZEROES);
-            }
-        if(O.bad_pixels) 
-            {
-                bad_pixels(O.bad_pixels);
-                SET_PROC_FLAG(LIBRAW_PROGRESS_BAD_PIXELS);
-            }
-        if (O.dark_frame)
-            {
-                subtract (O.dark_frame);
-                SET_PROC_FLAG(LIBRAW_PROGRESS_DARK_FRAME);
-            }
-        if(O.filtering_mode & LIBRAW_FILTERING_NOBLACKS)
-            C.black=0;
-
-        if (O.user_black >= 0) 
-            C.black = O.user_black;
-
-        if (O.user_sat > 0) 
-            C.maximum = O.user_sat;
-
-        pre_interpolate();
-        SET_PROC_FLAG(LIBRAW_PROGRESS_PRE_INTERPOLATE);
-
-        if (libraw_internal_data.internal_output_params.mix_green)
-            {
-                int i;
-                for (P1.colors=3, i=0; i < S.height*S.width; i++)
-                    imgdata.image[i][1] = (imgdata.image[i][1] + imgdata.image[i][3]) >> 1;
-            }
-        SET_PROC_FLAG(LIBRAW_PROGRESS_MIX_GREEN);
-
-        if (!P1.is_foveon && P1.colors == 3) 
-            median_filter();
-        SET_PROC_FLAG(LIBRAW_PROGRESS_MEDIAN_FILTER);
-
-        if (!P1.is_foveon && O.highlight == 2) 
-            blend_highlights();
-
-        if (!P1.is_foveon && O.highlight > 2) 
-            recover_highlights();
-        SET_PROC_FLAG(LIBRAW_PROGRESS_HIGHLIGHTS);
-
-        if (O.use_fuji_rotate) 
-            fuji_rotate();
-        SET_PROC_FLAG(LIBRAW_PROGRESS_FUJI_ROTATE);
-#ifndef NO_LCMS
-	if(O.camera_profile)
-            {
-                apply_profile(O.camera_profile,O.output_profile);
-                SET_PROC_FLAG(LIBRAW_PROGRESS_APPLY_PROFILE);
-            }
-#endif
-        if(!libraw_internal_data.output_data.histogram)
-            {
-                libraw_internal_data.output_data.histogram = (int (*)[LIBRAW_HISTOGRAM_SIZE]) malloc(sizeof(*libraw_internal_data.output_data.histogram)*4);
-                merror(libraw_internal_data.output_data.histogram,"LibRaw::dcraw_document_mode_processing()");
-            }
-        convert_to_rgb();
-        SET_PROC_FLAG(LIBRAW_PROGRESS_CONVERT_RGB);
-
-        if (O.use_fuji_rotate)
-            stretch();
-        SET_PROC_FLAG(LIBRAW_PROGRESS_STRETCH);
-
-        if (O.filtering_mode & LIBRAW_FILTERING_AUTOMATIC_BIT)
-            O.filtering_mode = LIBRAW_FILTERING_AUTOMATIC; // restore automated mode
-
-        return 0;
-    }
-    catch ( LibRaw_exceptions err) {
-        EXCEPTION_HANDLER(err);
-    }
-
-}
-
-#if 1
-#define FORC(cnt) for (c=0; c < cnt; c++)
-#define FORCC FORC(ret->colors)
-#define SWAP(a,b) { a ^= b; a ^= (b ^= a); }
-
-libraw_processed_image_t * LibRaw::dcraw_make_mem_thumb(int *errcode)
-{
-    if(!T.thumb)
-        {
-            if ( !ID.toffset) 
-                {
-                    if(errcode) *errcode= LIBRAW_NO_THUMBNAIL;
-                }
-            else
-                {
-                    if(errcode) *errcode= LIBRAW_OUT_OF_ORDER_CALL;
-                }
-            return NULL;
-        }
-
-    if (T.tformat == LIBRAW_THUMBNAIL_BITMAP)
-        {
-            libraw_processed_image_t * ret = 
-                (libraw_processed_image_t *)::malloc(sizeof(libraw_processed_image_t)+T.tlength);
+ * This file is provided for compatibility w/ old build scripts/tools:
+ * It includes multiple separate files that should be built separately
+ * if new build tools are used
 
-            if(!ret)
-                {
-                    if(errcode) *errcode= ENOMEM;
-                    return NULL;
-                }
+LibRaw is free software; you can redistribute it and/or modify
+it under the terms of the one of two licenses as you choose:
 
-            bzero(ret,sizeof(libraw_processed_image_t));
-            ret->type   = LIBRAW_IMAGE_BITMAP;
-            ret->height = T.theight;
-            ret->width  = T.twidth;
-            ret->colors = 3; 
-            ret->bits   = 8;
-            ret->gamma_corrected = 1;
-            ret->data_size = T.tlength;
-            memmove(ret->data,T.thumb,T.tlength);
-            if(errcode) *errcode= 0;
-            return ret;
-        }
-    else if (T.tformat == LIBRAW_THUMBNAIL_JPEG)
-        {
-            ushort exif[5];
-            int mk_exif = 0;
-            if(strcmp(T.thumb+6,"Exif")) mk_exif = 1;
-            
-            int dsize = T.tlength + mk_exif * (sizeof(exif)+sizeof(tiff_hdr));
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
 
-            libraw_processed_image_t * ret = 
-                (libraw_processed_image_t *)::malloc(sizeof(libraw_processed_image_t)+dsize);
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
 
-            if(!ret)
-                {
-                    if(errcode) *errcode= ENOMEM;
-                    return NULL;
-                }
-
-            bzero(ret,sizeof(libraw_processed_image_t));
-
-            ret->type = LIBRAW_IMAGE_JPEG;
-            ret->data_size = dsize;
-            
-            ret->data[0] = 0xff;
-            ret->data[1] = 0xd8;
-            if(mk_exif)
-                {
-                    struct tiff_hdr th;
-                    memcpy (exif, "\xff\xe1  Exif\0\0", 10);
-                    exif[1] = htons (8 + sizeof th);
-                    memmove(ret->data+2,exif,sizeof(exif));
-                    tiff_head (&th, 0);
-                    memmove(ret->data+(2+sizeof(exif)),&th,sizeof(th));
-                    memmove(ret->data+(2+sizeof(exif)+sizeof(th)),T.thumb+2,T.tlength-2);
-                }
-            else
-                {
-                    memmove(ret->data+2,T.thumb+2,T.tlength-2);
-                }
-            if(errcode) *errcode= 0;
-            return ret;
-            
-        }
-    else
-        {
-            if(errcode) *errcode= LIBRAW_UNSUPPORTED_THUMBNAIL;
-            return NULL;
-
-        }
-}
-
-
-
-libraw_processed_image_t *LibRaw::dcraw_make_mem_image(int *errcode)
-{
-    if((imgdata.progress_flags & LIBRAW_PROGRESS_THUMB_MASK) < LIBRAW_PROGRESS_PRE_INTERPOLATE)
-            {
-                if(errcode) *errcode= LIBRAW_OUT_OF_ORDER_CALL;
-                return NULL;
-            }
-
-    if(!libraw_internal_data.output_data.histogram)
-        {
-            libraw_internal_data.output_data.histogram = 
-                (int (*)[LIBRAW_HISTOGRAM_SIZE]) malloc(sizeof(*libraw_internal_data.output_data.histogram)*4);
-            merror(libraw_internal_data.output_data.histogram,"LibRaw::dcraw_make_mem_image()");
-        }
-
-    unsigned ds = S.height * S.width * (O.output_bps/8) * P1.colors;
-    libraw_processed_image_t *ret = (libraw_processed_image_t*)::malloc(sizeof(libraw_processed_image_t)+ds);
-    if(!ret)
-        {
-                if(errcode) *errcode= ENOMEM;
-                return NULL;
-        }
-    bzero(ret,sizeof(libraw_processed_image_t));
-    // metadata init
-
-    int s_iheight = S.iheight;
-    int s_iwidth = S.iwidth;
-    int s_width = S.width;
-    int s_hwight = S.height;
-
-    S.iheight = S.height;
-    S.iwidth  = S.width;
-
-
-    if (S.flip & 4) SWAP(S.height,S.width);
-
-
-    ret->type   = LIBRAW_IMAGE_BITMAP;
-    ret->height = S.height;
-    ret->width  = S.width;
-    ret->colors = P1.colors;
-    ret->bits   = O.output_bps;
-    ret->gamma_corrected = (O.output_bps == 8)?1:O.gamma_16bit;
-
-    ret->data_size = ds;
-
-    // Cut'n'paste from write_tiff_ppm, should be generalized later
-    uchar *bufp = ret->data;
-    uchar *ppm;
-    ushort *ppm2,lut16[0x10000];
-    int c, row, col, soff, rstep, cstep;
-
-
-    if (ret->bits == 8 || ret->gamma_corrected ) gamma_lut (lut16);
-    soff  = flip_index (0, 0);
-    cstep = flip_index (0, 1) - soff;
-    rstep = flip_index (1, 0) - flip_index (0, S.width);
-
-
-    for (row=0; row < ret->height; row++, soff += rstep) 
-        {
-            ppm2 = (ushort*) (ppm = bufp);
-            for (col=0; col < ret->width; col++, soff += cstep)
-                if (ret->bits == 8)
-                    FORCC ppm [col*ret->colors+c] = lut16[imgdata.image[soff][c]]/256;
-                else if(ret->gamma_corrected) 
-                    FORCC ppm2[col*ret->colors+c] =     lut16[imgdata.image[soff][c]];
-                else 
-                    FORCC ppm2[col*ret->colors+c] =     imgdata.image[soff][c];
-            bufp+=ret->colors*(ret->bits/8)*ret->width;
-        }
-    if(errcode) *errcode= 0;
-
-    S.iheight = s_iheight;
-    S.iwidth = s_iwidth;
-    S.width = s_width;
-    S.height = s_hwight;
-
-    return ret;
-}
-
-#undef FORC
-#undef FORCC
-#undef SWAP
-#endif
-
-
-int LibRaw::dcraw_ppm_tiff_writer(const char *filename)
-{
-    CHECK_ORDER_LOW(LIBRAW_PROGRESS_LOAD_RAW);
-
-    if(!imgdata.image) 
-        return LIBRAW_OUT_OF_ORDER_CALL;
-
-    if(!filename) 
-        return ENOENT;
-    FILE *f = fopen(filename,"wb");
-
-    if(!f) 
-        return errno;
-        
-    try {
-        if(!libraw_internal_data.output_data.histogram)
-            {
-                libraw_internal_data.output_data.histogram = 
-                    (int (*)[LIBRAW_HISTOGRAM_SIZE]) malloc(sizeof(*libraw_internal_data.output_data.histogram)*4);
-                merror(libraw_internal_data.output_data.histogram,"LibRaw::dcraw_ppm_tiff_writer()");
-            }
-        write_ppm_tiff(f);
-        SET_PROC_FLAG(LIBRAW_PROGRESS_FLIP);
-        fclose(f);
-        return 0;
-    }
-    catch ( LibRaw_exceptions err) {
-        fclose(f);
-        EXCEPTION_HANDLER(err);
-    }
-}
-
-void LibRaw::kodak_thumb_loader()
-{
-    // some kodak cameras
-    ushort s_height = S.height, s_width = S.width,s_iwidth = S.iwidth,s_iheight=S.iheight;
-    int s_colors = P1.colors;
-    unsigned s_filters = P1.filters;
-    ushort (*s_image)[4] = imgdata.image;
-
-    
-    S.height = T.theight;
-    S.width  = T.twidth;
-    P1.filters = 0;
-
-    if (thumb_load_raw == &CLASS kodak_ycbcr_load_raw) 
-        {
-            S.height += S.height & 1;
-            S.width  += S.width  & 1;
-        }
-    
-    imgdata.image = (ushort (*)[4]) calloc (S.iheight*S.iwidth, sizeof (*imgdata.image));
-    merror (imgdata.image, "LibRaw::kodak_thumb_loader()");
-
-    ID.input->seek(ID.toffset, SEEK_SET);
-    // read kodak thumbnail into T.image[]
-    (this->*thumb_load_raw)();
-
-    // copy-n-paste from image pipe
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
-#define MAX(a,b) ((a) > (b) ? (a) : (b))
-#define LIM(x,min,max) MAX(min,MIN(x,max))
-#define CLIP(x) LIM(x,0,65535)
-#define SWAP(a,b) { a ^= b; a ^= (b ^= a); }
-
-    // from scale_colors
-    {
-        double   dmax;
-        float scale_mul[4];
-        int c,val;
-        for (dmax=DBL_MAX, c=0; c < 3; c++) 
-                if (dmax > C.pre_mul[c])
-                    dmax = C.pre_mul[c];
-
-        for( c=0; c< 3; c++)
-                scale_mul[c] = (C.pre_mul[c] / dmax) * 65535.0 / C.maximum;
-        scale_mul[3] = scale_mul[1];
-
-        size_t size = S.height * S.width;
-        for (int i=0; i < size*4 ; i++) 
-            {
-                val = imgdata.image[0][i];
-                if(!val) continue;
-                val *= scale_mul[i & 3];
-                imgdata.image[0][i] = CLIP(val);
-            }
-    }
-
-    // from convert_to_rgb
-    ushort *img;
-    int row,col;
-    
-    int  (*t_hist)[LIBRAW_HISTOGRAM_SIZE] =  (int (*)[LIBRAW_HISTOGRAM_SIZE]) calloc(sizeof(*t_hist),4);
-    merror (t_hist, "LibRaw::kodak_thumb_loader()");
-    
-    float out[3], 
-        out_cam[3][4] = 
-        {
-            {2.81761312, -1.98369181, 0.166078627, 0}, 
-            {-0.111855984, 1.73688626, -0.625030339, 0}, 
-            {-0.0379119813, -0.891268849, 1.92918086, 0}
-        };
-
-    for (img=imgdata.image[0], row=0; row < S.height; row++)
-        for (col=0; col < S.width; col++, img+=4)
-            {
-                out[0] = out[1] = out[2] = 0;
-                for(int c=0;c<3;c++) 
-                    {
-                        out[0] += out_cam[0][c] * img[c];
-                        out[1] += out_cam[1][c] * img[c];
-                        out[2] += out_cam[2][c] * img[c];
-                    }
-                for(int c=0; c<3; c++)
-                    img[c] = CLIP((int) out[c]);
-                for(int c=0; c<P1.colors;c++)
-                    t_hist[c][img[c] >> 3]++;
-                    
-            }
-
-    // from gamma_lut
-    int  (*save_hist)[LIBRAW_HISTOGRAM_SIZE] = libraw_internal_data.output_data.histogram;
-    libraw_internal_data.output_data.histogram = t_hist;
-    
-    ushort *lut16 = (ushort*)calloc(0x10000,sizeof(ushort));
-    merror(lut16,"LibRaw::kodak_thumb_loader()");
-    gamma_lut(lut16);
-    
-    libraw_internal_data.output_data.histogram = save_hist;
-
-    free(t_hist);
-    
-    // from write_ppm_tiff - copy pixels into bitmap
-    
-    S.iheight = S.height;
-    S.iwidth  = S.width;
-    if (S.flip & 4) SWAP(S.height,S.width);
-
-    if(T.thumb) free(T.thumb);
-    T.thumb = (char*) calloc (S.width * S.height, P1.colors);
-    merror (T.thumb, "LibRaw::kodak_thumb_loader()");
-    T.tlength = S.width * S.height * P1.colors;
-
-    // from write_tiff_ppm
-    {
-        int soff  = flip_index (0, 0);
-        int cstep = flip_index (0, 1) - soff;
-        int rstep = flip_index (1, 0) - flip_index (0, S.width);
-        
-        for (int row=0; row < S.height; row++, soff += rstep) 
-            {
-                char *ppm = T.thumb + row*S.width*P1.colors;
-                for (int col=0; col < S.width; col++, soff += cstep)
-                    for(int c = 0; c < P1.colors; c++)
-                        ppm [col*P1.colors+c] = lut16[imgdata.image[soff][c]]/256;
-            }
-    }
-    free(lut16);
-    // restore variables
-    free(imgdata.image);
-    imgdata.image  = s_image;
-    
-    T.twidth = S.width;
-    S.width = s_width;
-
-    S.iwidth = s_iwidth;
-    S.iheight = s_iheight;
-
-    T.theight = S.height;
-    S.height = s_height;
-
-    T.tcolors = P1.colors;
-    P1.colors = s_colors;
-
-    P1.filters = s_filters;
-}
-#undef MIN
-#undef MAX
-#undef LIM
-#undef CLIP
-#undef SWAP
-
-
-void LibRaw::foveon_thumb_loader (void)
-{
-    unsigned bwide, row, col, bitbuf=0, bit=1, c, i;
-    struct decode *dindex;
-    short pred[3];
-    
-    if(T.thumb) free(T.thumb);
-    T.thumb = NULL;
-    
-    bwide = get4();
-    if (bwide > 0) 
-        {
-            if (bwide < T.twidth*3) return;
-            T.thumb = (char*)malloc(3*T.twidth * T.theight);
-            merror (T.thumb, "foveon_thumb()");
-            char *buf = (char*)malloc(bwide); 
-            merror (buf, "foveon_thumb()");
-            for (row=0; row < T.theight; row++) 
-                {
-                    ID.input->read(buf, 1, bwide);
-                    memmove(T.thumb+(row*T.twidth*3),buf,T.twidth*3);
-                }
-            free(buf);
-            T.tlength = 3*T.twidth * T.theight;
-            T.tformat = LIBRAW_THUMBNAIL_BITMAP;
-            return;
-        }
-    else 
-        {
-            foveon_decoder (256, 0);
-            T.thumb = (char*)malloc(3*T.twidth * T.theight);
-            char *bufp = T.thumb;
-            merror (T.thumb, "foveon_thumb()");
-            for (row=0; row < T.theight; row++) 
-                {
-                    memset (pred, 0, sizeof pred);
-                    if (!bit) get4();
-                    for (bit=col=0; col < T.twidth; col++)
-                        for(c=0;c<3;c++) 
-                            {
-                                for (dindex=first_decode; dindex->branch[0]; ) 
-                                    {
-                                        if ((bit = (bit-1) & 31) == 31)
-                                            for (i=0; i < 4; i++)
-                                                bitbuf = (bitbuf << 8) + ID.input->get_char();
-                                        dindex = dindex->branch[bitbuf >> bit & 1];
-                                    }
-                                pred[c] += dindex->leaf;
-                                (*bufp++)=pred[c];
-                            }
-                }
-            T.tformat = LIBRAW_THUMBNAIL_BITMAP;
-            T.tlength = 3*T.twidth * T.theight;
-        }
-    return;
-}
-
-
-// Äîñòàåò thumbnail èç ôàéëà, ñòàâèò thumb_format â ñîîòâåòñòâèè ñ ôîðìàòîì
-int LibRaw::unpack_thumb(void)
-{
-    CHECK_ORDER_LOW(LIBRAW_PROGRESS_IDENTIFY);
-    CHECK_ORDER_BIT(LIBRAW_PROGRESS_THUMB_LOAD);
-
-    try {
-        if ( !ID.toffset) 
-            {
-                return LIBRAW_NO_THUMBNAIL;
-            } 
-        else if (thumb_load_raw) 
-            {
-                kodak_thumb_loader();
-                T.tformat = LIBRAW_THUMBNAIL_BITMAP;
-                SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD);
-                return 0;
-            } 
-        else 
-            {
-                ID.input->seek(ID.toffset, SEEK_SET);
-                if ( write_thumb == &LibRaw::jpeg_thumb)
-                    {
-                        if(T.thumb) free(T.thumb);
-                        T.thumb = (char *) malloc (T.tlength);
-                        merror (T.thumb, "jpeg_thumb()");
-                        ID.input->read (T.thumb, 1, T.tlength);
-                        T.tcolors = 3;
-                        T.tformat = LIBRAW_THUMBNAIL_JPEG;
-                        SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD);
-                        return 0;
-                    }
-                else if (write_thumb == &LibRaw::ppm_thumb)
-                    {
-                        T.tlength = T.twidth * T.theight*3;
-                        if(T.thumb) free(T.thumb);
-
-                        T.thumb = (char *) malloc (T.tlength);
-                        merror (T.thumb, "ppm_thumb()");
-
-                        ID.input->read(T.thumb, 1, T.tlength);
-
-                        T.tformat = LIBRAW_THUMBNAIL_BITMAP;
-                        SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD);
-                        return 0;
-
-                    }
-                else if (write_thumb == &LibRaw::foveon_thumb)
-                    {
-                        foveon_thumb_loader();
-                        // may return with error, so format is set in
-                        // foveon thumb loader itself
-                        SET_PROC_FLAG(LIBRAW_PROGRESS_THUMB_LOAD);
-                        return 0;
-                    }
-                // else if -- all other write_thumb cases!
-                else
-                    {
-                        return LIBRAW_UNSUPPORTED_THUMBNAIL;
-                    }
-            }
-        // last resort
-        return LIBRAW_UNSUPPORTED_THUMBNAIL;
-    }
-    catch ( LibRaw_exceptions err) {
-        EXCEPTION_HANDLER(err);
-    }
-
-}
-
-int LibRaw::dcraw_thumb_writer(const char *fname)
-{
-//    CHECK_ORDER_LOW(LIBRAW_PROGRESS_THUMB_LOAD);
-
-    if(!fname) 
-        return ENOENT;
-        
-    FILE *tfp = fopen(fname,"wb");
-    
-    if(!tfp) 
-        return errno;
-
-    if(!T.thumb)
-	{
-		fclose(tfp);
-        	return LIBRAW_OUT_OF_ORDER_CALL;
-	}
-
-    try {
-        switch (T.tformat)
-            {
-            case LIBRAW_THUMBNAIL_JPEG:
-                jpeg_thumb_writer (tfp,T.thumb,T.tlength);
-                break;
-            case LIBRAW_THUMBNAIL_BITMAP:
-                fprintf (tfp, "P6\n%d %d\n255\n", T.twidth, T.theight);
-                fwrite (T.thumb, 1, T.tlength, tfp);
-                break;
-            default:
-                fclose(tfp);
-                return LIBRAW_UNSUPPORTED_THUMBNAIL;
-           }
-        fclose(tfp);
-        return 0;
-    }
-    catch ( LibRaw_exceptions err) {
-        fclose(tfp);
-        EXCEPTION_HANDLER(err);
-    }
-}
-
-int LibRaw::adjust_sizes_info_only(void)
-{
-    CHECK_ORDER_LOW(LIBRAW_PROGRESS_IDENTIFY);
-    CHECK_ORDER_HIGH(LIBRAW_PROGRESS_FUJI_ROTATE);
-    if (O.use_fuji_rotate)
-        {
-            if (IO.fuji_width) 
-                {
-                    // restore saved values
-                    if(IO.fheight)
-                        {
-                            S.height = IO.fheight;
-                            S.width = IO.fwidth;
-                            S.iheight = (S.height + IO.shrink) >> IO.shrink;
-                            S.iwidth  = (S.width  + IO.shrink) >> IO.shrink;
-                            S.raw_height -= 2*S.top_margin;
-                            IO.fheight = IO.fwidth = 0; // prevent repeated calls
-                        }
-                    // dcraw code
-                    IO.fuji_width = (IO.fuji_width - 1 + IO.shrink) >> IO.shrink;
-                    S.iwidth = (ushort)(IO.fuji_width / sqrt(0.5));
-                    S.iheight = (ushort)( (S.iheight - IO.fuji_width) / sqrt(0.5));
-                } 
-            else 
-                {
-                    if (S.pixel_aspect < 1) S.iheight = (ushort)( S.iheight / S.pixel_aspect + 0.5);
-                    if (S.pixel_aspect > 1) S.iwidth  = (ushort) (S.iwidth  * S.pixel_aspect + 0.5);
-                }
-        }
-    SET_PROC_FLAG(LIBRAW_PROGRESS_FUJI_ROTATE);
-    if (S.flip & 4)
-        {
-            unsigned short t = S.iheight;
-            S.iheight=S.iwidth;
-            S.iwidth = t;
-            SET_PROC_FLAG(LIBRAW_PROGRESS_FLIP);
-        }
-    return 0;
-}
-
-int LibRaw::rotate_fuji_raw(void)
-{
-    CHECK_ORDER_LOW(LIBRAW_PROGRESS_LOAD_RAW);
-    CHECK_ORDER_HIGH(LIBRAW_PROGRESS_PRE_INTERPOLATE);
-
-
-    if(!IO.fwidth) return LIBRAW_SUCCESS;
-    int row,col,r,c;
-    ushort (*newimage)[4];
-    ushort fiwidth,fiheight;
-
-    fiheight = (IO.fheight + IO.shrink) >> IO.shrink;
-    fiwidth = (IO.fwidth + IO.shrink) >> IO.shrink;
-    
-    newimage = (ushort (*)[4]) calloc (fiheight*fiwidth, sizeof (*newimage));
-    merror(newimage,"rotate_fuji_raw()");
-    for(row=0;row<S.height;row++)
-        {
-            for(col=0;col<S.width;col++)
-                {
-
-                    if (libraw_internal_data.unpacker_data.fuji_layout) {
-                        r = IO.fuji_width - 1 - col + (row >> 1);
-                        c = col + ((row+1) >> 1);
-                    } else {
-                        r = IO.fuji_width - 1 + row - (col >> 1);
-                        c = row + ((col+1) >> 1);
-                    }
-                    newimage[((r) >> IO.shrink)*fiwidth + ((c) >> IO.shrink)][FC(r,c)] = 
-                        imgdata.image[((row) >> IO.shrink)*S.iwidth + ((col) >> IO.shrink)][FC(r,c)];
-                }
-        }
-    // restore fuji sizes!
-    S.height = IO.fheight;
-    S.width = IO.fwidth;
-    S.iheight = (S.height + IO.shrink) >> IO.shrink;
-    S.iwidth  = (S.width  + IO.shrink) >> IO.shrink;
-    S.raw_height -= 2*S.top_margin;
-    IO.fheight = IO.fwidth = 0; // prevent repeated calls
-
-    free(imgdata.image);
-    imgdata.image = newimage;
-    return LIBRAW_SUCCESS;
-    
-}
-
-
-int LibRaw::dcraw_process(void)
-{
-    int quality,i;
-
-
-    CHECK_ORDER_LOW(LIBRAW_PROGRESS_LOAD_RAW);
-    CHECK_ORDER_HIGH(LIBRAW_PROGRESS_PRE_INTERPOLATE);
-
-    try {
-
-        if(IO.fwidth) 
-            rotate_fuji_raw();
-
-
-        if(!own_filtering_supported() && (O.filtering_mode & LIBRAW_FILTERING_AUTOMATIC_BIT))
-            O.filtering_mode = LIBRAW_FILTERING_AUTOMATIC_BIT; // turn on black and zeroes filtering
-
-        if(O.half_size) 
-            O.four_color_rgb = 1;
-
-        if (!(O.filtering_mode & LIBRAW_FILTERING_NOZEROES) && IO.zero_is_bad) 
-            {
-                remove_zeroes();
-                SET_PROC_FLAG(LIBRAW_PROGRESS_REMOVE_ZEROES);
-            }
-        if(O.bad_pixels) 
-            {
-                bad_pixels(O.bad_pixels);
-                SET_PROC_FLAG(LIBRAW_PROGRESS_BAD_PIXELS);
-            }
-        if (O.dark_frame)
-            {
-                subtract (O.dark_frame);
-                SET_PROC_FLAG(LIBRAW_PROGRESS_DARK_FRAME);
-            }
-
-        quality = 2 + !IO.fuji_width;
-
-        if(O.filtering_mode & LIBRAW_FILTERING_NOBLACKS)
-            C.black=0;
-
-        if (O.user_qual >= 0) quality = O.user_qual;
-        if (O.user_black >= 0) C.black = O.user_black;
-        if (O.user_sat > 0) C.maximum = O.user_sat;
-
-        if (P1.is_foveon && !O.document_mode) 
-            {
-                foveon_interpolate();
-                SET_PROC_FLAG(LIBRAW_PROGRESS_FOVEON_INTERPOLATE);
-            }
-
-        if (!P1.is_foveon && O.document_mode < 2)
-            {
-                scale_colors();
-                SET_PROC_FLAG(LIBRAW_PROGRESS_SCALE_COLORS);
-            }
-
-        pre_interpolate();
-        SET_PROC_FLAG(LIBRAW_PROGRESS_PRE_INTERPOLATE);
-
-        if (P1.filters && !O.document_mode) 
-            {
-                if (quality == 0)
-                    lin_interpolate();
-                else if (quality == 1 || P1.colors > 3)
-                    vng_interpolate();
-                else if (quality == 2)
-                    ppg_interpolate();
-                else 
-                    ahd_interpolate();
-                SET_PROC_FLAG(LIBRAW_PROGRESS_INTERPOLATE);
-            }
-        if (IO.mix_green)
-            {
-                for (P1.colors=3, i=0; i < S.height * S.width; i++)
-                    imgdata.image[i][1] = (imgdata.image[i][1] + imgdata.image[i][3]) >> 1;
-                SET_PROC_FLAG(LIBRAW_PROGRESS_MIX_GREEN);
-            }
-
-        if(!P1.is_foveon)
-            {
-                if (P1.colors == 3) 
-                    {
-                        median_filter();
-                        SET_PROC_FLAG(LIBRAW_PROGRESS_MEDIAN_FILTER);
-                    }
-            
-                if (O.highlight == 2) 
-                    {
-                        blend_highlights();
-                        SET_PROC_FLAG(LIBRAW_PROGRESS_HIGHLIGHTS);
-                    }
-            
-                if (O.highlight > 2) 
-                    {
-                        recover_highlights();
-                        SET_PROC_FLAG(LIBRAW_PROGRESS_HIGHLIGHTS);
-                    }
-            }
-        if (O.use_fuji_rotate) 
-            {
-                fuji_rotate();
-                SET_PROC_FLAG(LIBRAW_PROGRESS_FUJI_ROTATE);
-            }
-    
-        if(!libraw_internal_data.output_data.histogram)
-            {
-                libraw_internal_data.output_data.histogram = (int (*)[LIBRAW_HISTOGRAM_SIZE]) malloc(sizeof(*libraw_internal_data.output_data.histogram)*4);
-                merror(libraw_internal_data.output_data.histogram,"LibRaw::dcraw_process()");
-            }
-#ifndef NO_LCMS
-	if(O.camera_profile)
-            {
-                apply_profile(O.camera_profile,O.output_profile);
-                SET_PROC_FLAG(LIBRAW_PROGRESS_APPLY_PROFILE);
-            }
-#endif
-
-        convert_to_rgb();
-        SET_PROC_FLAG(LIBRAW_PROGRESS_CONVERT_RGB);
-
-        if (O.use_fuji_rotate) 
-            {
-                stretch();
-                SET_PROC_FLAG(LIBRAW_PROGRESS_STRETCH);
-            }
-        if (O.filtering_mode & LIBRAW_FILTERING_AUTOMATIC_BIT)
-            O.filtering_mode = LIBRAW_FILTERING_AUTOMATIC; // restore automated mode
-        return 0;
-    }
-    catch ( LibRaw_exceptions err) {
-        EXCEPTION_HANDLER(err);
-    }
-}
-
-// Supported cameras:
-static const char  *static_camera_list[] = 
-{
-"Adobe Digital Negative (DNG)",
-"Apple QuickTake 100",
-"Apple QuickTake 150",
-"Apple QuickTake 200",
-"AVT F-080C",
-"AVT F-145C",
-"AVT F-201C",
-"AVT F-510C",
-"AVT F-810C",
-"Canon PowerShot 600",
-"Canon PowerShot A5",
-"Canon PowerShot A5 Zoom",
-"Canon PowerShot A50",
-"Canon PowerShot A460 (CHDK hack)",
-"Canon PowerShot A530 (CHDK hack)",
-"Canon PowerShot A610 (CHDK hack)",
-"Canon PowerShot A620 (CHDK hack)",
-"Canon PowerShot A630 (CHDK hack)",
-"Canon PowerShot A640 (CHDK hack)",
-"Canon PowerShot A650 (CHDK hack)",
-"Canon PowerShot A710 IS (CHDK hack)",
-"Canon PowerShot A720 IS (CHDK hack)",
-"Canon PowerShot Pro70",
-"Canon PowerShot Pro90 IS",
-"Canon PowerShot G1",
-"Canon PowerShot G2",
-"Canon PowerShot G3",
-"Canon PowerShot G5",
-"Canon PowerShot G6",
-"Canon PowerShot G7 (CHDK hack)",
-"Canon PowerShot G9",
-"Canon PowerShot G10",
-"Canon PowerShot S2 IS (CHDK hack)",
-"Canon PowerShot S3 IS (CHDK hack)",
-"Canon PowerShot S5 IS (CHDK hack)",
-"Canon PowerShot SD300 (CHDK hack)",
-"Canon PowerShot S30",
-"Canon PowerShot S40",
-"Canon PowerShot S45",
-"Canon PowerShot S50",
-"Canon PowerShot S60",
-"Canon PowerShot S70",
-"Canon PowerShot Pro1",
-"Canon EOS D30",
-"Canon EOS D60",
-"Canon EOS 5D",
-"Canon EOS 5D Mark II",
-"Canon EOS 10D",
-"Canon EOS 20D",
-"Canon EOS 30D",
-"Canon EOS 40D",
-"Canon EOS 50D",
-"Canon EOS 300D / Digital Rebel / Kiss Digital",
-"Canon EOS 350D / Digital Rebel XT / Kiss Digital N",
-"Canon EOS 400D / Digital Rebel XTi / Kiss Digital X",
-"Canon EOS 450D / Digital Rebel XSi / Kiss Digital X2",
-"Canon EOS 1000D / Digital Rebel XS / Kiss Digital F",
-"Canon EOS D2000C",
-"Canon EOS-1D",
-"Canon EOS-1DS",
-"Canon EOS-1D Mark II",
-"Canon EOS-1D Mark III",
-"Canon EOS-1D Mark II N",
-"Canon EOS-1Ds Mark II",
-"Canon EOS-1Ds Mark III",
-"Casio QV-2000UX",
-"Casio QV-3000EX",
-"Casio QV-3500EX",
-"Casio QV-4000",
-"Casio QV-5700",
-"Casio QV-R41",
-"Casio QV-R51",
-"Casio QV-R61",
-"Casio EX-S100",
-"Casio EX-Z4",
-"Casio EX-Z50",
-"Casio EX-Z55",
-"Casio Exlim Pro 505",
-"Casio Exlim Pro 600",
-"Casio Exlim Pro 700",
-"Contax N Digital",
-"Creative PC-CAM 600",
-"Epson R-D1",
-"Foculus 531C",
-"Fuji FinePix E550",
-"Fuji FinePix E900",
-"Fuji FinePix F700",
-"Fuji FinePix F710",
-"Fuji FinePix F800",
-"Fuji FinePix F810",
-"Fuji FinePix S2Pro",
-"Fuji FinePix S3Pro",
-"Fuji FinePix S5Pro",
-"Fuji FinePix S20Pro",
-"Fuji FinePix S100FS",
-"Fuji FinePix S5000",
-"Fuji FinePix S5100/S5500",
-"Fuji FinePix S5200/S5600",
-"Fuji FinePix S6000fd",
-"Fuji FinePix S7000",
-"Fuji FinePix S9000/S9500",
-"Fuji FinePix S9100/S9600",
-"Fuji IS-1",
-"Hasselblad CFV",
-"Hasselblad H3D",
-"Hasselblad V96C",
-"Imacon Ixpress 16-megapixel",
-"Imacon Ixpress 22-megapixel",
-"Imacon Ixpress 39-megapixel",
-"ISG 2020x1520",
-"Kodak DC20 (see Oliver Hartman's page)",
-"Kodak DC25 (see Jun-ichiro Itoh's page)",
-"Kodak DC40",
-"Kodak DC50",
-"Kodak DC120 (also try kdc2tiff)",
-"Kodak DCS200",
-"Kodak DCS315C",
-"Kodak DCS330C",
-"Kodak DCS420",
-"Kodak DCS460",
-"Kodak DCS460A",
-"Kodak DCS520C",
-"Kodak DCS560C",
-"Kodak DCS620C",
-"Kodak DCS620X",
-"Kodak DCS660C",
-"Kodak DCS660M",
-"Kodak DCS720X",
-"Kodak DCS760C",
-"Kodak DCS760M",
-"Kodak EOSDCS1",
-"Kodak EOSDCS3B",
-"Kodak NC2000F",
-"Kodak ProBack",
-"Kodak PB645C",
-"Kodak PB645H",
-"Kodak PB645M",
-"Kodak DCS Pro 14n",
-"Kodak DCS Pro 14nx",
-"Kodak DCS Pro SLR/c",
-"Kodak DCS Pro SLR/n",
-"Kodak C330",
-"Kodak C603",
-"Kodak P850",
-"Kodak P880",
-"Kodak KAI-0340",
-"Konica KD-400Z",
-"Konica KD-510Z",
-"Leaf AFi 7",
-"Leaf Aptus 17",
-"Leaf Aptus 22",
-"Leaf Aptus 54S",
-"Leaf Aptus 65",
-"Leaf Aptus 75",
-"Leaf Aptus 75S",
-"Leaf Cantare",
-"Leaf CatchLight",
-"Leaf CMost",
-"Leaf DCB2",
-"Leaf Valeo 6",
-"Leaf Valeo 11",
-"Leaf Valeo 17",
-"Leaf Valeo 22",
-"Leaf Volare",
-"Leica Digilux 2",
-"Leica Digilux 3",
-"Leica D-LUX2",
-"Leica D-LUX3",
-"Leica D-LUX4",
-"Leica V-LUX1",
-"Logitech Fotoman Pixtura",
-"Mamiya ZD",
-"Micron 2010",
-"Minolta RD175",
-"Minolta DiMAGE 5",
-"Minolta DiMAGE 7",
-"Minolta DiMAGE 7i",
-"Minolta DiMAGE 7Hi",
-"Minolta DiMAGE A1",
-"Minolta DiMAGE A2",
-"Minolta DiMAGE A200",
-"Minolta DiMAGE G400",
-"Minolta DiMAGE G500",
-"Minolta DiMAGE G530",
-"Minolta DiMAGE G600",
-"Minolta DiMAGE Z2",
-"Minolta Alpha/Dynax/Maxxum 5D",
-"Minolta Alpha/Dynax/Maxxum 7D",
-"Nikon D1",
-"Nikon D1H",
-"Nikon D1X",
-"Nikon D2H",
-"Nikon D2Hs",
-"Nikon D2X",
-"Nikon D2Xs",
-"Nikon D3",
-"Nikon D3X",
-"Nikon D40",
-"Nikon D40X",
-"Nikon D50",
-"Nikon D60",
-"Nikon D70",
-"Nikon D70s",
-"Nikon D80",
-"Nikon D90",
-"Nikon D100",
-"Nikon D200",
-"Nikon D300",
-"Nikon D700",
-"Nikon E700 (\"DIAG RAW\" hack)",
-"Nikon E800 (\"DIAG RAW\" hack)",
-"Nikon E880 (\"DIAG RAW\" hack)",
-"Nikon E900 (\"DIAG RAW\" hack)",
-"Nikon E950 (\"DIAG RAW\" hack)",
-"Nikon E990 (\"DIAG RAW\" hack)",
-"Nikon E995 (\"DIAG RAW\" hack)",
-"Nikon E2100 (\"DIAG RAW\" hack)",
-"Nikon E2500 (\"DIAG RAW\" hack)",
-"Nikon E3200 (\"DIAG RAW\" hack)",
-"Nikon E3700 (\"DIAG RAW\" hack)",
-"Nikon E4300 (\"DIAG RAW\" hack)",
-"Nikon E4500 (\"DIAG RAW\" hack)",
-"Nikon E5000",
-"Nikon E5400",
-"Nikon E5700",
-"Nikon E8400",
-"Nikon E8700",
-"Nikon E8800",
-"Nikon Coolpix P6000",
-"Nikon Coolpix S6 (\"DIAG RAW\" hack)",
-"Nokia N95",
-"Olympus C3030Z",
-"Olympus C5050Z",
-"Olympus C5060WZ",
-"Olympus C7070WZ",
-"Olympus C70Z,C7000Z",
-"Olympus C740UZ",
-"Olympus C770UZ",
-"Olympus C8080WZ",
-"Olympus E-1",
-"Olympus E-3",
-"Olympus E-10",
-"Olympus E-20",
-"Olympus E-300",
-"Olympus E-330",
-"Olympus E-400",
-"Olympus E-410",
-"Olympus E-420",
-"Olympus E-500",
-"Olympus E-510",
-"Olympus E-520",
-"Olympus SP310",
-"Olympus SP320",
-"Olympus SP350",
-"Olympus SP500UZ",
-"Olympus SP510UZ",
-"Olympus SP550UZ",
-"Olympus SP560UZ",
-"Olympus SP570UZ",
-"Panasonic DMC-FZ8",
-"Panasonic DMC-FZ18",
-"Panasonic DMC-FZ28",
-"Panasonic DMC-FZ30",
-"Panasonic DMC-FZ50",
-"Panasonic DMC-FX150",
-"Panasonic DMC-G1",
-"Panasonic DMC-L1",
-"Panasonic DMC-L10",
-"Panasonic DMC-LC1",
-"Panasonic DMC-LX1",
-"Panasonic DMC-LX2",
-"Panasonic DMC-LX3",
-"Pentax *ist D",
-"Pentax *ist DL",
-"Pentax *ist DL2",
-"Pentax *ist DS",
-"Pentax *ist DS2",
-"Pentax K10D",
-"Pentax K20D",
-"Pentax K100D",
-"Pentax K100D Super",
-"Pentax K200D",
-"Pentax K2000/K-m",
-"Pentax Optio S",
-"Pentax Optio S4",
-"Pentax Optio 33WR",
-"Pentax Optio 750Z",
-"Phase One LightPhase",
-"Phase One H 10",
-"Phase One H 20",
-"Phase One H 25",
-"Phase One P 20",
-"Phase One P 25",
-"Phase One P 30",
-"Phase One P 45",
-"Pixelink A782",
-"Polaroid x530",
-"Rollei d530flex",
-"RoverShot 3320af",
-"Samsung GX-1S",
-"Samsung GX-10",
-"Samsung S85 (hacked)",
-"Sarnoff 4096x5440",
-"Sigma SD9",
-"Sigma SD10",
-"Sigma SD14",
-"Sinar 3072x2048",
-"Sinar 4080x4080",
-"Sinar 4080x5440",
-"Sinar STI format",
-"SMaL Ultra-Pocket 3",
-"SMaL Ultra-Pocket 4",
-"SMaL Ultra-Pocket 5",
-"Sony DSC-F828",
-"Sony DSC-R1",
-"Sony DSC-V3",
-"Sony DSLR-A100",
-"Sony DSLR-A200",
-"Sony DSLR-A300",
-"Sony DSLR-A350",
-"Sony DSLR-A700",
-"Sony DSLR-A900",
-"Sony XCD-SX910CR",
-"STV680 VGA",
-   NULL
-};
-
-const char** LibRaw::cameraList() { return static_camera_list;}
-int LibRaw::cameraCount() { return (sizeof(static_camera_list)/sizeof(static_camera_list[0]))-1; }
+ */
 
+#include "../internal/libraw_cxx_defs.h"
 
-const char * LibRaw::strprogress(enum LibRaw_progress p)
-{
-    switch(p)
-        {
-        case LIBRAW_PROGRESS_START:
-            return "Starting";
-        case LIBRAW_PROGRESS_OPEN :
-            return "Opening file";
-        case LIBRAW_PROGRESS_IDENTIFY :
-            return "Reading metadata";
-        case LIBRAW_PROGRESS_SIZE_ADJUST:
-            return "Adjusting size";
-        case LIBRAW_PROGRESS_LOAD_RAW:
-            return "Reading RAW data";
-        case LIBRAW_PROGRESS_REMOVE_ZEROES:
-            return "Clearing zero values";
-        case LIBRAW_PROGRESS_BAD_PIXELS :
-            return "Removing dead pixels";
-        case LIBRAW_PROGRESS_DARK_FRAME:
-            return "Subtracting dark frame data";
-        case LIBRAW_PROGRESS_FOVEON_INTERPOLATE:
-            return "Interpolating Foveon sensor data";
-        case LIBRAW_PROGRESS_SCALE_COLORS:
-            return "Scaling colors";
-        case LIBRAW_PROGRESS_PRE_INTERPOLATE:
-            return "Pre-interpolating";
-        case LIBRAW_PROGRESS_INTERPOLATE:
-            return "Interpolating";
-        case LIBRAW_PROGRESS_MIX_GREEN :
-            return "Mixing green channels";
-        case LIBRAW_PROGRESS_MEDIAN_FILTER   :
-            return "Median filter";
-        case LIBRAW_PROGRESS_HIGHLIGHTS:
-            return "Highlight recovery";
-        case LIBRAW_PROGRESS_FUJI_ROTATE :
-            return "Rotating Fuji diagonal data";
-        case LIBRAW_PROGRESS_FLIP :
-            return "Flipping image";
-        case LIBRAW_PROGRESS_APPLY_PROFILE:
-            return "ICC conversion";
-        case LIBRAW_PROGRESS_CONVERT_RGB:
-            return "Converting to RGB";
-        case LIBRAW_PROGRESS_STRETCH:
-            return "Stretching image";
-        case LIBRAW_PROGRESS_THUMB_LOAD:
-            return "Loading thumbnail";
-        default:
-            return "Some strange things";
-        }
-}
+#include "tables/cameralist.cpp"
+#include "decoders/fuji_compressed.cpp"
+#include "decoders/crx.cpp"
+#include "decoders/fp_dng.cpp"
+#include "decoders/decoders_libraw.cpp"
+#include "decoders/unpack.cpp"
+#include "decoders/unpack_thumb.cpp"
+
+#include "integration/dngsdk_glue.cpp"
+#include "integration/rawspeed_glue.cpp"
+
+#include "tables/colorconst.cpp"
+#include "utils/utils_libraw.cpp"
+#include "utils/init_close_utils.cpp"
+#include "utils/decoder_info.cpp"
+#include "utils/open.cpp"
+#include "utils/phaseone_processing.cpp"
+#include "utils/thumb_utils.cpp"
+
+#include "write/tiff_writer.cpp"
+#include "preprocessing/subtract_black.cpp"
+#include "preprocessing/raw2image.cpp"
+#include "postprocessing/postprocessing_utils.cpp"
+#include "postprocessing/dcraw_process.cpp"
+#include "postprocessing/mem_image.cpp"
+
+/* DS conflicts with a define in /usr/include/sys/regset.h on Solaris */
+#if defined __sun && defined DS
+#undef DS
+#endif
+#undef ID /* used in x3f utils */
+#include "x3f/x3f_utils_patched.cpp"
+#include "x3f/x3f_parse_process.cpp"
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/libraw_datastream.cpp libkdcraw/libkdcraw/libraw/src/libraw_datastream.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/libraw_datastream.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/libraw_datastream.cpp	2022-11-07 07:46:31.734795008 +0300
@@ -0,0 +1,705 @@
+/* -*- C++ -*-
+ * File: libraw_datastream.cpp
+ * Copyright 2008-2020 LibRaw LLC (info@libraw.org)
+ *
+ * LibRaw C++ interface (implementation)
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+*/
+
+#ifdef _WIN32
+#ifdef __MINGW32__
+#define _WIN32_WINNT 0x0500
+#include <stdexcept>
+#endif
+#endif
+
+#define LIBRAW_LIBRARY_BUILD
+#include "libraw/libraw.h"
+#include "libraw/libraw_types.h"
+#include "libraw/libraw_datastream.h"
+#include <sys/stat.h>
+#ifdef USE_JASPER
+#include <jasper/jasper.h> /* Decode RED camera movies */
+#else
+#define NO_JASPER
+#endif
+#ifdef USE_JPEG
+#include <jpeglib.h>
+#include <jerror.h>
+#else
+#define NO_JPEG
+#endif
+
+#ifdef USE_JPEG
+
+typedef struct
+{
+    struct jpeg_source_mgr pub; /* public fields */
+    LibRaw_abstract_datastream *instream;            /* source stream */
+    JOCTET *buffer;             /* start of buffer */
+    boolean start_of_file;      /* have we gotten any data yet? */
+} lr_jpg_source_mgr;
+
+typedef lr_jpg_source_mgr *lr_jpg_src_ptr;
+
+#define LR_JPEG_INPUT_BUF_SIZE 16384
+
+static void f_init_source(j_decompress_ptr cinfo)
+{
+    lr_jpg_src_ptr src = (lr_jpg_src_ptr)cinfo->src;
+    src->start_of_file = TRUE;
+}
+
+#ifdef ERREXIT
+#undef ERREXIT
+#endif
+
+#define ERREXIT(cinfo, code)                                                   \
+  ((cinfo)->err->msg_code = (code),                                            \
+   (*(cinfo)->err->error_exit)((j_common_ptr)(cinfo)))
+
+static boolean lr_fill_input_buffer(j_decompress_ptr cinfo)
+{
+    lr_jpg_src_ptr src = (lr_jpg_src_ptr)cinfo->src;
+    size_t nbytes;
+
+    nbytes = src->instream->read((void*)src->buffer, 1, LR_JPEG_INPUT_BUF_SIZE);
+
+    if (nbytes <= 0)
+    {
+        if (src->start_of_file) /* Treat empty input file as fatal error */
+            ERREXIT(cinfo, JERR_INPUT_EMPTY);
+        WARNMS(cinfo, JWRN_JPEG_EOF);
+        /* Insert a fake EOI marker */
+        src->buffer[0] = (JOCTET)0xFF;
+        src->buffer[1] = (JOCTET)JPEG_EOI;
+        nbytes = 2;
+    }
+
+    src->pub.next_input_byte = src->buffer;
+    src->pub.bytes_in_buffer = nbytes;
+    src->start_of_file = FALSE;
+    return TRUE;
+}
+
+static void lr_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
+{
+    struct jpeg_source_mgr *src = cinfo->src;
+    if (num_bytes > 0)
+    {
+        while (num_bytes > (long)src->bytes_in_buffer)
+        {
+            num_bytes -= (long)src->bytes_in_buffer;
+            (void)(*src->fill_input_buffer)(cinfo);
+            /* note we assume that fill_input_buffer will never return FALSE,
+             * so suspension need not be handled.
+             */
+        }
+        src->next_input_byte += (size_t)num_bytes;
+        src->bytes_in_buffer -= (size_t)num_bytes;
+    }
+}
+
+static void lr_term_source(j_decompress_ptr cinfo) {}
+
+static void lr_jpeg_src(j_decompress_ptr cinfo, LibRaw_abstract_datastream *inf)
+{
+    lr_jpg_src_ptr src;
+    if (cinfo->src == NULL)
+    { /* first time for this JPEG object? */
+        cinfo->src = (struct jpeg_source_mgr *)(*cinfo->mem->alloc_small)(
+            (j_common_ptr)cinfo, JPOOL_PERMANENT, sizeof(lr_jpg_source_mgr));
+        src = (lr_jpg_src_ptr)cinfo->src;
+        src->buffer = (JOCTET *)(*cinfo->mem->alloc_small)(
+            (j_common_ptr)cinfo, JPOOL_PERMANENT,
+            LR_JPEG_INPUT_BUF_SIZE * sizeof(JOCTET));
+    }
+    else if (cinfo->src->init_source != f_init_source)
+    {
+        ERREXIT(cinfo, JERR_BUFFER_SIZE);
+    }
+
+    src = (lr_jpg_src_ptr)cinfo->src;
+    src->pub.init_source = f_init_source;
+    src->pub.fill_input_buffer = lr_fill_input_buffer;
+    src->pub.skip_input_data = lr_skip_input_data;
+    src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
+    src->pub.term_source = lr_term_source;
+    src->instream = inf;
+    src->pub.bytes_in_buffer = 0;    /* forces fill_input_buffer on first read */
+    src->pub.next_input_byte = NULL; /* until buffer loaded */
+}
+#endif
+
+int LibRaw_abstract_datastream::jpeg_src(void *jpegdata)
+{
+#ifdef NO_JPEG
+    return -1;
+#else
+    j_decompress_ptr cinfo = (j_decompress_ptr)jpegdata;
+    buffering_off();
+    lr_jpeg_src(cinfo, this);
+    return 0; // OK
+#endif
+}
+
+
+// == LibRaw_file_datastream ==
+
+LibRaw_file_datastream::~LibRaw_file_datastream()
+{
+  if (jas_file)
+    fclose(jas_file);
+}
+
+LibRaw_file_datastream::LibRaw_file_datastream(const char *fname)
+    : filename(fname), _fsize(0)
+#ifdef LIBRAW_WIN32_UNICODEPATHS
+      ,
+      wfilename()
+#endif
+      ,
+      jas_file(NULL)
+{
+  if (filename.size() > 0)
+  {
+#ifndef LIBRAW_WIN32_CALLS
+    struct stat st;
+    if (!stat(filename.c_str(), &st))
+      _fsize = st.st_size;
+#else
+    struct _stati64 st;
+    if (!_stati64(filename.c_str(), &st))
+      _fsize = st.st_size;
+#endif
+#ifdef LIBRAW_USE_AUTOPTR
+    std::auto_ptr<std::filebuf> buf(new std::filebuf());
+#else
+    std::unique_ptr<std::filebuf> buf(new std::filebuf());
+#endif
+    buf->open(filename.c_str(), std::ios_base::in | std::ios_base::binary);
+    if (buf->is_open())
+    {
+#ifdef LIBRAW_USE_AUTOPTR
+      f = buf;
+#else
+      f = std::move(buf);
+#endif
+    }
+  }
+}
+#ifdef LIBRAW_WIN32_UNICODEPATHS
+LibRaw_file_datastream::LibRaw_file_datastream(const wchar_t *fname)
+    : filename(), wfilename(fname), jas_file(NULL), _fsize(0)
+{
+  if (wfilename.size() > 0)
+  {
+    struct _stati64 st;
+    if (!_wstati64(wfilename.c_str(), &st))
+      _fsize = st.st_size;
+#ifdef LIBRAW_USE_AUTOPTR
+    std::auto_ptr<std::filebuf> buf(new std::filebuf());
+#else
+    std::unique_ptr<std::filebuf> buf(new std::filebuf());
+#endif
+    buf->open(wfilename.c_str(), std::ios_base::in | std::ios_base::binary);
+    if (buf->is_open())
+    {
+#ifdef LIBRAW_USE_AUTOPTR
+      f = buf;
+#else
+      f = std::move(buf);
+#endif
+	}
+  }
+}
+const wchar_t *LibRaw_file_datastream::wfname()
+{
+  return wfilename.size() > 0 ? wfilename.c_str() : NULL;
+}
+#endif
+
+int LibRaw_file_datastream::valid() { return f.get() ? 1 : 0; }
+
+#define LR_STREAM_CHK()                                                        \
+  do                                                                           \
+  {                                                                            \
+    if (!f.get())                                                              \
+      throw LIBRAW_EXCEPTION_IO_EOF;                                           \
+  } while (0)
+
+int LibRaw_file_datastream::read(void *ptr, size_t size, size_t nmemb)
+{
+/* Visual Studio 2008 marks sgetn as insecure, but VS2010 does not. */
+#if defined(WIN32SECURECALLS) && (_MSC_VER < 1600)
+  LR_STREAM_CHK();
+  return int(f->_Sgetn_s(static_cast<char *>(ptr), nmemb * size, nmemb * size) /
+             (size > 0 ? size : 1));
+#else
+  LR_STREAM_CHK();
+  return int(f->sgetn(static_cast<char *>(ptr), std::streamsize(nmemb * size)) /
+             (size > 0 ? size : 1));
+#endif
+}
+
+int LibRaw_file_datastream::eof()
+{
+  LR_STREAM_CHK();
+  return f->sgetc() == EOF;
+}
+
+int LibRaw_file_datastream::seek(INT64 o, int whence)
+{
+  LR_STREAM_CHK();
+  std::ios_base::seekdir dir;
+  switch (whence)
+  {
+  case SEEK_SET:
+    dir = std::ios_base::beg;
+    break;
+  case SEEK_CUR:
+    dir = std::ios_base::cur;
+    break;
+  case SEEK_END:
+    dir = std::ios_base::end;
+    break;
+  default:
+    dir = std::ios_base::beg;
+  }
+  return f->pubseekoff((long)o, dir) < 0;
+}
+
+INT64 LibRaw_file_datastream::tell()
+{
+  LR_STREAM_CHK();
+  return f->pubseekoff(0, std::ios_base::cur);
+}
+
+char *LibRaw_file_datastream::gets(char *str, int sz)
+{
+  LR_STREAM_CHK();
+  std::istream is(f.get());
+  is.getline(str, sz);
+  if (is.fail())
+    return 0;
+  return str;
+}
+
+int LibRaw_file_datastream::scanf_one(const char *fmt, void *val)
+{
+  LR_STREAM_CHK();
+
+  std::istream is(f.get());
+
+  /* HUGE ASSUMPTION: *fmt is either "%d" or "%f" */
+  if (strcmp(fmt, "%d") == 0)
+  {
+    int d;
+    is >> d;
+    if (is.fail())
+      return EOF;
+    *(static_cast<int *>(val)) = d;
+  }
+  else
+  {
+    float f;
+    is >> f;
+    if (is.fail())
+      return EOF;
+    *(static_cast<float *>(val)) = f;
+  }
+
+  return 1;
+}
+
+const char *LibRaw_file_datastream::fname()
+{
+  return filename.size() > 0 ? filename.c_str() : NULL;
+}
+
+#undef LR_STREAM_CHK
+
+void *LibRaw_file_datastream::make_jas_stream()
+{
+#ifdef NO_JASPER
+  return NULL;
+#else
+#ifdef LIBRAW_WIN32_UNICODEPATHS
+  if (wfname())
+  {
+    jas_file = _wfopen(wfname(), L"rb");
+    return jas_stream_fdopen(fileno(jas_file), "rb");
+  }
+  else
+#endif
+  {
+    return jas_stream_fopen(fname(), "rb");
+  }
+#endif
+}
+
+// == LibRaw_buffer_datastream
+LibRaw_buffer_datastream::LibRaw_buffer_datastream(void *buffer, size_t bsize)
+{
+  buf = (unsigned char *)buffer;
+  streampos = 0;
+  streamsize = bsize;
+}
+
+LibRaw_buffer_datastream::~LibRaw_buffer_datastream() {}
+
+int LibRaw_buffer_datastream::read(void *ptr, size_t sz, size_t nmemb)
+{
+  size_t to_read = sz * nmemb;
+  if (to_read > streamsize - streampos)
+    to_read = streamsize - streampos;
+  if (to_read < 1)
+    return 0;
+  memmove(ptr, buf + streampos, to_read);
+  streampos += to_read;
+  return int((to_read + sz - 1) / (sz > 0 ? sz : 1));
+}
+
+int LibRaw_buffer_datastream::seek(INT64 o, int whence)
+{
+  switch (whence)
+  {
+  case SEEK_SET:
+    if (o < 0)
+      streampos = 0;
+    else if (size_t(o) > streamsize)
+      streampos = streamsize;
+    else
+      streampos = size_t(o);
+    return 0;
+  case SEEK_CUR:
+    if (o < 0)
+    {
+      if (size_t(-o) >= streampos)
+        streampos = 0;
+      else
+        streampos += (size_t)o;
+    }
+    else if (o > 0)
+    {
+      if (o + streampos > streamsize)
+        streampos = streamsize;
+      else
+        streampos += (size_t)o;
+    }
+    return 0;
+  case SEEK_END:
+    if (o > 0)
+      streampos = streamsize;
+    else if (size_t(-o) > streamsize)
+      streampos = 0;
+    else
+      streampos = streamsize + (size_t)o;
+    return 0;
+  default:
+    return 0;
+  }
+}
+
+INT64 LibRaw_buffer_datastream::tell()
+{
+  return INT64(streampos);
+}
+
+char *LibRaw_buffer_datastream::gets(char *s, int sz)
+{
+  unsigned char *psrc, *pdest, *str;
+  str = (unsigned char *)s;
+  psrc = buf + streampos;
+  pdest = str;
+  if(streampos >= streamsize) return NULL;
+  while ((size_t(psrc - buf) < streamsize) && ((pdest - str) < sz))
+  {
+    *pdest = *psrc;
+    if (*psrc == '\n')
+      break;
+    psrc++;
+    pdest++;
+  }
+  if (size_t(psrc - buf) < streamsize)
+    psrc++;
+  if ((pdest - str) < sz)
+    *(++pdest) = 0;
+  streampos = psrc - buf;
+  return s;
+}
+
+int LibRaw_buffer_datastream::scanf_one(const char *fmt, void *val)
+{
+  int scanf_res;
+  if (streampos > streamsize)
+    return 0;
+#ifndef WIN32SECURECALLS
+  scanf_res = sscanf((char *)(buf + streampos), fmt, val);
+#else
+  scanf_res = sscanf_s((char *)(buf + streampos), fmt, val);
+#endif
+  if (scanf_res > 0)
+  {
+    int xcnt = 0;
+    while (streampos < streamsize)
+    {
+      streampos++;
+      xcnt++;
+      if (buf[streampos] == 0 || buf[streampos] == ' ' ||
+          buf[streampos] == '\t' || buf[streampos] == '\n' || xcnt > 24)
+        break;
+    }
+  }
+  return scanf_res;
+}
+
+int LibRaw_buffer_datastream::eof()
+{
+  return streampos >= streamsize;
+}
+int LibRaw_buffer_datastream::valid() { return buf ? 1 : 0; }
+
+void *LibRaw_buffer_datastream::make_jas_stream()
+{
+#ifdef NO_JASPER
+  return NULL;
+#else
+  return jas_stream_memopen((char *)buf + streampos, streamsize - streampos);
+#endif
+}
+
+int LibRaw_buffer_datastream::jpeg_src(void *jpegdata)
+{
+#if defined(NO_JPEG) || !defined(USE_JPEG)
+  return -1;
+#else
+  j_decompress_ptr cinfo = (j_decompress_ptr)jpegdata;
+  jpeg_mem_src(cinfo, (unsigned char *)buf + streampos, streamsize - streampos);
+  return 0;
+#endif
+}
+
+// int LibRaw_buffer_datastream
+
+// == LibRaw_bigfile_datastream
+LibRaw_bigfile_datastream::LibRaw_bigfile_datastream(const char *fname)
+    : filename(fname)
+#ifdef LIBRAW_WIN32_UNICODEPATHS
+      ,
+      wfilename()
+#endif
+{
+  if (filename.size() > 0)
+  {
+#ifndef LIBRAW_WIN32_CALLS
+    struct stat st;
+    if (!stat(filename.c_str(), &st))
+      _fsize = st.st_size;
+#else
+    struct _stati64 st;
+    if (!_stati64(filename.c_str(), &st))
+      _fsize = st.st_size;
+#endif
+
+#ifndef WIN32SECURECALLS
+    f = fopen(fname, "rb");
+#else
+    if (fopen_s(&f, fname, "rb"))
+      f = 0;
+#endif
+  }
+  else
+  {
+    filename = std::string();
+    f = 0;
+  }
+}
+
+#ifdef LIBRAW_WIN32_UNICODEPATHS
+LibRaw_bigfile_datastream::LibRaw_bigfile_datastream(const wchar_t *fname)
+    : filename(), wfilename(fname)
+{
+  if (wfilename.size() > 0)
+  {
+    struct _stati64 st;
+    if (!_wstati64(wfilename.c_str(), &st))
+      _fsize = st.st_size;
+#ifndef WIN32SECURECALLS
+    f = _wfopen(wfilename.c_str(), L"rb");
+#else
+    if (_wfopen_s(&f, fname, L"rb"))
+      f = 0;
+#endif
+  }
+  else
+  {
+    wfilename = std::wstring();
+    f = 0;
+  }
+}
+const wchar_t *LibRaw_bigfile_datastream::wfname()
+{
+  return wfilename.size() > 0 ? wfilename.c_str() : NULL;
+}
+#endif
+
+LibRaw_bigfile_datastream::~LibRaw_bigfile_datastream()
+{
+  if (f)
+    fclose(f);
+}
+int LibRaw_bigfile_datastream::valid() { return f ? 1 : 0; }
+
+#define LR_BF_CHK()                                                            \
+  do                                                                           \
+  {                                                                            \
+    if (!f)                                                                    \
+      throw LIBRAW_EXCEPTION_IO_EOF;                                           \
+  } while (0)
+
+int LibRaw_bigfile_datastream::read(void *ptr, size_t size, size_t nmemb)
+{
+  LR_BF_CHK();
+  return int(fread(ptr, size, nmemb, f));
+}
+
+int LibRaw_bigfile_datastream::eof()
+{
+  LR_BF_CHK();
+  return feof(f);
+}
+
+int LibRaw_bigfile_datastream::seek(INT64 o, int whence)
+{
+  LR_BF_CHK();
+#if defined(_WIN32)
+#ifdef WIN32SECURECALLS
+  return _fseeki64(f, o, whence);
+#else
+  return fseek(f, (long)o, whence);
+#endif
+#else
+  return fseeko(f, o, whence);
+#endif
+}
+
+INT64 LibRaw_bigfile_datastream::tell()
+{
+  LR_BF_CHK();
+#if defined(_WIN32)
+#ifdef WIN32SECURECALLS
+  return _ftelli64(f);
+#else
+  return ftell(f);
+#endif
+#else
+  return ftello(f);
+#endif
+}
+
+char *LibRaw_bigfile_datastream::gets(char *str, int sz)
+{
+  LR_BF_CHK();
+  return fgets(str, sz, f);
+}
+
+int LibRaw_bigfile_datastream::scanf_one(const char *fmt, void *val)
+{
+  LR_BF_CHK();
+  return
+#ifndef WIN32SECURECALLS
+                   fscanf(f, fmt, val)
+#else
+                   fscanf_s(f, fmt, val)
+#endif
+      ;
+}
+
+const char *LibRaw_bigfile_datastream::fname()
+{
+  return filename.size() > 0 ? filename.c_str() : NULL;
+}
+
+
+void *LibRaw_bigfile_datastream::make_jas_stream()
+{
+#ifdef NO_JASPER
+  return NULL;
+#else
+  return jas_stream_fdopen(fileno(f), "rb");
+#endif
+}
+
+
+// == LibRaw_windows_datastream
+#ifdef LIBRAW_WIN32_CALLS
+
+LibRaw_windows_datastream::LibRaw_windows_datastream(const TCHAR *sFile)
+    : LibRaw_buffer_datastream(NULL, 0), hMap_(0), pView_(NULL)
+{
+  HANDLE hFile = CreateFile(sFile, GENERIC_READ, 0, 0, OPEN_EXISTING,
+                            FILE_ATTRIBUTE_NORMAL, 0);
+  if (hFile == INVALID_HANDLE_VALUE)
+    throw std::runtime_error("failed to open the file");
+
+  try
+  {
+    Open(hFile);
+  }
+  catch (...)
+  {
+    CloseHandle(hFile);
+    throw;
+  }
+
+  CloseHandle(hFile); // windows will defer the actual closing of this handle
+                      // until the hMap_ is closed
+  reconstruct_base();
+}
+
+// ctor: construct with a file handle - caller is responsible for closing the
+// file handle
+LibRaw_windows_datastream::LibRaw_windows_datastream(HANDLE hFile)
+    : LibRaw_buffer_datastream(NULL, 0), hMap_(0), pView_(NULL)
+{
+  Open(hFile);
+  reconstruct_base();
+}
+
+// dtor: unmap and close the mapping handle
+LibRaw_windows_datastream::~LibRaw_windows_datastream()
+{
+  if (pView_ != NULL)
+    ::UnmapViewOfFile(pView_);
+
+  if (hMap_ != 0)
+    ::CloseHandle(hMap_);
+}
+
+void LibRaw_windows_datastream::Open(HANDLE hFile)
+{
+  // create a file mapping handle on the file handle
+  hMap_ = ::CreateFileMapping(hFile, 0, PAGE_READONLY, 0, 0, 0);
+  if (hMap_ == NULL)
+    throw std::runtime_error("failed to create file mapping");
+
+  // now map the whole file base view
+  if (!::GetFileSizeEx(hFile, (PLARGE_INTEGER)&cbView_))
+    throw std::runtime_error("failed to get the file size");
+
+  pView_ = ::MapViewOfFile(hMap_, FILE_MAP_READ, 0, 0, (size_t)cbView_);
+  if (pView_ == NULL)
+    throw std::runtime_error("failed to map the file");
+}
+
+#endif
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/metadata/adobepano.cpp libkdcraw/libkdcraw/libraw/src/metadata/adobepano.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/metadata/adobepano.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/metadata/adobepano.cpp	2022-11-07 07:46:31.734795008 +0300
@@ -0,0 +1,149 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+void LibRaw::parseAdobePanoMakernote()
+{
+  uchar *PrivateMknBuf;
+  unsigned posPrivateMknBuf;
+  unsigned PrivateMknLength;
+  unsigned PrivateOrder;
+  unsigned PrivateEntries, PrivateTagID, PrivateTagType, PrivateTagCount;
+  unsigned PrivateTagBytes;
+  int truncated;
+
+#define CHECKSPACE(s)                                                          \
+  if (posPrivateMknBuf + (s) > PrivateMknLength)                               \
+  {                                                                            \
+    free(PrivateMknBuf);                                                       \
+    return;                                                                    \
+  }
+
+  order = 0x4d4d;
+  truncated = 0;
+  PrivateMknLength = get4();
+
+  if ((PrivateMknLength > 4) && (PrivateMknLength < 10240000) &&
+      (PrivateMknBuf = (uchar *)malloc(PrivateMknLength + 1024)))
+  { // 1024b for safety
+    fread(PrivateMknBuf, PrivateMknLength, 1, ifp);
+    PrivateOrder = sget2(PrivateMknBuf);
+    PrivateEntries = sget2(PrivateMknBuf + 2);
+    if ((PrivateEntries > 1000) ||
+        ((PrivateOrder != 0x4d4d) && (PrivateOrder != 0x4949)))
+    {
+      free(PrivateMknBuf);
+      return;
+    }
+    posPrivateMknBuf = 4;
+    while (PrivateEntries--)
+    {
+      order = 0x4d4d;
+      CHECKSPACE(8);
+      PrivateTagID = sget2(PrivateMknBuf + posPrivateMknBuf);
+      PrivateTagType = sget2(PrivateMknBuf + posPrivateMknBuf + 2);
+      PrivateTagCount = sget4(PrivateMknBuf + posPrivateMknBuf + 4);
+      posPrivateMknBuf += 8;
+      order = PrivateOrder;
+
+      if (truncated && !PrivateTagCount)
+        continue;
+
+      PrivateTagBytes = PrivateTagCount *
+          tagtype_dataunit_bytes[(PrivateTagType <= LIBRAW_EXIFTAG_TYPE_IFD8) ? PrivateTagType : 0];
+      if (PrivateTagID == 0x0002)
+      {
+        posPrivateMknBuf += 2;
+        CHECKSPACE(2);
+        if (sget2(PrivateMknBuf + posPrivateMknBuf))
+        {
+          truncated = 1;
+        }
+        else
+        {
+          posPrivateMknBuf += 2;
+        }
+      }
+      else if (PrivateTagID == 0x0013)
+      {
+        ushort nWB, cnt, tWB;
+        CHECKSPACE(2);
+        nWB = sget2(PrivateMknBuf + posPrivateMknBuf);
+        posPrivateMknBuf += 2;
+        if (nWB > 0x100)
+          break;
+        for (cnt = 0; cnt < nWB; cnt++)
+        {
+          CHECKSPACE(2);
+          tWB = sget2(PrivateMknBuf + posPrivateMknBuf);
+          if (tWB < 0x100)
+          {
+            CHECKSPACE(4);
+            icWBC[tWB][0] = sget2(PrivateMknBuf + posPrivateMknBuf + 2);
+            icWBC[tWB][2] = sget2(PrivateMknBuf + posPrivateMknBuf + 4);
+            icWBC[tWB][1] = icWBC[tWB][3] = 0x100;
+          }
+          posPrivateMknBuf += 6;
+        }
+      }
+      else if (PrivateTagID == 0x0027)
+      {
+        ushort nWB, cnt, tWB;
+        CHECKSPACE(2);
+        nWB = sget2(PrivateMknBuf + posPrivateMknBuf);
+        posPrivateMknBuf += 2;
+        if (nWB > 0x100)
+          break;
+        for (cnt = 0; cnt < nWB; cnt++)
+        {
+          CHECKSPACE(2);
+          tWB = sget2(PrivateMknBuf + posPrivateMknBuf);
+          if (tWB < 0x100)
+          {
+            CHECKSPACE(6);
+            icWBC[tWB][0] = sget2(PrivateMknBuf + posPrivateMknBuf + 2);
+            icWBC[tWB][1] = icWBC[tWB][3] =
+                sget2(PrivateMknBuf + posPrivateMknBuf + 4);
+            icWBC[tWB][2] = sget2(PrivateMknBuf + posPrivateMknBuf + 6);
+          }
+          posPrivateMknBuf += 8;
+        }
+      }
+      else if (PrivateTagID == 0x0121)
+      {
+        CHECKSPACE(4);
+        imPana.Multishot = sget4(PrivateMknBuf + posPrivateMknBuf);
+        posPrivateMknBuf += 4;
+      }
+      else
+      {
+        if (PrivateTagBytes > 4)
+          posPrivateMknBuf += PrivateTagBytes;
+        else if (!truncated)
+          posPrivateMknBuf += 4;
+        else
+        {
+          if (PrivateTagBytes <= 2)
+            posPrivateMknBuf += 2;
+          else
+            posPrivateMknBuf += 4;
+        }
+      }
+    }
+    free(PrivateMknBuf);
+  }
+#undef CHECKSPACE
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/metadata/canon.cpp libkdcraw/libkdcraw/libraw/src/metadata/canon.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/metadata/canon.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/metadata/canon.cpp	2022-11-07 07:46:31.734795008 +0300
@@ -0,0 +1,1098 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+#include "../../internal/dcraw_defs.h"
+#include "../../internal/libraw_cameraids.h"
+
+float LibRaw::_CanonConvertAperture(ushort in)
+{
+  if ((in == (ushort)0xffe0) || (in == (ushort)0x7fff))
+    return 0.0f;
+  return LibRaw::libraw_powf64l(2.0, in / 64.0);
+}
+
+static float _CanonConvertEV(short in)
+{
+  short EV, Sign, Frac;
+  float Frac_f;
+  EV = in;
+  if (EV < 0)
+  {
+    EV = -EV;
+    Sign = -1;
+  }
+  else
+  {
+    Sign = 1;
+  }
+  Frac = EV & 0x1f;
+  EV -= Frac; // remove fraction
+
+  if (Frac == 0x0c)
+  { // convert 1/3 and 2/3 codes
+    Frac_f = 32.0f / 3.0f;
+  }
+  else if (Frac == 0x14)
+  {
+    Frac_f = 64.0f / 3.0f;
+  }
+  else
+    Frac_f = (float)Frac;
+
+  return ((float)Sign * ((float)EV + Frac_f)) / 32.0f;
+}
+
+void LibRaw::setCanonBodyFeatures(unsigned long long id)
+{
+
+  ilm.CamID = id;
+  if ((id == CanonID_EOS_1D)           ||
+      (id == CanonID_EOS_1D_Mark_II)   ||
+      (id == CanonID_EOS_1D_Mark_II_N) ||
+      (id == CanonID_EOS_1D_Mark_III)  ||
+      (id == CanonID_EOS_1D_Mark_IV))
+  {
+    ilm.CameraFormat = LIBRAW_FORMAT_APSH;
+    ilm.CameraMount = LIBRAW_MOUNT_Canon_EF;
+  }
+  else if ((id == CanonID_EOS_1DS)           ||
+           (id == CanonID_EOS_1Ds_Mark_II)   ||
+           (id == CanonID_EOS_1Ds_Mark_III)  ||
+           (id == CanonID_EOS_1D_X)          ||
+           (id == CanonID_EOS_1D_X_Mark_II)  ||
+           (id == CanonID_EOS_1D_X_Mark_III) ||
+           (id == CanonID_EOS_1D_C)          ||
+           (id == CanonID_EOS_5D)            ||
+           (id == CanonID_EOS_5D_Mark_II)    ||
+           (id == CanonID_EOS_5D_Mark_III)   ||
+           (id == CanonID_EOS_5D_Mark_IV)    ||
+           (id == CanonID_EOS_5DS)           ||
+           (id == CanonID_EOS_5DS_R)         ||
+           (id == CanonID_EOS_6D)            ||
+           (id == CanonID_EOS_6D_Mark_II))
+  {
+    ilm.CameraFormat = LIBRAW_FORMAT_FF;
+    ilm.CameraMount = LIBRAW_MOUNT_Canon_EF;
+  }
+  else if ((id == CanonID_EOS_M)         ||
+           (id == CanonID_EOS_M2)        ||
+           (id == CanonID_EOS_M3)        ||
+           (id == CanonID_EOS_M10)       ||
+           (id == CanonID_EOS_M5)        ||
+           (id == CanonID_EOS_M50)       ||
+           (id == CanonID_EOS_M6)        ||
+           (id == CanonID_EOS_M100)      ||
+           (id == CanonID_EOS_M6_Mark_II))
+  {
+    ilm.CameraFormat = LIBRAW_FORMAT_APSC;
+    ilm.CameraMount = LIBRAW_MOUNT_Canon_EF_M;
+  }
+  else if ((id == CanonID_EOS_R) ||
+           (id == CanonID_EOS_RP))
+  {
+    ilm.CameraFormat = LIBRAW_FORMAT_FF;
+    ilm.CameraMount = LIBRAW_MOUNT_Canon_RF;
+  }
+  else if ((id == CanonID_EOS_D30) ||
+           (id == CanonID_EOS_D60) ||
+           (id > 0x80000000ULL))
+  {
+    ilm.CameraFormat = LIBRAW_FORMAT_APSC;
+    ilm.CameraMount = LIBRAW_MOUNT_Canon_EF;
+  }
+}
+
+void LibRaw::processCanonCameraInfo(unsigned long long id, uchar *CameraInfo,
+                                    unsigned maxlen, unsigned type, unsigned dng_writer)
+{
+  ushort iCanonLensID = 0, iCanonMaxFocal = 0, iCanonMinFocal = 0,
+         iCanonLens = 0, iCanonCurFocal = 0, iCanonFocalType = 0;
+
+  if (maxlen < 16)
+    return; // too short
+
+ if (tagtypeIs(LIBRAW_EXIFTAG_TYPE_UNDEFINED) &&
+     (sget2(CameraInfo) == 0xaaaa) && (dng_writer == nonDNG)) { // CameraOrientation
+    int c, i;
+    for (c = i = 2; (ushort)c != 0xbbbb && i < (int)maxlen; i++)
+      c = c << 8 | CameraInfo[i];
+    while (i < int(maxlen - 5))
+      if ((sget4(CameraInfo+i) == 257) && ((c = CameraInfo[i+8]) < 3)) {
+        imCanon.MakernotesFlip = "065"[c] - '0';
+        break;
+      } else i+=4;
+  }
+
+  CameraInfo[0] = 0;
+  CameraInfo[1] = 0;
+  if (tagtypeIs(LIBRAW_EXIFTAG_TYPE_LONG)) {
+    if ((maxlen == 94)  || (maxlen == 138) || (maxlen == 148) ||
+        (maxlen == 156) || (maxlen == 162) || (maxlen == 167) ||
+        (maxlen == 171) || (maxlen == 264) || (maxlen > 400))
+      imCommon.CameraTemperature = sget4(CameraInfo + ((maxlen - 3) << 2));
+    else if (maxlen == 72)
+      imCommon.CameraTemperature = sget4(CameraInfo + ((maxlen - 1) << 2));
+    else if ((maxlen == 85) || (maxlen == 93))
+      imCommon.CameraTemperature = sget4(CameraInfo + ((maxlen - 2) << 2));
+    else if ((maxlen == 96) || (maxlen == 104))
+      imCommon.CameraTemperature = sget4(CameraInfo + ((maxlen - 4) << 2));
+  }
+
+  switch (id)
+  {
+  case CanonID_EOS_1D:
+  case CanonID_EOS_1DS:
+    iCanonCurFocal = 10;
+    iCanonLensID = 13;
+    iCanonMinFocal = 14;
+    iCanonMaxFocal = 16;
+    if (!ilm.CurFocal)
+      ilm.CurFocal = sget2(CameraInfo + iCanonCurFocal);
+    if (!ilm.MinFocal)
+      ilm.MinFocal = sget2(CameraInfo + iCanonMinFocal);
+    if (!ilm.MaxFocal)
+      ilm.MaxFocal = sget2(CameraInfo + iCanonMaxFocal);
+    imCommon.CameraTemperature = 0.0f;
+    break;
+  case CanonID_EOS_1D_Mark_II:
+  case CanonID_EOS_1Ds_Mark_II:
+    iCanonCurFocal = 9;
+    iCanonLensID = 12;
+    iCanonMinFocal = 17;
+    iCanonMaxFocal = 19;
+    iCanonFocalType = 45;
+    break;
+  case CanonID_EOS_1D_Mark_II_N:
+    iCanonCurFocal = 9;
+    iCanonLensID = 12;
+    iCanonMinFocal = 17;
+    iCanonMaxFocal = 19;
+    break;
+  case CanonID_EOS_1D_Mark_III:
+  case CanonID_EOS_1Ds_Mark_III:
+    iCanonCurFocal = 29;
+    iCanonLensID = 273;
+    iCanonMinFocal = 275;
+    iCanonMaxFocal = 277;
+    break;
+  case CanonID_EOS_1D_Mark_IV:
+    iCanonCurFocal = 30;
+    iCanonLensID = 335;
+    iCanonMinFocal = 337;
+    iCanonMaxFocal = 339;
+    break;
+  case CanonID_EOS_1D_X:
+    iCanonCurFocal = 35;
+    iCanonLensID = 423;
+    iCanonMinFocal = 425;
+    iCanonMaxFocal = 427;
+    break;
+  case CanonID_EOS_5D:
+    iCanonCurFocal = 40;
+    if (!sget2Rev(CameraInfo + 12))
+      iCanonLensID = 151;
+    else
+      iCanonLensID = 12;
+    iCanonMinFocal = 147;
+    iCanonMaxFocal = 149;
+    break;
+  case CanonID_EOS_5D_Mark_II:
+    iCanonCurFocal = 30;
+    iCanonLensID = 230;
+    iCanonMinFocal = 232;
+    iCanonMaxFocal = 234;
+    break;
+  case CanonID_EOS_5D_Mark_III:
+    iCanonCurFocal = 35;
+    iCanonLensID = 339;
+    iCanonMinFocal = 341;
+    iCanonMaxFocal = 343;
+    break;
+  case CanonID_EOS_6D:
+    iCanonCurFocal = 35;
+    iCanonLensID = 353;
+    iCanonMinFocal = 355;
+    iCanonMaxFocal = 357;
+    break;
+  case CanonID_EOS_7D:
+    iCanonCurFocal = 30;
+    iCanonLensID = 274;
+    iCanonMinFocal = 276;
+    iCanonMaxFocal = 278;
+    break;
+  case CanonID_EOS_40D:
+    iCanonCurFocal = 29;
+    iCanonLensID = 214;
+    iCanonMinFocal = 216;
+    iCanonMaxFocal = 218;
+    iCanonLens = 2347;
+    break;
+  case CanonID_EOS_50D:
+    iCanonCurFocal = 30;
+    iCanonLensID = 234;
+    iCanonMinFocal = 236;
+    iCanonMaxFocal = 238;
+    break;
+  case CanonID_EOS_60D:
+    iCanonCurFocal = 30;
+    iCanonLensID = 232;
+    iCanonMinFocal = 234;
+    iCanonMaxFocal = 236;
+    break;
+  case CanonID_EOS_70D:
+    iCanonCurFocal = 35;
+    iCanonLensID = 358;
+    iCanonMinFocal = 360;
+    iCanonMaxFocal = 362;
+    break;
+  case CanonID_EOS_450D:
+    iCanonCurFocal = 29;
+    iCanonLensID = 222;
+    iCanonLens = 2355;
+    break;
+  case CanonID_EOS_500D:
+    iCanonCurFocal = 30;
+    iCanonLensID = 246;
+    iCanonMinFocal = 248;
+    iCanonMaxFocal = 250;
+    break;
+  case CanonID_EOS_550D:
+    iCanonCurFocal = 30;
+    iCanonLensID = 255;
+    iCanonMinFocal = 257;
+    iCanonMaxFocal = 259;
+    break;
+  case CanonID_EOS_600D:
+  case CanonID_EOS_1100D:
+    iCanonCurFocal = 30;
+    iCanonLensID = 234;
+    iCanonMinFocal = 236;
+    iCanonMaxFocal = 238;
+    break;
+  case CanonID_EOS_650D:
+  case CanonID_EOS_700D:
+    iCanonCurFocal = 35;
+    iCanonLensID = 295;
+    iCanonMinFocal = 297;
+    iCanonMaxFocal = 299;
+    break;
+  case CanonID_EOS_1000D:
+    iCanonCurFocal = 29;
+    iCanonLensID = 226;
+    iCanonMinFocal = 228;
+    iCanonMaxFocal = 230;
+    iCanonLens = 2359;
+    break;
+  }
+  if (iCanonFocalType)
+  {
+    if (iCanonFocalType >= maxlen)
+      return; // broken;
+    ilm.FocalType = CameraInfo[iCanonFocalType];
+    if (!ilm.FocalType) // zero means 'prime' here, replacing with standard '1'
+      ilm.FocalType = LIBRAW_FT_PRIME_LENS;
+  }
+  if (!ilm.CurFocal)
+  {
+    if (iCanonCurFocal >= maxlen)
+      return; // broken;
+    ilm.CurFocal = sget2Rev(CameraInfo + iCanonCurFocal);
+  }
+  if (!ilm.LensID)
+  {
+    if (iCanonLensID >= maxlen)
+      return; // broken;
+    ilm.LensID = sget2Rev(CameraInfo + iCanonLensID);
+  }
+  if (!ilm.MinFocal)
+  {
+    if (iCanonMinFocal >= maxlen)
+      return; // broken;
+    ilm.MinFocal = sget2Rev(CameraInfo + iCanonMinFocal);
+  }
+  if (!ilm.MaxFocal)
+  {
+    if (iCanonMaxFocal >= maxlen)
+      return; // broken;
+    ilm.MaxFocal = sget2Rev(CameraInfo + iCanonMaxFocal);
+  }
+  if (!ilm.Lens[0] && iCanonLens)
+  {
+    if (iCanonLens + 64 >= (int)maxlen) // broken;
+      return;
+
+    char *pl = (char *)CameraInfo + iCanonLens;
+    if (!strncmp(pl, "EF-S", 4))
+    {
+      memcpy(ilm.Lens, pl, 4);
+      ilm.Lens[4] = ' ';
+      memcpy(ilm.LensFeatures_pre, pl, 4);
+      ilm.LensMount = LIBRAW_MOUNT_Canon_EF_S;
+      ilm.LensFormat = LIBRAW_FORMAT_APSC;
+      memcpy(ilm.Lens + 5, pl + 4, 60);
+    }
+    else if (!strncmp(pl, "EF-M", 4))
+    {
+      memcpy(ilm.Lens, pl, 4);
+      ilm.Lens[4] = ' ';
+      memcpy(ilm.LensFeatures_pre, pl, 4);
+      ilm.LensMount = LIBRAW_MOUNT_Canon_EF_M;
+      ilm.LensFormat = LIBRAW_FORMAT_APSC;
+      memcpy(ilm.Lens + 5, pl + 4, 60);
+    }
+    else if (!strncmp(pl, "EF", 2))
+    {
+      memcpy(ilm.Lens, pl, 2);
+      ilm.Lens[2] = ' ';
+      memcpy(ilm.LensFeatures_pre, pl, 2);
+      ilm.LensMount = LIBRAW_MOUNT_Canon_EF;
+      ilm.LensFormat = LIBRAW_FORMAT_FF;
+      memcpy(ilm.Lens + 3, pl + 2, 62);
+    }
+    else if (!strncmp(ilm.Lens, "CN-E", 4))
+    {
+      memmove(ilm.Lens + 5, ilm.Lens + 4, 60);
+      ilm.Lens[4] = ' ';
+      memcpy(ilm.LensFeatures_pre, ilm.Lens, 4);
+      ilm.LensMount = LIBRAW_MOUNT_Canon_EF;
+      ilm.LensFormat = LIBRAW_FORMAT_FF;
+    }
+    else if (!strncmp(pl, "TS-E", 4))
+    {
+      memcpy(ilm.Lens, pl, 4);
+      ilm.Lens[4] = ' ';
+      memcpy(ilm.LensFeatures_pre, pl, 4);
+      ilm.LensMount = LIBRAW_MOUNT_Canon_EF;
+      ilm.LensFormat = LIBRAW_FORMAT_FF;
+      memcpy(ilm.Lens + 5, pl + 4, 60);
+    }
+    else if (!strncmp(pl, "MP-E", 4))
+    {
+      memcpy(ilm.Lens, pl, 4);
+      ilm.Lens[4] = ' ';
+      memcpy(ilm.LensFeatures_pre, pl, 4);
+      ilm.LensMount = LIBRAW_MOUNT_Canon_EF;
+      ilm.LensFormat = LIBRAW_FORMAT_FF;
+      memcpy(ilm.Lens + 5, pl + 4, 60);
+    }
+    else // non-Canon lens
+      memcpy(ilm.Lens, pl, 64);
+  }
+  return;
+}
+
+void LibRaw::Canon_CameraSettings(unsigned len)
+{
+  fseek(ifp, 10, SEEK_CUR);
+  imgdata.shootinginfo.DriveMode = get2(); // 5
+  get2();
+  imgdata.shootinginfo.FocusMode = get2(); // 7
+  imCanon.RecordMode = (get2(), get2()); // 9, format
+  fseek(ifp, 14, SEEK_CUR);
+  imgdata.shootinginfo.MeteringMode = get2(); // 17
+  get2();
+  imgdata.shootinginfo.AFPoint = get2();      // 19
+  imgdata.shootinginfo.ExposureMode = get2(); // 20
+  get2();
+  ilm.LensID = get2();          // 22
+  ilm.MaxFocal = get2();        // 23
+  ilm.MinFocal = get2();        // 24
+  ilm.FocalUnits = get2(); // 25
+  if (ilm.FocalUnits > 1)
+  {
+    ilm.MaxFocal /= (float)ilm.FocalUnits;
+    ilm.MinFocal /= (float)ilm.FocalUnits;
+  }
+  ilm.MaxAp = _CanonConvertAperture(get2()); // 26
+  ilm.MinAp = _CanonConvertAperture(get2()); // 27
+  if (len >= 36)
+  {
+    fseek(ifp, 12, SEEK_CUR);
+    imgdata.shootinginfo.ImageStabilization = get2(); // 34
+  }
+  else
+    return;
+  if (len >= 48)
+  {
+    fseek(ifp, 22, SEEK_CUR);
+    imCanon.SRAWQuality = get2(); // 46
+  }
+}
+
+void LibRaw::Canon_WBpresets(int skip1, int skip2)
+{
+  int c;
+  FORC4 imgdata.color.WB_Coeffs[LIBRAW_WBI_Daylight][RGGB_2_RGBG(c)] = get2();
+
+  if (skip1)
+    fseek(ifp, skip1, SEEK_CUR);
+  FORC4 imgdata.color.WB_Coeffs[LIBRAW_WBI_Shade][RGGB_2_RGBG(c)] = get2();
+
+  if (skip1)
+    fseek(ifp, skip1, SEEK_CUR);
+  FORC4 imgdata.color.WB_Coeffs[LIBRAW_WBI_Cloudy][RGGB_2_RGBG(c)] = get2();
+
+  if (skip1)
+    fseek(ifp, skip1, SEEK_CUR);
+  FORC4 imgdata.color.WB_Coeffs[LIBRAW_WBI_Tungsten][RGGB_2_RGBG(c)] = get2();
+
+  if (skip1)
+    fseek(ifp, skip1, SEEK_CUR);
+  FORC4 imgdata.color.WB_Coeffs[LIBRAW_WBI_FL_W][RGGB_2_RGBG(c)] = get2();
+
+  if (skip2)
+    fseek(ifp, skip2, SEEK_CUR);
+  FORC4 imgdata.color.WB_Coeffs[LIBRAW_WBI_Flash][RGGB_2_RGBG(c)] = get2();
+
+  return;
+}
+
+void LibRaw::Canon_WBCTpresets(short WBCTversion)
+{
+
+  int i;
+  float norm;
+
+  if (WBCTversion == 0)
+  { // tint, as shot R, as shot B, CСT
+    for (i = 0; i < 15; i++)
+    {
+      icWBCCTC[i][2] = icWBCCTC[i][4] = 1.0f;
+      fseek(ifp, 2, SEEK_CUR);
+      icWBCCTC[i][1] = 1024.0f / fMAX(get2(), 1.f);
+      icWBCCTC[i][3] = 1024.0f / fMAX(get2(), 1.f);
+      icWBCCTC[i][0] = get2();
+    }
+  }
+  else if (WBCTversion == 1)
+  { // as shot R, as shot B, tint, CСT
+    for (i = 0; i < 15; i++)
+    {
+      icWBCCTC[i][2] = icWBCCTC[i][4] = 1.0f;
+      icWBCCTC[i][1] = 1024.0f / fMAX(get2(), 1.f);
+      icWBCCTC[i][3] = 1024.0f / fMAX(get2(), 1.f);
+      fseek(ifp, 2, SEEK_CUR);
+      icWBCCTC[i][0] = get2();
+    }
+  }
+  else if (WBCTversion == 2)
+  { // tint, offset, as shot R, as shot B, CСT
+    if ((unique_id == CanonID_EOS_M3)  ||
+        (unique_id == CanonID_EOS_M10) ||
+        (imCanon.ColorDataSubVer == 0xfffc))
+    {
+      for (i = 0; i < 15; i++)
+      {
+        fseek(ifp, 4, SEEK_CUR);
+        icWBCCTC[i][2] = icWBCCTC[i][4] =
+            1.0f;
+        icWBCCTC[i][1] = 1024.0f / fMAX(1.f, get2());
+        icWBCCTC[i][3] = 1024.0f / fMAX(1.f, get2());
+        icWBCCTC[i][0] = get2();
+      }
+    }
+    else if (imCanon.ColorDataSubVer == 0xfffd)
+    {
+      for (i = 0; i < 15; i++)
+      {
+        fseek(ifp, 2, SEEK_CUR);
+        norm = (signed short)get2();
+        norm = 512.0f + norm / 8.0f;
+        icWBCCTC[i][2] = icWBCCTC[i][4] =
+            1.0f;
+        icWBCCTC[i][1] = (float)get2();
+        if (norm > 0.001f)
+          icWBCCTC[i][1] /= norm;
+        icWBCCTC[i][3] = (float)get2();
+        if (norm > 0.001f)
+          icWBCCTC[i][3] /= norm;
+        icWBCCTC[i][0] = get2();
+      }
+    }
+  }
+  return;
+}
+
+void LibRaw::parseCanonMakernotes(unsigned tag, unsigned type, unsigned len, unsigned dng_writer)
+{
+  int c;
+  unsigned i;
+
+  if (tag == 0x0001) {
+    Canon_CameraSettings(len);
+
+  } else if (tag == 0x0002) { // focal length
+    ilm.FocalType = get2();
+    ilm.CurFocal = get2();
+    if (ilm.FocalUnits > 1) {
+      ilm.CurFocal /= (float)ilm.FocalUnits;
+    }
+
+  } else if (tag == 0x0004) { // subdir, ShotInfo
+    short tempAp;
+    if (dng_writer == nonDNG) {
+      if ((i = (get4(), get2())) != 0x7fff &&
+          (!iso_speed || iso_speed == 65535)) {
+        iso_speed = 50 * libraw_powf64l(2.0, i / 32.0 - 4);
+      }
+      get4();
+      if (((i = get2()) != 0xffff) && !shutter) {
+        shutter = libraw_powf64l(2.0, (short)i / -32.0);
+      }
+      imCanon.wbi = (get2(), get2());
+      shot_order = (get2(), get2());
+      fseek(ifp, 4, SEEK_CUR);
+    } else
+      fseek(ifp, 24, SEEK_CUR);
+    tempAp = get2();
+    if (tempAp != 0)
+      imCommon.CameraTemperature = (float)(tempAp - 128);
+    tempAp = get2();
+    if (tempAp != -1)
+      imCommon.FlashGN = ((float)tempAp) / 32;
+    get2();
+
+    imCommon.FlashEC = _CanonConvertEV((signed short)get2());
+    fseek(ifp, 8 - 32, SEEK_CUR);
+    if ((tempAp = get2()) != 0x7fff)
+      ilm.CurAp = _CanonConvertAperture(tempAp);
+    if (ilm.CurAp < 0.7f) {
+      fseek(ifp, 32, SEEK_CUR);
+      ilm.CurAp = _CanonConvertAperture(get2());
+    }
+    if (!aperture)
+      aperture = ilm.CurAp;
+
+  } else if ((tag == 0x0007) && (dng_writer == nonDNG)) {
+    fgets(model2, 64, ifp);
+
+  } else if ((tag == 0x0008) && (dng_writer == nonDNG)) {
+    shot_order = get4();
+
+  } else if ((tag == 0x0009)  && (dng_writer == nonDNG)) {
+    fread(artist, 64, 1, ifp);
+
+  } else if (tag == 0x000c) {
+    unsigned tS = get4();
+    sprintf(imgdata.shootinginfo.BodySerial, "%d", tS);
+
+  } else if ((tag == 0x0029) && (dng_writer == nonDNG)) { // PowerShot G9
+    int Got_AsShotWB = 0;
+    fseek(ifp, 8, SEEK_CUR);
+    for (int linenum = 0; linenum < Canon_G9_linenums_2_StdWBi.size(); linenum++) {
+      if (Canon_G9_linenums_2_StdWBi[linenum] != LIBRAW_WBI_Unknown ) {
+        FORC4 icWBC[Canon_G9_linenums_2_StdWBi[linenum]][GRBG_2_RGBG(c)] = get4();
+        if (Canon_wbi2std[imCanon.wbi] == Canon_G9_linenums_2_StdWBi[linenum]) {
+          FORC4 cam_mul[c] = icWBC[Canon_G9_linenums_2_StdWBi[linenum]][c];
+          Got_AsShotWB = 1;
+        }
+      }
+      fseek(ifp, 16, SEEK_CUR);
+    }
+    if (!Got_AsShotWB)
+      FORC4 cam_mul[c] = icWBC[LIBRAW_WBI_Auto][c];
+
+  } else if ((tag == 0x0081) && (dng_writer == nonDNG)) { // EOS-1D, EOS-1DS
+    data_offset = get4();
+    fseek(ifp, data_offset + 41, SEEK_SET);
+    raw_height = get2() * 2;
+    raw_width = get2();
+    filters = 0x61616161;
+
+  } else if (tag == 0x0093) {
+    if (!imCanon.RF_lensID) {
+      fseek(ifp, 0x03d<<1, SEEK_CUR);
+      imCanon.RF_lensID = get2();
+    }
+
+  } else if (tag == 0x0095 && !ilm.Lens[0])
+  { // lens model tag
+    fread(ilm.Lens, 64, 1, ifp);
+    if (!strncmp(ilm.Lens, "EF-S", 4))
+    {
+      memmove(ilm.Lens + 5, ilm.Lens + 4, 60);
+      ilm.Lens[4] = ' ';
+      memcpy(ilm.LensFeatures_pre, ilm.Lens, 4);
+      ilm.LensMount = LIBRAW_MOUNT_Canon_EF_S;
+      ilm.LensFormat = LIBRAW_FORMAT_APSC;
+    }
+    else if (!strncmp(ilm.Lens, "EF-M", 4))
+    {
+      memmove(ilm.Lens + 5, ilm.Lens + 4, 60);
+      ilm.Lens[4] = ' ';
+      memcpy(ilm.LensFeatures_pre, ilm.Lens, 4);
+      ilm.LensMount = LIBRAW_MOUNT_Canon_EF_M;
+      ilm.LensFormat = LIBRAW_FORMAT_APSC;
+    }
+    else if (!strncmp(ilm.Lens, "EF", 2))
+    {
+      memmove(ilm.Lens + 3, ilm.Lens + 2, 62);
+      ilm.Lens[2] = ' ';
+      memcpy(ilm.LensFeatures_pre, ilm.Lens, 2);
+      ilm.LensMount = LIBRAW_MOUNT_Canon_EF;
+      ilm.LensFormat = LIBRAW_FORMAT_FF;
+    }
+    else if (!strncmp(ilm.Lens, "CN-E", 4))
+    {
+      memmove(ilm.Lens + 5, ilm.Lens + 4, 60);
+      ilm.Lens[4] = ' ';
+      memcpy(ilm.LensFeatures_pre, ilm.Lens, 4);
+      ilm.LensMount = LIBRAW_MOUNT_Canon_EF;
+      ilm.LensFormat = LIBRAW_FORMAT_FF;
+    }
+    else if (!strncmp(ilm.Lens, "TS-E", 4))
+    {
+      memmove(ilm.Lens + 5, ilm.Lens + 4, 60);
+      ilm.Lens[4] = ' ';
+      memcpy(ilm.LensFeatures_pre, ilm.Lens, 4);
+      ilm.LensMount = LIBRAW_MOUNT_Canon_EF;
+      ilm.LensFormat = LIBRAW_FORMAT_FF;
+    }
+    else if (!strncmp(ilm.Lens, "MP-E", 4))
+    {
+      memmove(ilm.Lens + 5, ilm.Lens + 4, 60);
+      ilm.Lens[4] = ' ';
+      memcpy(ilm.LensFeatures_pre, ilm.Lens, 4);
+      ilm.LensMount = LIBRAW_MOUNT_Canon_EF;
+      ilm.LensFormat = LIBRAW_FORMAT_FF;
+    }
+    else if (!strncmp(ilm.Lens, "RF", 2))
+    {
+      memmove(ilm.Lens + 3, ilm.Lens + 2, 62);
+      ilm.Lens[2] = ' ';
+      memcpy(ilm.LensFeatures_pre, ilm.Lens, 2);
+      ilm.LensMount = LIBRAW_MOUNT_Canon_RF;
+      ilm.LensFormat = LIBRAW_FORMAT_FF;
+    }
+  }
+  else if (tag == 0x009a)
+  { // AspectInfo
+    i = get4();
+    switch (i)
+    {
+    case 0:
+    case 12: /* APS-H crop */
+    case 13: /* APS-C crop */
+      imgdata.sizes.raw_inset_crop.aspect = LIBRAW_IMAGE_ASPECT_3to2;
+      break;
+    case 1:
+      imgdata.sizes.raw_inset_crop.aspect = LIBRAW_IMAGE_ASPECT_1to1;
+      break;
+    case 2:
+      imgdata.sizes.raw_inset_crop.aspect = LIBRAW_IMAGE_ASPECT_4to3;
+      break;
+    case 7:
+      imgdata.sizes.raw_inset_crop.aspect = LIBRAW_IMAGE_ASPECT_16to9;
+      break;
+    case 8:
+      imgdata.sizes.raw_inset_crop.aspect = LIBRAW_IMAGE_ASPECT_5to4;
+      break;
+    default:
+      imgdata.sizes.raw_inset_crop.aspect = LIBRAW_IMAGE_ASPECT_OTHER;
+      break;
+    }
+    imgdata.sizes.raw_inset_crop.cwidth = get4();
+    imgdata.sizes.raw_inset_crop.cheight = get4();
+    imgdata.sizes.raw_inset_crop.cleft = get4();
+    imgdata.sizes.raw_inset_crop.ctop = get4();
+
+  } else if ((tag == 0x00a4) && (dng_writer == nonDNG)) { // EOS-1D, EOS-1DS
+    fseek(ifp, imCanon.wbi * 48, SEEK_CUR);
+    FORC3 cam_mul[c] = get2();
+
+  } else if (tag == 0x00a9) {
+    long int save1 = ftell(ifp);
+    fseek(ifp, (0x1 << 1), SEEK_CUR);
+    FORC4 imgdata.color.WB_Coeffs[LIBRAW_WBI_Auto][RGGB_2_RGBG(c)] = get2();
+    Canon_WBpresets(0, 0);
+    fseek(ifp, save1, SEEK_SET);
+  }
+  else if (tag == 0x00b4)
+  {
+    switch (get2()) {
+    case 1:
+      imCommon.ColorSpace = LIBRAW_COLORSPACE_sRGB;
+      break;
+    case 2:
+      imCommon.ColorSpace = LIBRAW_COLORSPACE_AdobeRGB;
+      break;
+    default:
+      imCommon.ColorSpace = LIBRAW_COLORSPACE_Unknown;
+      break;
+    }
+  }
+  else if (tag == 0x00e0)
+  { // SensorInfo
+    imCanon.SensorWidth = (get2(), get2());
+    imCanon.SensorHeight = get2();
+    imCanon.SensorLeftBorder = (get2(), get2(), get2());
+    imCanon.SensorTopBorder = get2();
+    imCanon.SensorRightBorder = get2();
+    imCanon.SensorBottomBorder = get2();
+    imCanon.BlackMaskLeftBorder = get2();
+    imCanon.BlackMaskTopBorder = get2();
+    imCanon.BlackMaskRightBorder = get2();
+    imCanon.BlackMaskBottomBorder = get2();
+  }
+  else if (tag == 0x4001 && len > 500)
+  {
+    int bls = 0;
+    long int offsetChannelBlackLevel = 0L;
+    long int offsetChannelBlackLevel2 = 0L;
+    long int offsetWhiteLevels = 0L;
+    long int save1 = ftell(ifp);
+
+    switch (len)
+    {
+
+    case 582:
+      imCanon.ColorDataVer = 1; // 20D / 350D
+
+      fseek(ifp, save1 + (0x0019 << 1), SEEK_SET);
+      FORC4 cam_mul[RGGB_2_RGBG(c)] = (float)get2();
+      fseek(ifp, save1 + (0x001e << 1), SEEK_SET);
+      FORC4 icWBC[LIBRAW_WBI_Auto][RGGB_2_RGBG(c)] = get2();
+      fseek(ifp, save1 + (0x0041 << 1), SEEK_SET);
+      FORC4 icWBC[LIBRAW_WBI_Custom1][RGGB_2_RGBG(c)] = get2();
+      fseek(ifp, save1 + (0x0046 << 1), SEEK_SET);
+      FORC4 icWBC[LIBRAW_WBI_Custom2][RGGB_2_RGBG(c)] = get2();
+
+      fseek(ifp, save1 + (0x0023 << 1), SEEK_SET);
+      Canon_WBpresets(2, 2);
+      fseek(ifp, save1 + (0x004b << 1), SEEK_SET);
+      Canon_WBCTpresets(1); // ABCT
+      offsetChannelBlackLevel = save1 + (0x00a6 << 1);
+      break;
+
+    case 653:
+      imCanon.ColorDataVer = 2; // 1Dmk2 / 1DsMK2
+
+      fseek(ifp, save1 + (0x0018 << 1), SEEK_SET);
+      FORC4 icWBC[LIBRAW_WBI_Auto][RGGB_2_RGBG(c)] = get2();
+      fseek(ifp, save1 + (0x0022 << 1), SEEK_SET);
+      FORC4 cam_mul[RGGB_2_RGBG(c)] = (float)get2();
+      fseek(ifp, save1 + (0x0090 << 1), SEEK_SET);
+      FORC4 icWBC[LIBRAW_WBI_Custom1][RGGB_2_RGBG(c)] = get2();
+      fseek(ifp, save1 + (0x0095 << 1), SEEK_SET);
+      FORC4 icWBC[LIBRAW_WBI_Custom2][RGGB_2_RGBG(c)] = get2();
+      fseek(ifp, save1 + (0x009a << 1), SEEK_SET);
+      FORC4 icWBC[LIBRAW_WBI_Custom3][RGGB_2_RGBG(c)] = get2();
+
+      fseek(ifp, save1 + (0x0027 << 1), SEEK_SET);
+      Canon_WBpresets(2, 12);
+      fseek(ifp, save1 + (0x00a4 << 1), SEEK_SET);
+      Canon_WBCTpresets(1); // ABCT
+      offsetChannelBlackLevel = save1 + (0x011e << 1);
+      break;
+
+    case 796:
+      imCanon.ColorDataVer = 3; // 1DmkIIN / 5D / 30D / 400D
+      imCanon.ColorDataSubVer = get2();
+
+      fseek(ifp, save1 + (0x003f << 1), SEEK_SET);
+      FORC4 cam_mul[RGGB_2_RGBG(c)] = (float)get2();
+      fseek(ifp, save1 + (0x0044 << 1), SEEK_SET);
+      FORC4 icWBC[LIBRAW_WBI_Auto][RGGB_2_RGBG(c)] = get2();
+      fseek(ifp, save1 + (0x0049 << 1), SEEK_SET);
+      FORC4 icWBC[LIBRAW_WBI_Measured][RGGB_2_RGBG(c)] = get2();
+      fseek(ifp, save1 + (0x0071 << 1), SEEK_SET);
+      FORC4 icWBC[LIBRAW_WBI_Custom1][RGGB_2_RGBG(c)] = get2();
+      fseek(ifp, save1 + (0x0076 << 1), SEEK_SET);
+      FORC4 icWBC[LIBRAW_WBI_Custom2][RGGB_2_RGBG(c)] = get2();
+      fseek(ifp, save1 + (0x007b << 1), SEEK_SET);
+      FORC4 icWBC[LIBRAW_WBI_Custom3][RGGB_2_RGBG(c)] = get2();
+      fseek(ifp, save1 + (0x0080 << 1), SEEK_SET);
+      FORC4 icWBC[LIBRAW_WBI_Custom][RGGB_2_RGBG(c)] = get2();
+
+      fseek(ifp, save1 + (0x004e << 1), SEEK_SET);
+      Canon_WBpresets(2, 12);
+      fseek(ifp, save1 + (0x0085 << 1), SEEK_SET);
+      Canon_WBCTpresets(0); // BCAT
+      offsetChannelBlackLevel = save1 + (0x00c4 << 1);
+      break;
+
+    // 1DmkIII / 1DSmkIII / 1DmkIV / 5DmkII
+    // 7D / 40D / 50D / 60D / 450D / 500D
+    // 550D / 1000D / 1100D
+    case 674:
+    case 692:
+    case 702:
+    case 1227:
+    case 1250:
+    case 1251:
+    case 1337:
+    case 1338:
+    case 1346:
+      imCanon.ColorDataVer = 4;
+      imCanon.ColorDataSubVer = get2();
+
+      fseek(ifp, save1 + (0x003f << 1), SEEK_SET);
+      FORC4 cam_mul[RGGB_2_RGBG(c)] = (float)get2();
+      fseek(ifp, save1 + (0x0044 << 1), SEEK_SET);
+      FORC4 icWBC[LIBRAW_WBI_Auto][RGGB_2_RGBG(c)] = get2();
+      fseek(ifp, save1 + (0x0049 << 1), SEEK_SET);
+      FORC4 icWBC[LIBRAW_WBI_Measured][RGGB_2_RGBG(c)] = get2();
+
+      fseek(ifp, save1 + (0x004e << 1), SEEK_SET);
+      FORC4 sraw_mul[RGGB_2_RGBG(c)] = get2();
+      fseek(ifp, save1 + (0x0053 << 1), SEEK_SET);
+      Canon_WBpresets(2, 12);
+      fseek(ifp, save1 + (0x00a8 << 1), SEEK_SET);
+      Canon_WBCTpresets(0); // BCAT
+
+      if ((imCanon.ColorDataSubVer == 4) ||
+          (imCanon.ColorDataSubVer == 5))
+      {
+        offsetChannelBlackLevel = save1 + (0x02b4 << 1);
+        offsetWhiteLevels = save1 + (0x02b8 << 1);
+      }
+      else if ((imCanon.ColorDataSubVer == 6) ||
+               (imCanon.ColorDataSubVer == 7))
+      {
+        offsetChannelBlackLevel = save1 + (0x02cb << 1);
+        offsetWhiteLevels = save1 + (0x02cf << 1);
+      }
+      else if (imCanon.ColorDataSubVer == 9)
+      {
+        offsetChannelBlackLevel = save1 + (0x02cf << 1);
+        offsetWhiteLevels = save1 + (0x02d3 << 1);
+      }
+      else
+        offsetChannelBlackLevel = save1 + (0x00e7 << 1);
+      break;
+
+    case 5120:  // PowerShot G10, G12, G5 X, G7 X, G9 X, EOS M3, EOS M5, EOS M6
+      imCanon.ColorDataVer = 5;
+      imCanon.ColorDataSubVer = get2();
+
+      fseek(ifp, save1 + (0x0047 << 1), SEEK_SET);
+      FORC4 cam_mul[RGGB_2_RGBG(c)] = (float)get2();
+
+      if (imCanon.ColorDataSubVer == 0xfffc)
+      { // -4: G7 X Mark II, G9 X Mark II, G1 X Mark III, M5, M100, M6
+        fseek(ifp, save1 + (0x004f << 1), SEEK_SET);
+        FORC4 icWBC[LIBRAW_WBI_Auto][RGGB_2_RGBG(c)] = get2();
+        fseek(ifp, 8, SEEK_CUR);
+        FORC4 icWBC[LIBRAW_WBI_Measured][RGGB_2_RGBG(c)] =
+            get2();
+        fseek(ifp, 8, SEEK_CUR);
+        FORC4 icWBC[LIBRAW_WBI_Other][RGGB_2_RGBG(c)] = get2();
+        fseek(ifp, 8, SEEK_CUR);
+        Canon_WBpresets(8, 24);
+        fseek(ifp, 168, SEEK_CUR);
+        FORC4 icWBC[LIBRAW_WBI_FL_WW][RGGB_2_RGBG(c)] = get2();
+        fseek(ifp, 24, SEEK_CUR);
+        Canon_WBCTpresets(2); // BCADT
+        offsetChannelBlackLevel = save1 + (0x014d << 1);
+        offsetWhiteLevels = save1 + (0x0569 << 1);
+      }
+      else if (imCanon.ColorDataSubVer == 0xfffd)
+      { // -3: M10/M3/G1 X/G1 X II/G10/G11/G12/G15/G16/G3 X/G5 X/G7 X/G9
+        // X/S100/S110/S120/S90/S95/SX1 IX/SX50 HS/SX60 HS
+        fseek(ifp, save1 + (0x004c << 1), SEEK_SET);
+        FORC4 icWBC[LIBRAW_WBI_Auto][RGGB_2_RGBG(c)] = get2();
+        get2();
+        FORC4 icWBC[LIBRAW_WBI_Measured][RGGB_2_RGBG(c)] =
+            get2();
+        get2();
+        FORC4 icWBC[LIBRAW_WBI_Other][RGGB_2_RGBG(c)] = get2();
+        get2();
+        Canon_WBpresets(2, 12);
+        fseek(ifp, save1 + (0x00ba << 1), SEEK_SET);
+        Canon_WBCTpresets(2); // BCADT
+        offsetChannelBlackLevel = save1 + (0x0108 << 1);
+      }
+      break;
+
+    case 1273:
+    case 1275:
+      imCanon.ColorDataVer = 6; // 600D / 1200D
+      imCanon.ColorDataSubVer = get2();
+
+      fseek(ifp, save1 + (0x003f << 1), SEEK_SET);
+      FORC4 cam_mul[RGGB_2_RGBG(c)] = (float)get2();
+      fseek(ifp, save1 + (0x0044 << 1), SEEK_SET);
+      FORC4 icWBC[LIBRAW_WBI_Auto][RGGB_2_RGBG(c)] = get2();
+      fseek(ifp, save1 + (0x0049 << 1), SEEK_SET);
+      FORC4 icWBC[LIBRAW_WBI_Measured][RGGB_2_RGBG(c)] = get2();
+
+      fseek(ifp, save1 + (0x0062 << 1), SEEK_SET);
+      FORC4 sraw_mul[RGGB_2_RGBG(c)] = get2();
+      fseek(ifp, save1 + (0x0067 << 1), SEEK_SET);
+      Canon_WBpresets(2, 12);
+      fseek(ifp, save1 + (0x00bc << 1), SEEK_SET);
+      Canon_WBCTpresets(0); // BCAT
+      offsetChannelBlackLevel = save1 + (0x01df << 1);
+      offsetWhiteLevels = save1 + (0x01e3 << 1);
+      break;
+
+    // 1DX / 5DmkIII / 6D / 100D / 650D / 700D / EOS M / 7DmkII / 750D / 760D
+    case 1312:
+    case 1313:
+    case 1316:
+    case 1506:
+      imCanon.ColorDataVer = 7;
+      imCanon.ColorDataSubVer = get2();
+
+      fseek(ifp, save1 + (0x003f << 1), SEEK_SET);
+      FORC4 cam_mul[RGGB_2_RGBG(c)] = (float)get2();
+      fseek(ifp, save1 + (0x0044 << 1), SEEK_SET);
+      FORC4 icWBC[LIBRAW_WBI_Auto][RGGB_2_RGBG(c)] = get2();
+      fseek(ifp, save1 + (0x0049 << 1), SEEK_SET);
+      FORC4 icWBC[LIBRAW_WBI_Measured][RGGB_2_RGBG(c)] = get2();
+
+      fseek(ifp, save1 + (0x007b << 1), SEEK_SET);
+      FORC4 sraw_mul[RGGB_2_RGBG(c)] = get2();
+      fseek(ifp, save1 + (0x0080 << 1), SEEK_SET);
+      Canon_WBpresets(2, 12);
+      fseek(ifp, save1 + (0x00d5 << 1), SEEK_SET);
+      Canon_WBCTpresets(0); // BCAT
+
+      if (imCanon.ColorDataSubVer == 10)
+      {
+        offsetChannelBlackLevel = save1 + (0x01f8 << 1);
+        offsetWhiteLevels = save1 + (0x01fc << 1);
+      }
+      else if (imCanon.ColorDataSubVer == 11)
+      {
+        offsetChannelBlackLevel = save1 + (0x02d8 << 1);
+        offsetWhiteLevels = save1 + (0x02dc << 1);
+      }
+      break;
+
+    // 5DS / 5DS R / 80D / 1300D / 1500D / 3000D / 5D4 / 800D / 77D / 6D II /
+    // 200D
+    case 1560:
+    case 1592:
+    case 1353:
+    case 1602:
+      imCanon.ColorDataVer = 8;
+      imCanon.ColorDataSubVer = get2();
+
+      fseek(ifp, save1 + (0x003f << 1), SEEK_SET);
+      FORC4 cam_mul[RGGB_2_RGBG(c)] = (float)get2();
+      fseek(ifp, save1 + (0x0044 << 1), SEEK_SET);
+      FORC4 icWBC[LIBRAW_WBI_Auto][RGGB_2_RGBG(c)] = get2();
+      fseek(ifp, save1 + (0x0049 << 1), SEEK_SET);
+      FORC4 icWBC[LIBRAW_WBI_Measured][RGGB_2_RGBG(c)] = get2();
+
+      fseek(ifp, save1 + (0x0080 << 1), SEEK_SET);
+      FORC4 sraw_mul[RGGB_2_RGBG(c)] = get2();
+      fseek(ifp, save1 + (0x0085 << 1), SEEK_SET);
+      Canon_WBpresets(2, 12);
+      fseek(ifp, save1 + (0x0107 << 1), SEEK_SET);
+      Canon_WBCTpresets(0); // BCAT
+
+      if (imCanon.ColorDataSubVer == 14)
+      { // 1300D / 1500D / 3000D
+        offsetChannelBlackLevel = save1 + (0x022c << 1);
+        offsetWhiteLevels = save1 + (0x0230 << 1);
+      }
+      else
+      {
+        offsetChannelBlackLevel = save1 + (0x030a << 1);
+        offsetWhiteLevels = save1 + (0x030e << 1);
+      }
+      break;
+
+    case 1820: // M50, ColorDataSubVer 16
+    case 1824: // EOS R, SX740HS, ColorDataSubVer 17
+    case 1816: // EOS RP, SX70HS, ColorDataSubVer 18;
+               // EOS M6 Mark II, EOS 90D, G7XmkIII, ColorDataSubVer 19
+      imCanon.ColorDataVer = 9;
+      imCanon.ColorDataSubVer = get2();
+
+      fseek(ifp, save1 + (0x0047 << 1), SEEK_SET);
+      FORC4 cam_mul[RGGB_2_RGBG(c)] = (float)get2();
+      get2();
+      FORC4 icWBC[LIBRAW_WBI_Auto][RGGB_2_RGBG(c)] = get2();
+      get2();
+      FORC4 icWBC[LIBRAW_WBI_Measured][RGGB_2_RGBG(c)] = get2();
+      fseek(ifp, save1 + (0x0088 << 1), SEEK_SET);
+      Canon_WBpresets(2, 12);
+      fseek(ifp, save1 + (0x010a << 1), SEEK_SET);
+      Canon_WBCTpresets(0);
+      offsetChannelBlackLevel = save1 + (0x0318 << 1);
+      offsetChannelBlackLevel2 = save1 + (0x0149 << 1);
+      offsetWhiteLevels = save1 + (0x031c << 1);
+      break;
+
+    case 2024: // 1D X Mark III, ColorDataSubVer 32
+      imCanon.ColorDataVer = 10;
+      imCanon.ColorDataSubVer = get2();
+      fseek(ifp, save1 + (0x0055 << 1), SEEK_SET);
+      FORC4 cam_mul[RGGB_2_RGBG(c)] = (float)get2();
+      get2();
+      FORC4 icWBC[LIBRAW_WBI_Auto][RGGB_2_RGBG(c)] = get2();
+      get2();
+      FORC4 icWBC[LIBRAW_WBI_Measured][RGGB_2_RGBG(c)] = get2();
+      fseek(ifp, save1 + (0x0096 << 1), SEEK_SET);
+      Canon_WBpresets(2, 12);
+      fseek(ifp, save1 + (0x0118 << 1), SEEK_SET);
+      Canon_WBCTpresets(0);
+      offsetChannelBlackLevel = save1 + (0x0326 << 1);
+      offsetChannelBlackLevel2 = save1 + (0x0157 << 1);
+      offsetWhiteLevels = save1 + (0x032a << 1);
+      break;
+
+   default:
+	imCanon.ColorDataSubVer = get2();
+      break;
+    }
+
+    if (offsetChannelBlackLevel)
+    {
+      fseek(ifp, offsetChannelBlackLevel, SEEK_SET);
+      FORC4
+        bls += (imCanon.ChannelBlackLevel[RGGB_2_RGBG(c)] = get2());
+      imCanon.AverageBlackLevel = bls / 4;
+    }
+    if (offsetWhiteLevels)
+    {
+      if ((offsetWhiteLevels - offsetChannelBlackLevel) != 8L)
+        fseek(ifp, offsetWhiteLevels, SEEK_SET);
+      imCanon.NormalWhiteLevel = get2();
+      imCanon.SpecularWhiteLevel = get2();
+      FORC4
+        imgdata.color.linear_max[c] = imCanon.SpecularWhiteLevel;
+    }
+
+    if(!imCanon.AverageBlackLevel && offsetChannelBlackLevel2)
+    {
+        fseek(ifp, offsetChannelBlackLevel2, SEEK_SET);
+        FORC4
+            bls += (imCanon.ChannelBlackLevel[RGGB_2_RGBG(c)] = get2());
+        imCanon.AverageBlackLevel = bls / 4;
+    }
+    fseek(ifp, save1, SEEK_SET);
+
+  } else if (tag == 0x4013) {
+    get4();
+    imCanon.AFMicroAdjMode = get4();
+    float a = get4();
+    float b = get4();
+    if (fabsf(b) > 0.001f)
+      imCanon.AFMicroAdjValue = a / b;
+
+  } else if ((tag == 0x4021) && (dng_writer == nonDNG) &&
+             (imCanon.multishot[0] = get4()) &&
+             (imCanon.multishot[1] = get4())) {
+    if (len >= 4) {
+      imCanon.multishot[2] = get4();
+      imCanon.multishot[3] = get4();
+    }
+    FORC4 cam_mul[c] = 1024;
+  }
+
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/metadata/ciff.cpp libkdcraw/libkdcraw/libraw/src/metadata/ciff.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/metadata/ciff.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/metadata/ciff.cpp	2022-11-07 07:46:31.734795008 +0300
@@ -0,0 +1,416 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+#ifdef _MSC_VER
+#if _MSC_VER < 1800 /* below MSVC 2013 */
+float roundf(float f)
+{
+ return floorf(f + 0.5);
+}
+
+#endif
+#endif
+
+/*
+   CIFF block 0x1030 contains an 8x8 white sample.
+   Load this into white[][] for use in scale_colors().
+ */
+void LibRaw::ciff_block_1030()
+{
+  static const ushort key[] = {0x410, 0x45f3};
+  int i, bpp, row, col, vbits = 0;
+  unsigned long bitbuf = 0;
+
+  if ((get2(), get4()) != 0x80008 || !get4())
+    return;
+  bpp = get2();
+  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() ^ key[i++ & 1]);
+        vbits += 16;
+      }
+      white[row][col] = bitbuf >> (vbits -= bpp) & ~(-1 << bpp);
+    }
+}
+
+/*
+   Parse a CIFF file, better known as Canon CRW format.
+ */
+void LibRaw::parse_ciff(int offset, int length, int depth)
+{
+  int tboff, nrecs, c, type, len, save, wbi = -1;
+  ushort key[] = {0x410, 0x45f3};
+  ushort CanonColorInfo1_key;
+  ushort Appendix_A = 0;
+  INT64 WB_table_offset = 0;
+  int UseWBfromTable_as_AsShot = 1;
+  int Got_AsShotWB = 0;
+  INT64 fsize = ifp->size();
+  if (metadata_blocks++ > LIBRAW_MAX_METADATA_BLOCKS)
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+
+  fseek(ifp, offset + length - 4, SEEK_SET);
+  tboff = get4() + offset;
+  fseek(ifp, tboff, SEEK_SET);
+  nrecs = get2();
+  if (nrecs < 1)
+    return;
+  if ((nrecs | depth) > 127)
+    return;
+
+  if (nrecs * 10 + offset > fsize)
+    return;
+
+  while (nrecs--)
+  {
+    type = get2();
+    len = get4();
+    INT64 see = offset + get4();
+    save = ftell(ifp);
+
+    /* the following tags are not sub-tables
+     * they contain the value in the "len" field
+     * for such tags skip the check against filesize
+     */
+    if ((type != 0x2007) && (type != 0x580b) && (type != 0x501c) &&
+        (type != 0x5029) && (type != 0x5813) && (type != 0x5814) &&
+        (type != 0x5817) && (type != 0x5834) && (type != 0x580e))
+    {
+
+      if (see >= fsize)
+      { // At least one byte
+        fseek(ifp, save, SEEK_SET);
+        continue;
+      }
+      fseek(ifp, see, SEEK_SET);
+      if ((((type >> 8) + 8) | 8) == 0x38)
+      {
+        parse_ciff(ftell(ifp), len, depth + 1); /* Parse a sub-table */
+      }
+    }
+
+    if (type == 0x3004)
+    {
+      parse_ciff(ftell(ifp), len, depth + 1);
+    }
+    else if (type == 0x0810)
+    {
+      fread(artist, 64, 1, ifp);
+    }
+    else if (type == 0x080a)
+    {
+      fread(make, 64, 1, ifp);
+      fseek(ifp, strbuflen(make) - 63, SEEK_CUR);
+      fread(model, 64, 1, ifp);
+
+    } else if (type == 0x080b) {
+      char *p;
+      stmread(imCommon.firmware, (unsigned)len, ifp);
+      if (p = strrchr(imCommon.firmware, ' ')) {
+        imCanon.firmware = atof(p+1);
+      }
+
+    } else if (type == 0x1810)
+    {
+      width = get4();
+      height = get4();
+      pixel_aspect = int_to_float(get4());
+      flip = get4();
+    }
+    else if (type == 0x1835)
+    { /* Get the decoder table */
+      tiff_compress = get4();
+    }
+    else if (type == 0x2007)
+    {
+      thumb_offset = see;
+      thumb_length = len;
+    }
+    else if (type == 0x1818)
+    {
+      shutter = libraw_powf64l(2.0f, -int_to_float((get4(), get4())));
+      ilm.CurAp = aperture = libraw_powf64l(2.0f, int_to_float(get4()) / 2);
+    }
+    else if (type == 0x102a) // CanonShotInfo
+    {
+      //      iso_speed = pow (2.0, (get4(),get2())/32.0 - 4) * 50;
+      get2(); // skip one
+      iso_speed =
+          libraw_powf64l(2.0f, (get2() + get2()) / 32.0f - 5.0f) * 100.0f;
+      ilm.CurAp = aperture = _CanonConvertAperture((get2(), get2()));
+      shutter = libraw_powf64l(2.0, -((short)get2()) / 32.0);
+      imCanon.wbi = wbi = (get2(), get2());
+      if (wbi >= Canon_wbi2std.size())
+        wbi = 0;
+      fseek(ifp, 32, SEEK_CUR);
+      if (shutter > 1e6)
+        shutter = get2() / 10.0;
+    }
+    else if (type == 0x102c) // CanonColorInfo2 / Appendix A: Pro90IS, G1, G2, S30, S40
+    {
+      int CanonColorInfo2_type = get2(); // G1 1028, G2 272, Pro90 IS 769, S30 274, S40 273, EOS D30 276
+      if (CanonColorInfo2_type > 512) { /* Pro90 IS, G1 */
+        fseek(ifp, 118, SEEK_CUR);
+        FORC4 cam_mul[BG2RG1_2_RGBG(c)] = get2();
+      }
+      else if (CanonColorInfo2_type != 276) { /* G2, S30, S40 */
+        Appendix_A = 1;
+        WB_table_offset = -14;
+        fseek(ifp, 98, SEEK_CUR);
+        FORC4 cam_mul[GRBG_2_RGBG(c)] = get2();
+        if (cam_mul[0] > 0.001f) Got_AsShotWB = 1;
+      }
+    }
+    else if (type == 0x10a9) // ColorBalance: Canon D60, 10D, 300D, and clones
+    {
+      int bls = 0;
+/*
+      int table[] = {
+          LIBRAW_WBI_Auto,     // 0
+          LIBRAW_WBI_Daylight, // 1
+          LIBRAW_WBI_Cloudy,   // 2
+          LIBRAW_WBI_Tungsten, // 3
+          LIBRAW_WBI_FL_W,     // 4
+          LIBRAW_WBI_Flash,    // 5
+          LIBRAW_WBI_Custom,   // 6, absent in Canon D60
+          LIBRAW_WBI_Auto,     // 7, use this if camera is set to b/w JPEG
+          LIBRAW_WBI_Shade,    // 8
+          LIBRAW_WBI_Kelvin    // 9, absent in Canon D60
+      };
+*/
+      int nWB =
+          ((get2() - 2) / 8) -
+          1; // 2 bytes this, N recs 4*2bytes each, last rec is black level
+      if (nWB)
+        FORC4 icWBC[LIBRAW_WBI_Auto][RGGB_2_RGBG(c)] = get2();
+      if (nWB >= 7)
+        Canon_WBpresets(0, 0);
+      else
+        FORC4 cam_mul[c] = icWBC[LIBRAW_WBI_Auto][c];
+      if (nWB == 7) // mostly Canon EOS D60 + some fw#s for 300D;
+                    // check for 0x1668000 is unreliable
+      {
+        if ((wbi >= 0) && (wbi < 9) && (wbi != 6))
+        {
+          FORC4 cam_mul[c] = icWBC[Canon_wbi2std[wbi]][c];
+        }
+        else
+        {
+          FORC4 cam_mul[c] = icWBC[LIBRAW_WBI_Auto][c];
+        }
+      }
+      else if (nWB == 9) // Canon 10D, 300D
+      {
+        FORC4 icWBC[LIBRAW_WBI_Custom][RGGB_2_RGBG(c)] = get2();
+        FORC4 icWBC[LIBRAW_WBI_Kelvin][RGGB_2_RGBG(c)] = get2();
+        if ((wbi >= 0) && (wbi < 10))
+        {
+          FORC4 cam_mul[c] = icWBC[Canon_wbi2std[wbi]][c];
+        }
+        else
+        {
+          FORC4 cam_mul[c] = icWBC[LIBRAW_WBI_Auto][c];
+        }
+      }
+      FORC4
+      bls += (imCanon.ChannelBlackLevel[RGGB_2_RGBG(c)] = get2());
+      imCanon.AverageBlackLevel = bls / 4;
+    }
+    else if (type == 0x102d)
+    {
+      Canon_CameraSettings(len >> 1);
+    }
+
+    else if (type == 0x10b4) {
+      switch (get2()) {
+      case 1:
+        imCommon.ColorSpace = LIBRAW_COLORSPACE_sRGB;
+        break;
+      case 2:
+        imCommon.ColorSpace = LIBRAW_COLORSPACE_AdobeRGB;
+        break;
+      default:
+        imCommon.ColorSpace = LIBRAW_COLORSPACE_Unknown;
+        break;
+      }
+
+    } else if (type == 0x580b)
+    {
+      if (strcmp(model, "Canon EOS D30"))
+        sprintf(imgdata.shootinginfo.BodySerial, "%d", len);
+      else
+        sprintf(imgdata.shootinginfo.BodySerial, "%0x-%05d", len >> 16,
+                len & 0xffff);
+    }
+    else if (type == 0x0032) // CanonColorInfo1
+    {
+      if (len == 768) { // EOS D30
+
+        ushort q;
+        fseek(ifp, 4, SEEK_CUR);
+        for (int linenum = 0; linenum < Canon_D30_linenums_2_StdWBi.size(); linenum++) {
+          if (Canon_D30_linenums_2_StdWBi[linenum] != LIBRAW_WBI_Unknown) {
+            FORC4 {
+              q = get2();
+              icWBC[Canon_D30_linenums_2_StdWBi[linenum]][RGGB_2_RGBG(c)] =
+                (int)(roundf(1024000.0f / (float)MAX(1, q)));
+            }
+//         if (Canon_wbi2std[imCanon.wbi] == *(Canon_D30_linenums_2_StdWBi + linenum)) {
+//           FORC4 cam_mul[c] = icWBC[*(Canon_D30_linenums_2_StdWBi + linenum)][c];
+//           Got_AsShotWB = 1;
+//           }
+          }
+        }
+        fseek (ifp, 68-Canon_D30_linenums_2_StdWBi.size()*8, SEEK_CUR);
+
+        FORC4 {
+          q = get2();
+          cam_mul[RGGB_2_RGBG(c)] = 1024.0 / MAX(1, q);
+        }
+        if (!wbi)
+          cam_mul[0] = -1; // use my auto white balance
+
+      }
+      else if ((cam_mul[0] <= 0.001f) || // Pro1, G3, G5, G6, S45, S50, S60, S70
+               Appendix_A)               // G2, S30, S40
+      {
+        libraw_static_table_t linenums_2_StdWBi;
+        int AsShotWB_linenum = Canon_wbi2std.size();
+
+        CanonColorInfo1_key = get2();
+        if ((CanonColorInfo1_key == key[0]) && (len == 2048)) { // Pro1
+          linenums_2_StdWBi = Canon_KeyIs0x0410_Len2048_linenums_2_StdWBi;
+          WB_table_offset = 8;
+
+        } else if ((CanonColorInfo1_key == key[0]) && (len == 3072)) { // S60, S70, G6
+          linenums_2_StdWBi = Canon_KeyIs0x0410_Len3072_linenums_2_StdWBi;
+          WB_table_offset = 16;
+
+        } else if (!CanonColorInfo1_key && (len == 2048)) { // G2, S30, S40; S45, S50, G3, G5
+          key[0] = key[1] = 0;
+          linenums_2_StdWBi = Canon_KeyIsZero_Len2048_linenums_2_StdWBi;
+          if (imCanon.firmware < 1.02f)
+            UseWBfromTable_as_AsShot = 0;
+
+        } else goto next_tag;
+
+        if ((Canon_wbi2std[wbi] == LIBRAW_WBI_Auto)    ||
+            (Canon_wbi2std[wbi] == LIBRAW_WBI_Unknown) ||
+            Got_AsShotWB)
+          UseWBfromTable_as_AsShot = 0;
+
+        if (UseWBfromTable_as_AsShot) {
+          int temp_wbi;
+          if (Canon_wbi2std[wbi] == LIBRAW_WBI_Custom) temp_wbi = LIBRAW_WBI_Daylight;
+          else temp_wbi = wbi;
+          for (AsShotWB_linenum = 0; AsShotWB_linenum < linenums_2_StdWBi.size(); AsShotWB_linenum++) {
+            if (Canon_wbi2std[temp_wbi] == linenums_2_StdWBi[AsShotWB_linenum]) {
+              break;
+            }
+          }
+        }
+
+        fseek (ifp, 78+WB_table_offset, SEEK_CUR);
+        for (int linenum = 0; linenum < linenums_2_StdWBi.size(); linenum++) {
+          if (linenums_2_StdWBi[linenum] != LIBRAW_WBI_Unknown) {
+            FORC4 icWBC[linenums_2_StdWBi[linenum]][GRBG_2_RGBG(c)] = get2() ^ key[c & 1];
+            if (UseWBfromTable_as_AsShot && (AsShotWB_linenum == linenum)) {
+              FORC4 cam_mul[c] = icWBC[linenums_2_StdWBi[linenum]][c];
+              Got_AsShotWB = 1;
+            }
+          } else {
+            fseek(ifp, 8, SEEK_CUR);
+          }
+        }
+        if (!Got_AsShotWB)
+          cam_mul[0] = -1;
+      }
+    }
+    else if (type == 0x1030 && wbi >= 0 && (0x18040 >> wbi & 1))
+    {
+      ciff_block_1030(); // all that don't have 0x10a9
+    }
+    else if (type == 0x1031)
+    {
+			raw_width = imCanon.SensorWidth = (get2(), get2());
+			raw_height = imCanon.SensorHeight = get2();
+			imCanon.SensorLeftBorder = (get2(), get2(), get2());
+			imCanon.SensorTopBorder = get2();
+			imCanon.SensorRightBorder = get2();
+			imCanon.SensorBottomBorder = get2();
+			imCanon.BlackMaskLeftBorder = get2();
+			imCanon.BlackMaskTopBorder = get2();
+			imCanon.BlackMaskRightBorder = get2();
+			imCanon.BlackMaskBottomBorder = get2();
+    }
+    else if (type == 0x501c)
+    {
+      iso_speed = len & 0xffff;
+    }
+    else if (type == 0x5029)
+    {
+      ilm.CurFocal = len >> 16;
+      ilm.FocalType = len & 0xffff;
+      if (ilm.FocalType == LIBRAW_FT_ZOOM_LENS)
+      {
+        ilm.FocalUnits = 32;
+        if (ilm.FocalUnits > 1)
+          ilm.CurFocal /= (float)ilm.FocalUnits;
+      }
+      focal_len = ilm.CurFocal;
+    }
+    else if (type == 0x5813)
+    {
+      flash_used = int_to_float(len);
+    }
+    else if (type == 0x5814)
+    {
+      canon_ev = int_to_float(len);
+    }
+    else if (type == 0x5817)
+    {
+      shot_order = len;
+    }
+    else if (type == 0x5834)
+    {
+      unique_id = ((unsigned long long)len << 32) >> 32;
+      setCanonBodyFeatures(unique_id);
+    }
+    else if (type == 0x580e)
+    {
+      timestamp = len;
+    }
+    else if (type == 0x180e)
+    {
+      timestamp = get4();
+    }
+
+next_tag:;
+#ifdef LOCALTIME
+    if ((type | 0x4000) == 0x580e)
+      timestamp = mktime(gmtime(&timestamp));
+#endif
+    fseek(ifp, save, SEEK_SET);
+  }
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/metadata/cr3_parser.cpp libkdcraw/libkdcraw/libraw/src/metadata/cr3_parser.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/metadata/cr3_parser.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/metadata/cr3_parser.cpp	2022-11-07 07:46:31.734795008 +0300
@@ -0,0 +1,539 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+void LibRaw::selectCRXTrack(short maxTrack)
+{
+  if (maxTrack < 0)
+    return;
+  INT64 bitcounts[LIBRAW_CRXTRACKS_MAXCOUNT], maxbitcount = 0;
+  uint32_t maxjpegbytes = 0;
+  memset(bitcounts, 0, sizeof(bitcounts));
+  for (int i = 0; i <= maxTrack && i < LIBRAW_CRXTRACKS_MAXCOUNT; i++)
+  {
+    crx_data_header_t *d = &libraw_internal_data.unpacker_data.crx_header[i];
+    if (d->MediaType == 1) // RAW
+    {
+      bitcounts[i] = INT64(d->nBits) * INT64(d->f_width) * INT64(d->f_height);
+      if (bitcounts[i] > maxbitcount)
+        maxbitcount = bitcounts[i];
+    }
+    else if (d->MediaType == 2) // JPEG
+    {
+      if (d->MediaSize > maxjpegbytes)
+      {
+        maxjpegbytes = d->MediaSize;
+        thumb_offset = d->MediaOffset;
+        thumb_length = d->MediaSize;
+      }
+    }
+  }
+  if (maxbitcount < 8)
+    return;
+  int framei = -1, framecnt = 0;
+  for (int i = 0; i <= maxTrack && i < LIBRAW_CRXTRACKS_MAXCOUNT; i++)
+  {
+    if (bitcounts[i] == maxbitcount)
+    {
+      if (framecnt <= (int)shot_select)
+        framei = i;
+      framecnt++;
+    }
+  }
+  is_raw = framecnt;
+  if (framei >= 0 && framei < LIBRAW_CRXTRACKS_MAXCOUNT)
+  {
+    crx_data_header_t *d =
+        &libraw_internal_data.unpacker_data.crx_header[framei];
+    data_offset = d->MediaOffset;
+    data_size = d->MediaSize;
+    raw_width = d->f_width;
+    raw_height = d->f_height;
+    load_raw = &LibRaw::crxLoadRaw;
+    switch (d->cfaLayout)
+    {
+    case 0:
+      filters = 0x94949494;
+      break;
+    case 1:
+      filters = 0x61616161;
+      break;
+    case 2:
+      filters = 0x49494949;
+      break;
+    case 3:
+      filters = 0x16161616;
+      break;
+    }
+
+    libraw_internal_data.unpacker_data.crx_track_selected = framei;
+
+    int tiff_idx = -1;
+    INT64 tpixels = 0;
+    for (unsigned i = 0; i < tiff_nifds && i < LIBRAW_IFD_MAXCOUNT; i++)
+      if (INT64(tiff_ifd[i].t_height) * INT64(tiff_ifd[i].t_height) > tpixels)
+      {
+        tpixels = INT64(tiff_ifd[i].t_height) * INT64(tiff_ifd[i].t_height);
+        tiff_idx = i;
+      }
+    if (tiff_idx >= 0)
+      flip = tiff_ifd[tiff_idx].t_flip;
+  }
+}
+
+#define bad_hdr                                                                \
+  (((order != 0x4d4d) && (order != 0x4949)) || (get2() != 0x002a) ||           \
+   (get4() != 0x00000008))
+int LibRaw::parseCR3(unsigned long long oAtomList,
+                     unsigned long long szAtomList, short &nesting,
+                     char *AtomNameStack, short &nTrack, short &TrackType)
+{
+  /*
+  Atom starts with 4 bytes for Atom size and 4 bytes containing Atom name
+  Atom size includes the length of the header and the size of all "contained"
+  Atoms if Atom size == 1, Atom has the extended size stored in 8 bytes located
+  after the Atom name if Atom size == 0, it is the last top-level Atom extending
+  to the end of the file Atom name is often a 4 symbol mnemonic, but can be a
+  4-byte integer
+  */
+  const char UIID_Canon[17] =
+      "\x85\xc0\xb6\x87\x82\x0f\x11\xe0\x81\x11\xf4\xce\x46\x2b\x6a\x48";
+
+  /*
+  AtomType = 0 - unknown: "unk."
+  AtomType = 1 - container atom: "cont"
+  AtomType = 2 - leaf atom: "leaf"
+  AtomType = 3 - can be container, can be leaf: "both"
+  */
+  short AtomType;
+  static const struct
+  {
+    char AtomName[5];
+    short AtomType;
+  } AtomNamesList[] = {
+      {"dinf", 1},
+      {"edts", 1},
+      {"fiin", 1},
+      {"ipro", 1},
+      {"iprp", 1},
+      {"mdia", 1},
+      {"meco", 1},
+      {"mere", 1},
+      {"mfra", 1},
+      {"minf", 1},
+      {"moof", 1},
+      {"moov", 1},
+      {"mvex", 1},
+      {"paen", 1},
+      {"schi", 1},
+      {"sinf", 1},
+      {"skip", 1},
+      {"stbl", 1},
+      {"stsd", 1},
+      {"strk", 1},
+      {"tapt", 1},
+      {"traf", 1},
+      {"trak", 1},
+
+      {"cdsc", 2},
+      {"colr", 2},
+      {"dimg", 2},
+      // {"dref", 2},
+      {"free", 2},
+      {"frma", 2},
+      {"ftyp", 2},
+      {"hdlr", 2},
+      {"hvcC", 2},
+      {"iinf", 2},
+      {"iloc", 2},
+      {"infe", 2},
+      {"ipco", 2},
+      {"ipma", 2},
+      {"iref", 2},
+      {"irot", 2},
+      {"ispe", 2},
+      {"meta", 2},
+      {"mvhd", 2},
+      {"pitm", 2},
+      {"pixi", 2},
+      {"schm", 2},
+      {"thmb", 2},
+      {"tkhd", 2},
+      {"url ", 2},
+      {"urn ", 2},
+
+      {"CCTP", 1},
+      {"CRAW", 1},
+
+      {"JPEG", 2},
+      {"CDI1", 2},
+      {"CMP1", 2},
+
+      {"CNCV", 2},
+      {"CCDT", 2},
+      {"CTBO", 2},
+      {"CMT1", 2},
+      {"CMT2", 2},
+      {"CMT3", 2},
+      {"CMT4", 2},
+      {"THMB", 2},
+      {"co64", 2},
+      {"mdat", 2},
+      {"mdhd", 2},
+      {"nmhd", 2},
+      {"stsc", 2},
+      {"stsz", 2},
+      {"stts", 2},
+      {"vmhd", 2},
+
+      {"dref", 3},
+      {"uuid", 3},
+  };
+
+  const char sHandlerType[5][5] = {"unk.", "soun", "vide", "hint", "meta"};
+
+  int c, err;
+
+  ushort tL;                        // Atom length represented in 4 or 8 bytes
+  char nmAtom[5];                   // Atom name
+  unsigned long long oAtom, szAtom; // Atom offset and Atom size
+  unsigned long long oAtomContent,
+      szAtomContent; // offset and size of Atom content
+  unsigned long long lHdr;
+
+  char UIID[16];
+  uchar CMP1[36];
+  char HandlerType[5], MediaFormatID[5];
+  uint32_t relpos_inDir, relpos_inBox;
+  unsigned szItem, Tag, lTag;
+  ushort tItem;
+
+  nmAtom[0] = MediaFormatID[0] = nmAtom[4] = MediaFormatID[4] = '\0';
+  strcpy(HandlerType, sHandlerType[0]);
+  oAtom = oAtomList;
+  nesting++;
+  if (nesting > 31)
+    return -14; // too deep nesting
+  short s_order = order;
+
+  while ((oAtom + 8ULL) <= (oAtomList + szAtomList))
+  {
+    lHdr = 0ULL;
+    err = 0;
+    order = 0x4d4d;
+    fseek(ifp, oAtom, SEEK_SET);
+    szAtom = get4();
+    FORC4 nmAtom[c] = AtomNameStack[nesting * 4 + c] = fgetc(ifp);
+    AtomNameStack[(nesting + 1) * 4] = '\0';
+    tL = 4;
+    AtomType = 0;
+
+    for (c = 0; c < int(sizeof AtomNamesList / sizeof *AtomNamesList); c++)
+      if (!strcmp(nmAtom, AtomNamesList[c].AtomName))
+      {
+        AtomType = AtomNamesList[c].AtomType;
+        break;
+      }
+
+    if (!AtomType)
+    {
+      err = 1;
+    }
+
+    if (szAtom == 0ULL)
+    {
+      if (nesting != 0)
+      {
+        err = -2;
+        goto fin;
+      }
+      szAtom = szAtomList - oAtom;
+      oAtomContent = oAtom + 8ULL;
+      szAtomContent = szAtom - 8ULL;
+    }
+    else if (szAtom == 1ULL)
+    {
+      if ((oAtom + 16ULL) > (oAtomList + szAtomList))
+      {
+        err = -3;
+        goto fin;
+      }
+      tL = 8;
+      szAtom = (((unsigned long long)get4()) << 32) | get4();
+      oAtomContent = oAtom + 16ULL;
+      szAtomContent = szAtom - 16ULL;
+    }
+    else
+    {
+      oAtomContent = oAtom + 8ULL;
+      szAtomContent = szAtom - 8ULL;
+    }
+
+    if (!strcmp(nmAtom, "trak"))
+    {
+      nTrack++;
+      TrackType = 0;
+      if (nTrack >= LIBRAW_CRXTRACKS_MAXCOUNT)
+        break;
+    }
+    if (!strcmp(AtomNameStack, "moovuuid"))
+    {
+      lHdr = 16ULL;
+      fread(UIID, 1, lHdr, ifp);
+      if (!strncmp(UIID, UIID_Canon, lHdr))
+      {
+        AtomType = 1;
+      }
+      else
+        fseek(ifp, -lHdr, SEEK_CUR);
+    }
+    else if (!strcmp(AtomNameStack, "moovuuidCCTP"))
+    {
+      lHdr = 12ULL;
+    }
+    else if (!strcmp(AtomNameStack, "moovuuidCMT1"))
+    {
+      short q_order = order;
+      order = get2();
+      if ((tL != 4) || bad_hdr)
+      {
+        err = -4;
+        goto fin;
+      }
+      parse_tiff_ifd(oAtomContent);
+      order = q_order;
+    }
+    else if (!strcmp(AtomNameStack, "moovuuidCMT2"))
+    {
+      short q_order = order;
+      order = get2();
+      if ((tL != 4) || bad_hdr)
+      {
+        err = -5;
+        goto fin;
+      }
+      parse_exif(oAtomContent);
+      order = q_order;
+    }
+    else if (!strcmp(AtomNameStack, "moovuuidCMT3"))
+    {
+      short q_order = order;
+      order = get2();
+      if ((tL != 4) || bad_hdr)
+      {
+        err = -6;
+        goto fin;
+      }
+      fseek(ifp, -12L, SEEK_CUR);
+      parse_makernote(oAtomContent, 0);
+      order = q_order;
+    }
+    else if (!strcmp(AtomNameStack, "moovuuidCMT4"))
+    {
+      short q_order = order;
+      order = get2();
+      if ((tL != 4) || bad_hdr)
+      {
+        err = -6;
+        goto fin;
+      }
+      INT64 off = ftell(ifp);
+      parse_gps(oAtomContent);
+      fseek(ifp, off, SEEK_SET);
+      parse_gps_libraw(oAtomContent);
+      order = q_order;
+    }
+    else if (!strcmp(AtomNameStack, "moovtrakmdiahdlr"))
+    {
+      fseek(ifp, 8L, SEEK_CUR);
+      FORC4 HandlerType[c] = fgetc(ifp);
+      for (c = 1; c < int(sizeof sHandlerType / sizeof *sHandlerType); c++)
+        if (!strcmp(HandlerType, sHandlerType[c]))
+        {
+          TrackType = c;
+          break;
+        }
+    }
+    else if (!strcmp(AtomNameStack, "moovtrakmdiaminfstblstsd"))
+    {
+      if (szAtomContent >= 16)
+      {
+        fseek(ifp, 12L, SEEK_CUR);
+        lHdr = 8;
+      }
+      else
+      {
+        err = -7;
+        goto fin;
+      }
+      FORC4 MediaFormatID[c] = fgetc(ifp);
+      if ((TrackType == 2) && (!strcmp(MediaFormatID, "CRAW")))
+      {
+        if (szAtomContent >= 44)
+          fseek(ifp, 24L, SEEK_CUR);
+        else
+        {
+          err = -8;
+          goto fin;
+        }
+      }
+      else
+      {
+        AtomType = 2; // only continue for CRAW
+        lHdr = 0;
+      }
+#define current_track libraw_internal_data.unpacker_data.crx_header[nTrack]
+
+      /*ImageWidth =*/ get2();
+      /*ImageHeight =*/ get2();
+    }
+    else if (!strcmp(AtomNameStack, "moovtrakmdiaminfstblstsdCRAW"))
+    {
+      lHdr = 82;
+    }
+    else if (!strcmp(AtomNameStack, "moovtrakmdiaminfstblstsdCRAWCMP1"))
+    {
+      if (szAtomContent >= 40)
+        fread(CMP1, 1, 36, ifp);
+      else
+      {
+        err = -7;
+        goto fin;
+      }
+      if (!crxParseImageHeader(CMP1, nTrack))
+        current_track.MediaType = 1;
+    }
+    else if (!strcmp(AtomNameStack, "moovtrakmdiaminfstblstsdCRAWJPEG"))
+    {
+      current_track.MediaType = 2;
+    }
+    else if (!strcmp(AtomNameStack, "moovtrakmdiaminfstblstsz"))
+    {
+      if (szAtomContent == 12)
+        fseek(ifp, 4L, SEEK_CUR);
+      else if (szAtomContent == 16)
+        fseek(ifp, 12L, SEEK_CUR);
+      else
+      {
+        err = -9;
+        goto fin;
+      }
+      current_track.MediaSize = get4();
+    }
+    else if (!strcmp(AtomNameStack, "moovtrakmdiaminfstblco64"))
+    {
+      if (szAtomContent == 16)
+        fseek(ifp, 8L, SEEK_CUR);
+      else
+      {
+        err = -10;
+        goto fin;
+      }
+      current_track.MediaOffset = (((unsigned long long)get4()) << 32) | get4();
+    }
+
+    if (nTrack >= 0 && nTrack < LIBRAW_CRXTRACKS_MAXCOUNT &&
+        current_track.MediaSize && current_track.MediaOffset &&
+        ((oAtom + szAtom) >= (oAtomList + szAtomList)) &&
+        !strncmp(AtomNameStack, "moovtrakmdiaminfstbl", 20))
+    {
+      if ((TrackType == 4) && (!strcmp(MediaFormatID, "CTMD")))
+      {
+        order = 0x4949;
+        relpos_inDir = 0L;
+        while (relpos_inDir + 6 < current_track.MediaSize)
+        {
+          if (current_track.MediaOffset + relpos_inDir > ifp->size() - 6) // need at least 6 bytes
+          {
+              err = -11;
+              goto fin;
+          }
+          fseek(ifp, current_track.MediaOffset + relpos_inDir, SEEK_SET);
+          szItem = get4();
+          tItem = get2();
+          if (szItem < 1 || (  (relpos_inDir + szItem) > current_track.MediaSize))
+          {
+            err = -11;
+            goto fin;
+          }
+          if ((tItem == 7) || (tItem == 8) || (tItem == 9))
+          {
+            relpos_inBox = relpos_inDir + 12L;
+            while (relpos_inBox + 8 < relpos_inDir + szItem)
+            {
+              if (current_track.MediaOffset + relpos_inBox > ifp->size() - 8) // need at least 8 bytes
+              {
+                  err = -11;
+                  goto fin;
+              }
+              fseek(ifp, current_track.MediaOffset + relpos_inBox, SEEK_SET);
+              lTag = get4();
+              Tag = get4();
+              if (lTag < 8)
+              {
+                err = -12;
+                goto fin;
+              }
+              else if ((relpos_inBox + lTag) > (relpos_inDir + szItem))
+              {
+                err = -11;
+                goto fin;
+              }
+              if ((Tag == 0x927c) && ((tItem == 7) || (tItem == 8)))
+              {
+                fseek(ifp, current_track.MediaOffset + relpos_inBox + 8L,
+                      SEEK_SET);
+                short q_order = order;
+                order = get2();
+                if (bad_hdr)
+                {
+                  err = -13;
+                  goto fin;
+                }
+                fseek(ifp, -8L, SEEK_CUR);
+                libraw_internal_data.unpacker_data.CR3_CTMDtag = 1;
+                parse_makernote(current_track.MediaOffset + relpos_inBox + 8,
+                                0);
+                libraw_internal_data.unpacker_data.CR3_CTMDtag = 0;
+                order = q_order;
+              }
+              relpos_inBox += lTag;
+            }
+          }
+          relpos_inDir += szItem;
+        }
+        order = 0x4d4d;
+      }
+    }
+#undef current_track
+    if (AtomType == 1)
+    {
+      err = parseCR3(oAtomContent + lHdr, szAtomContent - lHdr, nesting,
+                     AtomNameStack, nTrack, TrackType);
+      if (err)
+        goto fin;
+    }
+    oAtom += szAtom;
+  }
+
+fin:
+  nesting--;
+  if (nesting >= 0)
+    AtomNameStack[nesting * 4] = '\0';
+  order = s_order;
+  return err;
+}
+#undef bad_hdr
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/metadata/epson.cpp libkdcraw/libkdcraw/libraw/src/metadata/epson.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/metadata/epson.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/metadata/epson.cpp	2022-11-07 07:46:31.734795008 +0300
@@ -0,0 +1,96 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+void LibRaw::parseEpsonMakernote(int base, int uptag, unsigned dng_writer)
+{
+
+#define isRIC imgdata.sizes.raw_inset_crop
+
+  unsigned entries, tag, type, len, save;
+  short morder, sorder = order;
+  ushort c;
+  INT64 fsize = ifp->size();
+
+  fseek(ifp, -2, SEEK_CUR);
+
+  entries = get2();
+  if (entries > 1000)
+    return;
+  morder = order;
+
+  while (entries--)
+  {
+    order = morder;
+    tiff_get(base, &tag, &type, &len, &save);
+    INT64 pos = ifp->tell();
+    if (len > 8 && pos + len > 2 * fsize)
+    {
+      fseek(ifp, save, SEEK_SET); // Recover tiff-read position!!
+      continue;
+    }
+
+    tag |= uptag << 16;
+    if (len > 100 * 1024 * 1024)
+      goto next; // 100Mb tag? No!
+
+    if (tag == 0x020b)
+    {
+      if (tagtypeIs(LIBRAW_EXIFTAG_TYPE_LONG))
+        isRIC.cwidth = get4();
+      else if (tagtypeIs(LIBRAW_EXIFTAG_TYPE_SHORT))
+        isRIC.cwidth = get2();
+    }
+    else if (tag == 0x020c)
+    {
+      if (tagtypeIs(LIBRAW_EXIFTAG_TYPE_LONG))
+        isRIC.cheight = get4();
+      else if (tagtypeIs(LIBRAW_EXIFTAG_TYPE_SHORT))
+        isRIC.cheight = get2();
+    }
+    else if (tag == 0x0400)
+    {                                 // sensor area
+      ushort sdims[4] = {0, 0, 0, 0}; // left margin, top margin, width, height
+      FORC4 sdims[c] = get2();
+      isRIC.cleft = (sdims[2] - sdims[0] - isRIC.cwidth) / 2;
+      isRIC.ctop = (sdims[3] - sdims[1] - isRIC.cheight) / 2;
+    }
+
+    if (dng_writer == nonDNG)
+    {
+
+      if (tag == 0x0280)
+      {
+        thumb_offset = ftell(ifp);
+        thumb_length = len;
+      }
+      else if (tag == 0x0401)
+      {
+        FORC4 cblack[RGGB_2_RGBG(c)] = get4();
+      }
+      else if (tag == 0x0e80)
+      {
+        fseek(ifp, 48, SEEK_CUR);
+        cam_mul[0] = get2() * 567.0 / 0x10000;
+        cam_mul[2] = get2() * 431.0 / 0x10000;
+      }
+    }
+
+  next:
+    fseek(ifp, save, SEEK_SET);
+  }
+  order = sorder;
+#undef isRIC
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/metadata/exif_gps.cpp libkdcraw/libkdcraw/libraw/src/metadata/exif_gps.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/metadata/exif_gps.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/metadata/exif_gps.cpp	2022-11-07 07:46:31.734795008 +0300
@@ -0,0 +1,414 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+#include "../../internal/libraw_cameraids.h"
+
+void LibRaw::parse_exif_interop(int base)
+{
+	unsigned entries, tag, type, len, save;
+	char value[4] = { 0,0,0,0 };
+	entries = get2();
+	INT64 fsize = ifp->size();
+	while (entries--)
+	{
+		tiff_get(base, &tag, &type, &len, &save);
+
+		INT64 savepos = ftell(ifp);
+		if (len > 8 && savepos + len > fsize * 2)
+		{
+			fseek(ifp, save, SEEK_SET); // Recover tiff-read position!!
+			continue;
+		}
+        if (callbacks.exif_cb)
+        {
+            callbacks.exif_cb(callbacks.exifparser_data, tag | 0x40000, type, len, order, ifp, base);
+            fseek(ifp, savepos, SEEK_SET);
+        }
+
+		switch (tag)
+		{
+		case 0x0001: // InteropIndex
+			fread(value, 1, MIN(4, len), ifp);
+			if (strncmp(value, "R98", 3) == 0 &&
+				// Canon bug, when [Canon].ColorSpace = AdobeRGB,
+				// but [ExifIFD].ColorSpace = Uncalibrated and
+				// [InteropIFD].InteropIndex = "R98"
+				imgdata.color.ExifColorSpace == LIBRAW_COLORSPACE_Unknown)
+				imgdata.color.ExifColorSpace = LIBRAW_COLORSPACE_sRGB;
+			else if (strncmp(value, "R03", 3) == 0)
+				imgdata.color.ExifColorSpace = LIBRAW_COLORSPACE_AdobeRGB;
+			break;
+		}
+		fseek(ifp, save, SEEK_SET);
+	}
+}
+
+void LibRaw::parse_exif(int base)
+{
+  unsigned entries, tag, type, len, save, c;
+  double expo, ape;
+
+  unsigned kodak = !strncmp(make, "EASTMAN", 7) && tiff_nifds < 3;
+
+  entries = get2();
+  if (!strncmp(make, "Hasselblad", 10) && (tiff_nifds > 3) && (entries > 512))
+    return;
+  INT64 fsize = ifp->size();
+  while (entries--)
+  {
+    tiff_get(base, &tag, &type, &len, &save);
+
+    INT64 savepos = ftell(ifp);
+    if (len > 8 && savepos + len > fsize * 2)
+    {
+      fseek(ifp, save, SEEK_SET); // Recover tiff-read position!!
+      continue;
+    }
+    if (callbacks.exif_cb)
+    {
+      callbacks.exif_cb(callbacks.exifparser_data, tag, type, len, order, ifp,
+                        base);
+      fseek(ifp, savepos, SEEK_SET);
+    }
+
+    switch (tag)
+    {
+	case 0xA005: // Interoperability IFD
+		fseek(ifp, get4() + base, SEEK_SET);
+		parse_exif_interop(base);
+		break;
+	case 0xA001: // ExifIFD.ColorSpace
+		c = get2();
+		if (c == 1 && imgdata.color.ExifColorSpace == LIBRAW_COLORSPACE_Unknown)
+			imgdata.color.ExifColorSpace = LIBRAW_COLORSPACE_sRGB;
+		else if (c == 2)
+			imgdata.color.ExifColorSpace = LIBRAW_COLORSPACE_AdobeRGB;
+		break;
+    case 0x9400:
+      imCommon.exifAmbientTemperature = getreal(type);
+      if ((imCommon.CameraTemperature > -273.15f) &&
+          ((OlyID == OlyID_TG_5) ||
+           (OlyID == OlyID_TG_6))
+      )
+        imCommon.CameraTemperature += imCommon.exifAmbientTemperature;
+      break;
+    case 0x9401:
+      imCommon.exifHumidity = getreal(type);
+      break;
+    case 0x9402:
+      imCommon.exifPressure = getreal(type);
+      break;
+    case 0x9403:
+      imCommon.exifWaterDepth = getreal(type);
+      break;
+    case 0x9404:
+      imCommon.exifAcceleration = getreal(type);
+      break;
+    case 0x9405:
+      imCommon.exifCameraElevationAngle = getreal(type);
+      break;
+
+    case 0xa405: // FocalLengthIn35mmFormat
+      imgdata.lens.FocalLengthIn35mmFormat = get2();
+      break;
+    case 0xa431: // BodySerialNumber
+      stmread(imgdata.shootinginfo.BodySerial, len, ifp);
+      break;
+    case 0xa432: // LensInfo, 42034dec, Lens Specification per EXIF standard
+      imgdata.lens.MinFocal = getreal(type);
+      imgdata.lens.MaxFocal = getreal(type);
+      imgdata.lens.MaxAp4MinFocal = getreal(type);
+      imgdata.lens.MaxAp4MaxFocal = getreal(type);
+      break;
+    case 0xa435: // LensSerialNumber
+      stmread(imgdata.lens.LensSerial, len, ifp);
+      if (!strncmp(imgdata.lens.LensSerial, "----", 4))
+        imgdata.lens.LensSerial[0] = '\0';
+      break;
+    case 0xa420: /* 42016, ImageUniqueID */
+      stmread(imgdata.color.ImageUniqueID, len, ifp);
+      break;
+    case 0xc65d: /* 50781, RawDataUniqueID */
+      imgdata.color.RawDataUniqueID[16] = 0;
+      fread(imgdata.color.RawDataUniqueID, 1, 16, ifp);
+      break;
+    case 0xc630: // DNG LensInfo, Lens Specification per EXIF standard
+      imgdata.lens.dng.MinFocal = getreal(type);
+      imgdata.lens.dng.MaxFocal = getreal(type);
+      imgdata.lens.dng.MaxAp4MinFocal = getreal(type);
+      imgdata.lens.dng.MaxAp4MaxFocal = getreal(type);
+      break;
+    case 0xc68b: /* 50827, OriginalRawFileName */
+      stmread(imgdata.color.OriginalRawFileName, len, ifp);
+      break;
+    case 0xa433: // LensMake
+      stmread(imgdata.lens.LensMake, len, ifp);
+      break;
+    case 0xa434: // LensModel
+      stmread(imgdata.lens.Lens, len, ifp);
+      if (!strncmp(imgdata.lens.Lens, "----", 4))
+        imgdata.lens.Lens[0] = '\0';
+      break;
+    case 0x9205:
+      imgdata.lens.EXIF_MaxAp = libraw_powf64l(2.0f, (getreal(type) / 2.0f));
+      break;
+    case 0x829a: // 33434
+      shutter = getreal(type);
+      if (tiff_nifds > 0 && tiff_nifds <= LIBRAW_IFD_MAXCOUNT)
+          tiff_ifd[tiff_nifds - 1].t_shutter = shutter;
+      break;
+    case 0x829d: // 33437, FNumber
+      aperture = getreal(type);
+      break;
+    case 0x8827: // 34855
+      iso_speed = get2();
+      break;
+    case 0x8831: // 34865
+      if (iso_speed == 0xffff && !strncasecmp(make, "FUJI", 4))
+        iso_speed = getreal(type);
+      break;
+    case 0x8832: // 34866
+      if (iso_speed == 0xffff &&
+          (!strncasecmp(make, "SONY", 4) || !strncasecmp(make, "CANON", 5)))
+        iso_speed = getreal(type);
+      break;
+    case 0x9003: // 36867
+    case 0x9004: // 36868
+      get_timestamp(0);
+      break;
+    case 0x9201: // 37377
+       if ((expo = -getreal(type)) < 128 && shutter == 0.)
+       {
+            shutter = libraw_powf64l(2.0, expo);
+            if (tiff_nifds > 0 && tiff_nifds <= LIBRAW_IFD_MAXCOUNT)
+              tiff_ifd[tiff_nifds - 1].t_shutter = shutter;
+       }
+      break;
+    case 0x9202: // 37378 ApertureValue
+      if ((fabs(ape = getreal(type)) < 256.0) && (!aperture))
+        aperture = libraw_powf64l(2.0, ape / 2);
+      break;
+    case 0x9209: // 37385
+      flash_used = getreal(type);
+      break;
+    case 0x920a: // 37386
+      focal_len = getreal(type);
+      break;
+    case 0x927c: // 37500
+      if (((make[0] == '\0') && !strncmp(model, "ov5647", 6)) ||
+          (!strncmp(make, "RaspberryPi", 11) &&
+           (!strncmp(model, "RP_OV5647", 9) ||
+            !strncmp(model, "RP_imx219", 9))))
+      {
+        char mn_text[512];
+        char *pos;
+        char ccms[512];
+        ushort l;
+        float num;
+
+        fgets(mn_text, MIN(len, 511), ifp);
+        mn_text[511] = 0;
+
+        pos = strstr(mn_text, "gain_r=");
+        if (pos)
+          cam_mul[0] = atof(pos + 7);
+        pos = strstr(mn_text, "gain_b=");
+        if (pos)
+          cam_mul[2] = atof(pos + 7);
+        if ((cam_mul[0] > 0.001f) && (cam_mul[2] > 0.001f))
+          cam_mul[1] = cam_mul[3] = 1.0f;
+        else
+          cam_mul[0] = cam_mul[2] = 0.0f;
+
+        pos = strstr(mn_text, "ccm=");
+        if (pos)
+        {
+          pos += 4;
+          char *pos2 = strstr(pos, " ");
+          if (pos2)
+          {
+            l = pos2 - pos;
+            memcpy(ccms, pos, l);
+            ccms[l] = '\0';
+#ifdef LIBRAW_WIN32_CALLS
+            // Win32 strtok is already thread-safe
+            pos = strtok(ccms, ",");
+#else
+            char *last = 0;
+            pos = strtok_r(ccms, ",", &last);
+#endif
+            if (pos)
+            {
+              for (l = 0; l < 4; l++)
+              {
+                num = 0.0;
+                for (c = 0; c < 3; c++)
+                {
+                  imgdata.color.ccm[l][c] = (float)atoi(pos);
+                  num += imgdata.color.ccm[l][c];
+#ifdef LIBRAW_WIN32_CALLS
+                  pos = strtok(NULL, ",");
+#else
+                  pos = strtok_r(NULL, ",", &last);
+#endif
+                  if (!pos)
+                    goto end; // broken
+                }
+                if (num > 0.01)
+                  FORC3 imgdata.color.ccm[l][c] = imgdata.color.ccm[l][c] / num;
+              }
+            }
+          }
+        }
+      end:;
+      }
+      else if (!strncmp(make, "SONY", 4) &&
+               (!strncmp(model, "DSC-V3", 6) || !strncmp(model, "DSC-F828", 8)))
+      {
+        parseSonySRF(len);
+        break;
+      }
+      else if ((len == 1) && !strncmp(make, "NIKON", 5))
+      {
+        c = get4();
+        if (c)
+          fseek(ifp, c, SEEK_SET);
+        is_NikonTransfer = 1;
+      }
+      parse_makernote(base, 0);
+      break;
+    case 0xa002: // 40962
+      if (kodak)
+        raw_width = get4();
+      break;
+    case 0xa003: // 40963
+      if (kodak)
+        raw_height = get4();
+      break;
+    case 0xa302: // 41730
+      if (get4() == 0x20002)
+        for (exif_cfa = c = 0; c < 8; c += 2)
+          exif_cfa |= fgetc(ifp) * 0x01010101U << c;
+    }
+    fseek(ifp, save, SEEK_SET);
+  }
+}
+
+void LibRaw::parse_gps_libraw(int base)
+{
+  unsigned entries, tag, type, len, save, c;
+
+  entries = get2();
+  if (entries > 40)
+    return;
+  if (entries > 0)
+    imgdata.other.parsed_gps.gpsparsed = 1;
+  INT64 fsize = ifp->size();
+  while (entries--)
+  {
+    tiff_get(base, &tag, &type, &len, &save);
+    if (len > 1024)
+    {
+      fseek(ifp, save, SEEK_SET); // Recover tiff-read position!!
+      continue;                   // no GPS tags are 1k or larger
+    }
+    INT64 savepos = ftell(ifp);
+    if (len > 8 && savepos + len > fsize * 2)
+    {
+        fseek(ifp, save, SEEK_SET); // Recover tiff-read position!!
+        continue;
+    }
+
+    if (callbacks.exif_cb)
+    {
+        callbacks.exif_cb(callbacks.exifparser_data, tag | 0x50000, type, len, order, ifp, base);
+        fseek(ifp, savepos, SEEK_SET);
+    }
+
+    switch (tag)
+    {
+    case 0x0001:
+      imgdata.other.parsed_gps.latref = getc(ifp);
+      break;
+    case 0x0003:
+      imgdata.other.parsed_gps.longref = getc(ifp);
+      break;
+    case 0x0005:
+      imgdata.other.parsed_gps.altref = getc(ifp);
+      break;
+    case 0x0002:
+      if (len == 3)
+        FORC(3) imgdata.other.parsed_gps.latitude[c] = getreal(type);
+      break;
+    case 0x0004:
+      if (len == 3)
+        FORC(3) imgdata.other.parsed_gps.longitude[c] = getreal(type);
+      break;
+    case 0x0007:
+      if (len == 3)
+        FORC(3) imgdata.other.parsed_gps.gpstimestamp[c] = getreal(type);
+      break;
+    case 0x0006:
+      imgdata.other.parsed_gps.altitude = getreal(type);
+      break;
+    case 0x0009:
+      imgdata.other.parsed_gps.gpsstatus = getc(ifp);
+      break;
+    }
+    fseek(ifp, save, SEEK_SET);
+  }
+}
+
+void LibRaw::parse_gps(int base)
+{
+  unsigned entries, tag, type, len, save, c;
+
+  entries = get2();
+  if (entries > 40)
+    return;
+  while (entries--)
+  {
+    tiff_get(base, &tag, &type, &len, &save);
+    if (len > 1024)
+    {
+      fseek(ifp, save, SEEK_SET); // Recover tiff-read position!!
+      continue;                   // no GPS tags are 1k or larger
+    }
+    switch (tag)
+    {
+    case 0x0001:
+    case 0x0003:
+    case 0x0005:
+      gpsdata[29 + tag / 2] = getc(ifp);
+      break;
+    case 0x0002:
+    case 0x0004:
+    case 0x0007:
+      FORC(6) gpsdata[tag / 3 * 6 + c] = get4();
+      break;
+    case 0x0006:
+      FORC(2) gpsdata[18 + c] = get4();
+      break;
+    case 0x0012: // 18
+    case 0x001d: // 29
+      fgets((char *)(gpsdata + 14 + tag / 3), MIN(len, 12), ifp);
+    }
+    fseek(ifp, save, SEEK_SET);
+  }
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/metadata/fuji.cpp libkdcraw/libkdcraw/libraw/src/metadata/fuji.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/metadata/fuji.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/metadata/fuji.cpp	2022-11-07 07:46:31.734795008 +0300
@@ -0,0 +1,1163 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+void LibRaw::parseAdobeRAFMakernote()
+{
+
+  uchar *PrivateMknBuf;
+  unsigned posPrivateMknBuf;
+  unsigned PrivateMknLength;
+  unsigned PrivateOrder;
+  unsigned PrivateEntries, PrivateTagID;
+  unsigned PrivateTagBytes;
+  unsigned wb_section_offset = 0;
+  int posWB;
+  int c;
+
+#define CHECKSPACE(s)                                                          \
+  if (posPrivateMknBuf + (s) > PrivateMknLength)                               \
+  {                                                                            \
+    free(PrivateMknBuf);                                                       \
+    return;                                                                    \
+  }
+#define isWB(posWB)                                                            \
+  sget2(posWB) != 0 && sget2(posWB + 2) != 0 && sget2(posWB + 4) != 0 &&       \
+      sget2(posWB + 6) != 0 && sget2(posWB + 8) != 0 &&                        \
+      sget2(posWB + 10) != 0 && sget2(posWB) != 0xff &&                        \
+      sget2(posWB + 2) != 0xff && sget2(posWB + 4) != 0xff &&                  \
+      sget2(posWB + 6) != 0xff && sget2(posWB + 8) != 0xff &&                  \
+      sget2(posWB + 10) != 0xff && sget2(posWB) == sget2(posWB + 6) &&         \
+      sget2(posWB) < sget2(posWB + 2) && sget2(posWB) < sget2(posWB + 4) &&    \
+      sget2(posWB) < sget2(posWB + 8) && sget2(posWB) < sget2(posWB + 10)
+#define imfRAFDataVersion imFuji.RAFDataVersion
+#define imfRAFVersion imFuji.RAFVersion
+
+  ushort use_WBcorr_coeffs = 0;
+  double wbR_corr = 1.0;
+  double wbB_corr = 1.0;
+
+  if (strstr(model, "S7000") ||
+      strstr(model, "S5000") ||
+      strstr(model, "F700")  ||
+      strstr(model, "S2Pro") ||
+      strstr(model, "S20Pro")) {
+    use_WBcorr_coeffs = 1;
+    wbR_corr = 10.0 / 17.0 / 0.652941;
+    wbB_corr = 2.0 /3.0 / (3.0 / 4.0 + 1.0 / 300.0);
+  }
+
+  order = 0x4d4d;
+  PrivateMknLength = get4();
+
+  if ((PrivateMknLength > 4) && (PrivateMknLength < 10240000) &&
+      (PrivateMknBuf = (uchar *)malloc(PrivateMknLength + 1024)))
+  { // 1024b for safety
+    fread(PrivateMknBuf, PrivateMknLength, 1, ifp);
+    PrivateOrder = sget2(PrivateMknBuf);
+    posPrivateMknBuf = sget4(PrivateMknBuf + 2) + 12;
+
+    memcpy(imFuji.SerialSignature, PrivateMknBuf + 6, 0x0c);
+    imFuji.SerialSignature[0x0c] = 0;
+    memcpy(model, PrivateMknBuf + 0x12, 0x20);
+    model[0x20] = 0;
+    memcpy(model2, PrivateMknBuf + 0x32, 4);
+    model2[4] = 0;
+    strcpy(imFuji.RAFVersion, model2);
+    PrivateEntries = sget2(PrivateMknBuf + posPrivateMknBuf);
+    if ((PrivateEntries > 1000) ||
+        ((PrivateOrder != 0x4d4d) && (PrivateOrder != 0x4949)))
+    {
+      free(PrivateMknBuf);
+      return;
+    }
+    posPrivateMknBuf += 2;
+    /*
+     * because Adobe DNG converter strips or misplaces 0xfnnn tags,
+     * Auto WB for following cameras is missing for now
+     * - F550EXR
+     * - F600EXR
+     * - F770EXR
+     * - F800EXR
+     * - F900EXR
+     * - HS10
+     * - HS11
+     * - HS20EXR
+     * - HS30EXR
+     * - HS50EXR
+     * - S1
+     * - SL1000
+     **/
+    while (PrivateEntries--)
+    {
+      order = 0x4d4d;
+      CHECKSPACE(4);
+      PrivateTagID = sget2(PrivateMknBuf + posPrivateMknBuf);
+      PrivateTagBytes = sget2(PrivateMknBuf + posPrivateMknBuf + 2);
+      posPrivateMknBuf += 4;
+      order = PrivateOrder;
+
+      if (PrivateTagID == 0x2000)
+      {
+        FORC4 icWBC[LIBRAW_WBI_Auto][GRGB_2_RGBG(c)] =
+            sget2(PrivateMknBuf + posPrivateMknBuf + (c << 1));
+        if (use_WBcorr_coeffs) {
+          icWBC[LIBRAW_WBI_Auto][0] *= wbR_corr;
+          icWBC[LIBRAW_WBI_Auto][2] *= wbB_corr;
+        }
+      }
+      else if (PrivateTagID == 0x2100)
+      {
+        FORC4 icWBC[LIBRAW_WBI_FineWeather][GRGB_2_RGBG(c)] =
+            sget2(PrivateMknBuf + posPrivateMknBuf + (c << 1));
+        if (use_WBcorr_coeffs) {
+          icWBC[LIBRAW_WBI_FineWeather][0] *= wbR_corr;
+          icWBC[LIBRAW_WBI_FineWeather][2] *= wbB_corr;
+        }
+      }
+      else if (PrivateTagID == 0x2200)
+      {
+        FORC4 icWBC[LIBRAW_WBI_Shade][GRGB_2_RGBG(c)] =
+            sget2(PrivateMknBuf + posPrivateMknBuf + (c << 1));
+        if (use_WBcorr_coeffs) {
+          icWBC[LIBRAW_WBI_Shade][0] *= wbR_corr;
+          icWBC[LIBRAW_WBI_Shade][2] *= wbB_corr;
+        }
+      }
+      else if (PrivateTagID == 0x2300)
+      {
+        FORC4 icWBC[LIBRAW_WBI_FL_D][GRGB_2_RGBG(c)] =
+            sget2(PrivateMknBuf + posPrivateMknBuf + (c << 1));
+        if (use_WBcorr_coeffs) {
+          icWBC[LIBRAW_WBI_FL_D][0] *= wbR_corr;
+          icWBC[LIBRAW_WBI_FL_D][2] *= wbB_corr;
+        }
+      }
+      else if (PrivateTagID == 0x2301)
+      {
+        FORC4 icWBC[LIBRAW_WBI_FL_N][GRGB_2_RGBG(c)] =
+            sget2(PrivateMknBuf + posPrivateMknBuf + (c << 1));
+        if (use_WBcorr_coeffs) {
+          icWBC[LIBRAW_WBI_FL_N][0] *= wbR_corr;
+          icWBC[LIBRAW_WBI_FL_N][2] *= wbB_corr;
+        }
+      }
+      else if (PrivateTagID == 0x2302)
+      {
+        FORC4 icWBC[LIBRAW_WBI_FL_WW][GRGB_2_RGBG(c)] =
+            sget2(PrivateMknBuf + posPrivateMknBuf + (c << 1));
+        if (use_WBcorr_coeffs) {
+          icWBC[LIBRAW_WBI_FL_WW][0] *= wbR_corr;
+          icWBC[LIBRAW_WBI_FL_WW][2] *= wbB_corr;
+        }
+      }
+      else if (PrivateTagID == 0x2310)
+      {
+        FORC4 icWBC[LIBRAW_WBI_FL_L][GRGB_2_RGBG(c)] =
+            sget2(PrivateMknBuf + posPrivateMknBuf + (c << 1));
+        if (use_WBcorr_coeffs) {
+          icWBC[LIBRAW_WBI_FL_L][0] *= wbR_corr;
+          icWBC[LIBRAW_WBI_FL_L][2] *= wbB_corr;
+        }
+      }
+      else if (PrivateTagID == 0x2311)
+      {
+        FORC4 icWBC[LIBRAW_WBI_FL_W][GRGB_2_RGBG(c)] =
+            sget2(PrivateMknBuf + posPrivateMknBuf + (c << 1));
+        if (use_WBcorr_coeffs) {
+          icWBC[LIBRAW_WBI_FL_W][0] *= wbR_corr;
+          icWBC[LIBRAW_WBI_FL_W][2] *= wbB_corr;
+        }
+      }
+      else if (PrivateTagID == 0x2400)
+      {
+        FORC4 icWBC[LIBRAW_WBI_Tungsten][GRGB_2_RGBG(c)] =
+            sget2(PrivateMknBuf + posPrivateMknBuf + (c << 1));
+        if (use_WBcorr_coeffs) {
+          icWBC[LIBRAW_WBI_Tungsten][0] *= wbR_corr;
+          icWBC[LIBRAW_WBI_Tungsten][2] *= wbB_corr;
+        }
+      }
+      else if (PrivateTagID == 0x2410)
+      {
+        FORC4 icWBC[LIBRAW_WBI_Flash][GRGB_2_RGBG(c)] =
+            sget2(PrivateMknBuf + posPrivateMknBuf + (c << 1));
+        if (use_WBcorr_coeffs) {
+          icWBC[LIBRAW_WBI_Flash][0] *= wbR_corr;
+          icWBC[LIBRAW_WBI_Flash][2] *= wbB_corr;
+        }
+      }
+      else if (PrivateTagID == 0x2f00)
+      {
+        int nWBs = MIN(sget4(PrivateMknBuf + posPrivateMknBuf), 6);
+        posWB = posPrivateMknBuf + 4;
+        for (int wb_ind = 0; wb_ind < nWBs; wb_ind++)
+        {
+          FORC4 icWBC[LIBRAW_WBI_Custom1 + wb_ind][GRGB_2_RGBG(c)] =
+              sget2(PrivateMknBuf + posWB + (c << 1));
+          if (use_WBcorr_coeffs) {
+            icWBC[LIBRAW_WBI_Custom1 + wb_ind][0] *= wbR_corr;
+            icWBC[LIBRAW_WBI_Custom1 + wb_ind][2] *= wbB_corr;
+          }
+          posWB += 8;
+        }
+      }
+      else if (PrivateTagID == 0x2ff0)
+      {
+        FORC4 cam_mul[GRGB_2_RGBG(c)] =
+            sget2(PrivateMknBuf + posPrivateMknBuf + (c << 1));
+        if (use_WBcorr_coeffs) {
+         cam_mul[0] *= wbR_corr;
+         cam_mul[2] *= wbB_corr;
+        }
+      }
+
+      else if (PrivateTagID == 0x9650)
+      {
+        CHECKSPACE(4);
+        short a = (short)sget2(PrivateMknBuf + posPrivateMknBuf);
+        float b = fMAX(1.0f, sget2(PrivateMknBuf + posPrivateMknBuf + 2));
+        imFuji.ExpoMidPointShift = a / b;
+      }
+      else if ((PrivateTagID == 0xc000) && (PrivateTagBytes > 3) &&
+               (PrivateTagBytes < 10240000))
+      {
+        order = 0x4949;
+        if (PrivateTagBytes != 4096) // not one of Fuji X-A3, X-A5, X-A7, X-A10, X-A20, X-T100, X-T200, XF10
+        {
+          if (!sget2(PrivateMknBuf + posPrivateMknBuf))
+            imfRAFDataVersion = sget2(PrivateMknBuf + posPrivateMknBuf + 2);
+
+          for (posWB = 0; posWB < (int)PrivateTagBytes - 16; posWB++)
+          {
+            if ((!memcmp(PrivateMknBuf + posWB, "TSNERDTS", 8) &&
+                 (sget2(PrivateMknBuf + posWB + 10) > 125)))
+            {
+              posWB += 10;
+              icWBC[LIBRAW_WBI_Auto][1] =
+                  icWBC[LIBRAW_WBI_Auto][3] =
+                      sget2(PrivateMknBuf + posWB);
+              icWBC[LIBRAW_WBI_Auto][0] =
+                  sget2(PrivateMknBuf + posWB + 2);
+              icWBC[LIBRAW_WBI_Auto][2] =
+                  sget2(PrivateMknBuf + posWB + 4);
+              break;
+            }
+          }
+          if (imfRAFDataVersion == 0x0146 || // X20
+              imfRAFDataVersion == 0x0149 || // X100S
+              imfRAFDataVersion == 0x0249)   // X100S
+          {
+            wb_section_offset = 0x1410;
+          }
+          else if (imfRAFDataVersion == 0x014d || // X-M1
+                   imfRAFDataVersion == 0x014e)   // X-A1, X-A2
+          {
+            wb_section_offset = 0x1474;
+          }
+          else if (imfRAFDataVersion == 0x014f || // X-E2
+                   imfRAFDataVersion == 0x024f || // X-E2
+                   imfRAFDataVersion == 0x025d)   // X-H1
+          {
+            wb_section_offset = 0x1480;
+          }
+          else if (imfRAFDataVersion == 0x0150) // XQ1, XQ2
+          {
+            wb_section_offset = 0x1414;
+          }
+          else if (imfRAFDataVersion == 0x0151 || // X-T1 w/diff. fws
+                   imfRAFDataVersion == 0x0251 || imfRAFDataVersion == 0x0351 ||
+                   imfRAFDataVersion == 0x0451 || imfRAFDataVersion == 0x0551)
+          {
+            wb_section_offset = 0x14b0;
+          }
+          else if (imfRAFDataVersion == 0x0152 || // X30
+                   imfRAFDataVersion == 0x0153)   // X100T
+          {
+            wb_section_offset = 0x1444;
+          }
+          else if (imfRAFDataVersion == 0x0154) // X-T10
+          {
+            wb_section_offset = 0x1824;
+          }
+          else if (imfRAFDataVersion == 0x0155) // X70
+          {
+            wb_section_offset = 0x17b4;
+          }
+          else if (imfRAFDataVersion == 0x0255) // X-Pro2
+          {
+            wb_section_offset = 0x135c;
+          }
+          else if (imfRAFDataVersion == 0x0258 || // X-T2
+                   imfRAFDataVersion == 0x025b)   // X-T20
+          {
+            wb_section_offset = 0x13dc;
+          }
+          else if (imfRAFDataVersion == 0x0259) // X100F
+          {
+            wb_section_offset = 0x1370;
+          }
+          else if (imfRAFDataVersion == 0x025a) // GFX 50S
+          {
+            wb_section_offset = 0x1424;
+          }
+          else if (imfRAFDataVersion == 0x025c) // X-E3
+          {
+            wb_section_offset = 0x141c;
+          }
+          else if (imfRAFDataVersion == 0x025e) // X-T3
+          {
+            wb_section_offset = 0x2014;
+          }
+          else if (imfRAFDataVersion == 0x025f) // X-T30, GFX 50R, GFX 100
+          {
+            if (!strcmp(model, "X-T30")) {
+              if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x20b8))
+                wb_section_offset = 0x20b8;
+              else if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x20c8))
+                wb_section_offset = 0x20c8;
+            }
+            else if (!strcmp(model, "GFX 50R"))
+              wb_section_offset = 0x1424;
+            else if (!strcmp(model, "GFX 100"))
+              wb_section_offset = 0x20e4;
+          }
+          else if (imfRAFDataVersion == 0x0260) // X-Pro3
+          {
+            wb_section_offset = 0x20e8;
+          }
+          else if (imfRAFDataVersion == 0x0261) // X100V
+          {
+            wb_section_offset = 0x2078;
+          }
+          else if (imfRAFDataVersion == 0x0262) // X-T4
+          {
+            wb_section_offset = 0x21c8;
+          }
+
+            /* try for unknown RAF Data versions */
+          else if (!strcmp(model, "X-Pro2"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x135c))
+              wb_section_offset = 0x135c;
+          }
+          else if (!strcmp(model, "X100F"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x1370))
+              wb_section_offset = 0x1370;
+          }
+          else if (!strcmp(model, "X-T2"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x13dc))
+              wb_section_offset = 0x13dc;
+          }
+          else if (!strcmp(model, "X-T20"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x13dc))
+              wb_section_offset = 0x13dc;
+          }
+          else if (!strcmp(model, "X20"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x1410))
+              wb_section_offset = 0x1410;
+          }
+          else if (!strcmp(model, "X100S"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x1410))
+              wb_section_offset = 0x1410;
+          }
+          else if (!strcmp(model, "XQ1"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x1414))
+              wb_section_offset = 0x1414;
+          }
+          else if (!strcmp(model, "XQ2"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x1414))
+              wb_section_offset = 0x1414;
+          }
+          else if (!strcmp(model, "X-E3"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x141c))
+              wb_section_offset = 0x141c;
+          }
+          else if (!strcmp(model, "GFX 50S"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x1424))
+              wb_section_offset = 0x1424;
+          }
+          else if (!strcmp(model, "GFX 50R"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x1424))
+              wb_section_offset = 0x1424;
+          }
+          else if (!strcmp(model, "X30"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x1444))
+              wb_section_offset = 0x1444;
+          }
+          else if (!strcmp(model, "X100T"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x1444))
+              wb_section_offset = 0x1444;
+          }
+          else if (!strcmp(model, "X-M1"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x1474))
+              wb_section_offset = 0x1474;
+          }
+          else if (!strcmp(model, "X-A1"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x1474))
+              wb_section_offset = 0x1474;
+          }
+          else if (!strcmp(model, "X-A2"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x1474))
+              wb_section_offset = 0x1474;
+          }
+          else if (!strcmp(model, "X-E2"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x1480))
+              wb_section_offset = 0x1480;
+          }
+          else if (!strcmp(model, "X-H1"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x1480))
+              wb_section_offset = 0x1480;
+          }
+          else if (!strcmp(model, "X-T1"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x14b0))
+              wb_section_offset = 0x14b0;
+          }
+          else if (!strcmp(model, "X70"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x17b4))
+              wb_section_offset = 0x17b4;
+          }
+          else if (!strcmp(model, "X-T10"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x1824))
+              wb_section_offset = 0x1824;
+          }
+          else if (!strcmp(model, "X-E2S"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x1840))
+              wb_section_offset = 0x1840;
+          }
+          else if (!strcmp(model, "X-T3"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x2014))
+              wb_section_offset = 0x2014;
+          }
+          else if (!strcmp(model, "X100V"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x20e8))
+              wb_section_offset = 0x2078;
+          }
+          else if (!strcmp(model, "X-T30"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x20b8))
+              wb_section_offset = 0x20b8;
+          }
+          else if (!strcmp(model, "GFX 100"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x20e4))
+              wb_section_offset = 0x20e4;
+          }
+          else if (!strcmp(model, "X-Pro3"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x20e8))
+              wb_section_offset = 0x20e8;
+          }
+          else if (!strcmp(model, "X-T4"))
+          {
+            if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x21c8))
+              wb_section_offset = 0x21c8;
+          }
+
+            /* no RAF Data version for the models below */
+          else if (!strcmp(model, "FinePix X100")) // X100 0 0x19f0 0x19e8
+          {
+            if (!strcmp(imfRAFVersion, "0069"))
+              wb_section_offset = 0x19e8;
+            else if (!strcmp(imfRAFVersion, "0100"))
+              wb_section_offset = 0x19f0;
+            else if (!strcmp(imfRAFVersion, "0110"))
+              wb_section_offset = 0x19f0;
+            else if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x19e8))
+              wb_section_offset = 0x19e8;
+            else if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x19f0))
+              wb_section_offset = 0x19f0;
+          }
+          else if (!strcmp(model, "X-E1")) // X-E1 0 0x13ac
+          {
+            if (!strcmp(imfRAFVersion, "0101"))
+              wb_section_offset = 0x13ac;
+            else if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x13ac))
+              wb_section_offset = 0x13ac;
+          }
+          else if (!strcmp(model, "X-Pro1")) // X-Pro1 0 0x13a4
+          {
+            if (!strcmp(imfRAFVersion, "0100"))
+              wb_section_offset = 0x13a4;
+            else if (!strcmp(imfRAFVersion, "0101"))
+              wb_section_offset = 0x13a4;
+            else if (!strcmp(imfRAFVersion, "0204"))
+              wb_section_offset = 0x13a4;
+            else if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x13a4))
+              wb_section_offset = 0x13a4;
+          }
+          else if (!strcmp(model, "XF1")) // XF1 0 0x138c
+          {
+            if (!strcmp(imfRAFVersion, "0100"))
+              wb_section_offset = 0x138c;
+            else if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x138c))
+              wb_section_offset = 0x138c;
+          }
+          else if (!strcmp(model, "X-S1")) // X-S1 0 0x1284
+          {
+            if (!strcmp(imfRAFVersion, "0100"))
+              wb_section_offset = 0x1284;
+            else if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x1284))
+              wb_section_offset = 0x1284;
+          }
+          else if (!strcmp(model, "X10")) // X10 0 0x1280 0x12d4
+          {
+            if (!strcmp(imfRAFVersion, "0100"))
+              wb_section_offset = 0x1280;
+            else if (!strcmp(imfRAFVersion, "0102"))
+              wb_section_offset = 0x1280;
+            else if (!strcmp(imfRAFVersion, "0103"))
+              wb_section_offset = 0x12d4;
+            else if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x1280))
+              wb_section_offset = 0x1280;
+            else if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x12d4))
+              wb_section_offset = 0x12d4;
+          }
+          else if (!strcmp(model, "XF1")) // XF1 0 0x138c
+          {
+            if (!strcmp(imfRAFVersion, "0100"))
+              wb_section_offset = 0x138c;
+            else if (isWB(PrivateMknBuf + posPrivateMknBuf + 0x138c))
+              wb_section_offset = 0x138c;
+          }
+          if (wb_section_offset &&
+              isWB(PrivateMknBuf + posPrivateMknBuf + wb_section_offset))
+          {
+
+            if (!imfRAFDataVersion)
+            {
+              posWB = posPrivateMknBuf + wb_section_offset - 6;
+              icWBC[LIBRAW_WBI_Auto][1] =
+                  icWBC[LIBRAW_WBI_Auto][3] =
+                      sget2(PrivateMknBuf + posWB);
+              icWBC[LIBRAW_WBI_Auto][0] =
+                  sget2(PrivateMknBuf + posWB + 2);
+              icWBC[LIBRAW_WBI_Auto][2] =
+                  sget2(PrivateMknBuf + posWB + 4);
+            }
+
+            posWB = posPrivateMknBuf + wb_section_offset;
+            for (int wb_ind = 0; wb_ind < Fuji_wb_list1.size(); posWB += 6, wb_ind++)
+            {
+              icWBC[Fuji_wb_list1[wb_ind]][1] =
+                  icWBC[Fuji_wb_list1[wb_ind]][3] =
+                      sget2(PrivateMknBuf + posWB);
+              icWBC[Fuji_wb_list1[wb_ind]][0] =
+                  sget2(PrivateMknBuf + posWB + 2);
+              icWBC[Fuji_wb_list1[wb_ind]][2] =
+                  sget2(PrivateMknBuf + posWB + 4);
+            }
+            int found = 0;
+            if ((imfRAFDataVersion == 0x0260) ||
+                (imfRAFDataVersion == 0x0261) ||
+                (imfRAFDataVersion == 0x0262))
+              posWB += 0x30;
+            posWB += 0xc0;
+            ushort Gval = sget2(PrivateMknBuf + posWB);
+            for (int posEndCCTsection = posWB; posEndCCTsection < (posWB + 30);
+                 posEndCCTsection += 6)
+            {
+              if (sget2(PrivateMknBuf + posEndCCTsection) != Gval)
+              {
+                if ((imfRAFDataVersion == 0x0260) ||
+                    (imfRAFDataVersion == 0x0261) ||
+                    (imfRAFDataVersion == 0x0262))
+                  wb_section_offset = posEndCCTsection - 34*3*2; // 34 records, 3 2-byte values in a record
+                else
+                  wb_section_offset = posEndCCTsection - 31*3*2; // 31 records, 3 2-byte values in a record
+                found = 1;
+                break;
+              }
+            }
+
+            if (found)
+            {
+              for (int iCCT = 0; iCCT < 31; iCCT++)
+              {
+                icWBCCTC[iCCT][0] = FujiCCT_K[iCCT];
+                icWBCCTC[iCCT][1] = sget2(PrivateMknBuf + wb_section_offset + iCCT * 6 + 2);
+                icWBCCTC[iCCT][2] = icWBCCTC[iCCT][4] = sget2(PrivateMknBuf + wb_section_offset + iCCT * 6);
+                icWBCCTC[iCCT][3] = sget2(PrivateMknBuf + wb_section_offset + iCCT * 6 + 4);
+              }
+            }
+          }
+        }
+        else // process 4K raf data
+        {
+          int wb[4];
+          int nWB, tWB, pWB;
+          int iCCT = 0;
+          is_4K_RAFdata = 1; /* X-A3, X-A5, X-A7, X-A10, X-A20, X-T100, X-T200, XF10 */
+          posWB = posPrivateMknBuf + 0x200;
+          for (int wb_ind = 0; wb_ind < 42; wb_ind++)
+          {
+            nWB = sget4(PrivateMknBuf + posWB);
+            posWB += 4;
+            tWB = sget4(PrivateMknBuf + posWB);
+            posWB += 4;
+            wb[0] = sget4(PrivateMknBuf + posWB) << 1;
+            posWB += 4;
+            wb[1] = sget4(PrivateMknBuf + posWB);
+            posWB += 4;
+            wb[3] = sget4(PrivateMknBuf + posWB);
+            posWB += 4;
+            wb[2] = sget4(PrivateMknBuf + posWB) << 1;
+            posWB += 4;
+
+            if (tWB && (iCCT < 255))
+            {
+              icWBCCTC[iCCT][0] = tWB;
+              FORC4 icWBCCTC[iCCT][c + 1] = wb[c];
+              iCCT++;
+            }
+            if (nWB != 0x46)
+            {
+              for (pWB = 1; pWB < Fuji_wb_list2.size(); pWB += 2)
+              {
+                if (Fuji_wb_list2[pWB] == nWB)
+                {
+                  FORC4 icWBC[Fuji_wb_list2[pWB - 1]][c] =
+                      wb[c];
+                  break;
+                }
+              }
+            }
+          }
+        }
+      }
+      posPrivateMknBuf += PrivateTagBytes;
+    }
+    free(PrivateMknBuf);
+  }
+#undef imfRAFVersion
+#undef imfRAFDataVersion
+#undef CHECKSPACE
+}
+void LibRaw::parseFujiMakernotes(unsigned tag, unsigned type, unsigned len,
+                                 unsigned dng_writer)
+{
+  if ((dng_writer == nonDNG) && (tag == 0x0010))
+  {
+    char FujiSerial[sizeof(imgdata.shootinginfo.InternalBodySerial)];
+    char *words[4];
+    char yy[2], mm[3], dd[3], ystr[16], ynum[16];
+    int year, nwords, ynum_len;
+    unsigned c;
+    memset(FujiSerial, 0, sizeof(imgdata.shootinginfo.InternalBodySerial));
+    ifp->read(FujiSerial, MIN(len,sizeof(FujiSerial)), 1);
+    nwords = getwords(FujiSerial, words, 4,
+                      sizeof(imgdata.shootinginfo.InternalBodySerial));
+    for (int i = 0; i < nwords; i++)
+    {
+      mm[2] = dd[2] = 0;
+      if (strnlen(words[i],
+                  sizeof(imgdata.shootinginfo.InternalBodySerial) - 1) < 18)
+      {
+        if (i == 0)
+        {
+          strncpy(imgdata.shootinginfo.InternalBodySerial, words[0],
+                  sizeof(imgdata.shootinginfo.InternalBodySerial) - 1);
+        }
+        else
+        {
+          char tbuf[sizeof(imgdata.shootinginfo.InternalBodySerial)];
+          snprintf(tbuf, sizeof(tbuf)-1, "%s %s",
+                   imgdata.shootinginfo.InternalBodySerial, words[i]);
+          strncpy(imgdata.shootinginfo.InternalBodySerial, tbuf,
+                  sizeof(imgdata.shootinginfo.InternalBodySerial) - 1);
+        }
+      }
+      else
+      {
+        strncpy(
+            dd,
+            words[i] +
+                strnlen(words[i],
+                        sizeof(imgdata.shootinginfo.InternalBodySerial) - 1) -
+                14,
+            2);
+        strncpy(
+            mm,
+            words[i] +
+                strnlen(words[i],
+                        sizeof(imgdata.shootinginfo.InternalBodySerial) - 1) -
+                16,
+            2);
+        strncpy(
+            yy,
+            words[i] +
+                strnlen(words[i],
+                        sizeof(imgdata.shootinginfo.InternalBodySerial) - 1) -
+                18,
+            2);
+        year = (yy[0] - '0') * 10 + (yy[1] - '0');
+        if (year < 70)
+          year += 2000;
+        else
+          year += 1900;
+
+        ynum_len = MIN(
+            int(sizeof(ynum) - 1),
+            (int)strnlen(words[i],
+                         sizeof(imgdata.shootinginfo.InternalBodySerial) - 1) -
+                18);
+        strncpy(ynum, words[i], ynum_len);
+        ynum[ynum_len] = 0;
+        for (int j = 0; ynum[j] && ynum[j + 1] && sscanf(ynum + j, "%2x", &c);
+             j += 2)
+          ystr[j / 2] = c;
+        ystr[ynum_len / 2 + 1] = 0;
+        strcpy(model2, ystr);
+
+        if (i == 0)
+        {
+          char tbuf[sizeof(imgdata.shootinginfo.InternalBodySerial)];
+
+          if (nwords == 1)
+          {
+            snprintf(
+                tbuf, sizeof(tbuf), "%s %s %d:%s:%s",
+                words[0] +
+                    strnlen(words[0],
+                            sizeof(imgdata.shootinginfo.InternalBodySerial) -
+                                1) -
+                    12,
+                ystr, year, mm, dd);
+          }
+          else
+          {
+            snprintf(
+                tbuf, sizeof(tbuf), "%s %d:%s:%s %s", ystr, year, mm, dd,
+                words[0] +
+                    strnlen(words[0],
+                            sizeof(imgdata.shootinginfo.InternalBodySerial) -
+                                1) -
+                    12);
+          }
+          strncpy(imgdata.shootinginfo.InternalBodySerial, tbuf,
+                  sizeof(imgdata.shootinginfo.InternalBodySerial) - 1);
+        }
+        else
+        {
+          char tbuf[sizeof(imgdata.shootinginfo.InternalBodySerial)];
+          snprintf(
+              tbuf, sizeof(tbuf), "%s %s %d:%s:%s %s",
+              imgdata.shootinginfo.InternalBodySerial, ystr, year, mm, dd,
+              words[i] +
+                  strnlen(words[i],
+                          sizeof(imgdata.shootinginfo.InternalBodySerial) - 1) -
+                  12);
+          strncpy(imgdata.shootinginfo.InternalBodySerial, tbuf,
+                  sizeof(imgdata.shootinginfo.InternalBodySerial) - 1);
+        }
+      }
+    }
+  }
+  else
+    switch (tag)
+    {
+    case 0x1002:
+      imFuji.WB_Preset = get2();
+      break;
+    case 0x1011:
+      imCommon.FlashEC = getreal(type);
+      break;
+    case 0x1020:
+      imFuji.Macro = get2();
+      break;
+    case 0x1021:
+      imFuji.FocusMode = imgdata.shootinginfo.FocusMode = get2();
+      break;
+    case 0x1022:
+      imFuji.AFMode = get2();
+      break;
+    case 0x1023:
+      imFuji.FocusPixel[0] = get2();
+      imFuji.FocusPixel[1] = get2();
+      break;
+    case 0x1034:
+      imFuji.ExrMode = get2();
+      break;
+    case 0x104d:
+      FujiCropMode = get2(); // odd: one of raw dimensions here can be lost
+      break;
+    case 0x1050:
+      imFuji.ShutterType = get2();
+      break;
+    case 0x1103:
+      imgdata.shootinginfo.DriveMode = get2();
+      imFuji.DriveMode = imgdata.shootinginfo.DriveMode & 0xff;
+      break;
+
+    case 0x1400:
+      imFuji.DynamicRange = get2();
+      break;
+    case 0x1401:
+      imFuji.FilmMode = get2();
+      break;
+    case 0x1402:
+      imFuji.DynamicRangeSetting = get2();
+      break;
+    case 0x1403:
+      imFuji.DevelopmentDynamicRange = get2();
+      break;
+    case 0x140b:
+      imFuji.AutoDynamicRange = get2();
+      break;
+    case 0x1443:
+      imFuji.DRangePriority = get2();
+      break;
+    case 0x1444:
+      imFuji.DRangePriorityAuto = get2();
+      break;
+    case 0x1445:
+      imFuji.DRangePriorityFixed = get2();
+      break;
+
+    case 0x1404:
+      ilm.MinFocal = getreal(type);
+      break;
+    case 0x1405:
+      ilm.MaxFocal = getreal(type);
+      break;
+    case 0x1406:
+      ilm.MaxAp4MinFocal = getreal(type);
+      break;
+    case 0x1407:
+      ilm.MaxAp4MaxFocal = getreal(type);
+      break;
+    case 0x1422:
+      imFuji.ImageStabilization[0] = get2();
+      imFuji.ImageStabilization[1] = get2();
+      imFuji.ImageStabilization[2] = get2();
+      imgdata.shootinginfo.ImageStabilization =
+          (imFuji.ImageStabilization[0] << 9) + imFuji.ImageStabilization[1];
+      break;
+    case 0x1431:
+      imFuji.Rating = get4();
+      break;
+    case 0x3820:
+      imFuji.FrameRate = get2();
+      break;
+    case 0x3821:
+      imFuji.FrameWidth = get2();
+      break;
+    case 0x3822:
+      imFuji.FrameHeight = get2();
+      break;
+    }
+  return;
+}
+
+void LibRaw::parse_fuji(int offset)
+{
+  unsigned entries, tag, len, save, c;
+  ushort raw_inset_present = 0;
+  ushort use_WBcorr_coeffs = 0;
+  double wbR_corr = 1.0;
+  double wbB_corr = 1.0;
+
+  fseek(ifp, offset, SEEK_SET);
+  entries = get4();
+  if (entries > 255)
+    return;
+  imgdata.process_warnings |= LIBRAW_WARN_PARSEFUJI_PROCESSED;
+
+  if (strstr(model, "S7000") ||
+      strstr(model, "S5000") ||
+      strstr(model, "F700")  ||
+      strstr(model, "S2Pro") ||
+      strstr(model, "S20Pro")) {
+    use_WBcorr_coeffs = 1;
+    wbR_corr = 10.0 / 17.0 / 0.652941;
+    wbB_corr = 2.0 /3.0 / (3.0 / 4.0 + 1.0 / 300.0);
+  }
+
+  while (entries--)
+  {
+    tag = get2();
+    len = get2();
+    save = ftell(ifp);
+
+    if (tag == 0x0100) // RawImageFullSize
+    {
+      raw_height = get2();
+      raw_width = get2();
+      raw_inset_present = 1;
+    }
+    else if (tag == 0x0121) // RawImageSize
+    {
+      height = get2();
+      if ((width = get2()) == 4284)
+        width += 3;
+    }
+    else if (tag == 0x0130) // FujiLayout
+    {
+      fuji_layout = fgetc(ifp) >> 7;
+      fuji_width = !(fgetc(ifp) & 8);
+    }
+    else if (tag == 0x0131) // XTransLayout
+    {
+      filters = 9;
+      char *xtrans_abs_alias = &xtrans_abs[0][0];
+      FORC(36)
+      {
+        int q = fgetc(ifp);
+        xtrans_abs_alias[35 - c] = MAX(0, MIN(q, 2)); /* & 3;*/
+      }
+    }
+    else if (tag == 0x2ff0) // WB_GRGBLevels
+    {
+      FORC4 cam_mul[GRGB_2_RGBG(c)] = get2();
+      if (use_WBcorr_coeffs) {
+       cam_mul[0] *= wbR_corr;
+       cam_mul[2] *= wbB_corr;
+      }
+    }
+
+    else if ((tag == 0x0110) && raw_inset_present) // RawImageCropTopLeft
+    {
+      imgdata.sizes.raw_inset_crop.ctop = get2();
+      imgdata.sizes.raw_inset_crop.cleft = get2();
+    }
+    else if ((tag == 0x0111) && raw_inset_present) // RawImageCroppedSize
+    {
+      imgdata.sizes.raw_inset_crop.cheight = get2();
+      imgdata.sizes.raw_inset_crop.cwidth = get2();
+    }
+    else if ((tag == 0x0115) && raw_inset_present) // RawImageAspectRatio
+    {
+      int a = get2();
+      int b = get2();
+      if (a * b == 6)
+        imgdata.sizes.raw_inset_crop.aspect = LIBRAW_IMAGE_ASPECT_3to2;
+      else if (a * b == 12)
+        imgdata.sizes.raw_inset_crop.aspect = LIBRAW_IMAGE_ASPECT_4to3;
+      else if (a * b == 144)
+        imgdata.sizes.raw_inset_crop.aspect = LIBRAW_IMAGE_ASPECT_16to9;
+      else if (a * b == 1)
+        imgdata.sizes.raw_inset_crop.aspect = LIBRAW_IMAGE_ASPECT_1to1;
+    }
+    else if (tag == 0x9200) // RelativeExposure
+    {
+      int a = get4();
+      if ((a == 0x01000100) || (a <= 0))
+        imFuji.BrightnessCompensation = 0.0f;
+      else if (a == 0x00100100)
+        imFuji.BrightnessCompensation = 4.0f;
+      else
+        imFuji.BrightnessCompensation =
+            24.0f - float(log((double)a) / log(2.0));
+    }
+    else if (tag == 0x9650) // RawExposureBias
+    {
+      short a = (short)get2();
+      float b = fMAX(1.0f, get2());
+      imFuji.ExpoMidPointShift = a / b;
+    }
+    else if (tag == 0x2f00) // WB_GRGBLevels
+    {
+      int nWBs = get4();
+      nWBs = MIN(nWBs, 6);
+      for (int wb_ind = 0; wb_ind < nWBs; wb_ind++)
+      {
+        FORC4 icWBC[LIBRAW_WBI_Custom1 + wb_ind][GRGB_2_RGBG(c)] = get2();
+        if (use_WBcorr_coeffs) {
+          icWBC[LIBRAW_WBI_Custom1 + wb_ind][0] *= wbR_corr;
+          icWBC[LIBRAW_WBI_Custom1 + wb_ind][2] *= wbB_corr;
+        }
+        fseek(ifp, 8, SEEK_CUR);
+      }
+    }
+    else if (tag == 0x2000) // WB_GRGBLevelsAuto
+    {
+      FORC4 icWBC[LIBRAW_WBI_Auto][GRGB_2_RGBG(c)] = get2();
+      if (use_WBcorr_coeffs) {
+        icWBC[LIBRAW_WBI_Auto][0] *= wbR_corr;
+        icWBC[LIBRAW_WBI_Auto][2] *= wbB_corr;
+      }
+    }
+    else if (tag == 0x2100) // WB_GRGBLevelsDaylight
+    {
+      FORC4 icWBC[LIBRAW_WBI_FineWeather][GRGB_2_RGBG(c)] = get2();
+      if (use_WBcorr_coeffs) {
+        icWBC[LIBRAW_WBI_FineWeather][0] *= wbR_corr;
+        icWBC[LIBRAW_WBI_FineWeather][2] *= wbB_corr;
+      }
+    }
+    else if (tag == 0x2200) // WB_GRGBLevelsCloudy
+    {
+      FORC4 icWBC[LIBRAW_WBI_Shade][GRGB_2_RGBG(c)] = get2();
+      if (use_WBcorr_coeffs) {
+        icWBC[LIBRAW_WBI_Shade][0] *= wbR_corr;
+        icWBC[LIBRAW_WBI_Shade][2] *= wbB_corr;
+      }
+    }
+    else if (tag == 0x2300) // WB_GRGBLevelsDaylightFluor
+    {
+      FORC4 icWBC[LIBRAW_WBI_FL_D][GRGB_2_RGBG(c)] = get2();
+      if (use_WBcorr_coeffs) {
+        icWBC[LIBRAW_WBI_FL_D][0] *= wbR_corr;
+        icWBC[LIBRAW_WBI_FL_D][2] *= wbB_corr;
+      }
+    }
+    else if (tag == 0x2301) // WB_GRGBLevelsDayWhiteFluor
+    {
+      FORC4 icWBC[LIBRAW_WBI_FL_N][GRGB_2_RGBG(c)] = get2();
+      if (use_WBcorr_coeffs) {
+        icWBC[LIBRAW_WBI_FL_N][0] *= wbR_corr;
+        icWBC[LIBRAW_WBI_FL_N][2] *= wbB_corr;
+      }
+    }
+    else if (tag == 0x2302) // WB_GRGBLevelsWhiteFluorescent
+    {
+      FORC4 icWBC[LIBRAW_WBI_FL_WW][GRGB_2_RGBG(c)] = get2();
+      if (use_WBcorr_coeffs) {
+        icWBC[LIBRAW_WBI_FL_WW][0] *= wbR_corr;
+        icWBC[LIBRAW_WBI_FL_WW][2] *= wbB_corr;
+      }
+    }
+    else if (tag == 0x2310) // WB_GRGBLevelsWarmWhiteFluor
+    {
+      FORC4 icWBC[LIBRAW_WBI_FL_L][GRGB_2_RGBG(c)] = get2();
+      if (use_WBcorr_coeffs) {
+        icWBC[LIBRAW_WBI_FL_L][0] *= wbR_corr;
+        icWBC[LIBRAW_WBI_FL_L][2] *= wbB_corr;
+      }
+    }
+    else if (tag == 0x2311) // WB_GRGBLevelsLivingRoomWarmWhiteFluor
+    {
+      FORC4 icWBC[LIBRAW_WBI_FL_W][GRGB_2_RGBG(c)] = get2();
+      if (use_WBcorr_coeffs) {
+        icWBC[LIBRAW_WBI_FL_W][0] *= wbR_corr;
+        icWBC[LIBRAW_WBI_FL_W][2] *= wbB_corr;
+      }
+    }
+    else if (tag == 0x2400) // WB_GRGBLevelsTungsten
+    {
+      FORC4 icWBC[LIBRAW_WBI_Tungsten][GRGB_2_RGBG(c)] = get2();
+      if (use_WBcorr_coeffs) {
+        icWBC[LIBRAW_WBI_Tungsten][0] *= wbR_corr;
+        icWBC[LIBRAW_WBI_Tungsten][2] *= wbB_corr;
+      }
+    }
+    else if (tag == 0x2410)
+    {
+      FORC4 icWBC[LIBRAW_WBI_Flash][GRGB_2_RGBG(c)] = get2();
+      if (use_WBcorr_coeffs) {
+        icWBC[LIBRAW_WBI_Flash][0] *= wbR_corr;
+        icWBC[LIBRAW_WBI_Flash][2] *= wbB_corr;
+      }
+    }
+
+    else if (tag == 0xc000) // RAFData
+    {
+      c = order;
+      order = 0x4949;
+      if (len > 20000)
+      {
+        tag = get4();
+        if (tag > 10000)
+        {
+          imFuji.RAFDataVersion = tag >> 16;
+          if (!imFuji.RAFDataVersion)
+            imFuji.RAFDataVersion = tag;
+          tag = get4();
+        }
+        if (tag > 10000)
+        { // if it is >1000 it normally contains 0x53545257, "WRTS"
+          tag = get4();
+        }
+        width = tag;
+        height = get4();
+        if (width > raw_width)
+          width = raw_width;
+        if (height > raw_height)
+          height = raw_height;
+      }
+      if (len == 4096) // X-A3, X-A5, X-A7, X-A10, X-A20, X-T100, X-T200, XF10
+      {                // Ill.A aligned to CCT 2850
+        int wb[4];
+        int nWB, tWB, pWB;
+        int iCCT = 0;
+        is_4K_RAFdata = 1;
+        fseek(ifp, save + 0x200, SEEK_SET);
+        for (int wb_ind = 0; wb_ind < 42; wb_ind++)
+        {
+          nWB = get4();
+          tWB = get4();
+          wb[0] = get4() << 1;
+          wb[1] = get4();
+          wb[3] = get4();
+          wb[2] = get4() << 1;
+          if (tWB && (iCCT < 255))
+          {
+            icWBCCTC[iCCT][0] = tWB;
+            FORC4 icWBCCTC[iCCT][c + 1] = wb[c];
+            iCCT++;
+          }
+          if (nWB != 70)
+          {
+            for (pWB = 1; pWB < Fuji_wb_list2.size(); pWB += 2)
+            {
+              if (Fuji_wb_list2[pWB] == nWB)
+              {
+                FORC4 icWBC[Fuji_wb_list2[pWB - 1]][c] =
+                    wb[c];
+                break;
+              }
+            }
+          }
+        }
+      }
+      else
+      {
+        libraw_internal_data.unpacker_data.posRAFData = save;
+        libraw_internal_data.unpacker_data.lenRAFData = (len >> 1);
+      }
+      order = c;
+    }
+    fseek(ifp, save + len, SEEK_SET);
+  }
+  height <<= fuji_layout;
+  width >>= fuji_layout;
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/metadata/hasselblad_model.cpp libkdcraw/libkdcraw/libraw/src/metadata/hasselblad_model.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/metadata/hasselblad_model.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/metadata/hasselblad_model.cpp	2022-11-07 07:46:31.734795008 +0300
@@ -0,0 +1,487 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+#include "../../internal/libraw_cameraids.h"
+
+  static const struct {
+    const int idx;
+    const char *FormatName;
+  } HassyRawFormat[] = {
+    { LIBRAW_HF_Unknown, "Unknown"},
+    { LIBRAW_HF_3FR, "-3FR"},
+    { LIBRAW_HF_FFF, "-FFF"},
+    { LIBRAW_HF_Imacon, "Imacon"},
+    { LIBRAW_HF_HasselbladDNG, "hDNG"},
+    { LIBRAW_HF_AdobeDNG, "aDNG"},
+    { LIBRAW_HF_AdobeDNG_fromPhocusDNG, "a(hDNG)"},
+  };
+
+const char* LibRaw::HassyRawFormat_idx2HR(unsigned idx) // HR means "human-readable"
+{
+    for (int i = 0; i < int(sizeof HassyRawFormat / sizeof *HassyRawFormat); i++)
+        if((unsigned)HassyRawFormat[i].idx == idx)
+            return HassyRawFormat[i].FormatName;
+    return 0;
+}
+
+void LibRaw::process_Hassy_Lens (int LensMount) {
+// long long unsigned id =
+//    mount*100000000ULL + series*10000000ULL +
+//    focal1*10000ULL + focal2*10 + version;
+  char *ps;
+  int c = atoi(strchr(imgdata.lens.Lens, ' ') +1);
+  if (!c)
+    return;
+
+  if (LensMount == LIBRAW_MOUNT_Hasselblad_H) {
+    if (imgdata.lens.Lens[2] == ' ') // HC lens
+      ilm.LensID = LensMount*100000000ULL + 10000000ULL;
+    else                             // HCD lens
+      ilm.LensID = LensMount*100000000ULL + 20000000ULL;
+    ilm.LensFormat = LIBRAW_FORMAT_645;
+  } else if (LensMount == LIBRAW_MOUNT_Hasselblad_XCD) {
+    ilm.LensFormat = LIBRAW_FORMAT_CROP645;
+    ilm.LensID = LensMount*100000000ULL;
+  } else
+    return;
+
+  ilm.LensMount = LensMount;
+  ilm.LensID += c*10000ULL;
+  if ((ps=strchr(imgdata.lens.Lens, '-'))) {
+    ilm.FocalType = LIBRAW_FT_ZOOM_LENS;
+    ilm.LensID += atoi(ps+1)*10ULL;
+  } else {
+    ilm.FocalType = LIBRAW_FT_PRIME_LENS;
+    ilm.LensID += c*10ULL;
+  }
+  if (strstr(imgdata.lens.Lens, "III"))
+    ilm.LensID += 3ULL;
+  else if (strstr(imgdata.lens.Lens, "II"))
+    ilm.LensID += 2ULL;
+}
+
+void LibRaw::parseHassyModel() {
+
+static const char *Hasselblad_Ctrl[] = { // manually selectable options only
+  "ELD", "ELX", "Winder CW", "CW", "Pinhole", "Flash Sync",
+  "SWC", "200 (Mod)", "200", "500 Mech.", "500", "H Series",
+  "H-Series", "H1", "H2", "Black Box", "LENSCONTROL S", "LENSCTRL S", "Generic",
+};
+
+static const char *Hasselblad_SensorEnclosures[] = {
+  "CFH", "CFV", "CFV", "CFII", "CF", "Ixpress",
+};
+
+  char tmp_model[64];
+  const char *ps;
+  int c;
+  int nPix = raw_width*raw_height;
+  int add_MP_toName = 1;
+  int norm_model_isSet = 0;
+
+  if (model[0] == ' ')
+    memmove(model, model+1, MIN(sizeof(model)-1,strlen(model)));
+
+  if (!imHassy.format) {
+    if (dng_version) {
+      if (!strncmp(software, "Adobe", 5)) {
+        if (!imgdata.color.OriginalRawFileName[0] ||
+            !imgdata.color.LocalizedCameraModel[0] ||
+            !strcasestr(imgdata.color.UniqueCameraModel, "coated"))
+          imHassy.format = LIBRAW_HF_AdobeDNG_fromPhocusDNG;
+        else
+          imHassy.format = LIBRAW_HF_AdobeDNG;
+      } else imHassy.format = LIBRAW_HF_HasselbladDNG;
+    } else if ((imHassy.nIFD_CM[0] != -1) &&
+               (imHassy.nIFD_CM[1] == -1) &&
+               !imHassy.mnColorMatrix[0][0]) {
+      imHassy.format = LIBRAW_HF_3FR;
+    } else imHassy.format = LIBRAW_HF_FFF;
+  }
+
+  if (!strncmp(imHassy.SensorUnitConnector, "Hasselblad ", 11))
+    memmove(imHassy.SensorUnitConnector, imHassy.SensorUnitConnector+11, 64-11);
+
+  if (imHassy.format == LIBRAW_HF_AdobeDNG) { // Adobe DNG, use LocalizedCameraModel
+      imgdata.color.LocalizedCameraModel[63] = 0; // make sure 0-termination
+    if ((ps = strrchr(imgdata.color.LocalizedCameraModel, '-')))
+      c = ps-imgdata.color.LocalizedCameraModel;
+    else c = strlen(imgdata.color.LocalizedCameraModel);
+    int cc = MIN(c, sizeof(tmp_model)-1);
+    memcpy(tmp_model, imgdata.color.LocalizedCameraModel,cc);
+    tmp_model[cc] = 0;
+    if (strcasestr(imgdata.color.UniqueCameraModel, "coated")) {
+      strncpy(normalized_model, imgdata.color.UniqueCameraModel,sizeof(imgdata.color.UniqueCameraModel)-1);
+      normalized_model[sizeof(imgdata.color.UniqueCameraModel) - 1] = 0;
+      norm_model_isSet = 1;
+    }
+      if (!strncmp(normalized_model, "Hasselblad ", 11))
+        memmove(normalized_model, normalized_model+11, 64-11);
+  }
+  else
+  {
+      strncpy(tmp_model, imgdata.color.UniqueCameraModel, sizeof(imgdata.color.UniqueCameraModel) - 1);
+      tmp_model[63] = 0;
+  }
+  if (!strncasecmp(tmp_model, "Hasselblad ", 11))
+    memmove(tmp_model, tmp_model+11, 64-11);
+
+// check if model tag contains manual CaptureSequenceInitiator info:
+  strncpy(imHassy.CaptureSequenceInitiator, model,31);
+  imHassy.CaptureSequenceInitiator[31] = 0;
+  FORC(int(sizeof Hasselblad_Ctrl / sizeof *Hasselblad_Ctrl)) {
+    if (strcasestr(model, Hasselblad_Ctrl[c])) {
+// yes, fill 'model' with sensor unit data
+      strncpy(model, tmp_model,63);
+      model[63] = 0;
+      break;
+    }
+  }
+
+  ps = strchr(model, '-');
+  if (ps) {                  // check if model contains both host body and sensor version, resolution, MS info
+    strncpy(imHassy.SensorUnit, model,63);
+    memcpy(imHassy.HostBody, model, ps-model);
+    imHassy.HostBody[ps-model] = 0;
+    if (!strncmp(ps-2, "II-", 3))
+      ps -=2;
+    strncpy(imHassy.Sensor, ps,7);
+    imHassy.Sensor[7] = 0;
+    add_MP_toName = 0;
+  } else {                   // model contains host body only
+    strncpy(imHassy.HostBody, model,63);
+    imHassy.HostBody[63] = 0;
+// fill 'model' with sensor unit data
+    strncpy(model, tmp_model,63);
+    model[63] = 0;
+  }
+
+  if (!strncmp(model, "503CWD", 6)) {
+    strncpy(imHassy.HostBody, model,63);
+    imHassy.HostBody[63] = 0;
+    ilm.CameraFormat = LIBRAW_FORMAT_66;
+    ilm.CameraMount = LIBRAW_MOUNT_Hasselblad_V;
+    if (model[6] == 'I' && model[7] == 'I')
+      strcpy(model, "CFVII");
+    else strcpy(model, "CFV");
+  } else if (strstr(model, "Hasselblad") &&
+             (model[10] != ' ')) {
+    strcpy(model, "CFV");
+    ilm.CameraMount = LIBRAW_MOUNT_DigitalBack;
+  } else {
+    FORC(int(sizeof Hasselblad_SensorEnclosures / sizeof *Hasselblad_SensorEnclosures)) {
+      if (strcasestr(model, Hasselblad_SensorEnclosures[c])) {
+        if (add_MP_toName) strcpy(model, Hasselblad_SensorEnclosures[c]);
+        ilm.CameraMount = LIBRAW_MOUNT_DigitalBack;
+        break;
+      }
+    }
+  }
+
+#define cpynorm(str)                \
+  if (!norm_model_isSet) {          \
+    strcpy(normalized_model, str);  \
+    norm_model_isSet = 1;           \
+  }
+
+  if ((imHassy.SensorCode == 4) &&
+      (imHassy.CoatingCode < 2)) {
+    strcpy(imHassy.Sensor, "-16");
+    cpynorm("16-Uncoated");
+
+  } else if ((imHassy.SensorCode == 6) &&
+             (imHassy.CoatingCode < 2)) {
+    strcpy(imHassy.Sensor, "-22");
+    cpynorm("22-Uncoated");
+
+  } else if ((imHassy.SensorCode == 8) &&
+             (imHassy.CoatingCode == 1)) {
+    strcpy(imHassy.Sensor, "-31");
+    cpynorm("31-Uncoated");
+
+  } else if ((imHassy.SensorCode == 9) &&
+             (imHassy.CoatingCode < 2)) {
+    strcpy(imHassy.Sensor, "-39");
+    cpynorm("39-Uncoated");
+
+  } else if ((imHassy.SensorCode == 9) &&
+             (imHassy.CoatingCode == 4)) {
+    strcpy(imHassy.Sensor, "-39");
+    strcpy(model, "H3DII");
+    add_MP_toName = 1;
+    cpynorm("39-Coated");
+
+  } else if ((imHassy.SensorCode == 13) &&
+             (imHassy.CoatingCode == 4)) {
+    strcpy(imHassy.Sensor, "-40");
+    cpynorm("40-Coated");
+
+  } else if ((imHassy.SensorCode == 13) &&
+             (imHassy.CoatingCode == 5)) {
+    strcpy(imHassy.Sensor, "-40");
+    cpynorm("40-Coated5");
+
+  } else if ((imHassy.SensorCode == 11) &&
+             (imHassy.CoatingCode == 4)) {
+    if (!strncmp(model, "H3D", 3))
+      strcpy(model, "H3DII-50");
+    else strcpy(imHassy.Sensor, "-50");
+    cpynorm("50-Coated");
+
+  } else if ((imHassy.SensorCode == 11) &&
+             (imHassy.CoatingCode == 5)) {
+    strcpy(imHassy.Sensor, "-50");
+    cpynorm("50-Coated5");
+
+  } else if ((imHassy.SensorCode == 15) &&
+             (imHassy.CoatingCode == 5)) {
+    strcpy(imHassy.Sensor, "-50c");
+    cpynorm("50-15-Coated5");
+    if (!strncmp(imHassy.CaptureSequenceInitiator, "X1D", 3)) {
+      imHassy.SensorSubCode = 2;
+      add_MP_toName = 0;
+      strcat(imHassy.Sensor, " II");
+      if (!strncasecmp(imHassy.CaptureSequenceInitiator, "X1D II 50C", 10)) {
+        strcpy(model, "X1D II 50C");
+        strcat(normalized_model, "-II");
+      } else {
+        strcpy(model, "X1D-50c");
+      }
+    }
+
+  } else if ((imHassy.SensorCode == 12) &&
+             (imHassy.CoatingCode == 4)) {
+    strcpy(imHassy.Sensor, "-60");
+    cpynorm("60-Coated");
+
+  } else if ((imHassy.SensorCode == 17) &&
+             (imHassy.CoatingCode == 5)) {
+    strcpy(imHassy.Sensor, "-100c");
+    cpynorm("100-17-Coated5");
+
+  } else if ((raw_width == 4090) || // V96C
+             ((raw_width == 4096) && (raw_height == 4096)) ||
+             ((raw_width == 4088) && (raw_height == 4088)) || // Adobe crop
+             ((raw_width == 4080) && (raw_height == 4080))) { // Phocus crop
+    strcpy(imHassy.Sensor, "-16");
+    cpynorm("16-Uncoated");
+    if (!imHassy.SensorCode) imHassy.SensorCode = 4;
+
+  } else if ((raw_width == 5568) && (raw_height == 3648)) {
+    strcpy(imHassy.Sensor, "-20c");
+
+  } else if (((raw_width == 4096) && (raw_height == 5456)) ||
+             ((raw_width == 4088) && (raw_height == 5448)) ||  // Adobe crop
+             ((raw_width == 4080) && (raw_height == 5440))) {  // Phocus crop
+    strcpy(imHassy.Sensor, "-22");
+    cpynorm("22-Uncoated");
+    if (!imHassy.SensorCode) imHassy.SensorCode = 6;
+
+  } else if (((raw_width == 6542) && (raw_height == 4916)) ||
+             ((raw_width == 6504) && (raw_height == 4880)) || // Adobe crop
+             ((raw_width == 6496) && (raw_height == 4872))) { // Phocus crop
+    strcpy(imHassy.Sensor, "-31");
+    cpynorm("31-Uncoated");
+    if (!imHassy.SensorCode) imHassy.SensorCode = 8;
+
+  } else if (((raw_width == 7262) && (raw_height == 5456)) || //
+             ((raw_width == 7224) && (raw_height == 5420)) || // Adobe crop
+             ((raw_width == 7216) && (raw_height == 5412)) || // Phocus crop
+             ((raw_width == 7212) && (raw_height == 5412)) || // CF-39, CFV-39, possibly v.II; Phocus crop
+// uncropped, when the exact size is unknown, should be:
+// - greater or equal to the smallest Phocus crop for the current size
+// - smaller than the smallest Phocus crop for the next size
+           ((nPix >= 7212*5412) && (nPix < 7304*5478))) {
+    strcpy(imHassy.Sensor, "-39");
+    if (!imHassy.SensorCode) imHassy.SensorCode = 9;
+    if (!strncmp(model, "H3D", 3)) {
+      if (((imHassy.format == LIBRAW_HF_Imacon) ||
+          strstr(imgdata.color.UniqueCameraModel, "H3D-39") ||
+          strstr(imgdata.color.LocalizedCameraModel, "H3D-39") ||
+          strstr(model, "H3D-39")) &&
+          !strstr(imgdata.color.UniqueCameraModel, "II") &&
+          !strstr(imgdata.color.LocalizedCameraModel, "II") &&
+          !strstr(model, "II")) {
+        strcpy(model, "H3D-39");
+        add_MP_toName = 0;
+        cpynorm("39-Uncoated");
+
+      } else {
+        strcpy(model, "H3DII-39");
+        add_MP_toName = 0;
+        cpynorm("39-Coated");
+        if (!imHassy.CoatingCode) imHassy.CoatingCode = 4;
+      }
+
+    } else
+      cpynorm("39-Uncoated");
+
+  } else if (((raw_width == 7410) && (raw_height == 5586)) || // (H4D-40, H5D-40)
+             ((raw_width == 7312) && (raw_height == 5486)) || // Adobe crop
+             ((raw_width == 7304) && (raw_height == 5478))) { // Phocus crop
+    strcpy(imHassy.Sensor, "-40");
+    if (!strncmp(model, "H4D", 3)) {
+      cpynorm("40-Coated");
+      if (!imHassy.SensorCode) imHassy.SensorCode = 13;
+      if (!imHassy.CoatingCode) imHassy.CoatingCode = 4;
+    } else {
+      cpynorm("40-Coated5");
+      if (!imHassy.SensorCode) imHassy.SensorCode = 13;
+      if (!imHassy.CoatingCode) imHassy.CoatingCode = 5;
+    }
+
+  } else if (((raw_width == 8282) && (raw_height == 6240)) || // (CFV-50, H3DII-50, H5D-50)
+             ((raw_width == 8184) && (raw_height == 6140)) || // Adobe crop
+             ((raw_width == 8176) && (raw_height == 6132))) { // Phocus crop
+    strcpy(imHassy.Sensor, "-50");
+    if (!strncmp(model, "H5D", 3)) {
+      cpynorm("50-Coated5");
+      if (!imHassy.SensorCode) imHassy.SensorCode = 11;
+      if (!imHassy.CoatingCode) imHassy.CoatingCode = 5;
+    } else {
+      cpynorm("50-Coated"); // CFV-50, H3DII-50,
+      if (!strncmp(model, "H3D", 3)) {
+        strcpy(model, "H3DII-50");
+      if (!imHassy.SensorCode) imHassy.SensorCode = 11;
+      if (!imHassy.CoatingCode) imHassy.CoatingCode = 4;
+        add_MP_toName = 0;
+      }
+    }
+
+  } else if (((raw_width == 8374) && (raw_height == 6304)) ||  // (H5D-50c)
+             ((raw_width == 8384) && (raw_height == 6304)) ||  // (X1D-50c, "X1D II 50C")
+             ((raw_width == 8280) && (raw_height == 6208)) ||  // Adobe crop
+             ((raw_width == 8272) && (raw_height == 6200))) {  // Phocus crop
+    cpynorm("50-15-Coated5");
+    if (!imHassy.SensorCode) imHassy.SensorCode = 15;
+    if (!imHassy.CoatingCode) imHassy.CoatingCode = 5;
+    strcpy(imHassy.Sensor, "-50c");
+    if ((raw_width == 8384) || !strncmp(imHassy.CaptureSequenceInitiator, "X1D", 3)) {
+      imHassy.SensorSubCode = 2;
+      add_MP_toName = 0;
+      strcat(imHassy.Sensor, " II");
+      if (!strncasecmp(imHassy.CaptureSequenceInitiator, "X1D II 50C", 10)) {
+        strcpy(model, "X1D II 50C");
+        strcat(normalized_model, "-II");
+      } else {
+        strcpy(model, "X1D-50c");
+      }
+    }
+
+  } else if (((raw_width == 9044) && (raw_height == 6732)) ||
+             ((raw_width == 8964) && (raw_height == 6716)) || // Adobe crop
+             ((raw_width == 8956) && (raw_height == 6708))) { // Phocus crop
+    strcpy(imHassy.Sensor, "-60");
+    cpynorm("60-Coated");
+    if (!imHassy.SensorCode) imHassy.SensorCode = 12;
+    if (!imHassy.CoatingCode) imHassy.CoatingCode = 4;
+
+
+  } else if (((raw_width == 10320) && (raw_height == 7752)) || // Phocus crop, A5D-80
+             ((nPix >= 10320*7752) && (nPix < 10520*8000))) {
+    strcpy(imHassy.Sensor, "-80");
+    cpynorm("80-Coated");
+
+  } else if (((raw_width == 12000) && (raw_height == 8816)) ||
+             ((raw_width == 11608) && (raw_height == 8708)) || // Adobe crop
+             ((raw_width == 11600) && (raw_height == 8700))) {  // Phocus crop
+    strcpy(imHassy.Sensor, "-100c");
+    cpynorm("100-17-Coated5");
+    if (!imHassy.SensorCode) imHassy.SensorCode = 17;
+    if (!imHassy.CoatingCode) imHassy.CoatingCode = 5;
+
+  }
+
+  if (raw_width == 4090)
+    strcpy(model, "V96C");
+
+  if (
+    (raw_width == 4090) ||
+		((raw_width ==  4096) && (raw_height ==  4096)) ||
+		((raw_width ==  5568) && (raw_height ==  3648)) ||
+		((raw_width ==  4096) && (raw_height ==  5456)) ||
+		((raw_width ==  6542) && (raw_height ==  4916)) ||
+		((raw_width ==  7262) && (raw_height ==  5456)) ||
+		((raw_width ==  7410) && (raw_height ==  5586)) ||
+		((raw_width ==  8282) && (raw_height ==  6240)) ||
+		((raw_width ==  8374) && (raw_height ==  6304)) ||
+		((raw_width ==  8384) && (raw_height ==  6304)) ||
+		((raw_width ==  9044) && (raw_height ==  6732)) ||
+		((raw_width == 10320) && (raw_height ==  7752)) ||
+		((raw_width == 12000) && (raw_height ==  8816))
+	)
+	imHassy.uncropped = 1;
+
+
+  if (model[0] && add_MP_toName)
+    strcat(model, imHassy.Sensor);
+  if (imHassy.Sensor[0] == '-')
+    memmove(imHassy.Sensor, imHassy.Sensor+1, strlen(imHassy.Sensor));
+
+  if (dng_version &&
+      (imHassy.SensorCode == 13) &&
+      (imHassy.CoatingCode == 4)) {
+    c = LIBRAW_HF_AdobeDNG;
+  } else if ((imHassy.format == LIBRAW_HF_HasselbladDNG) ||
+             (imHassy.format == LIBRAW_HF_AdobeDNG_fromPhocusDNG)) {
+    c = LIBRAW_HF_FFF;
+  } else if (imHassy.format == LIBRAW_HF_Imacon) {
+    c = LIBRAW_HF_3FR;
+  } else {
+    c = imHassy.format;
+  }
+  ps = HassyRawFormat_idx2HR(c);
+  if ((c == LIBRAW_HF_3FR) ||
+      (c == LIBRAW_HF_FFF))
+    strcat(normalized_model, ps);
+
+  if (((imHassy.CaptureSequenceInitiator[0] == 'H') &&
+       (imHassy.CaptureSequenceInitiator[1] != 'a')) ||
+      ((imHassy.CaptureSequenceInitiator[0] == 'A') &&
+       isdigit(imHassy.CaptureSequenceInitiator[1]))) {
+    ilm.CameraFormat = LIBRAW_FORMAT_645;
+    ilm.CameraMount = LIBRAW_MOUNT_Hasselblad_H;
+    if (imgdata.lens.Lens[0] == 'H')
+      process_Hassy_Lens(LIBRAW_MOUNT_Hasselblad_H);
+  } else if ((imHassy.CaptureSequenceInitiator[0] == 'X') &&
+             isdigit(imHassy.CaptureSequenceInitiator[1])) {
+    ilm.CameraFormat = LIBRAW_FORMAT_CROP645;
+    ilm.CameraMount = LIBRAW_MOUNT_Hasselblad_XCD;
+    if (imgdata.lens.Lens[0] == 'H') {
+      process_Hassy_Lens(LIBRAW_MOUNT_Hasselblad_H);
+      strcpy(ilm.Adapter, "XH");
+    } else {
+      if (imgdata.lens.Lens[0] == 'X') {
+        process_Hassy_Lens(LIBRAW_MOUNT_Hasselblad_XCD);
+      } else if (!imgdata.lens.Lens[0] &&
+                 (aperture > 1.0f)   &&
+                 (focal_len > 10.0f)) {
+        ilm.LensID = focal_len;
+        if (ilm.LensID == 35) {
+          ilm.FocalType = LIBRAW_FT_ZOOM_LENS;
+          ilm.LensID = LIBRAW_MOUNT_Hasselblad_XCD*100000000ULL +
+                       35*10000ULL + 75*10;
+        }
+        else
+          ilm.FocalType = LIBRAW_FT_PRIME_LENS;
+          ilm.LensID = LIBRAW_MOUNT_Hasselblad_XCD*100000000ULL +
+                       ilm.LensID*10000ULL + ilm.LensID*10;
+      }
+    }
+  }
+  if (normalized_model[0]  && !CM_found)
+    CM_found = adobe_coeff(maker_index, normalized_model);
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/metadata/identify.cpp libkdcraw/libkdcraw/libraw/src/metadata/identify.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/metadata/identify.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/metadata/identify.cpp	2022-11-07 07:46:31.734795008 +0300
@@ -0,0 +1,2941 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+#include "../../internal/libraw_cameraids.h"
+
+// clang-format on
+static const struct
+{
+    const int CorpId;
+    const char *CorpName;
+} CorpTable[] = {
+    {LIBRAW_CAMERAMAKER_Agfa,           "AgfaPhoto"},
+    {LIBRAW_CAMERAMAKER_Apple,          "Apple"},
+    {LIBRAW_CAMERAMAKER_Broadcom,       "Broadcom"},
+    {LIBRAW_CAMERAMAKER_Canon,          "Canon"},
+    {LIBRAW_CAMERAMAKER_Casio,          "Casio"},
+    {LIBRAW_CAMERAMAKER_CINE,           "CINE"},
+    {LIBRAW_CAMERAMAKER_Epson,          "Epson"},
+    {LIBRAW_CAMERAMAKER_Fujifilm,       "Fujifilm"},
+    {LIBRAW_CAMERAMAKER_Mamiya,         "Mamiya"},
+    {LIBRAW_CAMERAMAKER_Motorola,       "Motorola"},
+    {LIBRAW_CAMERAMAKER_Kodak,          "Kodak"},
+    {LIBRAW_CAMERAMAKER_Konica,         "Konica"},
+    {LIBRAW_CAMERAMAKER_Minolta,        "Minolta"},
+    {LIBRAW_CAMERAMAKER_Leica,          "Leica"},
+    {LIBRAW_CAMERAMAKER_Nikon,          "Nikon"},
+    {LIBRAW_CAMERAMAKER_Nokia,          "Nokia"},
+    {LIBRAW_CAMERAMAKER_Olympus,        "Olympus"},
+    {LIBRAW_CAMERAMAKER_Ricoh,          "Ricoh"},
+    {LIBRAW_CAMERAMAKER_Pentax,         "Pentax"},
+    {LIBRAW_CAMERAMAKER_PhaseOne,       "Phase One"},
+    {LIBRAW_CAMERAMAKER_PhaseOne,       "PhaseOne"},
+    {LIBRAW_CAMERAMAKER_Samsung,        "Samsung"},
+    {LIBRAW_CAMERAMAKER_Sigma,          "Sigma"},
+    {LIBRAW_CAMERAMAKER_Sinar,          "Sinar"},
+    {LIBRAW_CAMERAMAKER_Sony,           "Sony"},
+    {LIBRAW_CAMERAMAKER_YI,             "YI"},
+    // add corp. names below
+    {LIBRAW_CAMERAMAKER_Alcatel,        "Alcatel"},
+    {LIBRAW_CAMERAMAKER_Aptina,         "Aptina"},
+    {LIBRAW_CAMERAMAKER_AVT,            "AVT"},
+    {LIBRAW_CAMERAMAKER_Baumer,         "Baumer"},
+    {LIBRAW_CAMERAMAKER_Clauss,         "Clauss"},
+    {LIBRAW_CAMERAMAKER_Contax,         "Contax"},
+    {LIBRAW_CAMERAMAKER_Creative,       "Creative"},
+    {LIBRAW_CAMERAMAKER_DJI,            "DJI"},
+    {LIBRAW_CAMERAMAKER_Foculus,        "Foculus"},
+    {LIBRAW_CAMERAMAKER_Generic,        "Generic"},
+    {LIBRAW_CAMERAMAKER_Gione,          "Gione"},
+    {LIBRAW_CAMERAMAKER_GITUP,          "GITUP"},
+    {LIBRAW_CAMERAMAKER_Hasselblad,     "Hasselblad"},
+    {LIBRAW_CAMERAMAKER_HTC,            "HTC"},
+    {LIBRAW_CAMERAMAKER_I_Mobile,       "I_Mobile"},
+    {LIBRAW_CAMERAMAKER_Imacon,         "Imacon"},
+    {LIBRAW_CAMERAMAKER_JK_Imaging,     "JK Imaging"}, // Kodak
+    {LIBRAW_CAMERAMAKER_Leaf,           "Leaf"},
+    {LIBRAW_CAMERAMAKER_Lenovo,         "Lenovo"},
+    {LIBRAW_CAMERAMAKER_LG,             "LG"},
+    {LIBRAW_CAMERAMAKER_Logitech,       "Logitech"},
+    {LIBRAW_CAMERAMAKER_Matrix,         "Matrix"},
+    {LIBRAW_CAMERAMAKER_Meizu,          "Meizu"},
+    {LIBRAW_CAMERAMAKER_Micron,         "Micron"},
+    {LIBRAW_CAMERAMAKER_NGM,            "NGM"},
+    {LIBRAW_CAMERAMAKER_OmniVison,      "OmniVison"},
+    {LIBRAW_CAMERAMAKER_Panasonic,      "Panasonic"},
+    {LIBRAW_CAMERAMAKER_Photron,        "Photron"},
+    {LIBRAW_CAMERAMAKER_Pixelink,       "Pixelink"},
+    {LIBRAW_CAMERAMAKER_Polaroid,       "Polaroid"},
+    {LIBRAW_CAMERAMAKER_Rollei,         "Rollei"},
+    {LIBRAW_CAMERAMAKER_RoverShot,      "RoverShot"},
+    {LIBRAW_CAMERAMAKER_SMaL,           "SMaL"},
+    {LIBRAW_CAMERAMAKER_ST_Micro,       "ST Micro"},
+    {LIBRAW_CAMERAMAKER_THL,            "THL"},
+    {LIBRAW_CAMERAMAKER_Xiaomi,         "Xiaomi"},
+    {LIBRAW_CAMERAMAKER_XIAOYI,         "Xiayi"},
+    {LIBRAW_CAMERAMAKER_Yuneec,         "Yuneec"},
+    {LIBRAW_CAMERAMAKER_DXO,            "DxO"},
+    {LIBRAW_CAMERAMAKER_RED,            "Red"},
+    {LIBRAW_CAMERAMAKER_PhotoControl,   "Photo Control"},
+    {LIBRAW_CAMERAMAKER_Google,         "Google"},
+    {LIBRAW_CAMERAMAKER_GoPro,          "GoPro"},
+    {LIBRAW_CAMERAMAKER_Parrot,         "Parrot"},
+    {LIBRAW_CAMERAMAKER_Zeiss,          "Zeiss"}
+};
+// clang-format on
+
+int LibRaw::setMakeFromIndex(unsigned makei)
+{
+	if (makei <= LIBRAW_CAMERAMAKER_Unknown || makei >= LIBRAW_CAMERAMAKER_TheLastOne) return 0;
+
+	for (int i = 0; i < int(sizeof CorpTable / sizeof *CorpTable); i++)
+		if ((unsigned)CorpTable[i].CorpId == makei)
+		{
+			strcpy(normalized_make, CorpTable[i].CorpName);
+			maker_index = makei;
+			return 1;
+		}
+	return 0;
+}
+
+const char *LibRaw::cameramakeridx2maker(unsigned maker)
+{
+    for (int i = 0; i < int(sizeof CorpTable / sizeof *CorpTable); i++)
+        if((unsigned)CorpTable[i].CorpId == maker)
+            return CorpTable[i].CorpName;
+    return 0;
+}
+
+
+void LibRaw::fixupArri()
+{
+    struct alist_t
+    {
+        const char *a_model;
+        const char *a_software;
+        ushort a_width,a_height;
+        int a_black;
+        unsigned a_filters;
+        float a_aspect;
+    }
+    alist[] =
+    {
+        {"ALEXA65", "Alexa65  XT", 6560 ,3100, 256,0x49494949,1.f},
+
+        {"ALEXALF", "Alexa LF Plus W", 3840 ,2160, 256,0x49494949,1.0f },
+        {"ALEXALF", "Alexa LF Plus W", 4448 ,1856, 256,0x49494949,0.75f },
+        {"ALEXALF", "Alexa LF Plus W", 4448 ,3096, 256,0x49494949,1.f },
+
+        {"ALEXA", "Alexa Plus 4:3 SXT", 2880 ,1620, 256,0x61616161,.75f},
+        {"ALEXA", "Alexa Plus 4:3 SXT", 3168 ,1782, 256,0x61616161,0.75f},
+        {"ALEXA", "Alexa Plus 4:3 SXT", 3424 ,2202, 256,0x61616161,1.f},
+        {"ALEXA", "Alexa Plus 4:3 SXT", 2592 ,2160, 256,0x61616161,1.12f},
+
+        {"ALEXA", "Alexa Plus 4:3 XT", 2592 ,2160, 256,0x61616161,1.12f},
+        {"ALEXA", "Alexa Plus 4:3 XT", 2880 ,2160, 256,0x61616161,1.f},
+        {"ALEXA", "Alexa Plus 4:3 XT", 2880 ,1620, 256,0x61616161,0.75f},
+        {"ALEXA", "Alexa Plus 4:3 XT", 3424 ,2202, 256,0x61616161,1.f},
+    };
+    for(int i = 0; i < int(sizeof(alist)/sizeof(alist[0])); i++)
+        if(!strncasecmp(model,alist[i].a_model,strlen(alist[i].a_model)) && software
+            && !strncasecmp(software,alist[i].a_software,strlen(alist[i].a_software))
+            && width == alist[i].a_width && height == alist[i].a_height)
+        {
+            filters = alist[i].a_filters;
+            black = alist[i].a_black;
+            pixel_aspect = alist[i].a_aspect;
+            strcpy(model,software);
+            software[0]=0;
+            return;
+        }
+}
+
+/*
+   Identify which camera created this file, and set global variables
+   accordingly.
+ */
+void LibRaw::identify()
+{
+  // clang-format off
+  static const ushort canon[][11] = {
+      // raw_width, raw_height, left_margin, top_margin, width_decrement,
+      // height_decrement, mask01, mask03, mask11,
+	  // mask13, CFA_filters.
+	  { 1944, 1416, 0, 0, 48, 0 }, // 00 "PowerShot Pro90 IS"
+	  { 2144, 1560, 4, 8, 52, 2, 0, 0, 0, 25 }, // 01 "PowerShot S30", "PowerShot G1"
+	  { 2224, 1456, 48, 6, 0, 2 }, // 02 "EOS D30"
+	  { 2376, 1728, 12, 6, 52, 2 }, // 03 "PowerShot G2", "PowerShot S40", "PowerShot G3", "PowerShot S45"
+	  { 2672, 1968, 12, 6, 44, 2 }, // 04 "PowerShot G5", "PowerShot S50", "PowerShot S60"
+	  { 3152, 2068, 64, 12, 0, 0, 16 }, // 05 "EOS D60", "EOS 10D", "EOS 300D"
+	  { 3160, 2344, 44, 12, 4, 4 }, // 06 "PowerShot G6", "PowerShot S70"
+	  { 3344, 2484, 4, 6, 52, 6 }, // 07 "PowerShot Pro1"
+	  { 3516, 2328, 42, 14, 0, 0 }, // 08 "EOS 350D"
+	  { 3596, 2360, 74, 12, 0, 0 }, // 09 "EOS-1D Mark II", "EOS 20D", "EOS-1D Mark II N", "EOS 30D"
+	  { 3744, 2784, 52, 12, 8, 12 }, // 10 "PowerShot G11", "PowerShot S90", "PowerShot G12", "PowerShot S95"
+	  { 3944, 2622, 30, 18, 6, 2 }, // 11 "EOS 40D"
+	  { 3948, 2622, 42, 18, 0, 2 }, // 12 "EOS 400D", "EOS 1000D"
+	  { 3984, 2622, 76, 20, 0, 2, 14 }, // 13 "EOS-1D Mark III"
+	  { 4032, 2656, 112, 44, 10, 0 }, // 14 APS-C crop mode: "EOS 6D Mark II"??, "EOS RP"
+	  { 4104, 3048, 48, 12, 24, 12 }, // 15 "PowerShot G9"
+	  { 4116, 2178, 4, 2, 0, 0 },  // 16 ??
+	  { 4152, 2772, 192, 12, 0, 0 }, // 17 "PowerShot SX1 IS"
+	  { 4160, 3124, 104, 11, 8, 65 }, // 18 "PowerShot S100 (new)", "PowerShot S100V", "PowerShot G15", "PowerShot S110 (new)"
+	  { 4176, 3062, 96, 17, 8, 0, 0, 16, 0, 7, 0x49 }, // 19 "PowerShot SX50 HS"
+	  { 4192, 3062, 96, 17, 24, 0, 0, 16, 0, 0, 0x49 }, // 20 "PowerShot G16", "PowerShot S120"
+	  { 4312, 2876, 22, 18, 0, 2 }, // 21 "EOS 450D"
+	  { 4352, 2850, 144, 46, 0, 0 }, // 22 APS-C crop mode: "EOS R"
+	  { 4352, 2874, 62, 18, 0, 0 }, // 23 "EOS 1100D"
+	  { 4476, 2954, 90, 34, 0, 0 }, // 24 "EOS 5D"
+	  { 4480, 3348, 12, 10, 36, 12, 0, 0, 0, 18, 0x49 }, // 25 "PowerShot G10"
+	  { 4480, 3366, 80, 50, 0, 0 }, // 26 "PowerShot G1 X Mark II"
+	  { 4496, 3366, 80, 50, 12, 0 }, // 27 "PowerShot G1 X"
+	  { 4768, 3516, 96, 16, 0, 0, 0, 16 }, // 28 "PowerShot SX60 HS"
+	  { 4832, 3204, 62, 26, 0, 0 }, // 29 "EOS 500D"
+	  { 4832, 3228, 62, 51, 0, 0 }, // 30 "EOS 50D"
+	  { 5108, 3349, 98, 13, 0, 0 }, // 31 "EOS-1Ds Mark II"
+	  { 5120, 3318, 142, 45, 62, 0 }, // 32  "EOS-1D Mark IV"
+	  { 5280, 3528, 72, 52, 0, 0 }, // 33 "EOS M10", "EOS 650D", "EOS 700D", "EOS M", "EOS 100D", "EOS M2"
+	  { 5344, 3516, 142, 51, 0, 0 }, // 34 "EOS 550D", "EOS 600D", "EOS 60D", "EOS 1200D", "EOS 1300D", "EOS 3000D"
+	  { 5344, 3584, 126, 100, 0, 2 }, // 35 "EOS-1D X", "EOS-1D C"
+	  { 5344, 3950, 98, 18, 0, 0, 0, 24, 0, 0 }, // 36 "PowerShot SX70 HS"
+	  { 5360, 3516, 158, 51, 0, 0 }, // 37 "EOS 7D"
+	  { 5568, 3708, 72, 38, 0, 0 }, // 38; "EOS 7D Mark II", "EOS 6D", "EOS 70D", "EOS-1D X MARK II"
+	  { 5632, 3710, 96, 17, 0, 0, 0, 16, 0, 0, 0x49 }, // 39 "PowerShot G7 X", "PowerShot G3 X", "PowerShot G9 X", "PowerShot G5 X", "PowerShot G7 X Mark II", "PowerShot G9 X Mark II"
+	  { 5712, 3774, 62, 20, 10, 2 }, // 40 "EOS-1Ds Mark III"
+	  { 5792, 3804, 158, 51, 0, 0 }, // 41 "EOS 5D Mark II"
+	  { 5920, 3950, 122, 80, 2, 0 }, // 42 "EOS 5D Mark III"
+	  { 6096, 4051, 76, 35, 0, 0 }, // 43 "EOS 1500D"
+	  { 6096, 4056, 72, 34, 0, 0 }, // 44 "EOS M3", "EOS 760D", "EOS 750D"
+	  { 6288, 4056, 264, 36, 0, 0 }, // 45 "EOS M5", "EOS M100", "EOS M6", "PowerShot G1 X Mark III", "EOS 80D", "EOS 800D", "EOS 77D", "EOS 200D", "EOS 250D", "EOS M50"
+	  { 6384, 4224, 120, 44, 0, 0 }, // 46 "EOS 6D Mark II", "EOS RP"
+	  { 6880, 4544, 136, 42, 0, 0 }, // 47 "EOS 5D Mark IV"
+	  { 6888, 4546, 146, 48, 0, 0 }, // 48 "EOS R"
+	  { 7128, 4732, 144, 72, 0, 0 }, // 49 "EOS M6 II", "EOS 90D"
+	  { 8896, 5920, 160, 64, 0, 0 }, // 50 "EOS 5DS", "EOS 5DS R"
+  };
+
+  static const libraw_custom_camera_t const_table[] = {
+	  { 786432, 1024, 768, 0, 0, 0, 0, 0, 0x94, 0, 0, "AVT", "F-080C" },
+	  { 1447680, 1392, 1040, 0, 0, 0, 0, 0, 0x94, 0, 0, "AVT", "F-145C" },
+	  { 1920000, 1600, 1200, 0, 0, 0, 0, 0, 0x94, 0, 0, "AVT", "F-201C" },
+	  { 5067304, 2588, 1958, 0, 0, 0, 0, 0, 0x94, 0, 0, "AVT", "F-510C" },
+	  { 5067316, 2588, 1958, 0, 0, 0, 0, 0, 0x94, 0, 0, "AVT", "F-510C", 12 },
+	  { 10134608, 2588, 1958, 0, 0, 0, 0, 9, 0x94, 0, 0, "AVT", "F-510C" },
+	  { 10134620, 2588, 1958, 0, 0, 0, 0, 9, 0x94, 0, 0, "AVT", "F-510C", 12 },
+	  { 16157136, 3272, 2469, 0, 0, 0, 0, 9, 0x94, 0, 0, "AVT", "F-810C" },
+	  { 15980544, 3264, 2448, 0, 0, 0, 0, 8, 0x61, 0, 1, "AgfaPhoto", "DC-833m" },
+	  { 9631728, 2532, 1902, 0, 0, 0, 0, 96, 0x61, 0, 0, "Alcatel", "5035D" },
+	  { 31850496, 4608, 3456, 0, 0, 0, 0, 0, 0x94, 0, 0, "GITUP", "GIT2 4:3" },
+	  { 23887872, 4608, 2592, 0, 0, 0, 0, 0, 0x94, 0, 0, "GITUP", "GIT2 16:9" },
+	  { 32257024, 4624, 3488, 8, 2, 16, 2, 0, 0x94, 0, 0, "GITUP", "GIT2P 4:3" },
+	  { 24192768, 4624, 2616, 8, 2, 16, 2, 0, 0x94, 0, 0, "GITUP", "GIT2P 16:9" },
+	  { 18016000, 4000, 2252, 0, 0, 0, 0, 0, 0x94, 0, 0, "GITUP", "G3DUO 16:9" },
+	  //          {24000000, 4000, 3000, 0, 0, 0, 0, 0, 0x94, 0, 0, "GITUP",
+      //          "G3DUO 4:3"}, // Conflict w/ Samsung WB550
+
+      //   Android Raw dumps id start
+      //   File Size in bytes Horizontal Res Vertical Flag then bayer order eg
+      //   0x16 bbgr 0x94 rggb
+	  { 1540857, 2688, 1520, 0, 0, 0, 0, 1, 0x61, 0, 0, "Samsung", "S3" },
+	  { 2658304, 1212, 1096, 0, 0, 0, 0, 1, 0x16, 0, 0, "LG", "G3FrontMipi" },
+	  { 2842624, 1296, 1096, 0, 0, 0, 0, 1, 0x16, 0, 0, "LG", "G3FrontQCOM" },
+	  { 2969600, 1976, 1200, 0, 0, 0, 0, 1, 0x16, 0, 0, "Xiaomi", "MI3wMipi" },
+	  { 3170304, 1976, 1200, 0, 0, 0, 0, 1, 0x16, 0, 0, "Xiaomi", "MI3wQCOM" },
+	  { 3763584, 1584, 1184, 0, 0, 0, 0, 96, 0x61, 0, 0, "I_Mobile", "I_StyleQ6" },
+	  { 5107712, 2688, 1520, 0, 0, 0, 0, 1, 0x61, 0, 0, "OmniVisi", "UltraPixel1" },
+	  { 5382640, 2688, 1520, 0, 0, 0, 0, 1, 0x61, 0, 0, "OmniVisi", "UltraPixel2" },
+	  { 5664912, 2688, 1520, 0, 0, 0, 0, 1, 0x61, 0, 0, "OmniVisi", "4688" },
+	  { 5664912, 2688, 1520, 0, 0, 0, 0, 1, 0x61, 0, 0, "OmniVisi", "4688" },
+	  { 5364240, 2688, 1520, 0, 0, 0, 0, 1, 0x61, 0, 0, "OmniVisi", "4688" },
+	  { 6299648, 2592, 1944, 0, 0, 0, 0, 1, 0x16, 0, 0, "OmniVisi", "OV5648" },
+	  { 6721536, 2592, 1944, 0, 0, 0, 0, 0, 0x16, 0, 0, "OmniVisi", "OV56482" },
+	  { 6746112, 2592, 1944, 0, 0, 0, 0, 0, 0x16, 0, 0, "HTC", "OneSV" },
+	  { 9631728, 2532, 1902, 0, 0, 0, 0, 96, 0x61, 0, 0, "Sony", "5mp" },
+	  { 9830400, 2560, 1920, 0, 0, 0, 0, 96, 0x61, 0, 0, "NGM", "ForwardArt" },
+	  { 10186752, 3264, 2448, 0, 0, 0, 0, 1, 0x94, 0, 0, "Sony", "IMX219-mipi 8mp" },
+	  { 10223360, 2608, 1944, 0, 0, 0, 0, 96, 0x16, 0, 0, "Sony", "IMX" },
+	  { 10782464, 3282, 2448, 0, 0, 0, 0, 0, 0x16, 0, 0, "HTC", "MyTouch4GSlide" },
+	  { 10788864, 3282, 2448, 0, 0, 0, 0, 0, 0x16, 0, 0, "Xperia", "L" },
+	  { 15967488, 3264, 2446, 0, 0, 0, 0, 96, 0x16, 0, 0, "OmniVison", "OV8850" },
+	  { 16224256, 4208, 3082, 0, 0, 0, 0, 1, 0x16, 0, 0, "LG", "G3MipiL" },
+	  { 16424960, 4208, 3120, 0, 0, 0, 0, 1, 0x16, 0, 0, "IMX135", "MipiL" },
+	  { 17326080, 4164, 3120, 0, 0, 0, 0, 1, 0x16, 0, 0, "LG", "G3LQCom" },
+	  { 17522688, 4212, 3120, 0, 0, 0, 0, 0, 0x16, 0, 0, "Sony", "IMX135-QCOM" },
+	  { 19906560, 4608, 3456, 0, 0, 0, 0, 1, 0x16, 0, 0, "Gione", "E7mipi" },
+	  { 19976192, 5312, 2988, 0, 0, 0, 0, 1, 0x16, 0, 0, "LG", "G4" },
+	  { 20389888, 4632, 3480, 0, 0, 0, 0, 1, 0x16, 0, 0, "Xiaomi", "RedmiNote3Pro" },
+	  { 20500480, 4656, 3496, 0, 0, 0, 0, 1, 0x94, 0, 0, "Sony", "IMX298-mipi 16mp" },
+	  { 21233664, 4608, 3456, 0, 0, 0, 0, 1, 0x16, 0, 0, "Gione", "E7qcom" },
+	  { 26023936, 4192, 3104, 0, 0, 0, 0, 96, 0x94, 0, 0, "THL", "5000" },
+	  { 26257920, 4208, 3120, 0, 0, 0, 0, 96, 0x94, 0, 0, "Sony", "IMX214" },
+	  { 26357760, 4224, 3120, 0, 0, 0, 0, 96, 0x61, 0, 0, "OV", "13860" },
+	  { 41312256, 5248, 3936, 0, 0, 0, 0, 96, 0x61, 0, 0, "Meizu", "MX4" },
+	  { 42923008, 5344, 4016, 0, 0, 0, 0, 96, 0x61, 0, 0, "Sony", "IMX230" },
+      //   Android Raw dumps id end
+	  { 20137344, 3664, 2748, 0, 0, 0, 0, 0x40, 0x49, 0, 0, "Aptina", "MT9J003", 0xffff },
+	  { 2868726, 1384, 1036, 0, 0, 0, 0, 64, 0x49, 0, 8, "Baumer", "TXG14", 1078 },
+	  { 5298000, 2400, 1766, 12, 12, 44, 2, 40, 0x94, 0, 2, "Canon", "PowerShot SD300" }, // chdk hack
+	  { 6553440, 2664, 1968, 4, 4, 44, 4, 40, 0x94, 0, 2, "Canon", "PowerShot A460" }, // chdk hack
+	  { 6573120, 2672, 1968, 12, 8, 44, 0, 40, 0x94, 0, 2, "Canon", "PowerShot A610" }, // chdk hack
+	  { 6653280, 2672, 1992, 10, 6, 42, 2, 40, 0x94, 0, 2, "Canon", "PowerShot A530" }, // chdk hack
+	  { 7710960, 2888, 2136, 44, 8, 4, 0, 40, 0x94, 0, 2, "Canon", "PowerShot S3 IS" }, // chdk hack
+	  { 9219600, 3152, 2340, 36, 12, 4, 0, 40, 0x94, 0, 2, "Canon", "PowerShot A620" }, // chdk hack
+	  { 9243240, 3152, 2346, 12, 7, 44, 13, 40, 0x49, 0, 2, "Canon", "PowerShot A470" }, // chdk hack
+	  { 10341600, 3336, 2480, 6, 5, 32, 3, 40, 0x94, 0, 2, "Canon", "PowerShot A720 IS" }, // chdk hack
+	  { 10383120, 3344, 2484, 12, 6, 44, 6, 40, 0x94, 0, 2, "Canon", "PowerShot A630" }, // chdk hack
+	  { 12945240, 3736, 2772, 12, 6, 52, 6, 40, 0x94, 0, 2, "Canon", "PowerShot A640" }, // chdk hack
+	  { 15636240, 4104, 3048, 48, 12, 24, 12, 40, 0x94, 0, 2, "Canon", "PowerShot A650" }, // chdk hack
+	  { 15467760, 3720, 2772, 6, 12, 30, 0, 40, 0x94, 0, 2, "Canon", "PowerShot SX110 IS" }, // chdk hack
+	  { 15534576, 3728, 2778, 12, 9, 44, 9, 40, 0x94, 0, 2, "Canon", "PowerShot SX120 IS" }, // chdk hack
+	  { 18653760, 4080, 3048, 24, 12, 24, 12, 40, 0x94, 0, 2, "Canon", "PowerShot SX20 IS" }, // chdk hack
+	  { 18763488, 4104, 3048, 10, 22, 82, 22, 8, 0x49, 0, 0, "Canon", "PowerShot D10" }, // ? chdk hack ?
+	  { 19131120, 4168, 3060, 92, 16, 4, 1, 40, 0x94, 0, 2, "Canon", "PowerShot SX220 HS" }, // chdk hack
+	  { 21936096, 4464, 3276, 25, 10, 73, 12, 40, 0x16, 0, 2, "Canon", "PowerShot SX30 IS" }, // chdk hack
+	  { 24724224, 4704, 3504, 8, 16, 56, 8, 40, 0x49, 0, 2, "Canon", "PowerShot A3300 IS" }, // chdk hack
+	  { 30858240, 5248, 3920, 8, 16, 56, 16, 40, 0x94, 0, 2, "Canon", "IXUS 160" }, // chdk hack
+	  { 1976352, 1632, 1211, 0, 2, 0, 1, 0, 0x94, 0, 1, "Casio", "QV-2000UX" },
+	  { 3217760, 2080, 1547, 0, 0, 10, 1, 0, 0x94, 0, 1, "Casio", "QV-3*00EX" },
+	  { 6218368, 2585, 1924, 0, 0, 9, 0, 0, 0x94, 0, 1, "Casio", "QV-5700" },
+	  { 7816704, 2867, 2181, 0, 0, 34, 36, 0, 0x16, 0, 1, "Casio", "EX-Z60" },
+	  { 2937856, 1621, 1208, 0, 0, 1, 0, 0, 0x94, 7, 13, "Casio", "EX-S20" },
+	  { 4948608, 2090, 1578, 0, 0, 32, 34, 0, 0x94, 7, 1, "Casio", "EX-S100" },
+	  { 6054400, 2346, 1720, 2, 0, 32, 0, 0, 0x94, 7, 1, "Casio", "QV-R41" },
+	  { 7426656, 2568, 1928, 0, 0, 0, 0, 0, 0x94, 0, 1, "Casio", "EX-P505" },
+	  { 7530816, 2602, 1929, 0, 0, 22, 0, 0, 0x94, 7, 1, "Casio", "QV-R51" },
+	  { 7542528, 2602, 1932, 0, 0, 32, 0, 0, 0x94, 7, 1, "Casio", "EX-Z50" },
+	  { 7562048, 2602, 1937, 0, 0, 25, 0, 0, 0x16, 7, 1, "Casio", "EX-Z500" },
+	  { 7753344, 2602, 1986, 0, 0, 32, 26, 0, 0x94, 7, 1, "Casio", "EX-Z55" },
+	  { 9313536, 2858, 2172, 0, 0, 14, 30, 0, 0x94, 7, 1, "Casio", "EX-P600" },
+	  { 10834368, 3114, 2319, 0, 0, 27, 0, 0, 0x94, 0, 1, "Casio", "EX-Z750" },
+	  { 10843712, 3114, 2321, 0, 0, 25, 0, 0, 0x94, 0, 1, "Casio", "EX-Z75" },
+	  { 10979200, 3114, 2350, 0, 0, 32, 32, 0, 0x94, 7, 1, "Casio", "EX-P700" },
+	  { 12310144, 3285, 2498, 0, 0, 6, 30, 0, 0x94, 0, 1, "Casio", "EX-Z850" },
+	  { 12489984, 3328, 2502, 0, 0, 47, 35, 0, 0x94, 0, 1, "Casio", "EX-Z8" },
+	  { 15499264, 3754, 2752, 0, 0, 82, 0, 0, 0x94, 0, 1, "Casio", "EX-Z1050" },
+	  { 18702336, 4096, 3044, 0, 0, 24, 0, 80, 0x94, 7, 1, "Casio", "EX-ZR100" },
+	  { 7684000, 2260, 1700, 0, 0, 0, 0, 13, 0x94, 0, 1, "Casio", "QV-4000" },
+	  { 787456, 1024, 769, 0, 1, 0, 0, 0, 0x49, 0, 0, "Creative", "PC-CAM 600" },
+	  { 28829184, 4384, 3288, 0, 0, 0, 0, 36, 0x61, 0, 0, "DJI" },
+	  { 15151104, 4608, 3288, 0, 0, 0, 0, 0, 0x94, 0, 0, "Matrix" },
+	  { 3840000, 1600, 1200, 0, 0, 0, 0, 65, 0x49, 0, 0, "Foculus", "531C" },
+	  { 307200, 640, 480, 0, 0, 0, 0, 0, 0x94, 0, 0, "Generic" },
+	  { 62464, 256, 244, 1, 1, 6, 1, 0, 0x8d, 0, 0, "Kodak", "DC20" },
+	  { 124928, 512, 244, 1, 1, 10, 1, 0, 0x8d, 0, 0, "Kodak", "DC20" },
+	  { 1652736, 1536, 1076, 0, 52, 0, 0, 0, 0x61, 0, 0, "Kodak", "DCS200" },
+	  { 4159302, 2338, 1779, 1, 33, 1, 2, 0, 0x94, 0, 0, "Kodak", "C330" },
+	  { 4162462, 2338, 1779, 1, 33, 1, 2, 0, 0x94, 0, 0, "Kodak", "C330", 3160 },
+	  { 2247168, 1232, 912, 0, 0, 16, 0, 0, 0x00, 0, 0, "Kodak", "C330" },
+	  { 3370752, 1232, 912, 0, 0, 16, 0, 0, 0x00, 0, 0, "Kodak", "C330" },
+	  { 6163328, 2864, 2152, 0, 0, 0, 0, 0, 0x94, 0, 0, "Kodak", "C603" },
+	  { 6166488, 2864, 2152, 0, 0, 0, 0, 0, 0x94, 0, 0, "Kodak", "C603", 3160 },
+	  { 460800, 640, 480, 0, 0, 0, 0, 0, 0x00, 0, 0, "Kodak", "C603" },
+	  { 9116448, 2848, 2134, 0, 0, 0, 0, 0, 0x00, 0, 0, "Kodak", "C603" },
+	  { 12241200, 4040, 3030, 2, 0, 0, 13, 0, 0x49, 0, 0, "Kodak", "12MP" },
+	  { 12272756, 4040, 3030, 2, 0, 0, 13, 0, 0x49, 0, 0, "Kodak", "12MP", 31556 },
+	  { 18000000, 4000, 3000, 0, 0, 0, 0, 0, 0x00, 0, 0, "Kodak", "12MP" },
+	  { 614400, 640, 480, 0, 3, 0, 0, 64, 0x94, 0, 0, "Kodak", "KAI-0340" },
+	  { 15360000, 3200, 2400, 0, 0, 0, 0, 96, 0x16, 0, 0, "Lenovo", "A820" },
+	  { 3884928, 1608, 1207, 0, 0, 0, 0, 96, 0x16, 0, 0, "Micron", "2010", 3212 },
+	  { 1138688, 1534, 986, 0, 0, 0, 0, 0, 0x61, 0, 0, "Minolta", "RD175", 513 },
+	  { 1581060, 1305, 969, 0, 0, 18, 6, 6, 0x1e, 4, 1, "Nikon", "E900" }, // "diag raw" hack
+	  { 2465792, 1638, 1204, 0, 0, 22, 1, 6, 0x4b, 5, 1, "Nikon", "E950" }, // "diag raw" hack; possibly also Nikon E700, E800, E775;
+	                                                                        // Olympus C-2020Z
+	  { 2940928, 1616, 1213, 0, 0, 0, 7, 30, 0x94, 0, 1, "Nikon", "E2100" }, // "diag raw" hack; also Nikon E2500
+	  { 4771840, 2064, 1541, 0, 0, 0, 1, 6, 0xe1, 0, 1, "Nikon", "E990" }, // "diag raw" hack; possibly also Nikon E880, E885, E995;
+	                                                                       // Olympus C-3030Z
+	  { 4775936, 2064, 1542, 0, 0, 0, 0, 30, 0x94, 0, 1, "Nikon", "E3700" }, // "diag raw" hack; Nikon E3100, E3200, E3500;
+	                                                                         // Pentax "Optio 33WR"; possibly also Olympus C-740UZ
+	  { 5865472, 2288, 1709, 0, 0, 0, 1, 6, 0xb4, 0, 1, "Nikon", "E4500" }, // "diag raw" hack; possibly also Olympus C-4040Z
+	  { 5869568, 2288, 1710, 0, 0, 0, 0, 6, 0x16, 0, 1, "Nikon", "E4300" }, // "diag raw" hack; also Minolta "DiMAGE Z2"
+	  { 7438336, 2576, 1925, 0, 0, 0, 1, 6, 0xb4, 0, 1, "Nikon", "E5000" }, // also Nikon E5700
+	  { 8998912, 2832, 2118, 0, 0, 0, 0, 30, 0x94, 7, 1, "Nikon", "COOLPIX S6" }, // "diag raw" hack
+	  { 5939200, 2304, 1718, 0, 0, 0, 0, 30, 0x16, 0, 0, "Olympus", "C-770UZ" }, // possibly also Olympus C-4100Z, C-765UZ
+	  { 3178560, 2064, 1540, 0, 0, 0, 0, 0, 0x94, 0, 1, "Pentax", "Optio S V1.01" },
+	  { 4841984, 2090, 1544, 0, 0, 22, 0, 0, 0x94, 7, 1, "Pentax", "Optio S" },
+	  { 6114240, 2346, 1737, 0, 0, 22, 0, 0, 0x94, 7, 1, "Pentax", "Optio S4" },
+	  { 10702848, 3072, 2322, 0, 0, 0, 21, 30, 0x94, 0, 1, "Pentax", "Optio 750Z" },
+	  { 4147200, 1920, 1080, 0, 0, 0, 0, 0, 0x49, 0, 0, "Photron", "BC2-HD" },
+	  { 4151666, 1920, 1080, 0, 0, 0, 0, 0, 0x49, 0, 0, "Photron", "BC2-HD", 8 },
+	  { 13248000, 2208, 3000, 0, 0, 0, 0, 13, 0x61, 0, 0, "Pixelink", "A782" },
+	  { 6291456, 2048, 1536, 0, 0, 0, 0, 96, 0x61, 0, 0, "RoverShot", "3320AF" },
+	  { 311696, 644, 484, 0, 0, 0, 0, 0, 0x16, 0, 8, "ST Micro", "STV680 VGA" },
+	  { 16098048, 3288, 2448, 0, 0, 24, 0, 9, 0x94, 0, 1, "Samsung", "S85" }, // hack
+	  { 16215552, 3312, 2448, 0, 0, 48, 0, 9, 0x94, 0, 1, "Samsung", "S85" }, // hack
+	  { 20487168, 3648, 2808, 0, 0, 0, 0, 13, 0x94, 5, 1, "Samsung", "WB550" },
+	  { 24000000, 4000, 3000, 0, 0, 0, 0, 13, 0x94, 5, 1, "Samsung", "WB550" },
+	  { 12582980, 3072, 2048, 0, 0, 0, 0, 33, 0x61, 0, 0, "Sinar", "", 68 }, // Sinarback 23; same res. as Leaf Volare & Cantare
+	  { 33292868, 4080, 4080, 0, 0, 0, 0, 33, 0x61, 0, 0, "Sinar", "", 68 }, // Sinarback 44
+	  { 44390468, 4080, 5440, 0, 0, 0, 0, 33, 0x61, 0, 0, "Sinar", "", 68 }, // Sinarback 54
+	  { 1409024, 1376, 1024, 0, 0, 1, 0, 0, 0x49, 0, 0, "Sony", "XCD-SX910CR" },
+	  { 2818048, 1376, 1024, 0, 0, 1, 0, 97, 0x49, 0, 0, "Sony", "XCD-SX910CR" },
+  };
+
+  libraw_custom_camera_t
+      table[64 + sizeof(const_table) / sizeof(const_table[0])];
+
+
+  // clang-format on
+
+  char head[64] = {0}, *cp;
+  int hlen, fsize, flen, zero_fsize = 1, i, c;
+  struct jhead jh;
+
+  unsigned camera_count =
+      parse_custom_cameras(64, table, imgdata.params.custom_camera_strings);
+  for (int q = 0; q < int(sizeof(const_table) / sizeof(const_table[0])); q++)
+    memmove(&table[q + camera_count], &const_table[q], sizeof(const_table[0]));
+  camera_count += sizeof(const_table) / sizeof(const_table[0]);
+
+  tiff_flip = flip = filters = UINT_MAX; /* unknown */
+  raw_height = raw_width = fuji_width = fuji_layout = cr2_slice[0] = 0;
+  maximum = height = width = top_margin = left_margin = 0;
+  cdesc[0] = desc[0] = artist[0] = make[0] = model[0] = model2[0] = 0;
+  iso_speed = shutter = aperture = focal_len = 0;
+  unique_id = 0ULL;
+  tiff_nifds = 0;
+  is_NikonTransfer = 0;
+  is_Sony = 0;
+  is_pana_raw = 0;
+  maker_index = LIBRAW_CAMERAMAKER_Unknown;
+  is_4K_RAFdata = 0;
+  FujiCropMode = 0;
+  is_PentaxRicohMakernotes = 0;
+  normalized_model[0] = 0;
+  normalized_make[0] = 0;
+  CM_found = 0;
+  memset(tiff_ifd, 0, sizeof tiff_ifd);
+  libraw_internal_data.unpacker_data.crx_track_selected = -1;
+  libraw_internal_data.unpacker_data.CR3_CTMDtag = 0;
+  imgdata.makernotes.hasselblad.nIFD_CM[0] =
+    imgdata.makernotes.hasselblad.nIFD_CM[1] = -1;
+  imgdata.makernotes.kodak.ISOCalibrationGain = 1.0f;
+  imCommon.CameraTemperature = imCommon.SensorTemperature =
+      imCommon.SensorTemperature2 = imCommon.LensTemperature =
+          imCommon.AmbientTemperature = imCommon.BatteryTemperature =
+              imCommon.exifAmbientTemperature = -1000.0f;
+
+  imgdata.color.ExifColorSpace = LIBRAW_COLORSPACE_Unknown;
+  for (i = 0; i < LIBRAW_IFD_MAXCOUNT; i++)
+  {
+    tiff_ifd[i].dng_color[0].illuminant = tiff_ifd[i].dng_color[1].illuminant =
+        0xffff;
+    for (int c = 0; c < 4; c++)
+      tiff_ifd[i].dng_levels.analogbalance[c] = 1.0f;
+  }
+
+  memset(gpsdata, 0, sizeof gpsdata);
+  memset(cblack, 0, sizeof cblack);
+  memset(white, 0, sizeof white);
+  memset(mask, 0, sizeof mask);
+  thumb_offset = thumb_length = thumb_width = thumb_height = 0;
+  load_raw = thumb_load_raw = 0;
+  write_thumb = &LibRaw::jpeg_thumb;
+  data_offset = meta_offset = meta_length = tiff_bps = tiff_compress = 0;
+  kodak_cbpp = zero_after_ff = dng_version = load_flags = 0;
+  timestamp = shot_order = tiff_samples = black = is_foveon = 0;
+  mix_green = profile_length = data_error = zero_is_bad = 0;
+  pixel_aspect = is_raw = raw_color = 1;
+  tile_width = tile_length = 0;
+  metadata_blocks = 0;
+
+  for (i = 0; i < 4; i++)
+  {
+    cam_mul[i] = i == 1;
+    pre_mul[i] = i < 3;
+    FORC3 cmatrix[c][i] = 0;
+    FORC3 rgb_cam[c][i] = c == i;
+  }
+  colors = 3;
+  for (i = 0; i < 0x10000; i++)
+    curve[i] = i;
+
+  order = get2();
+  hlen = get4();
+  fseek(ifp, 0, SEEK_SET);
+
+  if (fread(head, 1, 64, ifp) < 64)
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+  libraw_internal_data.unpacker_data.lenRAFData =
+      libraw_internal_data.unpacker_data.posRAFData = 0;
+
+  fseek(ifp, 0, SEEK_END);
+  flen = fsize = ftell(ifp);
+  if ((cp = (char *)memmem(head, 32, (char *)"MMMM", 4)) ||
+      (cp = (char *)memmem(head, 32, (char *)"IIII", 4)))
+  {
+    parse_phase_one(cp - head);
+    if (cp - head && parse_tiff(0))
+      apply_tiff();
+  }
+  else if (order == 0x4949 || order == 0x4d4d)
+  {
+    if (!memcmp(head + 6, "HEAPCCDR", 8))
+    {
+      data_offset = hlen;
+      parse_ciff(hlen, flen - hlen, 0);
+      load_raw = &LibRaw::canon_load_raw;
+    }
+    else if (parse_tiff(0))
+      apply_tiff();
+  }
+  else if (!memcmp(head, "\xff\xd8\xff\xe1", 4) && !memcmp(head + 6, "Exif", 4))
+  {
+    fseek(ifp, 4, SEEK_SET);
+    data_offset = 4 + get2();
+    fseek(ifp, data_offset, SEEK_SET);
+    if (fgetc(ifp) != 0xff)
+      parse_tiff(12);
+    thumb_offset = 0;
+  }
+  else if (!memcmp(head + 25, "ARECOYK", 7)) // 'KYOCERA' right-to-left
+  {
+    strcpy(make, "Contax");
+    strcpy(model, "N Digital");
+    parse_kyocera();
+  }
+  else if (!strcmp(head, "PXN"))
+  {
+    strcpy(make, "Logitech");
+    strcpy(model, "Fotoman Pixtura");
+  }
+  else if (!strcmp(head, "qktk"))
+  {
+    strcpy(make, "Apple");
+    strcpy(model, "QuickTake 100");
+    load_raw = &LibRaw::quicktake_100_load_raw;
+  }
+  else if (!strcmp(head, "qktn"))
+  {
+    strcpy(make, "Apple");
+    strcpy(model, "QuickTake 150");
+    load_raw = &LibRaw::kodak_radc_load_raw;
+  }
+  else if (!memcmp(head, "FUJIFILM", 8))
+  {
+    memcpy(imFuji.SerialSignature, head + 0x10, 0x0c);
+    imFuji.SerialSignature[0x0c] = 0;
+    strncpy(model, head + 0x1c, 0x20);
+    model[0x20] = 0;
+    memcpy(model2, head + 0x3c, 4);
+    model2[4] = 0;
+    strcpy(imFuji.RAFVersion, model2);
+    fseek(ifp, 84, SEEK_SET);
+    thumb_offset = get4();
+    thumb_length = get4();
+    fseek(ifp, 92, SEEK_SET);
+    parse_fuji(get4());
+    if (thumb_offset > 120)
+    {
+      fseek(ifp, 120, SEEK_SET);
+      is_raw += (i = get4()) ? 1 : 0;
+      if (is_raw == 2 && shot_select)
+        parse_fuji(i);
+    }
+    load_raw = &LibRaw::unpacked_load_raw;
+    fseek(ifp, 100 + 28 * (shot_select > 0), SEEK_SET);
+    parse_tiff(data_offset = get4());
+    parse_tiff(thumb_offset + 12);
+    apply_tiff();
+  }
+  else if (!memcmp(head, "RIFF", 4))
+  {
+    fseek(ifp, 0, SEEK_SET);
+    parse_riff();
+  }
+  else if (!memcmp(head + 4, "ftypqt   ", 9))
+  {
+    fseek(ifp, 0, SEEK_SET);
+    parse_qt(fsize);
+    is_raw = 0;
+  }
+  else if (!memcmp(head, "\0\001\0\001\0@", 6))
+  {
+    fseek(ifp, 6, SEEK_SET);
+    fread(make, 1, 8, ifp);
+    fread(model, 1, 8, ifp);
+    fread(model2, 1, 16, ifp);
+    data_offset = get2();
+    get2();
+    raw_width = get2();
+    raw_height = get2();
+    load_raw = &LibRaw::nokia_load_raw;
+    filters = 0x61616161;
+  }
+  else if (!memcmp(head, "NOKIARAW", 8))
+  {
+    strcpy(make, "NOKIA");
+    order = 0x4949;
+    fseek(ifp, 300, SEEK_SET);
+    data_offset = get4();
+    i = get4(); // bytes count
+    width = get2();
+    height = get2();
+
+    // Data integrity check
+    if (width < 1 || width > 16000 || height < 1 || height > 16000 ||
+        i < (width * height) || i > (2 * width * height))
+      throw LIBRAW_EXCEPTION_IO_CORRUPT;
+
+    switch (tiff_bps = i * 8 / (width * height))
+    {
+    case 8:
+      load_raw = &LibRaw::eight_bit_load_raw;
+      break;
+    case 10:
+      load_raw = &LibRaw::nokia_load_raw;
+      break;
+    case 0:
+      throw LIBRAW_EXCEPTION_IO_CORRUPT;
+      break;
+    }
+    raw_height = height + (top_margin = i / (width * tiff_bps / 8) - height);
+    mask[0][3] = 1;
+    filters = 0x61616161;
+  }
+  else if (!memcmp(head, "ARRI", 4))
+  {
+    order = 0x4949;
+    fseek(ifp, 20, SEEK_SET);
+    width = get4();
+    height = get4();
+    strcpy(make, "ARRI");
+    fseek(ifp, 668, SEEK_SET);
+    fread(model, 1, 64, ifp);
+    model[63] = 0;
+    fseek(ifp, 760, SEEK_SET);
+    fread(software, 1, 64, ifp);
+    if((unsigned char)software[0] == 0xff) software[0] = 0;
+    software[63] = 0;
+    data_offset = 4096;
+    load_raw = &LibRaw::packed_load_raw;
+    load_flags = 88;
+    filters = 0x61616161;
+    fixupArri();
+  }
+  else if (!memcmp(head, "XPDS", 4))
+  {
+    order = 0x4949;
+    fseek(ifp, 0x800, SEEK_SET);
+    fread(make, 1, 41, ifp);
+    raw_height = get2();
+    raw_width = get2();
+    fseek(ifp, 56, SEEK_CUR);
+    fread(model, 1, 30, ifp);
+    data_offset = 0x10000;
+    load_raw = &LibRaw::canon_rmf_load_raw;
+    gamma_curve(0, 12.25, 1, 1023);
+  }
+  else if (!memcmp(head + 4, "RED1", 4))
+  {
+    strcpy(make, "Red");
+    strcpy(model, "One");
+    parse_redcine();
+    load_raw = &LibRaw::redcine_load_raw;
+    gamma_curve(1 / 2.4, 12.92, 1, 4095);
+    filters = 0x49494949;
+  }
+  else if (!memcmp(head, "DSC-Image", 9))
+    parse_rollei();
+  else if (!memcmp(head, "PWAD", 4))
+    parse_sinar_ia();
+  else if (!memcmp(head, "\0MRM", 4))
+    parse_minolta(0);
+  else if (!memcmp(head, "FOVb", 4))
+  {
+    parse_x3f(); /* Does nothing if USE_X3FTOOLS is not defined */
+  }
+  else if (!memcmp(head, "CI", 2))
+    parse_cine();
+#ifdef USE_6BY9RPI
+  else if (!memcmp(head, "BRCM", 4)) {
+	fseek(ifp, 0, SEEK_SET);
+	strcpy(make, "RaspberryPi");
+	strcpy(model, "Pi");
+	parse_raspberrypi();
+	}
+#endif
+  else if (!memcmp(head + 4, "ftypcrx ", 8))
+  {
+    int err;
+    unsigned long long szAtomList;
+    short nesting = -1;
+    short nTrack = -1;
+    short TrackType;
+    char AtomNameStack[128];
+    strcpy(make, "Canon");
+
+    szAtomList = ifp->size();
+    err = parseCR3(0ULL, szAtomList, nesting, AtomNameStack, nTrack, TrackType);
+    if ((err == 0 || err == -14) &&
+        nTrack >= 0) // no error, or too deep nesting
+      selectCRXTrack(nTrack);
+  }
+
+  if (make[0] == 0)
+    for (zero_fsize = i = 0; i < (int)camera_count; i++)
+      if (fsize == (int)table[i].fsize)
+      {
+        strcpy(make, table[i].t_make);
+        strcpy(model, table[i].t_model);
+        flip = table[i].flags >> 2;
+        zero_is_bad = table[i].flags & 2;
+        data_offset = table[i].offset == 0xffff ? 0 : table[i].offset;
+        raw_width = table[i].rw;
+        raw_height = table[i].rh;
+        left_margin = table[i].lm;
+        top_margin = table[i].tm;
+        width = raw_width - left_margin - table[i].rm;
+        height = raw_height - top_margin - table[i].bm;
+        filters = 0x1010101U * table[i].cf;
+        colors = 4 - !((filters & filters >> 1) & 0x5555);
+        load_flags = table[i].lf & 0xff;
+        if (table[i].lf & 0x100) /* Monochrome sensor dump */
+        {
+          colors = 1;
+          filters = 0;
+        }
+        switch (tiff_bps = (fsize - data_offset) * 8 / (raw_width * raw_height))
+        {
+        case 6:
+          load_raw = &LibRaw::minolta_rd175_load_raw;
+          ilm.CameraMount = LIBRAW_MOUNT_Minolta_A;
+          break;
+        case 8:
+          load_raw = &LibRaw::eight_bit_load_raw;
+          break;
+        case 10:
+          if ((fsize - data_offset) / raw_height * 3 >= raw_width * 4)
+          {
+            load_raw = &LibRaw::android_loose_load_raw;
+            break;
+          }
+          else if (load_flags & 1)
+          {
+            load_raw = &LibRaw::android_tight_load_raw;
+            break;
+          }
+        case 12:
+          load_flags |= 128;
+          load_raw = &LibRaw::packed_load_raw;
+          break;
+        case 16:
+          order = 0x4949 | 0x404 * (load_flags & 1);
+          tiff_bps -= load_flags >> 4;
+          tiff_bps -= load_flags = load_flags >> 1 & 7;
+          load_raw = table[i].offset == 0xffff
+                         ? &LibRaw::unpacked_load_raw_reversed
+                         : &LibRaw::unpacked_load_raw;
+        }
+        maximum = (1 << tiff_bps) - (1 << table[i].max);
+        break;
+      }
+  if (zero_fsize)
+    fsize = 0;
+  if (make[0] == 0)
+    parse_smal(0, flen);
+  if (make[0] == 0)
+  {
+    parse_jpeg(0);
+#ifdef USE_6BY9RPI
+	if (!(strncmp(model, "ov", 2) && strncmp(model, "RP_", 3))) {
+		//Assume that this isn't a raw unless the header can be found
+		is_raw = 0;
+
+		if (!strncasecmp(model, "RP_imx", 6)) {
+			const long offsets[] = {
+				//IMX219 offsets
+				10270208, //8MPix 3280x2464
+				2678784,  //1920x1080
+				2628608,  //1640x1232
+				1963008,  //1640x922
+				1233920,  //1280x720
+				445440,   //640x480
+				-1        //Marker for end of table
+			};
+			int offset_idx;
+			for (offset_idx = 0; offsets[offset_idx] != -1; offset_idx++) {
+				if (!fseek(ifp, -offsets[offset_idx], SEEK_END) &&
+					fread(head, 1, 32, ifp) && !strncmp(head, "BRCM", 4)) {
+
+					fseek(ifp, -32, SEEK_CUR);
+					strcpy(make, "SonyRPF");
+					parse_raspberrypi();
+					break;
+				}
+			}
+		}
+		else if (!strncasecmp(model, "RP_OV", 5) || !strncasecmp(model, "ov5647", 6)) {
+			const long offsets[] = {
+					6404096,  //5MPix 2592x1944
+					2717696,  //1920x1080
+					1625600,  //1296x972
+					1233920,  //1296x730
+					445440,   //640x480
+					-1        //Marker for end of table
+			};
+			int offset_idx;
+			for (offset_idx = 0; offsets[offset_idx] != -1; offset_idx++) {
+				if (!fseek(ifp, -offsets[offset_idx], SEEK_END) &&
+					fread(head, 1, 32, ifp) && !strncmp(head, "BRCM", 4)) {
+					fseek(ifp, -32, SEEK_CUR);
+					strcpy(make, "OmniVision");
+					width = raw_width;
+					//Defaults
+					raw_width = 2611;
+					filters = 0x16161616;
+					parse_raspberrypi();
+					break;
+				}
+			}
+	  }
+	}// else is_raw = 0;
+#else
+    fseek(ifp, 0, SEEK_END);
+    int sz = ftell(ifp);
+    if (!strncmp(model, "RP_imx219", 9) && sz >= 0x9cb600 &&
+        !fseek(ifp, -0x9cb600, SEEK_END) && fread(head, 1, 0x20, ifp) &&
+        !strncmp(head, "BRCM", 4))
+    {
+      strcpy(make, "Broadcom");
+      strcpy(model, "RPi IMX219");
+      if (raw_height > raw_width)
+        flip = 5;
+      data_offset = ftell(ifp) + 0x8000 - 0x20;
+      parse_broadcom();
+      black = 66;
+      maximum = 0x3ff;
+      load_raw = &LibRaw::broadcom_load_raw;
+      thumb_offset = 0;
+      thumb_length = sz - 0x9cb600 - 1;
+    }
+    else if (!(strncmp(model, "ov5647", 6) && strncmp(model, "RP_OV5647", 9)) &&
+             sz >= 0x61b800 && !fseek(ifp, -0x61b800, SEEK_END) &&
+             fread(head, 1, 0x20, ifp) && !strncmp(head, "BRCM", 4))
+    {
+      strcpy(make, "Broadcom");
+      if (!strncmp(model, "ov5647", 6))
+        strcpy(model, "RPi OV5647 v.1");
+      else
+        strcpy(model, "RPi OV5647 v.2");
+      if (raw_height > raw_width)
+        flip = 5;
+      data_offset = ftell(ifp) + 0x8000 - 0x20;
+      parse_broadcom();
+      black = 16;
+      maximum = 0x3ff;
+      load_raw = &LibRaw::broadcom_load_raw;
+      thumb_offset = 0;
+      thumb_length = sz - 0x61b800 - 1;
+    }
+    else
+      is_raw = 0;
+#endif
+  }
+
+  // make sure strings are terminated
+  desc[511] = artist[63] = make[63] = model[63] = model2[63] = 0;
+
+  for (i = 0; i < int(sizeof CorpTable / sizeof *CorpTable); i++)
+  {
+    if (strcasestr(make, CorpTable[i].CorpName))
+    { /* Simplify company names */
+      maker_index = CorpTable[i].CorpId;
+      strcpy(make, CorpTable[i].CorpName);
+    }
+  }
+
+  if ((makeIs(LIBRAW_CAMERAMAKER_Kodak) || makeIs(LIBRAW_CAMERAMAKER_Leica)) &&
+      ((cp = strcasestr(model, " DIGITAL CAMERA")) ||
+       (cp = strstr(model, "FILE VERSION")))) {
+    *cp = 0;
+  } else if (makeIs(LIBRAW_CAMERAMAKER_Ricoh) && !strncasecmp(model, "PENTAX", 6)) {
+    maker_index = LIBRAW_CAMERAMAKER_Pentax;
+    strcpy(make, "Pentax");
+  } else if (makeIs(LIBRAW_CAMERAMAKER_JK_Imaging) && !strncasecmp(model, "Kodak", 5)) {
+    maker_index = LIBRAW_CAMERAMAKER_Kodak;
+    strcpy(make, "Kodak");
+  }
+
+  remove_trailing_spaces(make, sizeof(make));
+  remove_trailing_spaces(model, sizeof(model));
+
+  i = strbuflen(make); /* Remove make from model */
+  if (!strncasecmp(model, make, i) && model[i++] == ' ')
+    memmove(model, model + i, 64 - i);
+
+  if (makeIs(LIBRAW_CAMERAMAKER_Fujifilm) && !strncmp(model, "FinePix", 7)) {
+    memmove(model, model + 7, strlen(model) - 6);
+    if (model[0] == ' ') {
+      memmove(model, model + 1, strlen(model));
+    }
+  } else if ((makeIs(LIBRAW_CAMERAMAKER_Kodak) || makeIs(LIBRAW_CAMERAMAKER_Konica)) &&
+           !strncmp(model, "Digital Camera ", 15)) {
+    memmove(model, model + 15, strlen(model) - 14);
+  }
+
+  desc[511] = artist[63] = make[63] = model[63] = model2[63] = 0;
+  if (!is_raw)
+    goto notraw;
+
+  if (!height)
+    height = raw_height;
+  if (!width)
+    width = raw_width;
+
+  identify_finetune_pentax();
+
+
+  if (dng_version)
+  {
+    if (filters == UINT_MAX)
+      filters = 0;
+    if (!filters)
+      colors = tiff_samples;
+    switch (tiff_compress)
+    {
+    case 0: // Compression not set, assuming uncompressed
+    case 1:
+#ifdef USE_DNGSDK
+      // Uncompressed float
+      if (load_raw != &LibRaw::float_dng_load_raw_placeholder)
+#endif
+        load_raw = &LibRaw::packed_dng_load_raw;
+      break;
+    case 7:
+      load_raw = &LibRaw::lossless_dng_load_raw;
+      break;
+    case 8:
+      load_raw = &LibRaw::deflate_dng_load_raw;
+      break;
+#ifdef USE_GPRSDK
+    case 9:
+        load_raw = &LibRaw::vc5_dng_load_raw_placeholder;
+        break;
+#endif
+    case 34892:
+      load_raw = &LibRaw::lossy_dng_load_raw;
+      break;
+    default:
+      load_raw = 0;
+    }
+    GetNormalizedModel();
+    if (makeIs(LIBRAW_CAMERAMAKER_Olympus) &&
+        (OlyID == OlyID_STYLUS_1) && // don't use normalized_model below, it is 'Stylus 1'
+        (strchr(model+6, 's') ||
+         strchr(model+6, 'S')))
+    {
+      width -= 16;
+    }
+    goto dng_skip;
+  }
+
+  if (makeIs(LIBRAW_CAMERAMAKER_Canon) && !fsize && tiff_bps != 15)
+  {
+      bool fromtable = false;
+    if (!load_raw)
+      load_raw = &LibRaw::lossless_jpeg_load_raw;
+    for (i = 0; i < int(sizeof canon / sizeof *canon); i++)
+      if (raw_width == canon[i][0] && raw_height == canon[i][1])
+      {
+        width = raw_width - (left_margin = canon[i][2]);
+        height = raw_height - (top_margin = canon[i][3]);
+        width -= canon[i][4];
+        height -= canon[i][5];
+        mask[0][1] = canon[i][6];
+        mask[0][3] = -canon[i][7];
+        mask[1][1] = canon[i][8];
+        mask[1][3] = -canon[i][9];
+        if (canon[i][10])
+          filters = canon[i][10] * 0x01010101U;
+        fromtable = true;
+      }
+    if ((unique_id | 0x20000ULL) ==
+        0x2720000ULL) // "PowerShot G11", "PowerShot S90": 0x2700000, 0x2720000
+                      // possibly "PowerShot SX120 IS" (if not chdk hack?): 0x2710000
+    {
+      left_margin = 8;
+      top_margin = 16;
+    }
+    if(!fromtable && imgdata.makernotes.canon.AverageBlackLevel) // not known, but metadata known
+    {
+        FORC4 cblack[c] = imgdata.makernotes.canon.ChannelBlackLevel[c];
+        black = cblack[4] = cblack[5] = 0;
+        // Prevent automatic BL calculation
+        mask[0][3] = 1;
+        mask[0][1] = 2;
+
+        if(imgdata.makernotes.canon.SensorWidth == raw_width
+            && imgdata.makernotes.canon.SensorHeight == raw_height)
+        {
+            left_margin = (imgdata.makernotes.canon.SensorLeftBorder+1) & 0xfffe; // round to 2
+            width = imgdata.makernotes.canon.SensorRightBorder - left_margin;
+            top_margin = (imgdata.makernotes.canon.SensorTopBorder +1)  & 0xfffe;
+            height = imgdata.makernotes.canon.SensorBottomBorder - top_margin;
+        }
+    }
+  }
+
+  identify_finetune_by_filesize(fsize);
+
+  if (!strcmp(model, "KAI-0340") && find_green(16, 16, 3840, 5120) < 25)
+  {
+    height = 480;
+    top_margin = filters = 0;
+    strcpy(model, "C603");
+  }
+
+  GetNormalizedModel();
+
+  identify_finetune_dcr(head, fsize, flen);
+
+  /* Early reject for damaged images */
+  if (!load_raw || height < 22 || width < 22 ||
+      (tiff_bps > 16 &&
+       (load_raw != &LibRaw::deflate_dng_load_raw &&
+        load_raw != &LibRaw::float_dng_load_raw_placeholder)) ||
+      tiff_samples > 4 || colors > 4 ||
+      colors < 1
+      /* alloc in unpack() may be fooled by size adjust */
+      || ((int)width + (int)left_margin > 65535) ||
+      ((int)height + (int)top_margin > 65535))
+  {
+    is_raw = 0;
+    RUN_CALLBACK(LIBRAW_PROGRESS_IDENTIFY, 1, 2);
+    return;
+  }
+  if (!model[0])
+  {
+    sprintf(model, "%dx%d", width, height);
+    strcpy(normalized_model, model);
+  }
+
+  if (!(imgdata.params.raw_processing_options &
+        LIBRAW_PROCESSING_ZEROFILTERS_FOR_MONOCHROMETIFFS) &&
+      (filters == UINT_MAX)) // Default dcraw behaviour
+    filters = 0x94949494;
+  else if (filters == UINT_MAX)
+  {
+    if (tiff_nifds > 0 && tiff_samples == 1)
+    {
+      colors = 1;
+      filters = 0;
+    }
+    else
+      filters = 0x94949494;
+  }
+
+  if (thumb_offset && !thumb_height)
+  {
+    fseek(ifp, thumb_offset, SEEK_SET);
+    if (ljpeg_start(&jh, 1))
+    {
+      thumb_width = jh.wide;
+      thumb_height = jh.high;
+    }
+  }
+
+dng_skip:
+  if (dng_version)
+	  identify_process_dng_fields();
+
+  /* Early reject for damaged images again (after dng fields processing) */
+  if (!load_raw || height < 22 || width < 22 ||
+      (tiff_bps > 16 &&
+       (load_raw != &LibRaw::deflate_dng_load_raw &&
+        load_raw != &LibRaw::float_dng_load_raw_placeholder)) ||
+      tiff_samples > 4 || colors > 4 || colors < 1)
+  {
+    is_raw = 0;
+    RUN_CALLBACK(LIBRAW_PROGRESS_IDENTIFY, 1, 2);
+    return;
+  }
+  {
+    // Check cam_mul range
+    int cmul_ok = 1;
+    FORCC if (cam_mul[c] <= 0.001f) cmul_ok = 0;
+    ;
+
+    if (cmul_ok)
+    {
+      double cmin = cam_mul[0], cmax;
+      double cnorm[4];
+      FORCC cmin = MIN(cmin, cam_mul[c]);
+      FORCC cnorm[c] = cam_mul[c] / cmin;
+      cmax = cmin = cnorm[0];
+      FORCC
+      {
+        cmin = MIN(cmin, cnorm[c]);
+        cmax = MIN(cmax, cnorm[c]);
+      }
+      if (cmin <= 0.01f || cmax > 100.f)
+        cmul_ok = false;
+    }
+    if (!cmul_ok)
+    {
+      if (cam_mul[0] > 0)
+        cam_mul[0] = 0;
+      cam_mul[3] = 0;
+    }
+  }
+  if ((use_camera_matrix & (((use_camera_wb || dng_version)?1:0) | 0x2)) &&
+      cmatrix[0][0] > 0.125)
+  {
+    memcpy(rgb_cam, cmatrix, sizeof cmatrix);
+    raw_color = 0;
+  }
+  if (raw_color && !CM_found)
+    CM_found = adobe_coeff(maker_index, normalized_model);
+  else if ((imgdata.color.cam_xyz[0][0] < 0.01) && !CM_found)
+    CM_found = adobe_coeff(maker_index, normalized_model, 1);
+
+  if (load_raw == &LibRaw::kodak_radc_load_raw)
+    if ((raw_color) && !CM_found)
+		CM_found = adobe_coeff(LIBRAW_CAMERAMAKER_Apple, "Quicktake");
+
+  if ((maker_index != LIBRAW_CAMERAMAKER_Unknown) && normalized_model[0])
+    SetStandardIlluminants (maker_index, normalized_model);
+
+  // Clear erorneus fuji_width if not set through parse_fuji or for DNG
+  if (fuji_width && !dng_version &&
+      !(imgdata.process_warnings & LIBRAW_WARN_PARSEFUJI_PROCESSED))
+    fuji_width = 0;
+
+  if (fuji_width)
+  {
+    fuji_width = width >> !fuji_layout;
+    filters = fuji_width & 1 ? 0x94949494 : 0x49494949;
+    width = (height >> fuji_layout) + fuji_width;
+    height = width - 1;
+    pixel_aspect = 1;
+  }
+  else
+  {
+    if (raw_height < height)
+      raw_height = height;
+    if (raw_width < width)
+      raw_width = width;
+  }
+  if (!tiff_bps)
+    tiff_bps = 12;
+  if (!maximum)
+  {
+    maximum = (1 << tiff_bps) - 1;
+    if (maximum < 0x10000 && curve[maximum] > 0 &&
+        load_raw == &LibRaw::sony_arw2_load_raw)
+      maximum = curve[maximum];
+  }
+  if (maximum > 0xffff)
+    maximum = 0xffff;
+  if (!load_raw || height < 22 || width < 22 ||
+      (tiff_bps > 16 &&
+       (load_raw != &LibRaw::deflate_dng_load_raw &&
+        load_raw != &LibRaw::float_dng_load_raw_placeholder)) ||
+      tiff_samples > 6 || colors > 4)
+    is_raw = 0;
+
+  if (raw_width < 22 || raw_width > 64000 || raw_height < 22 ||
+      pixel_aspect < 0.1 || pixel_aspect > 10. ||
+      raw_height > 64000)
+    is_raw = 0;
+   if(raw_width <= left_margin || raw_height <= top_margin)
+       is_raw = 0;
+   if (dng_version && (tiff_samples < 1 || tiff_samples > 4))
+       is_raw = 0; // we do not handle DNGs with more than 4 values per pixel
+#ifdef NO_JASPER
+  if (load_raw == &LibRaw::redcine_load_raw)
+  {
+    is_raw = 0;
+    imgdata.process_warnings |= LIBRAW_WARN_NO_JASPER;
+  }
+#endif
+#ifdef NO_JPEG
+  if (load_raw == &LibRaw::kodak_jpeg_load_raw ||
+      load_raw == &LibRaw::lossy_dng_load_raw)
+  {
+    is_raw = 0;
+    imgdata.process_warnings |= LIBRAW_WARN_NO_JPEGLIB;
+  }
+#endif
+  if (!cdesc[0])
+    strcpy(cdesc, colors == 3 ? "RGBG" : "GMCY");
+  if (!raw_height)
+    raw_height = height;
+  if (!raw_width)
+    raw_width = width;
+  if (filters > 999 && colors == 3)
+    filters |= ((filters >> 2 & 0x22222222) | (filters << 2 & 0x88888888)) &
+               filters << 1;
+notraw:
+  if (flip == (int)UINT_MAX)
+    flip = tiff_flip;
+  if (flip == (int)UINT_MAX)
+    flip = 0;
+
+  // Convert from degrees to bit-field if needed
+  if (flip > 89 || flip < -89)
+  {
+    switch ((flip + 3600) % 360)
+    {
+    case 270:
+      flip = 5;
+      break;
+    case 180:
+      flip = 3;
+      break;
+    case 90:
+      flip = 6;
+      break;
+    }
+  }
+
+  if (pana_bpp)
+    imgdata.color.raw_bps = pana_bpp;
+  else if ((load_raw == &LibRaw::phase_one_load_raw) ||
+           (load_raw == &LibRaw::phase_one_load_raw_c))
+    imgdata.color.raw_bps = ph1.format;
+  else
+    imgdata.color.raw_bps = tiff_bps;
+
+  RUN_CALLBACK(LIBRAW_PROGRESS_IDENTIFY, 1, 2);
+}
+
+void LibRaw::identify_process_dng_fields()
+{
+	if (!dng_version) return;
+	int c;
+	{
+		/* copy DNG data from per-IFD field to color.dng */
+		int iifd = find_ifd_by_offset(data_offset);
+		int pifd = find_ifd_by_offset(thumb_offset);
+
+#define CFAROUND(value, filters)                                               \
+  filters ? (filters >= 1000 ? ((value + 1) / 2) * 2 : ((value + 5) / 6) * 6)  \
+          : value
+
+#define IFDCOLORINDEX(ifd, subset, bit)                                        \
+  (tiff_ifd[ifd].dng_color[subset].parsedfields & bit)                         \
+      ? ifd                                                                    \
+      : ((tiff_ifd[0].dng_color[subset].parsedfields & bit) ? 0 : -1)
+
+#define IFDLEVELINDEX(ifd, bit)                                                \
+  (tiff_ifd[ifd].dng_levels.parsedfields & bit)                                \
+      ? ifd                                                                    \
+      : ((tiff_ifd[0].dng_levels.parsedfields & bit) ? 0 : -1)
+
+#define COPYARR(to, from) memmove(&to, &from, sizeof(from))
+
+		if (iifd < (int)tiff_nifds && iifd >= 0)
+		{
+			int sidx;
+			// Per field, not per structure
+			if (!(imgdata.params.raw_processing_options &
+				LIBRAW_PROCESSING_DONT_CHECK_DNG_ILLUMINANT))
+			{
+				int illidx[2], cmidx[2], calidx[2], abidx;
+				for (int i = 0; i < 2; i++)
+				{
+					illidx[i] = IFDCOLORINDEX(iifd, i, LIBRAW_DNGFM_ILLUMINANT);
+					cmidx[i] = IFDCOLORINDEX(iifd, i, LIBRAW_DNGFM_COLORMATRIX);
+					calidx[i] = IFDCOLORINDEX(iifd, i, LIBRAW_DNGFM_CALIBRATION);
+				}
+				abidx = IFDLEVELINDEX(iifd, LIBRAW_DNGFM_ANALOGBALANCE);
+				// Data found, all in same ifd, illuminants are inited
+				if (illidx[0] >= 0 && illidx[0] < (int)tiff_nifds &&
+					illidx[0] == illidx[1] && illidx[0] == cmidx[0] &&
+					illidx[0] == cmidx[1] &&
+					tiff_ifd[illidx[0]].dng_color[0].illuminant > 0 &&
+					tiff_ifd[illidx[0]].dng_color[1].illuminant > 0)
+				{
+					sidx = illidx[0]; // => selected IFD
+					double cc[4][4], cm[4][3], cam_xyz[4][3];
+					// CM -> Color Matrix
+					// CC -> Camera calibration
+					for (int j = 0; j < 4; j++)
+						for (int i = 0; i < 4; i++)
+							cc[j][i] = i == j;
+					int colidx = -1;
+
+					// IS D65 here?
+					for (int i = 0; i < 2; i++)
+					{
+						if (tiff_ifd[sidx].dng_color[i].illuminant == LIBRAW_WBI_D65)
+						{
+							colidx = i;
+							break;
+						}
+					}
+
+					// Other daylight-type ill
+					if (colidx < 0)
+						for (int i = 0; i < 2; i++)
+						{
+							int ill = tiff_ifd[sidx].dng_color[i].illuminant;
+							if (ill == LIBRAW_WBI_Daylight || ill == LIBRAW_WBI_D55 ||
+								ill == LIBRAW_WBI_D75 || ill == LIBRAW_WBI_D50 ||
+								ill == LIBRAW_WBI_Flash)
+							{
+								colidx = i;
+								break;
+							}
+						}
+					if (colidx >= 0) // Selected
+					{
+						// Init camera matrix from DNG
+						FORCC for (int j = 0; j < 3; j++) cm[c][j] =
+							tiff_ifd[sidx].dng_color[colidx].colormatrix[c][j];
+
+						if (calidx[colidx] == sidx)
+						{
+							for (int i = 0; i < colors && i < 4; i++)
+								FORCC
+								cc[i][c] = tiff_ifd[sidx].dng_color[colidx].calibration[i][c];
+						}
+
+						if (abidx == sidx)
+							for (int i = 0; i < colors && i < 4; i++)
+								FORCC cc[i][c] *= tiff_ifd[sidx].dng_levels.analogbalance[i];
+						int j;
+						FORCC for (int i = 0; i < 3; i++)
+                            for (cam_xyz[c][i] = j = 0; j < colors && j < 4; j++)
+							    cam_xyz[c][i] +=
+							        cc[c][j] * cm[j][i]; // add AsShotXY later * xyz[i];
+						cam_xyz_coeff(cmatrix, cam_xyz);
+					}
+				}
+			}
+
+			bool noFujiDNGCrop = makeIs(LIBRAW_CAMERAMAKER_Fujifilm)
+				&& (!strcmp(normalized_model, "S3Pro")
+					|| !strcmp(normalized_model, "S5Pro")
+					|| !strcmp(normalized_model, "S2Pro"));
+
+			if (!noFujiDNGCrop &&
+				(imgdata.params.raw_processing_options &LIBRAW_PROCESSING_USE_DNG_DEFAULT_CROP))
+			{
+				sidx = IFDLEVELINDEX(iifd, LIBRAW_DNGFM_CROPORIGIN);
+				int sidx2 = IFDLEVELINDEX(iifd, LIBRAW_DNGFM_CROPSIZE);
+				if (sidx >= 0 && sidx == sidx2 &&
+					tiff_ifd[sidx].dng_levels.default_crop[2] > 0 &&
+					tiff_ifd[sidx].dng_levels.default_crop[3] > 0)
+				{
+					int lm = tiff_ifd[sidx].dng_levels.default_crop[0];
+					int lmm = CFAROUND(lm, filters);
+					int tm = tiff_ifd[sidx].dng_levels.default_crop[1];
+					int tmm = CFAROUND(tm, filters);
+					int ww = tiff_ifd[sidx].dng_levels.default_crop[2];
+					int hh = tiff_ifd[sidx].dng_levels.default_crop[3];
+					if (lmm > lm)
+						ww -= (lmm - lm);
+					if (tmm > tm)
+						hh -= (tmm - tm);
+					if (left_margin + lm + ww <= raw_width &&
+						top_margin + tm + hh <= raw_height)
+					{
+						left_margin += lmm;
+						top_margin += tmm;
+						width = ww;
+						height = hh;
+					}
+				}
+			}
+			if (!(imgdata.color.dng_color[0].parsedfields &
+				LIBRAW_DNGFM_FORWARDMATRIX)) // Not set already (Leica makernotes)
+			{
+				sidx = IFDCOLORINDEX(iifd, 0, LIBRAW_DNGFM_FORWARDMATRIX);
+				if (sidx >= 0)
+					COPYARR(imgdata.color.dng_color[0].forwardmatrix,
+						tiff_ifd[sidx].dng_color[0].forwardmatrix);
+			}
+			if (!(imgdata.color.dng_color[1].parsedfields &
+				LIBRAW_DNGFM_FORWARDMATRIX)) // Not set already (Leica makernotes)
+			{
+				sidx = IFDCOLORINDEX(iifd, 1, LIBRAW_DNGFM_FORWARDMATRIX);
+				if (sidx >= 0)
+					COPYARR(imgdata.color.dng_color[1].forwardmatrix,
+						tiff_ifd[sidx].dng_color[1].forwardmatrix);
+			}
+			for (int ss = 0; ss < 2; ss++)
+			{
+				sidx = IFDCOLORINDEX(iifd, ss, LIBRAW_DNGFM_COLORMATRIX);
+				if (sidx >= 0)
+					COPYARR(imgdata.color.dng_color[ss].colormatrix,
+						tiff_ifd[sidx].dng_color[ss].colormatrix);
+
+				sidx = IFDCOLORINDEX(iifd, ss, LIBRAW_DNGFM_CALIBRATION);
+				if (sidx >= 0)
+					COPYARR(imgdata.color.dng_color[ss].calibration,
+						tiff_ifd[sidx].dng_color[ss].calibration);
+
+				sidx = IFDCOLORINDEX(iifd, ss, LIBRAW_DNGFM_ILLUMINANT);
+				if (sidx >= 0)
+					imgdata.color.dng_color[ss].illuminant =
+					tiff_ifd[sidx].dng_color[ss].illuminant;
+			}
+			// Levels
+			sidx = IFDLEVELINDEX(iifd, LIBRAW_DNGFM_ANALOGBALANCE);
+			if (sidx >= 0)
+				COPYARR(imgdata.color.dng_levels.analogbalance,
+					tiff_ifd[sidx].dng_levels.analogbalance);
+
+			sidx = IFDLEVELINDEX(iifd, LIBRAW_DNGFM_BASELINEEXPOSURE);
+			if (sidx >= 0)
+				imgdata.color.dng_levels.baseline_exposure =
+				tiff_ifd[sidx].dng_levels.baseline_exposure;
+
+			sidx = IFDLEVELINDEX(iifd, LIBRAW_DNGFM_WHITE);
+			if (sidx >= 0 && tiff_ifd[sidx].dng_levels.dng_whitelevel[0])
+				COPYARR(imgdata.color.dng_levels.dng_whitelevel,
+					tiff_ifd[sidx].dng_levels.dng_whitelevel);
+			else if (tiff_ifd[iifd].sample_format <= 2 && tiff_ifd[iifd].bps > 0 && tiff_ifd[iifd].bps < 32)
+				FORC4
+				imgdata.color.dng_levels.dng_whitelevel[c] = (1 << tiff_ifd[iifd].bps) - 1;
+
+
+
+			sidx = IFDLEVELINDEX(iifd, LIBRAW_DNGFM_ASSHOTNEUTRAL);
+			if (sidx >= 0)
+			{
+				COPYARR(imgdata.color.dng_levels.asshotneutral,
+					tiff_ifd[sidx].dng_levels.asshotneutral);
+				if (imgdata.color.dng_levels.asshotneutral[0])
+				{
+					cam_mul[3] = 0;
+					FORCC
+						if (fabs(imgdata.color.dng_levels.asshotneutral[c]) > 0.0001)
+							cam_mul[c] = 1 / imgdata.color.dng_levels.asshotneutral[c];
+				}
+			}
+			sidx = IFDLEVELINDEX(iifd, LIBRAW_DNGFM_BLACK);
+			if (sidx >= 0)
+			{
+				imgdata.color.dng_levels.dng_fblack =
+					tiff_ifd[sidx].dng_levels.dng_fblack;
+				imgdata.color.dng_levels.dng_black =
+					tiff_ifd[sidx].dng_levels.dng_black;
+				COPYARR(imgdata.color.dng_levels.dng_cblack,
+					tiff_ifd[sidx].dng_levels.dng_cblack);
+				COPYARR(imgdata.color.dng_levels.dng_fcblack,
+					tiff_ifd[sidx].dng_levels.dng_fcblack);
+			}
+
+
+			if (pifd >= 0)
+			{
+				sidx = IFDLEVELINDEX(pifd, LIBRAW_DNGFM_PREVIEWCS);
+				if (sidx >= 0)
+					imgdata.color.dng_levels.preview_colorspace =
+					tiff_ifd[sidx].dng_levels.preview_colorspace;
+			}
+			sidx = IFDLEVELINDEX(iifd, LIBRAW_DNGFM_OPCODE2);
+			if (sidx >= 0)
+				meta_offset = tiff_ifd[sidx].opcode2_offset;
+
+			sidx = IFDLEVELINDEX(iifd, LIBRAW_DNGFM_LINTABLE);
+			INT64 linoff = -1;
+			int linlen = 0;
+			if (sidx >= 0)
+			{
+				linoff = tiff_ifd[sidx].lineartable_offset;
+				linlen = tiff_ifd[sidx].lineartable_len;
+			}
+
+			if (linoff >= 0 && linlen > 0)
+			{
+				INT64 pos = ftell(ifp);
+				fseek(ifp, linoff, SEEK_SET);
+				linear_table(linlen);
+				fseek(ifp, pos, SEEK_SET);
+			}
+			// Need to add curve too
+		}
+		/* Copy DNG black level to LibRaw's */
+		if (load_raw == &LibRaw::lossy_dng_load_raw)
+		{
+			maximum = 0xffff;
+			FORC4 imgdata.color.linear_max[c] = imgdata.color.dng_levels.dng_whitelevel[c] = 0xffff;
+		}
+		else
+		{
+			maximum = imgdata.color.dng_levels.dng_whitelevel[0];
+		}
+		black = imgdata.color.dng_levels.dng_black;
+
+		if (tiff_samples == 2 && imgdata.color.dng_levels.dng_cblack[4] * imgdata.color.dng_levels.dng_cblack[5] * tiff_samples
+			== imgdata.color.dng_levels.dng_cblack[LIBRAW_CBLACK_SIZE - 1])
+		{
+			unsigned ff = filters;
+			if (filters > 999 && colors == 3)
+				filters |= ((filters >> 2 & 0x22222222) | (filters << 2 & 0x88888888)) &
+				filters << 1;
+
+			/* Special case, Fuji SuperCCD dng */
+			int csum[4] = { 0,0,0,0 }, ccount[4] = { 0,0,0,0 };
+			int i = 6 + shot_select;
+			for (unsigned row = 0; row < imgdata.color.dng_levels.dng_cblack[4]; row++)
+				for (unsigned col = 0; col < imgdata.color.dng_levels.dng_cblack[5]; col++)
+				{
+					csum[FC(row, col)] += imgdata.color.dng_levels.dng_cblack[i];
+					ccount[FC(row, col)]++;
+					i += tiff_samples;
+				}
+			for (int c = 0; c < 4; c++)
+				if (ccount[c])
+					imgdata.color.dng_levels.dng_cblack[c] += csum[c] / ccount[c];
+			imgdata.color.dng_levels.dng_cblack[4] = imgdata.color.dng_levels.dng_cblack[5] = 0;
+			filters = ff;
+		}
+		else if (tiff_samples > 2 && tiff_samples <= 4 && imgdata.color.dng_levels.dng_cblack[4] * imgdata.color.dng_levels.dng_cblack[5] * tiff_samples
+			== imgdata.color.dng_levels.dng_cblack[LIBRAW_CBLACK_SIZE - 1])
+		{
+			/* Special case, per_channel blacks in RepeatDim, average for per-channel */
+			int csum[4] = { 0,0,0,0 }, ccount[4] = { 0,0,0,0 };
+			int i = 6;
+			for (unsigned row = 0; row < imgdata.color.dng_levels.dng_cblack[4]; row++)
+				for (unsigned col = 0; col < imgdata.color.dng_levels.dng_cblack[5]; col++)
+					for (unsigned c = 0; c < tiff_samples && c < 4; c++)
+					{
+						csum[c] += imgdata.color.dng_levels.dng_cblack[i];
+						ccount[c]++;
+						i++;
+					}
+			for (int c = 0; c < 4; c++)
+				if (ccount[c])
+					imgdata.color.dng_levels.dng_cblack[c] += csum[c] / ccount[c];
+			imgdata.color.dng_levels.dng_cblack[4] = imgdata.color.dng_levels.dng_cblack[5] = 0;
+		}
+
+		memmove(cblack, imgdata.color.dng_levels.dng_cblack, sizeof(cblack));
+
+		if (iifd < (int)tiff_nifds && iifd >= 0)
+		{
+			int sidx = IFDLEVELINDEX(iifd, LIBRAW_DNGFM_LINEARRESPONSELIMIT);
+			if (sidx >= 0)
+			{
+				imgdata.color.dng_levels.LinearResponseLimit =
+					tiff_ifd[sidx].dng_levels.LinearResponseLimit;
+				if (imgdata.color.dng_levels.LinearResponseLimit > 0.1 &&
+					imgdata.color.dng_levels.LinearResponseLimit <= 1.0)
+				{
+					// And approx promote it to linear_max:
+					int bl4 = 0, bl64 = 0;
+					for (int chan = 0; chan < colors && chan < 4; chan++)
+						bl4 += cblack[chan];
+					bl4 /= LIM(colors, 1, 4);
+
+					if (cblack[4] * cblack[5] > 0)
+					{
+						unsigned cnt = 0;
+						for (unsigned c = 0; c < 4096 && c < cblack[4] * cblack[5]; c++)
+						{
+							bl64 += cblack[c + 6];
+							cnt++;
+						}
+						bl64 /= LIM(cnt, 1, 4096);
+					}
+					int rblack = black + bl4 + bl64;
+					for (int chan = 0; chan < colors && chan < 4; chan++)
+						imgdata.color.linear_max[chan] =
+						(maximum - rblack) *
+						imgdata.color.dng_levels.LinearResponseLimit +
+						rblack;
+				}
+			}
+		}
+	}
+}
+
+void LibRaw::identify_finetune_pentax()
+{
+	if (makeIs(LIBRAW_CAMERAMAKER_Pentax) ||
+		makeIs(LIBRAW_CAMERAMAKER_Samsung)) {
+		if (height == 2624 &&
+			width == 3936) // Pentax K10D, Samsung GX10;
+		{
+			height = 2616;
+			width = 3896;
+		}
+		if (height == 3136 &&
+			width == 4864) // Pentax K20D, Samsung GX20;
+		{
+			height = 3124;
+			width = 4688;
+			filters = 0x16161616;
+		}
+	}
+
+	if (makeIs(LIBRAW_CAMERAMAKER_Pentax)) {
+		if ((width == 4352) &&
+			((unique_id == PentaxID_K_r) ||
+			(unique_id == PentaxID_K_x)))
+		{
+			width = 4309;
+			filters = 0x16161616;
+		}
+		if ((width >= 4960) &&
+			((unique_id == PentaxID_K_5) ||
+			(unique_id == PentaxID_K_5_II) ||
+				(unique_id == PentaxID_K_5_II_s)))
+		{
+			left_margin = 10;
+			width = 4950;
+			filters = 0x16161616;
+		}
+		if ((width == 6080) && (unique_id == PentaxID_K_70))
+		{
+			height = 4016;
+			top_margin = 32;
+			width = 6020;
+			left_margin = 60;
+		}
+		if ((width == 4736) && (unique_id == PentaxID_K_7))
+		{
+			height = 3122;
+			width = 4684;
+			filters = 0x16161616;
+			top_margin = 2;
+		}
+		if ((width == 6080) && (unique_id == PentaxID_K_3_II))
+		{
+			left_margin = 4;
+			width = 6040;
+		}
+		if ((width == 6112) && (unique_id == PentaxID_KP))
+		{
+			// From DNG, maybe too strict
+			left_margin = 54;
+			top_margin = 28;
+			width = 6028;
+			height = raw_height - top_margin;
+		}
+		if ((width == 6080) && (unique_id == PentaxID_K_3))
+		{
+			left_margin = 4;
+			width = 6040;
+		}
+		if ((width == 7424) && (unique_id == PentaxID_645D))
+		{
+			height = 5502;
+			width = 7328;
+			filters = 0x61616161;
+			top_margin = 29;
+			left_margin = 48;
+		}
+	}
+	else if (makeIs(LIBRAW_CAMERAMAKER_Ricoh) &&
+		(height == 3014) && (width == 4096))  // Ricoh GX200
+		width = 4014;
+}
+
+void LibRaw::identify_finetune_by_filesize(int fsize)
+{
+
+	if (fsize == 4771840)
+	{ // hack Nikon 3mpix: E880, E885, E990, E995;
+	  // Olympus C-3030Z
+		if (!timestamp && nikon_e995())
+			strcpy(model, "E995");
+	}
+	else if (fsize == 2940928)
+	{ // hack Nikon 2mpix: E2100, E2500
+		if (!timestamp && !nikon_e2100())
+			strcpy(model, "E2500");
+	}
+	else if (fsize == 4775936)
+	{ // hack Nikon 3mpix: E3100, E3200, E3500, E3700;
+	  // Pentax "Optio 33WR";
+	  // Olympus C-740UZ
+		if (!timestamp)
+			nikon_3700();
+	}
+	else if (fsize == 5869568)
+	{ // hack Nikon 4mpix: E4300;
+	  // hack Minolta "DiMAGE Z2"
+		if (!timestamp && minolta_z2())
+		{
+			maker_index = LIBRAW_CAMERAMAKER_Minolta;
+			strcpy(make, "Minolta");
+			strcpy(model, "DiMAGE Z2");
+		}
+	}
+}
+
+void LibRaw::identify_finetune_dcr(char head[64], int fsize, int flen)
+{
+	static const short pana[][6] = {
+		// raw_width, raw_height, left_margin, top_margin, width_increment,
+		// height_increment
+		{3130, 1743, 4, 0, -6, 0},      /* 00 */
+		{3130, 2055, 4, 0, -6, 0},      /* 01 */
+		{3130, 2319, 4, 0, -6, 0},      /* 02 DMC-FZ8 */
+		{3170, 2103, 18, 0, -42, 20},   /* 03 */
+		{3170, 2367, 18, 13, -42, -21}, /* 04 */
+		{3177, 2367, 0, 0, -1, 0},      /* 05 DMC-L1 */
+		{3304, 2458, 0, 0, -1, 0},      /* 06 DMC-FZ30 */
+		{3330, 2463, 9, 0, -5, 0},      /* 07 DMC-FZ18 */
+		{3330, 2479, 9, 0, -17, 4},     /* 08 */
+		{3370, 1899, 15, 0, -44, 20},   /* 09 */
+		{3370, 2235, 15, 0, -44, 20},   /* 10 */
+		{3370, 2511, 15, 10, -44, -21}, /* 11 */
+		{3690, 2751, 3, 0, -8, -3},     /* 12 DMC-FZ50 */
+		{3710, 2751, 0, 0, -3, 0},      /* 13 DMC-L10 */
+		{3724, 2450, 0, 0, 0, -2},      /* 14 */
+		{3770, 2487, 17, 0, -44, 19},   /* 15 */
+		{3770, 2799, 17, 15, -44, -19}, /* 16 */
+		{3880, 2170, 6, 0, -6, 0},      /* 17 DMC-LX1 */
+		{4060, 3018, 0, 0, 0, -2},      /* 18 DMC-FZ35, DMC-FZ38 */
+		{4290, 2391, 3, 0, -8, -1},     /* 19 DMC-LX2 */
+		{4330, 2439, 17, 15, -44, -19}, /* 20 "D-LUX 3" */
+		{4508, 2962, 0, 0, -3, -4},     /* 21 */
+		{4508, 3330, 0, 0, -3, -6},     /* 22 */
+		{10480, 7794, 0, 0, -2, 0},     /* 23: G9 in high-res */
+	};
+	int i,c;
+	struct jhead jh;
+
+	if (makeIs(LIBRAW_CAMERAMAKER_Canon) && !tiff_flip && imCanon.MakernotesFlip)
+	{
+		tiff_flip = imCanon.MakernotesFlip;
+	}
+
+	else if (makeIs(LIBRAW_CAMERAMAKER_Nikon))
+	{
+		if (!load_raw)
+			load_raw = &LibRaw::packed_load_raw;
+		if (model[0] == 'E') // Nikon E8800, E8700, E8400, E5700, E5400, E5000,
+							 // others are diag hacks?
+			load_flags |= !data_offset << 2 | 2;
+	}
+	/* Set parameters based on camera name (for non-DNG files). */
+
+	/* Always 512 for arw2_load_raw */
+	else if (makeIs(LIBRAW_CAMERAMAKER_Sony) &&
+		(raw_width > 3888) && !black && !cblack[0])
+	{
+		black = (load_raw == &LibRaw::sony_arw2_load_raw)
+			? 512
+			: (128 << (tiff_bps - 12));
+	}
+
+	if (is_foveon) {
+		if (height * 2 < width)
+			pixel_aspect = 0.5;
+		if (height > width)
+			pixel_aspect = 2;
+		filters = 0;
+
+	}
+	else if (makeIs(LIBRAW_CAMERAMAKER_Pentax)) {
+		if ((unique_id == PentaxID_K_1) ||
+			(unique_id == PentaxID_K_1_Mark_II)) {
+			top_margin = 18;
+			height = raw_height - top_margin;
+			if (raw_width == 7392) {
+				left_margin = 6;
+				width = 7376;
+			}
+
+		}
+		else if (unique_id == PentaxID_Optio_S_V101) { // (fsize == 3178560)
+			cam_mul[0] *= 4;
+			cam_mul[2] *= 4;
+
+		}
+		else if (unique_id == PentaxID_Optio_33WR) { // (fsize == 4775936)
+			flip = 1;
+			filters = 0x16161616;
+
+		}
+		else if (unique_id == PentaxID_staristD) {
+			load_raw = &LibRaw::unpacked_load_raw;
+			data_error = -1;
+
+		}
+		else if (unique_id == PentaxID_staristDS) {
+			height -= 2;
+		}
+
+	}
+	else if (makeIs(LIBRAW_CAMERAMAKER_Canon)) {
+		if (tiff_bps == 15) { // Canon sRAW
+			if (width == 3344)
+				width = 3272;
+			else if (width == 3872)
+				width = 3866;
+
+			if (height > width) {
+				SWAP(height, width);
+				SWAP(raw_height, raw_width);
+			}
+			if (width == 7200 &&
+				height == 3888) { // Canon EOS 5DS (R);
+				raw_width = width = 6480;
+				raw_height = height = 4320;
+			}
+			filters = 0;
+			tiff_samples = colors = 3;
+			load_raw = &LibRaw::canon_sraw_load_raw;
+		}
+
+		if (!strcmp(normalized_model, "PowerShot 600")) {
+			height = 613;
+			width = 854;
+			raw_width = 896;
+			colors = 4;
+			filters = 0xe1e4e1e4;
+			load_raw = &LibRaw::canon_600_load_raw;
+
+		}
+		else if (!strcmp(normalized_model, "PowerShot A5") ||
+			!strcmp(normalized_model, "PowerShot A5 Zoom")) {
+			height = 773;
+			width = 960;
+			raw_width = 992;
+			pixel_aspect = 256 / 235.0;
+			filters = 0x1e4e1e4e;
+			goto canon_a5;
+
+		}
+		else if (!strcmp(normalized_model, "PowerShot A50")) {
+			height = 968;
+			width = 1290;
+			raw_width = 1320;
+			filters = 0x1b4e4b1e;
+			goto canon_a5;
+
+		}
+		else if (!strcmp(normalized_model, "PowerShot Pro70")) {
+			height = 1024;
+			width = 1552;
+			filters = 0x1e4b4e1b;
+		canon_a5:
+			colors = 4;
+			tiff_bps = 10;
+			load_raw = &LibRaw::packed_load_raw;
+			load_flags = 40;
+
+		}
+		else if (!strcmp(normalized_model, "PowerShot Pro90 IS") ||
+			!strcmp(normalized_model, "PowerShot G1")) {
+			colors = 4;
+			filters = 0xb4b4b4b4;
+
+		}
+		else if (!strcmp(normalized_model, "PowerShot A610")) { // chdk hack
+			if (canon_s2is())
+				strcpy(model + 10, "S2 IS"); // chdk hack
+
+		}
+		else if (!strcmp(normalized_model, "PowerShot SX220 HS")) { // chdk hack
+			mask[1][3] = -4;
+			top_margin = 16;
+			left_margin = 92;
+
+		}
+		else if (!strcmp(normalized_model, "PowerShot S120")) { // chdk hack
+			raw_width = 4192;
+			raw_height = 3062;
+			width = 4022;
+			height = 3016;
+			mask[0][0] = top_margin = 31;
+			mask[0][2] = top_margin + height;
+			left_margin = 120;
+			mask[0][1] = 23;
+			mask[0][3] = 72;
+
+		}
+		else if (!strcmp(normalized_model, "PowerShot G16")) {
+			mask[0][0] = 0;
+			mask[0][2] = 80;
+			mask[0][1] = 0;
+			mask[0][3] = 16;
+			top_margin = 29;
+			left_margin = 120;
+			width = raw_width - left_margin - 48;
+			height = raw_height - top_margin - 14;
+
+		}
+		else if (!strcmp(normalized_model, "PowerShot SX50 HS")) {
+			top_margin = 17;
+		}
+
+	}
+	else if (makeIs(LIBRAW_CAMERAMAKER_Nikon)) {
+		if (!strcmp(model, "D1"))
+		{
+			imgdata.other.analogbalance[0] = cam_mul[0];
+			imgdata.other.analogbalance[2] = cam_mul[2];
+			imgdata.other.analogbalance[1] = imgdata.other.analogbalance[3] =
+				cam_mul[1];
+			cam_mul[0] = cam_mul[1] = cam_mul[2] = 1.0f;
+		}
+
+		else if (!strcmp(model, "D1X"))
+		{
+			width -= 4;
+			pixel_aspect = 0.5;
+		}
+		else if (!strcmp(model, "D40X") ||
+			!strcmp(model, "D60") ||
+			!strcmp(model, "D80") ||
+			!strcmp(model, "D3000"))
+		{
+			height -= 3;
+			width -= 4;
+		}
+		else if (!strcmp(model, "D3") ||
+			!strcmp(model, "D3S") ||
+			!strcmp(model, "D700"))
+		{
+			width -= 4;
+			left_margin = 2;
+		}
+		else if (!strcmp(model, "D3100"))
+		{
+			width -= 28;
+			left_margin = 6;
+		}
+		else if (!strcmp(model, "D5000") ||
+			!strcmp(model, "D90"))
+		{
+			width -= 42;
+		}
+		else if (!strcmp(model, "D5100") ||
+			!strcmp(model, "D7000") ||
+			!strcmp(model, "COOLPIX A"))
+		{
+			width -= 44;
+		}
+		else if (!strcmp(model, "D3200") ||
+			!strcmp(model, "D600") ||
+			!strcmp(model, "D610") ||
+			!strncmp(model, "D800", 4)) // Nikons: D800, D800E
+		{
+			width -= 46;
+		}
+		else if (!strcmp(model, "D4") ||
+			!strcmp(model, "Df"))
+		{
+			width -= 52;
+			left_margin = 2;
+		}
+		else if (!strcmp(model, "D500"))
+		{
+			// Empty - to avoid width-1 below
+		}
+		else if (!strncmp(model, "D40", 3) ||
+			!strncmp(model, "D50", 3) ||
+			!strncmp(model, "D70", 3))
+		{
+			width--;
+		}
+		else if (!strcmp(model, "D100"))
+		{
+			if (load_flags) // compressed NEF
+				raw_width = (width += 3) + 3;
+		}
+		else if (!strcmp(model, "D200"))
+		{
+			left_margin = 1;
+			width -= 4;
+			filters = 0x94949494;
+		}
+		else if (!strncmp(model, "D2H", 3)) // Nikons: D2H, D2Hs
+		{
+			left_margin = 6;
+			width -= 14;
+		}
+		else if (!strncmp(model, "D2X", 3)) // Nikons: D2X, D2Xs
+		{
+			if (width == 3264) // in-camera Hi-speed crop: On
+				width -= 32;
+			else
+				width -= 8;
+		}
+		else if (!strncmp(model, "D300", 4)) // Nikons: D300, D300s
+		{
+			width -= 32;
+		}
+		else if (raw_width == 4032) // Nikon "COOLPIX P7700", "COOLPIX P7800",
+									// "COOLPIX P330", "COOLPIX P340"
+		{
+			if (!strcmp(normalized_model, "COOLPIX P7700"))
+			{
+				maximum = 65504;
+				load_flags = 0;
+			}
+			else if (!strcmp(normalized_model, "COOLPIX P7800"))
+			{
+				maximum = 65504;
+				load_flags = 0;
+			}
+			else if (!strcmp(model, "COOLPIX P340"))
+			{
+				load_flags = 0;
+			}
+		}
+		else if (!strncmp(model, "COOLPIX P", 9) &&
+			raw_width != 4032) // Nikon "COOLPIX P1000", "COOLPIX P6000",
+							   // "COOLPIX P7000", "COOLPIX P7100"
+		{
+			load_flags = 24;
+			filters = 0x94949494;
+			/* the following 'if' is most probably obsolete, because we now read black
+			 * level from metadata */
+			if ((model[9] == '7') && /* P7000, P7100 */
+				((iso_speed >= 400) || (iso_speed == 0)) &&
+				!strstr(software, "V1.2")) /* v. 1.2 seen for P7000 only */
+				black = 255;
+		}
+		else if (!strncmp(model, "COOLPIX B700", 12))
+		{
+			load_flags = 24;
+		}
+		else if (!strncmp(model, "1 ",
+			2)) // Nikons: "1 AW1", "1 J1", "1 J2", "1 J3", "1 J4",
+				// "1 J5", "1 S1", "1 S2", "1 V1", "1 V2", "1 V3"
+		{
+			height -= 2;
+		}
+		else if (fsize == 1581060) // hack Nikon 1mpix: E900
+		{
+			simple_coeff(3);
+			pre_mul[0] = 1.2085;
+			pre_mul[1] = 1.0943;
+			pre_mul[3] = 1.1103;
+		}
+		else if ((fsize == 4771840) &&  // hack Nikon 3mpix: E880, E885, E990
+			strcmp(model, "E995")) // but not E995
+		{
+			filters = 0xb4b4b4b4;
+			simple_coeff(3);
+			pre_mul[0] = 1.196;
+			pre_mul[1] = 1.246;
+			pre_mul[2] = 1.018;
+		}
+		else if ((fsize == 4775936) && // hack Nikon 3mpix: E3100, E3200, E3500
+			(atoi(model + 1) < 3700)) // but not E3700;
+		{
+			filters = 0x49494949;
+		}
+		else if (fsize == 5869568) // hack Nikon 4mpix: E4300;
+		{
+			load_flags = 6;
+		}
+		else if (!strcmp(model, "E2500"))
+		{
+			height -= 2;
+			load_flags = 6;
+			colors = 4;
+			filters = 0x4b4b4b4b;
+		}
+	}
+
+	else if (makeIs(LIBRAW_CAMERAMAKER_Olympus)) {
+		if (OlyID == OlyID_C_740UZ) { // (fsize == 4775936)
+			i = find_green(12, 32, 1188864, 3576832);
+			c = find_green(12, 32, 2383920, 2387016);
+			if (abs(i) < abs(c)) {
+				SWAP(i, c);
+				load_flags = 24;
+			}
+			if (i < 0)
+				filters = 0x61616161;
+		}
+		else if (OlyID == OlyID_C_770UZ) {
+			height = 1718;
+			width = 2304;
+			filters = 0x16161616;
+			load_raw = &LibRaw::packed_load_raw;
+			load_flags = 30;
+		}
+		else {
+			height += height & 1;
+			if (exif_cfa)
+				filters = exif_cfa;
+
+			if (width == 4100) // Olympus E-PL2, E-PL1, E-P2, E-P1, E-620, E-600, E-5, E-30;
+				width -= 4;
+
+			if (width == 4080) // Olympus E-PM1, E-PL3, E-P3;
+				width -= 24;
+
+			if (width == 10400) // Olympus PEN-F, E-M1-II, E-M1-III, E-M1X
+				width -= 12;
+
+			if (width == 8200) // E-M1-III in 50Mp mode, E-M1X
+				width -= 30;
+
+			if (width == 9280) { // Olympus E-M5 Mark II;
+				width -= 6;
+				height -= 6;
+			}
+
+			if (load_raw == &LibRaw::unpacked_load_raw)
+				load_flags = 4;
+			tiff_bps = 12;
+			if ((OlyID == OlyID_E_300) ||
+				(OlyID == OlyID_E_500)) {
+				width -= 20;
+				if (load_raw == &LibRaw::unpacked_load_raw) {
+					maximum = 0xfc3;
+					memset(cblack, 0, sizeof cblack);
+				}
+
+			}
+			else if (OlyID == OlyID_STYLUS_1) {
+				width -= 16;
+				maximum = 0xfff;
+
+			}
+			else if (OlyID == OlyID_E_330) {
+				width -= 30;
+				if (load_raw == &LibRaw::unpacked_load_raw)
+					maximum = 0xf79;
+
+			}
+			else if (OlyID == OlyID_SP_550UZ) {
+				thumb_length = flen - (thumb_offset = 0xa39800);
+				thumb_height = 480;
+				thumb_width = 640;
+
+			}
+			else if (OlyID == OlyID_TG_4) {
+				width -= 16;
+
+			}
+			else if ((OlyID == OlyID_TG_5) ||
+				(OlyID == OlyID_TG_6)) {
+				width -= 26;
+			}
+		}
+
+	}
+	else if (makeIs(LIBRAW_CAMERAMAKER_RoverShot) &&
+		(fsize == 6291456)) { // RoverShot 3320AF
+		fseek(ifp, 0x300000, SEEK_SET);
+		if ((order = guess_byte_order(0x10000)) == 0x4d4d)
+		{
+			height -= (top_margin = 16);
+			width -= (left_margin = 28);
+			maximum = 0xf5c0;
+			strcpy(make, "ISG");
+			model[0] = 0;
+		}
+
+	}
+	else if (makeIs(LIBRAW_CAMERAMAKER_Fujifilm)) {
+		if (!strcmp(model, "S2Pro"))
+		{
+			height = 2144;
+			width = 2880;
+			flip = 6;
+		}
+		else if (load_raw != &LibRaw::packed_load_raw && strncmp(model, "X-", 2) &&
+			filters >= 1000) // Bayer and not an X-model
+			maximum = (is_raw == 2 && shot_select) ? 0x2f00 : 0x3e00;
+
+		if (FujiCropMode == 1)
+		{ // FF crop on GFX
+			width = raw_width;
+			height = raw_height;
+		}
+		else if (FujiCropMode == 4)
+		{ /* electronic shutter, high speed mode (1.25x crop) */
+			height = raw_height;
+		}
+
+		top_margin = (raw_height >= height) ? (raw_height - height) >> 2 << 1 : 0;
+		left_margin = (raw_width >= width) ? (raw_width - width) >> 2 << 1 : 0;
+
+		if (!strcmp(model, "X-T3") || !strcmp(model, "X-T4") || !strcmp(model, "X100V") || !strcmp(model, "X-T30") || !strcmp(model, "X-Pro3"))
+		{
+			top_margin = 0;
+			if (FujiCropMode == 0)
+			{
+				top_margin = 6;
+				height = 4170;
+				left_margin = 0;
+				width = 6246;
+			}
+			else if (FujiCropMode == 4)
+			{ /* electronic shutter, high speed mode (1.25x crop) */
+				left_margin = 624;
+				width = 5004;
+			}
+		}
+
+		if (width == 2848 || // Fujifilm X-S1, X10, XF1
+			width == 3664)   // Fujifilm "HS10 HS11"
+			filters = 0x16161616;
+
+		if (width == 4032 || // Fujifilm X20, X30, XQ1, XQ2
+			width == 4952)   // Fujifilm X-A1, X-A2, X-E1, X-M1, X-Pro1
+			left_margin = 0;
+
+		if (width == 3328 &&
+			(width -= 66)) // Fujifilm F550EXR, F600EXR, F770EXR, F800EXR, F900EXR,
+						   // HS20EXR, HS30EXR, HS33EXR, HS50EXR
+			left_margin = 34;
+
+		if (width == 4936) // Fujifilm X-E2S, X-E2, X-T10, X-T1, X100S, X100T, X70
+			left_margin = 4;
+
+		if (width == 6032) // Fujifilm X100F, X-T2, X-T20, X-Pro2, X-H1, X-E3
+			left_margin = 0;
+
+		if (!strcmp(normalized_model, "DBP for GX680"))
+		{
+			/*
+			7712 2752 -> 5504 3856
+			*/
+
+			/*
+			width = 688;
+			height = 30848;
+			raw_width = 688;
+			raw_height = 30848;
+			*/
+
+			raw_width = 5504;
+			raw_height = 3856;
+			left_margin = 32;
+			top_margin = 8;
+			width = raw_width - left_margin - 32;
+			height = raw_height - top_margin - 8;
+
+			load_raw = &LibRaw::unpacked_load_raw_FujiDBP;
+			//  maximum = 0x0fff;
+			filters = 0x16161616;
+			load_flags = 0;
+			flip = 6;
+		}
+
+		if (!strcmp(model, "HS50EXR") || !strcmp(model, "F900EXR"))
+		{
+			width += 2;
+			left_margin = 0;
+			filters = 0x16161616;
+		}
+		if (!strncmp(model, "GFX 50", 6))
+		{
+			left_margin = 0;
+			top_margin = 0;
+		}
+		if (!strncmp(model, "GFX 100", 7))
+		{
+			left_margin = 0;
+			width = raw_width - 146;
+			height = raw_height - (top_margin = 2);
+			if (tiff_bps == 16)
+				maximum = 0xffff;
+		}
+		if (!strcmp(normalized_model, "S5100"))
+		{
+			height -= (top_margin = 6);
+		}
+		if (fuji_layout)
+			raw_width *= is_raw;
+		if (filters == 9)
+			FORC(36)
+			((char *)xtrans)[c] =
+			xtrans_abs[(c / 6 + top_margin) % 6][(c + left_margin) % 6];
+
+	}
+	else if (makeIs(LIBRAW_CAMERAMAKER_Konica)) {
+		if (!strcmp(model, "KD-400Z")) {
+			height = 1712;
+			width = 2312;
+			raw_width = 2336;
+			goto konica_400z;
+		}
+		else if (!strcmp(model, "KD-510Z")) {
+			goto konica_510z;
+		}
+
+	}
+	else if (makeIs(LIBRAW_CAMERAMAKER_Minolta)) {
+		if (fsize == 5869568) { // hack Minolta "DiMAGE Z2"
+			load_flags = 30;
+		}
+
+		if (!load_raw && (maximum = 0xfff))
+		{
+			load_raw = &LibRaw::unpacked_load_raw;
+		}
+		if (!strncmp(model, "DiMAGE A",
+			8)) // Minolta "DiMAGE A1", "DiMAGE A2", "DiMAGE A200"
+		{
+			if (!strcmp(model, "DiMAGE A200"))
+				filters = 0x49494949;
+			tiff_bps = 12;
+			load_raw = &LibRaw::packed_load_raw;
+		}
+		else if (!strncmp(normalized_model, "DG-", 3))
+		{
+			load_raw = &LibRaw::packed_load_raw;
+		}
+		else if (!strncmp(model, "DiMAGE G",
+			8)) // hack Minolta "DiMAGE G400", "DiMAGE G500",
+				// "DiMAGE G530", "DiMAGE G600"
+		{
+			if (model[8] == '4') // DiMAGE G400
+			{
+				height = 1716;
+				width = 2304;
+			}
+			else if (model[8] == '5') // DiMAGE G500 / G530
+			{
+			konica_510z:
+				height = 1956;
+				width = 2607;
+				raw_width = 2624;
+			}
+			else if (model[8] == '6') // DiMAGE G600
+			{
+				height = 2136;
+				width = 2848;
+			}
+			data_offset += 14;
+			filters = 0x61616161;
+		konica_400z:
+			load_raw = &LibRaw::unpacked_load_raw;
+			maximum = 0x3df;
+			order = 0x4d4d;
+		}
+
+	}
+	else if (makeIs(LIBRAW_CAMERAMAKER_Samsung)) {
+		if (raw_width == 4704) // Samsung NX100, NX10, NX11,
+		{
+			height -= top_margin = 8;
+			width -= 2 * (left_margin = 8);
+			load_flags = 32;
+		}
+		else if (!strcmp(model, "NX3000")) // Samsung NX3000; raw_width: 5600
+		{
+			top_margin = 38;
+			left_margin = 92;
+			width = 5456;
+			height = 3634;
+			filters = 0x61616161;
+			colors = 3;
+		}
+		else if (raw_height == 3714) // Samsung NX2000, NX300M, NX300, NX30, EK-GN120
+		{
+			height -= top_margin = 18;
+			left_margin = raw_width - (width = 5536);
+			if (raw_width != 5600)
+				left_margin = top_margin = 0;
+			filters = 0x61616161;
+			colors = 3;
+		}
+		else if (raw_width == 5632) // Samsung NX1000, NX200, NX20, NX210
+		{
+			order = 0x4949;
+			height = 3694;
+			top_margin = 2;
+			width = 5574 - (left_margin = 32 + tiff_bps);
+			if (tiff_bps == 12)
+				load_flags = 80;
+		}
+		else if (raw_width == 5664) // Samsung "NX mini"
+		{
+			height -= top_margin = 17;
+			left_margin = 96;
+			width = 5544;
+			filters = 0x49494949;
+		}
+		else if (raw_width == 6496) // Samsung NX1, NX500
+		{
+			filters = 0x61616161;
+			if (!black && !cblack[0] && !cblack[1] && !cblack[2] && !cblack[3])
+				black = 1 << (tiff_bps - 7);
+		}
+		else if (!strcmp(model, "EX1")) // Samsung EX1; raw_width: 3688
+		{
+			order = 0x4949;
+			height -= 20;
+			top_margin = 2;
+			if ((width -= 6) > 3682)
+			{
+				height -= 10;
+				width -= 46;
+				top_margin = 8;
+			}
+		}
+		else if (!strcmp(model, "WB2000")) // Samsung WB2000; raw_width: 3728
+		{
+			order = 0x4949;
+			height -= 3;
+			top_margin = 2;
+			if ((width -= 10) > 3718)
+			{
+				height -= 28;
+				width -= 56;
+				top_margin = 8;
+			}
+		}
+		else if (!strcmp(model, "WB550")) // Samsung WB550; raw_width: 4000
+		{
+			order = 0x4949;
+		}
+		else if (!strcmp(model, "EX2F")) // Samsung EX2F; raw_width: 4176
+		{
+			height = 3030;
+			width = 4040;
+			top_margin = 15;
+			left_margin = 24;
+			order = 0x4949;
+			filters = 0x49494949;
+			load_raw = &LibRaw::unpacked_load_raw;
+		}
+	}
+
+	else if (makeIs(LIBRAW_CAMERAMAKER_ST_Micro) && !strcmp(model, "STV680 VGA"))
+	{
+		black = 16;
+	}
+	else if (!strcmp(model, "N95"))
+	{
+		height = raw_height - (top_margin = 2);
+	}
+	else if (!strcmp(model, "640x480"))
+	{
+		gamma_curve(0.45, 4.5, 1, 255);
+	}
+	else if (makeIs(LIBRAW_CAMERAMAKER_Hasselblad))
+	{
+		if (load_raw == &LibRaw::lossless_jpeg_load_raw)
+			load_raw = &LibRaw::hasselblad_load_raw;
+
+		if ((imHassy.SensorCode == 4) && !strncmp(model, "V96C", 4)) { // Hasselblad V96C
+			strcpy(model, "V96C");
+			strcpy(normalized_model, model);
+			height -= (top_margin = 6);
+			width -= (left_margin = 3) + 7;
+			filters = 0x61616161;
+
+		}
+		else if ((imHassy.SensorCode == 9) && imHassy.uncropped) { // various Hasselblad '-39'
+			height = 5444;
+			width = 7248;
+			top_margin = 4;
+			left_margin = 7;
+			filters = 0x61616161;
+
+		}
+		else if ((imHassy.SensorCode == 13) && imHassy.uncropped) { // Hasselblad H4D-40, H5D-40
+			height -= 84;
+			width -= 82;
+			top_margin = 4;
+			left_margin = 41;
+			filters = 0x61616161;
+
+		}
+		else if ((imHassy.SensorCode == 11) && imHassy.uncropped) { // Hasselblad H5D-50
+			height -= 84;
+			width -= 82;
+			top_margin = 4;
+			left_margin = 41;
+			filters = 0x61616161;
+
+		}
+		else if ((imHassy.SensorCode == 15) &&
+			!imHassy.SensorSubCode && // Hasselblad H5D-50c
+			imHassy.uncropped) {
+			left_margin = 52;
+			top_margin = 100;
+			width = 8272;
+			height = 6200;
+			black = 256;
+
+		}
+		else if ((imHassy.SensorCode == 15) &&
+			(imHassy.SensorSubCode == 2) && // various Hasselblad X1D cameras
+			imHassy.uncropped) {
+			top_margin = 96;
+			height -= 96;
+			left_margin = 48;
+			width -= 106;
+			maximum = 0xffff;
+			tiff_bps = 16;
+
+		}
+		else if ((imHassy.SensorCode == 12) && imHassy.uncropped) { // Hasselblad H4D-60
+			if (black > 500) { // (imHassy.format == LIBRAW_HF_FFF)
+				top_margin = 12;
+				left_margin = 44;
+				width = 8956;
+				height = 6708;
+				memset(cblack, 0, sizeof(cblack));
+				black = 512;
+			}
+			else { // (imHassy.format == LIBRAW_HF_3FR)
+				top_margin = 8;
+				left_margin = 40;
+				width = 8964;
+				height = 6716;
+				black += load_flags = 256;
+				maximum = 0x8101;
+			}
+
+		}
+		else if ((imHassy.SensorCode == 17) && imHassy.uncropped) { // Hasselblad H6D-100c, A6D-100c
+			left_margin = 64;
+			width = 11608;
+			top_margin = 108;
+			height = raw_height - top_margin;
+		}
+
+		if (tiff_samples > 1)
+		{
+			is_raw = tiff_samples + 1;
+			if (!shot_select && !half_size)
+				filters = 0;
+		}
+	}
+	else if (makeIs(LIBRAW_CAMERAMAKER_Sinar))
+	{
+		if (!load_raw)
+			load_raw = &LibRaw::unpacked_load_raw;
+		if (is_raw > 1 && !shot_select)
+			filters = 0;
+		maximum = 0x3fff;
+	}
+
+	if (load_raw == &LibRaw::sinar_4shot_load_raw)
+	{
+		if (is_raw > 1 && !shot_select)
+			filters = 0;
+	}
+	else if (makeIs(LIBRAW_CAMERAMAKER_Leaf))
+	{
+		maximum = 0x3fff;
+		fseek(ifp, data_offset, SEEK_SET);
+		if (ljpeg_start(&jh, 1) && jh.bits == 15)
+			maximum = 0x1fff;
+		if (tiff_samples > 1)
+			filters = 0;
+		if (tiff_samples > 1 || tile_length < raw_height)
+		{
+			load_raw = &LibRaw::leaf_hdr_load_raw;
+			raw_width = tile_width;
+		}
+		if ((width | height) == 2048)
+		{
+			if (tiff_samples == 1)
+			{
+				filters = 1;
+				strcpy(cdesc, "RBTG");
+				strcpy(model, "CatchLight");
+				strcpy(normalized_model, model);
+				top_margin = 8;
+				left_margin = 18;
+				height = 2032;
+				width = 2016;
+			}
+			else
+			{
+				strcpy(model, "DCB2");
+				strcpy(normalized_model, model);
+				top_margin = 10;
+				left_margin = 16;
+				height = 2028;
+				width = 2022;
+			}
+		}
+		else if (width + height == 3144 + 2060)
+		{
+			if (!model[0])
+			{
+				strcpy(model, "Cantare");
+				strcpy(normalized_model, model);
+			}
+			if (width > height)
+			{
+				top_margin = 6;
+				left_margin = 32;
+				height = 2048;
+				width = 3072;
+				filters = 0x61616161;
+			}
+			else
+			{
+				left_margin = 6;
+				top_margin = 32;
+				width = 2048;
+				height = 3072;
+				filters = 0x16161616;
+			}
+			if (!cam_mul[0] || model[0] == 'V')
+				filters = 0;
+			else
+				is_raw = tiff_samples;
+		}
+		else if (width == 2116) // Leaf "Valeo 6"
+		{
+			strcpy(model, "Valeo 6");
+			strcpy(normalized_model, model);
+			height -= 2 * (top_margin = 30);
+			width -= 2 * (left_margin = 55);
+			filters = 0x49494949;
+		}
+		else if (width == 3171) // Leaf "Valeo 6"
+		{
+			strcpy(model, "Valeo 6");
+			strcpy(normalized_model, model);
+			height -= 2 * (top_margin = 24);
+			width -= 2 * (left_margin = 24);
+			filters = 0x16161616;
+		}
+	}
+	else if (makeIs(LIBRAW_CAMERAMAKER_Panasonic))
+	{
+		if (raw_width > 0 &&
+			((flen - data_offset) / (raw_width * 8 / 7) == raw_height))
+			load_raw = &LibRaw::panasonic_load_raw;
+		if (!load_raw)
+		{
+			load_raw = &LibRaw::unpacked_load_raw;
+			load_flags = 4;
+		}
+		zero_is_bad = 1;
+		if ((height += 12) > raw_height)
+			height = raw_height;
+		for (i = 0; i < int(sizeof pana / sizeof *pana); i++)
+			if (raw_width == pana[i][0] && raw_height == pana[i][1])
+			{
+				left_margin = pana[i][2];
+				top_margin = pana[i][3];
+				width += pana[i][4];
+				height += pana[i][5];
+			}
+		if (!tiff_bps && pana_bpp >= 12 && pana_bpp <= 14)
+			tiff_bps = pana_bpp;
+
+		filters = 0x01010101U *
+			(uchar) "\x94\x61\x49\x16"[((filters - 1) ^ (left_margin & 1) ^
+			(top_margin << 1)) &
+			3];
+
+	}
+	else if (makeIs(LIBRAW_CAMERAMAKER_Contax) &&
+		!strcmp(model, "N Digital")) {
+		height = 2047;
+		width = 3072;
+		filters = 0x61616161;
+		data_offset = 0x1a00;
+		load_raw = &LibRaw::packed_load_raw;
+
+	}
+	else if (makeIs(LIBRAW_CAMERAMAKER_Sony)) {
+		if (!strcmp(model, "DSC-F828")) { // Sony DSC-F828
+			width = 3288;
+			left_margin = 5;
+			mask[1][3] = -17;
+			data_offset = 862144;
+			load_raw = &LibRaw::sony_load_raw;
+			filters = 0x9c9c9c9c;
+			colors = 4;
+			strcpy(cdesc, "RGBE");
+
+		}
+		else if (!strcmp(model, "DSC-V3")) { // Sony DSC-V3
+			width = 3109;
+			left_margin = 59;
+			mask[0][1] = 9;
+			data_offset = 787392;
+			load_raw = &LibRaw::sony_load_raw;
+
+		}
+		else if (raw_width == 3984) { // Sony DSC-R1;
+			width = 3925;
+			order = 0x4d4d;
+
+		}
+		else if (raw_width == 4288) { // Sony ILCE-7S, ILCE-7SM2, DSLR-A700, DSLR-A500;
+			width -= 32;
+
+		}
+		else if (raw_width == 4600) { // Sony DSLR-A290, DSLR-A350, DSLR-A380;
+			if (!strcmp(model, "DSLR-A350"))
+				height -= 4;
+			black = 0;
+
+		}
+		else if (raw_width == 4928) {
+			// Sony DSLR-A580, NEX-C3, SLT-A35, DSC-HX99, SLT-A55,
+			// NEX-5N, SLT-A37, SLT-A57, NEX-F3, NEX-6, NEX-5R, NEX-3N, NEX-5T;
+			if (height < 3280)
+				width -= 8;
+
+		}
+		else if (raw_width == 5504) {
+			// Sony ILCE-3000, SLT-A58, DSC-RX100M3, ILCE-QX1,
+			// DSC-RX10M4, DSC-RX100M6, DSC-RX100, DSC-RX100M2, DSC-RX10,
+			// ILCE-5000, DSC-RX100M4, DSC-RX10M2, DSC-RX10M3,
+			// DSC-RX100M5, DSC-RX100M5A;
+			width -= height > 3664 ? 8 : 32;
+
+		}
+		else if (raw_width == 6048) {
+			// Sony SLT-A65, DSC-RX1, SLT-A77, DSC-RX1, ILCA-77M2,
+			// ILCE-7M3, NEX-7, SLT-A99, ILCE-7, DSC-RX1R, ILCE-6000,
+			// ILCE-5100, ILCE-7M2, ILCA-68, ILCE-6300, ILCE-9,
+			// ILCE-6500, ILCE-6400;
+			width -= 24;
+			if (strstr(normalized_model, "RX1") ||
+				strstr(normalized_model, "A99"))
+				width -= 6;
+
+		}
+		else if (raw_width == 7392) { // Sony ILCE-7R;
+			width -= 30;
+
+		}
+		else if (raw_width == 8000) {
+			// Sony ILCE-7RM2, ILCE-7RM2, ILCE-7RM3, DSC-RX1RM2, ILCA-99M2;
+			width -= 32;
+
+		}
+		else if (raw_width == 9600) { // Sony ILCE-7RM4
+			width -= 32;
+
+		}
+		else if (!strcmp(model, "DSLR-A100")) {
+			if (width == 3880) {
+				height--;
+				width = ++raw_width;
+			}
+			else {
+				height -= 4;
+				width -= 4;
+				order = 0x4d4d;
+				load_flags = 2;
+			}
+			filters = 0x61616161;
+		}
+	}
+
+	else if (!strcmp(model, "PIXL")) {
+		height -= top_margin = 4;
+		width -= left_margin = 32;
+		gamma_curve(0, 7, 1, 255);
+
+	}
+	else if (makeIs(LIBRAW_CAMERAMAKER_Kodak)) {
+
+		if (!strncasecmp(model, "EasyShare", 9)) {
+			data_offset = data_offset < 0x15000 ? 0x15000 : 0x17000;
+			load_raw = &LibRaw::packed_load_raw;
+
+		}
+		else if (!strcmp(model, "C603") ||
+			!strcmp(model, "C330") ||
+			!strcmp(model, "12MP")) {
+			order = 0x4949;
+			if (filters && data_offset) {
+				fseek(ifp, data_offset < 4096 ? 168 : 5252, SEEK_SET);
+				read_shorts(curve, 256);
+			}
+			else
+				gamma_curve(0, 3.875, 1, 255);
+
+			load_raw = filters ? &LibRaw::eight_bit_load_raw
+				: strcmp(model, "C330") ? &LibRaw::kodak_c603_load_raw
+				: &LibRaw::kodak_c330_load_raw;
+			load_flags = tiff_bps > 16;
+			tiff_bps = 8;
+
+		}
+		else {
+			if (!strncmp(model, "NC2000", 6) ||
+				!strncmp(model, "EOSDCS", 6) ||
+				!strncmp(model, "DCS4", 4)) {
+				width -= 4;
+				left_margin = 2;
+
+			}
+			else if (!strcmp(model, "DCS660M")) {
+				black = 214;
+
+			}
+			else if (!strcmp(model, "EOS D2000C")) {
+				filters = 0x61616161;
+				if (!black) black = curve[200];
+			}
+
+			if (filters == UINT_MAX) filters = 0x61616161;
+
+			if (!strcmp(model + 4, "20X"))
+				strcpy(cdesc, "MYCY");
+			if (!strcmp(model, "DC25")) {
+				data_offset = 15424;
+			}
+
+			if (!strncmp(model, "DC2", 3)) {
+				raw_height = 2 + (height = 242);
+				if (!strncmp(model, "DC290", 5))
+					iso_speed = 100;
+				if (!strncmp(model, "DC280", 5))
+					iso_speed = 70;
+				if (flen < 100000) {
+					raw_width = 256;
+					width = 249;
+					pixel_aspect = (4.0 * height) / (3.0 * width);
+				}
+				else {
+					raw_width = 512;
+					width = 501;
+					pixel_aspect = (493.0 * height) / (373.0 * width);
+				}
+				top_margin = left_margin = 1;
+				colors = 4;
+				filters = 0x8d8d8d8d;
+				simple_coeff(1);
+				pre_mul[1] = 1.179;
+				pre_mul[2] = 1.209;
+				pre_mul[3] = 1.036;
+				load_raw = &LibRaw::eight_bit_load_raw;
+
+			}
+			else if (!strcmp(model, "DC40")) {
+				height = 512;
+				width = 768;
+				data_offset = 1152;
+				load_raw = &LibRaw::kodak_radc_load_raw;
+				tiff_bps = 12;
+				FORC4 cam_mul[c] = 1.0f;
+
+			}
+			else if (!strcmp(model, "DC50")) {
+				height = 512;
+				width = 768;
+				iso_speed = 84;
+				data_offset = 19712;
+				load_raw = &LibRaw::kodak_radc_load_raw;
+				FORC4 cam_mul[c] = 1.0f;
+
+			}
+			else if (!strcmp(model, "DC120")) {
+				raw_height = height = 976;
+				raw_width = width = 848;
+				iso_speed = 160;
+				pixel_aspect = height / 0.75 / width;
+				load_raw = tiff_compress == 7 ? &LibRaw::kodak_jpeg_load_raw
+					: &LibRaw::kodak_dc120_load_raw;
+
+			}
+			else if (!strcmp(model, "DCS200")) {
+				thumb_height = 128;
+				thumb_width = 192;
+				thumb_offset = 6144;
+				thumb_misc = 360;
+				iso_speed = 140;
+				write_thumb = &LibRaw::layer_thumb;
+				black = 17;
+			}
+		}
+
+	}
+	else if (makeIs(LIBRAW_CAMERAMAKER_Logitech) &&
+		!strcmp(model, "Fotoman Pixtura")) {
+		height = 512;
+		width = 768;
+		data_offset = 3632;
+		load_raw = &LibRaw::kodak_radc_load_raw;
+		filters = 0x61616161;
+		simple_coeff(2);
+
+	}
+	else if (makeIs(LIBRAW_CAMERAMAKER_Apple) &&
+		!strncmp(model, "QuickTake", 9)) {
+		if (head[5])
+			strcpy(model + 10, "200");
+		fseek(ifp, 544, SEEK_SET);
+		height = get2();
+		width = get2();
+		data_offset = (get4(), get2()) == 30 ? 738 : 736;
+		if (height > width) {
+			SWAP(height, width);
+			fseek(ifp, data_offset - 6, SEEK_SET);
+			flip = ~get2() & 3 ? 5 : 6;
+		}
+		filters = 0x61616161;
+
+	}
+	else if (makeIs(LIBRAW_CAMERAMAKER_Rollei) &&
+		!load_raw) {
+		switch (raw_width) {
+		case 1316: // Rollei d530flex
+			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 = &LibRaw::rollei_load_raw;
+
+	}
+	else if (!strcmp(model, "GRAS-50S5C")) {
+		height = 2048;
+		width = 2440;
+		load_raw = &LibRaw::unpacked_load_raw;
+		data_offset = 0;
+		filters = 0x49494949;
+		order = 0x4949;
+		maximum = 0xfffC;
+
+	}
+	else if (!strcmp(model, "BB-500CL")) {
+		height = 2058;
+		width = 2448;
+		load_raw = &LibRaw::unpacked_load_raw;
+		data_offset = 0;
+		filters = 0x94949494;
+		order = 0x4949;
+		maximum = 0x3fff;
+
+	}
+	else if (!strcmp(model, "BB-500GE")) {
+		height = 2058;
+		width = 2456;
+		load_raw = &LibRaw::unpacked_load_raw;
+		data_offset = 0;
+		filters = 0x94949494;
+		order = 0x4949;
+		maximum = 0x3fff;
+
+	}
+	else if (!strcmp(model, "SVS625CL")) {
+		height = 2050;
+		width = 2448;
+		load_raw = &LibRaw::unpacked_load_raw;
+		data_offset = 0;
+		filters = 0x94949494;
+		order = 0x4949;
+		maximum = 0x0fff;
+	}
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/metadata/identify_tools.cpp libkdcraw/libkdcraw/libraw/src/metadata/identify_tools.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/metadata/identify_tools.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/metadata/identify_tools.cpp	2022-11-07 07:46:31.734795008 +0300
@@ -0,0 +1,140 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+short LibRaw::guess_byte_order(int words)
+{
+  uchar test[4][2];
+  int t = 2, msb;
+  double diff, sum[2] = {0, 0};
+
+  fread(test[0], 2, 2, ifp);
+  for (words -= 2; words--;)
+  {
+    fread(test[t], 2, 1, ifp);
+    for (msb = 0; msb < 2; msb++)
+    {
+      diff = (test[t ^ 2][msb] << 8 | test[t ^ 2][!msb]) -
+             (test[t][msb] << 8 | test[t][!msb]);
+      sum[msb] += diff * diff;
+    }
+    t = (t + 1) & 3;
+  }
+  return sum[0] < sum[1] ? 0x4d4d : 0x4949;
+}
+
+float LibRaw::find_green(int bps, int bite, int off0, int off1)
+{
+  UINT64 bitbuf = 0;
+  int vbits, col, i, c;
+  ushort img[2][2064];
+  double sum[] = {0, 0};
+  if (width > 2064)
+    return 0.f; // too wide
+
+  FORC(2)
+  {
+    fseek(ifp, c ? off1 : off0, SEEK_SET);
+    for (vbits = col = 0; col < width; col++)
+    {
+      for (vbits -= bps; vbits < 0; vbits += bite)
+      {
+        bitbuf <<= bite;
+        for (i = 0; i < bite; i += 8)
+          bitbuf |= (unsigned)(fgetc(ifp) << i);
+      }
+      img[c][col] = bitbuf << (64 - bps - vbits) >> (64 - bps);
+    }
+  }
+  FORC(width - 1)
+  {
+    sum[c & 1] += ABS(img[0][c] - img[1][c + 1]);
+    sum[~c & 1] += ABS(img[1][c] - img[0][c + 1]);
+  }
+  if (sum[0] >= 1.0 && sum[1] >= 1.0)
+    return 100 * log(sum[0] / sum[1]);
+  else
+    return 0.f;
+}
+
+void LibRaw::trimSpaces(char *s)
+{
+  char *p = s;
+  int l = strlen(p);
+  if (!l)
+    return;
+  while (isspace(p[l - 1]))
+    p[--l] = 0; /* trim trailing spaces */
+  while (*p && isspace(*p))
+    ++p, --l;   /* trim leading spaces */
+  memmove(s, p, l + 1);
+}
+
+void LibRaw::remove_trailing_spaces(char *string, size_t len)
+{
+  if (len < 1)
+    return; // not needed, b/c sizeof of make/model is 64
+  string[len - 1] = 0;
+  if (len < 3)
+    return; // also not needed
+  len = strnlen(string, len - 1);
+  for (int i = len - 1; i >= 0; i--)
+  {
+    if (isspace((unsigned char)string[i]))
+      string[i] = 0;
+    else
+      break;
+  }
+}
+
+void LibRaw::remove_caseSubstr(char *string, char *subStr) // replace a substring with an equal length of spaces
+{
+  char *found;
+  while ((found = strcasestr(string,subStr))) {
+    if (!found) return;
+    int fill_len = strlen(subStr);
+    int p = found - string;
+    for (int i=p; i<p+fill_len; i++) {
+      string[i] = 32;
+    }
+  }
+  trimSpaces (string);
+}
+
+void LibRaw::removeExcessiveSpaces(char *string) // replace repeating spaces with one space
+{
+	int orig_len = strlen(string);
+	int i = 0;   // counter for resulting string
+	int j = -1;
+	bool prev_char_is_space = false;
+	while (++j < orig_len && string[j] == ' ');
+	while (j < orig_len)  {
+		if (string[j] != ' ')  {
+				string[i++] = string[j++];
+				prev_char_is_space = false;
+		} else if (string[j++] == ' ') {
+			if (!prev_char_is_space) {
+				string[i++] = ' ';
+				prev_char_is_space = true;
+			}
+		}
+	}
+	if (string[i-1] == ' ')
+    string[i-1] = 0;
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/metadata/kodak.cpp libkdcraw/libkdcraw/libraw/src/metadata/kodak.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/metadata/kodak.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/metadata/kodak.cpp	2022-11-07 07:46:31.734795008 +0300
@@ -0,0 +1,363 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+void LibRaw::Kodak_KDC_WBtags(int wb, int wbi)
+{
+  int c;
+  FORC3 icWBC[wb][c] = get4();
+  icWBC[wb][3] = icWBC[wb][1];
+  if (wbi == wb)
+    FORC4 cam_mul[c] = icWBC[wb][c];
+  return;
+}
+
+void LibRaw::Kodak_DCR_WBtags(int wb, unsigned type, int wbi)
+{
+  float mul[3] = {1.0f, 1.0f, 1.0f}, num, mul2;
+  int c;
+  FORC3 mul[c] = (num = getreal(type)) <= 0.001f ? 1.0f : num;
+  icWBC[wb][1] = icWBC[wb][3] = mul[1];
+  mul2 = mul[1] * mul[1];
+  icWBC[wb][0] = mul2 / mul[0];
+  icWBC[wb][2] = mul2 / mul[2];
+  if (wbi == wb)
+    FORC4 cam_mul[c] = icWBC[wb][c];
+  return;
+}
+
+short LibRaw::KodakIllumMatrix(unsigned type, float *romm_camIllum)
+{
+  int c, j, romm_camTemp[9], romm_camScale[3];
+  if (tagtypeIs(LIBRAW_EXIFTAG_TYPE_SRATIONAL))
+  {
+    for (j = 0; j < 9; j++)
+      ((float *)romm_camIllum)[j] = getreal(type);
+    return 1;
+  }
+  else if (tagtypeIs(LIBRAW_EXIFTAG_TYPE_SLONG))
+  {
+    FORC3
+    {
+      romm_camScale[c] = 0;
+      for (j = 0; j < 3; j++)
+      {
+        romm_camTemp[c * 3 + j] = get4();
+        romm_camScale[c] += romm_camTemp[c * 3 + j];
+      }
+    }
+    if ((romm_camScale[0] > 0x1fff) && (romm_camScale[1] > 0x1fff) &&
+        (romm_camScale[2] > 0x1fff))
+    {
+      FORC3 for (j = 0; j < 3; j++)((float *)romm_camIllum)[c * 3 + j] =
+          ((float)romm_camTemp[c * 3 + j]) / ((float)romm_camScale[c]);
+      return 1;
+    }
+  }
+  return 0;
+}
+
+/* Thanks to Alexey Danilchenko for wb as-shot parsing code */
+void LibRaw::parse_kodak_ifd(int base)
+{
+  unsigned entries, tag, type, len, save;
+  int c, wbi = -1;
+
+  static const int wbtag_kdc[] = {
+      LIBRAW_WBI_Auto,        // 64037 / 0xfa25
+      LIBRAW_WBI_Fluorescent, // 64040 / 0xfa28
+      LIBRAW_WBI_Tungsten,    // 64039 / 0xfa27
+      LIBRAW_WBI_Daylight,    // 64041 / 0xfa29
+      -1,
+      -1,
+      LIBRAW_WBI_Shade // 64042 / 0xfa2a
+  };
+
+  static const int wbtag_dcr[] = {
+      LIBRAW_WBI_Daylight,    // 2120 / 0x0848
+      LIBRAW_WBI_Tungsten,    // 2121 / 0x0849
+      LIBRAW_WBI_Fluorescent, // 2122 / 0x084a
+      LIBRAW_WBI_Flash,       // 2123 / 0x084b
+      LIBRAW_WBI_Custom,      // 2124 / 0x084c
+      LIBRAW_WBI_Auto         // 2125 / 0x084d
+  };
+
+  //  int a_blck = 0;
+
+  entries = get2();
+  if (entries > 1024)
+    return;
+  INT64 fsize = ifp->size();
+  while (entries--)
+  {
+    tiff_get(base, &tag, &type, &len, &save);
+    INT64 savepos = ftell(ifp);
+    if (len > 8 && len + savepos > 2 * fsize)
+    {
+      fseek(ifp, save, SEEK_SET); // Recover tiff-read position!!
+      continue;
+    }
+    if (callbacks.exif_cb)
+    {
+      callbacks.exif_cb(callbacks.exifparser_data, tag | 0x20000, type, len,
+                        order, ifp, base);
+      fseek(ifp, savepos, SEEK_SET);
+    }
+    if (tag == 0x03eb) // 1003
+      imgdata.sizes.raw_inset_crop.cleft = get2();
+    else if (tag == 0x03ec) // 1004
+      imgdata.sizes.raw_inset_crop.ctop = get2();
+    else if (tag == 0x03ed) // 1005
+      imgdata.sizes.raw_inset_crop.cwidth = get2();
+    else if (tag == 0x03ee) // 1006
+      imgdata.sizes.raw_inset_crop.cheight = get2();
+    else if (tag == 0x03ef) // 1007
+    {
+      if (!strcmp(model, "EOS D2000C"))
+        black = get2();
+      else
+        imKodak.BlackLevelTop = get2();
+    }
+    else if (tag == 0x03f0) // 1008
+    {
+      if (!strcmp(model, "EOS D2000C"))
+      {
+        if (black) // already set by tag 1007 (0x03ef)
+          black = (black + get2()) / 2;
+        else
+          black = get2();
+      }
+      else
+        imKodak.BlackLevelBottom = get2();
+    }
+
+    else if (tag == 0x03f1)
+    { // 1009 Kodak TextualInfo
+      if (len > 0)
+      {
+        char kti[1024];
+        char *pkti;
+        int nsym = MIN(len, 1023);
+        fread(kti, 1, nsym, ifp);
+        kti[nsym] = 0;
+#ifdef LIBRAW_WIN32_CALLS
+        pkti = strtok(kti, "\x0a");
+#else
+        char *last = 0;
+        pkti = strtok_r(kti, "\x0a", &last);
+#endif
+        while (pkti != NULL)
+        {
+          c = 12;
+          if (((int)strlen(pkti) > c) && (!strncasecmp(pkti, "Camera body:", c)))
+          {
+            while ((pkti[c] == ' ') && (c < (int)strlen(pkti)))
+            {
+              c++;
+            }
+            strcpy(ilm.body, pkti + c);
+          }
+          c = 5;
+          if (((int)strlen(pkti) > c) && (!strncasecmp(pkti, "Lens:", c)))
+          {
+            ilm.CurFocal = atoi(pkti + c);
+          }
+          c = 9;
+          if (((int)strlen(pkti) > c) && (!strncasecmp(pkti, "Aperture:", c)))
+          {
+            while (((pkti[c] == ' ') || (pkti[c] == 'f')) && (c < (int)strlen(pkti)))
+            {
+              c++;
+            }
+            ilm.CurAp = atof(pkti + c);
+          }
+          c = 10;
+          if (((int)strlen(pkti) > c) && (!strncasecmp(pkti, "ISO Speed:", c)))
+          {
+            iso_speed = atoi(pkti + c);
+          }
+          c = 13;
+          if (((int)strlen(pkti) > c) && (!strncasecmp(pkti, "Focal Length:", c)))
+          {
+            ilm.CurFocal = atoi(pkti + c);
+          }
+          c = 13;
+          if (((int)strlen(pkti) > c) && (!strncasecmp(pkti, "Max Aperture:", c)))
+          {
+            while (((pkti[c] == ' ') || (pkti[c] == 'f')) && (c < (int)strlen(pkti)))
+            {
+              c++;
+            }
+            ilm.MaxAp4CurFocal = atof(pkti + c);
+          }
+          c = 13;
+          if (((int)strlen(pkti) > c) && (!strncasecmp(pkti, "Min Aperture:", c)))
+          {
+            while (((pkti[c] == ' ') || (pkti[c] == 'f')) && (c < (int)strlen(pkti)))
+            {
+              c++;
+            }
+            ilm.MinAp4CurFocal = atof(pkti + c);
+          }
+#ifdef LIBRAW_WIN32_CALLS
+          pkti = strtok(NULL, "\x0a");
+#else
+          pkti = strtok_r(NULL, "\x0a", &last);
+#endif
+        }
+      }
+    }
+
+    else if (tag == 0x03f3) // 1011
+      imCommon.FlashEC = getreal(type);
+
+    else if (tag == 0x03fc) // 1020
+    {
+      wbi = getint(type);
+      if ((wbi >= 0) && (wbi < 6) && (wbi != -2))
+        wbi = wbtag_dcr[wbi];
+    }
+    else if (tag == 0x03fd && len == 72) // 1021
+    {                                    /* WB set in software */
+      fseek(ifp, 40, SEEK_CUR);
+      FORC3 cam_mul[c] = 2048.0 / fMAX(1.0f, get2());
+      wbi = -2;
+    }
+
+    else if ((tag == 0x0406) && (len == 1)) // 1030
+      imCommon.CameraTemperature = getreal(type);
+    else if ((tag == 0x0413) && (len == 1)) // 1043
+      imCommon.SensorTemperature = getreal(type);
+    else if (tag == 0x0848) // 2120
+      Kodak_DCR_WBtags(LIBRAW_WBI_Daylight, type, wbi);
+    else if (tag == 0x0849) // 2121
+      Kodak_DCR_WBtags(LIBRAW_WBI_Tungsten, type, wbi);
+    else if (tag == 0x084a) // 2122
+      Kodak_DCR_WBtags(LIBRAW_WBI_Fluorescent, type, wbi);
+    else if (tag == 0x084b) // 2123
+      Kodak_DCR_WBtags(LIBRAW_WBI_Flash, type, wbi);
+    else if (tag == 0x084c) // 2124
+      Kodak_DCR_WBtags(LIBRAW_WBI_Custom, type, wbi);
+    else if (tag == 0x084d) // 2125
+    {
+      if (wbi == -1)
+        wbi = LIBRAW_WBI_Auto;
+      Kodak_DCR_WBtags(LIBRAW_WBI_Auto, type, wbi);
+    }
+    else if (tag == 0x089f) // 2207
+      imKodak.ISOCalibrationGain = getreal(type);
+    else if (tag == 0x0903) // 2307
+      imKodak.AnalogISO = iso_speed = getreal(type);
+    else if (tag == 0x090d) // 2317
+      linear_table(len);
+    else if (tag == 0x09ce) // 2510
+      stmread(imgdata.shootinginfo.InternalBodySerial, len, ifp);
+    else if (tag == 0x0e92) // 3730
+    {
+      imKodak.val018percent = get2();
+      imgdata.color.linear_max[0] = imgdata.color.linear_max[1] =
+          imgdata.color.linear_max[2] = imgdata.color.linear_max[3] =
+              (int)(((float)imKodak.val018percent) / 18.0f * 170.0f);
+    }
+    else if (tag == 0x0e93) // 3731
+      imgdata.color.linear_max[0] = imgdata.color.linear_max[1] =
+          imgdata.color.linear_max[2] = imgdata.color.linear_max[3] =
+              imKodak.val170percent = get2();
+    else if (tag == 0x0e94) // 3732
+      imKodak.val100percent = get2();
+    /*
+        else if (tag == 0x1784)    // 6020
+          iso_speed = getint(type);
+    */
+    else if (tag == 0xfa00) // 64000
+      stmread(imgdata.shootinginfo.BodySerial, len, ifp);
+    else if (tag == 0xfa0d) // 64013
+    {
+      wbi = fgetc(ifp);
+      if ((wbi >= 0) && (wbi < 7))
+        wbi = wbtag_kdc[wbi];
+    }
+    else if (tag == 0xfa13) // 64019
+      width = getint(type);
+    else if (tag == 0xfa14) // 64020
+      height = (getint(type) + 1) & -2;
+    /*
+          height = getint(type);
+
+        else if (tag == 0xfa16)  // 64022
+          raw_width = get2();
+        else if (tag == 0xfa17)  // 64023
+          raw_height = get2();
+    */
+    else if (tag == 0xfa18) // 64024
+    {
+      imKodak.offset_left = getint(LIBRAW_EXIFTAG_TYPE_SSHORT);
+      if (type != LIBRAW_EXIFTAG_TYPE_SSHORT)
+        imKodak.offset_left += 1;
+    }
+    else if (tag == 0xfa19) // 64025
+    {
+      imKodak.offset_top = getint(LIBRAW_EXIFTAG_TYPE_SSHORT);
+      if (type != LIBRAW_EXIFTAG_TYPE_SSHORT)
+        imKodak.offset_top += 1;
+    }
+
+    else if (tag == 0xfa25) // 64037
+      Kodak_KDC_WBtags(LIBRAW_WBI_Auto, wbi);
+    else if (tag == 0xfa27) // 64039
+      Kodak_KDC_WBtags(LIBRAW_WBI_Tungsten, wbi);
+    else if (tag == 0xfa28) // 64040
+      Kodak_KDC_WBtags(LIBRAW_WBI_Fluorescent, wbi);
+    else if (tag == 0xfa29) // 64041
+      Kodak_KDC_WBtags(LIBRAW_WBI_Daylight, wbi);
+    else if (tag == 0xfa2a) // 64042
+      Kodak_KDC_WBtags(LIBRAW_WBI_Shade, wbi);
+
+    else if (tag == 0xfa31) // 64049
+      imgdata.sizes.raw_inset_crop.cwidth = get2();
+    else if (tag == 0xfa32) // 64050
+      imgdata.sizes.raw_inset_crop.cheight = get2();
+    else if (tag == 0xfa3e) // 64062
+      imgdata.sizes.raw_inset_crop.cleft = get2();
+    else if (tag == 0xfa3f) // 64063
+      imgdata.sizes.raw_inset_crop.ctop = get2();
+
+    else if (((tag == 0x07e4) || (tag == 0xfb01)) &&
+             (len == 9)) // 2020 or 64257
+    {
+      if (KodakIllumMatrix(type, (float *)imKodak.romm_camDaylight))
+      {
+        romm_coeff(imKodak.romm_camDaylight);
+      }
+    }
+    else if (((tag == 0x07e5) || (tag == 0xfb02)) &&
+             (len == 9)) // 2021 or 64258
+      KodakIllumMatrix(type, (float *)imKodak.romm_camTungsten);
+    else if (((tag == 0x07e6) || (tag == 0xfb03)) &&
+             (len == 9)) // 2022 or 64259
+      KodakIllumMatrix(type, (float *)imKodak.romm_camFluorescent);
+    else if (((tag == 0x07e7) || (tag == 0xfb04)) &&
+             (len == 9)) // 2023 or 64260
+      KodakIllumMatrix(type, (float *)imKodak.romm_camFlash);
+    else if (((tag == 0x07e8) || (tag == 0xfb05)) &&
+             (len == 9)) // 2024 or 64261
+      KodakIllumMatrix(type, (float *)imKodak.romm_camCustom);
+    else if (((tag == 0x07e9) || (tag == 0xfb06)) &&
+             (len == 9)) // 2025 or 64262
+      KodakIllumMatrix(type, (float *)imKodak.romm_camAuto);
+
+    fseek(ifp, save, SEEK_SET);
+  }
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/metadata/leica.cpp libkdcraw/libkdcraw/libraw/src/metadata/leica.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/metadata/leica.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/metadata/leica.cpp	2022-11-07 07:46:31.734795008 +0300
@@ -0,0 +1,357 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+void LibRaw::setLeicaBodyFeatures(int LeicaMakernoteSignature)
+{
+  if (LeicaMakernoteSignature == -3)
+  { // M8
+    ilm.CameraFormat = LIBRAW_FORMAT_APSH;
+    ilm.CameraMount = LIBRAW_MOUNT_Leica_M;
+  }
+  else if (LeicaMakernoteSignature == -2)
+  { // DMR
+    ilm.CameraFormat = LIBRAW_FORMAT_Leica_DMR;
+    if ((model[0] == 'R') || (model[6] == 'R'))
+      ilm.CameraMount = LIBRAW_MOUNT_Leica_R;
+  }
+  else if (LeicaMakernoteSignature == 0)
+  { // DIGILUX 2
+    ilm.CameraMount = ilm.LensMount = LIBRAW_MOUNT_FixedLens;
+    ilm.FocalType = LIBRAW_FT_ZOOM_LENS;
+  }
+  else if ((LeicaMakernoteSignature == 0x0100) || // X1
+           (LeicaMakernoteSignature == 0x0500) || // X2, X-E (Typ 102)
+           (LeicaMakernoteSignature == 0x0700) || // X (Typ 113)
+           (LeicaMakernoteSignature == 0x1000))
+  { // X-U (Typ 113)
+    ilm.CameraFormat = ilm.LensFormat = LIBRAW_FORMAT_APSC;
+    ilm.CameraMount = ilm.LensMount = LIBRAW_MOUNT_FixedLens;
+    ilm.FocalType = LIBRAW_FT_PRIME_LENS;
+  }
+  else if (LeicaMakernoteSignature == 0x0400)
+  { // X VARIO (Typ 107)
+    ilm.CameraFormat = ilm.LensFormat = LIBRAW_FORMAT_APSC;
+    ilm.CameraMount = ilm.LensMount = LIBRAW_MOUNT_FixedLens;
+    ilm.FocalType = LIBRAW_FT_ZOOM_LENS;
+  }
+  else if ((LeicaMakernoteSignature == 0x0200) || // M10, M10-D, S (Typ 007)
+           (LeicaMakernoteSignature ==
+            0x02ff) || // M (Typ 240), M (Typ 262), M-D (Typ 262), M Monochrom
+                       // (Typ 246), S (Typ 006), S-E (Typ 006), S2, S3
+           (LeicaMakernoteSignature == 0x0300))
+  { // M9, M9 Monochrom, M Monochrom, M-E
+    if ((model[0] == 'M') || (model[6] == 'M'))
+    {
+      ilm.CameraFormat = LIBRAW_FORMAT_FF;
+      ilm.CameraMount = LIBRAW_MOUNT_Leica_M;
+    }
+    else if ((model[0] == 'S') || (model[6] == 'S'))
+    {
+      ilm.CameraFormat = LIBRAW_FORMAT_LeicaS;
+      ilm.CameraMount = LIBRAW_MOUNT_Leica_S;
+    }
+  }
+  else if ((LeicaMakernoteSignature == 0x0600) || // T (Typ 701), TL
+           (LeicaMakernoteSignature == 0x0900) || // SL2, SL (Typ 601), CL, Q2
+           (LeicaMakernoteSignature == 0x1a00))   // TL2
+  {
+    if ((model[0] == 'S') || (model[6] == 'S'))
+    {
+      ilm.CameraFormat = LIBRAW_FORMAT_FF;
+      ilm.CameraMount = LIBRAW_MOUNT_LPS_L;
+    }
+    else if ((model[0] == 'T') || (model[6] == 'T') || (model[0] == 'C') ||
+             (model[6] == 'C'))
+    {
+      ilm.CameraFormat = LIBRAW_FORMAT_APSC;
+      ilm.CameraMount = LIBRAW_MOUNT_LPS_L;
+    }
+    else if (((model[0] == 'Q') || (model[6] == 'Q')) &&
+             ((model[1] == '2') || (model[7] == '2')))
+    {
+      ilm.CameraFormat = ilm.LensFormat = LIBRAW_FORMAT_FF;
+      ilm.CameraMount = ilm.LensMount = LIBRAW_MOUNT_FixedLens;
+      ilm.FocalType = LIBRAW_FT_PRIME_LENS;
+    }
+  }
+  else if (LeicaMakernoteSignature == 0x0800)
+  { // Q (Typ 116)
+    ilm.CameraFormat = ilm.LensFormat = LIBRAW_FORMAT_FF;
+    ilm.CameraMount = ilm.LensMount = LIBRAW_MOUNT_FixedLens;
+    ilm.FocalType = LIBRAW_FT_PRIME_LENS;
+  }
+}
+
+void LibRaw::parseLeicaLensID()
+{
+  ilm.LensID = get4();
+  if (ilm.LensID)
+  {
+    ilm.LensID = ((ilm.LensID >> 2) << 8) | (ilm.LensID & 0x3);
+    if ((ilm.LensID > 0x00ff) && (ilm.LensID < 0x3b00))
+    {
+      ilm.LensMount = ilm.CameraMount;
+      ilm.LensFormat = LIBRAW_FORMAT_FF;
+    }
+  }
+}
+
+int LibRaw::parseLeicaLensName(unsigned len)
+{
+#define plln ilm.Lens
+  if (!len)
+  {
+    strcpy(plln, "N/A");
+    return 0;
+  }
+  stmread(plln, len, ifp);
+  if ((plln[0] == ' ') || !strncasecmp(plln, "not ", 4) ||
+      !strncmp(plln, "---", 3) || !strncmp(plln, "***", 3))
+  {
+    strcpy(plln, "N/A");
+    return 0;
+  }
+  else
+    return 1;
+#undef plln
+}
+
+int LibRaw::parseLeicaInternalBodySerial(unsigned len)
+{
+#define plibs imgdata.shootinginfo.InternalBodySerial
+  if (!len)
+  {
+    strcpy(plibs, "N/A");
+    return 0;
+  }
+  stmread(plibs, len, ifp);
+  if (!strncmp(plibs, "000000000000", 12))
+  {
+    plibs[0] = '0';
+    plibs[1] = '\0';
+    return 1;
+  }
+
+  if (strnlen(plibs, len) == 13)
+  {
+    for (int i = 3; i < 13; i++)
+    {
+      if (!isdigit(plibs[i]))
+        goto non_std;
+    }
+    memcpy(plibs + 15, plibs + 9, 4);
+    memcpy(plibs + 12, plibs + 7, 2);
+    memcpy(plibs + 9, plibs + 5, 2);
+    memcpy(plibs + 6, plibs + 3, 2);
+    plibs[3] = plibs[14] = ' ';
+    plibs[8] = plibs[11] = '/';
+    if (((short)(plibs[3] - '0') * 10 + (short)(plibs[4] - '0')) < 70)
+    {
+      memcpy(plibs + 4, "20", 2);
+    }
+    else
+    {
+      memcpy(plibs + 4, "19", 2);
+    }
+    return 2;
+  }
+non_std:
+#undef plibs
+  return 1;
+}
+
+void LibRaw::parseLeicaMakernote(int base, int uptag, unsigned MakernoteTagType)
+{
+  int c;
+  uchar ci, cj;
+  unsigned entries, tag, type, len, save;
+  short morder, sorder = order;
+  char buf[10];
+  int LeicaMakernoteSignature = -1;
+  INT64 fsize = ifp->size();
+  fread(buf, 1, 10, ifp);
+  if (strncmp(buf, "LEICA", 5))
+  {
+    fseek(ifp, -10, SEEK_CUR);
+    if (uptag == 0x3400)
+      LeicaMakernoteSignature = 0x3400;
+    else
+      LeicaMakernoteSignature = -2; // DMR
+  }
+  else
+  {
+    fseek(ifp, -2, SEEK_CUR);
+    LeicaMakernoteSignature = ((uchar)buf[6] << 8) | (uchar)buf[7];
+    if (!LeicaMakernoteSignature &&
+        (!strncmp(model, "M8", 2) || !strncmp(model + 6, "M8", 2)))
+      LeicaMakernoteSignature = -3;
+    if ((LeicaMakernoteSignature != 0x0000) &&
+        (LeicaMakernoteSignature != 0x0200) &&
+        (LeicaMakernoteSignature != 0x0800) &&
+        (LeicaMakernoteSignature != 0x0900) &&
+        (LeicaMakernoteSignature != 0x02ff))
+      base = ftell(ifp) - 8;
+  }
+  setLeicaBodyFeatures(LeicaMakernoteSignature);
+
+  entries = get2();
+  if (entries > 1000)
+    return;
+  morder = order;
+
+  while (entries--)
+  {
+    order = morder;
+    tiff_get(base, &tag, &type, &len, &save);
+
+    INT64 pos = ifp->tell();
+    if (len > 8 && pos + len > 2 * fsize)
+    {
+      fseek(ifp, save, SEEK_SET); // Recover tiff-read position!!
+      continue;
+    }
+    tag |= uptag << 16;
+    if (len > 100 * 1024 * 1024)
+      goto next; // 100Mb tag? No!
+
+    if (LeicaMakernoteSignature == -3)
+    { // M8
+      if (tag == 0x0310)
+      {
+        parseLeicaLensID();
+      }
+      else if ((tag == 0x0313) && (fabs(ilm.CurAp) < 0.17f))
+      {
+        ilm.CurAp = getreal(type);
+        if (ilm.CurAp > 126.3)
+        {
+          ilm.CurAp = 0.0f;
+        }
+      }
+      else if (tag == 0x0320)
+      {
+        imCommon.CameraTemperature = getreal(type);
+      }
+    }
+    else if (LeicaMakernoteSignature == -2)
+    { // DMR
+      if (tag == 0x000d)
+      {
+        FORC3 cam_mul[c] = get2();
+        cam_mul[3] = cam_mul[1];
+      }
+    }
+    else if (LeicaMakernoteSignature == 0)
+    { // DIGILUX 2
+      if (tag == 0x0007)
+      {
+        imgdata.shootinginfo.FocusMode = get2();
+      }
+      else if (tag == 0x001a)
+      {
+        imgdata.shootinginfo.ImageStabilization = get2();
+      }
+    }
+    else if ((LeicaMakernoteSignature == 0x0100) || // X1
+             (LeicaMakernoteSignature == 0x0400) || // X VARIO
+             (LeicaMakernoteSignature == 0x0500) || // X2, X-E (Typ 102)
+             (LeicaMakernoteSignature == 0x0700) || // X (Typ 113)
+             (LeicaMakernoteSignature == 0x1000))
+    { // X-U (Typ 113)
+      if (tag == 0x040d)
+      {
+        ci = fgetc(ifp);
+        cj = fgetc(ifp);
+        imgdata.shootinginfo.ExposureMode = ((ushort)ci << 8) | cj;
+      }
+    }
+    else if ((LeicaMakernoteSignature == 0x0600) || // TL, T (Typ 701)
+             (LeicaMakernoteSignature == 0x1a00))
+    { // TL2
+      if (tag == 0x040d)
+      {
+        ci = fgetc(ifp);
+        cj = fgetc(ifp);
+        imgdata.shootinginfo.ExposureMode = ((ushort)ci << 8) | cj;
+      }
+      else if (tag == 0x0303)
+      {
+        parseLeicaLensName(len);
+      }
+    }
+    else if (LeicaMakernoteSignature == 0x0200)
+    { // M10, M10-D, S (Typ 007)
+    }
+    else if (LeicaMakernoteSignature == 0x02ff)
+    { // M (Typ 240), M (Typ 262), M-D (Typ 262), M Monochrom (Typ 246),
+      // S (Typ 006), S-E (Typ 006), S2, S3
+      if (tag == 0x0303)
+      {
+        if (parseLeicaLensName(len))
+        {
+          ilm.LensMount = ilm.CameraMount;
+          ilm.LensFormat = ilm.CameraFormat;
+        }
+      }
+    }
+    else if (LeicaMakernoteSignature == 0x0300)
+    { // M9, M9 Monochrom, M Monochrom, M-E
+      if (tag == 0x3400)
+      {
+        parseLeicaMakernote(base, 0x3400, MakernoteTagType);
+      }
+    }
+    else if ((LeicaMakernoteSignature == 0x0800) || // Q (Typ 116)
+             (LeicaMakernoteSignature == 0x0900))
+    { // SL (Typ 601), CL
+      if ((tag == 0x0304) && (len == 1) && ((c = fgetc(ifp)) != 0) &&
+          (ilm.CameraMount == LIBRAW_MOUNT_LPS_L))
+      {
+        strcpy(ilm.Adapter, "M-Adapter L");
+        ilm.LensMount = LIBRAW_MOUNT_Leica_M;
+        ilm.LensFormat = LIBRAW_FORMAT_FF;
+        ilm.LensID = c * 256;
+      }
+      else if (tag == 0x0500)
+      {
+        parseLeicaInternalBodySerial(len);
+      }
+    }
+    else if (LeicaMakernoteSignature == 0x3400)
+    { // tag 0x3400 in M9, M9 Monochrom, M Monochrom
+      if (tag == 0x34003402)
+      {
+        imCommon.CameraTemperature = getreal(type);
+      }
+      else if (tag == 0x34003405)
+      {
+        parseLeicaLensID();
+      }
+      else if ((tag == 0x34003406) && (fabs(ilm.CurAp) < 0.17f))
+      {
+        ilm.CurAp = getreal(type);
+        if (ilm.CurAp > 126.3)
+        {
+          ilm.CurAp = 0.0f;
+        }
+      }
+    }
+
+  next:
+    fseek(ifp, save, SEEK_SET);
+  }
+  order = sorder;
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/metadata/makernotes.cpp libkdcraw/libkdcraw/libraw/src/metadata/makernotes.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/metadata/makernotes.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/metadata/makernotes.cpp	2022-11-07 07:46:31.734795008 +0300
@@ -0,0 +1,894 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+void LibRaw::parseSigmaMakernote (int base, int uptag, unsigned dng_writer) {
+unsigned wb_table1 [] = {
+  LIBRAW_WBI_Auto, LIBRAW_WBI_Daylight, LIBRAW_WBI_Shade, LIBRAW_WBI_Cloudy,
+  LIBRAW_WBI_Tungsten, LIBRAW_WBI_Fluorescent, LIBRAW_WBI_Flash,
+  LIBRAW_WBI_Custom, LIBRAW_WBI_Custom1, LIBRAW_WBI_Custom2
+};
+
+  unsigned entries, tag, type, len, save;
+  unsigned i;
+
+  entries = get2();
+  if (entries > 1000)
+    return;
+  while (entries--) {
+    tiff_get(base, &tag, &type, &len, &save);
+    if (tag == 0x0027) {
+      ilm.LensID = get2();
+    } else if (tag == 0x002a) {
+      ilm.MinFocal = getreal(type);
+      ilm.MaxFocal = getreal(type);
+    } else if (tag == 0x002b) {
+      ilm.MaxAp4MinFocal = getreal(type);
+      ilm.MaxAp4MaxFocal = getreal(type);
+    } else if (tag == 0x0120) {
+      const unsigned tblsz = (sizeof wb_table1 / sizeof wb_table1[0]);
+      if ((len >= tblsz) && (len%3 == 0) && len/3 <= tblsz) {
+        for (i=0; i<(len/3); i++) {
+          icWBC[wb_table1[i]][0] = (int)(getreal(type)*10000.0);
+          icWBC[wb_table1[i]][1] = icWBC[wb_table1[i]][3] = (int)(getreal(type)*10000.0);
+          icWBC[wb_table1[i]][2] = (int)(getreal(type)*10000.0);
+        }
+      }
+    }
+    fseek(ifp, save, SEEK_SET);
+  }
+
+  return;
+}
+
+void LibRaw::parse_makernote_0xc634(int base, int uptag, unsigned dng_writer)
+{
+
+  if (imgdata.params.raw_processing_options & LIBRAW_PROCESSING_SKIP_MAKERNOTES)
+    return;
+
+  if (metadata_blocks++ > LIBRAW_MAX_METADATA_BLOCKS)
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+
+  if (!strncmp(make, "NIKON", 5))
+  {
+    parseNikonMakernote(base, uptag, AdobeDNG);
+    return;
+  }
+  else if (!strncasecmp(make, "LEICA", 5))
+  {
+    parseLeicaMakernote(base, uptag, is_0xc634);
+    return;
+  }
+
+  short morder, sorder = order;
+  char buf[10];
+  INT64 fsize = ifp->size();
+
+  fread(buf, 1, 10, ifp);
+
+  if (!strcmp(buf, "EPSON"))
+  {
+    parseEpsonMakernote(base, uptag, AdobeDNG);
+    return;
+  }
+  else if (!strcmp(buf, "SIGMA"))
+  {
+    parseSigmaMakernote(base, uptag, AdobeDNG);
+    return;
+  }
+
+  unsigned entries, tag, type, len, save, c;
+
+  uchar *CanonCameraInfo;
+  unsigned lenCanonCameraInfo = 0;
+  unsigned typeCanonCameraInfo = 0;
+
+  uchar *table_buf_0x0116;
+  ushort table_buf_0x0116_len = 0;
+  uchar *table_buf_0x2010;
+  ushort table_buf_0x2010_len = 0;
+  uchar *table_buf_0x9050;
+  ushort table_buf_0x9050_len = 0;
+  uchar *table_buf_0x9400;
+  ushort table_buf_0x9400_len = 0;
+  uchar *table_buf_0x9402;
+  ushort table_buf_0x9402_len = 0;
+  uchar *table_buf_0x9403;
+  ushort table_buf_0x9403_len = 0;
+  uchar *table_buf_0x9406;
+  ushort table_buf_0x9406_len = 0;
+  uchar *table_buf_0x940c;
+  ushort table_buf_0x940c_len = 0;
+  uchar *table_buf_0x940e;
+  ushort table_buf_0x940e_len = 0;
+
+  if (!strcmp(buf, "OLYMPUS") || !strcmp(buf, "PENTAX ") ||
+      (!strncmp(make, "SAMSUNG", 7) && (dng_writer == CameraDNG)))
+  {
+    base = ftell(ifp) - 10;
+    fseek(ifp, -2, SEEK_CUR);
+    order = get2();
+    if (buf[0] == 'O')
+      get2();
+  }
+  else if (is_PentaxRicohMakernotes && (dng_writer == CameraDNG))
+  {
+    base = ftell(ifp) - 10;
+    fseek(ifp, -4, SEEK_CUR);
+    order = get2();
+    is_PentaxRicohMakernotes = 1;
+  }
+  else if (!strncmp(buf, "SONY", 4) ||
+           !strcmp(buf, "Panasonic"))
+  {
+    order = 0x4949;
+    fseek(ifp, 2, SEEK_CUR);
+  }
+  else if (!strncmp(buf, "FUJIFILM", 8))
+  {
+    base = ftell(ifp) - 10;
+    order = 0x4949;
+    fseek(ifp, 2, SEEK_CUR);
+  }
+  else if (!strcmp(buf, "OLYMP") ||
+           !strcmp(buf, "Ricoh"))
+  {
+    fseek(ifp, -2, SEEK_CUR);
+  }
+  else if (!strcmp(buf, "AOC") || !strcmp(buf, "QVC"))
+  {
+    fseek(ifp, -4, SEEK_CUR);
+  }
+  else
+  {
+    fseek(ifp, -10, SEEK_CUR);
+    if ((!strncmp(make, "SAMSUNG", 7) && (dng_writer == AdobeDNG)))
+      base = ftell(ifp);
+  }
+
+  entries = get2();
+  if (entries > 1000)
+    return;
+
+  if (!strncasecmp(make, "SONY", 4) ||
+      !strncasecmp(make, "Konica", 6) ||
+      !strncasecmp(make, "Minolta", 7) ||
+      (!strncasecmp(make, "Hasselblad", 10) &&
+       (!strncasecmp(model, "Stellar", 7) ||
+        !strncasecmp(model, "Lunar", 5) ||
+        !strncasecmp(model, "Lusso", 5) ||
+        !strncasecmp(model, "HV", 2))))
+    is_Sony = 1;
+
+  morder = order;
+  while (entries--)
+  {
+    order = morder;
+
+    tiff_get(base, &tag, &type, &len, &save);
+
+    INT64 pos = ifp->tell();
+    if (len > 8 && pos + len > 2 * fsize)
+    {
+      fseek(ifp, save, SEEK_SET); // Recover tiff-read position!!
+      continue;
+    }
+    tag |= uptag << 16;
+    if (len > 100 * 1024 * 1024)
+      goto next; // 100Mb tag? No!
+
+    if (!strncmp(make, "Canon", 5))
+    {
+      if (tag == 0x000d && len < 256000)
+      { // camera info
+        if (!tagtypeIs(LIBRAW_EXIFTAG_TYPE_LONG))
+        {
+          CanonCameraInfo = (uchar *)malloc(MAX(16, len));
+          fread(CanonCameraInfo, len, 1, ifp);
+        }
+        else
+        {
+          CanonCameraInfo = (uchar *)malloc(MAX(16, len * 4));
+          fread(CanonCameraInfo, len, 4, ifp);
+        }
+        lenCanonCameraInfo = len;
+        typeCanonCameraInfo = type;
+      }
+
+      else if (tag == 0x0010)
+      { // Canon ModelID
+        unique_id = get4();
+        setCanonBodyFeatures(unique_id);
+        if (lenCanonCameraInfo)
+        {
+          processCanonCameraInfo(unique_id, CanonCameraInfo, lenCanonCameraInfo,
+                                 typeCanonCameraInfo, AdobeDNG);
+          free(CanonCameraInfo);
+          CanonCameraInfo = 0;
+          lenCanonCameraInfo = 0;
+        }
+      }
+
+      else
+        parseCanonMakernotes(tag, type, len, AdobeDNG);
+    }
+
+    else if (!strncmp(make, "FUJI", 4)) {
+      parseFujiMakernotes(tag, type, len, AdobeDNG);
+
+    } else if (!strncasecmp(make, "Hasselblad", 10) && !is_Sony) {
+      if (tag == 0x0011) {
+        imHassy.SensorCode = getint(type);
+      } else if ((tag == 0x0015) && tagtypeIs(LIBRAW_EXIFTAG_TYPE_ASCII)) {
+        stmread (imHassy.SensorUnitConnector, len, ifp);
+      } else if (tag == 0x0016) {
+        imHassy.CoatingCode = getint(type);
+      } else if ((tag == 0x002a) &&
+                 tagtypeIs(LIBRAW_EXIFTAG_TYPE_SRATIONAL) &&
+                 (len == 12)) {
+        FORC4 for (int i = 0; i < 3; i++)
+                imHassy.mnColorMatrix[c][i] = getreal(type);
+
+      } else if (tag == 0x0031) {
+        imHassy.RecommendedCrop[0] = getint(type);
+        imHassy.RecommendedCrop[1] = getint(type);
+      }
+
+    } else if (!strncmp(make, "OLYMPUS", 7) ||
+             (!strncasecmp(make, "CLAUSS", 6) &&
+              !strncasecmp(model, "piX 5oo", 7)))
+    {
+
+      int SubDirOffsetValid =
+          strncmp(model, "E-300", 5) && strncmp(model, "E-330", 5) &&
+          strncmp(model, "E-400", 5) && strncmp(model, "E-500", 5) &&
+          strncmp(model, "E-1", 3);
+
+      if ((tag == 0x2010) || (tag == 0x2020) || (tag == 0x2030) ||
+          (tag == 0x2031) || (tag == 0x2040) || (tag == 0x2050) ||
+          (tag == 0x3000))
+      {
+        fseek(ifp, save - 4, SEEK_SET);
+        fseek(ifp, base + get4(), SEEK_SET);
+        parse_makernote_0xc634(base, tag, dng_writer);
+      }
+
+      if (!SubDirOffsetValid &&
+          ((len > 4) ||
+           ((tagtypeIs(LIBRAW_EXIFTAG_TYPE_SHORT) ||
+            tagtypeIs(LIBRAW_EXIFTAG_TYPE_SSHORT)) && (len > 2)) ||
+           ((tagtypeIs(LIBRAW_EXIFTAG_TYPE_LONG) ||
+             tagtypeIs(LIBRAW_EXIFTAG_TYPE_SLONG)) && (len > 1)) ||
+           tagtypeIs(LIBRAW_EXIFTAG_TYPE_RATIONAL) ||
+           (type > LIBRAW_EXIFTAG_TYPE_SLONG)))
+        goto skip_Oly_broken_tags;
+
+      else if ((tag >= 0x20100000) && (tag <= 0x2010ffff))
+        parseOlympus_Equipment((tag & 0x0000ffff), type, len, AdobeDNG);
+
+      else if ((tag >= 0x20200000) && (tag <= 0x2020ffff))
+        parseOlympus_CameraSettings(base, (tag & 0x0000ffff), type, len,
+                                    AdobeDNG);
+
+      else if ((tag == 0x20300108) || (tag == 0x20310109)) {
+        imOly.ColorSpace = get2();
+        switch (imOly.ColorSpace) {
+        case 0:
+          imCommon.ColorSpace = LIBRAW_COLORSPACE_sRGB;
+          break;
+        case 1:
+          imCommon.ColorSpace = LIBRAW_COLORSPACE_AdobeRGB;
+          break;
+        case 2:
+          imCommon.ColorSpace = LIBRAW_COLORSPACE_ProPhotoRGB;
+          break;
+        default:
+          imCommon.ColorSpace = LIBRAW_COLORSPACE_Unknown;
+          break;
+        }
+
+      } else if ((tag >= 0x20400000) && (tag <= 0x2040ffff))
+        parseOlympus_ImageProcessing((tag & 0x0000ffff), type, len, AdobeDNG);
+
+      else if ((tag >= 0x30000000) && (tag <= 0x3000ffff))
+        parseOlympus_RawInfo((tag & 0x0000ffff), type, len, AdobeDNG);
+
+      else
+        switch (tag)
+        {
+        case 0x0207:
+          getOlympus_CameraType2();
+          break;
+        case 0x1002:
+          ilm.CurAp = libraw_powf64l(2.0f, getreal(type) / 2);
+          break;
+        case 0x1007:
+          imCommon.SensorTemperature = (float)get2();
+          break;
+        case 0x1008:
+          imCommon.LensTemperature = (float)get2();
+          break;
+        case 0x20501500:
+          getOlympus_SensorTemperature(len);
+          break;
+        }
+
+    skip_Oly_broken_tags:;
+    }
+
+    else if (!strncmp(make, "PENTAX", 6) || !strncmp(model, "PENTAX", 6) ||
+             is_PentaxRicohMakernotes)
+    {
+      parsePentaxMakernotes(base, tag, type, len, dng_writer);
+    }
+    else if (!strncmp(make, "SAMSUNG", 7))
+    {
+      if (dng_writer == AdobeDNG)
+        parseSamsungMakernotes(base, tag, type, len, dng_writer);
+      else
+        parsePentaxMakernotes(base, tag, type, len, dng_writer);
+    }
+    else if (is_Sony)
+    {
+      parseSonyMakernotes(
+          base, tag, type, len, AdobeDNG, table_buf_0x0116,
+          table_buf_0x0116_len, table_buf_0x2010, table_buf_0x2010_len,
+          table_buf_0x9050, table_buf_0x9050_len, table_buf_0x9400,
+          table_buf_0x9400_len, table_buf_0x9402, table_buf_0x9402_len,
+          table_buf_0x9403, table_buf_0x9403_len, table_buf_0x9406,
+          table_buf_0x9406_len, table_buf_0x940c, table_buf_0x940c_len,
+          table_buf_0x940e, table_buf_0x940e_len);
+    }
+  next:
+    fseek(ifp, save, SEEK_SET);
+  }
+
+  order = sorder;
+}
+
+void LibRaw::parse_makernote(int base, int uptag)
+{
+
+  if (imgdata.params.raw_processing_options & LIBRAW_PROCESSING_SKIP_MAKERNOTES)
+    return;
+
+  if (metadata_blocks++ > LIBRAW_MAX_METADATA_BLOCKS)
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+
+  if (!strncmp(make, "NIKON", 5))
+  {
+    parseNikonMakernote(base, uptag, nonDNG);
+    return;
+  }
+  else if (!strncasecmp(make, "LEICA", 5))
+  {
+    parseLeicaMakernote(base, uptag, is_0x927c);
+    return;
+  }
+
+  if (!strncmp(make, "Nokia", 5))
+    return;
+
+  char buf[10];
+  char another_buf[128];
+
+  fseek(ifp, -12, SEEK_CUR);
+  fread (another_buf, 1, 12, ifp);
+  if (!strncmp(another_buf, "SONY", 4) ||
+      !strncmp(another_buf, "VHAB", 4)) { // Sony branded as Hasselblad
+    is_Sony = 1;
+  }
+
+  fread(buf, 1, 10, ifp);
+
+  if (!strncmp(buf, "KDK", 3)  || /* these aren't TIFF tables */
+      !strncmp(buf, "VER", 3)  ||
+      !strncmp(buf, "IIII", 4) ||
+      !strncmp(buf, "MMMM", 4))
+    return;
+
+  if (!strcmp(buf, "EPSON"))
+  {
+    parseEpsonMakernote(base, uptag, nonDNG);
+    return;
+  }
+  else if (!strcmp(buf, "SIGMA"))
+  {
+    parseSigmaMakernote(base, uptag, CameraDNG);
+    return;
+  }
+
+
+  unsigned entries, tag, type, len, save, c;
+  unsigned i, wb[4] = {0, 0, 0, 0};
+  short morder, sorder = order;
+
+  uchar *CanonCameraInfo;
+  unsigned lenCanonCameraInfo = 0;
+  unsigned typeCanonCameraInfo = 0;
+  imCanon.wbi = 0;
+
+  uchar *table_buf_0x0116;
+  ushort table_buf_0x0116_len = 0;
+  uchar *table_buf_0x2010;
+  ushort table_buf_0x2010_len = 0;
+  uchar *table_buf_0x9050;
+  ushort table_buf_0x9050_len = 0;
+  uchar *table_buf_0x9400;
+  ushort table_buf_0x9400_len = 0;
+  uchar *table_buf_0x9402;
+  ushort table_buf_0x9402_len = 0;
+  uchar *table_buf_0x9403;
+  ushort table_buf_0x9403_len = 0;
+  uchar *table_buf_0x9406;
+  ushort table_buf_0x9406_len = 0;
+  uchar *table_buf_0x940c;
+  ushort table_buf_0x940c_len = 0;
+  uchar *table_buf_0x940e;
+  ushort table_buf_0x940e_len = 0;
+
+  INT64 fsize = ifp->size();
+
+  /*
+       The MakerNote might have its own TIFF header (possibly with
+       its own byte-order!), or it might just be a table.
+  */
+
+  if (!strncmp(buf, "KC", 2) || /* Konica KD-400Z, KD-510Z */
+      !strncmp(buf, "MLY", 3))  /* Minolta DiMAGE G series */
+  {
+    order = 0x4d4d;
+    while ((i = ftell(ifp)) < data_offset && i < 16384)
+    {
+      wb[0] = wb[2];
+      wb[2] = wb[1];
+      wb[1] = wb[3];
+      wb[3] = get2();
+      if (wb[1] == 256 && wb[3] == 256 && wb[0] > 256 && wb[0] < 640 &&
+          wb[2] > 256 && wb[2] < 640)
+        FORC4 cam_mul[c] = wb[c];
+    }
+    goto quit;
+  }
+
+  if (!strcmp(buf, "OLYMPUS") ||
+      !strcmp(buf, "PENTAX "))
+  {
+    base = ftell(ifp) - 10;
+    fseek(ifp, -2, SEEK_CUR);
+    order = get2();
+    if (buf[0] == 'O')
+      get2();
+  }
+  else if (!strncmp(buf, "SONY", 4) || // DSLR-A100
+           !strcmp(buf, "Panasonic")) {
+    if (buf[0] == 'S')
+      is_Sony = 1;
+    goto nf;
+  }
+  else if (!strncmp(buf, "FUJIFILM", 8))
+  {
+    base = ftell(ifp) - 10;
+  nf:
+    order = 0x4949;
+    fseek(ifp, 2, SEEK_CUR);
+  }
+  else if (!strcmp (buf, "OLYMP")    ||
+           !strncmp(buf, "LEICA", 5) ||
+           !strcmp (buf, "Ricoh"))
+  {
+    fseek(ifp, -2, SEEK_CUR);
+  }
+  else if (!strcmp(buf, "AOC") || // Pentax, tribute to Asahi Optical Co.
+           !strcmp(buf, "QVC"))   // Casio, from "QV-Camera"
+  {
+    fseek(ifp, -4, SEEK_CUR);
+  }
+  else if (!strncmp(buf, "CMT3", 4))
+  {
+    order = sget2((uchar *)(buf + 4));
+    fseek(ifp, 2L, SEEK_CUR);
+  }
+  else if (libraw_internal_data.unpacker_data.CR3_CTMDtag)
+  {
+    order = sget2((uchar *)buf);
+    fseek(ifp, -2L, SEEK_CUR);
+  }
+  else
+  {
+    fseek(ifp, -10, SEEK_CUR);
+    if (!strncmp(make, "SAMSUNG", 7))
+      base = ftell(ifp);
+  }
+
+  if (!is_Sony &&
+      (!strncasecmp(make, "SONY", 4) ||
+       !strncasecmp(make, "Konica", 6) ||
+       !strncasecmp(make, "Minolta", 7) ||
+       (!strncasecmp(make, "Hasselblad", 10) &&
+        (!strncasecmp(model, "Stellar", 7) ||
+         !strncasecmp(model, "Lunar", 5) ||
+         !strncasecmp(model, "Lusso", 5) ||
+         !strncasecmp(model, "HV", 2))))) {
+    is_Sony = 1;
+  }
+
+  if (strcasestr(make, "Kodak") &&
+      (sget2((uchar *)buf) > 1) && // check number of entries
+      (sget2((uchar *)buf) < 128) &&
+      (sget2((uchar *)(buf + 4)) > 0) && // check type
+      (sget2((uchar *)(buf + 4)) < 13) &&
+      (sget4((uchar *)(buf + 6)) < 256) // check count
+  )
+    imKodak.MakerNoteKodak8a = 1; // Kodak P712 / P850 / P880
+
+  entries = get2();
+  if (entries > 1000)
+    return;
+
+  morder = order;
+  while (entries--)
+  {
+    order = morder;
+    tiff_get(base, &tag, &type, &len, &save);
+    tag |= uptag << 16;
+
+    INT64 _pos = ftell(ifp);
+    INT64 _pos2;
+    if (len > 100 * 1024 * 1024)
+	goto next; // 100Mb tag? No!
+    if (len > 8 && _pos + len > 2 * fsize)
+    {
+      fseek(ifp, save, SEEK_SET); // Recover tiff-read position!!
+      continue;
+    }
+    if (imKodak.MakerNoteKodak8a)
+    {
+      if ((tag == 0xff00) && tagtypeIs(LIBRAW_EXIFTAG_TYPE_LONG) && (len == 1))
+      {
+        INT64 _pos1 = get4();
+        if ((_pos1 < fsize) && (_pos1 > 0))
+        {
+          fseek(ifp, _pos1, SEEK_SET);
+          parse_makernote(base, tag);
+        }
+      }
+      else if (tag == 0xff00f90b)
+      {
+        imKodak.clipBlack = get2();
+      }
+      else if (tag == 0xff00f90c)
+      {
+        imKodak.clipWhite = imgdata.color.linear_max[0] =
+            imgdata.color.linear_max[1] = imgdata.color.linear_max[2] =
+                imgdata.color.linear_max[3] = get2();
+      }
+    }
+    else if (!strncmp(make, "Canon", 5))
+    {
+      if (tag == 0x000d && len < 256000) // camera info
+      {
+        if (!tagtypeIs(LIBRAW_EXIFTAG_TYPE_LONG))
+        {
+          CanonCameraInfo = (uchar *)malloc(MAX(16, len));
+          fread(CanonCameraInfo, len, 1, ifp);
+        }
+        else
+        {
+          CanonCameraInfo = (uchar *)malloc(MAX(16, len * 4));
+          fread(CanonCameraInfo, len, 4, ifp);
+        }
+        lenCanonCameraInfo = len;
+        typeCanonCameraInfo = type;
+      }
+
+      else if (tag == 0x0010) // Canon ModelID
+      {
+        unique_id = get4();
+        setCanonBodyFeatures(unique_id);
+        if (lenCanonCameraInfo)
+        {
+          processCanonCameraInfo(unique_id, CanonCameraInfo, lenCanonCameraInfo,
+                                 typeCanonCameraInfo, nonDNG);
+          free(CanonCameraInfo);
+          CanonCameraInfo = 0;
+          lenCanonCameraInfo = 0;
+        }
+      }
+
+      else
+        parseCanonMakernotes(tag, type, len, nonDNG);
+    }
+
+    else if (!strncmp(make, "FUJI", 4))
+      parseFujiMakernotes(tag, type, len, nonDNG);
+
+    else if (!strncasecmp(model, "Hasselblad X1D", 14) ||
+             !strncasecmp(model, "Hasselblad H6D", 14) ||
+             !strncasecmp(model, "Hasselblad A6D", 14))
+    {
+      if (tag == 0x0045)
+      {
+        imHassy.BaseISO = get4();
+      }
+      else if (tag == 0x0046)
+      {
+        imHassy.Gain = getreal(type);
+      }
+    }
+
+    else if (!strncmp(make, "PENTAX", 6) ||
+             !strncmp(make, "RICOH", 5) ||
+             !strncmp(model, "PENTAX", 6))
+    {
+      if (!strncmp(model, "GR", 2) ||
+          !strncmp(model, "GXR", 3))
+      {
+        parseRicohMakernotes(base, tag, type, len, CameraDNG);
+      }
+      else
+      {
+        parsePentaxMakernotes(base, tag, type, len, nonDNG);
+      }
+    }
+
+    else if (!strncmp(make, "SAMSUNG", 7))
+    {
+      if (!dng_version)
+        parseSamsungMakernotes(base, tag, type, len, nonDNG);
+      else
+        parsePentaxMakernotes(base, tag, type, len, CameraDNG);
+    }
+
+    else if (is_Sony)
+    {
+      if ((tag == 0xb028) && (len == 1) && tagtypeIs(LIBRAW_EXIFTAG_TYPE_LONG))
+      { // DSLR-A100
+        if ((c = get4()))
+        {
+          fseek(ifp, c, SEEK_SET);
+          parse_makernote(base, tag);
+        }
+      }
+      else
+      {
+        parseSonyMakernotes(
+            base, tag, type, len, nonDNG, table_buf_0x0116,
+            table_buf_0x0116_len, table_buf_0x2010, table_buf_0x2010_len,
+            table_buf_0x9050, table_buf_0x9050_len, table_buf_0x9400,
+            table_buf_0x9400_len, table_buf_0x9402, table_buf_0x9402_len,
+            table_buf_0x9403, table_buf_0x9403_len, table_buf_0x9406,
+            table_buf_0x9406_len, table_buf_0x940c, table_buf_0x940c_len,
+            table_buf_0x940e, table_buf_0x940e_len);
+      }
+    }
+    fseek(ifp, _pos, SEEK_SET);
+
+    if (!strncasecmp(make, "Hasselblad", 10) && !is_Sony) {
+      if (tag == 0x0011)
+        imHassy.SensorCode = getint(type);
+      else if (tag == 0x0016)
+        imHassy.CoatingCode = getint(type);
+      else if ((tag == 0x002a) &&
+               tagtypeIs(LIBRAW_EXIFTAG_TYPE_SRATIONAL) &&
+               (len == 12)) {
+        FORC4 for (int i = 0; i < 3; i++)
+                imHassy.mnColorMatrix[c][i] = getreal(type);
+
+      } else if (tag == 0x0031) {
+        imHassy.RecommendedCrop[0] = getint(type);
+        imHassy.RecommendedCrop[1] = getint(type);
+      }
+    }
+
+    if ((tag == 0x0004 || tag == 0x0114) && !strncmp(make, "KONICA", 6))
+    {
+      fseek(ifp, tag == 0x0004 ? 140 : 160, SEEK_CUR);
+      switch (get2())
+      {
+      case 72:
+        flip = 0;
+        break;
+      case 76:
+        flip = 6;
+        break;
+      case 82:
+        flip = 5;
+        break;
+      }
+    }
+
+    _pos2 = ftell(ifp);
+    if (!strncasecmp(make, "Olympus", 7) ||
+        (!strncasecmp(make, "CLAUSS", 6) && !strncasecmp(model, "piX 5oo", 7)))
+    {
+      if ((tag == 0x2010) || (tag == 0x2020) || (tag == 0x2030) ||
+          (tag == 0x2031) || (tag == 0x2040) || (tag == 0x2050) ||
+          (tag == 0x3000))
+      {
+        if (tagtypeIs(LIBRAW_EXIFTOOLTAGTYPE_binary))
+        {
+          parse_makernote(base, tag);
+        }
+        else if (tagtypeIs(LIBRAW_EXIFTAG_TYPE_IFD))
+        {
+          fseek(ifp, base + get4(), SEEK_SET);
+          parse_makernote(base, tag);
+        }
+      }
+      else if (tag == 0x0207)
+      {
+        getOlympus_CameraType2();
+      }
+      else if ((tag == 0x0404) || (tag == 0x101a))
+      {
+        if (!imgdata.shootinginfo.BodySerial[0])
+          stmread(imgdata.shootinginfo.BodySerial, len, ifp);
+      }
+      else if (tag == 0x1002)
+      {
+        ilm.CurAp = libraw_powf64l(2.0f, getreal(type) / 2);
+      }
+      else if (tag == 0x1007)
+      {
+        imCommon.SensorTemperature = (float)get2();
+      }
+      else if (tag == 0x1008)
+      {
+        imCommon.LensTemperature = (float)get2();
+      }
+      else if ((tag == 0x1011) && strcmp(software, "v757-71"))
+      {
+        for (i = 0; i < 3; i++)
+        {
+          if (!imOly.ColorSpace)
+          {
+            FORC3 cmatrix[i][c] = ((short)get2()) / 256.0;
+          }
+          else
+          {
+            FORC3 imgdata.color.ccm[i][c] = ((short)get2()) / 256.0;
+          }
+        }
+      }
+      else if (tag == 0x1012)
+      {
+        FORC4 cblack[RGGB_2_RGBG(c)] = get2();
+      }
+      else if (tag == 0x1017)
+      {
+        cam_mul[0] = get2() / 256.0;
+      }
+      else if (tag == 0x1018)
+      {
+        cam_mul[2] = get2() / 256.0;
+      }
+      else if ((tag >= 0x20100000) && (tag <= 0x2010ffff))
+      {
+        parseOlympus_Equipment((tag & 0x0000ffff), type, len, nonDNG);
+      }
+      else if ((tag >= 0x20200000) && (tag <= 0x2020ffff))
+      {
+        parseOlympus_CameraSettings(base, (tag & 0x0000ffff), type, len,
+                                    nonDNG);
+      }
+      else if ((tag == 0x20300108) || (tag == 0x20310109))
+      {
+        imOly.ColorSpace = get2();
+        switch (imOly.ColorSpace) {
+        case 0:
+          imCommon.ColorSpace = LIBRAW_COLORSPACE_sRGB;
+          break;
+        case 1:
+          imCommon.ColorSpace = LIBRAW_COLORSPACE_AdobeRGB;
+          break;
+        case 2:
+          imCommon.ColorSpace = LIBRAW_COLORSPACE_ProPhotoRGB;
+          break;
+        default:
+          imCommon.ColorSpace = LIBRAW_COLORSPACE_Unknown;
+          break;
+        }
+      }
+      else if ((tag >= 0x20400000) && (tag <= 0x2040ffff))
+      {
+        parseOlympus_ImageProcessing((tag & 0x0000ffff), type, len, nonDNG);
+      }
+      else if (tag == 0x20501500)
+      {
+        getOlympus_SensorTemperature(len);
+      }
+      else if ((tag >= 0x30000000) && (tag <= 0x3000ffff))
+      {
+        parseOlympus_RawInfo((tag & 0x0000ffff), type, len, nonDNG);
+      }
+    }
+    fseek(ifp, _pos2, SEEK_SET);
+
+    if ((tag == 0x0015) &&
+        tagtypeIs(LIBRAW_EXIFTAG_TYPE_ASCII) &&
+        is_raw)
+    { // Hasselblad
+      stmread (imHassy.SensorUnitConnector, len, ifp);
+    }
+
+    if (tagtypeIs(LIBRAW_EXIFTAG_TYPE_UNDEFINED) &&
+        ((tag == 0x0081) || // Minolta
+         (tag == 0x0100)))  // Olympus
+    {
+      thumb_offset = ftell(ifp);
+      thumb_length = len;
+    }
+    if ((tag == 0x0088) && // Minolta, possibly Olympus too
+        tagtypeIs(LIBRAW_EXIFTAG_TYPE_LONG) &&
+        (thumb_offset = get4()))
+      thumb_offset += base;
+
+    if ((tag == 0x0089) && // Minolta, possibly Olympus too
+        tagtypeIs(LIBRAW_EXIFTAG_TYPE_LONG))
+      thumb_length = get4();
+
+    if (tagtypeIs(LIBRAW_EXIFTAG_TYPE_UNDEFINED) &&  // Nikon
+        ((tag == 0x008c) ||
+         (tag == 0x0096))) {
+      meta_offset = ftell(ifp);
+    }
+
+    if ((tag == 0x00a1) &&
+        tagtypeIs(LIBRAW_EXIFTAG_TYPE_UNDEFINED) &&
+        strncasecmp(make, "Samsung", 7))
+    {
+      order = 0x4949;
+      fseek(ifp, 140, SEEK_CUR);
+      FORC3 cam_mul[c] = get4();
+    }
+
+    if (tag == 0xb001 && tagtypeIs(LIBRAW_EXIFTAG_TYPE_SHORT)) // Sony ModelID
+    {
+      unique_id = get2();
+    }
+    if (tag == 0x0200 && len == 3) // Olympus
+      shot_order = (get4(), get4());
+
+    if (tag == 0x0f00 && tagtypeIs(LIBRAW_EXIFTAG_TYPE_UNDEFINED))
+    {
+      if (len == 614)
+        fseek(ifp, 176, SEEK_CUR);
+      else if (len == 734 || len == 1502) // Kodak, Minolta, Olympus
+        fseek(ifp, 148, SEEK_CUR);
+      else
+        goto next;
+      goto get2_256;
+    }
+
+    if (tag == 0x2011 && len == 2) // Casio
+    {
+    get2_256:
+      order = 0x4d4d;
+      cam_mul[0] = get2() / 256.0;
+      cam_mul[2] = get2() / 256.0;
+    }
+
+  next:
+    fseek(ifp, save, SEEK_SET);
+  }
+quit:
+  order = sorder;
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/metadata/mediumformat.cpp libkdcraw/libkdcraw/libraw/src/metadata/mediumformat.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/metadata/mediumformat.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/metadata/mediumformat.cpp	2022-11-07 07:46:31.734795008 +0300
@@ -0,0 +1,501 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+void LibRaw::parse_phase_one(int base)
+{
+  unsigned entries, tag, type, len, data, save, i, c;
+  float romm_cam[3][3];
+  char *cp;
+
+  memset(&ph1, 0, sizeof ph1);
+  fseek(ifp, base, SEEK_SET);
+  order = get4() & 0xffff;
+  if (get4() >> 8 != 0x526177)
+    return; /* "Raw" */
+  unsigned offset = get4();
+  if (offset == 0xbad0bad)
+    return;
+  fseek(ifp, offset + base, SEEK_SET);
+  entries = get4();
+  if (entries > 8192)
+    return; // too much??
+  get4();
+  while (entries--)
+  {
+    tag = get4();
+    type = get4();
+    len = get4();
+    data = get4();
+    save = ftell(ifp);
+    fseek(ifp, base + data, SEEK_SET);
+    switch (tag)
+    {
+
+    case 0x0102:
+      stmread(imgdata.shootinginfo.BodySerial, len, ifp);
+      if ((imgdata.shootinginfo.BodySerial[0] == 0x4c) &&
+          (imgdata.shootinginfo.BodySerial[1] == 0x49))
+      {
+        unique_id = (((imgdata.shootinginfo.BodySerial[0] & 0x3f) << 5) |
+                     (imgdata.shootinginfo.BodySerial[2] & 0x3f)) -
+                    0x41;
+      }
+      else
+      {
+        unique_id = (((imgdata.shootinginfo.BodySerial[0] & 0x3f) << 5) |
+                     (imgdata.shootinginfo.BodySerial[1] & 0x3f)) -
+                    0x41;
+      }
+      setPhaseOneFeatures(unique_id);
+      break;
+    case 0x0203:
+      stmread(imgdata.makernotes.phaseone.Software, len, ifp);
+    case 0x0204:
+      stmread(imgdata.makernotes.phaseone.SystemType, len, ifp);
+    case 0x0211:
+      imCommon.SensorTemperature2 = int_to_float(data);
+      break;
+    case 0x0401:
+      if (tagtypeIs(LIBRAW_EXIFTAG_TYPE_LONG))
+        ilm.CurAp = libraw_powf64l(2.0f, (int_to_float(data) / 2.0f));
+      else
+        ilm.CurAp = libraw_powf64l(2.0f, (getreal(type) / 2.0f));
+      break;
+    case 0x0403:
+      if (tagtypeIs(LIBRAW_EXIFTAG_TYPE_LONG))
+        ilm.CurFocal = int_to_float(data);
+      else
+        ilm.CurFocal = getreal(type);
+      break;
+    case 0x0410:
+      stmread(ilm.body, len, ifp);
+      if (((unsigned char)ilm.body[0]) == 0xff)
+        ilm.body[0] = 0;
+      break;
+    case 0x0412:
+      stmread(ilm.Lens, len, ifp);
+      if (((unsigned char)ilm.Lens[0]) == 0xff)
+        ilm.Lens[0] = 0;
+      break;
+    case 0x0414:
+      if (tagtypeIs(LIBRAW_EXIFTAG_TYPE_LONG))
+      {
+        ilm.MaxAp4CurFocal = libraw_powf64l(2.0f, (int_to_float(data) / 2.0f));
+      }
+      else
+      {
+        ilm.MaxAp4CurFocal = libraw_powf64l(2.0f, (getreal(type) / 2.0f));
+      }
+      break;
+    case 0x0415:
+      if (tagtypeIs(LIBRAW_EXIFTAG_TYPE_LONG))
+      {
+        ilm.MinAp4CurFocal = libraw_powf64l(2.0f, (int_to_float(data) / 2.0f));
+      }
+      else
+      {
+        ilm.MinAp4CurFocal = libraw_powf64l(2.0f, (getreal(type) / 2.0f));
+      }
+      break;
+    case 0x0416:
+      if (tagtypeIs(LIBRAW_EXIFTAG_TYPE_LONG))
+      {
+        ilm.MinFocal = int_to_float(data);
+      }
+      else
+      {
+        ilm.MinFocal = getreal(type);
+      }
+      if (ilm.MinFocal > 1000.0f)
+      {
+        ilm.MinFocal = 0.0f;
+      }
+      break;
+    case 0x0417:
+      if (tagtypeIs(LIBRAW_EXIFTAG_TYPE_LONG))
+      {
+        ilm.MaxFocal = int_to_float(data);
+      }
+      else
+      {
+        ilm.MaxFocal = getreal(type);
+      }
+      break;
+
+    case 0x0100:
+      flip = "0653"[data & 3] - '0';
+      break;
+    case 0x0106:
+      for (i = 0; i < 9; i++)
+        imgdata.color.P1_color[0].romm_cam[i] = ((float *)romm_cam)[i] =
+            getreal(LIBRAW_EXIFTAG_TYPE_FLOAT);
+      romm_coeff(romm_cam);
+      break;
+    case 0x0107:
+      FORC3 cam_mul[c] = getreal(LIBRAW_EXIFTAG_TYPE_FLOAT);
+      break;
+    case 0x0108:
+      raw_width = data;
+      break;
+    case 0x0109:
+      raw_height = data;
+      break;
+    case 0x010a:
+      left_margin = data;
+      break;
+    case 0x010b:
+      top_margin = data;
+      break;
+    case 0x010c:
+      width = data;
+      break;
+    case 0x010d:
+      height = data;
+      break;
+    case 0x010e:
+      ph1.format = data;
+      break;
+    case 0x010f:
+      data_offset = data + base;
+      break;
+    case 0x0110:
+      meta_offset = data + base;
+      meta_length = len;
+      break;
+    case 0x0112:
+      ph1.key_off = save - 4;
+      break;
+    case 0x0210:
+      ph1.tag_210 = int_to_float(data);
+      imCommon.SensorTemperature = ph1.tag_210;
+      break;
+    case 0x021a:
+      ph1.tag_21a = data;
+      break;
+    case 0x021c:
+      strip_offset = data + base;
+      break;
+    case 0x021d:
+      ph1.t_black = data;
+      break;
+    case 0x0222:
+      ph1.split_col = data;
+      break;
+    case 0x0223:
+      ph1.black_col = data + base;
+      break;
+    case 0x0224:
+      ph1.split_row = data;
+      break;
+    case 0x0225:
+      ph1.black_row = data + base;
+      break;
+    case 0x0226:
+      for (i = 0; i < 9; i++)
+        imgdata.color.P1_color[1].romm_cam[i] = getreal(LIBRAW_EXIFTAG_TYPE_FLOAT);
+      break;
+    case 0x0301:
+      model[63] = 0;
+      imgdata.makernotes.phaseone.FirmwareString[255] = 0;
+      fread(imgdata.makernotes.phaseone.FirmwareString, 1, 255, ifp);
+      memcpy(model, imgdata.makernotes.phaseone.FirmwareString, 63);
+      if ((cp = strstr(model, " camera")))
+        *cp = 0;
+      else if ((cp = strchr(model, ',')))
+        *cp = 0;
+      /* minus and the letter after it are not always present
+        if present, last letter means:
+          C : Contax 645AF
+          H : Hasselblad H1 / H2
+          M : Mamiya
+          V : Hasselblad 555ELD / 553ELX / 503CW / 501CM; not included below
+        because of adapter conflicts (Mamiya RZ body) if not present, Phase One
+        645 AF, Mamiya 645AFD Series, or anything
+       */
+      strcpy(imgdata.makernotes.phaseone.SystemModel, model);
+      if ((cp = strchr(model, '-')))
+      {
+        if (cp[1] == 'C')
+        {
+          strcpy(ilm.body, "Contax 645AF");
+          ilm.CameraMount = LIBRAW_MOUNT_Contax645;
+          ilm.CameraFormat = LIBRAW_FORMAT_645;
+        }
+        else if (cp[1] == 'M')
+        {
+          strcpy(ilm.body, "Mamiya 645");
+          ilm.CameraMount = LIBRAW_MOUNT_Mamiya645;
+          ilm.CameraFormat = LIBRAW_FORMAT_645;
+        }
+        else if (cp[1] == 'H')
+        {
+          strcpy(ilm.body, "Hasselblad H1/H2");
+          ilm.CameraMount = LIBRAW_MOUNT_Hasselblad_H;
+          ilm.CameraFormat = LIBRAW_FORMAT_645;
+        }
+        *cp = 0;
+      }
+    }
+    fseek(ifp, save, SEEK_SET);
+  }
+
+  if (!ilm.body[0] && !imgdata.shootinginfo.BodySerial[0])
+  {
+    fseek(ifp, meta_offset, SEEK_SET);
+    order = get2();
+    fseek(ifp, 6, SEEK_CUR);
+    fseek(ifp, meta_offset + get4(), SEEK_SET);
+    entries = get4();
+    get4();
+    while (entries--)
+    {
+      tag = get4();
+      len = get4();
+      data = get4();
+      save = ftell(ifp);
+      fseek(ifp, meta_offset + data, SEEK_SET);
+      if (tag == 0x0407)
+      {
+        stmread(imgdata.shootinginfo.BodySerial, len, ifp);
+        if ((imgdata.shootinginfo.BodySerial[0] == 0x4c) &&
+            (imgdata.shootinginfo.BodySerial[1] == 0x49))
+        {
+          unique_id = (((imgdata.shootinginfo.BodySerial[0] & 0x3f) << 5) |
+                       (imgdata.shootinginfo.BodySerial[2] & 0x3f)) -
+                      0x41;
+        }
+        else
+        {
+          unique_id = (((imgdata.shootinginfo.BodySerial[0] & 0x3f) << 5) |
+                       (imgdata.shootinginfo.BodySerial[1] & 0x3f)) -
+                      0x41;
+        }
+        setPhaseOneFeatures(unique_id);
+      }
+      fseek(ifp, save, SEEK_SET);
+    }
+  }
+  load_raw = ph1.format < 3 ? &LibRaw::phase_one_load_raw
+                            : &LibRaw::phase_one_load_raw_c;
+  maximum = 0xffff;
+  strcpy(make, "Phase One");
+  if (model[0])
+    return;
+  switch (raw_height)
+  {
+  case 2060:
+    strcpy(model, "LightPhase");
+    break;
+  case 2682:
+    strcpy(model, "H 10");
+    break;
+  case 4128:
+    strcpy(model, "H 20");
+    break;
+  case 5488:
+    strcpy(model, "H 25");
+    break;
+  }
+}
+
+void LibRaw::parse_mos(int offset)
+{
+  char data[40];
+  int from, i, c, neut[4], planes = 0, frot = 0;
+  unsigned skip;
+  static const char *mod[] = {
+      /* DM22, DM28, DM40, DM56 are somewhere here too */
+      "",             //  0
+      "DCB2",         //  1
+      "Volare",       //  2
+      "Cantare",      //  3
+      "CMost",        //  4
+      "Valeo 6",      //  5
+      "Valeo 11",     //  6
+      "Valeo 22",     //  7
+      "Valeo 11p",    //  8
+      "Valeo 17",     //  9
+      "",             // 10
+      "Aptus 17",     // 11
+      "Aptus 22",     // 12
+      "Aptus 75",     // 13
+      "Aptus 65",     // 14
+      "Aptus 54S",    // 15
+      "Aptus 65S",    // 16
+      "Aptus 75S",    // 17
+      "AFi 5",        // 18
+      "AFi 6",        // 19
+      "AFi 7",        // 20
+      "AFi-II 7",     // 21
+      "Aptus-II 7",   // 22 (same CMs as Mamiya DM33)
+      "",             // 23
+      "Aptus-II 6",   // 24 (same CMs as Mamiya DM28)
+      "AFi-II 10",    // 25
+      "",             // 26
+      "Aptus-II 10",  // 27 (same CMs as Mamiya DM56)
+      "Aptus-II 5",   // 28 (same CMs as Mamiya DM22)
+      "",             // 29
+      "DM33",         // 30, make is Mamiya
+      "",             // 31
+      "",             // 32
+      "Aptus-II 10R", // 33
+      "Aptus-II 8",   // 34 (same CMs as Mamiya DM40)
+      "",             // 35
+      "Aptus-II 12",  // 36
+      "",             // 37
+      "AFi-II 12"     // 38
+  };
+  float romm_cam[3][3];
+
+  fseek(ifp, offset, SEEK_SET);
+  while (!feof(ifp))
+  {
+    if (get4() != 0x504b5453)
+      break;
+    get4();
+    fread(data, 1, 40, ifp);
+    skip = get4();
+    from = ftell(ifp);
+
+    if (!strcmp(data, "CameraObj_camera_type"))
+    {
+      stmread(ilm.body, (unsigned)skip, ifp);
+      if (ilm.body[0])
+      {
+        if (!strncmp(ilm.body, "Mamiya R", 8))
+        {
+          ilm.CameraMount = LIBRAW_MOUNT_Mamiya67;
+          ilm.CameraFormat = LIBRAW_FORMAT_67;
+        }
+        else if (!strncmp(ilm.body, "Hasselblad 5", 12))
+        {
+          ilm.CameraFormat = LIBRAW_FORMAT_66;
+          ilm.CameraMount = LIBRAW_MOUNT_Hasselblad_V;
+        }
+        else if (!strncmp(ilm.body, "Hasselblad H", 12))
+        {
+          ilm.CameraMount = LIBRAW_MOUNT_Hasselblad_H;
+          ilm.CameraFormat = LIBRAW_FORMAT_645;
+        }
+        else if (!strncmp(ilm.body, "Mamiya 6", 8) ||
+                 !strncmp(ilm.body, "Phase One 6", 11))
+        {
+          ilm.CameraMount = LIBRAW_MOUNT_Mamiya645;
+          ilm.CameraFormat = LIBRAW_FORMAT_645;
+        }
+        else if (!strncmp(ilm.body, "Large F", 7))
+        {
+          ilm.CameraMount = LIBRAW_MOUNT_LF;
+          ilm.CameraFormat = LIBRAW_FORMAT_LF;
+        }
+        else if (!strncmp(model, "Leaf AFi", 8))
+        {
+          ilm.CameraMount = LIBRAW_MOUNT_Rollei_bayonet;
+          ilm.CameraFormat = LIBRAW_FORMAT_66;
+        }
+      }
+    }
+    if (!strcmp(data, "back_serial_number"))
+    {
+      char buffer[sizeof(imgdata.shootinginfo.BodySerial)];
+      char *words[4];
+      stmread(buffer, (unsigned)skip, ifp);
+      /*nwords = */
+          getwords(buffer, words, 4, sizeof(imgdata.shootinginfo.BodySerial));
+      strcpy(imgdata.shootinginfo.BodySerial, words[0]);
+    }
+    if (!strcmp(data, "CaptProf_serial_number"))
+    {
+      char buffer[sizeof(imgdata.shootinginfo.InternalBodySerial)];
+      char *words[4];
+      stmread(buffer, (unsigned)skip, ifp);
+      /*nwords =*/ getwords(buffer, words, 4,
+                        sizeof(imgdata.shootinginfo.InternalBodySerial));
+      strcpy(imgdata.shootinginfo.InternalBodySerial, words[0]);
+    }
+
+    if (!strcmp(data, "JPEG_preview_data"))
+    {
+      thumb_offset = from;
+      thumb_length = skip;
+    }
+    if (!strcmp(data, "icc_camera_profile"))
+    {
+      profile_offset = from;
+      profile_length = skip;
+    }
+    if (!strcmp(data, "ShootObj_back_type"))
+    {
+      fscanf(ifp, "%d", &i);
+      if ((unsigned)i < sizeof mod / sizeof(*mod))
+      {
+        strcpy(model, mod[i]);
+        if (!strncmp(model, "AFi", 3))
+        {
+          ilm.CameraMount = LIBRAW_MOUNT_Rollei_bayonet;
+          ilm.CameraFormat = LIBRAW_FORMAT_66;
+        }
+        ilm.CamID = i;
+      }
+    }
+    if (!strcmp(data, "icc_camera_to_tone_matrix"))
+    {
+      for (i = 0; i < 9; i++)
+        ((float *)romm_cam)[i] = int_to_float(get4());
+      romm_coeff(romm_cam);
+    }
+    if (!strcmp(data, "CaptProf_color_matrix"))
+    {
+      for (i = 0; i < 9; i++)
+        fscanf(ifp, "%f", (float *)romm_cam + i);
+      romm_coeff(romm_cam);
+    }
+    if (!strcmp(data, "CaptProf_number_of_planes"))
+      fscanf(ifp, "%d", &planes);
+    if (!strcmp(data, "CaptProf_raw_data_rotation"))
+      fscanf(ifp, "%d", &flip);
+    if (!strcmp(data, "CaptProf_mosaic_pattern"))
+      FORC4
+      {
+        fscanf(ifp, "%d", &i);
+        if (i == 1)
+          frot = c ^ (c >> 1); // 0123 -> 0132
+      }
+    if (!strcmp(data, "ImgProf_rotation_angle"))
+    {
+      fscanf(ifp, "%d", &i);
+      flip = i - flip;
+    }
+    if (!strcmp(data, "NeutObj_neutrals") && !cam_mul[0])
+    {
+      FORC4 fscanf(ifp, "%d", neut + c);
+      FORC3
+      if (neut[c + 1])
+        cam_mul[c] = (float)neut[0] / neut[c + 1];
+    }
+    if (!strcmp(data, "Rows_data"))
+      load_flags = get4();
+    parse_mos(from);
+    fseek(ifp, skip + from, SEEK_SET);
+  }
+  if (planes)
+    filters = (planes == 1) * 0x01010101U *
+              (uchar) "\x94\x61\x16\x49"[(flip / 90 + frot) & 3];
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/metadata/minolta.cpp libkdcraw/libkdcraw/libraw/src/metadata/minolta.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/metadata/minolta.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/metadata/minolta.cpp	2022-11-07 07:46:31.734795008 +0300
@@ -0,0 +1,112 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+void LibRaw::parse_minolta(int base)
+{
+  int tag, len, offset, high = 0, wide = 0, i, c;
+  short sorder = order;
+  INT64 save;
+
+  fseek(ifp, base, SEEK_SET);
+  if (fgetc(ifp) || fgetc(ifp) - 'M' || fgetc(ifp) - 'R')
+    return;
+  order = fgetc(ifp) * 0x101;
+  offset = base + get4() + 8;
+  INT64 fsize = ifp->size();
+  if (offset > fsize - 8) // At least 8 bytes for tag/len
+    offset = fsize - 8;
+
+  while ((save = ftell(ifp)) < offset)
+  {
+    for (tag = i = 0; i < 4; i++)
+      tag = tag << 8 | fgetc(ifp);
+    len = get4();
+    if (len < 0)
+      return; // just ignore wrong len?? or raise bad file exception?
+    if ((INT64)len + save + 8LL > fsize)
+      return; // just ignore out of file metadata, stop parse
+    switch (tag)
+    {
+    case 0x505244: /* PRD */
+      fseek(ifp, 8, SEEK_CUR);
+      high = get2();
+      wide = get2();
+      imSony.prd_ImageHeight = get2();
+      imSony.prd_ImageWidth = get2();
+      fseek(ifp, 1L, SEEK_CUR);
+      imSony.prd_RawBitDepth = (ushort)fgetc(ifp);
+      imSony.prd_StorageMethod = (ushort)fgetc(ifp);
+      fseek(ifp, 4L, SEEK_CUR);
+      imSony.prd_BayerPattern = (ushort)fgetc(ifp);
+      break;
+    case 0x524946: /* RIF */
+      if (!strncasecmp(model, "DSLR-A100", 9))
+      {
+        fseek(ifp, 8, SEEK_CUR);
+        icWBC[LIBRAW_WBI_Tungsten][0] = get2();
+        icWBC[LIBRAW_WBI_Tungsten][2] = get2();
+        icWBC[LIBRAW_WBI_Daylight][0] = get2();
+        icWBC[LIBRAW_WBI_Daylight][2] = get2();
+        icWBC[LIBRAW_WBI_Cloudy][0] = get2();
+        icWBC[LIBRAW_WBI_Cloudy][2] = get2();
+        icWBC[LIBRAW_WBI_FL_W][0] = get2();
+        icWBC[LIBRAW_WBI_FL_W][2] = get2();
+        icWBC[LIBRAW_WBI_Flash][0] = get2();
+        icWBC[LIBRAW_WBI_Flash][2] = get2();
+        get4();
+        icWBC[LIBRAW_WBI_Shade][0] = get2();
+        icWBC[LIBRAW_WBI_Shade][2] = get2();
+        icWBC[LIBRAW_WBI_FL_D][0] = get2();
+        icWBC[LIBRAW_WBI_FL_D][2] = get2();
+        icWBC[LIBRAW_WBI_FL_N][0] = get2();
+        icWBC[LIBRAW_WBI_FL_N][2] = get2();
+        icWBC[LIBRAW_WBI_FL_WW][0] = get2();
+        icWBC[LIBRAW_WBI_FL_WW][2] = get2();
+        icWBC[LIBRAW_WBI_Daylight][1] = icWBC[LIBRAW_WBI_Daylight][3] = icWBC
+            [LIBRAW_WBI_Tungsten]
+            [1] = icWBC[LIBRAW_WBI_Tungsten][3] = icWBC[LIBRAW_WBI_Flash][1] =
+                icWBC[LIBRAW_WBI_Flash][3] = icWBC[LIBRAW_WBI_Cloudy][1] =
+                    icWBC[LIBRAW_WBI_Cloudy][3] = icWBC[LIBRAW_WBI_Shade][1] =
+                        icWBC[LIBRAW_WBI_Shade][3] = icWBC[LIBRAW_WBI_FL_D][1] =
+                            icWBC[LIBRAW_WBI_FL_D][3] =
+                                icWBC[LIBRAW_WBI_FL_N][1] =
+                                    icWBC[LIBRAW_WBI_FL_N][3] =
+                                        icWBC[LIBRAW_WBI_FL_W][1] =
+                                            icWBC[LIBRAW_WBI_FL_W][3] =
+                                                icWBC[LIBRAW_WBI_FL_WW][1] =
+                                                    icWBC[LIBRAW_WBI_FL_WW][3] =
+                                                        0x100;
+      }
+      break;
+    case 0x574247: /* WBG */
+      get4();
+      i = strcmp(model, "DiMAGE A200") ? 0 : 3;
+      FORC4 cam_mul[c ^ (c >> 1) ^ i] = get2();
+      break;
+    case 0x545457: /* TTW */
+      parse_tiff(ftell(ifp));
+      data_offset = offset;
+    }
+    fseek(ifp, save + len + 8, SEEK_SET);
+  }
+  raw_height = high;
+  raw_width = wide;
+  order = sorder;
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/metadata/misc_parsers.cpp libkdcraw/libkdcraw/libraw/src/metadata/misc_parsers.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/metadata/misc_parsers.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/metadata/misc_parsers.cpp	2022-11-07 07:46:31.734795008 +0300
@@ -0,0 +1,680 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+/*
+   Returns 1 for a Coolpix 2100, 0 for anything else.
+ */
+int LibRaw::nikon_e2100()
+{
+  uchar 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;
+}
+
+void LibRaw::nikon_3700()
+{
+  int bits, i;
+  uchar dp[24];
+  static const struct
+  {
+    int bits;
+    char t_make[12], t_model[15];
+    int t_maker_idx;
+  } table[] = {{0x00, "Pentax", "Optio 33WR", LIBRAW_CAMERAMAKER_Pentax},
+               {0x03, "Nikon", "E3200", LIBRAW_CAMERAMAKER_Nikon},
+               {0x32, "Nikon", "E3700", LIBRAW_CAMERAMAKER_Nikon},
+               {0x33, "Olympus", "C-740UZ", LIBRAW_CAMERAMAKER_Olympus}};
+
+  fseek(ifp, 3072, SEEK_SET);
+  fread(dp, 1, 24, ifp);
+  bits = (dp[8] & 3) << 4 | (dp[20] & 3);
+  for (i = 0; i < int(sizeof table / sizeof *table); i++)
+    if (bits == table[i].bits)
+    {
+      strcpy(make, table[i].t_make);
+      maker_index = table[i].t_maker_idx;
+      strcpy(model, table[i].t_model);
+    }
+}
+
+/*
+   Separates a Minolta DiMAGE Z2 from a Nikon E4300.
+ */
+int LibRaw::minolta_z2()
+{
+  int i, nz;
+  char tail[424];
+
+  fseek(ifp, -sizeof tail, SEEK_END);
+  fread(tail, 1, sizeof tail, ifp);
+  for (nz = i = 0; i < int(sizeof tail); i++)
+    if (tail[i])
+      nz++;
+  return nz > 20;
+}
+
+int LibRaw::canon_s2is()
+{
+  unsigned row;
+
+  for (row = 0; row < 100; row++)
+  {
+    fseek(ifp, row * 3340 + 3284, SEEK_SET);
+    if (getc(ifp) > 15)
+      return 1;
+  }
+  return 0;
+}
+
+void LibRaw::parse_redcine()
+{
+  unsigned i, len, rdvo;
+
+  order = 0x4d4d;
+  is_raw = 0;
+  fseek(ifp, 52, SEEK_SET);
+  width = get4();
+  height = get4();
+  fseek(ifp, 0, SEEK_END);
+  fseek(ifp, -(i = ftello(ifp) & 511), SEEK_CUR);
+  if (get4() != i || get4() != 0x52454f42)
+  {
+    fseek(ifp, 0, SEEK_SET);
+    while ((len = get4()) != (unsigned)EOF)
+    {
+      if (get4() == 0x52454456)
+        if (is_raw++ == shot_select)
+          data_offset = ftello(ifp) - 8;
+      fseek(ifp, len - 8, SEEK_CUR);
+    }
+  }
+  else
+  {
+    rdvo = get4();
+    fseek(ifp, 12, SEEK_CUR);
+    is_raw = get4();
+    fseeko(ifp, rdvo + 8 + shot_select * 4, SEEK_SET);
+    data_offset = get4();
+  }
+}
+
+void LibRaw::parse_cine()
+{
+  unsigned off_head, off_setup, off_image, i, temp;
+
+  order = 0x4949;
+  fseek(ifp, 4, SEEK_SET);
+  is_raw = get2() == 2;
+  fseek(ifp, 14, SEEK_CUR);
+  is_raw *= get4();
+  off_head = get4();
+  off_setup = get4();
+  off_image = get4();
+  timestamp = get4();
+  if ((i = get4()))
+    timestamp = i;
+  fseek(ifp, off_head + 4, SEEK_SET);
+  raw_width = get4();
+  raw_height = get4();
+  switch (get2(), get2())
+  {
+  case 8:
+    load_raw = &LibRaw::eight_bit_load_raw;
+    break;
+  case 16:
+    load_raw = &LibRaw::unpacked_load_raw;
+  }
+  fseek(ifp, off_setup + 792, SEEK_SET);
+  strcpy(make, "CINE");
+  sprintf(model, "%d", get4());
+  fseek(ifp, 12, SEEK_CUR);
+  switch ((i = get4()) & 0xffffff)
+  {
+  case 3:
+    filters = 0x94949494;
+    break;
+  case 4:
+    filters = 0x49494949;
+    break;
+  default:
+    is_raw = 0;
+  }
+  fseek(ifp, 72, SEEK_CUR);
+  switch ((get4() + 3600) % 360)
+  {
+  case 270:
+    flip = 4;
+    break;
+  case 180:
+    flip = 1;
+    break;
+  case 90:
+    flip = 7;
+    break;
+  case 0:
+    flip = 2;
+  }
+  cam_mul[0] = getreal(LIBRAW_EXIFTAG_TYPE_FLOAT);
+  cam_mul[2] = getreal(LIBRAW_EXIFTAG_TYPE_FLOAT);
+  temp = get4();
+  maximum = ~((~0u) << LIM(temp, 1, 31));
+  fseek(ifp, 668, SEEK_CUR);
+  shutter = get4() / 1000000000.0;
+  fseek(ifp, off_image, SEEK_SET);
+  if (shot_select < is_raw)
+    fseek(ifp, shot_select * 8, SEEK_CUR);
+  data_offset = (INT64)get4() + 8;
+  data_offset += (INT64)get4() << 32;
+}
+
+void LibRaw::parse_qt(int end)
+{
+  unsigned save, size;
+  char tag[4];
+
+  order = 0x4d4d;
+  while (ftell(ifp) + 7 < end)
+  {
+    save = ftell(ifp);
+    if ((size = get4()) < 8)
+      return;
+    if ((int)size < 0)
+      return; // 2+GB is too much
+    if (save + size < save)
+      return; // 32bit overflow
+    fread(tag, 4, 1, ifp);
+    if (!memcmp(tag, "moov", 4) || !memcmp(tag, "udta", 4) ||
+        !memcmp(tag, "CNTH", 4))
+      parse_qt(save + size);
+    if (!memcmp(tag, "CNDA", 4))
+      parse_jpeg(ftell(ifp));
+    fseek(ifp, save + size, SEEK_SET);
+  }
+}
+
+void LibRaw::parse_smal(int offset, int fsize)
+{
+  int ver;
+
+  fseek(ifp, offset + 2, SEEK_SET);
+  order = 0x4949;
+  ver = fgetc(ifp);
+  if (ver == 6)
+    fseek(ifp, 5, SEEK_CUR);
+  if (get4() != (unsigned)fsize)
+    return;
+  if (ver > 6)
+    data_offset = get4();
+  raw_height = height = get2();
+  raw_width = width = get2();
+  strcpy(make, "SMaL");
+  sprintf(model, "v%d %dx%d", ver, width, height);
+  if (ver == 6)
+    load_raw = &LibRaw::smal_v6_load_raw;
+  if (ver == 9)
+    load_raw = &LibRaw::smal_v9_load_raw;
+}
+
+void LibRaw::parse_riff()
+{
+  unsigned i, size, end;
+  char tag[4], date[64], month[64];
+  static const char mon[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+                                  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+  struct tm t;
+
+  order = 0x4949;
+  fread(tag, 4, 1, ifp);
+  size = get4();
+  end = ftell(ifp) + size;
+  if (!memcmp(tag, "RIFF", 4) || !memcmp(tag, "LIST", 4))
+  {
+    int maxloop = 1000;
+    get4();
+    while (ftell(ifp) + 7 < end && !feof(ifp) && maxloop--)
+      parse_riff();
+  }
+  else if (!memcmp(tag, "nctg", 4))
+  {
+    while (ftell(ifp) + 7 < end)
+    {
+      i = get2();
+      size = get2();
+      if ((i + 1) >> 1 == 10 && size == 20)
+        get_timestamp(0);
+      else
+        fseek(ifp, size, SEEK_CUR);
+    }
+  }
+  else if (!memcmp(tag, "IDIT", 4) && size < 64)
+  {
+    fread(date, 64, 1, ifp);
+    date[size] = 0;
+    memset(&t, 0, sizeof t);
+    if (sscanf(date, "%*s %s %d %d:%d:%d %d", month, &t.tm_mday, &t.tm_hour,
+               &t.tm_min, &t.tm_sec, &t.tm_year) == 6)
+    {
+      for (i = 0; i < 12 && strcasecmp(mon[i], month); i++)
+        ;
+      t.tm_mon = i;
+      t.tm_year -= 1900;
+      if (mktime(&t) > 0)
+        timestamp = mktime(&t);
+    }
+  }
+  else
+    fseek(ifp, size, SEEK_CUR);
+}
+
+void LibRaw::parse_rollei()
+{
+  char line[128], *val;
+  struct tm t;
+
+  fseek(ifp, 0, SEEK_SET);
+  memset(&t, 0, sizeof t);
+  do
+  {
+    line[0] = 0;
+    if (!fgets(line, 128, ifp))
+      break;
+    if(!line[0]) break; // zero-length
+    if ((val = strchr(line, '=')))
+      *val++ = 0;
+    else
+      val = line + strbuflen(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"))
+      thumb_offset = atoi(val);
+    if (!strcmp(line, "X  "))
+      raw_width = atoi(val);
+    if (!strcmp(line, "Y  "))
+      raw_height = atoi(val);
+    if (!strcmp(line, "TX "))
+      thumb_width = atoi(val);
+    if (!strcmp(line, "TY "))
+      thumb_height = atoi(val);
+    if (!strcmp(line, "APT"))
+      aperture = atof(val);
+    if (!strcmp(line, "SPE"))
+      shutter = atof(val);
+    if (!strcmp(line, "FOCLEN"))
+      focal_len = atof(val);
+    if (!strcmp(line, "BLKOFS"))
+      black = atoi(val) +1;
+    if (!strcmp(line, "ORI"))
+      switch (atoi(val)) {
+      case 1:
+        flip = 6;
+        break;
+      case 2:
+        flip = 3;
+        break;
+      case 3:
+        flip = 5;
+        break;
+      }
+    if (!strcmp(line, "CUTRECT")) {
+      sscanf(val, "%hu %hu %hu %hu",
+             &imgdata.sizes.raw_inset_crop.cleft,
+             &imgdata.sizes.raw_inset_crop.ctop,
+             &imgdata.sizes.raw_inset_crop.cwidth,
+             &imgdata.sizes.raw_inset_crop.cheight);
+    }
+  } while (strncmp(line, "EOHD", 4));
+  data_offset = thumb_offset + thumb_width * thumb_height * 2;
+  t.tm_year -= 1900;
+  t.tm_mon -= 1;
+  if (mktime(&t) > 0)
+    timestamp = mktime(&t);
+  strcpy(make, "Rollei");
+  strcpy(model, "d530flex");
+  write_thumb = &LibRaw::rollei_thumb;
+}
+
+void LibRaw::parse_sinar_ia()
+{
+  int entries, off;
+  char str[8], *cp;
+
+  order = 0x4949;
+  fseek(ifp, 4, SEEK_SET);
+  entries = get4();
+  if (entries < 1 || entries > 8192)
+    return;
+  fseek(ifp, get4(), SEEK_SET);
+  while (entries--)
+  {
+    off = get4();
+    get4();
+    fread(str, 8, 1, ifp);
+    str[7] = 0; // Ensure end of string
+    if (!strcmp(str, "META"))
+      meta_offset = off;
+    if (!strcmp(str, "THUMB"))
+      thumb_offset = off;
+    if (!strcmp(str, "RAW0"))
+      data_offset = off;
+  }
+  fseek(ifp, meta_offset + 20, SEEK_SET);
+  fread(make, 64, 1, ifp);
+  make[63] = 0;
+  if ((cp = strchr(make, ' ')))
+  {
+    strcpy(model, cp + 1);
+    *cp = 0;
+  }
+  raw_width = get2();
+  raw_height = get2();
+  load_raw = &LibRaw::unpacked_load_raw;
+  thumb_width = (get4(), get2());
+  thumb_height = get2();
+  write_thumb = &LibRaw::ppm_thumb;
+  maximum = 0x3fff;
+}
+
+void LibRaw::parse_kyocera()
+{
+
+  int c;
+  static const ushort table[13] = {25,  32,  40,  50,  64,  80, 100,
+                                   125, 160, 200, 250, 320, 400};
+
+  fseek(ifp, 33, SEEK_SET);
+  get_timestamp(1);
+  fseek(ifp, 52, SEEK_SET);
+  c = get4();
+  if ((c > 6) && (c < 20))
+    iso_speed = table[c - 7];
+  shutter = libraw_powf64l(2.0f, (((float)get4()) / 8.0f)) / 16000.0f;
+  FORC4 cam_mul[RGGB_2_RGBG(c)] = get4();
+  fseek(ifp, 88, SEEK_SET);
+  aperture = libraw_powf64l(2.0f, ((float)get4()) / 16.0f);
+  fseek(ifp, 112, SEEK_SET);
+  focal_len = get4();
+
+  fseek(ifp, 104, SEEK_SET);
+  ilm.MaxAp4CurFocal = libraw_powf64l(2.0f, ((float)get4()) / 16.0f);
+  fseek(ifp, 124, SEEK_SET);
+  stmread(ilm.Lens, 32, ifp);
+  ilm.CameraMount = LIBRAW_MOUNT_Contax_N;
+  ilm.CameraFormat = LIBRAW_FORMAT_FF;
+  if (ilm.Lens[0])
+  {
+    ilm.LensMount = LIBRAW_MOUNT_Contax_N;
+    ilm.LensFormat = LIBRAW_FORMAT_FF;
+  }
+}
+
+int LibRaw::parse_jpeg(int offset)
+{
+  int len, save, hlen, mark;
+  fseek(ifp, offset, SEEK_SET);
+  if (fgetc(ifp) != 0xff || fgetc(ifp) != 0xd8)
+    return 0;
+
+  while (fgetc(ifp) == 0xff && (mark = fgetc(ifp)) != 0xda)
+  {
+    order = 0x4d4d;
+    len = get2() - 2;
+    save = ftell(ifp);
+    if (mark == 0xc0 || mark == 0xc3 || mark == 0xc9)
+    {
+      fgetc(ifp);
+      raw_height = get2();
+      raw_width = get2();
+    }
+    order = get2();
+    hlen = get4();
+    if (get4() == 0x48454150 && (save + hlen) >= 0 &&
+        (save + hlen) <= ifp->size()) /* "HEAP" */
+    {
+      parse_ciff(save + hlen, len - hlen, 0);
+    }
+    if (parse_tiff(save + 6))
+      apply_tiff();
+    fseek(ifp, save + len, SEEK_SET);
+  }
+  return 1;
+}
+
+void LibRaw::parse_thumb_note(int base, unsigned toff, unsigned tlen)
+{
+  unsigned entries, tag, type, len, save;
+
+  entries = get2();
+  while (entries--)
+  {
+    tiff_get(base, &tag, &type, &len, &save);
+    if (tag == toff)
+      thumb_offset = get4() + base;
+    if (tag == tlen)
+      thumb_length = get4();
+    fseek(ifp, save, SEEK_SET);
+  }
+}
+
+void LibRaw::parse_broadcom()
+{
+
+  /* This structure is at offset 0xb0 from the 'BRCM' ident. */
+  struct
+  {
+    uint8_t umode[32];
+    uint16_t uwidth;
+    uint16_t uheight;
+    uint16_t padding_right;
+    uint16_t padding_down;
+    uint32_t unknown_block[6];
+    uint16_t transform;
+    uint16_t format;
+    uint8_t bayer_order;
+    uint8_t bayer_format;
+  } header;
+
+  header.bayer_order = 0;
+  fseek(ifp, 0xb0 - 0x20, SEEK_CUR);
+  fread(&header, 1, sizeof(header), ifp);
+  /* load_flags is not used in broadcom loader, so reuse it for raw_stride */
+  load_flags =
+      ((((((header.uwidth + header.padding_right) * 5) + 3) >> 2) + 0x1f) &
+       (~0x1f));
+  raw_width = width = header.uwidth;
+  raw_height = height = header.uheight;
+  filters = 0x16161616; /* default Bayer order is 2, BGGR */
+
+  switch (header.bayer_order)
+  {
+  case 0: /* RGGB */
+    filters = 0x94949494;
+    break;
+  case 1: /* GBRG */
+    filters = 0x49494949;
+    break;
+  case 3: /* GRBG */
+    filters = 0x61616161;
+    break;
+  }
+}
+
+/*
+   Returns 1 for a Coolpix 995, 0 for anything else.
+ */
+int LibRaw::nikon_e995()
+{
+  int i, histo[256];
+  const uchar often[] = {0x00, 0x55, 0xaa, 0xff};
+
+  memset(histo, 0, sizeof histo);
+  fseek(ifp, -2000, SEEK_END);
+  for (i = 0; i < 2000; i++)
+    histo[fgetc(ifp)]++;
+  for (i = 0; i < 4; i++)
+    if (histo[often[i]] < 200)
+      return 0;
+  return 1;
+}
+
+/*
+   Since the TIFF DateTime string has no timezone information,
+   assume that the camera's clock was set to Universal Time.
+ */
+void LibRaw::get_timestamp(int reversed)
+{
+  struct tm t;
+  char str[20];
+  int i;
+
+  str[19] = 0;
+  if (reversed)
+    for (i = 19; i--;)
+      str[i] = fgetc(ifp);
+  else
+    fread(str, 19, 1, ifp);
+  memset(&t, 0, sizeof t);
+  if (sscanf(str, "%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;
+  t.tm_isdst = -1;
+  if (mktime(&t) > 0)
+    timestamp = mktime(&t);
+}
+
+#ifdef USE_6BY9RPI
+void LibRaw::parse_raspberrypi()
+{
+	//This structure is at offset 0xB0 from the 'BRCM' ident.
+	struct brcm_raw_header {
+		uint8_t name[32];
+		uint16_t h_width;
+		uint16_t h_height;
+		uint16_t padding_right;
+		uint16_t padding_down;
+		uint32_t dummy[6];
+		uint16_t transform;
+		uint16_t format;
+		uint8_t bayer_order;
+		uint8_t bayer_format;
+	};
+	//Values taken from https://github.com/raspberrypi/userland/blob/master/interface/vctypes/vc_image_types.h
+#define BRCM_FORMAT_BAYER  33
+#define BRCM_BAYER_RAW8    2
+#define BRCM_BAYER_RAW10   3
+#define BRCM_BAYER_RAW12   4
+#define BRCM_BAYER_RAW14   5
+#define BRCM_BAYER_RAW16   6
+
+	struct brcm_raw_header header;
+	uint8_t brcm_tag[4];
+
+	// Sanity check that the caller has found a BRCM header
+	if (!fread(brcm_tag, 1, sizeof(brcm_tag), ifp) ||
+		memcmp(brcm_tag, "BRCM", sizeof(brcm_tag)))
+		return;
+
+	width = raw_width;
+	data_offset = ftell(ifp) + 0x8000 - sizeof(brcm_tag);
+
+	if (!fseek(ifp, 0xB0 - sizeof(brcm_tag), SEEK_CUR) &&
+		fread(&header, 1, sizeof(header), ifp)) {
+		switch (header.bayer_order) {
+		case 0: //RGGB
+			filters = 0x94949494;
+			break;
+		case 1: //GBRG
+			filters = 0x49494949;
+			break;
+		default:
+		case 2: //BGGR
+			filters = 0x16161616;
+			break;
+		case 3: //GRBG
+			filters = 0x61616161;
+			break;
+		}
+
+		if (header.format == BRCM_FORMAT_BAYER) {
+			switch (header.bayer_format) {
+			case BRCM_BAYER_RAW8:
+				load_raw = &LibRaw::rpi_load_raw8;
+				//1 pixel per byte
+				raw_stride = ((header.h_width + header.padding_right) + 31)&(~31);
+				width = header.h_width;
+				raw_height = height = header.h_height;
+				is_raw = 1;
+				order = 0x4d4d;
+				break;
+			case BRCM_BAYER_RAW10:
+				load_raw = &LibRaw::nokia_load_raw;
+				//4 pixels per 5 bytes
+				raw_stride = (((((header.h_width + header.padding_right) * 5) + 3) >> 2) + 31)&(~31);
+				width = header.h_width;
+				raw_height = height = header.h_height;
+				is_raw = 1;
+				order = 0x4d4d;
+				break;
+			case BRCM_BAYER_RAW12:
+				load_raw = &LibRaw::rpi_load_raw12;
+				//2 pixels per 3 bytes
+				raw_stride = (((((header.h_width + header.padding_right) * 3) + 1) >> 1) + 31)&(~31);
+				width = header.h_width;
+				raw_height = height = header.h_height;
+				is_raw = 1;
+				order = 0x4d4d;
+				break;
+			case BRCM_BAYER_RAW14:
+				load_raw = &LibRaw::rpi_load_raw14;
+				//4 pixels per 7 bytes
+				raw_stride = (((((header.h_width + header.padding_right) * 7) + 3) >> 2) + 31)&(~31);
+				width = header.h_width;
+				raw_height = height = header.h_height;
+				is_raw = 1;
+				order = 0x4d4d;
+				break;
+			case BRCM_BAYER_RAW16:
+				load_raw = &LibRaw::rpi_load_raw16;
+				//1 pixel per 2 bytes
+				raw_stride = (((header.h_width + header.padding_right) << 1) + 31)&(~31);
+				width = header.h_width;
+				raw_height = height = header.h_height;
+				is_raw = 1;
+				order = 0x4d4d;
+				break;
+			default:
+				break;
+			}
+		}
+	}
+}
+#endif
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/metadata/nikon.cpp libkdcraw/libkdcraw/libraw/src/metadata/nikon.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/metadata/nikon.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/metadata/nikon.cpp	2022-11-07 07:46:31.734795008 +0300
@@ -0,0 +1,824 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+static const uchar xlat[2][256] = {
+    {0xc1, 0xbf, 0x6d, 0x0d, 0x59, 0xc5, 0x13, 0x9d, 0x83, 0x61, 0x6b, 0x4f,
+     0xc7, 0x7f, 0x3d, 0x3d, 0x53, 0x59, 0xe3, 0xc7, 0xe9, 0x2f, 0x95, 0xa7,
+     0x95, 0x1f, 0xdf, 0x7f, 0x2b, 0x29, 0xc7, 0x0d, 0xdf, 0x07, 0xef, 0x71,
+     0x89, 0x3d, 0x13, 0x3d, 0x3b, 0x13, 0xfb, 0x0d, 0x89, 0xc1, 0x65, 0x1f,
+     0xb3, 0x0d, 0x6b, 0x29, 0xe3, 0xfb, 0xef, 0xa3, 0x6b, 0x47, 0x7f, 0x95,
+     0x35, 0xa7, 0x47, 0x4f, 0xc7, 0xf1, 0x59, 0x95, 0x35, 0x11, 0x29, 0x61,
+     0xf1, 0x3d, 0xb3, 0x2b, 0x0d, 0x43, 0x89, 0xc1, 0x9d, 0x9d, 0x89, 0x65,
+     0xf1, 0xe9, 0xdf, 0xbf, 0x3d, 0x7f, 0x53, 0x97, 0xe5, 0xe9, 0x95, 0x17,
+     0x1d, 0x3d, 0x8b, 0xfb, 0xc7, 0xe3, 0x67, 0xa7, 0x07, 0xf1, 0x71, 0xa7,
+     0x53, 0xb5, 0x29, 0x89, 0xe5, 0x2b, 0xa7, 0x17, 0x29, 0xe9, 0x4f, 0xc5,
+     0x65, 0x6d, 0x6b, 0xef, 0x0d, 0x89, 0x49, 0x2f, 0xb3, 0x43, 0x53, 0x65,
+     0x1d, 0x49, 0xa3, 0x13, 0x89, 0x59, 0xef, 0x6b, 0xef, 0x65, 0x1d, 0x0b,
+     0x59, 0x13, 0xe3, 0x4f, 0x9d, 0xb3, 0x29, 0x43, 0x2b, 0x07, 0x1d, 0x95,
+     0x59, 0x59, 0x47, 0xfb, 0xe5, 0xe9, 0x61, 0x47, 0x2f, 0x35, 0x7f, 0x17,
+     0x7f, 0xef, 0x7f, 0x95, 0x95, 0x71, 0xd3, 0xa3, 0x0b, 0x71, 0xa3, 0xad,
+     0x0b, 0x3b, 0xb5, 0xfb, 0xa3, 0xbf, 0x4f, 0x83, 0x1d, 0xad, 0xe9, 0x2f,
+     0x71, 0x65, 0xa3, 0xe5, 0x07, 0x35, 0x3d, 0x0d, 0xb5, 0xe9, 0xe5, 0x47,
+     0x3b, 0x9d, 0xef, 0x35, 0xa3, 0xbf, 0xb3, 0xdf, 0x53, 0xd3, 0x97, 0x53,
+     0x49, 0x71, 0x07, 0x35, 0x61, 0x71, 0x2f, 0x43, 0x2f, 0x11, 0xdf, 0x17,
+     0x97, 0xfb, 0x95, 0x3b, 0x7f, 0x6b, 0xd3, 0x25, 0xbf, 0xad, 0xc7, 0xc5,
+     0xc5, 0xb5, 0x8b, 0xef, 0x2f, 0xd3, 0x07, 0x6b, 0x25, 0x49, 0x95, 0x25,
+     0x49, 0x6d, 0x71, 0xc7},
+    {0xa7, 0xbc, 0xc9, 0xad, 0x91, 0xdf, 0x85, 0xe5, 0xd4, 0x78, 0xd5, 0x17,
+     0x46, 0x7c, 0x29, 0x4c, 0x4d, 0x03, 0xe9, 0x25, 0x68, 0x11, 0x86, 0xb3,
+     0xbd, 0xf7, 0x6f, 0x61, 0x22, 0xa2, 0x26, 0x34, 0x2a, 0xbe, 0x1e, 0x46,
+     0x14, 0x68, 0x9d, 0x44, 0x18, 0xc2, 0x40, 0xf4, 0x7e, 0x5f, 0x1b, 0xad,
+     0x0b, 0x94, 0xb6, 0x67, 0xb4, 0x0b, 0xe1, 0xea, 0x95, 0x9c, 0x66, 0xdc,
+     0xe7, 0x5d, 0x6c, 0x05, 0xda, 0xd5, 0xdf, 0x7a, 0xef, 0xf6, 0xdb, 0x1f,
+     0x82, 0x4c, 0xc0, 0x68, 0x47, 0xa1, 0xbd, 0xee, 0x39, 0x50, 0x56, 0x4a,
+     0xdd, 0xdf, 0xa5, 0xf8, 0xc6, 0xda, 0xca, 0x90, 0xca, 0x01, 0x42, 0x9d,
+     0x8b, 0x0c, 0x73, 0x43, 0x75, 0x05, 0x94, 0xde, 0x24, 0xb3, 0x80, 0x34,
+     0xe5, 0x2c, 0xdc, 0x9b, 0x3f, 0xca, 0x33, 0x45, 0xd0, 0xdb, 0x5f, 0xf5,
+     0x52, 0xc3, 0x21, 0xda, 0xe2, 0x22, 0x72, 0x6b, 0x3e, 0xd0, 0x5b, 0xa8,
+     0x87, 0x8c, 0x06, 0x5d, 0x0f, 0xdd, 0x09, 0x19, 0x93, 0xd0, 0xb9, 0xfc,
+     0x8b, 0x0f, 0x84, 0x60, 0x33, 0x1c, 0x9b, 0x45, 0xf1, 0xf0, 0xa3, 0x94,
+     0x3a, 0x12, 0x77, 0x33, 0x4d, 0x44, 0x78, 0x28, 0x3c, 0x9e, 0xfd, 0x65,
+     0x57, 0x16, 0x94, 0x6b, 0xfb, 0x59, 0xd0, 0xc8, 0x22, 0x36, 0xdb, 0xd2,
+     0x63, 0x98, 0x43, 0xa1, 0x04, 0x87, 0x86, 0xf7, 0xa6, 0x26, 0xbb, 0xd6,
+     0x59, 0x4d, 0xbf, 0x6a, 0x2e, 0xaa, 0x2b, 0xef, 0xe6, 0x78, 0xb6, 0x4e,
+     0xe0, 0x2f, 0xdc, 0x7c, 0xbe, 0x57, 0x19, 0x32, 0x7e, 0x2a, 0xd0, 0xb8,
+     0xba, 0x29, 0x00, 0x3c, 0x52, 0x7d, 0xa8, 0x49, 0x3b, 0x2d, 0xeb, 0x25,
+     0x49, 0xfa, 0xa3, 0xaa, 0x39, 0xa7, 0xc5, 0xa7, 0x50, 0x11, 0x36, 0xfb,
+     0xc6, 0x67, 0x4a, 0xf5, 0xa5, 0x12, 0x65, 0x7e, 0xb0, 0xdf, 0xaf, 0x4e,
+     0xb3, 0x61, 0x7f, 0x2f} };
+
+
+
+void LibRaw::processNikonLensData(uchar *LensData, unsigned len)
+{
+
+  ushort i;
+  if (imgdata.lens.nikon.LensType & 0x80) {
+    strcpy (ilm.LensFeatures_pre, "AF-P");
+  } else if (!(imgdata.lens.nikon.LensType & 0x01)) {
+    ilm.LensFeatures_pre[0] = 'A';
+    ilm.LensFeatures_pre[1] = 'F';
+  } else {
+    ilm.LensFeatures_pre[0] = 'M';
+    ilm.LensFeatures_pre[1] = 'F';
+  }
+
+  if (imgdata.lens.nikon.LensType & 0x40) {
+    ilm.LensFeatures_suf[0] = 'E';
+  } else if (imgdata.lens.nikon.LensType & 0x04) {
+    ilm.LensFeatures_suf[0] = 'G';
+  } else if (imgdata.lens.nikon.LensType & 0x02) {
+    ilm.LensFeatures_suf[0] = 'D';
+  }
+
+  if (imgdata.lens.nikon.LensType & 0x08)
+  {
+    ilm.LensFeatures_suf[1] = ' ';
+    ilm.LensFeatures_suf[2] = 'V';
+    ilm.LensFeatures_suf[3] = 'R';
+  }
+
+  if (imgdata.lens.nikon.LensType & 0x10)
+  {
+    ilm.LensMount = ilm.CameraMount = LIBRAW_MOUNT_Nikon_CX;
+    ilm.CameraFormat = ilm.LensFormat = LIBRAW_FORMAT_1INCH;
+  }
+  else
+    ilm.LensMount = ilm.CameraMount = LIBRAW_MOUNT_Nikon_F;
+
+  if (imgdata.lens.nikon.LensType & 0x20)
+  {
+    strcpy(ilm.Adapter, "FT-1");
+    ilm.LensMount = LIBRAW_MOUNT_Nikon_F;
+    ilm.CameraMount = LIBRAW_MOUNT_Nikon_CX;
+    ilm.CameraFormat = LIBRAW_FORMAT_1INCH;
+  }
+
+  imgdata.lens.nikon.LensType = imgdata.lens.nikon.LensType & 0xdf;
+
+  if ((len < 20) || (len == 58))
+  {
+    switch (len)
+    {
+    case 9:
+      i = 2;
+      break;
+    case 15:
+      i = 7;
+      break;
+    case 16:
+      i = 8;
+      break;
+    case 58: // "Z 6", "Z 7", "Z 50", D780
+      if (model[6] == 'Z')
+        ilm.CameraMount = LIBRAW_MOUNT_Nikon_Z;
+      if (imNikon.HighSpeedCropFormat != 12)
+        ilm.CameraFormat = LIBRAW_FORMAT_FF;
+      i = 1;
+      while ((LensData[i] == LensData[0]) && (i < 17))
+        i++;
+      if (i == 17)
+      {
+        ilm.LensMount = LIBRAW_MOUNT_Nikon_Z;
+        ilm.LensID = sget2(LensData + 0x2c);
+        switch (ilm.LensID) {
+          case 11: case 12:
+            ilm.LensFormat = LIBRAW_FORMAT_APSC;
+            break;
+          case 1:  case 2:  case 4:  case 8:
+          case 9: case 13: case 14: case 15:
+            ilm.LensFormat = LIBRAW_FORMAT_FF;
+            break;
+        }
+        if (ilm.MaxAp4CurFocal < 0.7f)
+          ilm.MaxAp4CurFocal = libraw_powf64l(
+              2.0f, (float)sget2(LensData + 0x32) / 384.0f - 1.0f);
+        if (ilm.CurAp < 0.7f)
+          ilm.CurAp = libraw_powf64l(
+              2.0f, (float)sget2(LensData + 0x34) / 384.0f - 1.0f);
+        if (fabsf(ilm.CurFocal) < 1.1f)
+          ilm.CurFocal = sget2(LensData + 0x38);
+        return;
+      }
+      i = 9;
+      ilm.LensMount = LIBRAW_MOUNT_Nikon_F;
+      if (ilm.CameraMount == LIBRAW_MOUNT_Nikon_Z)
+        strcpy(ilm.Adapter, "FTZ");
+      break;
+    }
+    imgdata.lens.nikon.LensIDNumber = LensData[i];
+    imgdata.lens.nikon.LensFStops = LensData[i + 1];
+    ilm.LensFStops = (float)imgdata.lens.nikon.LensFStops / 12.0f;
+    if (fabsf(ilm.MinFocal) < 1.1f)
+    {
+      if ((imgdata.lens.nikon.LensType ^ (uchar)0x01) || LensData[i + 2])
+        ilm.MinFocal =
+            5.0f * libraw_powf64l(2.0f, (float)LensData[i + 2] / 24.0f);
+      if ((imgdata.lens.nikon.LensType ^ (uchar)0x01) || LensData[i + 3])
+        ilm.MaxFocal =
+            5.0f * libraw_powf64l(2.0f, (float)LensData[i + 3] / 24.0f);
+      if ((imgdata.lens.nikon.LensType ^ (uchar)0x01) || LensData[i + 4])
+        ilm.MaxAp4MinFocal =
+            libraw_powf64l(2.0f, (float)LensData[i + 4] / 24.0f);
+      if ((imgdata.lens.nikon.LensType ^ (uchar)0x01) || LensData[i + 5])
+        ilm.MaxAp4MaxFocal =
+            libraw_powf64l(2.0f, (float)LensData[i + 5] / 24.0f);
+    }
+    imgdata.lens.nikon.MCUVersion = LensData[i + 6];
+    if (i != 2)
+    {
+      if ((LensData[i - 1]) && (fabsf(ilm.CurFocal) < 1.1f))
+        ilm.CurFocal =
+            5.0f * libraw_powf64l(2.0f, (float)LensData[i - 1] / 24.0f);
+      if (LensData[i + 7])
+        imgdata.lens.nikon.EffectiveMaxAp =
+            libraw_powf64l(2.0f, (float)LensData[i + 7] / 24.0f);
+    }
+    ilm.LensID = (unsigned long long)LensData[i] << 56 |
+                 (unsigned long long)LensData[i + 1] << 48 |
+                 (unsigned long long)LensData[i + 2] << 40 |
+                 (unsigned long long)LensData[i + 3] << 32 |
+                 (unsigned long long)LensData[i + 4] << 24 |
+                 (unsigned long long)LensData[i + 5] << 16 |
+                 (unsigned long long)LensData[i + 6] << 8 |
+                 (unsigned long long)imgdata.lens.nikon.LensType;
+  }
+  else if ((len == 459) || (len == 590))
+  {
+    memcpy(ilm.Lens, LensData + 390, 64);
+  }
+  else if (len == 509)
+  {
+    memcpy(ilm.Lens, LensData + 391, 64);
+  }
+  else if (len == 879)
+  {
+    memcpy(ilm.Lens, LensData + 680, 64);
+  }
+
+  return;
+}
+
+void LibRaw::Nikon_NRW_WBtag(int wb, int skip)
+{
+
+  int r, g0, g1, b;
+  if (skip)
+    get4(); // skip wb "CCT", it is not unique
+  r = get4();
+  g0 = get4();
+  g1 = get4();
+  b = get4();
+  if (r && g0 && g1 && b)
+  {
+    icWBC[wb][0] = r << 1;
+    icWBC[wb][1] = g0;
+    icWBC[wb][2] = b << 1;
+    icWBC[wb][3] = g1;
+  }
+  return;
+}
+
+void LibRaw::parseNikonMakernote(int base, int uptag, unsigned dng_writer)
+{
+
+  unsigned offset = 0, entries, tag, type, len, save;
+
+  unsigned c, i;
+  uchar *LensData_buf;
+  uchar ColorBalanceData_buf[324];
+  int ColorBalanceData_ready = 0;
+  uchar ci, cj, ck;
+  unsigned serial = 0;
+  unsigned custom_serial = 0;
+  unsigned LensData_len = 0;
+
+  short morder, sorder = order;
+  char buf[10];
+  INT64 fsize = ifp->size();
+
+  fread(buf, 1, 10, ifp);
+
+  if (!strcmp(buf, "Nikon"))
+  {
+    if (buf[6] != '\2')
+      return;
+    base = ftell(ifp);
+    order = get2();
+    if (get2() != 42)
+      goto quit;
+    offset = get4();
+    fseek(ifp, offset - 8, SEEK_CUR);
+  }
+  else
+  {
+    fseek(ifp, -10, SEEK_CUR);
+  }
+
+  entries = get2();
+  if (entries > 1000)
+    return;
+  morder = order;
+
+  while (entries--)
+  {
+    order = morder;
+    tiff_get(base, &tag, &type, &len, &save);
+
+    INT64 pos = ifp->tell();
+    if (len > 8 && pos + len > 2 * fsize)
+    {
+      fseek(ifp, save, SEEK_SET); // Recover tiff-read position!!
+      continue;
+    }
+    tag |= uptag << 16;
+    if (len > 100 * 1024 * 1024)
+      goto next; // 100Mb tag? No!
+
+    if (tag == 0x0002)
+    {
+      if (!iso_speed)
+        iso_speed = (get2(), get2());
+    }
+    else if (tag == 0x000a)
+    {
+      ilm.LensMount = ilm.CameraMount = LIBRAW_MOUNT_FixedLens;
+      ilm.FocalType = LIBRAW_FT_ZOOM_LENS;
+    }
+    else if ((tag == 0x000c) && (len == 4) && tagtypeIs(LIBRAW_EXIFTAG_TYPE_RATIONAL))
+    {
+      cam_mul[0] = getreal(type);
+      cam_mul[2] = getreal(type);
+      cam_mul[1] = getreal(type);
+      cam_mul[3] = getreal(type);
+    }
+    else if (tag == 0x0011)
+    {
+      if (is_raw)
+      {
+        fseek(ifp, get4() + base, SEEK_SET);
+        parse_tiff_ifd(base);
+      }
+    }
+    else if (tag == 0x0012)
+    {
+      ci = fgetc(ifp);
+      cj = fgetc(ifp);
+      ck = fgetc(ifp);
+      if (ck)
+        imCommon.FlashEC = (float)(ci * cj) / (float)ck;
+    }
+    else if (tag == 0x0014)
+    {
+      if (tagtypeIs(LIBRAW_EXIFTOOLTAGTYPE_binary))
+      {
+        if (len == 2560)
+        { // E5400, E8400, E8700, E8800
+          fseek(ifp, 0x4e0L, SEEK_CUR);
+          order = 0x4d4d;
+          cam_mul[0] = get2() / 256.0;
+          cam_mul[2] = get2() / 256.0;
+          cam_mul[1] = cam_mul[3] = 1.0;
+          icWBC[LIBRAW_WBI_Auto][0] = get2();
+          icWBC[LIBRAW_WBI_Auto][2] = get2();
+          icWBC[LIBRAW_WBI_Daylight][0] = get2();
+          icWBC[LIBRAW_WBI_Daylight][2] = get2();
+          fseek(ifp, 0x18L, SEEK_CUR);
+          icWBC[LIBRAW_WBI_Tungsten][0] = get2();
+          icWBC[LIBRAW_WBI_Tungsten][2] = get2();
+          fseek(ifp, 0x18L, SEEK_CUR);
+          icWBC[LIBRAW_WBI_FL_W][0] = get2();
+          icWBC[LIBRAW_WBI_FL_W][2] = get2();
+          icWBC[LIBRAW_WBI_FL_N][0] = get2();
+          icWBC[LIBRAW_WBI_FL_N][2] = get2();
+          icWBC[LIBRAW_WBI_FL_D][0] = get2();
+          icWBC[LIBRAW_WBI_FL_D][2] = get2();
+          icWBC[LIBRAW_WBI_Cloudy][0] = get2();
+          icWBC[LIBRAW_WBI_Cloudy][2] = get2();
+          fseek(ifp, 0x18L, SEEK_CUR);
+          icWBC[LIBRAW_WBI_Flash][0] = get2();
+          icWBC[LIBRAW_WBI_Flash][2] = get2();
+
+          icWBC[LIBRAW_WBI_Auto][1] = icWBC[LIBRAW_WBI_Auto][3] =
+            icWBC[LIBRAW_WBI_Daylight][1] = icWBC[LIBRAW_WBI_Daylight][3] =
+            icWBC[LIBRAW_WBI_Tungsten][1] = icWBC[LIBRAW_WBI_Tungsten][3] =
+            icWBC[LIBRAW_WBI_FL_W][1] = icWBC[LIBRAW_WBI_FL_W][3] =
+            icWBC[LIBRAW_WBI_FL_N][1] = icWBC[LIBRAW_WBI_FL_N][3] =
+            icWBC[LIBRAW_WBI_FL_D][1] = icWBC[LIBRAW_WBI_FL_D][3] =
+            icWBC[LIBRAW_WBI_Cloudy][1] = icWBC[LIBRAW_WBI_Cloudy][3] =
+            icWBC[LIBRAW_WBI_Flash][1] = icWBC[LIBRAW_WBI_Flash][3] = 256;
+
+          if (strncmp(model, "E8700", 5))
+          {
+            fseek(ifp, 0x18L, SEEK_CUR);
+            icWBC[LIBRAW_WBI_Shade][0] = get2();
+            icWBC[LIBRAW_WBI_Shade][2] = get2();
+            icWBC[LIBRAW_WBI_Shade][1] = icWBC[LIBRAW_WBI_Shade][3] = 256;
+          }
+        }
+        else if (len == 1280)
+        { // E5000, E5700
+          cam_mul[0] = cam_mul[1] = cam_mul[2] = cam_mul[3] = 1.0;
+        }
+        else
+        {
+          fread(buf, 1, 10, ifp);
+          if (!strncmp(buf, "NRW ", 4))
+          { // P6000, P7000, P7100, B700, P1000
+            if (!strcmp(buf + 4, "0100"))
+            { // P6000
+              fseek(ifp, 0x13deL, SEEK_CUR);
+              cam_mul[0] = get4() << 1;
+              cam_mul[1] = get4();
+              cam_mul[3] = get4();
+              cam_mul[2] = get4() << 1;
+              Nikon_NRW_WBtag(LIBRAW_WBI_Daylight, 0);
+              Nikon_NRW_WBtag(LIBRAW_WBI_Cloudy, 0);
+              fseek(ifp, 0x10L, SEEK_CUR);
+              Nikon_NRW_WBtag(LIBRAW_WBI_Tungsten, 0);
+              Nikon_NRW_WBtag(LIBRAW_WBI_FL_W, 0);
+              Nikon_NRW_WBtag(LIBRAW_WBI_Flash, 0);
+              fseek(ifp, 0x10L, SEEK_CUR);
+              Nikon_NRW_WBtag(LIBRAW_WBI_Custom, 0);
+              Nikon_NRW_WBtag(LIBRAW_WBI_Auto, 0);
+            }
+            else
+            { // P7000, P7100, B700, P1000
+              fseek(ifp, 0x16L, SEEK_CUR);
+              black = get2();
+              if (cam_mul[0] < 0.1f)
+              {
+                fseek(ifp, 0x16L, SEEK_CUR);
+                cam_mul[0] = get4() << 1;
+                cam_mul[1] = get4();
+                cam_mul[3] = get4();
+                cam_mul[2] = get4() << 1;
+              }
+              else
+              {
+                fseek(ifp, 0x26L, SEEK_CUR);
+              }
+              if (len != 332)
+              { // not A1000
+                Nikon_NRW_WBtag(LIBRAW_WBI_Daylight, 1);
+                Nikon_NRW_WBtag(LIBRAW_WBI_Cloudy, 1);
+                Nikon_NRW_WBtag(LIBRAW_WBI_Shade, 1);
+                Nikon_NRW_WBtag(LIBRAW_WBI_Tungsten, 1);
+                Nikon_NRW_WBtag(LIBRAW_WBI_FL_W, 1);
+                Nikon_NRW_WBtag(LIBRAW_WBI_FL_N, 1);
+                Nikon_NRW_WBtag(LIBRAW_WBI_FL_D, 1);
+                Nikon_NRW_WBtag(LIBRAW_WBI_HT_Mercury, 1);
+                fseek(ifp, 0x14L, SEEK_CUR);
+                Nikon_NRW_WBtag(LIBRAW_WBI_Custom, 1);
+                Nikon_NRW_WBtag(LIBRAW_WBI_Auto, 1);
+              }
+              else
+              {
+                fseek(ifp, 0xc8L, SEEK_CUR);
+                Nikon_NRW_WBtag(LIBRAW_WBI_Auto, 1);
+              }
+            }
+          }
+        }
+      }
+    }
+    else if (tag == 0x001b)
+    {
+      imNikon.HighSpeedCropFormat = get2();
+      imNikon.SensorHighSpeedCrop.cwidth = get2();
+      imNikon.SensorHighSpeedCrop.cheight = get2();
+      imNikon.SensorWidth = get2();
+      imNikon.SensorHeight = get2();
+      imNikon.SensorHighSpeedCrop.cleft = get2();
+      imNikon.SensorHighSpeedCrop.ctop = get2();
+      switch (imNikon.HighSpeedCropFormat)
+      {
+      case 0:
+      case 1:
+      case 2:
+      case 4:
+        imgdata.sizes.raw_inset_crop.aspect = LIBRAW_IMAGE_ASPECT_3to2;
+        break;
+      case 11:
+        ilm.CameraFormat = LIBRAW_FORMAT_FF;
+        imgdata.sizes.raw_inset_crop.aspect = LIBRAW_IMAGE_ASPECT_3to2;
+        break;
+      case 12:
+        ilm.CameraFormat = LIBRAW_FORMAT_APSC;
+        imgdata.sizes.raw_inset_crop.aspect = LIBRAW_IMAGE_ASPECT_3to2;
+        break;
+      case 3:
+        imgdata.sizes.raw_inset_crop.aspect = LIBRAW_IMAGE_ASPECT_5to4;
+        break;
+      case 6:
+        imgdata.sizes.raw_inset_crop.aspect = LIBRAW_IMAGE_ASPECT_16to9;
+        break;
+      case 17:
+        imgdata.sizes.raw_inset_crop.aspect = LIBRAW_IMAGE_ASPECT_1to1;
+        break;
+      default:
+        imgdata.sizes.raw_inset_crop.aspect = LIBRAW_IMAGE_ASPECT_OTHER;
+        break;
+      }
+    }
+    else if (tag == 0x001d)
+    { // serial number
+      if (len > 0)
+      {
+        int model_len = (int)strbuflen(model);
+        while ((c = fgetc(ifp)) && (len-- > 0) && (c != (unsigned)EOF))
+        {
+          if ((!custom_serial) && (!isdigit(c)))
+          {
+            if (((model_len == 3) && !strcmp(model, "D50")) ||
+                ((model_len >= 4) && !isalnum(model[model_len - 4]) &&
+                 !strncmp(&model[model_len - 3], "D50", 3)))
+            {
+              custom_serial = 34;
+            }
+            else
+            {
+              custom_serial = 96;
+            }
+            break;
+          }
+          serial = serial * 10 + (isdigit(c) ? c - '0' : c % 10);
+        }
+        if (!imgdata.shootinginfo.BodySerial[0])
+          sprintf(imgdata.shootinginfo.BodySerial, "%d", serial);
+      }
+    }
+    else if (tag == 0x001e) {
+      switch (get2()) {
+      case 1:
+        imCommon.ColorSpace = LIBRAW_COLORSPACE_sRGB;
+        break;
+      case 2:
+        imCommon.ColorSpace = LIBRAW_COLORSPACE_AdobeRGB;
+        break;
+      default:
+        imCommon.ColorSpace = LIBRAW_COLORSPACE_Unknown;
+        break;
+      }
+    } else if (tag == 0x0025)
+    {
+      if (!iso_speed || (iso_speed == 65535))
+      {
+        iso_speed =
+            int(100.0 *
+                libraw_powf64l(2.0f, double((uchar)fgetc(ifp)) / 12.0 - 5.0));
+      }
+    }
+    else if (tag == 0x003b)
+    { // WB for multi-exposure (ME); all 1s for regular exposures
+      imNikon.ME_WB[0] = getreal(type);
+      imNikon.ME_WB[2] = getreal(type);
+      imNikon.ME_WB[1] = getreal(type);
+      imNikon.ME_WB[3] = getreal(type);
+    }
+    else if (tag == 0x003d)
+    { // not corrected for file bitcount, to be patched in open_datastream
+      FORC4 cblack[RGGB_2_RGBG(c)] = get2();
+      i = cblack[3];
+      FORC3 if (i > cblack[c]) i = cblack[c];
+      FORC4 cblack[c] -= i;
+      black += i;
+    }
+    else if (tag == 0x0045)
+    { /* upper left pixel (x,y), size (width,height) */
+      imgdata.sizes.raw_inset_crop.cleft = get2();
+      imgdata.sizes.raw_inset_crop.ctop = get2();
+      imgdata.sizes.raw_inset_crop.cwidth = get2();
+      imgdata.sizes.raw_inset_crop.cheight = get2();
+    }
+    else if (tag == 0x0082)
+    { // lens attachment
+      stmread(ilm.Attachment, len, ifp);
+    }
+    else if (tag == 0x0083)
+    { // lens type
+      imgdata.lens.nikon.LensType = fgetc(ifp);
+    }
+    else if (tag == 0x0084)
+    { // lens
+      ilm.MinFocal = getreal(type);
+      ilm.MaxFocal = getreal(type);
+      ilm.MaxAp4MinFocal = getreal(type);
+      ilm.MaxAp4MaxFocal = getreal(type);
+    }
+    else if (tag == 0x008b)
+    { // lens f-stops
+      ci = fgetc(ifp);
+      cj = fgetc(ifp);
+      ck = fgetc(ifp);
+      if (ck)
+      {
+        imgdata.lens.nikon.LensFStops = ci * cj * (12 / ck);
+        ilm.LensFStops = (float)imgdata.lens.nikon.LensFStops / 12.0f;
+      }
+    }
+    else if ((tag == 0x008c) || (tag == 0x0096))
+    {
+      meta_offset = ftell(ifp);
+    }
+    else if (tag == 0x0093)
+    {
+      imNikon.NEFCompression = i = get2();
+      if ((i == 7) || (i == 9))
+      {
+        ilm.LensMount = LIBRAW_MOUNT_FixedLens;
+        ilm.CameraMount = LIBRAW_MOUNT_FixedLens;
+      }
+    }
+    else if (tag == 0x0097)
+    { // ver97
+      FORC4 imNikon.ColorBalanceVersion =
+          imNikon.ColorBalanceVersion * 10 + fgetc(ifp) - '0';
+      switch (imNikon.ColorBalanceVersion)
+      {
+      case 100: // NIKON D100
+        fseek(ifp, 0x44L, SEEK_CUR);
+        FORC4 cam_mul[RBGG_2_RGBG(c)] = get2();
+        break;
+      case 102: // NIKON D2H
+        fseek(ifp, 0x6L, SEEK_CUR);
+        FORC4 cam_mul[RGGB_2_RGBG(c)] = get2();
+        break;
+      case 103: // NIKON D70, D70s
+        fseek(ifp, 0x10L, SEEK_CUR);
+        FORC4 cam_mul[c] = get2();
+      }
+      if (imNikon.ColorBalanceVersion >= 200)
+      {
+        /*
+        204: NIKON D2X, D2Xs
+        205: NIKON D50
+        206: NIKON D2Hs
+        207: NIKON D200
+        208: NIKON D40, D40X, D80
+        209: NIKON D3, D3X, D300, D700
+        210: NIKON D60
+        211: NIKON D90, D5000
+        212: NIKON D300S
+        213: NIKON D3000
+        214: NIKON D3S
+        215: NIKON D3100
+        216: NIKON D5100, D7000
+        217: NIKON D4, D600, D800, D800E, D3200
+        -= unknown =-
+        218: NIKON D5200, D7100
+        219: NIKON D5300
+        220: NIKON D610, Df
+        221: NIKON D3300
+        222: NIKON D4S
+        223: NIKON D750, D810
+        224: NIKON D3400, D3500, D5500, D5600, D7200
+        225: NIKON D5, D500
+        226: NIKON D7500
+        227: NIKON D850
+         */
+        if (imNikon.ColorBalanceVersion != 205)
+        {
+          fseek(ifp, 0x118L, SEEK_CUR);
+        }
+        ColorBalanceData_ready =
+            (fread(ColorBalanceData_buf, 324, 1, ifp) == 1);
+      }
+      if ((imNikon.ColorBalanceVersion >= 400) &&
+          (imNikon.ColorBalanceVersion <= 405))
+      { // 1 J1, 1 V1, 1 J2, 1 V2, 1 J3, 1 S1, 1 AW1, 1 S2, 1 J4, 1 V3, 1 J5
+        ilm.CameraFormat = LIBRAW_FORMAT_1INCH;
+        ilm.CameraMount = LIBRAW_MOUNT_Nikon_CX;
+      }
+      else if ((imNikon.ColorBalanceVersion >= 500) &&
+               (imNikon.ColorBalanceVersion <= 502))
+      { // P7700, P7800, P330, P340
+        ilm.CameraMount = ilm.LensMount = LIBRAW_MOUNT_FixedLens;
+        ilm.FocalType = LIBRAW_FT_ZOOM_LENS;
+      }
+      else if (imNikon.ColorBalanceVersion == 601)
+      { // Coolpix A
+        ilm.CameraFormat = ilm.LensFormat = LIBRAW_FORMAT_APSC;
+        ilm.CameraMount = ilm.LensMount = LIBRAW_MOUNT_FixedLens;
+        ilm.FocalType = LIBRAW_FT_PRIME_LENS;
+      }
+    }
+    else if (tag == 0x0098)
+    { // contains lens data
+      FORC4 imNikon.LensDataVersion =
+          imNikon.LensDataVersion * 10 + fgetc(ifp) - '0';
+      switch (imNikon.LensDataVersion)
+      {
+      case 100:
+        LensData_len = 9;
+        break;
+      case 101:
+      case 201: // encrypted, starting from v.201
+      case 202:
+      case 203:
+        LensData_len = 15;
+        break;
+      case 204:
+        LensData_len = 16;
+        break;
+      case 400:
+        LensData_len = 459;
+        break;
+      case 401:
+        LensData_len = 590;
+        break;
+      case 402:
+        LensData_len = 509;
+        break;
+      case 403:
+        LensData_len = 879;
+        break;
+      case 800:
+        LensData_len = 58;
+        break;
+      }
+      if (LensData_len)
+      {
+        LensData_buf = (uchar *)malloc(LensData_len);
+        fread(LensData_buf, LensData_len, 1, ifp);
+      }
+    }
+    else if (tag == 0x00a0)
+    {
+      stmread(imgdata.shootinginfo.BodySerial, len, ifp);
+    }
+    else if (tag == 0x00a7)
+    { // shutter count
+      imNikon.key = fgetc(ifp) ^ fgetc(ifp) ^ fgetc(ifp) ^ fgetc(ifp);
+      if (custom_serial)
+      {
+        ci = xlat[0][custom_serial];
+      }
+      else
+      {
+        ci = xlat[0][serial & 0xff];
+      }
+      cj = xlat[1][imNikon.key];
+      ck = 0x60;
+      if (((unsigned)(imNikon.ColorBalanceVersion - 200) < 18) &&
+          ColorBalanceData_ready)
+      {
+        for (i = 0; i < 324; i++)
+          ColorBalanceData_buf[i] ^= (cj += ci * ck++);
+        i = "66666>666;6A;:;555"[imNikon.ColorBalanceVersion - 200] - '0';
+        FORC4 cam_mul[c ^ (c >> 1) ^ (i & 1)] =
+            sget2(ColorBalanceData_buf + (i & -2) + c * 2);
+      }
+
+      if (LensData_len)
+      {
+        if (imNikon.LensDataVersion > 200)
+        {
+          cj = xlat[1][imNikon.key];
+          ck = 0x60;
+          for (i = 0; i < LensData_len; i++)
+          {
+            LensData_buf[i] ^= (cj += ci * ck++);
+          }
+        }
+        processNikonLensData(LensData_buf, LensData_len);
+        LensData_len = 0;
+        free(LensData_buf);
+      }
+    }
+    else if (tag == 0x00a8)
+    { // contains flash data
+      FORC4 imNikon.FlashInfoVersion =
+          imNikon.FlashInfoVersion * 10 + fgetc(ifp) - '0';
+    }
+    else if (tag == 0x00b0)
+    {
+      get4(); // ME (multi-exposure) tag version, 4 symbols
+      imNikon.ExposureMode = get4();
+      imNikon.nMEshots = get4();
+      imNikon.MEgainOn = get4();
+    }
+    else if (tag == 0x00b9)
+    {
+      imNikon.AFFineTune = fgetc(ifp);
+      imNikon.AFFineTuneIndex = fgetc(ifp);
+      imNikon.AFFineTuneAdj = (int8_t)fgetc(ifp);
+    }
+    else if ((tag == 0x0100) && tagtypeIs(LIBRAW_EXIFTAG_TYPE_UNDEFINED))
+    {
+      thumb_offset = ftell(ifp);
+      thumb_length = len;
+    }
+    else if (tag == 0x0e01)
+    { /* Nikon Software / in-camera edit Note */
+      int loopc = 0;
+      int WhiteBalanceAdj_active = 0;
+      order = 0x4949;
+      fseek(ifp, 22, SEEK_CUR);
+      for (offset = 22; offset + 22 < len; offset += 22 + i)
+      {
+        if (loopc++ > 1024)
+          throw LIBRAW_EXCEPTION_IO_CORRUPT;
+        tag = get4();
+        fseek(ifp, 14, SEEK_CUR);
+        i = get4() - 4;
+
+        if (tag == 0x76a43204)
+        {
+          WhiteBalanceAdj_active = fgetc(ifp);
+        }
+        else if (tag == 0xbf3c6c20)
+        {
+          if (WhiteBalanceAdj_active)
+          {
+            union {
+              double dbl;
+              unsigned long long lng;
+            } un;
+            un.dbl = getreal(LIBRAW_EXIFTAG_TYPE_DOUBLE);
+            if ((un.lng != 0x3FF0000000000000ULL) &&
+                (un.lng != 0x000000000000F03FULL))
+            {
+              cam_mul[0] = un.dbl;
+              cam_mul[2] = getreal(LIBRAW_EXIFTAG_TYPE_DOUBLE);
+              cam_mul[1] = cam_mul[3] = 1.0;
+              i -= 16;
+            }
+            else
+              i -= 8;
+          }
+          fseek(ifp, i, SEEK_CUR);
+        }
+        else if (tag == 0x76a43207)
+        {
+          flip = get2();
+        }
+        else
+        {
+          fseek(ifp, i, SEEK_CUR);
+        }
+      }
+    }
+    else if (tag == 0x0e22)
+    {
+      FORC4 imNikon.NEFBitDepth[c] = get2();
+    }
+  next:
+    fseek(ifp, save, SEEK_SET);
+  }
+quit:
+  order = sorder;
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/metadata/normalize_model.cpp libkdcraw/libkdcraw/libraw/src/metadata/normalize_model.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/metadata/normalize_model.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/metadata/normalize_model.cpp	2022-11-07 07:46:31.734795008 +0300
@@ -0,0 +1,1381 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+#include "../../internal/libraw_cameraids.h"
+
+void LibRaw::GetNormalizedModel()
+{
+
+  int i, j;
+  char *ps;
+
+  static const struct
+  {
+    unsigned long long id;
+    char t_model[20];
+  } unique[] =
+// clang-format off
+    {
+      { CanonID_EOS_M50,           "EOS M50"}, // Kiss M
+      { CanonID_EOS_M6_Mark_II,    "EOS M6 Mark II"},
+      { CanonID_EOS_M200,          "EOS M200"},
+      { CanonID_EOS_D30,           "EOS D30"},
+      { CanonID_EOS_D60,           "EOS D60"},
+      { CanonID_EOS_M3,            "EOS M3"},
+      { CanonID_EOS_M10,           "EOS M10"},
+      { CanonID_EOS_M5,            "EOS M5"},
+      { CanonID_EOS_M100,          "EOS M100"},
+      { CanonID_EOS_M6,            "EOS M6"},
+      { CanonID_EOS_1D,            "EOS-1D"},
+      { CanonID_EOS_1DS,           "EOS-1DS"},
+      { CanonID_EOS_10D,           "EOS 10D"},
+      { CanonID_EOS_1D_Mark_III,   "EOS-1D Mark III"},
+      { CanonID_EOS_300D,          "EOS 300D"}, // Digital Rebel / Kiss Digital
+      { CanonID_EOS_1D_Mark_II,    "EOS-1D Mark II"},
+      { CanonID_EOS_20D,           "EOS 20D"},
+      { CanonID_EOS_450D,          "EOS 450D"}, // Digital Rebel XSi / Kiss X2
+      { CanonID_EOS_1Ds_Mark_II,   "EOS-1Ds Mark II"},
+      { CanonID_EOS_350D,          "EOS 350D"}, // Digital Rebel XT / Kiss Digital N
+      { CanonID_EOS_40D,           "EOS 40D"},
+      { CanonID_EOS_5D,            "EOS 5D"},
+      { CanonID_EOS_1Ds_Mark_III,  "EOS-1Ds Mark III"},
+      { CanonID_EOS_5D_Mark_II,    "EOS 5D Mark II"},
+      { CanonID_EOS_1D_Mark_II_N,  "EOS-1D Mark II N"},
+      { CanonID_EOS_30D,           "EOS 30D"},
+      { CanonID_EOS_400D,          "EOS 400D"}, // Digital Rebel XTi / Kiss Digital X
+      { CanonID_EOS_7D,            "EOS 7D"},
+      { CanonID_EOS_500D,          "EOS 500D"},   // Rebel T1i / Kiss X3
+      { CanonID_EOS_1000D,         "EOS 1000D"}, // Digital Rebel XS / Kiss F
+      { CanonID_EOS_50D,           "EOS 50D"},
+      { CanonID_EOS_1D_X,          "EOS-1D X"},
+      { CanonID_EOS_550D,          "EOS 550D"}, // Rebel T2i / Kiss X4
+      { CanonID_EOS_1D_Mark_IV,    "EOS-1D Mark IV"},
+      { CanonID_EOS_5D_Mark_III,   "EOS 5D Mark III"},
+      { CanonID_EOS_600D,          "EOS 600D"}, // Rebel T3i / Kiss X5
+      { CanonID_EOS_60D,           "EOS 60D"},
+      { CanonID_EOS_1100D,         "EOS 1100D"}, // Rebel T3 / Kiss X50
+      { CanonID_EOS_7D_Mark_II,    "EOS 7D Mark II"},
+      { CanonID_EOS_650D,          "EOS 650D"}, // Rebel T4i / Kiss X6i
+      { CanonID_EOS_6D,            "EOS 6D"},
+      { CanonID_EOS_1D_C,          "EOS-1D C"},
+      { CanonID_EOS_70D,           "EOS 70D"},
+      { CanonID_EOS_700D,          "EOS 700D"},   // Rebel T5i / Kiss X7i
+      { CanonID_EOS_1200D,         "EOS 1200D"}, // Rebel T5 / Kiss X70 / Hi
+      { CanonID_EOS_1D_X_Mark_II,  "EOS-1D X Mark II"},
+      { CanonID_EOS_M,             "EOS M"},
+      { CanonID_EOS_100D,          "EOS 100D"}, // Rebel SL1 / Kiss X7
+      { CanonID_EOS_760D,          "EOS 760D"}, // Rebel T6s / 8000D
+      { CanonID_EOS_5D_Mark_IV,    "EOS 5D Mark IV"},
+      { CanonID_EOS_80D,           "EOS 80D"},
+      { CanonID_EOS_M2,            "EOS M2"},
+      { CanonID_EOS_5DS,           "EOS 5DS"},
+      { CanonID_EOS_750D,          "EOS 750D"}, // Rebel T6i / Kiss X8i
+      { CanonID_EOS_5DS_R,         "EOS 5DS R"},
+      { CanonID_EOS_1300D,         "EOS 1300D"}, // Rebel T6 / Kiss X80
+      { CanonID_EOS_800D,          "EOS 800D"},   // Rebel T7i / Kiss X9i
+      { CanonID_EOS_6D_Mark_II,    "EOS 6D Mark II"},
+      { CanonID_EOS_77D,           "EOS 77D"},     // 9000D
+      { CanonID_EOS_200D,          "EOS 200D"},   // Rebel SL2 / Kiss X9
+      { CanonID_EOS_3000D,         "EOS 3000D"}, // Rebel T100 / 4000D
+      { CanonID_EOS_1D_X_Mark_III, "EOS-1D X Mark III"},
+      { CanonID_EOS_R,             "EOS R"},
+      { CanonID_EOS_1500D,         "EOS 1500D"}, // Rebel T7 / 2000D / Kiss X90
+      { CanonID_EOS_RP,            "EOS RP"},
+      { CanonID_EOS_250D,          "EOS 250D"}, // Rebel SL3 / 200D II / Kiss X10
+      { CanonID_EOS_90D,           "EOS 90D"},
+    },
+#if 0
+    olyque[] = {
+      { OlyID_E_20,            "E-20"},
+      { OlyID_E_20,            "E-20,E-20N,E-20P"},
+      { OlyID_E_1,             "E-1"},
+      { OlyID_E_300,           "E-300"},
+      { OlyID_SP_550UZ,        "SP-550UZ"},
+      { OlyID_SP_550UZ,        "SP550UZ"},
+      { OlyID_SP_510UZ,        "SP-510UZ"},
+      { OlyID_SP_510UZ,        "SP510UZ"},
+      { OlyID_SP_560UZ,        "SP-560UZ"},
+      { OlyID_SP_560UZ,        "SP560UZ"},
+      { OlyID_SP_570UZ,        "SP-570UZ"},
+      { OlyID_SP_570UZ,        "SP570UZ"},
+      { OlyID_SP_565UZ,        "SP-565UZ"},
+      { OlyID_SP_565UZ,        "SP565UZ"},
+      { OlyID_XZ_1,            "XZ-1"},
+      { OlyID_XZ_2,            "XZ-2"},
+      { OlyID_XZ_10,           "XZ-10"},
+      { OlyID_STYLUS_1,        "Stylus 1"},
+      { OlyID_STYLUS_1,        "STYLUS1"},
+      { OlyID_STYLUS_1,        "STYLUS1,1s"},
+      { OlyID_SH_2,            "SH-2"},
+      { OlyID_TG_4,            "TG-4"},
+      { OlyID_TG_5,            "TG-5"},
+      { OlyID_TG_6,            "TG-6"},
+      { OlyID_E_10,            "E-10"},
+      { OlyID_AIR_A01,         "AIR A01"},
+      { OlyID_AIR_A01,         "AIR-A01"},
+      { OlyID_E_330,           "E-330"},
+      { OlyID_E_500,           "E-500"},
+      { OlyID_E_400,           "E-400"},
+      { OlyID_E_510,           "E-510"},
+      { OlyID_E_3,             "E-3"},
+      { OlyID_E_410,           "E-410"},
+      { OlyID_E_420,           "E-420"},
+      { OlyID_E_30,            "E-30"},
+      { OlyID_E_520,           "E-520"},
+      { OlyID_E_P1,            "E-P1"},
+      { OlyID_E_620,           "E-620"},
+      { OlyID_E_P2,            "E-P2"},
+      { OlyID_E_PL1,           "E-PL1"},
+      { OlyID_E_450,           "E-450"},
+      { OlyID_E_600,           "E-600"},
+      { OlyID_E_P3,            "E-P3"},
+      { OlyID_E_5,             "E-5"},
+      { OlyID_E_PL2,           "E-PL2"},
+      { OlyID_E_M5,            "E-M5"},
+      { OlyID_E_PL3,           "E-PL3"},
+      { OlyID_E_PM1,           "E-PM1"},
+      { OlyID_E_PL1s,          "E-PL1s"},
+      { OlyID_E_PL5,           "E-PL5"},
+      { OlyID_E_PM2,           "E-PM2"},
+      { OlyID_E_P5,            "E-P5"},
+      { OlyID_E_PL6,           "E-PL6"},
+      { OlyID_E_PL7,           "E-PL7"},
+      { OlyID_E_M1,            "E-M1"},
+      { OlyID_E_M10,           "E-M10"},
+      { OlyID_E_M5_Mark_II,    "E-M5 Mark II"},
+      { OlyID_E_M5_Mark_II,    "E-M5MarkII"},
+      { OlyID_E_M5_Mark_II,    "E-M5_M2"},
+      { OlyID_E_M10_Mark_II,   "E-M10 Mark II"}, // Clauss piX 5oo
+      { OlyID_E_M10_Mark_II,   "E-M10MarkII"},
+      { OlyID_E_M10_Mark_II,   "E-M10_M2"},
+      { OlyID_PEN_F,           "PEN-F"},
+      { OlyID_E_PL8,           "E-PL8"},
+      { OlyID_E_M1_Mark_II,    "E-M1 Mark II"},
+      { OlyID_E_M1_Mark_II,    "E-M1MarkII"},
+      { OlyID_E_M1_Mark_II,    "E-M1_M2"},
+      { OlyID_E_M10_Mark_III,  "E-M10 Mark III"},
+      { OlyID_E_M10_Mark_III,  "E-M10_M3"},
+      { OlyID_E_PL9,           "E-PL9"},
+      { OlyID_E_M1X,           "E-M1X"},
+      { OlyID_E_PL10,          "E-PL10"},
+      { OlyID_E_M5_Mark_III,   "E-M5 Mark III"},
+      { OlyID_E_M5_Mark_III,   "E-M5MarkIII"},
+      { OlyID_E_M5_Mark_III,   "E-M5_M3"},
+      { OlyID_E_M1_Mark_III,   "E-M1 Mark III"},
+      { OlyID_E_M1_Mark_III,   "E-M1MarkIII"},
+      { OlyID_E_M1_Mark_III,   "E-M1_M3"},
+      { OlyID_C_3030Z,         "C-3030Z"},
+      { OlyID_C_3030Z,         "C3030Z"},
+      { OlyID_C_5050Z,         "C-5050Z"},
+      { OlyID_C_5050Z,         "C5050Z"},
+      { OlyID_C_350Z,          "C-350Z"},
+      { OlyID_C_350Z,          "X200,D560Z,C350Z"},
+      { OlyID_C_740UZ,         "C-740UZ"},
+      { OlyID_C_740UZ,         "C740UZ"},
+      { OlyID_C_5060WZ,        "C-5060WZ"},
+      { OlyID_C_5060WZ,        "C5060WZ"},
+      { OlyID_C_8080WZ,        "C-8080WZ"},
+      { OlyID_C_8080WZ,        "C8080WZ"},
+      { OlyID_C_770UZ,         "C-770UZ"},
+      { OlyID_C_770UZ,         "C770UZ"},
+      { OlyID_C_7070WZ,        "C-7070WZ"},
+      { OlyID_C_7070WZ,        "C7070WZ"},
+      { OlyID_C_7000Z,         "C-7000Z"},
+      { OlyID_C_7000Z,         "C70Z,C7000Z"},
+      { OlyID_SP_500UZ,        "SP-500UZ"},
+      { OlyID_SP_500UZ,        "SP500UZ"},
+      { OlyID_SP_310,          "SP-310"},
+      { OlyID_SP_310,          "SP310"},
+      { OlyID_SP_350,          "SP-350"},
+      { OlyID_SP_350,          "SP350"},
+      { OlyID_SP_320,          "SP-320"},
+      { OlyID_SP_320,          "SP320"},
+    },
+
+    penique[] = {
+      { PentaxID_Optio_S,      "Optio S"},
+      { PentaxID_Optio_S_V101, "Optio S V1.01"},
+      { PentaxID_staristD,     "*istD"},
+      { PentaxID_staristD,     "*ist D"},
+      { PentaxID_Optio_33WR,   "Optio 33WR"},
+      { PentaxID_Optio_S4,     "Optio S4"},
+      { PentaxID_Optio_750Z,   "Optio 750Z"},
+      { PentaxID_staristDS,    "*istDS"},
+      { PentaxID_staristDS,    "*ist DS"},
+      { PentaxID_staristDL,    "*istDL"},
+      { PentaxID_staristDL,    "*ist DL"},
+      { PentaxID_staristDS2,   "*istDS2"},
+      { PentaxID_staristDS2,   "*ist DS2"},
+      { PentaxID_GX_1S,        "GX-1S"},       // Samsung
+      { PentaxID_staristDL2,   "*istDL2"},
+      { PentaxID_staristDL2,   "*ist DL2"},
+      { PentaxID_GX_1L,        "GX-1L"},       // Samsung
+      { PentaxID_K100D,        "K100D"},
+      { PentaxID_K110D,        "K110D"},
+      { PentaxID_K100D_Super,  "K100D Super"},
+      { PentaxID_K10D,         "K10D"},
+      { PentaxID_GX10,         "GX10"},        // Samsung
+      { PentaxID_GX10,         "GX-10"},       // Samsung
+      { PentaxID_K20D,         "K20D"},
+      { PentaxID_GX20,         "GX20"},        // Samsung
+      { PentaxID_GX20,         "GX-20"},       // Samsung
+      { PentaxID_K200D,        "K200D"},
+      { PentaxID_K2000,        "K2000"},
+      { PentaxID_K_m,          "K-m"},
+      { PentaxID_K_7,          "K-7"},
+      { PentaxID_K_x,          "K-x"},
+      { PentaxID_645D,         "645D"},
+      { PentaxID_K_r,          "K-r"},
+      { PentaxID_K_5,          "K-5"},
+      { PentaxID_Q,            "Q"},
+      { PentaxID_K_01,         "K-01"},
+      { PentaxID_K_30,         "K-30"},
+      { PentaxID_Q10,          "Q10"},
+      { PentaxID_K_5_II,       "K-5 II"},
+      { PentaxID_K_5_II_s,     "K-5 II s"},
+      { PentaxID_Q7,           "Q7"},
+      { PentaxID_MX_1,         "MX-1"},
+      { PentaxID_K_50,         "K-50"},
+      { PentaxID_K_3,          "K-3"},
+      { PentaxID_K_500,        "K-500"},
+      { PentaxID_645Z,         "645Z"},
+      { PentaxID_K_S1,         "K-S1"},
+      { PentaxID_K_S2,         "K-S2"},        // Ricoh
+      { PentaxID_Q_S1,         "Q-S1"},
+      { PentaxID_K_1,          "K-1"},         // Ricoh
+      { PentaxID_K_3_II,       "K-3 II"},      // Ricoh
+      { PentaxID_GR_III,       "GR III"},      // Ricoh
+      { PentaxID_K_70,         "K-70"},        // Ricoh
+      { PentaxID_KP,           "KP"},          // Ricoh
+      { PentaxID_K_1_Mark_II,  "K-1 Mark II"}, // Ricoh
+    },
+#endif
+    sonique[] = {
+      { SonyID_DSC_R1,         "DSC-R1"},
+      { SonyID_DSLR_A100,      "DSLR-A100"},
+      { SonyID_DSLR_A900,      "DSLR-A900"},
+      { SonyID_DSLR_A700,      "DSLR-A700"},
+      { SonyID_DSLR_A200,      "DSLR-A200"},
+      { SonyID_DSLR_A350,      "DSLR-A350"},
+      { SonyID_DSLR_A300,      "DSLR-A300"},
+      { SonyID_DSLR_A900_APSC, "DSLR-A900"},
+      { SonyID_DSLR_A380,      "DSLR-A380"},    // DSLR-A390
+      { SonyID_DSLR_A330,      "DSLR-A330"},
+      { SonyID_DSLR_A230,      "DSLR-A230"},
+      { SonyID_DSLR_A290,      "DSLR-A290"},
+      { SonyID_DSLR_A850,      "DSLR-A850"},
+      { SonyID_DSLR_A850_APSC, "DSLR-A850"},
+      { SonyID_DSLR_A550,      "DSLR-A550"},
+      { SonyID_DSLR_A500,      "DSLR-A500"},
+      { SonyID_DSLR_A450,      "DSLR-A450"},
+      { SonyID_NEX_5,          "NEX-5"},
+      { SonyID_NEX_3,          "NEX-3"},
+      { SonyID_SLT_A33,        "SLT-A33"},
+      { SonyID_SLT_A55,        "SLT-A55"},      // SLT-A55V
+      { SonyID_DSLR_A560,      "DSLR-A560"},
+      { SonyID_DSLR_A580,      "DSLR-A580"},
+      { SonyID_NEX_C3,         "NEX-C3"},
+      { SonyID_SLT_A35,        "SLT-A35"},
+      { SonyID_SLT_A65,        "SLT-A65"},      // SLT-A65V
+      { SonyID_SLT_A77,        "SLT-A77"},      // SLT-A77V
+      { SonyID_NEX_5N,         "NEX-5N"},
+      { SonyID_NEX_7,          "NEX-7"},        // Hasselblad Lunar
+      { SonyID_NEX_VG20,       "NEX-VG20"},
+      { SonyID_SLT_A37,        "SLT-A37"},
+      { SonyID_SLT_A57,        "SLT-A57"},
+      { SonyID_NEX_F3,         "NEX-F3"},
+      { SonyID_SLT_A99,        "SLT-A99"},      // SLT-A99V / Hasselblad HV
+      { SonyID_NEX_6,          "NEX-6"},
+      { SonyID_NEX_5R,         "NEX-5R"},
+      { SonyID_DSC_RX100,      "DSC-RX100"},    // Hasselblad Stellar
+      { SonyID_DSC_RX1,        "DSC-RX1"},
+      { SonyID_NEX_VG900,      "NEX-VG900"},
+      { SonyID_NEX_VG30,       "NEX-VG30"},
+      { SonyID_ILCE_3000,      "ILCE-3000"},    // ILCE-3500
+      { SonyID_SLT_A58,        "SLT-A58"},
+      { SonyID_NEX_3N,         "NEX-3N"},
+      { SonyID_ILCE_7,         "ILCE-7"},
+      { SonyID_NEX_5T,         "NEX-5T"},
+      { SonyID_DSC_RX100M2,    "DSC-RX100M2"},  // Hasselblad Stellar II
+      { SonyID_DSC_RX10,       "DSC-RX10"},
+      { SonyID_DSC_RX1R,       "DSC-RX1R"},
+      { SonyID_ILCE_7R,        "ILCE-7R"},      // Hasselblad Lusso
+      { SonyID_ILCE_6000,      "ILCE-6000"},
+      { SonyID_ILCE_5000,      "ILCE-5000"},
+      { SonyID_DSC_RX100M3,    "DSC-RX100M3"},
+      { SonyID_ILCE_7S,        "ILCE-7S"},
+      { SonyID_ILCA_77M2,      "ILCA-77M2"},
+      { SonyID_ILCE_5100,      "ILCE-5100"},
+      { SonyID_ILCE_7M2,       "ILCE-7M2"},
+      { SonyID_DSC_RX100M4,    "DSC-RX100M4"},
+      { SonyID_DSC_RX10M2,     "DSC-RX10M2"},
+      { SonyID_DSC_RX1RM2,     "DSC-RX1RM2"},
+      { SonyID_ILCE_QX1,       "ILCE-QX1"},
+      { SonyID_ILCE_7RM2,      "ILCE-7RM2"},
+      { SonyID_ILCE_7SM2,      "ILCE-7SM2"},
+      { SonyID_ILCA_68,        "ILCA-68"},
+      { SonyID_ILCA_99M2,      "ILCA-99M2"},
+      { SonyID_DSC_RX10M3,     "DSC-RX10M3"},
+      { SonyID_DSC_RX100M5,    "DSC-RX100M5"},
+      { SonyID_ILCE_6300,      "ILCE-6300"},
+      { SonyID_ILCE_9,         "ILCE-9"},
+      { SonyID_ILCE_6500,      "ILCE-6500"},
+      { SonyID_ILCE_7RM3,      "ILCE-7RM3"},
+      { SonyID_ILCE_7M3,       "ILCE-7M3"},
+      { SonyID_DSC_RX0,        "DSC-RX0"},
+      { SonyID_DSC_RX10M4,     "DSC-RX10M4"},
+      { SonyID_DSC_RX100M6,    "DSC-RX100M6"},
+      { SonyID_DSC_HX99,       "DSC-HX99"},
+      { SonyID_DSC_RX100M5A,   "DSC-RX100M5A"},
+      { SonyID_ILCE_6400,      "ILCE-6400"},
+      { SonyID_DSC_RX0M2,      "DSC-RX0M2"},
+      { SonyID_DSC_RX100M7,    "DSC-RX100M7"},
+      { SonyID_ILCE_7RM4,      "ILCE-7RM4"},
+      { SonyID_ILCE_9M2,       "ILCE-9M2"},
+      { SonyID_ILCE_6600,      "ILCE-6600"},
+      { SonyID_ILCE_6100,      "ILCE-6100"},
+    };
+
+  static const char *orig;
+
+  static const char fujialias[][16] = {
+    "@DBP for GX680", "DX-2000",
+    "@F500EXR", "F505EXR",
+    "@F600EXR", "F605EXR",
+    "@F770EXR", "F775EXR",
+    "@HS10", "HS10 HS11",
+    "@HS20EXR", "HS22EXR",
+    "@HS30EXR", "HS33EXR", "HS35EXR",
+    "@S5100", "S5500",
+    "@S5200", "S5600",
+    "@S6000fd", "S6500fd",
+    "@S9000", "S9500",
+    "@S9100", "S9600",
+    "@S200EXR", "S205EXR",
+    "@X-T1 IR", "X-T1IR",
+  };
+
+  static const char kodakalias[][16] = {
+    "@DCS Pro 14N", "Camerz ZDS 14", // Camerz rebadge make: "Photo Control"
+    "@DCS720X", "SCS2000",
+    "@DCS520C", "EOS D2000C", "EOS D2000", // EOS rebadge make: Canon
+    "@DCS560C", "EOS D6000C", "EOS D6000", // EOS rebadge make: Canon
+    "@DCS460M", "DCS460A", // 'A' was supposed to stand for 'achromatic', marketing changed it to 'M'
+    "@DCS460",  "DCS460C", "DCS460D",
+    "@DCS465",  "DCS465C", "DCS465D",
+    "@EOSDCS1", "EOSDCS1B", "EOSDCS1C",
+    "@EOSDCS3", "EOSDCS3B", "EOSDCS3C",
+  };
+
+  static const struct
+  {
+    const char *Kmodel;
+    ushort mount;
+  } Kodak_mounts[] = {
+      {"DCS465", LIBRAW_MOUNT_DigitalBack},
+      {"DCS5", LIBRAW_MOUNT_Canon_EF},
+      {"DCS Pro SLR/c", LIBRAW_MOUNT_Canon_EF},
+      {"DCS", LIBRAW_MOUNT_Nikon_F},
+      {"EOS", LIBRAW_MOUNT_Canon_EF},
+      {"NC2000", LIBRAW_MOUNT_Nikon_F}, // AP "News Camera"
+      {"Pixpro S-1", LIBRAW_MOUNT_mFT},
+      {"ProBack", LIBRAW_MOUNT_DigitalBack},
+      {"SCS1000", LIBRAW_MOUNT_Canon_EF},
+  };
+
+  static const char *KodakMonochrome[] = {
+      "DCS420M",    "DCS420A",  "DCS420I",
+      "DCS460M",    "DCS460A",  "DCS460I",
+      "DCS465M",    "DCS465A",  "DCS465I",
+      "DCS560M",    "DCS660M",  "DCS760M", "EOS D2000M", "EOS D6000M",
+      "EOSDCS1M",   "EOSDCS1I",
+      "EOSDCS3M",   "EOSDCS3I",
+      "EOSDCS5M",   "EOSDCS5I",
+      "NC2000M",    "NC2000A",  "NC2000I",
+  };
+
+  static const char leafalias[][16] = {
+      // Leaf re-badged to Mamiya
+    "@Aptus-II 5",  "DM22",
+    "@Aptus-II 6",  "DM28",
+    "@Aptus-II 7",  "DM33",
+    "@Aptus-II 8",  "DM40",
+    "@Aptus-II 10", "DM56",
+  };
+
+  static const char KonicaMinolta_aliases[][24] = {
+    "@DG-5D", "DYNAX 5D", "MAXXUM 5D", "ALPHA-5 DIGITAL", "ALPHA SWEET DIGITAL",
+    "@DG-7D", "DYNAX 7D", "MAXXUM 7D", "ALPHA-7 DIGITAL",
+  };
+
+  static const char nikonalias[][16] = {
+      "@COOLPIX 2100",  "E2100",         "@COOLPIX 2500",  "E2500",
+      "@COOLPIX 3200",  "E3200",         "@COOLPIX 3700",  "E3700",
+      "@COOLPIX 4300",  "E4300",         "@COOLPIX 4500",  "E4500",
+      "@COOLPIX 5000",  "E5000",         "@COOLPIX 5400",  "E5400",
+      "@COOLPIX 5700",  "E5700",         "@COOLPIX 8400",  "E8400",
+      "@COOLPIX 8700",  "E8700",         "@COOLPIX 8800",  "E8800",
+      "@COOLPIX 700",   "E700",          "@COOLPIX 800",   "E800",
+      "@COOLPIX 880",   "E880",          "@COOLPIX 900",   "E900",
+      "@COOLPIX 950",   "E950",          "@COOLPIX 990",   "E990",
+      "@COOLPIX 995",   "E995",          "@COOLPIX P7700", "COOLPIX Deneb",
+      "@COOLPIX P7800", "COOLPIX Kalon",
+  };
+
+  static const char olyalias[][32] = { // Olympus
+    "@AIR A01", "AIR-A01",
+    "@C-3030Z", "C3030Z",
+    "@C-5050Z", "C5050Z",
+    "@C-5060WZ", "C5060WZ",
+    "@C-7000Z", "C7000Z", "C70Z,C7000Z", "C70Z",
+    "@C-7070WZ", "C7070WZ",
+    "@C-8080WZ", "C8080WZ",
+    "@C-350Z", "C350Z", "X200,D560Z,C350Z", "X200", "D560Z",
+    "@C-740UZ", "C740UZ",
+    "@C-770UZ", "C770UZ",
+    "@E-20", "E-20,E-20N,E-20P", "E-20N", "E-20P",
+    "@E-M10 Mark II", "E-M10MarkII", "E-M10_M2", "piX 5oo",
+    "@E-M10 Mark III", "E-M10MarkIII", "E-M10_M3",
+    "@E-M1 Mark II", "E-M1MarkII", "E-M1_M2",
+    "@E-M1 Mark III", "E-M1MarkIII", "E-M1_M3",
+    "@E-M5 Mark III", "E-M5MarkIII", "E-M5_M3",
+    "@E-M5 Mark II", "E-M5MarkII", "E-M5_M2",
+    "@SH-2", "SH-3",
+    "@SP-310", "SP310",
+    "@SP-320", "SP320",
+    "@SP-350", "SP350",
+    "@SP-500UZ", "SP500UZ",
+    "@SP-510UZ", "SP510UZ",
+    "@SP-550UZ", "SP550UZ",
+    "@SP-560UZ", "SP560UZ",
+    "@SP-565UZ", "SP565UZ",
+    "@SP-570UZ", "SP570UZ",
+    "@Stylus 1", "STYLUS1", "STYLUS1s", "STYLUS1,1s",
+  };
+
+  static const char panalias[][16] = { // Panasonic, PanaLeica
+// fixed lens
+    "@DMC-FX150", "DMC-FX180",
+    "@DC-FZ1000M2", "DC-FZ10002", "V-Lux 5",
+    "@DMC-FZ1000", "V-LUX (Typ 114)",
+    "@DMC-FZ2500", "DMC-FZ2000", "DMC-FZH1",
+    "@DMC-FZ100", "V-LUX 2",
+    "@DMC-FZ150", "V-LUX 3",
+    "@DMC-FZ200", "V-LUX 4",
+    "@DMC-FZ300", "DMC-FZ330",
+    "@DMC-FZ35", "DMC-FZ38",
+    "@DMC-FZ40", "DMC-FZ42", "DMC-FZ45", "DC-FZ40", "DC-FZ42", "DC-FZ45",
+    "@DMC-FZ50", "V-LUX 1", "V-LUX1",
+    "@DMC-FZ70", "DMC-FZ72",
+    "@DC-FZ80", "DC-FZ81", "DC-FZ82", "DC-FZ83", "DC-FZ85",
+    "@DMC-LC1", "DIGILUX 2", "Digilux 2", "DIGILUX2",
+    "@DMC-LF1", "C (Typ 112)",
+    "@DC-LX100M2", "D-Lux 7",
+    "@DMC-LX100", "D-LUX (Typ 109)", "D-Lux (Typ 109)",
+    "@DMC-LX1", "D-Lux2", "D-LUX2", "D-LUX 2",
+    "@DMC-LX2", "D-LUX 3", "D-LUX3",
+    "@DMC-LX3", "D-LUX 4",
+    "@DMC-LX5", "D-LUX 5",
+    "@DMC-LX7", "D-LUX 6",
+    "@DMC-LX9", "DMC-LX10", "DMC-LX15",
+    "@DMC-ZS100", "DMC-ZS110", "DMC-TZ100", "DMC-TZ101", "DMC-TZ110", "DMC-TX1",
+    "@DC-ZS200", "DC-ZS220", "DC-TZ200", "DC-TZ202", "DC-TZ220", "DC-TX2", "C-Lux", "CAM-DC25",
+    "@DMC-ZS40", "DMC-TZ60", "DMC-TZ61",
+    "@DMC-ZS50", "DMC-TZ70", "DMC-TZ71",
+    "@DMC-ZS60", "DMC-TZ80", "DMC-TZ81", "DMC-TZ82", "DMC-TZ85",
+    "@DC-ZS70", "DC-TZ90", "DC-TZ91", "DC-TZ92", "DC-TZ93",
+    "@DC-ZS80", "DC-TZ95", "DC-TZ96", "DC-TZ97",
+
+// interchangeable lens
+    "@DC-G99",   "DC-G90",   "DC-G91",  "DC-G95",
+    "@DMC-G7",   "DMC-G70",
+    "@DMC-G8",   "DMC-G80",  "DMC-G81", "DMC-G85",
+    "@DMC-GH4",  "AG-GH4",   "CGO4",
+    "@DC-GF10",  "DC-GF90",  "DC-GX880",
+    "@DC-GF9",   "DC-GX850", "DC-GX800",
+    "@DMC-GM1",  "DMC-GM1S",
+    "@DMC-GX85", "DMC-GX80", "DMC-GX7MK2",
+    "@DC-GX9",   "DC-GX7MK3",
+    "@DMC-L1",   "DIGILUX 3", "DIGILUX3", // full 4/3 mount, not m43
+  };
+
+  static const char phase1alias[][16] = {
+    "@H20",  "H 20",
+    "@H25",  "H 25",
+    "@P20+", "P 20+",
+    "@P20",  "P 20",
+    "@P21+", "P 21+", "M18", // "Mamiya M18"
+    "@P21",  "P 21",
+    "@P25+", "P 25+", "M22", // "Mamiya M22"
+    "@P25",  "P 25",
+    "@P30+", "P 30+", "M31", // "Mamiya M31"
+    "@P30",  "P 30",
+    "@P40+", "P 40+",
+    "@P40",  "P 40",
+    "@P45+", "P 45+",
+    "@P45",  "P 45",
+    "@P65+", "P 65+",
+    "@P65",  "P 65",
+  };
+
+  static const char SamsungPentax_aliases[][16] = {
+    "@*istDL2", "*ist DL2", "GX-1L",
+    "@*istDS2", "*ist DS2", "GX-1S",
+    "@*istDL",  "*ist DL",
+    "@*istDS",  "*ist DS",
+    "@*istD",   "*ist D",
+    "@K10D", "GX10", "GX-10",
+    "@K20D", "GX20", "GX-20",
+    "@K-m", "K2000",
+  };
+
+  static const char samsungalias[][64] = {
+    "@EX1", "TL500",
+    "@NX U", "EK-GN100", "EK-GN110", "EK-GN120", "EK-KN120", "Galaxy NX",
+    "@NX mini", "NXF1",
+    "@WB2000", "TL350",
+      //    "@WB5000", "WB5000/HZ25W", // no spaces around the slash separating names
+      //    "@WB5500", "WB5500 / VLUU WB5500 / SAMSUNG HZ50W",
+      //    "@WB500", "WB510 / VLUU WB500 / SAMSUNG HZ10W",
+      //    "@WB550", "WB560 / VLUU WB550 / SAMSUNG HZ15W",
+      //    "@WB650", "SAMSUNG WB650 / VLUU WB650 / SAMSUNG WB660" aka HZ35W
+  };
+
+//clang-format on
+  if (makeIs(LIBRAW_CAMERAMAKER_VLUU)) {
+	  setMakeFromIndex(LIBRAW_CAMERAMAKER_Samsung);
+  }
+
+  if (makeIs(LIBRAW_CAMERAMAKER_Samsung) &&
+      (ilm.CameraMount == LIBRAW_MOUNT_Pentax_K)) {
+	  setMakeFromIndex(LIBRAW_CAMERAMAKER_Pentax);
+
+  } else if (makeIs(LIBRAW_CAMERAMAKER_Unknown)) {
+    if (strcasestr(model, "Google")) {
+		  setMakeFromIndex(LIBRAW_CAMERAMAKER_Google);
+    }
+#ifdef USE_6BY9RPI
+	else if(strcasestr(make,"RaspberryPi"))
+		setMakeFromIndex(LIBRAW_CAMERAMAKER_Broadcom);
+#endif
+  }
+  else if (makeIs(LIBRAW_CAMERAMAKER_Hasselblad) && is_Sony)
+  {
+	  setMakeFromIndex(LIBRAW_CAMERAMAKER_Sony);
+  }
+  else if (makeIs(LIBRAW_CAMERAMAKER_Clauss) && (OlyID == OlyID_E_M10_Mark_II))
+  {
+	  setMakeFromIndex(LIBRAW_CAMERAMAKER_Olympus);
+
+  } else if (makeIs(LIBRAW_CAMERAMAKER_Canon) &&
+             (!strncmp(model, "EOS D2000", 9) || // don't use unique_id here
+              !strncmp(model, "EOS D6000", 9) || // because ids for Monochrome models are unknown
+              !strncmp(model, "EOSDCS", 6))) {
+    setMakeFromIndex(LIBRAW_CAMERAMAKER_Kodak);
+//    if (unique_id == CanonID_EOS_D2000C) {
+//
+//    } else if (unique_id  == CanonID_EOS_D6000C) {
+///
+//    }
+
+  } else if (makeIs(LIBRAW_CAMERAMAKER_PhotoControl) &&
+             !strncasecmp(model, "Camerz ZDS 14", 13)) {
+	  setMakeFromIndex(LIBRAW_CAMERAMAKER_Kodak);
+
+  } else {
+    strcpy(normalized_make, make);
+  }
+
+  if (makeIs(LIBRAW_CAMERAMAKER_Apple)) {
+    if ((imgdata.color.UniqueCameraModel[0]) &&
+        (!strncmp(model, "iPad", 4) || !strncmp(model, "iPhone", 6)))
+    strcpy(model, imgdata.color.UniqueCameraModel);
+
+  } else if (makeIs(LIBRAW_CAMERAMAKER_Kodak)) {
+    if ((model[6] == ' ') &&
+        (!strncmp(model, "DCS4", 4) ||
+         !strncmp(model, "NC2000", 6)))
+    {
+      model[6] = 0;
+    }
+    if ((model[6] != 'A') &&
+        (model[6] != 'I') &&
+        (model[6] != 'M') &&
+        !strncmp(model, "NC2000", 6))
+    {
+      model[6] = 0;
+    }
+  }
+
+  else if (makeIs(LIBRAW_CAMERAMAKER_Ricoh) &&
+           !strncmp(model, "GXR", 3)) {
+    strcpy(ilm.body, "Ricoh GXR");
+    if (!imgdata.lens.Lens[0] && imgdata.color.UniqueCameraModel[0]) {
+      strcpy (imgdata.lens.Lens, imgdata.color.UniqueCameraModel);
+      remove_caseSubstr (imgdata.lens.Lens, (char *)"Ricoh");
+      remove_caseSubstr (imgdata.lens.Lens, (char *)"Lens");
+      removeExcessiveSpaces (imgdata.lens.Lens);
+    }
+    if (ilm.LensID == LIBRAW_LENS_NOT_SET) {
+      if (strstr(imgdata.lens.Lens, "50mm"))
+        ilm.LensID = 1;
+      else if (strstr(imgdata.lens.Lens, "S10"))
+        ilm.LensID = 2;
+      else if (strstr(imgdata.lens.Lens, "P10"))
+        ilm.LensID = 3;
+      else if (strstr(imgdata.lens.Lens, "28mm"))
+        ilm.LensID = 5;
+      else if (strstr(imgdata.lens.Lens, "A16"))
+        ilm.LensID = 6;
+    }
+    switch (ilm.LensID) {
+    case 1: // GR Lens A12 50mm F2.5 Macro
+      strcpy(model, "GXR A12 50mm");
+      ilm.CameraFormat = ilm.LensFormat = LIBRAW_FORMAT_APSC;
+      ilm.CameraMount = LIBRAW_MOUNT_RicohModule;
+      ilm.LensMount = LIBRAW_MOUNT_FixedLens;
+      ilm.FocalType = LIBRAW_FT_PRIME_LENS;
+      break;
+    case 2:
+      strcpy(model, "GXR S10");
+      ilm.CameraFormat = ilm.LensFormat = LIBRAW_FORMAT_1div1p7INCH;
+      ilm.CameraMount = LIBRAW_MOUNT_RicohModule;
+      ilm.LensMount = LIBRAW_MOUNT_FixedLens;
+      ilm.FocalType = LIBRAW_FT_ZOOM_LENS;
+      break;
+    case 3: // Ricoh Lens P10 28-300mm F3.5-5.6 VC
+      strcpy(model, "GXR P10");
+      ilm.CameraFormat = ilm.LensFormat = LIBRAW_FORMAT_1div2p3INCH;
+      ilm.CameraMount = LIBRAW_MOUNT_RicohModule;
+      ilm.LensMount = LIBRAW_MOUNT_FixedLens;
+      ilm.FocalType = LIBRAW_FT_ZOOM_LENS;
+      break;
+    case 5: // GR Lens A12 28mm F2.5
+      strcpy(model, "GXR A12 28mm");
+      ilm.CameraFormat = ilm.LensFormat = LIBRAW_FORMAT_APSC;
+      ilm.CameraMount = LIBRAW_MOUNT_RicohModule;
+      ilm.LensMount = LIBRAW_MOUNT_FixedLens;
+      ilm.FocalType = LIBRAW_FT_PRIME_LENS;
+      break;
+    case 6: // Ricoh Lens A16 24-85mm F3.5-5.5
+      strcpy(model, "GXR A16");
+      ilm.CameraFormat = ilm.LensFormat = LIBRAW_FORMAT_APSC;
+      ilm.CameraMount = LIBRAW_MOUNT_RicohModule;
+      ilm.LensMount = LIBRAW_MOUNT_FixedLens;
+      ilm.FocalType = LIBRAW_FT_ZOOM_LENS;
+      break;
+    case 8: // Ricoh Mount A12 (Leica M lenses)
+      strcpy(model, "GXR Mount A12");
+      ilm.CameraFormat = LIBRAW_FORMAT_APSC;
+      ilm.CameraMount = LIBRAW_MOUNT_Leica_M;
+      ilm.LensID = LIBRAW_LENS_NOT_SET;
+      break;
+    }
+  }
+
+  strcpy(normalized_model, model);
+
+  if (makeIs(LIBRAW_CAMERAMAKER_Canon))
+  {
+    if ((unique_id) && (unique_id != CanonID_EOS_D2000C) && (unique_id != CanonID_EOS_D6000C))
+    {
+      for (i = 0; i < int(sizeof unique / sizeof *unique); i++)
+      {
+        if (unique_id == unique[i].id)
+        {
+          strcpy(model, unique[i].t_model);
+          strcpy(normalized_model, unique[i].t_model);
+          break;
+        }
+      }
+    }
+  }
+  else if (makeIs(LIBRAW_CAMERAMAKER_Fujifilm))
+  {
+    for (i = 0; i < int(sizeof fujialias / sizeof *fujialias); i++)
+    {
+      if (fujialias[i][0] == '@')
+      {
+        orig = fujialias[i] + 1;
+        if (!strcmp(model, orig)) break;
+      }
+      else if (!strcmp(model, fujialias[i]))
+      {
+        strcpy(normalized_model, orig);
+        break;
+      }
+    }
+
+  } else if (makeIs(LIBRAW_CAMERAMAKER_Hasselblad)) {
+    parseHassyModel();
+  }
+  else if (makeIs(LIBRAW_CAMERAMAKER_Mamiya))
+  {
+    for (i = 0; i < int(sizeof phase1alias / sizeof *phase1alias); i++)
+    { // re-badged Phase One backs
+      if (phase1alias[i][0] == '@') orig = phase1alias[i] + 1;
+      else if (!strcmp(model, phase1alias[i]))
+      {
+        setMakeFromIndex(LIBRAW_CAMERAMAKER_PhaseOne);
+        strcpy(normalized_model, orig);
+        break;
+      }
+    }
+    for (i = 0; i < int(sizeof leafalias / sizeof *leafalias); i++)
+    { // re-badged Leaf backs
+      if (leafalias[i][0] == '@') orig = leafalias[i] + 1;
+      else if (!strcmp(model, leafalias[i]))
+      {
+        setMakeFromIndex(LIBRAW_CAMERAMAKER_Leaf);
+        strcpy(normalized_model, orig);
+        break;
+      }
+    }
+
+    /* repeating, because make for some Mamiya re-badged Leaf backs is set to
+     * Leaf */
+  }
+  else if (makeIs(LIBRAW_CAMERAMAKER_Leaf))
+  {
+    for (i = 0; i < int(sizeof leafalias / sizeof *leafalias); i++)
+    { // re-badged Leaf backs
+      if (leafalias[i][0] == '@')
+      {
+        orig = leafalias[i] + 1;
+        if (!strcmp(model, orig)) break;
+      }
+      else if (!strcmp(model, leafalias[i]))
+      { // maybe to change regular "make" to "Mamiya" too
+        strcpy(normalized_model, orig);
+        break;
+      }
+    }
+    if ((ps = strchr(normalized_model, '(')))
+      *ps = 0;
+  }
+  else if (makeIs(LIBRAW_CAMERAMAKER_Minolta) ||
+           makeIs(LIBRAW_CAMERAMAKER_Konica))
+  {
+    if (makeIs(LIBRAW_CAMERAMAKER_Konica) && !strncasecmp(model, "DiMAGE", 6))
+    {
+      setMakeFromIndex(LIBRAW_CAMERAMAKER_Minolta);
+      strcpy(make, "Minolta");
+    }
+    else
+    {
+      for (i = 0;
+           i<int(sizeof KonicaMinolta_aliases / sizeof *KonicaMinolta_aliases);
+           i++)
+      {
+        if (KonicaMinolta_aliases[i][0] == '@')
+        {
+          orig = KonicaMinolta_aliases[i] + 1;
+          if (!strcmp(model, orig))
+          {
+            setMakeFromIndex(LIBRAW_CAMERAMAKER_Minolta);
+            strcpy(make, "Minolta");
+            break;
+          }
+        }
+        else if (!strcasecmp(model, KonicaMinolta_aliases[i]))
+        {
+          setMakeFromIndex(LIBRAW_CAMERAMAKER_Minolta);
+          strcpy(make, "Minolta");
+          strcpy(normalized_model, orig);
+          break;
+        }
+      }
+    }
+  }
+  else if (makeIs(LIBRAW_CAMERAMAKER_Nikon))
+  {
+    for (i = 0; i < int(sizeof nikonalias / sizeof *nikonalias); i++)
+    {
+      if (nikonalias[i][0] == '@')
+      {
+        orig = nikonalias[i] + 1;
+        if (!strcmp(model, orig)) break;
+      }
+      else if (!strcmp(model, nikonalias[i]))
+      {
+        strcpy(normalized_model, orig);
+        break;
+      }
+    }
+
+  } else if (makeIs(LIBRAW_CAMERAMAKER_Olympus)) {
+    for (i = 0; i < int(sizeof olyalias / sizeof *olyalias); i++) {
+      if (olyalias[i][0] == '@') {
+        orig = olyalias[i] + 1;
+        if (!strcmp(model, orig)) break;
+      } else if (!strcmp(model, olyalias[i])) {
+        strcpy(normalized_model, orig);
+        break;
+      }
+    }
+
+    if (!OlyID) {
+      if (!strcmp(normalized_model, "C-740UZ")) {
+        ilm.CamID = OlyID = unique_id = OlyID_C_740UZ;
+
+      } else if (!strcmp(normalized_model, "C-770UZ")) {
+        ilm.CamID = OlyID = unique_id = OlyID_C_770UZ;
+      }
+    }
+
+  } else if (makeIs(LIBRAW_CAMERAMAKER_Panasonic) ||
+             makeIs(LIBRAW_CAMERAMAKER_Leica) ||
+             makeIs(LIBRAW_CAMERAMAKER_Yuneec))
+  {
+    for (i = 0; i < int(sizeof panalias / sizeof *panalias); i++)
+    {
+      if (panalias[i][0] == '@')
+      {
+        orig = panalias[i] + 1;
+        if (!strcmp(model, orig)) break;
+      }
+      else if (!strcmp(model, panalias[i]))
+      {
+        setMakeFromIndex(LIBRAW_CAMERAMAKER_Panasonic);
+        strcpy(normalized_model, orig);
+        break;
+      }
+    }
+  } else if (makeIs(LIBRAW_CAMERAMAKER_Pentax)) {
+
+    if (!unique_id) {
+      if (!strcmp(model, "Optio S")) {
+        ilm.CamID = unique_id = PentaxID_Optio_S;
+      } else if (!strcmp(model, "Optio S V1.01")) {
+        ilm.CamID = unique_id = PentaxID_Optio_S_V101;
+      } else if (!strcmp(model, "Optio S4")) {
+        ilm.CamID = unique_id = PentaxID_Optio_S4;
+      } else if (!strcmp(model, "Optio 750Z")) {
+        ilm.CamID = unique_id = PentaxID_Optio_750Z;
+      } else if (!strcmp(model, "Optio 33WR")) {
+        ilm.CamID = unique_id = PentaxID_Optio_33WR;
+      }
+    }
+
+    for (i = 0;
+    i < int(sizeof SamsungPentax_aliases / sizeof *SamsungPentax_aliases);
+    i++) {
+      if (SamsungPentax_aliases[i][0] == '@') {
+        orig = SamsungPentax_aliases[i] + 1;
+        if (!strcmp(model, orig)) break;
+      } else if (!strcmp(model, SamsungPentax_aliases[i])) {
+        strcpy(normalized_model, orig);
+        break;
+      }
+    }
+    if (!strncmp(model, "GR", 2)) {
+	  setMakeFromIndex(LIBRAW_CAMERAMAKER_Ricoh);
+      strcpy(make, "Ricoh");
+    }
+
+  } else if (makeIs(LIBRAW_CAMERAMAKER_PhaseOne))
+  {
+    for (i = 0; i < int(sizeof phase1alias / sizeof *phase1alias); i++)
+    {
+      if (phase1alias[i][0] == '@')
+      {
+        orig = phase1alias[i] + 1;
+        if (!strcmp(model, orig)) break;
+      }
+      else if (!strcmp(model, phase1alias[i]))
+      {
+        strcpy(normalized_model, orig);
+        break;
+      }
+    }
+  }
+  else if (makeIs(LIBRAW_CAMERAMAKER_Samsung))
+  {
+    j = 0;
+    if (strstr(model, "WB5500") || strstr(model, "HZ50W"))
+    {
+      strcpy(model, "WB5500");
+      j++;
+    }
+    else if (strstr(model, "WB5000") || strstr(model, "HZ25W"))
+    {
+      strcpy(model, "WB5000");
+      j++;
+    }
+    else if (strstr(model, "WB550") || strstr(model, "HZ15W"))
+    {
+      strcpy(model, "WB550");
+      j++;
+    }
+    else if (strstr(model, "WB500") || strstr(model, "HZ10W"))
+    {
+      strcpy(model, "WB500");
+      j++;
+    }
+    if (j)
+    {
+      strcpy(normalized_model, model);
+    }
+    else
+    {
+      for (i = 0; i < int(sizeof samsungalias / sizeof *samsungalias); i++)
+      {
+        if (samsungalias[i][0] == '@')
+        {
+          orig = samsungalias[i] + 1;
+          if (!strcmp(model, orig)) break;
+        }
+        else if (!strcmp(model, samsungalias[i]))
+        {
+          strcpy(normalized_model, orig);
+          break;
+        }
+      }
+    }
+
+  } else if (makeIs(LIBRAW_CAMERAMAKER_Sony)) {
+    if (unique_id)
+    {
+      for (i = 0; i < int(sizeof sonique / sizeof *sonique); i++)
+      {
+        if (unique_id == sonique[i].id)
+        {
+          if (!strcmp(make, "Sony"))
+            strcpy(model, sonique[i].t_model);
+          strcpy(normalized_model, sonique[i].t_model);
+          break;
+        }
+      }
+    }
+
+  } else if (makeIs(LIBRAW_CAMERAMAKER_Kodak)) {
+    remove_caseSubstr (normalized_model, (char *)"EasyShare");
+    remove_caseSubstr (normalized_model, (char *)"ZOOM");
+    removeExcessiveSpaces (normalized_model);
+    for (i = 0; i < int(sizeof kodakalias / sizeof *kodakalias); i++)
+    {
+      if (kodakalias[i][0] == '@')
+      {
+        orig = kodakalias[i] + 1;
+        if (!strcmp(model, orig)) break;
+      }
+      else if (!strcmp(model, kodakalias[i]))
+      {
+        strcpy(normalized_model, orig);
+        break;
+      }
+    }
+
+    if (strstr(model, "DC25"))
+    {
+      strcpy(model, "DC25");
+      strcpy(normalized_model, model);
+    }
+    else if (!strcmp(model, "40"))
+    {
+      strcpy(model, "DC40");
+      strcpy(normalized_model, model);
+    }
+    else if (strstr(model, "DC50"))
+    {
+      strcpy(model, "DC50");
+      strcpy(normalized_model, model);
+    }
+    else if (strstr(model, "DC120"))
+    {
+      strcpy(model, "DC120");
+      strcpy(normalized_model, model);
+    }
+
+    for (i = 0; i < int(sizeof KodakMonochrome / sizeof *KodakMonochrome); i++)
+    {
+      if (!strncmp(model, KodakMonochrome[i], strlen(KodakMonochrome[i])))
+      {
+        colors = 1;
+        filters = 0;
+      }
+    }
+  }
+
+  if (ilm.body[0])
+  {
+    if ((ilm.CameraMount != LIBRAW_MOUNT_Hasselblad_V) &&
+        !strncmp(ilm.body, "Hasselblad ", 11) &&
+        ((ilm.body[11] == 'C') || (ilm.body[11] == '2') ||
+         (ilm.body[11] == '5') || (ilm.body[11] == '9')))
+    {
+      ilm.CameraFormat = LIBRAW_FORMAT_66;
+      ilm.CameraMount = LIBRAW_MOUNT_Hasselblad_V;
+    }
+    else if (!strncmp(ilm.body, "XF", 2) || !strncmp(ilm.body, "645DF", 5))
+    {
+      ilm.CameraMount = LIBRAW_MOUNT_Mamiya645;
+      ilm.CameraFormat = LIBRAW_FORMAT_645;
+    }
+    else if (!strncmp(ilm.body, "Sinarcam", 2))
+    {
+      ilm.CameraMount = LIBRAW_MOUNT_LF;
+      ilm.CameraFormat = LIBRAW_FORMAT_LF;
+      strcat(ilm.body, " shutter system");
+    }
+  }
+
+  if (makeIs(LIBRAW_CAMERAMAKER_Kodak))
+  {
+    if (((ilm.CameraMount == LIBRAW_MOUNT_DigitalBack) ||
+         (ilm.CameraMount == LIBRAW_MOUNT_Unknown)) &&
+        !strncmp(model2, "PB645", 5))
+    {
+      ilm.CameraFormat = LIBRAW_FORMAT_645;
+      if (model2[5] == 'C')
+      {
+        ilm.CameraMount = LIBRAW_MOUNT_Contax645;
+        strcpy(ilm.body, "Contax 645");
+      }
+      else if (model2[5] == 'H')
+      {
+        ilm.CameraMount = LIBRAW_MOUNT_Hasselblad_H;
+        strcpy(ilm.body, "Hasselblad H1/H2");
+      }
+      else if (model2[5] == 'M')
+      {
+        ilm.CameraMount = LIBRAW_MOUNT_Mamiya645;
+        strcpy(ilm.body, "Mamiya 645");
+      }
+
+    } else if (!strncasecmp(model, "PIXPRO S-1", 10)) {
+      ilm.CameraFormat = LIBRAW_FORMAT_FT;
+    } else if (!strncasecmp(model, "PIXPRO ", 7)) {
+      ilm.CameraFormat = LIBRAW_FORMAT_1div2p3INCH;
+    }
+  }
+
+  else if (makeIs(LIBRAW_CAMERAMAKER_Fujifilm))
+  {
+    if (!strncmp(normalized_model, "DBP", 3))
+    {
+      strcpy(ilm.body, "Fujifilm GX680");
+    }
+  }
+
+  if ((ilm.CameraFormat == LIBRAW_FORMAT_Unknown) ||
+      (ilm.CameraMount == LIBRAW_MOUNT_Unknown) ||
+      (ilm.CameraMount == LIBRAW_MOUNT_IL_UM))
+  {
+
+    if (makeIs(LIBRAW_CAMERAMAKER_Canon))
+    {
+      if (strncmp(normalized_model, "EOS", 3))
+      {
+        ilm.CameraMount = LIBRAW_MOUNT_FixedLens;
+      }
+    }
+    else if (makeIs(LIBRAW_CAMERAMAKER_Nikon))
+    {
+      if (normalized_model[0] == 'D')
+      {
+        ilm.CameraMount = LIBRAW_MOUNT_Nikon_F;
+      }
+      else
+      {
+        ilm.CameraMount = LIBRAW_MOUNT_FixedLens;
+      }
+    }
+    else if (makeIs(LIBRAW_CAMERAMAKER_Panasonic))
+    {
+      if (!strncmp(normalized_model, "DC-S", 4))
+      {
+        ilm.CameraFormat = LIBRAW_FORMAT_FF;
+        ilm.CameraMount = LIBRAW_MOUNT_LPS_L;
+      }
+      else if (!strncmp(normalized_model, "DMC-L1", 6) ||
+               !strncmp(normalized_model, "DMC-L10", 7))
+      {
+        ilm.CameraFormat = ilm.CameraMount = LIBRAW_FORMAT_FT;
+      }
+      else if (!strncmp(normalized_model + 2, "-G", 2) ||
+               !strncmp(normalized_model + 3, "-G", 2))
+      {
+        ilm.CameraFormat = LIBRAW_FORMAT_FT;
+        ilm.CameraMount = LIBRAW_MOUNT_mFT;
+      }
+      else
+      {
+        ilm.CameraMount = LIBRAW_MOUNT_FixedLens;
+        ilm.FocalType = LIBRAW_FT_ZOOM_LENS;
+        if (!strncmp(normalized_model + 2, "-LX100", 6) || // DC-LX100M2
+            !strncmp(normalized_model + 3, "-LX100", 6))
+        { // DMC-LX100
+          ilm.CameraFormat = ilm.LensFormat = LIBRAW_FORMAT_FT;
+        }
+        else if (!strncmp(normalized_model, "DMC-CM1", 7))
+        {
+          ilm.FocalType = LIBRAW_FT_PRIME_LENS;
+        }
+      }
+    }
+    else if (makeIs(LIBRAW_CAMERAMAKER_Fujifilm))
+    {
+      if (!strncmp(normalized_model, "GFX ", 4))
+      {
+        ilm.CameraFormat = LIBRAW_FORMAT_CROP645;
+        ilm.CameraMount = LIBRAW_MOUNT_Fuji_GF;
+      }
+      else if (!strncmp(normalized_model, "X-", 2) &&
+               strncmp(normalized_model, "X-S1", 4))
+      {
+        ilm.CameraFormat = LIBRAW_FORMAT_APSC;
+        ilm.CameraMount = LIBRAW_MOUNT_Fuji_X;
+      }
+      else if (((normalized_model[0] == 'S') && // S2Pro, S3Pro, S5Pro
+                (normalized_model[2] == 'P')) ||
+               !strncasecmp(normalized_model, "IS Pro", 6))
+      {
+        ilm.CameraFormat = LIBRAW_FORMAT_APSC;
+        ilm.CameraMount = LIBRAW_MOUNT_Nikon_F;
+      }
+      else if (!strncmp(normalized_model, "DBP", 3))
+      {
+        ilm.CameraFormat = LIBRAW_FORMAT_68;
+        ilm.CameraMount = LIBRAW_MOUNT_Fuji_GX;
+      }
+      else
+      {
+        ilm.CameraMount = LIBRAW_MOUNT_FixedLens;
+      }
+    }
+    else if (makeIs(LIBRAW_CAMERAMAKER_Samsung))
+    {
+      if ((normalized_model[0] == 'N') && (normalized_model[1] == 'X'))
+      { // DNG converters delete makernotes
+        if ((normalized_model[2] == 'F') && (normalized_model[3] == '1'))
+        {
+          ilm.CameraMount = LIBRAW_MOUNT_Samsung_NX_M;
+          ilm.CameraFormat = LIBRAW_FORMAT_1INCH;
+        }
+        else
+        {
+          ilm.CameraMount = LIBRAW_MOUNT_Samsung_NX;
+          ilm.CameraFormat = LIBRAW_FORMAT_APSC;
+        }
+      }
+      else
+      {
+        ilm.CameraMount = LIBRAW_MOUNT_FixedLens;
+      }
+    }
+    else if (makeIs(LIBRAW_CAMERAMAKER_Kodak))
+    {
+      ilm.CameraMount = LIBRAW_MOUNT_FixedLens;
+      for (i = 0; i < int(sizeof Kodak_mounts / sizeof *Kodak_mounts); i++)
+      {
+        if (!strncmp(normalized_model, Kodak_mounts[i].Kmodel,
+                     strlen(Kodak_mounts[i].Kmodel)))
+        {
+          ilm.CameraMount = Kodak_mounts[i].mount;
+          break;
+        }
+      }
+    }
+    else if (makeIs(LIBRAW_CAMERAMAKER_Minolta))
+    {
+      if (!strcmp(normalized_model, "DG-5D") ||
+          !strcmp(normalized_model, "DG-7D"))
+      {
+        ilm.CameraFormat = LIBRAW_FORMAT_APSC;
+        ilm.CameraMount = LIBRAW_MOUNT_Minolta_A;
+      }
+      else if (!strncasecmp(normalized_model, "DiMAGE", 6))
+      {
+        ilm.CameraMount = LIBRAW_MOUNT_FixedLens;
+      }
+    }
+    else if (makeIs(LIBRAW_CAMERAMAKER_Casio) ||
+             makeIs(LIBRAW_CAMERAMAKER_Creative))
+    {
+      ilm.CameraMount = LIBRAW_MOUNT_FixedLens;
+    }
+    else if (makeIs(LIBRAW_CAMERAMAKER_Sigma))
+    {
+      if (!strncmp(normalized_model, "fp", 2))
+      {
+        ilm.CameraFormat = LIBRAW_FORMAT_FF;
+        ilm.CameraMount = LIBRAW_MOUNT_LPS_L;
+      }
+      else if (!strncasecmp(normalized_model, "SD", 2))
+      {
+        ilm.CameraMount = LIBRAW_MOUNT_Sigma_X3F;
+        if (!strcmp(normalized_model, "SD1") || (normalized_model[4] == 'M'))
+        {
+          ilm.CameraFormat = LIBRAW_FORMAT_SigmaMerrill;
+        }
+        else if (normalized_model[11] == 'H')
+        { // 'sd Quattro H'
+          ilm.CameraFormat = LIBRAW_FORMAT_SigmaAPSH;
+        }
+        else if (normalized_model[4] == 'Q')
+        { // 'sd Quattro'
+          ilm.CameraFormat = LIBRAW_FORMAT_APSC;
+        }
+        else
+        {
+          ilm.CameraFormat = LIBRAW_FORMAT_SigmaAPSC;
+        }
+      }
+      else if (!strncasecmp(normalized_model, "DP", 2))
+      {
+        ilm.CameraMount = LIBRAW_MOUNT_FixedLens;
+        if (normalized_model[4] == 'M')
+        {
+          ilm.CameraFormat = LIBRAW_FORMAT_SigmaMerrill;
+        }
+        else if (normalized_model[4] == 'Q')
+        {
+          ilm.CameraFormat = LIBRAW_FORMAT_APSC;
+        }
+        else
+        {
+          ilm.CameraFormat = LIBRAW_FORMAT_SigmaAPSC;
+        }
+      }
+    }
+    else if (makeIs(LIBRAW_CAMERAMAKER_Konica))
+    {
+      if (!strncmp(model, "KD-", 3))
+      { // Konica KD-400Z, KD-510Z
+        ilm.CameraMount = LIBRAW_MOUNT_FixedLens;
+      }
+    }
+    else if (makeIs(LIBRAW_CAMERAMAKER_Mamiya))
+    {
+      if (!strncmp(normalized_model, "ZD", 2))
+      {
+        ilm.CameraFormat = LIBRAW_FORMAT_3648;
+        ilm.CameraMount = LIBRAW_MOUNT_Mamiya645;
+      }
+    }
+    else if (makeIs(LIBRAW_CAMERAMAKER_Sony))
+    {
+      if (!strncmp(normalized_model, "XCD-", 4))
+      {
+        ilm.CameraMount = LIBRAW_MOUNT_C;
+      }
+      else if (!strncmp(normalized_model, "DSC-V3", 6) ||
+               !strncmp(normalized_model, "DSC-F828", 8))
+      {
+        ilm.CameraMount = LIBRAW_MOUNT_FixedLens;
+        imSony.CameraType = LIBRAW_SONY_DSC;
+      }
+    }
+    else if (makeIs(LIBRAW_CAMERAMAKER_Polaroid) &&
+             !strncmp(normalized_model, "x530", 4))
+    {
+      ilm.CameraMount = LIBRAW_MOUNT_FixedLens;
+    }
+    else if (makeIs(LIBRAW_CAMERAMAKER_Rollei) &&
+             !strncmp(normalized_model, "d530flex", 8))
+    {
+      ilm.CameraMount = LIBRAW_MOUNT_FixedLens;
+    }
+    else if (makeIs(LIBRAW_CAMERAMAKER_Pentax) &&
+             !strncmp(normalized_model, "Optio", 5)) {
+        ilm.CameraMount = LIBRAW_MOUNT_FixedLens;
+    }
+    else if (makeIs(LIBRAW_CAMERAMAKER_Epson) &&
+             !strncmp(normalized_model, "R-D1", 4))
+    {
+      ilm.CameraMount = LIBRAW_MOUNT_Leica_M;
+      ilm.CameraFormat = LIBRAW_FORMAT_APSC;
+    }
+  }
+
+  if ((ilm.LensMount == LIBRAW_MOUNT_Canon_RF) &&
+      (ilm.LensID == 61182)                    &&
+      (imCanon.RF_lensID != 0))                {
+    ilm.LensID = imCanon.RF_lensID;
+  }
+
+  if (ilm.LensMount == LIBRAW_MOUNT_Unknown)
+  {
+    if (makeIs(LIBRAW_CAMERAMAKER_Samsung))
+    {
+      if ((imgdata.lens.Lens[0] == 'N') && (imgdata.lens.Lens[1] == 'X'))
+      { // same DNG problem
+        if (imgdata.lens.Lens[2] == '-')
+        {
+          ilm.LensMount = LIBRAW_MOUNT_Samsung_NX_M;
+          ilm.LensFormat = LIBRAW_FORMAT_1INCH;
+        }
+        else
+        {
+          ilm.LensMount = LIBRAW_MOUNT_Samsung_NX;
+          ilm.LensFormat = LIBRAW_FORMAT_APSC;
+        }
+      }
+    }
+  }
+
+  if (ilm.LensID == LIBRAW_LENS_NOT_SET)
+  {
+    if (makeIs(LIBRAW_CAMERAMAKER_Samsung))
+    {
+      if ((ilm.LensMount == LIBRAW_MOUNT_Samsung_NX) && xmpdata && (strlen(xmpdata) > 9) &&
+          (ps = strstr(xmpdata, "LensID=\"(")))
+      {
+        ilm.LensID = atoi(ps + 9);
+      }
+    }
+  }
+
+  if (ilm.CameraMount == LIBRAW_MOUNT_FixedLens)
+  {
+    if (ilm.CameraFormat)
+      ilm.LensFormat = ilm.CameraFormat;
+    if (ilm.LensMount == LIBRAW_MOUNT_Unknown)
+      ilm.LensMount = LIBRAW_MOUNT_FixedLens;
+  }
+
+  if ((ilm.CameraMount != LIBRAW_MOUNT_Unknown) &&
+      (ilm.CameraMount != LIBRAW_MOUNT_FixedLens) &&
+      (ilm.LensMount == LIBRAW_MOUNT_Unknown)) {
+    if (ilm.LensID == LIBRAW_LENS_NOT_SET) ilm.LensMount = LIBRAW_MOUNT_IL_UM;
+    else ilm.LensMount = ilm.CameraMount;
+    }
+}
+
+void LibRaw::SetStandardIlluminants (unsigned makerIdx, const char* normModel) {
+  int i = -1;
+  int c;
+  if (!icWBC[LIBRAW_WBI_Ill_A][0] &&
+      !icWBC[LIBRAW_WBI_D65][0]) {
+    if (makerIdx == LIBRAW_CAMERAMAKER_Olympus) {
+      while (++i, icWBCCTC[i][0]) {
+        if (icWBCCTC[i][0] == 3000)
+          FORC4 icWBC[LIBRAW_WBI_Ill_A][c] = icWBCCTC[i][c+1];
+        else if (icWBCCTC[i][0] == 6600)
+          FORC4 icWBC[LIBRAW_WBI_D65][c] = icWBCCTC[i][c+1];
+      }
+    }
+  }
+
+  if (!icWBC[LIBRAW_WBI_Ill_A][0] && icWBC[LIBRAW_WBI_Tungsten][0])
+    FORC4 icWBC[LIBRAW_WBI_Ill_A][c] = icWBC[LIBRAW_WBI_Tungsten][c];
+
+  if (!icWBC[LIBRAW_WBI_D65][0] && icWBC[LIBRAW_WBI_FL_N][0])
+    FORC4 icWBC[LIBRAW_WBI_D65][c] = icWBC[LIBRAW_WBI_FL_N][c];
+
+  return;
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/metadata/olympus.cpp libkdcraw/libkdcraw/libraw/src/metadata/olympus.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/metadata/olympus.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/metadata/olympus.cpp	2022-11-07 07:46:31.734795008 +0300
@@ -0,0 +1,510 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+#include "../../internal/libraw_cameraids.h"
+
+void LibRaw::setOlympusBodyFeatures(unsigned long long id)
+{
+  ilm.CamID = id;
+
+  if ((id == OlyID_E_1)   ||
+      (id == OlyID_E_300) ||
+      ((id & 0x00ffff0000ULL) == 0x0030300000ULL))
+  {
+    ilm.CameraFormat = LIBRAW_FORMAT_FT;
+
+    if ((id == OlyID_E_1)   ||
+        (id == OlyID_E_300) ||
+        ((id >= OlyID_E_330) && (id <= OlyID_E_520)) ||
+        (id == OlyID_E_620) ||
+        (id == OlyID_E_450) ||
+        (id == OlyID_E_600) ||
+        (id == OlyID_E_5))
+    {
+      ilm.CameraMount = LIBRAW_MOUNT_FT;
+    }
+    else
+    {
+      ilm.CameraMount = LIBRAW_MOUNT_mFT;
+    }
+  }
+  else
+  {
+    ilm.LensMount = ilm.CameraMount = LIBRAW_MOUNT_FixedLens;
+  }
+  return;
+}
+
+void LibRaw::getOlympus_CameraType2()
+{
+
+  if (OlyID != 0x0ULL)
+    return;
+
+  int i = 0;
+  fread(imOly.CameraType2, 6, 1, ifp);
+  imOly.CameraType2[5] = 0;
+  while ((i < 6) && imOly.CameraType2[i])
+  {
+    OlyID = OlyID << 8 | imOly.CameraType2[i];
+    if (i < 5 && isspace(imOly.CameraType2[i + 1])) {
+      imOly.CameraType2[i + 1] = '\0';
+      break;
+    }
+    i++;
+  }
+  if (OlyID == OlyID_NORMA) {
+    if (strcmp(model, "SP510UZ")) OlyID = OlyID_SP_510UZ;
+    else OlyID = 0x0ULL;
+  }
+  unique_id = OlyID;
+  setOlympusBodyFeatures(OlyID);
+  return;
+}
+
+void LibRaw::getOlympus_SensorTemperature(unsigned len)
+{
+  if (OlyID != 0x0ULL)
+  {
+    short temp = get2();
+    if ((OlyID == OlyID_E_1)  ||
+        (OlyID == OlyID_E_M5) ||
+        (len != 1))
+      imCommon.SensorTemperature = (float)temp;
+    else if ((temp != -32768) && (temp != 0))
+    {
+      if (temp > 199)
+        imCommon.SensorTemperature = 86.474958f - 0.120228f * (float)temp;
+      else
+        imCommon.SensorTemperature = (float)temp;
+    }
+  }
+  return;
+}
+
+void LibRaw::parseOlympus_Equipment(unsigned tag, unsigned type, unsigned len,
+                                    unsigned dng_writer)
+{
+  // uptag 2010
+
+  switch (tag)
+  {
+  case 0x0100:
+    getOlympus_CameraType2();
+    break;
+  case 0x0101:
+    if ((!imgdata.shootinginfo.BodySerial[0]) && (dng_writer == nonDNG))
+      stmread(imgdata.shootinginfo.BodySerial, len, ifp);
+    break;
+  case 0x0102:
+    stmread(imgdata.shootinginfo.InternalBodySerial, len, ifp);
+    break;
+  case 0x0201:
+  {
+    unsigned char bits[4];
+    fread(bits, 1, 4, ifp);
+    ilm.LensID = (unsigned long long)bits[0] << 16 |
+                 (unsigned long long)bits[2] << 8 | (unsigned long long)bits[3];
+    ilm.LensMount = LIBRAW_MOUNT_FT;
+    ilm.LensFormat = LIBRAW_FORMAT_FT;
+    if (((ilm.LensID < 0x20000) || (ilm.LensID > 0x4ffff)) &&
+        (ilm.LensID & 0x10))
+      ilm.LensMount = LIBRAW_MOUNT_mFT;
+  }
+  break;
+  case 0x0202:
+    if ((!imgdata.lens.LensSerial[0]))
+      stmread(imgdata.lens.LensSerial, len, ifp);
+    break;
+  case 0x0203:
+    stmread(ilm.Lens, len, ifp);
+    break;
+  case 0x0205:
+    ilm.MaxAp4MinFocal = libraw_powf64l(sqrt(2.0f), get2() / 256.0f);
+    break;
+  case 0x0206:
+    ilm.MaxAp4MaxFocal = libraw_powf64l(sqrt(2.0f), get2() / 256.0f);
+    break;
+  case 0x0207:
+    ilm.MinFocal = (float)get2();
+    break;
+  case 0x0208:
+    ilm.MaxFocal = (float)get2();
+    if (ilm.MaxFocal > 1000.0f)
+      ilm.MaxFocal = ilm.MinFocal;
+    break;
+  case 0x020a:
+    ilm.MaxAp4CurFocal = libraw_powf64l(sqrt(2.0f), get2() / 256.0f);
+    break;
+  case 0x0301:
+    ilm.TeleconverterID = fgetc(ifp) << 8;
+    fgetc(ifp);
+    ilm.TeleconverterID = ilm.TeleconverterID | fgetc(ifp);
+    break;
+  case 0x0303:
+    stmread(ilm.Teleconverter, len, ifp);
+    if (!strlen(ilm.Teleconverter) && strchr(ilm.Lens, '+')) {
+      if (strstr(ilm.Lens, "MC-20"))
+        strcpy(ilm.Teleconverter, "MC-20");
+      else if (strstr(ilm.Lens, "MC-14"))
+        strcpy(ilm.Teleconverter, "MC-14");
+      else if (strstr(ilm.Lens, "EC-20"))
+        strcpy(ilm.Teleconverter, "EC-20");
+      else if (strstr(ilm.Lens, "EC-14"))
+        strcpy(ilm.Teleconverter, "EC-14");    }
+    break;
+  case 0x0403:
+    stmread(ilm.Attachment, len, ifp);
+    break;
+  }
+
+  return;
+}
+void LibRaw::parseOlympus_CameraSettings(int base, unsigned tag, unsigned type,
+                                         unsigned len, unsigned dng_writer)
+{
+  // uptag 0x2020
+
+  int c;
+  uchar uc;
+  switch (tag)
+  {
+  case 0x0101:
+    if (dng_writer == nonDNG)
+    {
+      thumb_offset = get4() + base;
+    }
+    break;
+  case 0x0102:
+    if (dng_writer == nonDNG)
+    {
+      thumb_length = get4();
+    }
+    break;
+  case 0x0200:
+    imgdata.shootinginfo.ExposureMode = get2();
+    break;
+  case 0x0202:
+    imgdata.shootinginfo.MeteringMode = get2();
+    break;
+  case 0x0301:
+    imgdata.shootinginfo.FocusMode = imOly.FocusMode[0] = get2();
+    if (len == 2)
+    {
+      imOly.FocusMode[1] = get2();
+    }
+    break;
+  case 0x0304:
+    for (c = 0; c < 64; c++)
+    {
+      imOly.AFAreas[c] = get4();
+    }
+    break;
+  case 0x0305:
+    for (c = 0; c < 5; c++)
+    {
+      imOly.AFPointSelected[c] = getreal(type);
+    }
+    break;
+  case 0x0306:
+    fread(&uc, 1, 1, ifp);
+    imOly.AFFineTune = uc;
+    break;
+  case 0x0307:
+    FORC3 imOly.AFFineTuneAdj[c] = get2();
+    break;
+  case 0x0401:
+    imCommon.FlashEC = getreal(type);
+    break;
+  case 0x0507:
+    imOly.ColorSpace = get2();
+    switch (imOly.ColorSpace) {
+    case 0:
+      imCommon.ColorSpace = LIBRAW_COLORSPACE_sRGB;
+      break;
+    case 1:
+      imCommon.ColorSpace = LIBRAW_COLORSPACE_AdobeRGB;
+      break;
+    case 2:
+      imCommon.ColorSpace = LIBRAW_COLORSPACE_ProPhotoRGB;
+      break;
+    default:
+      imCommon.ColorSpace = LIBRAW_COLORSPACE_Unknown;
+      break;
+    }
+    break;
+  case 0x0600:
+    imgdata.shootinginfo.DriveMode = imOly.DriveMode[0] = get2();
+    for (c = 1; c < (int)len && c < 5; c++)
+    {
+      imOly.DriveMode[c] = get2();
+    }
+    break;
+  case 0x0604:
+    imgdata.shootinginfo.ImageStabilization = get4();
+    break;
+  }
+
+  return;
+}
+
+void LibRaw::parseOlympus_ImageProcessing(unsigned tag, unsigned type,
+                                          unsigned len, unsigned dng_writer)
+{
+  // uptag 0x2040
+
+  int i, c, wb[4], nWB, tWB, wbG;
+  ushort CT;
+  short sorder;
+
+  if ((tag == 0x0100) && (dng_writer == nonDNG))
+  {
+    cam_mul[0] = get2() / 256.0;
+    cam_mul[2] = get2() / 256.0;
+  }
+  else if ((tag == 0x0101) && (len == 2) &&
+           ((OlyID == OlyID_E_410) || (OlyID == OlyID_E_510)))
+  {
+    for (i = 0; i < 64; i++)
+    {
+      icWBCCTC[i][2] = icWBCCTC[i][4] = icWBC[i][1] = icWBC[i][3] = 0x100;
+    }
+    for (i = 64; i < 256; i++)
+    {
+      icWBC[i][1] = icWBC[i][3] = 0x100;
+    }
+  }
+  else if ((tag > 0x0101) && (tag <= 0x0111))
+  {
+    nWB = tag - 0x0101;
+    tWB = Oly_wb_list2[nWB << 1];
+    CT = Oly_wb_list2[(nWB << 1) | 1];
+    wb[0] = get2();
+    wb[2] = get2();
+    if (tWB != 0x100)
+    {
+      icWBC[tWB][0] = wb[0];
+      icWBC[tWB][2] = wb[2];
+    }
+    if (CT)
+    {
+      icWBCCTC[nWB - 1][0] = CT;
+      icWBCCTC[nWB - 1][1] = wb[0];
+      icWBCCTC[nWB - 1][3] = wb[2];
+    }
+    if (len == 4)
+    {
+      wb[1] = get2();
+      wb[3] = get2();
+      if (tWB != 0x100)
+      {
+        icWBC[tWB][1] = wb[1];
+        icWBC[tWB][3] = wb[3];
+      }
+      if (CT)
+      {
+        icWBCCTC[nWB - 1][2] = wb[1];
+        icWBCCTC[nWB - 1][4] = wb[3];
+      }
+    }
+  }
+  else if ((tag >= 0x0112) && (tag <= 0x011e))
+  {
+    nWB = tag - 0x0112;
+    wbG = get2();
+    tWB = Oly_wb_list2[nWB << 1];
+    if (nWB)
+      icWBCCTC[nWB - 1][2] = icWBCCTC[nWB - 1][4] = wbG;
+    if (tWB != 0x100)
+      icWBC[tWB][1] = icWBC[tWB][3] = wbG;
+  }
+  else if (tag == 0x011f)
+  {
+    wbG = get2();
+    if (icWBC[LIBRAW_WBI_Flash][0])
+      icWBC[LIBRAW_WBI_Flash][1] =
+          icWBC[LIBRAW_WBI_Flash][3] = wbG;
+    FORC4 if (icWBC[LIBRAW_WBI_Custom1 + c][0])
+        icWBC[LIBRAW_WBI_Custom1 + c][1] =
+        icWBC[LIBRAW_WBI_Custom1 + c][3] = wbG;
+  }
+  else if (tag == 0x0121)
+  {
+    icWBC[LIBRAW_WBI_Flash][0] = get2();
+    icWBC[LIBRAW_WBI_Flash][2] = get2();
+    if (len == 4)
+    {
+      icWBC[LIBRAW_WBI_Flash][1] = get2();
+      icWBC[LIBRAW_WBI_Flash][3] = get2();
+    }
+  }
+  else if ((tag == 0x0200) && (dng_writer == nonDNG) &&
+           strcmp(software, "v757-71"))
+  {
+    for (i = 0; i < 3; i++)
+    {
+      if (!imOly.ColorSpace)
+      {
+        FORC3 cmatrix[i][c] = ((short)get2()) / 256.0;
+      }
+      else
+      {
+        FORC3 imgdata.color.ccm[i][c] = ((short)get2()) / 256.0;
+      }
+    }
+  }
+  else if ((tag == 0x0600) && (dng_writer == nonDNG))
+  {
+    FORC4 cblack[RGGB_2_RGBG(c)] = get2();
+  }
+  else if ((tag == 0x0612) && (dng_writer == nonDNG))
+  {
+    imgdata.sizes.raw_inset_crop.cleft = get2();
+  }
+  else if ((tag == 0x0613) && (dng_writer == nonDNG))
+  {
+    imgdata.sizes.raw_inset_crop.ctop = get2();
+  }
+  else if ((tag == 0x0614) && (dng_writer == nonDNG))
+  {
+    imgdata.sizes.raw_inset_crop.cwidth = get2();
+  }
+  else if ((tag == 0x0615) && (dng_writer == nonDNG))
+  {
+    imgdata.sizes.raw_inset_crop.cheight = get2();
+  }
+  else if ((tag == 0x0805) && (len == 2))
+  {
+    imOly.SensorCalibration[0] = getreal(type);
+    imOly.SensorCalibration[1] = getreal(type);
+    if ((dng_writer == nonDNG)&& (OlyID != OlyID_XZ_1))
+      FORC4 imgdata.color.linear_max[c] = imOly.SensorCalibration[0];
+  }
+  else if (tag == 0x1112)
+  {
+    sorder = order;
+    order = 0x4d4d;
+    c = get2();
+    order = sorder;
+    switch (c)
+    {
+    case 0x21:
+      imgdata.sizes.raw_inset_crop.aspect = LIBRAW_IMAGE_ASPECT_3to2;
+      break;
+    case 0x31:
+      imgdata.sizes.raw_inset_crop.aspect = LIBRAW_IMAGE_ASPECT_16to9;
+      break;
+    case 0x41:
+      imgdata.sizes.raw_inset_crop.aspect = LIBRAW_IMAGE_ASPECT_1to1;
+      break;
+    case 0x91:
+      imgdata.sizes.raw_inset_crop.aspect = LIBRAW_IMAGE_ASPECT_4to3;
+      break;
+    }
+  }
+  else if (tag == 0x1113)
+  {
+    imgdata.sizes.raw_inset_crop.cleft = get2();
+    imgdata.sizes.raw_inset_crop.ctop = get2();
+    imgdata.sizes.raw_inset_crop.cwidth =
+        get2() - imgdata.sizes.raw_inset_crop.cleft;
+    imgdata.sizes.raw_inset_crop.cheight =
+        get2() - imgdata.sizes.raw_inset_crop.ctop;
+  }
+  else if (tag == 0x1306)
+  {
+    c = get2();
+    if ((c != 0) && (c != 100))
+    {
+      if (c < 61)
+        imCommon.CameraTemperature = (float)c;
+      else
+        imCommon.CameraTemperature = (float)(c - 32) / 1.8f;
+      if ((imCommon.exifAmbientTemperature > -273.15f) &&
+          ((OlyID == OlyID_TG_5) ||
+           (OlyID == OlyID_TG_6))
+      )
+        imCommon.CameraTemperature += imCommon.exifAmbientTemperature;
+    }
+  }
+
+  return;
+}
+
+void LibRaw::parseOlympus_RawInfo(unsigned tag, unsigned type, unsigned len,
+                                  unsigned dng_writer)
+{
+  // uptag 0x3000
+
+  int wb_ind, c, i;
+
+  if ((tag == 0x0110) && strcmp(software, "v757-71"))
+  {
+    icWBC[LIBRAW_WBI_Auto][0] = get2();
+    icWBC[LIBRAW_WBI_Auto][2] = get2();
+    if (len == 2)
+    {
+      for (i = 0; i < 256; i++)
+        icWBC[i][1] = icWBC[i][3] = 0x100;
+    }
+  }
+  else if ((((tag >= 0x0120) && (tag <= 0x0124)) ||
+            ((tag >= 0x0130) && (tag <= 0x0133))) &&
+           strcmp(software, "v757-71"))
+  {
+    if (tag <= 0x0124)
+      wb_ind = tag - 0x0120;
+    else
+      wb_ind = tag - 0x0130 + 5;
+
+    icWBC[Oly_wb_list1[wb_ind]][0] = get2();
+    icWBC[Oly_wb_list1[wb_ind]][2] = get2();
+  }
+  else if ((tag == 0x0200) && (dng_writer == nonDNG))
+  {
+    for (i = 0; i < 3; i++)
+    {
+      if (!imOly.ColorSpace)
+      {
+        FORC3 cmatrix[i][c] = ((short)get2()) / 256.0;
+      }
+      else
+      {
+        FORC3 imgdata.color.ccm[i][c] = ((short)get2()) / 256.0;
+      }
+    }
+  }
+  else if ((tag == 0x0600) && (dng_writer == nonDNG))
+  {
+    FORC4 cblack[RGGB_2_RGBG(c)] = get2();
+  }
+  else if ((tag == 0x0612) && (dng_writer == nonDNG))
+  {
+    imgdata.sizes.raw_inset_crop.cleft = get2();
+  }
+  else if ((tag == 0x0613) && (dng_writer == nonDNG))
+  {
+    imgdata.sizes.raw_inset_crop.ctop = get2();
+  }
+  else if ((tag == 0x0614) && (dng_writer == nonDNG))
+  {
+    imgdata.sizes.raw_inset_crop.cwidth = get2();
+  }
+  else if ((tag == 0x0615) && (dng_writer == nonDNG))
+  {
+    imgdata.sizes.raw_inset_crop.cheight = get2();
+  }
+  return;
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/metadata/p1.cpp libkdcraw/libkdcraw/libraw/src/metadata/p1.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/metadata/p1.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/metadata/p1.cpp	2022-11-07 07:46:31.734795008 +0300
@@ -0,0 +1,190 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+void LibRaw::setPhaseOneFeatures(unsigned long long id)
+{
+
+  ushort i;
+  static const struct
+  {
+    unsigned long long id;
+    char t_model[32];
+    int CamMnt;
+    int CamFmt;
+  } p1_unique[] = {
+      // Phase One section:
+      {0x001ULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x00aULL, "PhaseOne/Mamiya", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x00cULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x010ULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x011ULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x012ULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x013ULL, "PhaseOne/Mamiya", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x014ULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x015ULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x016ULL, "PhaseOne/Mamiya", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x017ULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x018ULL, "Hasselblad H", LIBRAW_MOUNT_Hasselblad_H, LIBRAW_FORMAT_645},
+      {0x019ULL, "PhaseOne/Mamiya", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x020ULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x022ULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x023ULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x024ULL, "Hasselblad H", LIBRAW_MOUNT_Hasselblad_H, LIBRAW_FORMAT_645},
+      {0x025ULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x026ULL, "PhaseOne/Mamiya", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x027ULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x028ULL, "Hasselblad H", LIBRAW_MOUNT_Hasselblad_H, LIBRAW_FORMAT_645},
+      {0x029ULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x02aULL, "PhaseOne/Mamiya", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x02cULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x02dULL, "Hasselblad H", LIBRAW_MOUNT_Hasselblad_H, LIBRAW_FORMAT_645},
+      {0x02eULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x02fULL, "PhaseOne/Mamiya", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x030ULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x031ULL, "Hasselblad H", LIBRAW_MOUNT_Hasselblad_H, LIBRAW_FORMAT_645},
+      {0x032ULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x033ULL, "PhaseOne/Mamiya", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x034ULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x035ULL, "Hasselblad H", LIBRAW_MOUNT_Hasselblad_H, LIBRAW_FORMAT_645},
+      {0x036ULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x037ULL, "PhaseOne/Mamiya", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x043ULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x044ULL, "Hasselblad H", LIBRAW_MOUNT_Hasselblad_H, LIBRAW_FORMAT_645},
+      {0x045ULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x046ULL, "PhaseOne/Mamiya", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x047ULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x048ULL, "Hasselblad H", LIBRAW_MOUNT_Hasselblad_H, LIBRAW_FORMAT_645},
+      {0x049ULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x04aULL, "PhaseOne/Mamiya", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x04cULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x04dULL, "Hasselblad H", LIBRAW_MOUNT_Hasselblad_H, LIBRAW_FORMAT_645},
+      {0x04eULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x04fULL, "PhaseOne/Mamiya", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x050ULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x051ULL, "Hasselblad H", LIBRAW_MOUNT_Hasselblad_H, LIBRAW_FORMAT_645},
+      {0x052ULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x053ULL, "PhaseOne/Mamiya", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x054ULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x055ULL, "Hasselblad H", LIBRAW_MOUNT_Hasselblad_H, LIBRAW_FORMAT_645},
+      {0x056ULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x057ULL, "PhaseOne/Mamiya", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x063ULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x064ULL, "Hasselblad H", LIBRAW_MOUNT_Hasselblad_H, LIBRAW_FORMAT_645},
+      {0x065ULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x066ULL, "PhaseOne/Mamiya", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x067ULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x068ULL, "Hasselblad H", LIBRAW_MOUNT_Hasselblad_H, LIBRAW_FORMAT_645},
+      {0x069ULL, "PhaseOne/Mamiya", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x06aULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x070ULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x071ULL, "Hasselblad H", LIBRAW_MOUNT_Hasselblad_H, LIBRAW_FORMAT_645},
+      {0x072ULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x073ULL, "PhaseOne/Mamiya", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x083ULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x084ULL, "Hasselblad H", LIBRAW_MOUNT_Hasselblad_H, LIBRAW_FORMAT_645},
+      {0x085ULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x086ULL, "PhaseOne/Mamiya", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x087ULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x088ULL, "Hasselblad H", LIBRAW_MOUNT_Hasselblad_H, LIBRAW_FORMAT_645},
+      {0x089ULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x08aULL, "PhaseOne/Mamiya", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x08cULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x08dULL, "Hasselblad H", LIBRAW_MOUNT_Hasselblad_H, LIBRAW_FORMAT_645},
+      {0x08eULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x08fULL, "PhaseOne/Mamiya", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x094ULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x095ULL, "Hasselblad H", LIBRAW_MOUNT_Hasselblad_H, LIBRAW_FORMAT_645},
+      {0x096ULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x097ULL, "PhaseOne/Mamiya", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x0a0ULL, "A-250", LIBRAW_MOUNT_Alpa, LIBRAW_FORMAT_69},
+      {0x0a1ULL, "A-260", LIBRAW_MOUNT_Alpa, LIBRAW_FORMAT_69},
+      {0x0a2ULL, "A-280", LIBRAW_MOUNT_Alpa, LIBRAW_FORMAT_69},
+      {0x0a7ULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x0a8ULL, "Hasselblad H", LIBRAW_MOUNT_Hasselblad_H, LIBRAW_FORMAT_645},
+      {0x0a9ULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x0aaULL, "PhaseOne/Mamiya", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x0acULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x0adULL, "Hasselblad H", LIBRAW_MOUNT_Hasselblad_H, LIBRAW_FORMAT_645},
+      {0x0aeULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x0afULL, "PhaseOne/Mamiya", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x0b0ULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x0b1ULL, "Hasselblad H", LIBRAW_MOUNT_Hasselblad_H, LIBRAW_FORMAT_645},
+      {0x0b2ULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x0b3ULL, "PhaseOne/Mamiya", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x0b4ULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x0b5ULL, "Hasselblad H", LIBRAW_MOUNT_Hasselblad_H, LIBRAW_FORMAT_645},
+      {0x0b6ULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x0b7ULL, "PhaseOne/Mamiya", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x0d0ULL, "Hasselblad V", LIBRAW_MOUNT_Hasselblad_V, LIBRAW_FORMAT_66},
+      {0x0d3ULL, "PhaseOne/Mamiya", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x1c0ULL, "Phase One 645AF", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x1c9ULL, "Phase One 645DF", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x1d7ULL, "Phase One 645DF+", LIBRAW_MOUNT_Mamiya645, LIBRAW_FORMAT_645},
+      {0x2c0ULL, "Phase One iXA", LIBRAW_MOUNT_Unknown, LIBRAW_FORMAT_Unknown},
+      {0x2c1ULL, "Phase One iXA - R", LIBRAW_MOUNT_Unknown,
+       LIBRAW_FORMAT_Unknown},
+      {0x2c2ULL, "Phase One iXU 150", LIBRAW_MOUNT_Unknown,
+       LIBRAW_FORMAT_Unknown},
+      {0x2c3ULL, "Phase One iXU 150 - NIR", LIBRAW_MOUNT_Unknown,
+       LIBRAW_FORMAT_Unknown},
+      {0x2c4ULL, "Phase One iXU 180", LIBRAW_MOUNT_Unknown,
+       LIBRAW_FORMAT_Unknown},
+      {0x2d1ULL, "Phase One iXR", LIBRAW_MOUNT_Unknown, LIBRAW_FORMAT_Unknown},
+      // Leaf section:
+      {0x14dULL, "Mamiya", LIBRAW_MOUNT_Unknown, LIBRAW_FORMAT_Unknown},
+      {0x149ULL, "Universal", LIBRAW_MOUNT_Unknown, LIBRAW_FORMAT_Unknown},
+      {0x14aULL, "Hasselblad H1/H2", LIBRAW_MOUNT_Hasselblad_H,
+       LIBRAW_FORMAT_645},
+      {0x14cULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x150ULL, "AFi", LIBRAW_MOUNT_Rollei_bayonet, LIBRAW_FORMAT_66},
+      {0x147ULL, "Mamiya", LIBRAW_MOUNT_Unknown, LIBRAW_FORMAT_Unknown},
+      {0x144ULL, "Universal", LIBRAW_MOUNT_Unknown, LIBRAW_FORMAT_Unknown},
+      {0x145ULL, "Hasselblad H1/H2", LIBRAW_MOUNT_Hasselblad_H,
+       LIBRAW_FORMAT_645},
+      {0x146ULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x14fULL, "AFi", LIBRAW_MOUNT_Rollei_bayonet, LIBRAW_FORMAT_66},
+      {0x154ULL, "Mamiya", LIBRAW_MOUNT_Unknown, LIBRAW_FORMAT_Unknown},
+      {0x151ULL, "Universal", LIBRAW_MOUNT_Unknown, LIBRAW_FORMAT_Unknown},
+      {0x152ULL, "Hasselblad H1/H2", LIBRAW_MOUNT_Hasselblad_H,
+       LIBRAW_FORMAT_645},
+      {0x153ULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x143ULL, "Mamiya", LIBRAW_MOUNT_Unknown, LIBRAW_FORMAT_Unknown},
+      {0x140ULL, "Universal", LIBRAW_MOUNT_Unknown, LIBRAW_FORMAT_Unknown},
+      {0x142ULL, "Hasselblad H1/H2", LIBRAW_MOUNT_Hasselblad_H,
+       LIBRAW_FORMAT_645},
+      {0x141ULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x14eULL, "AFi", LIBRAW_MOUNT_Rollei_bayonet, LIBRAW_FORMAT_66},
+      {0x171ULL, "Universal", LIBRAW_MOUNT_Unknown, LIBRAW_FORMAT_Unknown},
+      {0x172ULL, "Mamiya", LIBRAW_MOUNT_Unknown, LIBRAW_FORMAT_Unknown},
+      {0x173ULL, "Hasselblad H1/H2", LIBRAW_MOUNT_Hasselblad_H,
+       LIBRAW_FORMAT_645},
+      {0x174ULL, "Contax 645", LIBRAW_MOUNT_Contax645, LIBRAW_FORMAT_645},
+      {0x175ULL, "AFi", LIBRAW_MOUNT_Rollei_bayonet, LIBRAW_FORMAT_66},
+  };
+  ilm.CamID = id;
+  if (id && !ilm.body[0])
+  {
+    for (i = 0; i < sizeof p1_unique / sizeof *p1_unique; i++)
+      if (id == p1_unique[i].id)
+      {
+        strcpy(ilm.body, p1_unique[i].t_model);
+        ilm.CameraFormat = p1_unique[i].CamFmt;
+        ilm.CameraMount = p1_unique[i].CamMnt;
+      }
+  }
+  return;
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/metadata/pentax.cpp libkdcraw/libkdcraw/libraw/src/metadata/pentax.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/metadata/pentax.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/metadata/pentax.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,537 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+#include "../../internal/libraw_cameraids.h"
+
+void LibRaw::setPentaxBodyFeatures(unsigned long long id)
+{
+
+  ilm.CamID = id;
+
+  switch (id) {
+  case PentaxID_staristD:
+  case PentaxID_staristDS:
+  case PentaxID_staristDL:
+  case PentaxID_staristDS2:
+  case PentaxID_GX_1S:
+  case PentaxID_staristDL2:
+  case PentaxID_GX_1L:
+  case PentaxID_K100D:
+  case PentaxID_K110D:
+  case PentaxID_K100D_Super:
+  case PentaxID_K10D:
+  case PentaxID_GX10:
+  case PentaxID_K20D:
+  case PentaxID_GX20:
+  case PentaxID_K200D:
+  case PentaxID_K2000:
+  case PentaxID_K_m:
+  case PentaxID_K_7:
+  case PentaxID_K_x:
+  case PentaxID_K_r:
+  case PentaxID_K_5:
+  case PentaxID_K_01:
+  case PentaxID_K_30:
+  case PentaxID_K_5_II:
+  case PentaxID_K_5_II_s:
+  case PentaxID_K_50:
+  case PentaxID_K_3:
+  case PentaxID_K_500:
+  case PentaxID_K_S1:
+  case PentaxID_K_S2:
+  case PentaxID_K_3_II:
+  case PentaxID_K_70:
+  case PentaxID_KP:
+    ilm.CameraMount = LIBRAW_MOUNT_Pentax_K;
+    ilm.CameraFormat = LIBRAW_FORMAT_APSC;
+    break;
+  case PentaxID_K_1:
+  case PentaxID_K_1_Mark_II:
+    ilm.CameraMount = LIBRAW_MOUNT_Pentax_K;
+    ilm.CameraFormat = LIBRAW_FORMAT_FF;
+    break;
+  case PentaxID_645D:
+  case PentaxID_645Z:
+    ilm.CameraMount = LIBRAW_MOUNT_Pentax_645;
+    ilm.CameraFormat = LIBRAW_FORMAT_CROP645;
+    break;
+  case PentaxID_Q:
+  case PentaxID_Q10:
+    ilm.CameraMount = LIBRAW_MOUNT_Pentax_Q;
+    ilm.CameraFormat = LIBRAW_FORMAT_1div2p3INCH;
+    break;
+  case PentaxID_Q7:
+  case PentaxID_Q_S1:
+    ilm.CameraMount = LIBRAW_MOUNT_Pentax_Q;
+    ilm.CameraFormat = LIBRAW_FORMAT_1div1p7INCH;
+    break;
+  case PentaxID_MX_1:
+    ilm.LensMount = LIBRAW_MOUNT_FixedLens;
+    ilm.CameraMount = LIBRAW_MOUNT_FixedLens;
+    ilm.CameraFormat = LIBRAW_FORMAT_1div1p7INCH;
+    ilm.FocalType = LIBRAW_FT_ZOOM_LENS;
+    break;
+  case PentaxID_GR_III:
+    ilm.CameraMount = LIBRAW_MOUNT_FixedLens;
+    ilm.LensMount = LIBRAW_MOUNT_FixedLens;
+    ilm.CameraFormat = LIBRAW_FORMAT_APSC;
+    ilm.LensFormat = LIBRAW_FORMAT_APSC;
+    ilm.FocalType = LIBRAW_FT_PRIME_LENS;
+    break;
+  default:
+    ilm.LensMount = LIBRAW_MOUNT_FixedLens;
+    ilm.CameraMount = LIBRAW_MOUNT_FixedLens;
+  }
+  return;
+}
+
+void LibRaw::PentaxISO(ushort c)
+{
+  int code[] = {3,    4,    5,   6,   7,   8,   9,   10,  11,  12,  13,  14,
+                15,   16,   17,  18,  19,  20,  21,  22,  23,  24,  25,  26,
+                27,   28,   29,  30,  31,  32,  33,  34,  35,  36,  37,  38,
+                39,   40,   41,  42,  43,  44,  45,  50,  100, 200, 400, 800,
+                1600, 3200, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267,
+                268,  269,  270, 271, 272, 273, 274, 275, 276, 277, 278};
+  double value[] = {
+      50,     64,     80,     100,    125,    160,    200,    250,    320,
+      400,    500,    640,    800,    1000,   1250,   1600,   2000,   2500,
+      3200,   4000,   5000,   6400,   8000,   10000,  12800,  16000,  20000,
+      25600,  32000,  40000,  51200,  64000,  80000,  102400, 128000, 160000,
+      204800, 258000, 325000, 409600, 516000, 650000, 819200, 50,     100,
+      200,    400,    800,    1600,   3200,   50,     70,     100,    140,
+      200,    280,    400,    560,    800,    1100,   1600,   2200,   3200,
+      4500,   6400,   9000,   12800,  18000,  25600,  36000,  51200};
+#define numel (sizeof(code) / sizeof(code[0]))
+  int i;
+  for (i = 0; i < (int)numel; i++)
+  {
+    if (code[i] == c)
+    {
+      iso_speed = value[i];
+      return;
+    }
+  }
+  if (i == numel)
+    iso_speed = 65535.0f;
+}
+#undef numel
+
+void LibRaw::PentaxLensInfo(unsigned long long id, unsigned len) // tag 0x0207
+{
+  ushort iLensData = 0;
+  uchar *table_buf;
+  table_buf = (uchar *)malloc(MAX(len, 128));
+  fread(table_buf, len, 1, ifp);
+  if ((id < PentaxID_K100D) ||
+      (((id == PentaxID_K100D) ||
+        (id == PentaxID_K110D) ||
+        (id == PentaxID_K100D_Super)) &&
+       ((!table_buf[20] ||
+        (table_buf[20] == 0xff)))))
+  {
+    iLensData = 3;
+    if (ilm.LensID == LIBRAW_LENS_NOT_SET)
+      ilm.LensID = (((unsigned)table_buf[0]) << 8) + table_buf[1];
+  }
+  else
+    switch (len)
+    {
+    case 90: // LensInfo3
+      iLensData = 13;
+      if (ilm.LensID == LIBRAW_LENS_NOT_SET)
+        ilm.LensID = ((unsigned)((table_buf[1] & 0x0f) + table_buf[3]) << 8) +
+                     table_buf[4];
+      break;
+    case 91: // LensInfo4
+      iLensData = 12;
+      if (ilm.LensID == LIBRAW_LENS_NOT_SET)
+        ilm.LensID = ((unsigned)((table_buf[1] & 0x0f) + table_buf[3]) << 8) +
+                     table_buf[4];
+      break;
+    case 80: // LensInfo5
+    case 128:
+      iLensData = 15;
+      if (ilm.LensID == LIBRAW_LENS_NOT_SET)
+        ilm.LensID = ((unsigned)((table_buf[1] & 0x0f) + table_buf[4]) << 8) +
+                     table_buf[5];
+      break;
+    case 168: // Ricoh GR III, id 0x1320e
+      break;
+    default:
+      if (id >= 0x12b9cULL) // LensInfo2
+      {
+        iLensData = 4;
+        if (ilm.LensID == LIBRAW_LENS_NOT_SET)
+          ilm.LensID = ((unsigned)((table_buf[0] & 0x0f) + table_buf[2]) << 8) +
+                       table_buf[3];
+      }
+    }
+  if (iLensData)
+  {
+    if (table_buf[iLensData + 9] && (fabs(ilm.CurFocal) < 0.1f))
+      ilm.CurFocal = 10 * (table_buf[iLensData + 9] >> 2) *
+                     libraw_powf64l(4, (table_buf[iLensData + 9] & 0x03) - 2);
+    if (table_buf[iLensData + 10] & 0xf0)
+      ilm.MaxAp4CurFocal = libraw_powf64l(
+          2.0f, (float)((table_buf[iLensData + 10] & 0xf0) >> 4) / 4.0f);
+    if (table_buf[iLensData + 10] & 0x0f)
+      ilm.MinAp4CurFocal = libraw_powf64l(
+          2.0f, (float)((table_buf[iLensData + 10] & 0x0f) + 10) / 4.0f);
+
+    if (iLensData != 12)
+    {
+      switch (table_buf[iLensData] & 0x06)
+      {
+      case 0:
+        ilm.MinAp4MinFocal = 22.0f;
+        break;
+      case 2:
+        ilm.MinAp4MinFocal = 32.0f;
+        break;
+      case 4:
+        ilm.MinAp4MinFocal = 45.0f;
+        break;
+      case 6:
+        ilm.MinAp4MinFocal = 16.0f;
+        break;
+      }
+      if (table_buf[iLensData] & 0x70)
+        ilm.LensFStops =
+            ((float)(((table_buf[iLensData] & 0x70) >> 4) ^ 0x07)) / 2.0f +
+            5.0f;
+
+      ilm.MinFocusDistance = (float)(table_buf[iLensData + 3] & 0xf8);
+      ilm.FocusRangeIndex = (float)(table_buf[iLensData + 3] & 0x07);
+
+      if ((table_buf[iLensData + 14] > 1) && (fabs(ilm.MaxAp4CurFocal) < 0.7f))
+        ilm.MaxAp4CurFocal = libraw_powf64l(
+            2.0f, (float)((table_buf[iLensData + 14] & 0x7f) - 1) / 32.0f);
+    }
+    else if ((id != 0x12e76ULL) && // K-5
+             (table_buf[iLensData + 15] > 1) &&
+             (fabs(ilm.MaxAp4CurFocal) < 0.7f))
+    {
+      ilm.MaxAp4CurFocal = libraw_powf64l(
+          2.0f, (float)((table_buf[iLensData + 15] & 0x7f) - 1) / 32.0f);
+    }
+  }
+  free(table_buf);
+  return;
+}
+
+void LibRaw::parsePentaxMakernotes(int base, unsigned tag, unsigned type,
+                                   unsigned len, unsigned dng_writer)
+{
+
+  int c;
+  if (tag == 0x0005)
+  {
+    unique_id = get4();
+    setPentaxBodyFeatures(unique_id);
+  }
+  else if (tag == 0x0008)
+  { /* 4 is raw, 7 is raw w/ pixel shift, 8 is raw w/ dynamic pixel shift */
+    imPentax.Quality = get2();
+  }
+  else if (tag == 0x000d)
+  {
+    imgdata.shootinginfo.FocusMode = imPentax.FocusMode = get2();
+  }
+  else if (tag == 0x000e)
+  {
+    imgdata.shootinginfo.AFPoint = imPentax.AFPointSelected = get2();
+  }
+  else if (tag == 0x000f)
+  {
+    imPentax.AFPointsInFocus = getint(type);
+  }
+  else if (tag == 0x0010)
+  {
+    imPentax.FocusPosition = get2();
+  }
+  else if (tag == 0x0013)
+  {
+    ilm.CurAp = (float)get2() / 10.0f;
+  }
+  else if (tag == 0x0014)
+  {
+    PentaxISO(get2());
+  }
+  else if (tag == 0x0017)
+  {
+    imgdata.shootinginfo.MeteringMode = get2();
+  }
+  else if (tag == 0x001b) {
+    cam_mul[2] = get2() / 256.0;
+  }
+  else if (tag == 0x001c) {
+    cam_mul[0] = get2() / 256.0;
+  }
+  else if (tag == 0x001d)
+  {
+    ilm.CurFocal = (float)get4() / 100.0f;
+  }
+  else if (tag == 0x0034)
+  {
+    uchar uc;
+    FORC4
+    {
+      fread(&uc, 1, 1, ifp);
+      imPentax.DriveMode[c] = uc;
+    }
+    imgdata.shootinginfo.DriveMode = imPentax.DriveMode[0];
+  }
+  else if (tag == 0x0037) {
+    switch (get2()) {
+    case 0:
+      imCommon.ColorSpace = LIBRAW_COLORSPACE_sRGB;
+      break;
+    case 1:
+      imCommon.ColorSpace = LIBRAW_COLORSPACE_AdobeRGB;
+      break;
+    default:
+      imCommon.ColorSpace = LIBRAW_COLORSPACE_Unknown;
+      break;
+    }
+  }
+  else if (tag == 0x0038)
+  {
+    imgdata.sizes.raw_inset_crop.cleft = get2();
+    imgdata.sizes.raw_inset_crop.ctop = get2();
+  }
+  else if (tag == 0x0039)
+  {
+    imgdata.sizes.raw_inset_crop.cwidth = get2();
+    imgdata.sizes.raw_inset_crop.cheight = get2();
+  }
+  else if (tag == 0x003f)
+  {
+    unsigned a = unsigned(fgetc(ifp)) << 8;
+    ilm.LensID = a | fgetc(ifp);
+  }
+  else if (tag == 0x0047)
+  {
+    imCommon.CameraTemperature = (float)fgetc(ifp);
+  }
+  else if (tag == 0x004d)
+  {
+    if (tagtypeIs(LIBRAW_EXIFTAG_TYPE_SLONG))
+      imCommon.FlashEC = getreal(type) / 256.0f;
+    else
+      imCommon.FlashEC = (float)((signed short)fgetc(ifp)) / 6.0f;
+  }
+  else if (tag == 0x005c)
+  {
+    fgetc(ifp);
+    imgdata.shootinginfo.ImageStabilization = (short)fgetc(ifp);
+  }
+  else if (tag == 0x0072)
+  {
+    imPentax.AFAdjustment = get2();
+  }
+  else if ((tag == 0x007e) && (dng_writer == nonDNG))
+  {
+    imgdata.color.linear_max[0] = imgdata.color.linear_max[1] =
+        imgdata.color.linear_max[2] = imgdata.color.linear_max[3] =
+            get4();
+  }
+  else if (tag == 0x0080)
+  {
+    short a = (short)fgetc(ifp);
+    switch (a)
+    {
+    case 0:
+      imgdata.sizes.raw_inset_crop.aspect = LIBRAW_IMAGE_ASPECT_4to3;
+      break;
+    case 1:
+      imgdata.sizes.raw_inset_crop.aspect = LIBRAW_IMAGE_ASPECT_3to2;
+      break;
+    case 2:
+      imgdata.sizes.raw_inset_crop.aspect = LIBRAW_IMAGE_ASPECT_16to9;
+      break;
+    case 3:
+      imgdata.sizes.raw_inset_crop.aspect = LIBRAW_IMAGE_ASPECT_1to1;
+      break;
+    }
+  }
+
+  else if ((tag == 0x0200) && (dng_writer == nonDNG)) { // Pentax black level
+    FORC4 cblack[RGGB_2_RGBG(c)] = get2();
+  }
+
+  else if ((tag == 0x0201) && (dng_writer == nonDNG)) { // Pentax As Shot WB
+    FORC4 cam_mul[RGGB_2_RGBG(c)] = get2();
+  }
+
+  else if ((tag == 0x0203) && (dng_writer == nonDNG))
+  {
+    for (int i = 0; i < 3; i++)
+      FORC3 cmatrix[i][c] = ((short)get2()) / 8192.0;
+  }
+  else if (tag == 0x0205)
+  { // Pentax K-m has multiexposure set to 8 when no multi-exposure is in effect
+    if (len < 25)
+    {
+      fseek(ifp, 10, SEEK_CUR);
+      imPentax.MultiExposure = fgetc(ifp) & 0x0f;
+    }
+  }
+  else if (tag == 0x0207)
+  {
+    if (len < 65535) // Safety belt
+      PentaxLensInfo(ilm.CamID, len);
+  }
+  else if ((tag >= 0x020d) && (tag <= 0x0214))
+  {
+    FORC4 icWBC[Pentax_wb_list1[tag - 0x020d]][RGGB_2_RGBG(c)] = get2();
+  }
+
+  else if ((tag == 0x0220) && (dng_writer == nonDNG)) {
+    meta_offset = ftell(ifp);
+  }
+
+  else if (tag == 0x0221)
+  {
+    int nWB = get2();
+    if (nWB <= int(sizeof(icWBCCTC) / sizeof(icWBCCTC[0])))
+      FORC(nWB)
+      {
+        icWBCCTC[c][0] = (unsigned)0xcfc6 - get2();
+        fseek(ifp, 2, SEEK_CUR);
+        icWBCCTC[c][1] = get2();
+        icWBCCTC[c][2] = icWBCCTC[c][4] = 0x2000;
+        icWBCCTC[c][3] = get2();
+      }
+  }
+  else if (tag == 0x0215)
+  {
+    fseek(ifp, 16, SEEK_CUR);
+    sprintf(imgdata.shootinginfo.InternalBodySerial, "%d", get4());
+  }
+  else if (tag == 0x0229)
+  {
+    stmread(imgdata.shootinginfo.BodySerial, len, ifp);
+  }
+  else if (tag == 0x022d)
+  {
+    int wb_ind;
+    getc(ifp);
+    for (int wb_cnt = 0; wb_cnt < Pentax_wb_list2.size(); wb_cnt++)
+    {
+      wb_ind = getc(ifp);
+      if (wb_ind >= 0 && wb_ind < Pentax_wb_list2.size() )
+        FORC4 icWBC[Pentax_wb_list2[wb_ind]][RGGB_2_RGBG(c)] = get2();
+    }
+  }
+  else if (tag == 0x0239)
+  { // Q-series lens info (LensInfoQ)
+    char LensInfo[20];
+    fseek(ifp, 12, SEEK_CUR);
+    stread(ilm.Lens, 30, ifp);
+    strcat(ilm.Lens, " ");
+    stread(LensInfo, 20, ifp);
+    strcat(ilm.Lens, LensInfo);
+  }
+}
+
+void LibRaw::parseRicohMakernotes(int base, unsigned tag, unsigned type,
+                                  unsigned len, unsigned dng_writer)
+{
+  char buffer[17];
+  if (tag == 0x0005)
+  {
+    int c;
+    int count = 0;
+    fread(buffer, 16, 1, ifp);
+    buffer[16] = 0;
+    FORC(16)
+    {
+      if ((isspace(buffer[c])) || (buffer[c] == 0x2D) || (isalnum(buffer[c])))
+        count++;
+      else
+        break;
+    }
+    if (count == 16)
+    {
+      if (strncmp(model, "GXR", 3))
+      {
+        sprintf(imgdata.shootinginfo.BodySerial, "%8s", buffer + 8);
+      }
+      buffer[8] = 0;
+      sprintf(imgdata.shootinginfo.InternalBodySerial, "%8s", buffer);
+    }
+    else
+    {
+      sprintf(imgdata.shootinginfo.BodySerial, "%02x%02x%02x%02x", buffer[4],
+              buffer[5], buffer[6], buffer[7]);
+      sprintf(imgdata.shootinginfo.InternalBodySerial, "%02x%02x%02x%02x",
+              buffer[8], buffer[9], buffer[10], buffer[11]);
+    }
+  }
+  else if ((tag == 0x1001) && tagtypeIs(LIBRAW_EXIFTAG_TYPE_SHORT))
+  {
+    ilm.CameraMount = LIBRAW_MOUNT_FixedLens;
+    ilm.LensMount = LIBRAW_MOUNT_FixedLens;
+    ilm.CameraFormat = LIBRAW_FORMAT_APSC;
+    ilm.LensID = LIBRAW_LENS_NOT_SET;
+    ilm.FocalType = LIBRAW_FT_PRIME_LENS;
+    imgdata.shootinginfo.ExposureProgram = get2();
+  }
+  else if (tag == 0x1002)
+  {
+    imgdata.shootinginfo.DriveMode = get2();
+  }
+  else if (tag == 0x1006)
+  {
+    imgdata.shootinginfo.FocusMode = get2();
+  }
+  else if ((tag == 0x100b) && tagtypeIs(LIBRAW_EXIFTAG_TYPE_SRATIONAL))
+  {
+    imCommon.FlashEC = getreal(type);
+  }
+  else if ((tag == 0x1017) && (get2() == 2))
+  {
+    strcpy(ilm.Attachment, "Wide-Angle Adapter");
+  }
+  else if (tag == 0x1500)
+  {
+    ilm.CurFocal = getreal(type);
+  }
+  else if ((tag == 0x2001) && !strncmp(model, "GXR", 3))
+  {
+    short cur_tag;
+    fseek(ifp, 20, SEEK_CUR);
+    /*ntags =*/ get2();
+    cur_tag = get2();
+    while (cur_tag != 0x002c)
+    {
+      fseek(ifp, 10, SEEK_CUR);
+      cur_tag = get2();
+    }
+    fseek(ifp, 6, SEEK_CUR);
+    fseek(ifp, get4(), SEEK_SET);
+    for (int i=0; i<4; i++) {
+      stread(buffer, 16, ifp);
+      if ((buffer[0] == 'S') && (buffer[1] == 'I') && (buffer[2] == 'D'))
+        memcpy(imgdata.shootinginfo.BodySerial, buffer+4, 12);
+      else if ((buffer[0] == 'R') && (buffer[1] == 'L'))
+        ilm.LensID = buffer[2] - '0';
+      else if ((buffer[0] == 'L') && (buffer[1] == 'I') && (buffer[2] == 'D'))
+        memcpy(imgdata.lens.LensSerial, buffer+4, 12);
+    }
+  }
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/metadata/samsung.cpp libkdcraw/libkdcraw/libraw/src/metadata/samsung.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/metadata/samsung.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/metadata/samsung.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,182 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+void LibRaw::parseSamsungMakernotes(int base, unsigned tag, unsigned type,
+                                    unsigned len, unsigned dng_writer)
+{
+  int i, c;
+  if (tag == 0x0002)
+  {
+    imSamsung.DeviceType = get4();
+    if (imSamsung.DeviceType == 0x2000)
+    {
+      ilm.CameraMount = LIBRAW_MOUNT_Samsung_NX;
+      ilm.CameraFormat = LIBRAW_FORMAT_APSC;
+    }
+    else if (!strncmp(model, "NX mini", 7))
+    { // device type 0x1000: 'NX mini', EX2F, EX1, WB2000
+      ilm.CameraMount = LIBRAW_MOUNT_Samsung_NX_M;
+      ilm.CameraFormat = LIBRAW_FORMAT_1INCH;
+    }
+    else
+    {
+      ilm.CameraMount = LIBRAW_MOUNT_FixedLens;
+      ilm.LensMount = LIBRAW_MOUNT_FixedLens;
+    }
+  }
+  else if (tag == 0x0003)
+  {
+    ilm.CamID = unique_id = get4();
+  }
+  else if (tag == 0x0043)
+  {
+    if ((i = get4()))
+    {
+      imCommon.CameraTemperature = (float)i;
+      if (get4() == 10)
+        imCommon.CameraTemperature /= 10.0f;
+    }
+  }
+  else if ((tag == 0xa002) && (dng_writer != AdobeDNG))
+  {
+    stmread(imgdata.shootinginfo.BodySerial, len, ifp);
+  }
+  else if (tag == 0xa003)
+  {
+    ilm.LensID = get2();
+    if (ilm.LensID)
+      ilm.LensMount = LIBRAW_MOUNT_Samsung_NX;
+  }
+  else if (tag == 0xa004)
+  { // LensFirmware
+    stmread(imSamsung.LensFirmware, len, ifp);
+  }
+  else if (tag == 0xa005)
+  {
+    stmread(imgdata.lens.InternalLensSerial, len, ifp);
+  }
+  else if (tag == 0xa010)
+  {
+    FORC4 imSamsung.ImageSizeFull[c] = get4();
+    FORC4 imSamsung.ImageSizeCrop[c] = get4();
+  }
+  else if ((tag == 0xa011) && ((len == 1) || (len == 2)) && tagtypeIs(LIBRAW_EXIFTAG_TYPE_SHORT))
+  {
+    imSamsung.ColorSpace[0] = (int)get2();
+		switch (imSamsung.ColorSpace[0]) {
+		case 0:
+			imCommon.ColorSpace = LIBRAW_COLORSPACE_sRGB;
+			break;
+		case 1:
+			imCommon.ColorSpace = LIBRAW_COLORSPACE_AdobeRGB;
+			break;
+		default:
+			imCommon.ColorSpace = LIBRAW_COLORSPACE_Unknown;
+			break;
+		}
+    if (len == 2)
+      imSamsung.ColorSpace[1] = (int)get2();
+  }
+  else if (tag == 0xa019)
+  {
+    ilm.CurAp = getreal(type);
+  }
+  else if ((tag == 0xa01a) && (unique_id != 0x5000000) &&
+           (!imgdata.lens.FocalLengthIn35mmFormat))
+  {
+    ilm.FocalLengthIn35mmFormat = get4();
+    if (ilm.FocalLengthIn35mmFormat >= 160)
+      ilm.FocalLengthIn35mmFormat /= 10.0f;
+    if ((ilm.CameraMount == LIBRAW_MOUNT_Samsung_NX_M) &&
+        (imSamsung.LensFirmware[10] < '6'))
+      ilm.FocalLengthIn35mmFormat *= 1.6f;
+  }
+  else if (tag == 0xa020)
+  {
+    FORC(11) imSamsung.key[c] = get4();
+  }
+  else if ((tag == 0xa021) && (dng_writer == nonDNG))
+  {
+    FORC4 cam_mul[RGGB_2_RGBG(c)] = get4() - imSamsung.key[c];
+  }
+  else if (tag == 0xa022)
+  {
+    FORC4 icWBC[LIBRAW_WBI_Auto][RGGB_2_RGBG(c)] =
+        get4() - imSamsung.key[c + 4];
+    if (icWBC[LIBRAW_WBI_Auto][0] <
+        (icWBC[LIBRAW_WBI_Auto][1] >> 1))
+    {
+      icWBC[LIBRAW_WBI_Auto][1] =
+          icWBC[LIBRAW_WBI_Auto][1] >> 4;
+      icWBC[LIBRAW_WBI_Auto][3] =
+          icWBC[LIBRAW_WBI_Auto][3] >> 4;
+    }
+  }
+  else if (tag == 0xa023)
+  {
+    ushort ki[4] = {8, 9, 10, 0};
+    FORC4 icWBC[LIBRAW_WBI_Ill_A][RGGB_2_RGBG(c)] =
+        get4() - imSamsung.key[ki[c]];
+    if (icWBC[LIBRAW_WBI_Ill_A][0] <
+        (icWBC[LIBRAW_WBI_Ill_A][1] >> 1))
+    {
+      icWBC[LIBRAW_WBI_Ill_A][1] =
+          icWBC[LIBRAW_WBI_Ill_A][1] >> 4;
+      icWBC[LIBRAW_WBI_Ill_A][3] =
+          icWBC[LIBRAW_WBI_Ill_A][3] >> 4;
+    }
+  }
+  else if (tag == 0xa024)
+  {
+    FORC4 icWBC[LIBRAW_WBI_D65][RGGB_2_RGBG(c)] =
+        get4() - imSamsung.key[c + 1];
+    if (icWBC[LIBRAW_WBI_D65][0] <
+        (icWBC[LIBRAW_WBI_D65][1] >> 1))
+    {
+      icWBC[LIBRAW_WBI_D65][1] =
+          icWBC[LIBRAW_WBI_D65][1] >> 4;
+      icWBC[LIBRAW_WBI_D65][3] =
+          icWBC[LIBRAW_WBI_D65][3] >> 4;
+    }
+  }
+  else if (tag == 0xa025)
+  {
+    unsigned t = get4() + imSamsung.key[0];
+    if (t == 4096)
+      imSamsung.DigitalGain = 1.0;
+    else
+      imSamsung.DigitalGain = ((double)t) / 4096.0;
+  }
+  else if ((tag == 0xa028) && (dng_writer == nonDNG))
+  {
+    FORC4 cblack[RGGB_2_RGBG(c)] = get4() - imSamsung.key[c];
+  }
+  else if ((tag == 0xa030) && (len == 9))
+  {
+    for (i = 0; i < 3; i++)
+      FORC3 imgdata.color.ccm[i][c] =
+          (float)((short)((get4() + imSamsung.key[i * 3 + c]))) / 256.0;
+  }
+  else if ((tag == 0xa032) && (len == 9) && (dng_writer == nonDNG))
+  {
+    double aRGB_cam[3][3];
+    FORC(9)
+    ((double *)aRGB_cam)[c] =
+        ((double)((short)((get4() + imSamsung.key[c])))) / 256.0;
+    aRGB_coeff(aRGB_cam);
+  }
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/metadata/sony.cpp libkdcraw/libkdcraw/libraw/src/metadata/sony.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/metadata/sony.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/metadata/sony.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,2102 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+#include "../../internal/libraw_cameraids.h"
+
+static ushort saneSonyCameraInfo(uchar a, uchar b, uchar c, uchar d, uchar e,
+                                 uchar f)
+{
+  if ((a >> 4) > 9)
+    return 0;
+  else if ((a & 0x0f) > 9)
+    return 0;
+  else if ((b >> 4) > 9)
+    return 0;
+  else if ((b & 0x0f) > 9)
+    return 0;
+  else if ((c >> 4) > 9)
+    return 0;
+  else if ((c & 0x0f) > 9)
+    return 0;
+  else if ((d >> 4) > 9)
+    return 0;
+  else if ((d & 0x0f) > 9)
+    return 0;
+  else if ((e >> 4) > 9)
+    return 0;
+  else if ((e & 0x0f) > 9)
+    return 0;
+  else if ((f >> 4) > 9)
+    return 0;
+  else if ((f & 0x0f) > 9)
+    return 0;
+  return 1;
+}
+static float my_roundf(float x)
+{
+  float t;
+  if (x >= 0.0)
+  {
+    t = ceilf(x);
+    if (t - x > 0.5)
+      t -= 1.0;
+    return t;
+  }
+  else
+  {
+    t = ceilf(-x);
+    if (t + x > 0.5)
+      t -= 1.0;
+    return -t;
+  }
+}
+
+static ushort bcd2dec(uchar data)
+{
+  if ((data >> 4) > 9)
+    return 0;
+  else if ((data & 0x0f) > 9)
+    return 0;
+  else
+    return (data >> 4) * 10 + (data & 0x0f);
+}
+
+static uchar SonySubstitution[257] =
+    "\x00\x01\x32\xb1\x0a\x0e\x87\x28\x02\xcc\xca\xad\x1b\xdc\x08\xed\x64\x86"
+    "\xf0\x4f\x8c\x6c\xb8\xcb\x69\xc4\x2c\x03"
+    "\x97\xb6\x93\x7c\x14\xf3\xe2\x3e\x30\x8e\xd7\x60\x1c\xa1\xab\x37\xec\x75"
+    "\xbe\x23\x15\x6a\x59\x3f\xd0\xb9\x96\xb5"
+    "\x50\x27\x88\xe3\x81\x94\xe0\xc0\x04\x5c\xc6\xe8\x5f\x4b\x70\x38\x9f\x82"
+    "\x80\x51\x2b\xc5\x45\x49\x9b\x21\x52\x53"
+    "\x54\x85\x0b\x5d\x61\xda\x7b\x55\x26\x24\x07\x6e\x36\x5b\x47\xb7\xd9\x4a"
+    "\xa2\xdf\xbf\x12\x25\xbc\x1e\x7f\x56\xea"
+    "\x10\xe6\xcf\x67\x4d\x3c\x91\x83\xe1\x31\xb3\x6f\xf4\x05\x8a\x46\xc8\x18"
+    "\x76\x68\xbd\xac\x92\x2a\x13\xe9\x0f\xa3"
+    "\x7a\xdb\x3d\xd4\xe7\x3a\x1a\x57\xaf\x20\x42\xb2\x9e\xc3\x8b\xf2\xd5\xd3"
+    "\xa4\x7e\x1f\x98\x9c\xee\x74\xa5\xa6\xa7"
+    "\xd8\x5e\xb0\xb4\x34\xce\xa8\x79\x77\x5a\xc1\x89\xae\x9a\x11\x33\x9d\xf5"
+    "\x39\x19\x65\x78\x16\x71\xd2\xa9\x44\x63"
+    "\x40\x29\xba\xa0\x8f\xe4\xd6\x3b\x84\x0d\xc2\x4e\x58\xdd\x99\x22\x6b\xc9"
+    "\xbb\x17\x06\xe5\x7d\x66\x43\x62\xf6\xcd"
+    "\x35\x90\x2e\x41\x8d\x6d\xaa\x09\x73\x95\x0c\xf1\x1d\xde\x4c\x2f\x2d\xf7"
+    "\xd1\x72\xeb\xef\x48\xc7\xf8\xf9\xfa\xfb"
+    "\xfc\xfd\xfe\xff";
+
+void LibRaw::sony_decrypt(unsigned *data, int len, int start, int key)
+{
+#ifndef LIBRAW_NOTHREADS
+#define pad tls->sony_decrypt.pad
+#define p tls->sony_decrypt.p
+#else
+  static unsigned pad[128], p;
+#endif
+  if (start)
+  {
+    for (p = 0; p < 4; p++)
+      pad[p] = key = key * 48828125ULL + 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;
+    for (p = 0; p < 127; p++)
+      pad[p] = htonl(pad[p]);
+  }
+  while (len--)
+  {
+    *data++ ^= pad[p & 127] = pad[(p + 1) & 127] ^ pad[(p + 65) & 127];
+    p++;
+  }
+#ifndef LIBRAW_NOTHREADS
+#undef pad
+#undef p
+#endif
+}
+void LibRaw::setSonyBodyFeatures(unsigned long long id)
+{
+  ushort idx;
+  static const struct
+  {
+    ushort scf[11];
+    /*
+    scf[0]  camera id
+    scf[1]  camera format
+    scf[2]  camera mount: Minolta A, Sony E, fixed,
+    scf[3]  camera type: DSLR, NEX, SLT, ILCE, ILCA, DSC
+    scf[4]  lens mount
+    scf[5]  tag 0x2010 group (0 if not used)
+    scf[6]  offset of Sony ISO in 0x2010 table, 0xffff if not valid
+    scf[7]  offset of ShutterCount3 in 0x9050 table, 0xffff if not valid
+    scf[8]  offset of MeteringMode in 0x2010 table, 0xffff if not valid
+    scf[9]  offset of ExposureProgram in 0x2010 table, 0xffff if not valid
+    scf[10] offset of ReleaseMode2 in 0x2010 table, 0xffff if not valid
+    */
+  } SonyCamFeatures[] = {
+      {SonyID_DSLR_A100, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
+       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_DSLR_A900, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0, 0,
+       0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_DSLR_A700, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
+       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_DSLR_A200, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
+       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_DSLR_A350, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
+       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_DSLR_A300, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
+       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_DSLR_A900, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
+       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_DSLR_A380, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
+       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_DSLR_A330, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
+       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_DSLR_A230, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
+       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_DSLR_A290, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
+       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x10b, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x10c, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_DSLR_A850, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0, 0,
+       0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_DSLR_A850, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
+       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x10f, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x110, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_DSLR_A550, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
+       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_DSLR_A500, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
+       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_DSLR_A450, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
+       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x114, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x115, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_NEX_5, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_NEX, 0, 0,
+       0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_NEX_3, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_NEX, 0, 0,
+       0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_SLT_A33, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_SLT, 0, 0,
+       0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_SLT_A55, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_SLT, 0, 0,
+       0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_DSLR_A560, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
+       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_DSLR_A580, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_DSLR, 0,
+       0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_NEX_C3, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_NEX, 0, 0,
+       0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_SLT_A35, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_SLT, 0, 0,
+       0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_SLT_A65, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_SLT, 0, 2,
+       0x1218, 0x01bd, 0x1178, 0x1179, 0x112c},
+      {SonyID_SLT_A77, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_SLT, 0, 2,
+       0x1218, 0x01bd, 0x1178, 0x1179, 0x112c},
+      {SonyID_NEX_5N, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_NEX, 0, 1,
+       0x113e, 0x01bd, 0x1174, 0x1175, 0x112c},
+      {SonyID_NEX_7, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_NEX, 0, 2,
+       0x1218, 0x01bd, 0x1178, 0x1179, 0x112c},
+      {SonyID_NEX_VG20, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_NEX, 0, 2,
+       0x1218, 0x01bd, 0x1178, 0x1179, 0x112c},
+      {SonyID_SLT_A37, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_SLT, 0, 3,
+       0x11f4, 0x01bd, 0x1154, 0x1155, 0x1108},
+      {SonyID_SLT_A57, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_SLT, 0, 3,
+       0x11f4, 0x01bd, 0x1154, 0x1155, 0x1108},
+      {SonyID_NEX_F3, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_NEX, 0, 3,
+       0x11f4, 0x01bd, 0x1154, 0x1155, 0x1108},
+      {SonyID_SLT_A99, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_SLT, 0, 5,
+       0x1254, 0x01aa, 0x11ac, 0x11ad, 0x1160},
+      {SonyID_NEX_6, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_NEX, 0, 5,
+       0x1254, 0x01aa, 0x11ac, 0x11ad, 0x1160},
+      {SonyID_NEX_5R, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_NEX, 0, 5,
+       0x1254, 0x01aa, 0x11ac, 0x11ad, 0x1160},
+      {SonyID_DSC_RX100, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
+       LIBRAW_MOUNT_FixedLens, 5, 0x1254, 0xffff, 0x11ac, 0x11ad, 0x1160},
+      {SonyID_DSC_RX1, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
+       LIBRAW_MOUNT_FixedLens, 5, 0x1258, 0xffff, 0x11ac, 0x11ad, 0x1160},
+      {SonyID_NEX_VG900, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_NEX, 0, 5,
+       0x1254, 0x01aa, 0x11ac, 0x11ad, 0x1160},
+      {SonyID_NEX_VG30, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_NEX, 0, 5,
+       0x1254, 0x01aa, 0x11ac, 0x11ad, 0x1160},
+      {0x12d, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_ILCE_3000, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 5,
+       0x1280, 0x01aa, 0x11ac, 0x11ad, 0x1160},
+      {SonyID_SLT_A58, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_SLT, 0, 5,
+       0x1280, 0x01aa, 0x11ac, 0x11ad, 0x1160},
+      {0x130, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_NEX_3N, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_NEX, 0, 5,
+       0x1280, 0x01aa, 0x11ac, 0x11ad, 0x1160},
+      {SonyID_ILCE_7, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 7,
+       0x0344, 0xffff, 0x025c, 0x025d, 0x0210},
+      {SonyID_NEX_5T, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_NEX, 0, 5,
+       0x1254, 0x01aa, 0x11ac, 0x11ad, 0x1160},
+      {SonyID_DSC_RX100M2, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
+       LIBRAW_MOUNT_FixedLens, 6, 0x113c, 0xffff, 0x1064, 0x1065, 0x1018},
+      {SonyID_DSC_RX10, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
+       LIBRAW_MOUNT_FixedLens, 7, 0x0344, 0xffff, 0x025c, 0x025d, 0x0210},
+      {SonyID_DSC_RX1R, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
+       LIBRAW_MOUNT_FixedLens, 5, 0x1258, 0xffff, 0x11ac, 0x11ad, 0x1160},
+      {SonyID_ILCE_7R, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 7,
+       0x0344, 0xffff, 0x025c, 0x025d, 0x0210},
+      {SonyID_ILCE_6000, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 7,
+       0x0344, 0xffff, 0x025c, 0x025d, 0x0210},
+      {SonyID_ILCE_5000, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 7,
+       0x0344, 0x01aa, 0x025c, 0x025d, 0x0210},
+      {0x13a, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x13b, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x13c, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_DSC_RX100M3, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
+       LIBRAW_MOUNT_FixedLens, 7, 0x0344, 0xffff, 0x025c, 0x025d, 0x0210},
+      {SonyID_ILCE_7S, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 7,
+       0x0344, 0xffff, 0x025c, 0x025d, 0x0210},
+      {SonyID_ILCA_77M2, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_ILCA, 0,
+       7, 0x0344, 0x01a0, 0x025c, 0x025d, 0x0210},
+      {0x140, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x141, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x142, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x143, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x144, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x145, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x146, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x147, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x148, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x149, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x14a, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x14b, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x14c, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x14d, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x14e, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x14f, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x150, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x151, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x152, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_ILCE_5100, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 7,
+       0x0344, 0x01a0, 0x025c, 0x025d, 0x0210},
+      {SonyID_ILCE_7M2, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 7,
+       0x0344, 0xffff, 0x025c, 0x025d, 0x0210},
+      {SonyID_DSC_RX100M4, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
+       LIBRAW_MOUNT_FixedLens, 8, 0x0346, 0xffff, 0x025c, 0x025d, 0x0210},
+      {SonyID_DSC_RX10M2, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
+       LIBRAW_MOUNT_FixedLens, 8, 0x0346, 0xffff, 0x025c, 0x025d, 0x0210},
+      {0x157, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_DSC_RX1RM2, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
+       LIBRAW_MOUNT_FixedLens, 8, 0x0346, 0xffff, 0x025c, 0x025d, 0x0210},
+      {0x159, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_ILCE_QX1, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 7,
+       0x0344, 0x01a0, 0x025c, 0x025d, 0x0210},
+      {SonyID_ILCE_7RM2, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 8,
+       0x0346, 0x01cb, 0x025c, 0x025d, 0x0210},
+      {0x15c, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x15d, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_ILCE_7SM2, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 8,
+       0x0346, 0x01cb, 0x025c, 0x025d, 0x0210},
+      {0x15f, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {0x160, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_ILCA_68, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_ILCA, 0,
+       7, 0x0344, 0x01a0, 0x025c, 0x025d, 0x0210},
+      {SonyID_ILCA_99M2, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Minolta_A, LIBRAW_SONY_ILCA, 0, 8,
+       0x0346, 0x01cd, 0x025c, 0x025d, 0x0210},
+      {SonyID_DSC_RX10M3, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
+       LIBRAW_MOUNT_FixedLens, 8, 0x0346, 0xffff, 0x025c, 0x025d, 0x0210},
+      {SonyID_DSC_RX100M5, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
+       LIBRAW_MOUNT_FixedLens, 8, 0x0346, 0xffff, 0x025c, 0x025d, 0x0210},
+      {SonyID_ILCE_6300, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 8,
+       0x0346, 0x01cd, 0x025c, 0x025d, 0x0210},
+      {SonyID_ILCE_9, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 9,
+       0x0320, 0x019f, 0x024b, 0x024c, 0x0208},
+      {0x167, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_ILCE_6500, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 8,
+       0x0346, 0x01cd, 0x025c, 0x025d, 0x0210},
+      {0x169, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_ILCE_7RM3, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 9,
+       0x0320, 0x019f, 0x024b, 0x024c, 0x0208},
+      {SonyID_ILCE_7M3, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 9,
+       0x0320, 0x019f, 0x024b, 0x024c, 0x0208},
+      {SonyID_DSC_RX0, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
+       LIBRAW_MOUNT_FixedLens, 8, 0x0346, 0xffff, 0x025c, 0x025d, 0x0210},
+      {SonyID_DSC_RX10M4, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
+       LIBRAW_MOUNT_FixedLens, 9, 0x0320, 0xffff, 0x024b, 0x024c, 0x0208},
+      {SonyID_DSC_RX100M6, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
+       LIBRAW_MOUNT_FixedLens, 9, 0x0320, 0xffff, 0x024b, 0x024c, 0x0208},
+      {SonyID_DSC_HX99, LIBRAW_FORMAT_1div2p3INCH, LIBRAW_MOUNT_FixedLens,
+       LIBRAW_SONY_DSC, LIBRAW_MOUNT_FixedLens, 9, 0x0320, 0xffff, 0x024b,
+       0x024c, 0x0208},
+      {0x170, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_DSC_RX100M5A, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
+       LIBRAW_MOUNT_FixedLens, 9, 0x0320, 0xffff, 0x024b, 0x024c, 0x0208},
+      {0x172, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_ILCE_6400, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 9,
+       0x0320, 0x019f, 0x024b, 0x024c, 0x0208},
+      {SonyID_DSC_RX0M2, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
+       LIBRAW_MOUNT_FixedLens, 9, 0x0320, 0xffff, 0x024b, 0x024c, 0x0208},
+      {0x175, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_DSC_RX100M7, LIBRAW_FORMAT_1INCH, LIBRAW_MOUNT_FixedLens, LIBRAW_SONY_DSC,
+       LIBRAW_MOUNT_FixedLens, 9, 0x0320, 0xffff, 0x024b, 0x024c, 0x0208},
+      {SonyID_ILCE_7RM4, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 9,
+       0x0320, 0x019f, 0x024b, 0x024c, 0x0208},
+      {SonyID_ILCE_9M2, LIBRAW_FORMAT_FF, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 9,
+       0x0320, 0x019f, 0x024b, 0x024c, 0x0208},
+      {0x179, 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+      {SonyID_ILCE_6600, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 9,
+       0x0320, 0x019f, 0x024b, 0x024c, 0x0208},
+      {SonyID_ILCE_6100, LIBRAW_FORMAT_APSC, LIBRAW_MOUNT_Sony_E, LIBRAW_SONY_ILCE, 0, 9,
+       0x0320, 0x019f, 0x024b, 0x024c, 0x0208},
+
+  };
+  ilm.CamID = id;
+
+  if (id == SonyID_DSC_R1)
+  {
+    ilm.CameraMount = ilm.LensMount = LIBRAW_MOUNT_FixedLens;
+    imSony.CameraType = LIBRAW_SONY_DSC;
+    imSony.group2010 = 0;
+    imSony.real_iso_offset = 0xffff;
+    imSony.ImageCount3_offset = 0xffff;
+    return;
+  }
+  else
+    idx = id - 0x100ULL;
+
+  if ((idx >= 0) && (idx < sizeof SonyCamFeatures / sizeof *SonyCamFeatures))
+  {
+    if (!SonyCamFeatures[idx].scf[2])
+      return;
+    ilm.CameraFormat = SonyCamFeatures[idx].scf[1];
+    ilm.CameraMount = SonyCamFeatures[idx].scf[2];
+    imSony.CameraType = SonyCamFeatures[idx].scf[3];
+    if (SonyCamFeatures[idx].scf[4])
+      ilm.LensMount = SonyCamFeatures[idx].scf[4];
+    imSony.group2010 = SonyCamFeatures[idx].scf[5];
+    imSony.real_iso_offset = SonyCamFeatures[idx].scf[6];
+    imSony.ImageCount3_offset = SonyCamFeatures[idx].scf[7];
+    imSony.MeteringMode_offset = SonyCamFeatures[idx].scf[8];
+    imSony.ExposureProgram_offset = SonyCamFeatures[idx].scf[9];
+    imSony.ReleaseMode2_offset = SonyCamFeatures[idx].scf[10];
+  }
+
+  char *sbstr = strstr(software, " v");
+  if (sbstr != NULL)
+  {
+    sbstr += 2;
+    imSony.firmware = atof(sbstr);
+
+    if ((id == SonyID_ILCE_7) ||
+        (id == SonyID_ILCE_7R))
+    {
+      if (imSony.firmware < 1.2f)
+        imSony.ImageCount3_offset = 0x01aa;
+      else
+        imSony.ImageCount3_offset = 0x01c0;
+    }
+    else if (id == SonyID_ILCE_6000)
+    {
+      if (imSony.firmware < 2.0f)
+        imSony.ImageCount3_offset = 0x01aa;
+      else
+        imSony.ImageCount3_offset = 0x01c0;
+    }
+    else if ((id == SonyID_ILCE_7S) ||
+             (id == SonyID_ILCE_7M2))
+    {
+      if (imSony.firmware < 1.2f)
+        imSony.ImageCount3_offset = 0x01a0;
+      else
+        imSony.ImageCount3_offset = 0x01b6;
+    }
+  }
+}
+
+void LibRaw::parseSonyLensType2(uchar a, uchar b)
+{
+  ushort lid2;
+  lid2 = (((ushort)a) << 8) | ((ushort)b);
+//  printf ("==>> 2: lid2 %d\n", lid2);
+  if (!lid2)
+    return;
+  if (lid2 < 0x100)
+  {
+    if ((ilm.AdapterID != 0x4900) && (ilm.AdapterID != 0xef00))
+    {
+      ilm.AdapterID = lid2;
+      switch (lid2)
+      {
+      case 1:
+      case 2:
+      case 3:
+      case 6:
+        ilm.LensMount = LIBRAW_MOUNT_Minolta_A;
+        break;
+      case 44:
+      case 78:
+      case 184:
+      case 234:
+      case 239:
+        ilm.LensMount = LIBRAW_MOUNT_Canon_EF;
+        break;
+      }
+    }
+  }
+  else
+    ilm.LensID = lid2;
+
+  if ((lid2 >= 50481) &&
+      (lid2 < 50500)) {
+    strcpy(ilm.Adapter, "MC-11");
+    ilm.AdapterID = 0x4900;
+  } else if ((lid2 > 0xef00) &&
+             (lid2 < 0xffff) &&
+             (lid2 != 0xff00)) {
+    ilm.AdapterID = 0xef00;
+    ilm.LensID -= ilm.AdapterID;
+    ilm.LensMount = LIBRAW_MOUNT_Canon_EF;
+  }
+
+  return;
+}
+
+void LibRaw::parseSonyLensFeatures(uchar a, uchar b)
+{
+
+  ushort features;
+  features = (((ushort)a) << 8) | ((ushort)b);
+
+  if ((ilm.LensMount == LIBRAW_MOUNT_Canon_EF) ||
+      (ilm.LensMount != LIBRAW_MOUNT_Sigma_X3F) || !features)
+    return;
+
+  ilm.LensFeatures_pre[0] = 0;
+  ilm.LensFeatures_suf[0] = 0;
+  if ((features & 0x0200) && (features & 0x0100))
+    strcpy(ilm.LensFeatures_pre, "E");
+  else if (features & 0x0200)
+    strcpy(ilm.LensFeatures_pre, "FE");
+  else if (features & 0x0100)
+    strcpy(ilm.LensFeatures_pre, "DT");
+
+  if (!ilm.LensFormat && !ilm.LensMount)
+  {
+    ilm.LensFormat = LIBRAW_FORMAT_FF;
+    ilm.LensMount = LIBRAW_MOUNT_Minolta_A;
+
+    if ((features & 0x0200) && (features & 0x0100))
+    {
+      ilm.LensFormat = LIBRAW_FORMAT_APSC;
+      ilm.LensMount = LIBRAW_MOUNT_Sony_E;
+    }
+    else if (features & 0x0200)
+    {
+      ilm.LensMount = LIBRAW_MOUNT_Sony_E;
+    }
+    else if (features & 0x0100)
+    {
+      ilm.LensFormat = LIBRAW_FORMAT_APSC;
+    }
+  }
+
+  if (features & 0x4000)
+    strnXcat(ilm.LensFeatures_pre, " PZ");
+
+  if (features & 0x0008)
+    strnXcat(ilm.LensFeatures_suf, " G");
+  else if (features & 0x0004)
+    strnXcat(ilm.LensFeatures_suf, " ZA");
+
+  if ((features & 0x0020) && (features & 0x0040))
+    strnXcat(ilm.LensFeatures_suf, " Macro");
+  else if (features & 0x0020)
+    strnXcat(ilm.LensFeatures_suf, " STF");
+  else if (features & 0x0040)
+    strnXcat(ilm.LensFeatures_suf, " Reflex");
+  else if (features & 0x0080)
+    strnXcat(ilm.LensFeatures_suf, " Fisheye");
+
+  if (features & 0x0001)
+    strnXcat(ilm.LensFeatures_suf, " SSM");
+  else if (features & 0x0002)
+    strnXcat(ilm.LensFeatures_suf, " SAM");
+
+  if (features & 0x8000)
+    strnXcat(ilm.LensFeatures_suf, " OSS");
+
+  if (features & 0x2000)
+    strnXcat(ilm.LensFeatures_suf, " LE");
+
+  if (features & 0x0800)
+    strnXcat(ilm.LensFeatures_suf, " II");
+
+  if (ilm.LensFeatures_suf[0] == ' ')
+    memmove(ilm.LensFeatures_suf, ilm.LensFeatures_suf + 1,
+            strbuflen(ilm.LensFeatures_suf) - 1);
+
+  return;
+}
+
+void LibRaw::process_Sony_0x0116(uchar *buf, ushort len, unsigned long long id)
+{
+  int i = 0;
+
+  if (((id == SonyID_DSLR_A900)      ||
+       (id == SonyID_DSLR_A900_APSC) ||
+       (id == SonyID_DSLR_A850)      ||
+       (id == SonyID_DSLR_A850_APSC)) &&
+      (len >= 2))
+    i = 1;
+  else if ((id >= SonyID_DSLR_A550) && (len >= 3))
+    i = 2;
+  else
+    return;
+
+  imCommon.BatteryTemperature = (float)(buf[i] - 32) / 1.8f;
+}
+
+void LibRaw::process_Sony_0x2010(uchar *buf, ushort len)
+{
+  if (!imSony.group2010)
+    return;
+
+  if ((imSony.real_iso_offset != 0xffff) &&
+      (len >= (imSony.real_iso_offset + 2)) && (imCommon.real_ISO < 0.1f))
+  {
+    uchar s[2];
+    s[0] = SonySubstitution[buf[imSony.real_iso_offset]];
+    s[1] = SonySubstitution[buf[imSony.real_iso_offset + 1]];
+    imCommon.real_ISO =
+        100.0f * libraw_powf64l(2.0f, (16 - ((float)sget2(s)) / 256.0f));
+  }
+
+  if (len >= (imSony.MeteringMode_offset + 2))
+  {
+    imgdata.shootinginfo.MeteringMode =
+        SonySubstitution[buf[imSony.MeteringMode_offset]];
+    imgdata.shootinginfo.ExposureProgram =
+        SonySubstitution[buf[imSony.ExposureProgram_offset]];
+  }
+
+  if (len >= (imSony.ReleaseMode2_offset + 2))
+  {
+    imgdata.shootinginfo.DriveMode =
+        SonySubstitution[buf[imSony.ReleaseMode2_offset]];
+  }
+}
+
+void LibRaw::process_Sony_0x9050(uchar *buf, ushort len, unsigned long long id)
+{
+  ushort lid;
+  uchar s[4];
+  int c;
+
+  if ((ilm.CameraMount != LIBRAW_MOUNT_Sony_E) &&
+      (ilm.CameraMount != LIBRAW_MOUNT_FixedLens))
+  {
+    if (len < 2)
+      return;
+    if (buf[0])
+      ilm.MaxAp4CurFocal =
+        my_roundf(
+          libraw_powf64l(2.0f, ((float)SonySubstitution[buf[0]] / 8.0 - 1.06f) / 2.0f) *
+             10.0f) / 10.0f;
+
+    if (buf[1])
+      ilm.MinAp4CurFocal =
+        my_roundf(
+          libraw_powf64l(2.0f, ((float)SonySubstitution[buf[1]] / 8.0 - 1.06f) / 2.0f) *
+             10.0f) / 10.0f;
+  }
+
+  if (ilm.CameraMount != LIBRAW_MOUNT_FixedLens)
+  {
+    if (len <= 0x106)
+      return;
+    if (buf[0x3d] | buf[0x3c])
+    {
+      lid = SonySubstitution[buf[0x3d]] << 8 | SonySubstitution[buf[0x3c]];
+      ilm.CurAp = libraw_powf64l(2.0f, ((float)lid / 256.0f - 16.0f) / 2.0f);
+    }
+    if (buf[0x105] &&
+        (ilm.LensMount != LIBRAW_MOUNT_Canon_EF) &&
+        (ilm.LensMount != LIBRAW_MOUNT_Sigma_X3F)) {
+      switch (SonySubstitution[buf[0x105]]) {
+        case 1:
+          ilm.LensMount = LIBRAW_MOUNT_Minolta_A;
+          break;
+        case 2:
+          ilm.LensMount = LIBRAW_MOUNT_Sony_E;
+        break;
+      }
+    }
+    if (buf[0x106]) {
+      switch (SonySubstitution[buf[0x106]]) {
+        case 1:
+          ilm.LensFormat = LIBRAW_FORMAT_APSC;
+          break;
+        case 2:
+          ilm.LensFormat = LIBRAW_FORMAT_FF;
+        break;
+      }
+    }
+  }
+
+  if (ilm.CameraMount == LIBRAW_MOUNT_Sony_E)
+  {
+    if (len <= 0x108)
+      return;
+    parseSonyLensType2(
+        SonySubstitution[buf[0x0108]], // LensType2 - Sony lens ids
+        SonySubstitution[buf[0x0107]]);
+  }
+
+  if (len <= 0x10a)
+    return;
+  if ((ilm.LensID == LIBRAW_LENS_NOT_SET) && (ilm.CameraMount == LIBRAW_MOUNT_Minolta_A) &&
+      (buf[0x010a] | buf[0x0109]))
+  {
+    ilm.LensID = // LensType - Minolta/Sony lens ids
+        SonySubstitution[buf[0x010a]] << 8 | SonySubstitution[buf[0x0109]];
+
+    if ((ilm.LensID > 0x4900) && (ilm.LensID <= 0x5900))
+    {
+      ilm.AdapterID = 0x4900;
+      ilm.LensID -= ilm.AdapterID;
+      ilm.LensMount = LIBRAW_MOUNT_Sigma_X3F;
+      strcpy(ilm.Adapter, "MC-11");
+    }
+
+    else if ((ilm.LensID > 0xef00) && (ilm.LensID < 0xffff) &&
+             (ilm.LensID != 0xff00))
+    {
+      ilm.AdapterID = 0xef00;
+      ilm.LensID -= ilm.AdapterID;
+      ilm.LensMount = LIBRAW_MOUNT_Canon_EF;
+    }
+  }
+
+  if ((id >= SonyID_SLT_A65) && (id <= SonyID_NEX_F3))
+  {
+    if (len <= 0x116)
+      return;
+    // "SLT-A65", "SLT-A77", "NEX-7", "NEX-VG20",
+    // "SLT-A37", "SLT-A57", "NEX-F3", "Lunar"
+    parseSonyLensFeatures(SonySubstitution[buf[0x115]],
+                          SonySubstitution[buf[0x116]]);
+  }
+  else if (ilm.CameraMount != LIBRAW_MOUNT_FixedLens)
+  {
+    if (len <= 0x117)
+      return;
+    parseSonyLensFeatures(SonySubstitution[buf[0x116]],
+                          SonySubstitution[buf[0x117]]);
+  }
+
+  if ((id == SonyID_ILCE_7RM2) ||
+      (id == SonyID_ILCE_7SM2) ||
+      (id == SonyID_ILCA_99M2) ||
+      (id == SonyID_ILCE_6300) ||
+      (id == SonyID_ILCE_9)    ||
+      (id == SonyID_ILCE_6500) ||
+      (id == SonyID_ILCE_7RM3) ||
+      (id == SonyID_ILCE_7M3)  ||
+      (id == SonyID_ILCE_6400) ||
+      (id == SonyID_ILCE_7RM4) ||
+      (id == SonyID_ILCE_9M2)  ||
+      (id == SonyID_ILCE_6600) ||
+      (id == SonyID_ILCE_6100))
+  {
+    if (len <= 0x8d)
+      return;
+    unsigned long long b88 = SonySubstitution[buf[0x88]];
+    unsigned long long b89 = SonySubstitution[buf[0x89]];
+    unsigned long long b8a = SonySubstitution[buf[0x8a]];
+    unsigned long long b8b = SonySubstitution[buf[0x8b]];
+    unsigned long long b8c = SonySubstitution[buf[0x8c]];
+    unsigned long long b8d = SonySubstitution[buf[0x8d]];
+    sprintf(imgdata.shootinginfo.InternalBodySerial, "%06llx",
+            (b88 << 40) + (b89 << 32) + (b8a << 24) + (b8b << 16) + (b8c << 8) +
+                b8d);
+  }
+  else if (ilm.CameraMount == LIBRAW_MOUNT_Minolta_A)
+  {
+    if (len <= 0xf4)
+      return;
+    unsigned long long bf0 = SonySubstitution[buf[0xf0]];
+    unsigned long long bf1 = SonySubstitution[buf[0xf1]];
+    unsigned long long bf2 = SonySubstitution[buf[0xf2]];
+    unsigned long long bf3 = SonySubstitution[buf[0xf3]];
+    unsigned long long bf4 = SonySubstitution[buf[0xf4]];
+    sprintf(imgdata.shootinginfo.InternalBodySerial, "%05llx",
+            (bf0 << 32) + (bf1 << 24) + (bf2 << 16) + (bf3 << 8) + bf4);
+  }
+  else if ((ilm.CameraMount == LIBRAW_MOUNT_Sony_E) &&
+           (id != SonyID_NEX_5N) &&
+           (id != SonyID_NEX_7)  &&
+           (id != SonyID_NEX_VG20))
+  {
+    if (len <= 0x7f)
+      return;
+    unsigned b7c = SonySubstitution[buf[0x7c]];
+    unsigned b7d = SonySubstitution[buf[0x7d]];
+    unsigned b7e = SonySubstitution[buf[0x7e]];
+    unsigned b7f = SonySubstitution[buf[0x7f]];
+    sprintf(imgdata.shootinginfo.InternalBodySerial, "%04x",
+            (b7c << 24) + (b7d << 16) + (b7e << 8) + b7f);
+  }
+
+  if ((imSony.ImageCount3_offset != 0xffff) &&
+      (len >= (imSony.ImageCount3_offset + 4)))
+  {
+    FORC4 s[c] = SonySubstitution[buf[imSony.ImageCount3_offset + c]];
+    imSony.ImageCount3 = sget4(s);
+  }
+
+  return;
+}
+
+void LibRaw::process_Sony_0x9400(uchar *buf, ushort len, unsigned long long id)
+{
+
+  uchar s[4];
+  int c;
+  uchar bufx = buf[0];
+
+  if (((bufx == 0x23) || (bufx == 0x24) || (bufx == 0x26) || (bufx == 0x28)) &&
+      (len >= 0x1f))
+  { // 0x9400 'c' version
+
+    if ((id == SonyID_ILCE_9)       ||
+        (id == SonyID_ILCE_7RM3)    ||
+        (id == SonyID_ILCE_7M3)     ||
+        (id == SonyID_DSC_RX10M4)   ||
+        (id == SonyID_DSC_RX100M6)  ||
+        (id == SonyID_DSC_HX99)     ||
+        (id == SonyID_DSC_RX100M5A) ||
+        (id == SonyID_ILCE_6400)    ||
+        (id == SonyID_DSC_RX0M2)    ||
+        (id == SonyID_DSC_RX100M7)  ||
+        (id == SonyID_ILCE_7RM4)    ||
+        (id == SonyID_ILCE_9M2)     ||
+        (id == SonyID_ILCE_6600)    ||
+        (id == SonyID_ILCE_6100))
+    {
+      imSony.ShotNumberSincePowerUp = SonySubstitution[buf[0x0a]];
+    }
+    else
+    {
+      FORC4 s[c] = SonySubstitution[buf[0x0a + c]];
+      imSony.ShotNumberSincePowerUp = sget4(s);
+    }
+
+    imSony.Sony0x9400_version = 0xc;
+
+    imSony.Sony0x9400_ReleaseMode2 = SonySubstitution[buf[0x09]];
+
+    FORC4 s[c] = SonySubstitution[buf[0x12 + c]];
+    imSony.Sony0x9400_SequenceImageNumber = sget4(s);
+
+    imSony.Sony0x9400_SequenceLength1 = SonySubstitution[buf[0x16]]; // shots
+
+    FORC4 s[c] = SonySubstitution[buf[0x1a + c]];
+    imSony.Sony0x9400_SequenceFileNumber = sget4(s);
+
+    imSony.Sony0x9400_SequenceLength2 = SonySubstitution[buf[0x1e]]; // files
+  }
+
+  else if ((bufx == 0x0c) && (len >= 0x1f))
+  { // 0x9400 'b' version
+    imSony.Sony0x9400_version = 0xb;
+
+    FORC4 s[c] = SonySubstitution[buf[0x08 + c]];
+    imSony.Sony0x9400_SequenceImageNumber = sget4(s);
+
+    FORC4 s[c] = SonySubstitution[buf[0x0c + c]];
+    imSony.Sony0x9400_SequenceFileNumber = sget4(s);
+
+    imSony.Sony0x9400_ReleaseMode2 = SonySubstitution[buf[0x10]];
+
+    imSony.Sony0x9400_SequenceLength1 = SonySubstitution[buf[0x1e]];
+  }
+
+  else if ((bufx == 0x0a) && (len >= 0x23))
+  { // 0x9400 'a' version
+    imSony.Sony0x9400_version = 0xa;
+
+    FORC4 s[c] = SonySubstitution[buf[0x08 + c]];
+    imSony.Sony0x9400_SequenceImageNumber = sget4(s);
+
+    FORC4 s[c] = SonySubstitution[buf[0x0c + c]];
+    imSony.Sony0x9400_SequenceFileNumber = sget4(s);
+
+    imSony.Sony0x9400_ReleaseMode2 = SonySubstitution[buf[0x10]];
+
+    imSony.Sony0x9400_SequenceLength1 = SonySubstitution[buf[0x22]];
+  }
+
+  else
+    return;
+}
+
+void LibRaw::process_Sony_0x9402(uchar *buf, ushort len)
+{
+
+  if (len < 23)
+    return;
+
+  imgdata.shootinginfo.FocusMode = SonySubstitution[buf[0x16]];
+
+  if ((imSony.CameraType == LIBRAW_SONY_SLT) ||
+      (imSony.CameraType == LIBRAW_SONY_ILCA))
+    return;
+
+  uchar bufx = buf[0x00];
+  if ((bufx == 0x05) || (bufx == 0xff) || (buf[0x02] != 0xff))
+    return;
+
+  imCommon.AmbientTemperature =
+      (float)((short)SonySubstitution[buf[0x04]]);
+
+  return;
+}
+
+void LibRaw::process_Sony_0x9403(uchar *buf, ushort len)
+{
+  if (len < 6)
+    return;
+  uchar bufx = SonySubstitution[buf[4]];
+  if ((bufx == 0x00) || (bufx == 0x94))
+    return;
+
+  imCommon.SensorTemperature = (float)((short)SonySubstitution[buf[5]]);
+
+  return;
+}
+
+void LibRaw::process_Sony_0x9406(uchar *buf, ushort len)
+{
+  if (len < 6)
+    return;
+  uchar bufx = buf[0];
+  if ((bufx != 0x01) && (bufx != 0x08) && (bufx != 0x1b))
+    return;
+  bufx = buf[2];
+  if ((bufx != 0x08) && (bufx != 0x1b))
+    return;
+
+  imCommon.BatteryTemperature =
+      (float)(SonySubstitution[buf[5]] - 32) / 1.8f;
+
+  return;
+}
+
+void LibRaw::process_Sony_0x940c(uchar *buf, ushort len)
+{
+  if ((imSony.CameraType != LIBRAW_SONY_ILCE) &&
+      (imSony.CameraType != LIBRAW_SONY_NEX))
+    return;
+  if (len <= 0x000a)
+    return;
+
+  ushort lid2;
+  if ((ilm.LensMount != LIBRAW_MOUNT_Canon_EF) &&
+      (ilm.LensMount != LIBRAW_MOUNT_Sigma_X3F))
+  {
+    switch (SonySubstitution[buf[0x0008]])
+    {
+    case 1:
+    case 5:
+      ilm.LensMount = LIBRAW_MOUNT_Minolta_A;
+      break;
+    case 4:
+      ilm.LensMount = LIBRAW_MOUNT_Sony_E;
+      break;
+    }
+  }
+  lid2 = (((ushort)SonySubstitution[buf[0x000a]]) << 8) |
+         ((ushort)SonySubstitution[buf[0x0009]]);
+  if ((lid2 > 0) &&
+      ((lid2 < 32784) || (ilm.LensID == 0x1999) || (ilm.LensID == 0xffff)))
+    parseSonyLensType2(
+        SonySubstitution[buf[0x000a]], // LensType2 - Sony lens ids
+        SonySubstitution[buf[0x0009]]);
+  if ((lid2 == 44) || (lid2 == 78) || (lid2 == 184) || (lid2 == 234) ||
+      (lid2 == 239))
+    ilm.AdapterID = lid2;
+  return;
+}
+
+void LibRaw::process_Sony_0x940e(uchar *buf, ushort len, unsigned long long id)
+{
+  if (((imSony.CameraType != LIBRAW_SONY_SLT) &&
+       (imSony.CameraType != LIBRAW_SONY_ILCA)) ||
+      (id == SonyID_SLT_A33)  ||
+      (id == SonyID_SLT_A55) ||
+      (id == SonyID_SLT_A35)  ||
+      (len < 3))
+    return;
+
+  imSony.AFType = SonySubstitution[buf[0x02]];
+
+  if (imSony.CameraType == LIBRAW_SONY_ILCA)
+  {
+    if (len >= 0x06)
+    {
+      imgdata.shootinginfo.FocusMode = SonySubstitution[buf[0x05]];
+    }
+    if (len >= 0x0051)
+    {
+      imSony.AFMicroAdjValue = SonySubstitution[buf[0x0050]];
+    }
+  }
+  else
+  {
+    if (len >= 0x0c)
+    {
+      imgdata.shootinginfo.FocusMode = SonySubstitution[buf[0x0b]];
+    }
+    if (len >= 0x017e)
+    {
+      imSony.AFMicroAdjValue = SonySubstitution[buf[0x017d]];
+    }
+  }
+
+  if (imSony.AFMicroAdjValue != 0)
+    imSony.AFMicroAdjOn = 1;
+}
+
+void LibRaw::parseSonyMakernotes(
+    int base, unsigned tag, unsigned type, unsigned len, unsigned dng_writer,
+    uchar *&table_buf_0x0116, ushort &table_buf_0x0116_len,
+    uchar *&table_buf_0x2010, ushort &table_buf_0x2010_len,
+    uchar *&table_buf_0x9050, ushort &table_buf_0x9050_len,
+    uchar *&table_buf_0x9400, ushort &table_buf_0x9400_len,
+    uchar *&table_buf_0x9402, ushort &table_buf_0x9402_len,
+    uchar *&table_buf_0x9403, ushort &table_buf_0x9403_len,
+    uchar *&table_buf_0x9406, ushort &table_buf_0x9406_len,
+    uchar *&table_buf_0x940c, ushort &table_buf_0x940c_len,
+    uchar *&table_buf_0x940e, ushort &table_buf_0x940e_len)
+{
+
+  ushort lid, a, c, d;
+  uchar *table_buf;
+  uchar uc;
+  uchar s[2];
+  int LensDataValid = 0;
+  unsigned uitemp;
+
+  if (tag == 0xb001)
+  { // Sony ModelID
+    unique_id = get2();
+    setSonyBodyFeatures(unique_id);
+
+    if (table_buf_0x0116_len)
+    {
+      process_Sony_0x0116(table_buf_0x0116, table_buf_0x0116_len, unique_id);
+      free(table_buf_0x0116);
+      table_buf_0x0116_len = 0;
+    }
+
+    if (table_buf_0x2010_len)
+    {
+      process_Sony_0x2010(table_buf_0x2010, table_buf_0x2010_len);
+      free(table_buf_0x2010);
+      table_buf_0x2010_len = 0;
+    }
+
+    if (table_buf_0x9050_len)
+    {
+      process_Sony_0x9050(table_buf_0x9050, table_buf_0x9050_len, unique_id);
+      free(table_buf_0x9050);
+      table_buf_0x9050_len = 0;
+    }
+
+    if (table_buf_0x9400_len)
+    {
+      process_Sony_0x9400(table_buf_0x9400, table_buf_0x9400_len, unique_id);
+      free(table_buf_0x9400);
+      table_buf_0x9400_len = 0;
+    }
+
+    if (table_buf_0x9402_len)
+    {
+      process_Sony_0x9402(table_buf_0x9402, table_buf_0x9402_len);
+      free(table_buf_0x9402);
+      table_buf_0x9402_len = 0;
+    }
+
+    if (table_buf_0x9403_len)
+    {
+      process_Sony_0x9403(table_buf_0x9403, table_buf_0x9403_len);
+      free(table_buf_0x9403);
+      table_buf_0x9403_len = 0;
+    }
+
+    if (table_buf_0x9406_len)
+    {
+      process_Sony_0x9406(table_buf_0x9406, table_buf_0x9406_len);
+      free(table_buf_0x9406);
+      table_buf_0x9406_len = 0;
+    }
+
+    if (table_buf_0x940c_len)
+    {
+      process_Sony_0x940c(table_buf_0x940c, table_buf_0x940c_len);
+      free(table_buf_0x940c);
+      table_buf_0x940c_len = 0;
+    }
+
+    if (table_buf_0x940e_len)
+    {
+      process_Sony_0x940e(table_buf_0x940e, table_buf_0x940e_len, unique_id);
+      free(table_buf_0x940e);
+      table_buf_0x940e_len = 0;
+    }
+  }
+  else if (tag == 0xb000)
+  {
+    FORC4 imSony.FileFormat = imSony.FileFormat * 10 + fgetc(ifp);
+  }
+  else if (tag == 0xb026)
+  {
+    uitemp = get4();
+    if (uitemp != 0xffffffff)
+      imgdata.shootinginfo.ImageStabilization = uitemp;
+  }
+  else if (((tag == 0x0001) || // Minolta CameraSettings, big endian
+            (tag == 0x0003)) &&
+           (len >= 196))
+  {
+    table_buf = (uchar *)malloc(len);
+    fread(table_buf, len, 1, ifp);
+
+    lid = 0x01 << 2;
+    imgdata.shootinginfo.ExposureMode =
+        (unsigned)table_buf[lid] << 24 | (unsigned)table_buf[lid + 1] << 16 |
+        (unsigned)table_buf[lid + 2] << 8 | (unsigned)table_buf[lid + 3];
+
+    lid = 0x06 << 2;
+    imgdata.shootinginfo.DriveMode =
+        (unsigned)table_buf[lid] << 24 | (unsigned)table_buf[lid + 1] << 16 |
+        (unsigned)table_buf[lid + 2] << 8 | (unsigned)table_buf[lid + 3];
+
+    lid = 0x07 << 2;
+    imgdata.shootinginfo.MeteringMode =
+        (unsigned)table_buf[lid] << 24 | (unsigned)table_buf[lid + 1] << 16 |
+        (unsigned)table_buf[lid + 2] << 8 | (unsigned)table_buf[lid + 3];
+
+    lid = 0x25 << 2;
+    imSony.MinoltaCamID =
+        (unsigned)table_buf[lid] << 24 | (unsigned)table_buf[lid + 1] << 16 |
+        (unsigned)table_buf[lid + 2] << 8 | (unsigned)table_buf[lid + 3];
+    if (imSony.MinoltaCamID != 0xffffffff)
+      ilm.CamID = imSony.MinoltaCamID;
+
+    lid = 0x30 << 2;
+    imgdata.shootinginfo.FocusMode =
+        (unsigned)table_buf[lid] << 24 | (unsigned)table_buf[lid + 1] << 16 |
+        (unsigned)table_buf[lid + 2] << 8 | (unsigned)table_buf[lid + 3];
+
+    free(table_buf);
+  }
+  else if ((tag == 0x0004) && // Minolta CameraSettings7D, big endian
+           (len >= 227))
+  {
+    table_buf = (uchar *)malloc(len);
+    fread(table_buf, len, 1, ifp);
+
+    lid = 0x0;
+    imgdata.shootinginfo.ExposureMode =
+        (ushort)table_buf[lid] << 8 | (ushort)table_buf[lid + 1];
+
+    lid = 0x0e << 1;
+    imgdata.shootinginfo.FocusMode =
+        (ushort)table_buf[lid] << 8 | (ushort)table_buf[lid + 1];
+
+    lid = 0x10 << 1;
+    imgdata.shootinginfo.AFPoint =
+        (ushort)table_buf[lid] << 8 | (ushort)table_buf[lid + 1];
+
+    lid = 0x25 << 1;
+    switch ((ushort)table_buf[lid] << 8 | (ushort)table_buf[lid + 1]) {
+    case 0:
+    case 1:
+      imCommon.ColorSpace = LIBRAW_COLORSPACE_sRGB;
+      break;
+    case 4:
+      imCommon.ColorSpace = LIBRAW_COLORSPACE_AdobeRGB;
+      break;
+    default:
+      imCommon.ColorSpace = LIBRAW_COLORSPACE_Unknown;
+      break;
+    }
+
+    lid = 0x71 << 1;
+    imgdata.shootinginfo.ImageStabilization =
+        (ushort)table_buf[lid] << 8 | (ushort)table_buf[lid + 1];
+
+    free(table_buf);
+  }
+  else if ((tag == 0x0010) && // CameraInfo
+           strncasecmp(model, "DSLR-A100", 9) &&
+           !strncasecmp(make, "SONY", 4) &&
+           ((len == 368) ||  // a700                         : CameraInfo
+            (len == 5478) || // a850, a900                   : CameraInfo
+            (len == 5506) || // a200, a300, a350             : CameraInfo2
+            (len == 6118) || // a230, a290, a330, a380, a390 : CameraInfo2
+            (len == 15360))  // a450, a500, a550, a560, a580 : CameraInfo3
+                             // a33, a35, a55
+                             // NEX-3, NEX-5, NEX-5C, NEX-C3, NEX-VG10E
+
+  )
+  {
+    table_buf = (uchar *)malloc(len);
+    fread(table_buf, len, 1, ifp);
+    if (memcmp(table_buf, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) &&
+        memcmp(table_buf, "\x00\x00\x00\x00\x00\x00\x00\x00", 8))
+    {
+      LensDataValid = 1;
+    }
+    switch (len)
+    {
+    case 368:  // a700: CameraInfo
+    case 5478: // a850, a900: CameraInfo
+      if ((!dng_writer) ||
+          (saneSonyCameraInfo(table_buf[0], table_buf[3], table_buf[2],
+                              table_buf[5], table_buf[4], table_buf[7])))
+      {
+        if (LensDataValid)
+        {
+          if (table_buf[0] | table_buf[3])
+            ilm.MinFocal = bcd2dec(table_buf[0]) * 100 + bcd2dec(table_buf[3]);
+          if (table_buf[2] | table_buf[5])
+            ilm.MaxFocal = bcd2dec(table_buf[2]) * 100 + bcd2dec(table_buf[5]);
+          if (table_buf[4])
+            ilm.MaxAp4MinFocal = bcd2dec(table_buf[4]) / 10.0f;
+          if (table_buf[4])
+            ilm.MaxAp4MaxFocal = bcd2dec(table_buf[7]) / 10.0f;
+          parseSonyLensFeatures(table_buf[1], table_buf[6]);
+        }
+
+        imSony.AFPointSelected = table_buf[21];
+        imgdata.shootinginfo.AFPoint = (ushort)table_buf[25];
+
+        if (len == 5478)
+        {
+          imSony.AFMicroAdjValue = table_buf[304] - 20;
+          imSony.AFMicroAdjOn = (((table_buf[305] & 0x80) == 0x80) ? 1 : 0);
+          imSony.AFMicroAdjRegisteredLenses = table_buf[305] & 0x7f;
+        }
+      }
+      break;
+    default:
+      // CameraInfo2 & 3
+      if ((!dng_writer) ||
+          (saneSonyCameraInfo(table_buf[1], table_buf[2], table_buf[3],
+                              table_buf[4], table_buf[5], table_buf[6])))
+      {
+        if ((LensDataValid) && strncasecmp(model, "NEX-5C", 6))
+        {
+          if (table_buf[1] | table_buf[2])
+            ilm.MinFocal = bcd2dec(table_buf[1]) * 100 + bcd2dec(table_buf[2]);
+          if (table_buf[3] | table_buf[4])
+            ilm.MaxFocal = bcd2dec(table_buf[3]) * 100 + bcd2dec(table_buf[4]);
+          if (table_buf[5])
+            ilm.MaxAp4MinFocal = bcd2dec(table_buf[5]) / 10.0f;
+          if (table_buf[6])
+            ilm.MaxAp4MaxFocal = bcd2dec(table_buf[6]) / 10.0f;
+          parseSonyLensFeatures(table_buf[0], table_buf[7]);
+        }
+
+        if (!strncasecmp(model, "DSLR-A450", 9) ||
+            !strncasecmp(model, "DSLR-A500", 9) ||
+            !strncasecmp(model, "DSLR-A550", 9))
+        {
+          imSony.AFPointSelected = table_buf[0x14];
+          imgdata.shootinginfo.FocusMode = table_buf[0x15];
+          imgdata.shootinginfo.AFPoint = (ushort)table_buf[0x18];
+        }
+        else if (!strncasecmp(model, "SLT-", 4) ||
+                 !strncasecmp(model, "DSLR-A560", 9) ||
+                 !strncasecmp(model, "DSLR-A580", 9))
+        {
+          imSony.AFPointSelected = table_buf[0x1c];
+          imgdata.shootinginfo.FocusMode = table_buf[0x1d];
+          imgdata.shootinginfo.AFPoint = (ushort)table_buf[0x20];
+        }
+      }
+    }
+    free(table_buf);
+  }
+  else if ((!dng_writer) && ((tag == 0x0020) || (tag == 0xb0280020)))
+  {
+    if (!strncasecmp(model, "DSLR-A100", 9))
+    { // WBInfoA100
+      fseek(ifp, 0x49dc, SEEK_CUR);
+      stmread(imgdata.shootinginfo.InternalBodySerial, 13, ifp);
+    }
+    else if ((len ==
+              19154) || // a200 a230 a290 a300 a330 a350 a380 a390 : FocusInfo
+             (len == 19148))
+    { // a700 a850 a900                          : FocusInfo
+      table_buf = (uchar *)malloc(128);
+      fread(table_buf, 128, 1, ifp);
+      imgdata.shootinginfo.DriveMode = table_buf[14];
+      imgdata.shootinginfo.ExposureProgram = table_buf[63];
+      free(table_buf);
+    }
+    else if (len == 20480) // a450 a500 a550 a560 a580 a33 a35 a55 : MoreInfo
+                           // NEX-3 NEX-5 NEX-C3 NEX-VG10E         : MoreInfo
+    {
+      a = get2();
+      /*b =*/ get2();
+      c = get2();
+      d = get2();
+      if ((a) && (c == 1))
+      {
+        fseek(ifp, d - 8, SEEK_CUR);
+        table_buf = (uchar *)malloc(256);
+        fread(table_buf, 256, 1, ifp);
+        imgdata.shootinginfo.DriveMode = table_buf[1];
+        imgdata.shootinginfo.ExposureProgram = table_buf[2];
+        imgdata.shootinginfo.MeteringMode = table_buf[3];
+        switch (table_buf[6]) {
+        case 1:
+          imCommon.ColorSpace = LIBRAW_COLORSPACE_sRGB;
+          break;
+        case 2:
+          imCommon.ColorSpace = LIBRAW_COLORSPACE_AdobeRGB;
+          break;
+        default:
+          imCommon.ColorSpace = LIBRAW_COLORSPACE_Unknown;
+          break;
+        }
+        if (strncasecmp(model, "DSLR-A450", 9) &&
+            strncasecmp(model, "DSLR-A500", 9) &&
+            strncasecmp(model, "DSLR-A550", 9))
+          imgdata.shootinginfo.FocusMode = table_buf[0x13];
+        else
+          imgdata.shootinginfo.FocusMode = table_buf[0x2c];
+        free(table_buf);
+      }
+    }
+  }
+  else if (tag == 0x0102)
+  {
+    imSony.Quality = get4();
+  }
+  else if (tag == 0x0104)
+  {
+    imCommon.FlashEC = getreal(type);
+  }
+  else if (tag == 0x0105)
+  { // Teleconverter
+    ilm.TeleconverterID = get4();
+  }
+  else if (tag == 0x0107)
+  {
+    uitemp = get4();
+    if (uitemp == 1)
+      imgdata.shootinginfo.ImageStabilization = 0;
+    else if (uitemp == 5)
+      imgdata.shootinginfo.ImageStabilization = 1;
+    else
+      imgdata.shootinginfo.ImageStabilization = uitemp;
+  }
+  else if ((tag == 0xb0280088) && (dng_writer == nonDNG))
+  {
+    thumb_offset = get4() + base;
+  }
+  else if ((tag == 0xb0280089) && (dng_writer == nonDNG))
+  {
+    thumb_length = get4();
+  }
+  else if (((tag == 0x0114) || // CameraSettings
+            (tag == 0xb0280114)) &&
+           (len < 256000))
+  {
+    table_buf = (uchar *)malloc(len);
+    fread(table_buf, len, 1, ifp);
+    switch (len)
+    {
+    case 260: // Sony a100, big endian
+      imgdata.shootinginfo.ExposureMode =
+          ((ushort)table_buf[0]) << 8 | ((ushort)table_buf[1]);
+      lid = 0x0a << 1;
+      imgdata.shootinginfo.DriveMode =
+          ((ushort)table_buf[lid]) << 8 | ((ushort)table_buf[lid + 1]);
+      lid = 0x0c << 1;
+      imgdata.shootinginfo.FocusMode =
+          ((ushort)table_buf[lid]) << 8 | ((ushort)table_buf[lid + 1]);
+      lid = 0x0d << 1;
+      imSony.AFPointSelected = table_buf[lid + 1];
+      lid = 0x0e << 1;
+      imSony.AFAreaModeSetting = table_buf[lid + 1];
+      lid = 0x12 << 1;
+      imgdata.shootinginfo.MeteringMode =
+          ((ushort)table_buf[lid]) << 8 | ((ushort)table_buf[lid + 1]);
+
+      lid = 0x17 << 1;
+      switch ((ushort)table_buf[lid] << 8 | (ushort)table_buf[lid + 1]) {
+      case 0:
+        imCommon.ColorSpace = LIBRAW_COLORSPACE_sRGB;
+        break;
+      case 2:
+        imCommon.ColorSpace = LIBRAW_COLORSPACE_MonochromeGamma;
+        break;
+      case 5:
+        imCommon.ColorSpace = LIBRAW_COLORSPACE_AdobeRGB;
+        break;
+      default:
+        imCommon.ColorSpace = LIBRAW_COLORSPACE_Unknown;
+        break;
+      }
+
+      break;
+    case 448: // Minolta "DYNAX 5D" and its aliases, big endian
+      lid = 0x0a << 1;
+      imgdata.shootinginfo.ExposureMode =
+          ((ushort)table_buf[lid]) << 8 | ((ushort)table_buf[lid + 1]);
+      lid = 0x25 << 1;
+      imgdata.shootinginfo.MeteringMode =
+          ((ushort)table_buf[lid]) << 8 | ((ushort)table_buf[lid + 1]);
+
+      lid = 0x2f << 1;
+      switch ((ushort)table_buf[lid] << 8 | (ushort)table_buf[lid + 1]) {
+      case 0:
+      case 1:
+        imCommon.ColorSpace = LIBRAW_COLORSPACE_sRGB;
+        break;
+      case 2:
+        imCommon.ColorSpace = LIBRAW_COLORSPACE_MonochromeGamma;
+        break;
+      case 4:
+      case 5:
+        imCommon.ColorSpace = LIBRAW_COLORSPACE_AdobeRGB;
+        break;
+      default:
+        imCommon.ColorSpace = LIBRAW_COLORSPACE_Unknown;
+        break;
+      }
+
+      lid = 0xbd << 1;
+      imgdata.shootinginfo.ImageStabilization =
+          ((ushort)table_buf[lid]) << 8 | ((ushort)table_buf[lid + 1]);
+      break;
+    case 280: // a200 a300 a350 a700
+    case 364: // a850 a900
+      // CameraSettings and CameraSettings2 are big endian
+      if (table_buf[2] | table_buf[3])
+      {
+        lid = (((ushort)table_buf[2]) << 8) | ((ushort)table_buf[3]);
+        ilm.CurAp = libraw_powf64l(2.0f, ((float)lid / 8.0f - 1.0f) / 2.0f);
+      }
+      lid = 0x04 << 1;
+      imgdata.shootinginfo.DriveMode = table_buf[lid + 1];
+      lid = 0x1b << 1;
+      switch (((ushort)table_buf[lid]) << 8 | ((ushort)table_buf[lid + 1])) {
+      case 0:
+        imCommon.ColorSpace = LIBRAW_COLORSPACE_sRGB;
+        break;
+      case 1:
+      case 5:
+        imCommon.ColorSpace = LIBRAW_COLORSPACE_AdobeRGB;
+        break;
+      default:
+        imCommon.ColorSpace = LIBRAW_COLORSPACE_Unknown;
+        break;
+      }
+      lid = 0x4d << 1;
+      imgdata.shootinginfo.FocusMode =
+          ((ushort)table_buf[lid]) << 8 | ((ushort)table_buf[lid + 1]);
+      if (!imCommon.ColorSpace ||
+          (imCommon.ColorSpace == LIBRAW_COLORSPACE_Unknown)) {
+        lid = 0x83 << 1;
+        switch (((ushort)table_buf[lid]) << 8 | ((ushort)table_buf[lid + 1])) {
+        case 6:
+          imCommon.ColorSpace = LIBRAW_COLORSPACE_sRGB;
+          break;
+        case 5:
+          imCommon.ColorSpace = LIBRAW_COLORSPACE_AdobeRGB;
+          break;
+        default:
+          imCommon.ColorSpace = LIBRAW_COLORSPACE_Unknown;
+          break;
+        }
+      }
+      break;
+    case 332: // a230 a290 a330 a380 a390
+      // CameraSettings and CameraSettings2 are big endian
+      if (table_buf[2] | table_buf[3])
+      {
+        lid = (((ushort)table_buf[2]) << 8) | ((ushort)table_buf[3]);
+        ilm.CurAp = libraw_powf64l(2.0f, ((float)lid / 8.0f - 1.0f) / 2.0f);
+      }
+      lid = 0x4d << 1;
+      imgdata.shootinginfo.FocusMode =
+          ((ushort)table_buf[lid]) << 8 | ((ushort)table_buf[lid + 1]);
+      lid = 0x7e << 1;
+      imgdata.shootinginfo.DriveMode = table_buf[lid + 1];
+      break;
+    case 1536: // a560 a580 a33 a35 a55 NEX-3 NEX-5 NEX-5C NEX-C3 NEX-VG10E
+    case 2048: // a450 a500 a550
+      // CameraSettings3 are little endian
+      switch (table_buf[0x0e]) {
+      case 1:
+        imCommon.ColorSpace = LIBRAW_COLORSPACE_sRGB;
+        break;
+      case 2:
+        imCommon.ColorSpace = LIBRAW_COLORSPACE_AdobeRGB;
+        break;
+      default:
+        imCommon.ColorSpace = LIBRAW_COLORSPACE_Unknown;
+        break;
+      }
+      imgdata.shootinginfo.DriveMode = table_buf[0x34];
+      parseSonyLensType2(table_buf[1016], table_buf[1015]);
+      if (ilm.LensMount != LIBRAW_MOUNT_Canon_EF)
+      {
+        switch (table_buf[153])
+        {
+        case 16:
+          ilm.LensMount = LIBRAW_MOUNT_Minolta_A;
+          break;
+        case 17:
+          ilm.LensMount = LIBRAW_MOUNT_Sony_E;
+          break;
+        }
+      }
+      break;
+    }
+    free(table_buf);
+  }
+  else if ((tag == 0x3000) && (len < 256000))
+  {
+    table_buf = (uchar *)malloc(len);
+    fread(table_buf, len, 1, ifp);
+    for (int i = 0; i < 20; i++)
+      imSony.SonyDateTime[i] = table_buf[6 + i];
+    free(table_buf);
+  }
+  else if (tag == 0x0116 && len < 256000)
+  {
+    table_buf_0x0116 = (uchar *)malloc(len);
+    table_buf_0x0116_len = len;
+    fread(table_buf_0x0116, len, 1, ifp);
+    if (ilm.CamID)
+    {
+      process_Sony_0x0116(table_buf_0x0116, table_buf_0x0116_len, ilm.CamID);
+      free(table_buf_0x0116);
+      table_buf_0x0116_len = 0;
+    }
+  }
+  else if (tag == 0x2008)
+  {
+    imSony.LongExposureNoiseReduction = get4();
+  }
+  else if (tag == 0x2009)
+  {
+    imSony.HighISONoiseReduction = get2();
+  }
+  else if (tag == 0x200a)
+  {
+    imSony.HDR[0] = get2();
+    imSony.HDR[1] = get2();
+  }
+  else if (tag == 0x2010 && len < 256000)
+  {
+    table_buf_0x2010 = (uchar *)malloc(len);
+    table_buf_0x2010_len = len;
+    fread(table_buf_0x2010, len, 1, ifp);
+    if (ilm.CamID)
+    {
+      process_Sony_0x2010(table_buf_0x2010, table_buf_0x2010_len);
+      free(table_buf_0x2010);
+      table_buf_0x2010_len = 0;
+    }
+  }
+  else if (tag == 0x201a)
+  {
+    imSony.ElectronicFrontCurtainShutter = get4();
+  }
+  else if (tag == 0x201b)
+  {
+    if ((imSony.CameraType != LIBRAW_SONY_DSC) ||
+        (ilm.CamID == SonyID_DSC_RX10M4)   ||
+        (ilm.CamID == SonyID_DSC_RX100M6)  ||
+        (ilm.CamID == SonyID_DSC_RX100M5A) ||
+        (ilm.CamID == SonyID_DSC_RX0M2)    ||
+        (ilm.CamID == SonyID_DSC_RX100M7))
+    {
+      fread(&uc, 1, 1, ifp);
+      imgdata.shootinginfo.FocusMode = (short)uc;
+    }
+  }
+  else if (tag == 0x201c)
+  {
+    if ((imSony.CameraType != LIBRAW_SONY_DSC) ||
+        (ilm.CamID == SonyID_DSC_RX10M4)   ||
+        (ilm.CamID == SonyID_DSC_RX100M6)  ||
+        (ilm.CamID == SonyID_DSC_RX100M5A) ||
+        (ilm.CamID == SonyID_DSC_RX0M2)    ||
+        (ilm.CamID == SonyID_DSC_RX100M7))
+    {
+      imSony.AFAreaModeSetting = fgetc(ifp);
+    }
+  }
+  else if (tag == 0x201d)
+  {
+    if (((imSony.AFAreaModeSetting == 3) &&
+         ((imSony.CameraType == LIBRAW_SONY_ILCE) ||
+          (imSony.CameraType == LIBRAW_SONY_NEX) ||
+          (ilm.CamID == SonyID_DSC_RX10M4)    ||
+          (ilm.CamID == SonyID_DSC_RX100M6)   ||
+          (ilm.CamID == SonyID_DSC_RX100M5A)  ||
+          (ilm.CamID == SonyID_DSC_RX0M2)     ||
+          (ilm.CamID == SonyID_DSC_RX100M7))) ||
+        ((imSony.AFAreaModeSetting == 4) &&
+         (imSony.CameraType == LIBRAW_SONY_ILCA)))
+    {
+      imSony.FlexibleSpotPosition[0] = get2();
+      imSony.FlexibleSpotPosition[1] = get2();
+    }
+  }
+  else if (tag == 0x201e)
+  {
+    if (imSony.CameraType != LIBRAW_SONY_DSC)
+    {
+      imSony.AFPointSelected = fgetc(ifp);
+    }
+  }
+  else if (tag == 0x2020)
+  {
+    if (imSony.CameraType != LIBRAW_SONY_DSC)
+    {
+      fread(imSony.AFPointsUsed, 1, 10, ifp);
+    }
+  }
+  else if (tag == 0x2021)
+  {
+    if ((imSony.CameraType != LIBRAW_SONY_DSC) ||
+        (ilm.CamID == SonyID_DSC_RX10M4)   ||
+        (ilm.CamID == SonyID_DSC_RX100M6)  ||
+        (ilm.CamID == SonyID_DSC_RX100M5A) ||
+        (ilm.CamID == SonyID_DSC_RX0M2)    ||
+        (ilm.CamID == SonyID_DSC_RX100M7))
+    {
+      imSony.AFTracking = fgetc(ifp);
+    }
+  }
+  else if (tag == 0x2027)
+  {
+    FORC4 imSony.FocusLocation[c] = get2();
+  }
+  else if (tag == 0x2028)
+  {
+    if (get2())
+    {
+      imSony.VariableLowPassFilter = get2();
+    }
+  }
+  else if (tag == 0x2029)
+  {
+    imSony.RAWFileType = get2();
+  }
+  else if (tag == 0x202c)
+  {
+    imSony.MeteringMode2 = get2();
+  }
+  else if (tag == 0x202f)
+  {
+    imSony.PixelShiftGroupID = get4();
+    imSony.PixelShiftGroupPrefix = imSony.PixelShiftGroupID >> 22;
+    imSony.PixelShiftGroupID =
+        ((imSony.PixelShiftGroupID >> 17) & (unsigned)0x1f) *
+            (unsigned)1000000 +
+        ((imSony.PixelShiftGroupID >> 12) & (unsigned)0x1f) * (unsigned)10000 +
+        ((imSony.PixelShiftGroupID >> 6) & (unsigned)0x3f) * (unsigned)100 +
+        (imSony.PixelShiftGroupID & (unsigned)0x3f);
+
+    imSony.numInPixelShiftGroup = fgetc(ifp);
+    imSony.nShotsInPixelShiftGroup = fgetc(ifp);
+  }
+  else if (tag == 0x9050 && len < 256000)
+  { // little endian
+    table_buf_0x9050 = (uchar *)malloc(len);
+    table_buf_0x9050_len = len;
+    fread(table_buf_0x9050, len, 1, ifp);
+
+    if (ilm.CamID)
+    {
+      process_Sony_0x9050(table_buf_0x9050, table_buf_0x9050_len, ilm.CamID);
+      free(table_buf_0x9050);
+      table_buf_0x9050_len = 0;
+    }
+  }
+  else if (tag == 0x9400 && len < 256000)
+  {
+    table_buf_0x9400 = (uchar *)malloc(len);
+    table_buf_0x9400_len = len;
+    fread(table_buf_0x9400, len, 1, ifp);
+    if (ilm.CamID)
+    {
+      process_Sony_0x9400(table_buf_0x9400, table_buf_0x9400_len, unique_id);
+      free(table_buf_0x9400);
+      table_buf_0x9400_len = 0;
+    }
+  }
+  else if (tag == 0x9402 && len < 256000)
+  {
+    table_buf_0x9402 = (uchar *)malloc(len);
+    table_buf_0x9402_len = len;
+    fread(table_buf_0x9402, len, 1, ifp);
+    if (ilm.CamID)
+    {
+      process_Sony_0x9402(table_buf_0x9402, table_buf_0x9402_len);
+      free(table_buf_0x9402);
+      table_buf_0x9402_len = 0;
+    }
+  }
+  else if (tag == 0x9403 && len < 256000)
+  {
+    table_buf_0x9403 = (uchar *)malloc(len);
+    table_buf_0x9403_len = len;
+    fread(table_buf_0x9403, len, 1, ifp);
+    if (ilm.CamID)
+    {
+      process_Sony_0x9403(table_buf_0x9403, table_buf_0x9403_len);
+      free(table_buf_0x9403);
+      table_buf_0x9403_len = 0;
+    }
+  }
+  else if ((tag == 0x9405) && (len < 256000) && (len > 0x64))
+  {
+    table_buf = (uchar *)malloc(len);
+    fread(table_buf, len, 1, ifp);
+    uc = table_buf[0x0];
+    if (imCommon.real_ISO < 0.1f)
+    {
+      if ((uc == 0x25) || (uc == 0x3a) || (uc == 0x76) || (uc == 0x7e) ||
+          (uc == 0x8b) || (uc == 0x9a) || (uc == 0xb3) || (uc == 0xe1))
+      {
+        s[0] = SonySubstitution[table_buf[0x04]];
+        s[1] = SonySubstitution[table_buf[0x05]];
+        imCommon.real_ISO =
+            100.0f * libraw_powf64l(2.0f, (16 - ((float)sget2(s)) / 256.0f));
+      }
+    }
+    free(table_buf);
+  }
+  else if (tag == 0x9406 && len < 256000)
+  {
+    table_buf_0x9406 = (uchar *)malloc(len);
+    table_buf_0x9406_len = len;
+    fread(table_buf_0x9406, len, 1, ifp);
+    if (ilm.CamID)
+    {
+      process_Sony_0x9406(table_buf_0x9406, table_buf_0x9406_len);
+      free(table_buf_0x9406);
+      table_buf_0x9406_len = 0;
+    }
+  }
+  else if (tag == 0x940c && len < 256000)
+  {
+    table_buf_0x940c = (uchar *)malloc(len);
+    table_buf_0x940c_len = len;
+    fread(table_buf_0x940c, len, 1, ifp);
+    if (ilm.CamID)
+    {
+      process_Sony_0x940c(table_buf_0x940c, table_buf_0x940c_len);
+      free(table_buf_0x940c);
+      table_buf_0x940c_len = 0;
+    }
+  }
+  else if (tag == 0x940e && len < 256000)
+  {
+    table_buf_0x940e = (uchar *)malloc(len);
+    table_buf_0x940e_len = len;
+    fread(table_buf_0x940e, len, 1, ifp);
+    if (ilm.CamID)
+    {
+      process_Sony_0x940e(table_buf_0x940e, table_buf_0x940e_len, ilm.CamID);
+      free(table_buf_0x940e);
+      table_buf_0x940e_len = 0;
+    }
+  }
+  else if (((tag == 0xb027) || (tag == 0x010c)) && (ilm.LensID == LIBRAW_LENS_NOT_SET))
+  {
+    ilm.LensID = get4();
+//    printf ("==>> 1: ilm.LensID %lld\n", ilm.LensID);
+    if ((ilm.LensID > 0x4900) && (ilm.LensID <= 0x5900))
+    {
+      ilm.AdapterID = 0x4900;
+      ilm.LensID -= ilm.AdapterID;
+      ilm.LensMount = LIBRAW_MOUNT_Sigma_X3F;
+      strcpy(ilm.Adapter, "MC-11");
+    }
+
+    else if ((ilm.LensID > 0xef00) && (ilm.LensID < 0xffff) &&
+             (ilm.LensID != 0xff00))
+    {
+      ilm.AdapterID = 0xef00;
+      ilm.LensID -= ilm.AdapterID;
+      ilm.LensMount = LIBRAW_MOUNT_Canon_EF;
+    }
+
+    else if (((ilm.LensID != LIBRAW_LENS_NOT_SET) && (ilm.LensID < 0xef00)) ||
+             (ilm.LensID == 0xff00))
+      ilm.LensMount = LIBRAW_MOUNT_Minolta_A;
+    /*
+        if (tag == 0x010c)
+          ilm.CameraMount = LIBRAW_MOUNT_Minolta_A;
+    */
+  }
+  else if (tag == 0xb02a && len < 256000)
+  { // Sony LensSpec
+    table_buf = (uchar *)malloc(len);
+    fread(table_buf, len, 1, ifp);
+    if ((!dng_writer) ||
+        (saneSonyCameraInfo(table_buf[1], table_buf[2], table_buf[3],
+                            table_buf[4], table_buf[5], table_buf[6])))
+    {
+      if (table_buf[1] | table_buf[2])
+        ilm.MinFocal = bcd2dec(table_buf[1]) * 100 + bcd2dec(table_buf[2]);
+      if (table_buf[3] | table_buf[4])
+        ilm.MaxFocal = bcd2dec(table_buf[3]) * 100 + bcd2dec(table_buf[4]);
+      if (table_buf[5])
+        ilm.MaxAp4MinFocal = bcd2dec(table_buf[5]) / 10.0f;
+      if (table_buf[6])
+        ilm.MaxAp4MaxFocal = bcd2dec(table_buf[6]) / 10.0f;
+      parseSonyLensFeatures(table_buf[0], table_buf[7]);
+    }
+    free(table_buf);
+  }
+  else if ((tag == 0xb02b) && !imgdata.sizes.raw_inset_crop.cwidth &&
+           (len == 2))
+  {
+    imgdata.sizes.raw_inset_crop.cheight = get4();
+    imgdata.sizes.raw_inset_crop.cwidth = get4();
+  }
+  else if (tag == 0xb041)
+  {
+    imgdata.shootinginfo.ExposureMode = get2();
+  }
+
+  // MetaVersion: (unique_id >= 286)
+}
+
+void LibRaw::parseSonySR2(uchar *cbuf_SR2, unsigned SR2SubIFDOffset,
+                          unsigned SR2SubIFDLength, unsigned dng_writer)
+{
+  unsigned c;
+  unsigned entries, tag_id, tag_type, tag_datalen;
+  INT64 tag_offset, tag_dataoffset;
+  int TagProcessed;
+  int tag_dataunitlen;
+  float num;
+  int i;
+  int WBCTC_count;
+#define CHECKBUFFER_N(offset,N)                                     \
+  do                                                                \
+  {                                                                 \
+    if ((((offset) + (N)) > SR2SubIFDLength) || ((offset) < 0))     \
+      return;														\
+  } while (0)
+
+  CHECKBUFFER_N(0, 2);
+  entries = sget2(cbuf_SR2);
+  if (entries > 1000)
+    return;
+  tag_offset = 2;
+  WBCTC_count = 0;
+  while (entries--) {
+    if (tiff_sget (SR2SubIFDOffset, cbuf_SR2, SR2SubIFDLength,
+                   &tag_offset, &tag_id, &tag_type, &tag_dataoffset,
+                   &tag_datalen, &tag_dataunitlen) == 0) {
+      TagProcessed = 0;
+      if (dng_writer == nonDNG) {
+        switch (tag_id) {
+        case 0x7300:
+			CHECKBUFFER_N(tag_dataoffset + tag_dataunitlen * 4,0);
+			FORC4 cblack[c] = sget2(cbuf_SR2 + tag_dataoffset + tag_dataunitlen * c);
+          TagProcessed = 1;
+          break;
+        case 0x7303:
+			CHECKBUFFER_N(tag_dataoffset + tag_dataunitlen * 4, 0);
+			FORC4 cam_mul[GRBG_2_RGBG(c)] = sget2(cbuf_SR2 + tag_dataoffset + tag_dataunitlen * c);
+          TagProcessed = 1;
+          break;
+        case 0x7310:
+			CHECKBUFFER_N(tag_dataoffset + tag_dataunitlen * 4, 0);
+			FORC4 cblack[RGGB_2_RGBG(c)] = sget2(cbuf_SR2 + tag_dataoffset + tag_dataunitlen * c);
+          i = cblack[3];
+          FORC3 if (i > (int)cblack[c]) i = cblack[c];
+          FORC4 cblack[c] -= i;
+          black = i;
+          TagProcessed = 1;
+          break;
+        case 0x7313:
+			CHECKBUFFER_N(tag_dataoffset + tag_dataunitlen * 4, 0);
+			FORC4 cam_mul[RGGB_2_RGBG(c)] = sget2(cbuf_SR2 + tag_dataoffset + tag_dataunitlen * c);
+          TagProcessed = 1;
+          break;
+        case 0x74a0:
+			CHECKBUFFER_N(tag_dataoffset, 4);
+			ilm.MaxAp4MaxFocal = sgetreal(tag_type, cbuf_SR2 + tag_dataoffset);
+          TagProcessed = 1;
+          break;
+        case 0x74a1:
+			CHECKBUFFER_N(tag_dataoffset, 4);
+			ilm.MaxAp4MinFocal = sgetreal(tag_type, cbuf_SR2 + tag_dataoffset);
+          TagProcessed = 1;
+          break;
+        case 0x74a2:
+			CHECKBUFFER_N(tag_dataoffset, 4);
+			ilm.MaxFocal = sgetreal(tag_type, cbuf_SR2 + tag_dataoffset);
+          TagProcessed = 1;
+          break;
+        case 0x74a3:
+			CHECKBUFFER_N(tag_dataoffset, 4);
+			ilm.MinFocal = sgetreal(tag_type, cbuf_SR2 + tag_dataoffset);
+          TagProcessed = 1;
+          break;
+        case 0x7800:
+			CHECKBUFFER_N(tag_dataoffset + tag_dataunitlen * 8, 2);
+		  for (i = 0; i < 3; i++)
+          {
+            num = 0.0;
+            for (c = 0; c < 3; c++)
+            {
+              imgdata.color.ccm[i][c] =
+                  (float)((short)sget2(cbuf_SR2 + tag_dataoffset + tag_dataunitlen * (i * 3 + c)));
+              num += imgdata.color.ccm[i][c];
+            }
+            if (num > 0.01)
+              FORC3 imgdata.color.ccm[i][c] = imgdata.color.ccm[i][c] / num;
+          }
+          TagProcessed = 1;
+          break;
+        case 0x787f:
+          if (tag_datalen == 3)
+          {
+			  CHECKBUFFER_N(tag_dataoffset + tag_dataunitlen * 2, 2);
+			  FORC3 imgdata.color.linear_max[c] = sget2(cbuf_SR2 + tag_dataoffset + tag_dataunitlen * c);
+            imgdata.color.linear_max[3] = imgdata.color.linear_max[1];
+          }
+          else if (tag_datalen == 1)
+          {
+			  CHECKBUFFER_N(tag_dataoffset, 2);
+			  imgdata.color.linear_max[0] = imgdata.color.linear_max[1] =
+                imgdata.color.linear_max[2] = imgdata.color.linear_max[3] =
+                    sget2(cbuf_SR2 + tag_dataoffset);
+          }
+          TagProcessed = 1;
+          break;
+        }
+      }
+
+      if (!TagProcessed) {
+        if ((tag_id >= 0x7480) && (tag_id <= 0x7486)) {
+          i = tag_id - 0x7480;
+          if (Sony_SR2_wb_list[i] > 255) {
+            icWBCCTC[WBCTC_count][0] = Sony_SR2_wb_list[i];
+			CHECKBUFFER_N(tag_dataoffset + tag_dataunitlen * 2, 2);
+			FORC3 icWBCCTC[WBCTC_count][c + 1] = sget2(cbuf_SR2 + tag_dataoffset + tag_dataunitlen * c);
+            icWBCCTC[WBCTC_count][4] = icWBCCTC[WBCTC_count][2];
+            WBCTC_count++;
+          } else {
+			  CHECKBUFFER_N(tag_dataoffset + tag_dataunitlen * 2, 2);
+			  FORC3 icWBC[Sony_SR2_wb_list[i]][c] = sget2(cbuf_SR2 + tag_dataoffset + tag_dataunitlen * c);
+            icWBC[Sony_SR2_wb_list[i]][3] = icWBC[Sony_SR2_wb_list[i]][1];
+          }
+        } else if ((tag_id >= 0x7820) && (tag_id <= 0x782d)) {
+          i = tag_id - 0x7820;
+          if (Sony_SR2_wb_list1[i] > 255) {
+            icWBCCTC[WBCTC_count][0] = Sony_SR2_wb_list1[i];
+			CHECKBUFFER_N(tag_dataoffset + tag_dataunitlen * 2, 2);
+			FORC3 icWBCCTC[WBCTC_count][c + 1] = sget2(cbuf_SR2 + tag_dataoffset + tag_dataunitlen * c);
+            icWBCCTC[WBCTC_count][4] = icWBCCTC[WBCTC_count][2];
+            if (Sony_SR2_wb_list1[i] == 3200) {
+              FORC3 icWBC[LIBRAW_WBI_StudioTungsten][c] = icWBCCTC[WBCTC_count][c + 1];
+              icWBC[LIBRAW_WBI_StudioTungsten][3] = icWBC[LIBRAW_WBI_StudioTungsten][1];
+            }
+            WBCTC_count++;
+          } else {
+			  CHECKBUFFER_N(tag_dataoffset + tag_dataunitlen * 2, 2);
+			  FORC3 icWBC[Sony_SR2_wb_list1[i]][c] = sget2(cbuf_SR2 + tag_dataoffset + tag_dataunitlen * c);
+            icWBC[Sony_SR2_wb_list1[i]][3] = icWBC[Sony_SR2_wb_list1[i]][1];
+          }
+        } else if (tag_id == 0x7302) {
+			CHECKBUFFER_N(tag_dataoffset + tag_dataunitlen * 3, 2);
+			FORC4 icWBC[LIBRAW_WBI_Auto][GRBG_2_RGBG(c)] = sget2(cbuf_SR2 + tag_dataoffset + tag_dataunitlen * c);
+        } else if (tag_id == 0x7312) {
+			CHECKBUFFER_N(tag_dataoffset + tag_dataunitlen * 3, 2);
+			FORC4 icWBC[LIBRAW_WBI_Auto][RGGB_2_RGBG(c)] = sget2(cbuf_SR2 + tag_dataoffset + tag_dataunitlen * c);
+        }
+      }
+    }
+  }
+}
+#undef CHECKBUFFER_N
+
+void LibRaw::parseSonySRF(unsigned len)
+{
+
+  if ((len > 0xfffff) || (len == 0))
+    return;
+
+  INT64 save = ftell(ifp);
+  INT64 offset =
+      0x0310c0 - save; /* for non-DNG this value normally is 0x8ddc */
+  if (len < offset || offset < 0)
+    return;
+  INT64 decrypt_len = offset >> 2; /* master key offset value is the next
+                                      un-encrypted metadata field after SRF0 */
+
+  unsigned i, nWB;
+  unsigned MasterKey, SRF2Key;
+  INT64 srf_offset, tag_offset, tag_dataoffset;
+  int tag_dataunitlen;
+  uchar *srf_buf;
+  ushort entries;
+  unsigned tag_id, tag_type, tag_datalen;
+
+  srf_buf = (uchar *)malloc(len+64);
+  fread(srf_buf, len, 1, ifp);
+
+  offset += srf_buf[offset] << 2;
+
+#define CHECKBUFFER_SGET4(offset)                                              \
+  do                                                                           \
+  {                                                                            \
+    if ((((offset) + 4) > len) || ((offset) < 0))                              \
+      goto restore_after_parseSonySRF;                                         \
+  } while (0)
+
+#define CHECKBUFFER_SGET2(offset)                                              \
+  do                                                                           \
+  {                                                                            \
+    if ( ((offset + 2) > len) || ((offset) < 0))                               \
+      goto restore_after_parseSonySRF;                                         \
+  } while (0)
+
+  CHECKBUFFER_SGET4(offset);
+
+  /* master key is stored in big endian */
+  MasterKey = ((unsigned)srf_buf[offset] << 24) |
+              ((unsigned)srf_buf[offset + 1] << 16) |
+              ((unsigned)srf_buf[offset + 2] << 8) |
+              (unsigned)srf_buf[offset + 3];
+
+  /* skip SRF0 */
+  srf_offset = 0;
+  CHECKBUFFER_SGET2(srf_offset);
+  entries = sget2(srf_buf + srf_offset);
+  if (entries > 1000)
+    goto restore_after_parseSonySRF;
+  offset = srf_offset + 2;
+  CHECKBUFFER_SGET4(offset);
+  CHECKBUFFER_SGET4(offset + 12 * entries);
+  srf_offset = sget4(srf_buf + offset + 12 * entries) -
+               save; /* SRF0 ends with SRF1 abs. position */
+
+  /* get SRF1, it has fixed 40 bytes length and contains keys to decode metadata
+   * and raw data */
+  if (srf_offset < 0 || decrypt_len < srf_offset / 4)
+    goto restore_after_parseSonySRF;
+  sony_decrypt((unsigned *)(srf_buf + srf_offset), decrypt_len - srf_offset / 4,
+               1, MasterKey);
+  CHECKBUFFER_SGET2(srf_offset);
+  entries = sget2(srf_buf + srf_offset);
+  if (entries > 1000)
+    goto restore_after_parseSonySRF;
+  offset = srf_offset + 2;
+  tag_offset = offset;
+
+  while (entries--) {
+    if (tiff_sget (save, srf_buf, len,
+                   &tag_offset, &tag_id, &tag_type, &tag_dataoffset,
+                   &tag_datalen, &tag_dataunitlen) == 0) {
+      if (tag_id == 0x0000) {
+		  CHECKBUFFER_SGET4(tag_dataoffset);
+		  SRF2Key = sget4(srf_buf + tag_dataoffset);
+      } else if (tag_id == 0x0001) {
+		  CHECKBUFFER_SGET4(tag_dataoffset);
+		  /*RawDataKey =*/ sget4(srf_buf + tag_dataoffset);
+      }
+    } else goto restore_after_parseSonySRF;
+  }
+  offset = tag_offset;
+
+  /* get SRF2 */
+  CHECKBUFFER_SGET4(offset);
+  srf_offset =
+      sget4(srf_buf + offset) - save; /* SRFn ends with SRFn+1 position */
+  if (srf_offset < 0 || decrypt_len < srf_offset / 4)
+    goto restore_after_parseSonySRF;
+  sony_decrypt((unsigned *)(srf_buf + srf_offset), decrypt_len - srf_offset / 4,
+               1, SRF2Key);
+  CHECKBUFFER_SGET2(srf_offset);
+  entries = sget2(srf_buf + srf_offset);
+  if (entries > 1000)
+    goto restore_after_parseSonySRF;
+  offset = srf_offset + 2;
+  tag_offset = offset;
+
+  while (entries--) {
+	  if (tiff_sget(save, srf_buf, len,
+                   &tag_offset, &tag_id, &tag_type, &tag_dataoffset,
+                   &tag_datalen, &tag_dataunitlen) == 0) {
+      if ((tag_id >= 0x00c0) && (tag_id <= 0x00ce)) {
+        i = (tag_id - 0x00c0) % 3;
+        nWB = (tag_id - 0x00c0) / 3;
+		CHECKBUFFER_SGET4(tag_dataoffset);
+		icWBC[Sony_SRF_wb_list[nWB]][i] = sget4(srf_buf + tag_dataoffset);
+        if (i == 1) {
+          icWBC[Sony_SRF_wb_list[nWB]][3] =
+            icWBC[Sony_SRF_wb_list[nWB]][i];
+        }
+      } else if ((tag_id >= 0x00d0) && (tag_id <= 0x00d2)) {
+        i = (tag_id - 0x00d0) % 3;
+		CHECKBUFFER_SGET4(tag_dataoffset);
+		cam_mul[i] = sget4(srf_buf + tag_dataoffset);
+        if (i == 1) {
+          cam_mul[3] = cam_mul[i];
+        }
+      } else switch (tag_id) {
+        /*
+        0x0002  SRF6Offset
+        0x0003  SRFDataOffset (?)
+        0x0004  RawDataOffset
+        0x0005  RawDataLength
+        */
+      case 0x0043:
+		  CHECKBUFFER_SGET4(tag_dataoffset); // need to add extra space
+		  ilm.MaxAp4MaxFocal = sgetreal(tag_type, srf_buf + tag_dataoffset);
+        break;
+      case 0x0044:
+		  CHECKBUFFER_SGET4(tag_dataoffset);
+		  ilm.MaxAp4MinFocal = sgetreal(tag_type, srf_buf + tag_dataoffset);
+        break;
+      case 0x0045:
+		  CHECKBUFFER_SGET4(tag_dataoffset);
+		  ilm.MinFocal = sgetreal(tag_type, srf_buf + tag_dataoffset);
+        break;
+      case 0x0046:
+		  CHECKBUFFER_SGET4(tag_dataoffset);
+		  ilm.MaxFocal = sgetreal(tag_type, srf_buf + tag_dataoffset);
+        break;
+      }
+    } else goto restore_after_parseSonySRF;
+  }
+  offset = tag_offset;
+
+restore_after_parseSonySRF:
+  free(srf_buf);
+  fseek(ifp, save, SEEK_SET);
+#undef CHECKBUFFER_SGET4
+#undef CHECKBUFFER_SGET2
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/metadata/tiff.cpp libkdcraw/libkdcraw/libraw/src/metadata/tiff.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/metadata/tiff.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/metadata/tiff.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,2011 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+#include "../../internal/libraw_cameraids.h"
+
+int LibRaw::parse_tiff_ifd(int base)
+{
+  unsigned entries, tag, type, len, plen = 16, save, utmp;
+  int ifd, use_cm = 0, cfa, i, j, c, ima_len = 0;
+  char *cbuf, *cp;
+  uchar cfa_pat[16], cfa_pc[] = {0, 1, 2, 3}, tab[256];
+  double fm[3][4], cc[4][4], cm[4][3], cam_xyz[4][3], num;
+  double ab[] = {1, 1, 1, 1}, asn[] = {0, 0, 0, 0}, xyz[] = {1, 1, 1};
+  unsigned sony_curve[] = {0, 0, 0, 0, 0, 4095};
+  unsigned *buf, sony_offset = 0, sony_length = 0, sony_key = 0;
+  struct jhead jh;
+
+  ushort *rafdata;
+
+  if (tiff_nifds >= sizeof tiff_ifd / sizeof tiff_ifd[0])
+    return 1;
+  ifd = tiff_nifds++;
+  for (j = 0; j < 4; j++)
+    for (i = 0; i < 4; i++)
+      cc[j][i] = i == j;
+  entries = get2();
+  if (entries > 512)
+    return 1;
+
+  INT64 fsize = ifp->size();
+
+  while (entries--)
+  {
+    tiff_get(base, &tag, &type, &len, &save);
+    INT64 savepos = ftell(ifp);
+    if (len > 8 && savepos + len > 2 * fsize)
+    {
+      fseek(ifp, save, SEEK_SET); // Recover tiff-read position!!
+      continue;
+    }
+    if (callbacks.exif_cb)
+    {
+      callbacks.exif_cb(callbacks.exifparser_data,
+                        tag | (is_pana_raw ? 0x30000 : ((ifd + 1) << 20)), type,
+                        len, order, ifp, base);
+      fseek(ifp, savepos, SEEK_SET);
+    }
+
+    if (!is_pana_raw)
+    { /* processing of EXIF tags that collide w/ PanasonicRaw tags */
+      switch (tag)
+      {
+      case 0x0001:
+        if (len == 4)
+          is_pana_raw = get4();
+        break;
+      case 0x000b: /* 11, Std. EXIF Software tag */
+        fgets(software, 64, ifp);
+        if (!strncmp(software, "Adobe", 5) || !strncmp(software, "dcraw", 5) ||
+            !strncmp(software, "UFRaw", 5) || !strncmp(software, "Bibble", 6) ||
+            !strcmp(software, "Digital Photo Professional"))
+          is_raw = 0;
+        break;
+      case 0x001c: /*  28, safeguard, probably not needed */
+      case 0x001d: /*  29, safeguard, probably not needed */
+      case 0x001e: /*  30, safeguard, probably not needed */
+        cblack[tag - 0x001c] = get2();
+        cblack[3] = cblack[1];
+        break;
+
+      case 0x0111: /* 273, StripOffset */
+        if (len > 1 && len < 16384)
+        {
+          off_t sav = ftell(ifp);
+          tiff_ifd[ifd].strip_offsets = (int *)calloc(len, sizeof(int));
+          tiff_ifd[ifd].strip_offsets_count = len;
+          for (int i = 0; i < (int)len; i++)
+            tiff_ifd[ifd].strip_offsets[i] = get4() + base;
+          fseek(ifp, sav, SEEK_SET); // restore position
+        }
+        /* fallback */
+      case 0x0201: /* 513, JpegIFOffset */
+      case 0xf007: // 61447
+        tiff_ifd[ifd].offset = get4() + base;
+        if (!tiff_ifd[ifd].bps && tiff_ifd[ifd].offset > 0)
+        {
+          fseek(ifp, tiff_ifd[ifd].offset, SEEK_SET);
+          if (ljpeg_start(&jh, 1))
+          {
+            tiff_ifd[ifd].comp = 6;
+            tiff_ifd[ifd].t_width = jh.wide;
+            tiff_ifd[ifd].t_height = jh.high;
+            tiff_ifd[ifd].bps = jh.bits;
+            tiff_ifd[ifd].samples = jh.clrs;
+            if (!(jh.sraw || (jh.clrs & 1)))
+              tiff_ifd[ifd].t_width *= jh.clrs;
+            if ((tiff_ifd[ifd].t_width > 4 * tiff_ifd[ifd].t_height) & ~jh.clrs)
+            {
+              tiff_ifd[ifd].t_width /= 2;
+              tiff_ifd[ifd].t_height *= 2;
+            }
+            i = order;
+            parse_tiff(tiff_ifd[ifd].offset + 12);
+            order = i;
+          }
+        }
+        break;
+      }
+    }
+    else
+    { /* processing Panasonic-specific "PanasonicRaw" tags */
+      switch (tag)
+      {
+      case 0x0004: /*   4, SensorTopBorder */
+        imgdata.sizes.raw_inset_crop.ctop = get2();
+        break;
+      case 0x000a: /*  10, BitsPerSample */
+        pana_bpp = get2();
+        break;
+      case 0x000b: /*  11, Compression */
+        imPana.Compression = get2();
+        break;
+      case 0x000e: /*  14, LinearityLimitRed */
+      case 0x000f: /*  15, LinearityLimitGreen */
+      case 0x0010: /*  16, LinearityLimitBlue */
+        imgdata.color.linear_max[tag - 14] = get2();
+        if (tag == 0x000f) // 15, LinearityLimitGreen
+          imgdata.color.linear_max[3] = imgdata.color.linear_max[1];
+        break;
+      case 0x0013: /*  19, WBInfo */
+        if ((i = get2()) > 0x100)
+          break;
+        for (c = 0; c < i; c++)
+        {
+          if ((j = get2()) < 0x100)
+          {
+            icWBC[j][0] = get2();
+            icWBC[j][2] = get2();
+            icWBC[j][1] = icWBC[j][3] =
+                0x100;
+          }
+          else // light source out of EXIF numbers range
+            get4();
+        }
+        break;
+      case 0x0018: /* 24, HighISOMultiplierRed */
+      case 0x0019: /* 25, HighISOMultiplierGreen */
+      case 0x001a: /* 26, HighISOMultiplierBlue */
+        imPana.HighISOMultiplier[tag - 0x0018] = get2();
+        break;
+      case 0x001c: /*  28, BlackLevelRed */
+      case 0x001d: /*  29, BlackLevelGreen */
+      case 0x001e: /*  30, BlackLevelBlue */
+        pana_black[tag - 0x001c] = get2();
+        break;
+      case 0x002d: /*  45, RawFormat */
+                   /* pana_encoding: tag 0x002d (45dec)
+                        not used - DMC-LX1/FZ30/FZ50/L1/LX1/LX2
+                        2 - RAW DMC-FZ8/FZ18
+                        3 - RAW DMC-L10
+                        4 - RW2 for most other models, including G9 in "pixel shift off"
+                      mode and YUNEEC CGO4            (must add 15 to black levels for
+                      RawFormat == 4)            5 - RW2 DC-GH5s; G9 in "pixel shift on"
+                      mode            6 - RW2            DC-S1, DC-S1R in "pixel shift off"
+                      mode            7 -            RW2 DC-S1R (probably            DC-S1 too) in
+                      "pixel shift on" mode
+                   */
+        pana_encoding = get2();
+        break;
+      case 0x002f: /*  47, CropTop */
+        imgdata.sizes.raw_inset_crop.ctop = get2();
+        break;
+      case 0x0030: /*  48, CropLeft */
+        imgdata.sizes.raw_inset_crop.cleft = get2();
+        break;
+      case 0x0031: /*  49, CropBottom */
+        imgdata.sizes.raw_inset_crop.cheight =
+            get2() - imgdata.sizes.raw_inset_crop.ctop;
+        break;
+      case 0x0032: /*  50, CropRight */
+        imgdata.sizes.raw_inset_crop.cwidth =
+            get2() - imgdata.sizes.raw_inset_crop.cleft;
+        break;
+      case 0x0037: /*  55, ISO if  ISO in 0x8827 & ISO in 0x0017 == 65535 */
+        if (iso_speed == 65535)
+          iso_speed = get4();
+        break;
+      case 0x011c: /* 284, Gamma */
+      {
+        int n = get2();
+        if (n >= 1024)
+          imPana.gamma = (float)n / 1024.0f;
+        else if (n >= 256)
+          imPana.gamma = (float)n / 256.0f;
+        else
+          imPana.gamma = (float)n / 100.0f;
+      }
+      break;
+      case 0x0120: /* 288, CameraIFD, contains tags 0x1xxx, 0x2xxx, 0x3xxx */
+      {
+        unsigned sorder = order;
+        unsigned long sbase = base;
+        base = ftell(ifp);
+        order = get2();
+        fseek(ifp, 2, SEEK_CUR);
+        fseek(ifp, get4() - 8, SEEK_CUR);
+        parse_tiff_ifd(base);
+        base = sbase;
+        order = sorder;
+      }
+      break;
+      case 0x0121: /* 289, Multishot, 0 is Off, 65536 is Pixel Shift */
+        imPana.Multishot = get4();
+        break;
+      case 0x1201:
+        if (tagtypeIs(LIBRAW_EXIFTAG_TYPE_SHORT)) {
+          ilm.LensID = fgetc(ifp);
+          if (ilm.LensID && (ilm.LensID != 0xff)) ilm.LensID <<= 16;
+        } else if (type == 258) {
+          unsigned n = get4();
+          if (n == 257) {
+            ilm.LensMount = LIBRAW_MOUNT_LPS_L;
+            ilm.LensFormat = LIBRAW_FORMAT_FF;
+          }
+        }
+        break;
+      case 0x1202:
+        if (ilm.LensID != 0xffffffffffffffff) {
+          utmp = (fgetc(ifp) << 8) | fgetc(ifp);
+          if (utmp) ilm.LensID += utmp;
+          else ilm.LensID = 0xffffffffffffffff;
+        }
+        break;
+      case 0x1203: /* 4611, FocalLengthIn35mmFormat, contained in 0x0120
+                      CameraIFD */
+        if (imgdata.lens.FocalLengthIn35mmFormat < 0.65f)
+          imgdata.lens.FocalLengthIn35mmFormat = get2();
+        break;
+      case 0x2009: /* 8201, contained in 0x0120 CameraIFD */
+        if ((pana_encoding == 4) || (pana_encoding == 5))
+        {
+          i = MIN(8, len);
+          int permut[8] = {3, 2, 1, 0, 3 + 4, 2 + 4, 1 + 4, 0 + 4};
+          imPana.BlackLevelDim = len;
+          for (j = 0; j < i; j++)
+          {
+            imPana.BlackLevel[permut[j]] =
+                (float)(get2()) / (float)(powf(2.f, 14.f - pana_bpp));
+          }
+        }
+        break;
+      case 0x3420: /* 13344, WB_RedLevelAuto, contained in 0x0120 CameraIFD */
+        icWBC[LIBRAW_WBI_Auto][0] = get2();
+        icWBC[LIBRAW_WBI_Auto][1] = icWBC[LIBRAW_WBI_Auto][3] = 1024.0f;
+        break;
+      case 0x3421: /* 13345, WB_BlueLevelAuto, contained in 0x0120 CameraIFD */
+        icWBC[LIBRAW_WBI_Auto][2] = get2();
+        break;
+      case 0x0002: /*   2, ImageWidth */
+        tiff_ifd[ifd].t_width = getint(type);
+        break;
+      case 0x0003: /*   3, ImageHeight */
+        tiff_ifd[ifd].t_height = getint(type);
+        break;
+      case 0x0005: /*   5, SensorLeftBorder */
+        width = get2();
+        imgdata.sizes.raw_inset_crop.cleft = width;
+        break;
+      case 0x0006: /*   6, SensorBottomBorder */
+        height = get2();
+        imgdata.sizes.raw_inset_crop.cheight =
+            height - imgdata.sizes.raw_inset_crop.ctop;
+        break;
+      case 0x0007: /*   7, SensorRightBorder */
+        i = get2();
+        width += i;
+        imgdata.sizes.raw_inset_crop.cwidth =
+            i - imgdata.sizes.raw_inset_crop.cleft;
+        break;
+      case 0x0009: /*   9, CFAPattern */
+        if ((i = get2()))
+          filters = i;
+        break;
+      case 0x0011: /*  17, RedBalance */
+      case 0x0012: /*  18, BlueBalance */
+        if (tagtypeIs(LIBRAW_EXIFTAG_TYPE_SHORT) && len == 1)
+          cam_mul[(tag - 0x0011) * 2] = get2() / 256.0;
+        break;
+      case 0x0017: /*  23, ISO */
+        if (tagtypeIs(LIBRAW_EXIFTAG_TYPE_SHORT))
+          iso_speed = get2();
+        break;
+      case 0x0024: /*  36, WBRedLevel */
+      case 0x0025: /*  37, WBGreenLevel */
+      case 0x0026: /*  38, WBBlueLevel */
+        cam_mul[tag - 0x0024] = get2();
+        break;
+      case 0x0027: /*  39, WBInfo2 */
+        if ((i = get2()) > 0x100)
+          break;
+        for (c = 0; c < i; c++)
+        {
+          if ((j = get2()) < 0x100)
+          {
+            icWBC[j][0] = get2();
+            icWBC[j][1] = icWBC[j][3] = get2();
+            icWBC[j][2] = get2();
+          }
+          else
+            fseek(ifp, 6, SEEK_CUR);
+        }
+        break;
+        if (len < 50 || cam_mul[0] > 0.001f)
+          break;
+        fseek(ifp, 12, SEEK_CUR);
+        FORC3 cam_mul[c] = get2();
+        break;
+      case 0x002e: /*  46, JpgFromRaw */
+        if ((type != LIBRAW_EXIFTAG_TYPE_UNDEFINED) || (fgetc(ifp) != 0xff) || (fgetc(ifp) != 0xd8))
+          break;
+        thumb_offset = ftell(ifp) - 2;
+        thumb_length = len;
+        break;
+
+      case 0x0118: /* 280, Panasonic RW2 offset */
+        if (type != LIBRAW_EXIFTAG_TYPE_LONG)
+          break;
+        load_raw = &LibRaw::panasonic_load_raw;
+        load_flags = 0x2008;
+      case 0x0111: /* 273, StripOffset */
+        if (len > 1 && len < 16384)
+        {
+          off_t sav = ftell(ifp);
+          tiff_ifd[ifd].strip_offsets = (int *)calloc(len, sizeof(int));
+          tiff_ifd[ifd].strip_offsets_count = len;
+          for (int i = 0; i < (int)len; i++)
+            tiff_ifd[ifd].strip_offsets[i] = get4() + base;
+          fseek(ifp, sav, SEEK_SET); // restore position
+        }
+        /* fallthrough */
+        tiff_ifd[ifd].offset = get4() + base;
+        if (!tiff_ifd[ifd].bps && tiff_ifd[ifd].offset > 0)
+        {
+          fseek(ifp, tiff_ifd[ifd].offset, SEEK_SET);
+          if (ljpeg_start(&jh, 1))
+          {
+            tiff_ifd[ifd].comp = 6;
+            tiff_ifd[ifd].t_width = jh.wide;
+            tiff_ifd[ifd].t_height = jh.high;
+            tiff_ifd[ifd].bps = jh.bits;
+            tiff_ifd[ifd].samples = jh.clrs;
+            if (!(jh.sraw || (jh.clrs & 1)))
+              tiff_ifd[ifd].t_width *= jh.clrs;
+            if ((tiff_ifd[ifd].t_width > 4 * tiff_ifd[ifd].t_height) & ~jh.clrs)
+            {
+              tiff_ifd[ifd].t_width /= 2;
+              tiff_ifd[ifd].t_height *= 2;
+            }
+            i = order;
+            parse_tiff(tiff_ifd[ifd].offset + 12);
+            order = i;
+          }
+        }
+        break;
+      }
+
+    } /* processing of Panasonic-specific tags finished */
+
+    switch (tag)
+    {            /* processing of general EXIF tags */
+    case 0xf000: /* 61440, Fuji HS10 table */
+      fseek(ifp, get4() + base, SEEK_SET);
+      parse_tiff_ifd(base);
+      break;
+    case 0x00fe: /* NewSubfileType */
+      tiff_ifd[ifd].newsubfiletype = getreal(type);
+      break;
+    case 0x0100: /* 256, ImageWidth */
+    case 0xf001: /* 61441, Fuji RAF IFD 0xf001 RawImageFullWidth */
+      tiff_ifd[ifd].t_width = getint(type);
+      break;
+    case 0x0101: /* 257, ImageHeight */
+    case 0xf002: /* 61442, Fuji RAF IFD 0xf002 RawImageFullHeight */
+      tiff_ifd[ifd].t_height = getint(type);
+      break;
+    case 0x0102: /* 258, BitsPerSample */
+    case 0xf003: /* 61443, Fuji RAF IFD 0xf003 */
+      tiff_ifd[ifd].samples = len & 7;
+      tiff_ifd[ifd].bps = getint(type);
+      if (tiff_bps < (unsigned)tiff_ifd[ifd].bps)
+        tiff_bps = tiff_ifd[ifd].bps;
+      break;
+    case 0xf006: /* 61446, Fuji RAF IFD 0xf006 */
+      raw_height = 0;
+      if (tiff_ifd[ifd].bps > 12)
+        break;
+      load_raw = &LibRaw::packed_load_raw;
+      load_flags = get4() ? 24 : 80;
+      break;
+    case 0x0103: /* 259, Compression */
+                 /*
+                  262	  = Kodak 262
+                  32767  = Sony ARW Compressed
+                  32769  = Packed RAW
+                  32770  = Samsung SRW Compressed
+                  32772  = Samsung SRW Compressed 2
+                  32867  = Kodak KDC Compressed
+                  34713  = Nikon NEF Compressed
+                  65000  = Kodak DCR Compressed
+                  65535  = Pentax PEF Compressed
+                 */
+      tiff_ifd[ifd].comp = getint(type);
+      break;
+    case 0x0106: /* 262, PhotometricInterpretation */
+      tiff_ifd[ifd].phint = get2();
+      break;
+    case 0x010e: /* 270, ImageDescription */
+      fread(desc, 512, 1, ifp);
+      break;
+    case 0x010f: /* 271, Make */
+      fgets(make, 64, ifp);
+      break;
+    case 0x0110: /* 272, Model */
+      if (!strncmp(make, "Hasselblad", 10) && model[0] &&
+          (imHassy.format != LIBRAW_HF_Imacon))
+        break;
+      fgets(model, 64, ifp);
+      break;
+    case 0x0116: // 278
+      tiff_ifd[ifd].rows_per_strip = getint(type);
+      break;
+    case 0x0112: /* 274, Orientation */
+      tiff_ifd[ifd].t_flip = "50132467"[get2() & 7] - '0';
+      break;
+    case 0x0115: /* 277, SamplesPerPixel */
+      tiff_ifd[ifd].samples = getint(type) & 7;
+      break;
+    case 0x0152: /* Extrasamples */
+      tiff_ifd[ifd].extrasamples = (getint(type) & 0xff) + 1024;
+      break;
+    case 0x0117: /* 279, StripByteCounts */
+      if (len > 1 && len < 16384)
+      {
+        off_t sav = ftell(ifp);
+        tiff_ifd[ifd].strip_byte_counts = (int *)calloc(len, sizeof(int));
+        tiff_ifd[ifd].strip_byte_counts_count = len;
+        for (int i = 0; i < (int)len; i++)
+          tiff_ifd[ifd].strip_byte_counts[i] = get4();
+        fseek(ifp, sav, SEEK_SET); // restore position
+      }
+      /* fallback */
+    case 0x0202: // 514
+    case 0xf008: // 61448
+      tiff_ifd[ifd].bytes = get4();
+      break;
+    case 0xf00e: // 61454, FujiFilm "As Shot"
+      FORC3 cam_mul[(4 - c) % 3] = getint(type);
+      break;
+    case 0x0131: /* 305, Software */
+      fgets(software, 64, ifp);
+      if (!strncmp(software, "Adobe", 5) || !strncmp(software, "dcraw", 5) ||
+          !strncmp(software, "UFRaw", 5) || !strncmp(software, "Bibble", 6) ||
+          !strcmp(software, "Digital Photo Professional"))
+        is_raw = 0;
+      break;
+    case 0x0132: /* 306, DateTime */
+      get_timestamp(0);
+      break;
+    case 0x013b: /* 315, Artist */
+      fread(artist, 64, 1, ifp);
+      break;
+    case 0x013d: // 317
+      tiff_ifd[ifd].predictor = getint(type);
+      break;
+    case 0x0142: /* 322, TileWidth */
+      tiff_ifd[ifd].t_tile_width = getint(type);
+      break;
+    case 0x0143: /* 323, TileLength */
+      tiff_ifd[ifd].t_tile_length = getint(type);
+      break;
+    case 0x0144: /* 324, TileOffsets */
+      tiff_ifd[ifd].offset = len > 1 ? ftell(ifp) : get4();
+      if (len == 1)
+        tiff_ifd[ifd].t_tile_width = tiff_ifd[ifd].t_tile_length = 0;
+      if (len == 4)
+      {
+        load_raw = &LibRaw::sinar_4shot_load_raw;
+        is_raw = 5;
+      }
+      break;
+    case 0x0145: // 325
+      tiff_ifd[ifd].bytes = len > 1 ? ftell(ifp) : get4();
+      break;
+    case 0x014a: /* 330, SubIFDs */
+      if (!strcmp(model, "DSLR-A100") && tiff_ifd[ifd].t_width == 3872)
+      {
+        load_raw = &LibRaw::sony_arw_load_raw;
+        data_offset = get4() + base;
+        ifd++;
+        if (ifd >= int(sizeof tiff_ifd / sizeof tiff_ifd[0]))
+          throw LIBRAW_EXCEPTION_IO_CORRUPT;
+        break;
+      }
+      if (!strncmp(make, "Hasselblad", 10) &&
+          libraw_internal_data.unpacker_data.hasselblad_parser_flag)
+      {
+        fseek(ifp, ftell(ifp) + 4, SEEK_SET);
+        fseek(ifp, get4() + base, SEEK_SET);
+        parse_tiff_ifd(base);
+        break;
+      }
+      if (len > 1000)
+        len = 1000; /* 1000 SubIFDs is enough */
+      while (len--)
+      {
+        i = ftell(ifp);
+        fseek(ifp, get4() + base, SEEK_SET);
+        if (parse_tiff_ifd(base))
+          break;
+        fseek(ifp, i + 4, SEEK_SET);
+      }
+      break;
+    case 0x0153: // 339
+      tiff_ifd[ifd].sample_format = getint(type);
+      break;
+    case 0x0190: // 400
+      strcpy(make, "Sarnoff");
+      maximum = 0xfff;
+      break;
+    case 0x02bc: // 700
+      if ((tagtypeIs(LIBRAW_EXIFTAG_TYPE_BYTE) ||
+          tagtypeIs(LIBRAW_EXIFTAG_TYPE_ASCII) ||
+          tagtypeIs(LIBRAW_EXIFTAG_TYPE_SBYTE) ||
+          tagtypeIs(LIBRAW_EXIFTOOLTAGTYPE_binary)) &&
+          (len > 1) && (len < 5100000))
+      {
+        xmpdata = (char *)malloc(xmplen = len + 1);
+        fread(xmpdata, len, 1, ifp);
+        xmpdata[len] = 0;
+      }
+      break;
+    case 0x7000:
+      imSony.SonyRawFileType = get2();
+      break;
+    case 0x7010: // 28688
+      FORC4 sony_curve[c + 1] = get2() >> 2 & 0xfff;
+      for (i = 0; i < 5; i++)
+        for (j = sony_curve[i] + 1; j <= (int)sony_curve[i + 1]; j++)
+          curve[j] = curve[j - 1] + (1 << i);
+      break;
+    case 0x7200: // 29184, Sony SR2Private
+      sony_offset = get4();
+      break;
+    case 0x7201: // 29185, Sony SR2Private
+      sony_length = get4();
+      break;
+    case 0x7221: // 29217, Sony SR2Private
+      sony_key = get4();
+      break;
+    case 0x7250: // 29264, Sony SR2Private
+      parse_minolta(ftell(ifp));
+      raw_width = 0;
+      break;
+    case 0x7303: // 29443, Sony SR2SubIFD
+      FORC4 cam_mul[GRBG_2_RGBG(c)] = get2();
+      break;
+    case 0x7313: // 29459, Sony SR2SubIFD
+      FORC4 cam_mul[RGGB_2_RGBG(c)] = get2();
+      break;
+    case 0x7310: // 29456, Sony SR2SubIFD
+      FORC4 cblack[RGGB_2_RGBG(c)] = get2();
+      i = cblack[3];
+      FORC3 if (i > (int)cblack[c]) i = cblack[c];
+      FORC4 cblack[c] -= i;
+      black = i;
+      break;
+    case 0x827d: /* 33405, Model2 */
+                 /*
+                  for Kodak ProBack 645 PB645x-yyyy 'x' is:
+                  'M' for Mamiya 645
+                  'C' for Contax 645
+                  'H' for Hasselblad H-series
+                 */
+      fgets(model2, 64, ifp);
+      break;
+    case 0x828d: /* 33421, CFARepeatPatternDim */
+      if (get2() == 6 && get2() == 6)
+        tiff_ifd[ifd].t_filters = filters = 9;
+      break;
+    case 0x828e: /* 33422, CFAPattern */
+      if (filters == 9)
+      {
+        FORC(36)((char *)xtrans)[c] = fgetc(ifp) & 3;
+        break;
+      }
+    case 0xfd09: /* 64777, Kodak P-series */
+      if (len == 36)
+      {
+        tiff_ifd[ifd].t_filters = filters = 9;
+        colors = 3;
+        FORC(36) xtrans[0][c] = fgetc(ifp) & 3;
+      }
+      else if (len > 0)
+      {
+        if ((plen = len) > 16)
+          plen = 16;
+        fread(cfa_pat, 1, plen, ifp);
+        for (colors = cfa = i = 0; i < (int)plen && colors < 4; i++)
+        {
+          if (cfa_pat[i] > 31)
+            continue; // Skip wrong data
+          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;
+      }
+      break;
+    case 0x8290: // 33424
+    case 0xfe00: // 65024
+      fseek(ifp, get4() + base, SEEK_SET);
+      parse_kodak_ifd(base);
+      break;
+    case 0x829a: /* 33434, ExposureTime */
+      tiff_ifd[ifd].t_shutter = shutter = getreal(type);
+      break;
+    case 0x829d: /* 33437, FNumber */
+      aperture = getreal(type);
+      break;
+    case 0x9400:
+      imCommon.exifAmbientTemperature = getreal(type);
+      if ((imCommon.CameraTemperature > -273.15f) &&
+          ((OlyID == OlyID_TG_5) || (OlyID == OlyID_TG_6)))
+        imCommon.CameraTemperature +=
+            imCommon.exifAmbientTemperature;
+      break;
+    case 0x9401:
+      imCommon.exifHumidity = getreal(type);
+      break;
+    case 0x9402:
+      imCommon.exifPressure = getreal(type);
+      break;
+    case 0x9403:
+      imCommon.exifWaterDepth = getreal(type);
+      break;
+    case 0x9404:
+      imCommon.exifAcceleration = getreal(type);
+      break;
+    case 0x9405:
+      imCommon.exifCameraElevationAngle = getreal(type);
+      break;
+    case 0xa405: // FocalLengthIn35mmFormat
+      imgdata.lens.FocalLengthIn35mmFormat = get2();
+      break;
+    case 0xa431: // BodySerialNumber
+    case 0xc62f:
+      stmread(imgdata.shootinginfo.BodySerial, len, ifp);
+      break;
+    case 0xa432: // LensInfo, 42034dec, Lens Specification per EXIF standard
+      imgdata.lens.MinFocal = getreal(type);
+      imgdata.lens.MaxFocal = getreal(type);
+      imgdata.lens.MaxAp4MinFocal = getreal(type);
+      imgdata.lens.MaxAp4MaxFocal = getreal(type);
+      break;
+    case 0xa435: // LensSerialNumber
+      stmread(imgdata.lens.LensSerial, len, ifp);
+      break;
+    case 0xc630: // DNG LensInfo, Lens Specification per EXIF standard
+      imgdata.lens.MinFocal = getreal(type);
+      imgdata.lens.MaxFocal = getreal(type);
+      imgdata.lens.MaxAp4MinFocal = getreal(type);
+      imgdata.lens.MaxAp4MaxFocal = getreal(type);
+      break;
+    case 0xa420: /* 42016, ImageUniqueID */
+      stmread(imgdata.color.ImageUniqueID, len, ifp);
+      break;
+    case 0xc65d: /* 50781, RawDataUniqueID */
+      imgdata.color.RawDataUniqueID[16] = 0;
+      fread(imgdata.color.RawDataUniqueID, 1, 16, ifp);
+      break;
+    case 0xa433: // LensMake
+      stmread(imgdata.lens.LensMake, len, ifp);
+      break;
+    case 0xa434: // LensModel
+      stmread(imgdata.lens.Lens, len, ifp);
+      if (!strncmp(imgdata.lens.Lens, "----", 4))
+        imgdata.lens.Lens[0] = 0;
+      break;
+    case 0x9205:
+      imgdata.lens.EXIF_MaxAp = libraw_powf64l(2.0f, (getreal(type) / 2.0f));
+      break;
+    case 0x8602: /* 34306, Leaf white balance */
+      FORC4
+      {
+        int q = get2();
+        if (q)
+          cam_mul[GRGB_2_RGBG(c)] = 4096.0 / q;
+      }
+      break;
+    case 0x8603: /* 34307, Leaf CatchLight color matrix */
+      fread(software, 1, 7, ifp);
+      if (strncmp(software, "MATRIX", 6))
+        break;
+      colors = 4;
+      for (raw_color = i = 0; i < 3; i++)
+      {
+        FORC4 fscanf(ifp, "%f", &rgb_cam[i][GRGB_2_RGBG(c)]);
+        if (!use_camera_wb)
+          continue;
+        num = 0;
+        FORC4 num += rgb_cam[i][c];
+        FORC4 rgb_cam[i][c] /= MAX(1, num);
+      }
+      break;
+    case 0x8606: /* 34310, Leaf metadata */
+      parse_mos(ftell(ifp));
+    case 0x85ff: // 34303
+      strcpy(make, "Leaf");
+      break;
+    case 0x8769: /* 34665, EXIF tag */
+      fseek(ifp, get4() + base, SEEK_SET);
+      parse_exif(base);
+      break;
+    case 0x8825: /* 34853, GPSInfo tag */
+    {
+      unsigned pos;
+      fseek(ifp, pos = (get4() + base), SEEK_SET);
+      parse_gps(base);
+      fseek(ifp, pos, SEEK_SET);
+      parse_gps_libraw(base);
+    }
+    break;
+    case 0x8773: /* 34675, InterColorProfile */
+    case 0xc68f: /* 50831, AsShotICCProfile */
+      profile_offset = ftell(ifp);
+      profile_length = len;
+      break;
+    case 0x9102: /* 37122, CompressedBitsPerPixel */
+      kodak_cbpp = get4();
+      break;
+    case 0x920a: /* 37386, FocalLength */
+      focal_len = getreal(type);
+      break;
+    case 0x9211: /* 37393, ImageNumber */
+      shot_order = getint(type);
+      break;
+    case 0x9215: /* 37397, ExposureIndex */
+      imCommon.exifExposureIndex = getreal(type);
+      break;
+    case 0x9218: /* 37400, old Kodak KDC tag */
+      for (raw_color = i = 0; i < 3; i++)
+      {
+        getreal(type);
+        FORC3 rgb_cam[i][c] = getreal(type);
+      }
+      break;
+    case 0xa010: // 40976
+      strip_offset = get4();
+      switch (tiff_ifd[ifd].comp)
+      {
+      case 0x8002: // 32770
+        load_raw = &LibRaw::samsung_load_raw;
+        break;
+      case 0x8004: // 32772
+        load_raw = &LibRaw::samsung2_load_raw;
+        break;
+      case 0x8005: // 32773
+        load_raw = &LibRaw::samsung3_load_raw;
+        break;
+      }
+      break;
+    case 0xb4c3: /* 46275, Imacon tags */
+      imHassy.format = LIBRAW_HF_Imacon;
+      strcpy(make, "Imacon");
+      data_offset = ftell(ifp);
+      ima_len = len;
+      break;
+    case 0xb4c7: // 46279
+      if (!ima_len)
+        break;
+      fseek(ifp, 38, SEEK_CUR);
+    case 0xb4c2: // 46274
+      fseek(ifp, 40, SEEK_CUR);
+      raw_width = get4();
+      raw_height = get4();
+      left_margin = get4() & 7;
+      width = raw_width - left_margin - (get4() & 7);
+      top_margin = get4() & 7;
+      height = raw_height - top_margin - (get4() & 7);
+      if (raw_width == 7262 && ima_len == 234317952)
+      {
+        height = 5412;
+        width = 7216;
+        left_margin = 7;
+        filters = 0;
+      }
+      else if (raw_width == 7262)
+      {
+        height = 5444;
+        width = 7244;
+        left_margin = 7;
+      }
+      fseek(ifp, 52, SEEK_CUR);
+      FORC3 cam_mul[c] = getreal(11);
+      fseek(ifp, 114, SEEK_CUR);
+      flip = (get2() >> 7) * 90;
+      if (width * (height * 6l) == ima_len)
+      {
+        if (flip % 180 == 90)
+          SWAP(width, height);
+        raw_width = width;
+        raw_height = height;
+        left_margin = top_margin = filters = flip = 0;
+      }
+      c = unsigned(height) * unsigned(width) / 1000000;
+      if (c == 32)
+        c--;
+      sprintf(model, "Ixpress %d-Mp", c);
+      load_raw = &LibRaw::imacon_full_load_raw;
+      if (filters)
+      {
+        if (left_margin & 1)
+          filters = 0x61616161;
+        load_raw = &LibRaw::unpacked_load_raw;
+      }
+      maximum = 0xffff;
+      break;
+    case 0xc516: /* 50454, Sinar tag */
+    case 0xc517: // 50455
+      if (len < 1 || len > 2560000 || !(cbuf = (char *)malloc(len)))
+        break;
+      if (fread(cbuf, 1, len, ifp) != (int)len)
+        throw LIBRAW_EXCEPTION_IO_CORRUPT; // cbuf to be free'ed in recycle
+      cbuf[len - 1] = 0;
+      for (cp = cbuf - 1; cp && cp < cbuf + len; cp = strchr(cp, '\n'))
+        if (!strncmp(++cp, "Neutral ", 8))
+          sscanf(cp + 8, "%f %f %f", cam_mul, cam_mul + 1, cam_mul + 2);
+      free(cbuf);
+      break;
+    case 0xc51a: // 50458
+      if (!make[0])
+        strcpy(make, "Hasselblad");
+      break;
+    case 0xc51b: /* 50459, Hasselblad tag */
+      if (!libraw_internal_data.unpacker_data.hasselblad_parser_flag)
+      {
+        libraw_internal_data.unpacker_data.hasselblad_parser_flag = 1;
+        i = order;
+        j = ftell(ifp);
+        c = tiff_nifds;
+        order = get2();
+        fseek(ifp, j + (get2(), get4()), SEEK_SET);
+        parse_tiff_ifd(j);
+        maximum = 0xffff;
+        tiff_nifds = c;
+        order = i;
+        break;
+      }
+    case 0xc612: /* 50706, DNGVersion */
+      FORC4 dng_version = (dng_version << 8) + fgetc(ifp);
+      if (!make[0])
+        strcpy(make, "DNG");
+      is_raw = 1;
+      break;
+    case 0xc614: /* 50708, UniqueCameraModel */
+      stmread(imgdata.color.UniqueCameraModel, len, ifp);
+      if (model[0])
+        break;
+      strncpy(make, imgdata.color.UniqueCameraModel,
+              MIN(len, sizeof(imgdata.color.UniqueCameraModel)));
+      if ((cp = strchr(make, ' ')))
+      {
+        strcpy(model, cp + 1);
+        *cp = 0;
+      }
+      break;
+    case 0xc616: /* 50710, CFAPlaneColor */
+      if (filters == 9)
+        break;
+      if (len > 4)
+        len = 4;
+      colors = len;
+      fread(cfa_pc, 1, colors, ifp);
+    guess_cfa_pc:
+      FORCC tab[cfa_pc[c]] = c;
+      cdesc[c] = 0;
+      for (i = 16; i--;)
+        filters = filters << 2 | tab[cfa_pat[i % plen]];
+      filters -= !filters;
+      tiff_ifd[ifd].t_filters = filters;
+      break;
+    case 0xc617: /* 50711, CFALayout */
+      if (get2() == 2)
+        tiff_ifd[ifd].t_fuji_width = fuji_width = 1;
+      break;
+    case 0x0123: // 291
+    case 0xc618: /* 50712, LinearizationTable */
+      tiff_ifd[ifd].dng_levels.parsedfields |= LIBRAW_DNGFM_LINTABLE;
+      tiff_ifd[ifd].lineartable_offset = ftell(ifp);
+      tiff_ifd[ifd].lineartable_len = len;
+      linear_table(len);
+      break;
+    case 0xc619: /* 50713, BlackLevelRepeatDim */
+      tiff_ifd[ifd].dng_levels.parsedfields |= LIBRAW_DNGFM_BLACK;
+      tiff_ifd[ifd].dng_levels.dng_fcblack[4] =
+          tiff_ifd[ifd].dng_levels.dng_cblack[4] = cblack[4] = get2();
+      tiff_ifd[ifd].dng_levels.dng_fcblack[5] =
+          tiff_ifd[ifd].dng_levels.dng_cblack[5] = cblack[5] = get2();
+      if (cblack[4] * cblack[5] >
+          (LIBRAW_CBLACK_SIZE -
+           7)) // Use last cblack item as DNG black level count
+        tiff_ifd[ifd].dng_levels.dng_fcblack[4] =
+            tiff_ifd[ifd].dng_levels.dng_fcblack[5] =
+                tiff_ifd[ifd].dng_levels.dng_cblack[4] =
+                    tiff_ifd[ifd].dng_levels.dng_cblack[5] = cblack[4] =
+                        cblack[5] = 1;
+      break;
+
+    case 0xf00c:
+      if (!is_4K_RAFdata)
+      {
+        unsigned fwb[4];
+        FORC4 fwb[c] = get4();
+        if (fwb[3] < 0x100)
+        {
+          icWBC[fwb[3]][0] = fwb[1];
+          icWBC[fwb[3]][1] = icWBC[fwb[3]][3] = fwb[0];
+          icWBC[fwb[3]][2] = fwb[2];
+          if ((fwb[3] == 17) &&
+              (libraw_internal_data.unpacker_data.lenRAFData > 3) &&
+              (libraw_internal_data.unpacker_data.lenRAFData < 10240000))
+          {
+            INT64 f_save = ftell(ifp);
+            rafdata = (ushort *)malloc(
+                sizeof(ushort) * libraw_internal_data.unpacker_data.lenRAFData);
+            fseek(ifp, libraw_internal_data.unpacker_data.posRAFData, SEEK_SET);
+            fread(rafdata, sizeof(ushort),
+                  libraw_internal_data.unpacker_data.lenRAFData, ifp);
+            fseek(ifp, f_save, SEEK_SET);
+
+            uchar *PrivateMknBuf = (uchar *)rafdata;
+            int PrivateMknLength = libraw_internal_data.unpacker_data.lenRAFData
+                                   << 1;
+            for (int pos = 0; pos < PrivateMknLength - 16; pos++)
+            {
+              if (!memcmp(PrivateMknBuf + pos, "TSNERDTS", 8))
+              {
+                imFuji.isTSNERDTS = 1;
+                break;
+              }
+            }
+
+            /* 0xc000 tag version, second ushort; valid if the first ushort is 0
+             */
+            if (!rafdata[0])
+              imFuji.RAFDataVersion = rafdata[1];
+            int fj;
+            for (int fi = 0;
+                 fi < int(libraw_internal_data.unpacker_data.lenRAFData - 3); fi++)
+            { // find Tungsten WB
+              if ((fwb[0] == rafdata[fi]) && (fwb[1] == rafdata[fi + 1]) &&
+                  (fwb[2] == rafdata[fi + 2]))
+              {
+                if (rafdata[fi - 15] !=
+                    fwb[0]) // 15 is offset of Tungsten WB from the first
+                            // preset, Fine Weather WB
+                  continue;
+                for (int wb_ind = 0, ofst = fi - 15; wb_ind < Fuji_wb_list1.size();
+                     wb_ind++, ofst += 3)
+                {
+                  icWBC[Fuji_wb_list1[wb_ind]][1] =
+                      icWBC[Fuji_wb_list1[wb_ind]][3] = rafdata[ofst];
+                  icWBC[Fuji_wb_list1[wb_ind]][0] = rafdata[ofst + 1];
+                  icWBC[Fuji_wb_list1[wb_ind]][2] = rafdata[ofst + 2];
+                }
+
+                if ((imFuji.RAFDataVersion == 0x0260) || // X-Pro3
+                    (imFuji.RAFDataVersion == 0x0261) || // X100V
+                    (imFuji.RAFDataVersion == 0x0262))   // X-T4
+                  fi += 24;
+                fi += 96;
+                for (fj = fi; fj < (fi + 15); fj += 3)
+                {
+                  if (rafdata[fj] != rafdata[fi])
+                  {
+                    fj -= 93;
+// X-Pro3 has 34 CCT values instead of 31, manual still clames 2500 to 10000 K
+// aligned 3000 K to Incandescent, as it is usual w/ other Fujifilm cameras
+                    if ((imFuji.RAFDataVersion == 0x0260) || // X-Pro3
+                        (imFuji.RAFDataVersion == 0x0261) || // X100V
+                        (imFuji.RAFDataVersion == 0x0262))   // X-T4
+                      fj -= 9;
+                    for (int iCCT = 0, ofst = fj; iCCT < 31;
+                         iCCT++, ofst += 3)
+                    {
+                      icWBCCTC[iCCT][0] = FujiCCT_K[iCCT];
+                      icWBCCTC[iCCT][1] = rafdata[ofst + 1];
+                      icWBCCTC[iCCT][2] = icWBCCTC[iCCT][4] = rafdata[ofst];
+                      icWBCCTC[iCCT][3] = rafdata[ofst + 2];
+                    }
+                    break;
+                  }
+                }
+                free(rafdata);
+                break;
+              }
+            }
+          }
+        }
+        FORC4 fwb[c] = get4();
+        if (fwb[3] < 0x100)
+        {
+          icWBC[fwb[3]][0] = fwb[1];
+          icWBC[fwb[3]][1] =
+              icWBC[fwb[3]][3] = fwb[0];
+          icWBC[fwb[3]][2] = fwb[2];
+        }
+      }
+      break;
+    case 0xf00d:
+      if (!is_4K_RAFdata)
+      {
+        FORC3 icWBC[LIBRAW_WBI_Auto][(4 - c) % 3] = getint(type);
+        icWBC[LIBRAW_WBI_Auto][3] = icWBC[LIBRAW_WBI_Auto][1];
+        //        free(rafdata);
+      }
+      break;
+    case 0xc615: /* 50709, LocalizedCameraModel */
+      stmread(imgdata.color.LocalizedCameraModel, len, ifp);
+      break;
+    case 0xf00a: // 61450
+      cblack[4] = cblack[5] = MIN(sqrt((double)len), 64);
+    case 0xc61a: /* 50714, BlackLevel */
+      if (tiff_ifd[ifd].samples > 1 &&
+          tiff_ifd[ifd].samples == (int)len) // LinearDNG, per-channel black
+      {
+        tiff_ifd[ifd].dng_levels.parsedfields |= LIBRAW_DNGFM_BLACK;
+        for (i = 0; i < 4 && i < (int)len; i++)
+        {
+          tiff_ifd[ifd].dng_levels.dng_fcblack[i] = getreal(type);
+          tiff_ifd[ifd].dng_levels.dng_cblack[i] = cblack[i] =
+              tiff_ifd[ifd].dng_levels.dng_fcblack[i] + 0.5;
+        }
+
+        tiff_ifd[ifd].dng_levels.dng_fblack =
+            tiff_ifd[ifd].dng_levels.dng_black = black = 0;
+      }
+      else if (tiff_ifd[ifd].samples > 1 // Linear DNG w repeat dim
+               && (tiff_ifd[ifd].samples * cblack[4] * cblack[5] == len))
+      {
+        tiff_ifd[ifd].dng_levels.parsedfields |= LIBRAW_DNGFM_BLACK;
+        tiff_ifd[ifd].dng_levels.dng_cblack[LIBRAW_CBLACK_SIZE - 1] =
+            cblack[LIBRAW_CBLACK_SIZE - 1] = len;
+        for (i = 0; i < (int)len && i < LIBRAW_CBLACK_SIZE - 7; i++)
+        {
+          tiff_ifd[ifd].dng_levels.dng_fcblack[i + 6] = getreal(type);
+          tiff_ifd[ifd].dng_levels.dng_cblack[i + 6] = cblack[i + 6] =
+              tiff_ifd[ifd].dng_levels.dng_fcblack[i + 6] + 0.5;
+        }
+      }
+      else if ((cblack[4] * cblack[5] < 2) && len == 1)
+      {
+        tiff_ifd[ifd].dng_levels.parsedfields |= LIBRAW_DNGFM_BLACK;
+        tiff_ifd[ifd].dng_levels.dng_fblack = getreal(type);
+        black = tiff_ifd[ifd].dng_levels.dng_black =
+            tiff_ifd[ifd].dng_levels.dng_fblack;
+      }
+      else if (cblack[4] * cblack[5] <= len)
+      {
+        FORC(int(cblack[4] * cblack[5]))
+        {
+          tiff_ifd[ifd].dng_levels.dng_fcblack[6 + c] = getreal(type);
+          cblack[6 + c] = tiff_ifd[ifd].dng_levels.dng_fcblack[6 + c];
+        }
+        black = 0;
+        FORC4
+        cblack[c] = 0;
+
+        if (tag == 0xc61a)
+        {
+          tiff_ifd[ifd].dng_levels.parsedfields |= LIBRAW_DNGFM_BLACK;
+          FORC(int(cblack[4] * cblack[5]))
+          tiff_ifd[ifd].dng_levels.dng_cblack[6 + c] = cblack[6 + c];
+          tiff_ifd[ifd].dng_levels.dng_fblack = 0;
+          tiff_ifd[ifd].dng_levels.dng_black = 0;
+          FORC4
+          tiff_ifd[ifd].dng_levels.dng_fcblack[c] =
+              tiff_ifd[ifd].dng_levels.dng_cblack[c] = 0;
+        }
+      }
+      break;
+    case 0xc61b: /* 50715, BlackLevelDeltaH */
+    case 0xc61c: /* 50716, BlackLevelDeltaV */
+      for (num = i = 0; i < (int)len && i < 65536; i++)
+        num += getreal(type);
+      if (len > 0)
+      {
+        black += num / len + 0.5;
+        tiff_ifd[ifd].dng_levels.dng_fblack += num / float(len);
+        tiff_ifd[ifd].dng_levels.dng_black += num / len + 0.5;
+        tiff_ifd[ifd].dng_levels.parsedfields |= LIBRAW_DNGFM_BLACK;
+      }
+      break;
+    case 0xc61d: /* 50717, WhiteLevel */
+      tiff_ifd[ifd].dng_levels.parsedfields |= LIBRAW_DNGFM_WHITE;
+      tiff_ifd[ifd].dng_levels.dng_whitelevel[0] = maximum = getint(type);
+      if (tiff_ifd[ifd].samples > 1) // Linear DNG case
+        for (i = 1; i < 4 && i < (int)len; i++)
+          tiff_ifd[ifd].dng_levels.dng_whitelevel[i] = getint(type);
+      break;
+    case 0xc61e: /* DefaultScale */
+    {
+      float q1 = getreal(type);
+      float q2 = getreal(type);
+      if (q1 > 0.00001f && q2 > 0.00001f)
+      {
+        pixel_aspect = q1 / q2;
+        if (pixel_aspect > 0.995 && pixel_aspect < 1.005)
+          pixel_aspect = 1.0;
+      }
+    }
+    break;
+    case 0xc61f: /* 50719, DefaultCropOrigin */
+      if (len == 2)
+      {
+        tiff_ifd[ifd].dng_levels.parsedfields |= LIBRAW_DNGFM_CROPORIGIN;
+        tiff_ifd[ifd].dng_levels.default_crop[0] = getreal(type);
+        tiff_ifd[ifd].dng_levels.default_crop[1] = getreal(type);
+        if (!strncasecmp(make, "SONY", 4))
+        {
+          imgdata.sizes.raw_inset_crop.cleft =
+              tiff_ifd[ifd].dng_levels.default_crop[0];
+          imgdata.sizes.raw_inset_crop.ctop =
+              tiff_ifd[ifd].dng_levels.default_crop[1];
+        }
+      }
+      break;
+
+    case 0xc620: /* 50720, DefaultCropSize */
+      if (len == 2)
+      {
+        tiff_ifd[ifd].dng_levels.parsedfields |= LIBRAW_DNGFM_CROPSIZE;
+        tiff_ifd[ifd].dng_levels.default_crop[2] = getreal(type);
+        tiff_ifd[ifd].dng_levels.default_crop[3] = getreal(type);
+        if (!strncasecmp(make, "SONY", 4))
+        {
+          imgdata.sizes.raw_inset_crop.cwidth =
+              tiff_ifd[ifd].dng_levels.default_crop[2];
+          imgdata.sizes.raw_inset_crop.cheight =
+              tiff_ifd[ifd].dng_levels.default_crop[3];
+        }
+      }
+      break;
+
+    case 0x74c7:
+      if ((len == 2) && !strncasecmp(make, "SONY", 4))
+      {
+        imgdata.sizes.raw_inset_crop.cleft = get4();
+        imgdata.sizes.raw_inset_crop.ctop = get4();
+      }
+      break;
+
+    case 0x74c8:
+      if ((len == 2) && !strncasecmp(make, "SONY", 4))
+      {
+        imgdata.sizes.raw_inset_crop.cwidth = get4();
+        imgdata.sizes.raw_inset_crop.cheight = get4();
+      }
+      break;
+
+    case 0xc65a: // 50778
+      tiff_ifd[ifd].dng_color[0].illuminant = get2();
+      tiff_ifd[ifd].dng_color[0].parsedfields |= LIBRAW_DNGFM_ILLUMINANT;
+      break;
+    case 0xc65b: // 50779
+      tiff_ifd[ifd].dng_color[1].illuminant = get2();
+      tiff_ifd[ifd].dng_color[1].parsedfields |= LIBRAW_DNGFM_ILLUMINANT;
+      break;
+
+    case 0xc621: /* 50721, ColorMatrix1 */
+    case 0xc622: /* 50722, ColorMatrix2 */
+    {
+      int chan = (len == 9) ? 3 : (len == 12 ? 4 : 0);
+      i = tag == 0xc621 ? 0 : 1;
+      if (chan)
+      {
+        tiff_ifd[ifd].dng_color[i].parsedfields |= LIBRAW_DNGFM_COLORMATRIX;
+        imHassy.nIFD_CM[i] = ifd;
+      }
+      FORC(chan) for (j = 0; j < 3; j++)
+      {
+        tiff_ifd[ifd].dng_color[i].colormatrix[c][j] = cm[c][j] = getreal(type);
+      }
+      use_cm = 1;
+    }
+    break;
+
+    case 0xc714: /* ForwardMatrix1 */
+    case 0xc715: /* ForwardMatrix2 */
+    {
+      int chan = (len == 9) ? 3 : (len == 12 ? 4 : 0);
+      i = tag == 0xc714 ? 0 : 1;
+      if (chan)
+        tiff_ifd[ifd].dng_color[i].parsedfields |= LIBRAW_DNGFM_FORWARDMATRIX;
+      for (j = 0; j < 3; j++)
+        FORC(chan)
+        {
+          tiff_ifd[ifd].dng_color[i].forwardmatrix[j][c] = fm[j][c] =
+              getreal(type);
+        }
+    }
+    break;
+
+    case 0xc623: /* 50723, CameraCalibration1 */
+    case 0xc624: /* 50724, CameraCalibration2 */
+    {
+      int chan = (len == 9) ? 3 : (len == 16 ? 4 : 0);
+      j = tag == 0xc623 ? 0 : 1;
+      if (chan)
+        tiff_ifd[ifd].dng_color[j].parsedfields |= LIBRAW_DNGFM_CALIBRATION;
+      for (i = 0; i < chan; i++)
+        FORC(chan)
+        {
+          tiff_ifd[ifd].dng_color[j].calibration[i][c] = cc[i][c] =
+              getreal(type);
+        }
+    }
+    break;
+    case 0xc627: /* 50727, AnalogBalance */
+      if (len >= 3)
+        tiff_ifd[ifd].dng_levels.parsedfields |= LIBRAW_DNGFM_ANALOGBALANCE;
+      for (c = 0; c < (int)len && c < 4; c++)
+      {
+        tiff_ifd[ifd].dng_levels.analogbalance[c] = ab[c] = getreal(type);
+      }
+      break;
+    case 0xc628: /* 50728, AsShotNeutral */
+      if (len >= 3)
+        tiff_ifd[ifd].dng_levels.parsedfields |= LIBRAW_DNGFM_ASSHOTNEUTRAL;
+      for (c = 0; c < (int)len && c < 4; c++)
+        tiff_ifd[ifd].dng_levels.asshotneutral[c] = asn[c] = getreal(type);
+      break;
+    case 0xc629: /* 50729, AsShotWhiteXY */
+      xyz[0] = getreal(type);
+      xyz[1] = getreal(type);
+      xyz[2] = 1 - xyz[0] - xyz[1];
+      FORC3 xyz[c] /= LibRaw_constants::d65_white[c];
+      break;
+    case 0xc62a: /* DNG: 50730 BaselineExposure */
+      tiff_ifd[ifd].dng_levels.parsedfields |= LIBRAW_DNGFM_BASELINEEXPOSURE;
+      tiff_ifd[ifd].dng_levels.baseline_exposure = getreal(type);
+      break;
+    case 0xc62e: /* DNG: 50734 LinearResponseLimit */
+      tiff_ifd[ifd].dng_levels.parsedfields |= LIBRAW_DNGFM_LINEARRESPONSELIMIT;
+      tiff_ifd[ifd].dng_levels.LinearResponseLimit = getreal(type);
+      break;
+
+    case 0xc634: /* 50740 : DNG Adobe, DNG Pentax, Sony SR2, DNG Private */
+      if (!(imgdata.params.raw_processing_options &
+            LIBRAW_PROCESSING_SKIP_MAKERNOTES))
+      {
+        char mbuf[64];
+        INT64 curr_pos, start_pos = ftell(ifp);
+        unsigned MakN_order, m_sorder = order;
+        unsigned MakN_length;
+        unsigned pos_in_original_raw;
+        fread(mbuf, 1, 6, ifp);
+
+        if (!strcmp(mbuf, "Adobe"))
+        {
+          order = 0x4d4d; // Adobe header is always in "MM" / big endian
+          curr_pos = start_pos + 6;
+          while (curr_pos + 8 - start_pos <= len)
+          {
+            fread(mbuf, 1, 4, ifp);
+            curr_pos += 8;
+
+            if (!strncmp(mbuf, "Pano", 4))
+            { // PanasonicRaw, yes, they use "Pano" as signature
+              parseAdobePanoMakernote();
+            }
+
+            if (!strncmp(mbuf, "MakN", 4))
+            {
+              MakN_length = get4();
+              MakN_order = get2();
+              pos_in_original_raw = get4();
+              order = MakN_order;
+
+              INT64 save_pos = ifp->tell();
+              parse_makernote_0xc634(curr_pos + 6 - pos_in_original_raw, 0,
+                                     AdobeDNG);
+
+              curr_pos = save_pos + MakN_length - 6;
+              fseek(ifp, curr_pos, SEEK_SET);
+
+              fread(mbuf, 1, 4, ifp);
+              curr_pos += 8;
+
+              if (!strncmp(mbuf, "Pano ", 4))
+              {
+                parseAdobePanoMakernote();
+              }
+
+              if (!strncmp(mbuf, "RAF ", 4))
+              { // Fujifilm Raw, AdobeRAF
+                parseAdobeRAFMakernote();
+              }
+
+              if (!strncmp(mbuf, "SR2 ", 4))
+              {
+                order = 0x4d4d;
+                MakN_length = get4();
+                MakN_order = get2();
+                pos_in_original_raw = get4();
+                order = MakN_order;
+
+                unsigned *buf_SR2;
+                unsigned entries, tag, type, len, save;
+                unsigned SR2SubIFDOffset = 0;
+                unsigned SR2SubIFDLength = 0;
+                unsigned SR2SubIFDKey = 0;
+                int base = curr_pos + 6 - pos_in_original_raw;
+                entries = get2();
+                while (entries--)
+                {
+                  tiff_get(base, &tag, &type, &len, &save);
+
+                  if (tag == 0x7200)
+                  {
+                    SR2SubIFDOffset = get4();
+                  }
+                  else if (tag == 0x7201)
+                  {
+                    SR2SubIFDLength = get4();
+                  }
+                  else if (tag == 0x7221)
+                  {
+                    SR2SubIFDKey = get4();
+                  }
+                  fseek(ifp, save, SEEK_SET);
+                }
+
+                if (SR2SubIFDLength && (SR2SubIFDLength < 10240000) &&
+                    (buf_SR2 = (unsigned *)malloc(SR2SubIFDLength + 1024)))
+                { // 1024b for safety
+                  fseek(ifp, SR2SubIFDOffset + base, SEEK_SET);
+                  fread(buf_SR2, SR2SubIFDLength, 1, ifp);
+                  sony_decrypt(buf_SR2, SR2SubIFDLength / 4, 1, SR2SubIFDKey);
+                  parseSonySR2((uchar *)buf_SR2, SR2SubIFDOffset,
+                               SR2SubIFDLength, AdobeDNG);
+
+                  free(buf_SR2);
+                }
+
+              } /* SR2 processed */
+              break;
+            }
+          }
+        }
+        else
+        {
+          fread(mbuf + 6, 1, 2, ifp);
+          if (!strcmp(mbuf, "RICOH") && ((sget2((uchar *)mbuf + 6) == 0x4949) ||
+                                         (sget2((uchar *)mbuf + 6) == 0x4d4d)))
+          {
+            is_PentaxRicohMakernotes = 1;
+          }
+          if (!strcmp(mbuf, "PENTAX ") || !strcmp(mbuf, "SAMSUNG") ||
+              is_PentaxRicohMakernotes)
+          {
+            fseek(ifp, start_pos, SEEK_SET);
+            parse_makernote_0xc634(base, 0, CameraDNG);
+          }
+        }
+        fseek(ifp, start_pos, SEEK_SET);
+        order = m_sorder;
+      }
+      if (dng_version)
+      {
+        break;
+      }
+      parse_minolta(j = get4() + base);
+      fseek(ifp, j, SEEK_SET);
+      parse_tiff_ifd(base);
+      break;
+    case 0xc640: // 50752
+      read_shorts(cr2_slice, 3);
+      break;
+    case 0xc68b: /* 50827, OriginalRawFileName */
+      stmread(imgdata.color.OriginalRawFileName, len, ifp);
+      break;
+    case 0xc68d: /* 50829 ActiveArea */
+      tiff_ifd[ifd].t_tm = top_margin = getint(type);
+      tiff_ifd[ifd].t_lm = left_margin = getint(type);
+      tiff_ifd[ifd].t_vheight = height = getint(type) - top_margin;
+      tiff_ifd[ifd].t_vwidth = width = getint(type) - left_margin;
+      break;
+    case 0xc68e: /* 50830 MaskedAreas */
+      for (i = 0; i < (int)len && i < 32; i++)
+        ((int *)mask)[i] = getint(type);
+      black = 0;
+      break;
+    case 0xc71a: /* 50970, PreviewColorSpace */
+      tiff_ifd[ifd].dng_levels.parsedfields |= LIBRAW_DNGFM_PREVIEWCS;
+      tiff_ifd[ifd].dng_levels.preview_colorspace = getint(type);
+      break;
+    case 0xc741: /* 51009, OpcodeList2 */
+      tiff_ifd[ifd].dng_levels.parsedfields |= LIBRAW_DNGFM_OPCODE2;
+      tiff_ifd[ifd].opcode2_offset = meta_offset = ftell(ifp);
+      break;
+    case 0xfd04: /* 64772, Kodak P-series */
+      if (len < 13)
+        break;
+      fseek(ifp, 16, SEEK_CUR);
+      data_offset = get4();
+      fseek(ifp, 28, SEEK_CUR);
+      data_offset += get4();
+      load_raw = &LibRaw::packed_load_raw;
+      break;
+    case 0xfe02: // 65026
+      if (tagtypeIs(LIBRAW_EXIFTAG_TYPE_ASCII))
+        fgets(model2, 64, ifp);
+    }
+    fseek(ifp, save, SEEK_SET);
+  }
+  if (sony_length && sony_length < 10240000 &&
+      (buf = (unsigned *)malloc(sony_length)))
+  {
+    fseek(ifp, sony_offset, SEEK_SET);
+    fread(buf, sony_length, 1, ifp);
+    sony_decrypt(buf, sony_length / 4, 1, sony_key);
+    parseSonySR2((uchar *)buf, sony_offset, sony_length, nonDNG);
+    free(buf);
+  }
+  for (i = 0; i < colors && i < 4; i++)
+    FORCC cc[i][c] *= ab[i];
+  if (use_cm)
+  {
+    FORCC for (i = 0; i < 3; i++) for (cam_xyz[c][i] = j = 0; j < colors; j++)
+        cam_xyz[c][i] += cc[c][j] * cm[j][i] * xyz[i];
+    cam_xyz_coeff(cmatrix, cam_xyz);
+  }
+  if (asn[0])
+  {
+    cam_mul[3] = 0;
+    FORCC
+    if (fabs(asn[c]) > 0.0001)
+      cam_mul[c] = 1 / asn[c];
+  }
+  if (!use_cm)
+    FORCC if (fabs(cc[c][c]) > 0.0001) pre_mul[c] /= cc[c][c];
+  return 0;
+}
+
+int LibRaw::parse_tiff(int base)
+{
+  int doff;
+  fseek(ifp, base, SEEK_SET);
+  order = get2();
+  if (order != 0x4949 && order != 0x4d4d)
+    return 0;
+  get2();
+  while ((doff = get4()))
+  {
+    fseek(ifp, doff + base, SEEK_SET);
+    if (parse_tiff_ifd(base))
+      break;
+  }
+  return 1;
+}
+
+struct ifd_size_t
+{
+  int ifdi;
+  INT64 databits;
+};
+
+int ifd_size_t_cmp(const void *a, const void *b)
+{
+  if (!a || !b)
+    return 0;
+  const ifd_size_t *ai = (ifd_size_t *)a;
+  const ifd_size_t *bi = (ifd_size_t *)b;
+  return bi->databits > ai->databits ? 1
+                                     : (bi->databits < ai->databits ? -1 : 0);
+}
+
+void LibRaw::apply_tiff()
+{
+  int max_samp = 0, ties = 0, raw = -1, thm = -1, i;
+  unsigned long long ns, os;
+  struct jhead jh;
+
+  thumb_misc = 16;
+  if (thumb_offset)
+  {
+    fseek(ifp, thumb_offset, SEEK_SET);
+    if (ljpeg_start(&jh, 1))
+    {
+      if ((unsigned)jh.bits < 17 && (unsigned)jh.wide < 0x10000 &&
+          (unsigned)jh.high < 0x10000)
+      {
+        thumb_misc = jh.bits;
+        thumb_width = jh.wide;
+        thumb_height = jh.high;
+      }
+    }
+  }
+  for (i = tiff_nifds; i--;)
+  {
+    if (tiff_ifd[i].t_shutter)
+      shutter = tiff_ifd[i].t_shutter;
+    tiff_ifd[i].t_shutter = shutter;
+  }
+
+  if (dng_version)
+  {
+    int ifdc = 0;
+    for (i = 0; i < (int)tiff_nifds; i++)
+    {
+      if (tiff_ifd[i].t_width < 1 || tiff_ifd[i].t_width > 65535 ||
+          tiff_ifd[i].t_height < 1 || tiff_ifd[i].t_height > 65535)
+        continue; /* wrong image dimensions */
+
+      int samp = tiff_ifd[i].samples;
+      if (samp == 2)
+        samp = 1; // Fuji 2-frame
+      max_samp = LIM(MAX(max_samp, samp), 1,
+                     3); // max_samp is needed for thumbnail selection below
+
+      if (tiff_ifd[i].phint != 32803 && tiff_ifd[i].phint != 34892)
+        continue;
+
+      if ((tiff_ifd[i].newsubfiletype == 0) // main image
+                                            // Enhanced demosaiced:
+          || (tiff_ifd[i].newsubfiletype == 16 &&
+              (imgdata.params.raw_processing_options &
+               LIBRAW_PROCESSING_DNG_ADD_ENHANCED))
+          // Preview: 0x1 or 0x10001
+          || ((tiff_ifd[i].newsubfiletype & 0xffff) == 1 &&
+              (imgdata.params.raw_processing_options &
+               LIBRAW_PROCESSING_DNG_ADD_PREVIEWS)))
+      {
+        // Add this IFD to dng_frames
+        libraw_internal_data.unpacker_data.dng_frames[ifdc] =
+            ((tiff_ifd[i].newsubfiletype & 0xffff) << 16) | ((i << 8) & 0xff00);
+        ifdc++;
+        // Fuji SuperCCD: second frame:
+        if ((tiff_ifd[i].newsubfiletype == 0) && tiff_ifd[i].samples == 2)
+        {
+          libraw_internal_data.unpacker_data.dng_frames[ifdc] =
+              ((tiff_ifd[i].newsubfiletype & 0xffff) << 16) |
+              ((i << 8) & 0xff00) | 1;
+          ifdc++;
+        }
+      }
+    }
+    if (ifdc)
+    {
+      if (ifdc > 1 && (imgdata.params.raw_processing_options &
+                       LIBRAW_PROCESSING_DNG_PREFER_LARGEST_IMAGE))
+      {
+        ifd_size_t arr[LIBRAW_IFD_MAXCOUNT * 2];
+        memset(arr, 0, sizeof(arr));
+        for (int i = 0; i < ifdc && i < LIBRAW_IFD_MAXCOUNT * 2; i++)
+        {
+          int ifdidx =
+              (libraw_internal_data.unpacker_data.dng_frames[i] >> 8) & 0xff;
+          arr[i].ifdi = libraw_internal_data.unpacker_data.dng_frames[i];
+          arr[i].databits =
+              tiff_ifd[ifdidx].t_width * tiff_ifd[ifdidx].t_height *
+                  tiff_ifd[ifdidx].samples * tiff_ifd[ifdidx].bps +
+              (0x100 -
+               (arr[i].ifdi & 0xff)); // add inverted frame # to ensure same
+                                      // sort order for similar sized frames.
+        }
+        qsort(arr, MIN(ifdc, LIBRAW_IFD_MAXCOUNT * 2), sizeof(arr[0]),
+              ifd_size_t_cmp);
+        for (int i = 0; i < ifdc && i < LIBRAW_IFD_MAXCOUNT * 2; i++)
+          libraw_internal_data.unpacker_data.dng_frames[i] = arr[i].ifdi;
+      }
+
+      int idx = LIM((int)shot_select, 0, ifdc - 1);
+      i = (libraw_internal_data.unpacker_data.dng_frames[idx] >> 8) &
+          0xff; // extract frame# back
+
+      raw_width = tiff_ifd[i].t_width;
+      raw_height = tiff_ifd[i].t_height;
+      tiff_bps = tiff_ifd[i].bps;
+      tiff_compress = tiff_ifd[i].comp;
+      tiff_sampleformat = tiff_ifd[i].sample_format;
+      data_offset = tiff_ifd[i].offset;
+      data_size = tiff_ifd[i].bytes;
+      tiff_flip = tiff_ifd[i].t_flip;
+      tiff_samples = tiff_ifd[i].samples;
+      tile_width = tiff_ifd[i].t_tile_width;
+      tile_length = tiff_ifd[i].t_tile_length;
+      fuji_width = tiff_ifd[i].t_fuji_width;
+      if (tiff_samples != 2) /* special case: Fuji SuperCCD */
+      {
+        if (tiff_ifd[i].phint == 34892)
+          filters = 0;
+        else if (i > 0 && tiff_ifd[i].phint == 32803 &&
+                 tiff_ifd[0].phint == 32803 && !tiff_ifd[i].t_filters &&
+                 tiff_ifd[0].t_filters)
+          filters = tiff_ifd[0].t_filters;
+        else
+          filters = tiff_ifd[i].t_filters;
+        width = tiff_ifd[i].t_vwidth;
+        height = tiff_ifd[i].t_vheight;
+        top_margin = tiff_ifd[i].t_tm;
+        left_margin = tiff_ifd[i].t_lm;
+        shutter = tiff_ifd[i].t_shutter;
+        if (tiff_ifd[i].dng_levels.dng_whitelevel[0])
+          maximum = tiff_ifd[i].dng_levels.dng_whitelevel[0];
+        else if (tiff_ifd[i].sample_format <= 2 && tiff_bps > 0 &&
+                 tiff_bps < 32) // SampleFormat: 0-default(1), 1 - Uint, 2 - Int
+          maximum = (1 << tiff_bps) - 1;
+        else if (tiff_ifd[i].sample_format == 3)
+          maximum = 1; // Defaults for FP
+      }
+      raw = i;
+      is_raw = ifdc;
+    }
+    else
+      is_raw = 0;
+  }
+  else
+  {
+    for (i = 0; i < (int)tiff_nifds; i++)
+    {
+      if (tiff_ifd[i].t_width < 1 || tiff_ifd[i].t_width > 65535 ||
+          tiff_ifd[i].t_height < 1 || tiff_ifd[i].t_height > 65535)
+        continue; /* wrong image dimensions */
+      if (max_samp < tiff_ifd[i].samples)
+        max_samp = tiff_ifd[i].samples;
+      if (max_samp > 3)
+        max_samp = 3;
+
+      os = unsigned(raw_width) * unsigned(raw_height);
+      ns = unsigned(tiff_ifd[i].t_width) * unsigned(tiff_ifd[i].t_height);
+      if (tiff_bps)
+      {
+        os *= tiff_bps;
+        ns *= tiff_ifd[i].bps;
+      }
+      /* too complex if below, so separate if to skip RGB+Alpha TIFFs*/
+      if (tiff_ifd[i].phint == 2 && tiff_ifd[i].extrasamples > 0 && tiff_ifd[i].samples > 3)
+          continue; // SKIP RGB+Alpha IFDs
+
+      if ((tiff_ifd[i].comp != 6 || tiff_ifd[i].samples != 3) &&
+            unsigned(tiff_ifd[i].t_width | tiff_ifd[i].t_height) < 0x10000 &&
+            (unsigned)tiff_ifd[i].bps < 33 &&
+            (unsigned)tiff_ifd[i].samples < 13 && ns &&
+            ((ns > os && (ties = 1)) || (ns == os && (int)shot_select == ties++)))
+        {
+        raw_width = tiff_ifd[i].t_width;
+        raw_height = tiff_ifd[i].t_height;
+        tiff_bps = tiff_ifd[i].bps;
+        tiff_compress = tiff_ifd[i].comp;
+        tiff_sampleformat = tiff_ifd[i].sample_format;
+        data_offset = tiff_ifd[i].offset;
+        data_size = tiff_ifd[i].bytes;
+        tiff_flip = tiff_ifd[i].t_flip;
+        tiff_samples = tiff_ifd[i].samples;
+        tile_width = tiff_ifd[i].t_tile_width;
+        tile_length = tiff_ifd[i].t_tile_length;
+        shutter = tiff_ifd[i].t_shutter;
+        raw = i;
+        }
+    }
+    if (is_raw == 1 && ties)
+      is_raw = ties;
+  }
+  if (is_NikonTransfer && raw >= 0)
+  {
+    if (tiff_ifd[raw].bps == 16)
+    {
+      if (tiff_compress == 1)
+      {
+        if ((raw_width * raw_height * 3) == (tiff_ifd[raw].bytes << 1))
+        {
+          tiff_bps = tiff_ifd[raw].bps = 12;
+        }
+        else
+        {
+          tiff_bps = tiff_ifd[raw].bps = 14;
+        }
+      }
+    }
+    else if (tiff_ifd[raw].bps == 8)
+    {
+      if (tiff_compress == 1)
+      {
+        is_NikonTransfer = 2; // 8-bit debayered TIFF, like CoolScan NEFs
+        imgdata.params.coolscan_nef_gamma = 2.2f;
+      }
+    }
+  }
+
+  if (!tile_width)
+    tile_width = INT_MAX;
+  if (!tile_length)
+    tile_length = INT_MAX;
+  for (i = tiff_nifds; i--;)
+    if (tiff_ifd[i].t_flip)
+      tiff_flip = tiff_ifd[i].t_flip;
+
+#if 0
+  if (raw < 0 && is_raw)
+      is_raw = 0;
+#endif
+
+  if (raw >= 0 && !load_raw)
+    switch (tiff_compress)
+    {
+    case 32767:
+      if (!dng_version &&
+          INT64(tiff_ifd[raw].bytes) == INT64(raw_width) * INT64(raw_height))
+      {
+        tiff_bps = 14;
+        load_raw = &LibRaw::sony_arw2_load_raw;
+        break;
+      }
+      if (!dng_version && !strncasecmp(make, "Sony", 4) &&
+          INT64(tiff_ifd[raw].bytes) ==
+              INT64(raw_width) * INT64(raw_height) * 2LL)
+      {
+        tiff_bps = 14;
+        load_raw = &LibRaw::unpacked_load_raw;
+        break;
+      }
+      if (INT64(tiff_ifd[raw].bytes) * 8ULL !=
+          INT64(raw_width) * INT64(raw_height) * INT64(tiff_bps))
+      {
+        raw_height += 8;
+        load_raw = &LibRaw::sony_arw_load_raw;
+        break;
+      }
+      load_flags = 79;
+    case 32769:
+      load_flags++;
+    case 32770:
+    case 32773:
+      goto slr;
+    case 0:
+    case 1:
+#ifdef USE_DNGSDK
+      if (dng_version && tiff_sampleformat == 3 &&
+          (tiff_bps > 8 && (tiff_bps % 8 == 0))) // 16,24,32,48...
+      {
+        load_raw = &LibRaw::float_dng_load_raw_placeholder;
+        break;
+      }
+#endif
+      // Sony 14-bit uncompressed
+      if (!dng_version && !strncasecmp(make, "Sony", 4) &&
+          INT64(tiff_ifd[raw].bytes) ==
+              INT64(raw_width) * INT64(raw_height) * 2LL)
+      {
+        tiff_bps = 14;
+        load_raw = &LibRaw::unpacked_load_raw;
+        break;
+      }
+      if (!dng_version && !strncasecmp(make, "Sony", 4) &&
+          tiff_ifd[raw].samples == 4 &&
+          INT64(tiff_ifd[raw].bytes) ==
+              INT64(raw_width) * INT64(raw_height) * 8LL) // Sony ARQ
+      {
+        // maybe to detect ARQ with the following:
+        // if (tiff_ifd[raw].phint == 32892)
+        tiff_bps = 14;
+        tiff_samples = 4;
+        load_raw = &LibRaw::sony_arq_load_raw;
+        filters = 0;
+        strcpy(cdesc, "RGBG");
+        break;
+      }
+      if (!strncasecmp(make, "Nikon", 5) &&
+          (!strncmp(software, "Nikon Scan", 10) || (is_NikonTransfer == 2) ||
+           strcasestr(model, "COOLSCAN")))
+      {
+        load_raw = &LibRaw::nikon_coolscan_load_raw;
+        raw_color = 1;
+        filters = 0;
+        break;
+      }
+      if ((!strncmp(make, "OLYMPUS", 7) ||
+           (!strncasecmp(make, "CLAUSS", 6) &&
+            !strncasecmp(model, "piX 5oo", 7))) && // 0x5330303539 works here
+          (INT64(tiff_ifd[raw].bytes) * 2ULL ==
+           INT64(raw_width) * INT64(raw_height) * 3ULL))
+        load_flags = 24;
+      if (!dng_version && INT64(tiff_ifd[raw].bytes) * 5ULL ==
+                              INT64(raw_width) * INT64(raw_height) * 8ULL)
+      {
+        load_flags = 81;
+        tiff_bps = 12;
+      }
+    slr:
+      switch (tiff_bps)
+      {
+      case 8:
+        load_raw = &LibRaw::eight_bit_load_raw;
+        break;
+      case 12:
+        if (tiff_ifd[raw].phint == 2)
+          load_flags = 6;
+        if (!strncasecmp(make, "NIKON", 5) &&
+            !strncasecmp(model, "COOLPIX A1000", 13) &&
+            data_size == raw_width * raw_height * 2)
+          load_raw = &LibRaw::unpacked_load_raw;
+        else
+          load_raw = &LibRaw::packed_load_raw;
+        break;
+      case 14:
+        load_flags = 0;
+      case 16:
+        load_raw = &LibRaw::unpacked_load_raw;
+        if ((!strncmp(make, "OLYMPUS", 7) ||
+             (!strncasecmp(make, "CLAUSS", 6) &&
+              !strncasecmp(model, "piX 5oo", 7))) && // 0x5330303539 works here
+            (INT64(tiff_ifd[raw].bytes) * 7ULL >
+             INT64(raw_width) * INT64(raw_height)))
+          load_raw = &LibRaw::olympus_load_raw;
+      }
+      break;
+    case 6:
+    case 7:
+    case 99:
+      load_raw = &LibRaw::lossless_jpeg_load_raw;
+      break;
+    case 262:
+      load_raw = &LibRaw::kodak_262_load_raw;
+      break;
+    case 34713:
+      if ((INT64(raw_width) + 9LL) / 10LL * 16LL * INT64(raw_height) ==
+          INT64(tiff_ifd[raw].bytes))
+      {
+        load_raw = &LibRaw::packed_load_raw;
+        load_flags = 1;
+      }
+      else if (INT64(raw_width) * INT64(raw_height) * 3LL ==
+               INT64(tiff_ifd[raw].bytes) * 2LL)
+      {
+        load_raw = &LibRaw::packed_load_raw;
+        if (model[0] == 'N')
+          load_flags = 80;
+      }
+      else if (INT64(raw_width) * INT64(raw_height) * 3LL ==
+               INT64(tiff_ifd[raw].bytes))
+      {
+        load_raw = &LibRaw::nikon_yuv_load_raw;
+        gamma_curve(1 / 2.4, 12.92, 1, 4095);
+        memset(cblack, 0, sizeof cblack);
+        filters = 0;
+      }
+      else if (INT64(raw_width) * INT64(raw_height) * 2LL ==
+               INT64(tiff_ifd[raw].bytes))
+      {
+        load_raw = &LibRaw::unpacked_load_raw;
+        load_flags = 4;
+        order = 0x4d4d;
+      }
+      else if (INT64(raw_width) * INT64(raw_height) * 3LL ==
+               INT64(tiff_ifd[raw].bytes) * 2LL)
+      {
+        load_raw = &LibRaw::packed_load_raw;
+        load_flags = 80;
+      }
+      else if (tiff_ifd[raw].rows_per_strip &&
+               tiff_ifd[raw].strip_offsets_count &&
+               tiff_ifd[raw].strip_offsets_count ==
+                   tiff_ifd[raw].strip_byte_counts_count)
+      {
+        int fit = 1;
+        for (int i = 0; i < tiff_ifd[raw].strip_byte_counts_count - 1;
+             i++) // all but last
+          if (INT64(tiff_ifd[raw].strip_byte_counts[i]) * 2LL !=
+              INT64(tiff_ifd[raw].rows_per_strip) * INT64(raw_width) * 3LL)
+          {
+            fit = 0;
+            break;
+          }
+        if (fit)
+          load_raw = &LibRaw::nikon_load_striped_packed_raw;
+        else
+          load_raw = &LibRaw::nikon_load_raw; // fallback
+      }
+      else if ((((INT64(raw_width) * 3LL / 2LL) + 15LL) / 16LL) * 16LL *
+                   INT64(raw_height) ==
+               INT64(tiff_ifd[raw].bytes))
+      {
+        load_raw = &LibRaw::nikon_load_padded_packed_raw;
+        load_flags = (((INT64(raw_width) * 3ULL / 2ULL) + 15ULL) / 16ULL) *
+                     16ULL; // bytes per row
+      }
+      else
+        load_raw = &LibRaw::nikon_load_raw;
+      break;
+    case 65535:
+      load_raw = &LibRaw::pentax_load_raw;
+      break;
+    case 65000:
+      switch (tiff_ifd[raw].phint)
+      {
+      case 2:
+        load_raw = &LibRaw::kodak_rgb_load_raw;
+        filters = 0;
+        break;
+      case 6:
+        load_raw = &LibRaw::kodak_ycbcr_load_raw;
+        filters = 0;
+        break;
+      case 32803:
+        load_raw = &LibRaw::kodak_65000_load_raw;
+      }
+    case 32867:
+    case 34892:
+      break;
+    case 8:
+      break;
+#ifdef USE_GPRSDK
+    case 9:
+      if (dng_version)
+        break; /* Compression=9 supported for dng if we compiled with GPR SDK */
+               /* Else: fallthrough */
+#endif
+    default:
+      is_raw = 0;
+    }
+  if (!dng_version)
+  {
+      if (((tiff_samples == 3 && tiff_ifd[raw].bytes &&
+          !(tiff_bps == 16 &&
+              !strncmp(make, "Leaf", 4)) && // Allow Leaf/16bit/3color files
+          tiff_bps != 14 &&
+          (tiff_compress & -16) != 32768) ||
+          (tiff_bps == 8 && strncmp(make, "Phase", 5) &&
+              strncmp(make, "Leaf", 4) && !strcasestr(make, "Kodak") &&
+              !strstr(model2, "DEBUG RAW"))) &&
+          !strcasestr(model, "COOLSCAN") && strncmp(software, "Nikon Scan", 10) &&
+          is_NikonTransfer != 2)
+          is_raw = 0;
+
+      if (is_raw && raw >= 0 && tiff_ifd[raw].phint == 2 && tiff_ifd[raw].extrasamples > 0 && tiff_ifd[raw].samples > 3)
+          is_raw = 0; // SKIP RGB+Alpha IFDs
+  }
+
+  for (i = 0; i < (int)tiff_nifds; i++)
+    if (i != raw &&
+        (tiff_ifd[i].samples == max_samp ||
+         (tiff_ifd[i].comp == 7 &&
+          tiff_ifd[i].samples == 1)) /* Allow 1-bps JPEGs */
+        && tiff_ifd[i].bps > 0 && tiff_ifd[i].bps < 33 &&
+        tiff_ifd[i].phint != 32803 && tiff_ifd[i].phint != 34892 &&
+        unsigned(tiff_ifd[i].t_width | tiff_ifd[i].t_height) < 0x10000 &&
+        unsigned(tiff_ifd[i].t_width * tiff_ifd[i].t_height /
+                (SQR(tiff_ifd[i].bps) + 1)) >
+            unsigned(thumb_width * thumb_height / (SQR(thumb_misc) + 1)) &&
+        tiff_ifd[i].comp != 34892)
+    {
+      thumb_width = tiff_ifd[i].t_width;
+      thumb_height = tiff_ifd[i].t_height;
+      thumb_offset = tiff_ifd[i].offset;
+      thumb_length = tiff_ifd[i].bytes;
+      thumb_misc = tiff_ifd[i].bps;
+      thm = i;
+    }
+  if (thm >= 0)
+  {
+    thumb_misc |= tiff_ifd[thm].samples << 5;
+    switch (tiff_ifd[thm].comp)
+    {
+    case 0:
+      write_thumb = &LibRaw::layer_thumb;
+      break;
+    case 1:
+      if (tiff_ifd[thm].bps <= 8)
+        write_thumb = &LibRaw::ppm_thumb;
+      else if (!strncmp(make, "Imacon", 6))
+        write_thumb = &LibRaw::ppm16_thumb;
+      else
+        thumb_load_raw = &LibRaw::kodak_thumb_load_raw;
+      break;
+    case 65000:
+      thumb_load_raw = tiff_ifd[thm].phint == 6 ? &LibRaw::kodak_ycbcr_load_raw
+                                                : &LibRaw::kodak_rgb_load_raw;
+    }
+  }
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/postprocessing/aspect_ratio.cpp libkdcraw/libkdcraw/libraw/src/postprocessing/aspect_ratio.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/postprocessing/aspect_ratio.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/postprocessing/aspect_ratio.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,110 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+void LibRaw::fuji_rotate()
+{
+  int i, row, col;
+  double step;
+  float r, c, fr, fc;
+  unsigned ur, uc;
+  ushort wide, high, (*img)[4], (*pix)[4];
+
+  if (!fuji_width)
+    return;
+  fuji_width = (fuji_width - 1 + shrink) >> shrink;
+  step = sqrt(0.5);
+  wide = fuji_width / step;
+  high = (height - fuji_width) / step;
+  img = (ushort(*)[4])calloc(high, wide * sizeof *img);
+  merror(img, "fuji_rotate()");
+
+  RUN_CALLBACK(LIBRAW_PROGRESS_FUJI_ROTATE, 0, 2);
+
+  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 > (unsigned)height - 2 || uc > (unsigned)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;
+  RUN_CALLBACK(LIBRAW_PROGRESS_FUJI_ROTATE, 1, 2);
+}
+
+void LibRaw::stretch()
+{
+  ushort newdim, (*img)[4], *pix0, *pix1;
+  int row, col, c;
+  double rc, frac;
+
+  if (pixel_aspect == 1)
+    return;
+  RUN_CALLBACK(LIBRAW_PROGRESS_STRETCH, 0, 2);
+  if (pixel_aspect < 1)
+  {
+    newdim = height / pixel_aspect + 0.5;
+    img = (ushort(*)[4])calloc(width, newdim * sizeof *img);
+    merror(img, "stretch()");
+    for (rc = row = 0; row < newdim; row++, rc += pixel_aspect)
+    {
+      frac = rc - (c = rc);
+      pix0 = pix1 = image[c * width];
+      if (c + 1 < height)
+        pix1 += width * 4;
+      for (col = 0; col < width; col++, pix0 += 4, pix1 += 4)
+        FORCC img[row * width + col][c] =
+            pix0[c] * (1 - frac) + pix1[c] * frac + 0.5;
+    }
+    height = newdim;
+  }
+  else
+  {
+    newdim = width * pixel_aspect + 0.5;
+    img = (ushort(*)[4])calloc(height, newdim * sizeof *img);
+    merror(img, "stretch()");
+    for (rc = col = 0; col < newdim; col++, rc += 1 / pixel_aspect)
+    {
+      frac = rc - (c = rc);
+      pix0 = pix1 = image[c];
+      if (c + 1 < width)
+        pix1 += 4;
+      for (row = 0; row < height; row++, pix0 += width * 4, pix1 += width * 4)
+        FORCC img[row * newdim + col][c] =
+            pix0[c] * (1 - frac) + pix1[c] * frac + 0.5;
+    }
+    width = newdim;
+  }
+  free(image);
+  image = img;
+  RUN_CALLBACK(LIBRAW_PROGRESS_STRETCH, 1, 2);
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/postprocessing/dcraw_process.cpp libkdcraw/libkdcraw/libraw/src/postprocessing/dcraw_process.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/postprocessing/dcraw_process.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/postprocessing/dcraw_process.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,254 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/libraw_cxx_defs.h"
+
+int LibRaw::dcraw_process(void)
+{
+  int quality, i;
+
+  int iterations = -1, dcb_enhance = 1, noiserd = 0;
+  float preser = 0;
+  float expos = 1.0;
+
+  CHECK_ORDER_LOW(LIBRAW_PROGRESS_LOAD_RAW);
+  //    CHECK_ORDER_HIGH(LIBRAW_PROGRESS_PRE_INTERPOLATE);
+
+  try
+  {
+
+    int no_crop = 1;
+
+    if (~O.cropbox[2] && ~O.cropbox[3])
+      no_crop = 0;
+
+    libraw_decoder_info_t di;
+    get_decoder_info(&di);
+
+    bool is_bayer = (imgdata.idata.filters || P1.colors == 1);
+    int subtract_inline =
+        !O.bad_pixels && !O.dark_frame && is_bayer && !IO.zero_is_bad;
+
+    raw2image_ex(subtract_inline); // allocate imgdata.image and copy data!
+
+    // Adjust sizes
+
+    int save_4color = O.four_color_rgb;
+
+    if (IO.zero_is_bad)
+    {
+      remove_zeroes();
+      SET_PROC_FLAG(LIBRAW_PROGRESS_REMOVE_ZEROES);
+    }
+
+    if (O.bad_pixels && no_crop)
+    {
+      bad_pixels(O.bad_pixels);
+      SET_PROC_FLAG(LIBRAW_PROGRESS_BAD_PIXELS);
+    }
+
+    if (O.dark_frame && no_crop)
+    {
+      subtract(O.dark_frame);
+      SET_PROC_FLAG(LIBRAW_PROGRESS_DARK_FRAME);
+    }
+    /* pre subtract black callback: check for it above to disable subtract
+     * inline */
+
+    if (callbacks.pre_subtractblack_cb)
+      (callbacks.pre_subtractblack_cb)(this);
+
+    quality = 2 + !IO.fuji_width;
+
+    if (O.user_qual >= 0)
+      quality = O.user_qual;
+
+    if (!subtract_inline || !C.data_maximum)
+    {
+      adjust_bl();
+      subtract_black_internal();
+    }
+
+    if (!(di.decoder_flags & LIBRAW_DECODER_FIXEDMAXC))
+      adjust_maximum();
+
+    if (O.user_sat > 0)
+      C.maximum = O.user_sat;
+
+    if (P1.is_foveon)
+    {
+      if (load_raw == &LibRaw::x3f_load_raw)
+      {
+        // Filter out zeroes
+        for (int i = 0; i < S.height * S.width; i++)
+        {
+          for (int c = 0; c < 4; c++)
+            if ((short)imgdata.image[i][c] < 0)
+              imgdata.image[i][c] = 0;
+        }
+      }
+      SET_PROC_FLAG(LIBRAW_PROGRESS_FOVEON_INTERPOLATE);
+    }
+
+    if (O.green_matching && !O.half_size)
+    {
+      green_matching();
+    }
+
+    if (callbacks.pre_scalecolors_cb)
+      (callbacks.pre_scalecolors_cb)(this);
+
+    if (!O.no_auto_scale)
+    {
+      scale_colors();
+      SET_PROC_FLAG(LIBRAW_PROGRESS_SCALE_COLORS);
+    }
+
+    if (callbacks.pre_preinterpolate_cb)
+      (callbacks.pre_preinterpolate_cb)(this);
+
+    pre_interpolate();
+
+    SET_PROC_FLAG(LIBRAW_PROGRESS_PRE_INTERPOLATE);
+
+    if (O.dcb_iterations >= 0)
+      iterations = O.dcb_iterations;
+    if (O.dcb_enhance_fl >= 0)
+      dcb_enhance = O.dcb_enhance_fl;
+    if (O.fbdd_noiserd >= 0)
+      noiserd = O.fbdd_noiserd;
+
+    /* pre-exposure correction callback */
+
+    if (O.exp_correc > 0)
+    {
+      expos = O.exp_shift;
+      preser = O.exp_preser;
+      exp_bef(expos, preser);
+    }
+
+    if (callbacks.pre_interpolate_cb)
+      (callbacks.pre_interpolate_cb)(this);
+
+    /* post-exposure correction fallback */
+    if (P1.filters && !O.no_interpolation)
+    {
+      if (noiserd > 0 && P1.colors == 3 && P1.filters)
+        fbdd(noiserd);
+
+      if (P1.filters > 1000 && callbacks.interpolate_bayer_cb)
+        (callbacks.interpolate_bayer_cb)(this);
+      else if (P1.filters == 9 && callbacks.interpolate_xtrans_cb)
+        (callbacks.interpolate_xtrans_cb)(this);
+      else if (quality == 0)
+        lin_interpolate();
+      else if (quality == 1 || P1.colors > 3)
+        vng_interpolate();
+      else if (quality == 2 && P1.filters > 1000)
+        ppg_interpolate();
+      else if (P1.filters == LIBRAW_XTRANS)
+      {
+        // Fuji X-Trans
+        xtrans_interpolate(quality > 2 ? 3 : 1);
+      }
+      else if (quality == 3)
+        ahd_interpolate(); // really don't need it here due to fallback op
+      else if (quality == 4)
+        dcb(iterations, dcb_enhance);
+
+      else if (quality == 11)
+        dht_interpolate();
+      else if (quality == 12)
+        aahd_interpolate();
+      // fallback to AHD
+      else
+      {
+        ahd_interpolate();
+        imgdata.process_warnings |= LIBRAW_WARN_FALLBACK_TO_AHD;
+      }
+
+      SET_PROC_FLAG(LIBRAW_PROGRESS_INTERPOLATE);
+    }
+    if (IO.mix_green)
+    {
+      for (P1.colors = 3, i = 0; i < S.height * S.width; i++)
+        imgdata.image[i][1] = (imgdata.image[i][1] + imgdata.image[i][3]) >> 1;
+      SET_PROC_FLAG(LIBRAW_PROGRESS_MIX_GREEN);
+    }
+
+    if (callbacks.post_interpolate_cb)
+      (callbacks.post_interpolate_cb)(this);
+    else if (!P1.is_foveon && P1.colors == 3 && O.med_passes > 0)
+    {
+      median_filter();
+      SET_PROC_FLAG(LIBRAW_PROGRESS_MEDIAN_FILTER);
+    }
+
+    if (O.highlight == 2)
+    {
+      blend_highlights();
+      SET_PROC_FLAG(LIBRAW_PROGRESS_HIGHLIGHTS);
+    }
+
+    if (O.highlight > 2)
+    {
+      recover_highlights();
+      SET_PROC_FLAG(LIBRAW_PROGRESS_HIGHLIGHTS);
+    }
+
+    if (O.use_fuji_rotate)
+    {
+      fuji_rotate();
+      SET_PROC_FLAG(LIBRAW_PROGRESS_FUJI_ROTATE);
+    }
+
+    if (!libraw_internal_data.output_data.histogram)
+    {
+      libraw_internal_data.output_data.histogram =
+          (int(*)[LIBRAW_HISTOGRAM_SIZE])malloc(
+              sizeof(*libraw_internal_data.output_data.histogram) * 4);
+      merror(libraw_internal_data.output_data.histogram,
+             "LibRaw::dcraw_process()");
+    }
+#ifndef NO_LCMS
+    if (O.camera_profile)
+    {
+      apply_profile(O.camera_profile, O.output_profile);
+      SET_PROC_FLAG(LIBRAW_PROGRESS_APPLY_PROFILE);
+    }
+#endif
+
+    if (callbacks.pre_converttorgb_cb)
+      (callbacks.pre_converttorgb_cb)(this);
+
+    convert_to_rgb();
+    SET_PROC_FLAG(LIBRAW_PROGRESS_CONVERT_RGB);
+
+    if (callbacks.post_converttorgb_cb)
+      (callbacks.post_converttorgb_cb)(this);
+
+    if (O.use_fuji_rotate)
+    {
+      stretch();
+      SET_PROC_FLAG(LIBRAW_PROGRESS_STRETCH);
+    }
+    O.four_color_rgb = save_4color; // also, restore
+
+    return 0;
+  }
+  catch (LibRaw_exceptions err)
+  {
+    EXCEPTION_HANDLER(err);
+  }
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/postprocessing/mem_image.cpp libkdcraw/libkdcraw/libraw/src/postprocessing/mem_image.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/postprocessing/mem_image.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/postprocessing/mem_image.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,289 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/libraw_cxx_defs.h"
+
+libraw_processed_image_t *LibRaw::dcraw_make_mem_thumb(int *errcode)
+{
+  if (!T.thumb)
+  {
+    if (!ID.toffset && !(imgdata.thumbnail.tlength > 0 &&
+                         load_raw == &LibRaw::broadcom_load_raw) // RPi
+    )
+    {
+      if (errcode)
+        *errcode = LIBRAW_NO_THUMBNAIL;
+    }
+    else
+    {
+      if (errcode)
+        *errcode = LIBRAW_OUT_OF_ORDER_CALL;
+    }
+    return NULL;
+  }
+
+  if (T.tlength < 64u)
+  {
+      if (errcode)
+          *errcode = EINVAL;
+      return NULL;
+  }
+
+  if (INT64(T.tlength) > 1024ULL * 1024ULL * LIBRAW_MAX_THUMBNAIL_MB)
+  {
+      if (errcode)
+          *errcode = LIBRAW_TOO_BIG;
+      return NULL;
+  }
+
+  if (T.tformat == LIBRAW_THUMBNAIL_BITMAP)
+  {
+    libraw_processed_image_t *ret = (libraw_processed_image_t *)::malloc(
+        sizeof(libraw_processed_image_t) + T.tlength);
+
+    if (!ret)
+    {
+      if (errcode)
+        *errcode = ENOMEM;
+      return NULL;
+    }
+
+    memset(ret, 0, sizeof(libraw_processed_image_t));
+    ret->type = LIBRAW_IMAGE_BITMAP;
+    ret->height = T.theight;
+    ret->width = T.twidth;
+    ret->colors = 3;
+    ret->bits = 8;
+    ret->data_size = T.tlength;
+    memmove(ret->data, T.thumb, T.tlength);
+    if (errcode)
+      *errcode = 0;
+    return ret;
+  }
+  else if (T.tformat == LIBRAW_THUMBNAIL_JPEG)
+  {
+    ushort exif[5];
+    int mk_exif = 0;
+    if (strcmp(T.thumb + 6, "Exif"))
+      mk_exif = 1;
+
+    int dsize = T.tlength + mk_exif * (sizeof(exif) + sizeof(tiff_hdr));
+
+    libraw_processed_image_t *ret = (libraw_processed_image_t *)::malloc(
+        sizeof(libraw_processed_image_t) + dsize);
+
+    if (!ret)
+    {
+      if (errcode)
+        *errcode = ENOMEM;
+      return NULL;
+    }
+
+    memset(ret, 0, sizeof(libraw_processed_image_t));
+
+    ret->type = LIBRAW_IMAGE_JPEG;
+    ret->data_size = dsize;
+
+    ret->data[0] = 0xff;
+    ret->data[1] = 0xd8;
+    if (mk_exif)
+    {
+      struct tiff_hdr th;
+      memcpy(exif, "\xff\xe1  Exif\0\0", 10);
+      exif[1] = htons(8 + sizeof th);
+      memmove(ret->data + 2, exif, sizeof(exif));
+      tiff_head(&th, 0);
+      memmove(ret->data + (2 + sizeof(exif)), &th, sizeof(th));
+      memmove(ret->data + (2 + sizeof(exif) + sizeof(th)), T.thumb + 2,
+              T.tlength - 2);
+    }
+    else
+    {
+      memmove(ret->data + 2, T.thumb + 2, T.tlength - 2);
+    }
+    if (errcode)
+      *errcode = 0;
+    return ret;
+  }
+  else
+  {
+    if (errcode)
+      *errcode = LIBRAW_UNSUPPORTED_THUMBNAIL;
+    return NULL;
+  }
+}
+
+// jlb
+// macros for copying pixels to either BGR or RGB formats
+#define FORBGR for (c = P1.colors - 1; c >= 0; c--)
+#define FORRGB for (c = 0; c < P1.colors; c++)
+
+void LibRaw::get_mem_image_format(int *width, int *height, int *colors,
+                                  int *bps) const
+
+{
+  *width = S.width;
+  *height = S.height;
+  if (imgdata.progress_flags < LIBRAW_PROGRESS_FUJI_ROTATE)
+  {
+    if (O.use_fuji_rotate)
+    {
+      if (IO.fuji_width)
+      {
+        int fuji_width = (IO.fuji_width - 1 + IO.shrink) >> IO.shrink;
+        *width = (ushort)(fuji_width / sqrt(0.5));
+        *height = (ushort)((*height - fuji_width) / sqrt(0.5));
+      }
+      else
+      {
+        if (S.pixel_aspect < 0.995)
+          *height = (ushort)(*height / S.pixel_aspect + 0.5);
+        if (S.pixel_aspect > 1.005)
+          *width = (ushort)(*width * S.pixel_aspect + 0.5);
+      }
+    }
+  }
+  if (S.flip & 4)
+  {
+    std::swap(*width, *height);
+  }
+  *colors = P1.colors;
+  *bps = O.output_bps;
+}
+
+int LibRaw::copy_mem_image(void *scan0, int stride, int bgr)
+
+{
+  // the image memory pointed to by scan0 is assumed to be in the format
+  // returned by get_mem_image_format
+  if ((imgdata.progress_flags & LIBRAW_PROGRESS_THUMB_MASK) <
+      LIBRAW_PROGRESS_PRE_INTERPOLATE)
+    return LIBRAW_OUT_OF_ORDER_CALL;
+
+  if (libraw_internal_data.output_data.histogram)
+  {
+    int perc, val, total, t_white = 0x2000, c;
+    perc = S.width * S.height * O.auto_bright_thr;
+    if (IO.fuji_width)
+      perc /= 2;
+    if (!((O.highlight & ~2) || O.no_auto_bright))
+      for (t_white = c = 0; c < P1.colors; c++)
+      {
+        for (val = 0x2000, total = 0; --val > 32;)
+          if ((total += libraw_internal_data.output_data.histogram[c][val]) >
+              perc)
+            break;
+        if (t_white < val)
+          t_white = val;
+      }
+    gamma_curve(O.gamm[0], O.gamm[1], 2, (t_white << 3) / O.bright);
+  }
+
+  int s_iheight = S.iheight;
+  int s_iwidth = S.iwidth;
+  int s_width = S.width;
+  int s_hwight = S.height;
+
+  S.iheight = S.height;
+  S.iwidth = S.width;
+
+  if (S.flip & 4)
+    SWAP(S.height, S.width);
+  uchar *ppm;
+  ushort *ppm2;
+  int c, row, col, soff, rstep, cstep;
+
+  soff = flip_index(0, 0);
+  cstep = flip_index(0, 1) - soff;
+  rstep = flip_index(1, 0) - flip_index(0, S.width);
+
+  for (row = 0; row < S.height; row++, soff += rstep)
+  {
+    uchar *bufp = ((uchar *)scan0) + row * stride;
+    ppm2 = (ushort *)(ppm = bufp);
+    // keep trivial decisions in the outer loop for speed
+    if (bgr)
+    {
+      if (O.output_bps == 8)
+      {
+        for (col = 0; col < S.width; col++, soff += cstep)
+          FORBGR *ppm++ = imgdata.color.curve[imgdata.image[soff][c]] >> 8;
+      }
+      else
+      {
+        for (col = 0; col < S.width; col++, soff += cstep)
+          FORBGR *ppm2++ = imgdata.color.curve[imgdata.image[soff][c]];
+      }
+    }
+    else
+    {
+      if (O.output_bps == 8)
+      {
+        for (col = 0; col < S.width; col++, soff += cstep)
+          FORRGB *ppm++ = imgdata.color.curve[imgdata.image[soff][c]] >> 8;
+      }
+      else
+      {
+        for (col = 0; col < S.width; col++, soff += cstep)
+          FORRGB *ppm2++ = imgdata.color.curve[imgdata.image[soff][c]];
+      }
+    }
+
+    //            bufp += stride;           // go to the next line
+  }
+
+  S.iheight = s_iheight;
+  S.iwidth = s_iwidth;
+  S.width = s_width;
+  S.height = s_hwight;
+
+  return 0;
+}
+#undef FORBGR
+#undef FORRGB
+
+libraw_processed_image_t *LibRaw::dcraw_make_mem_image(int *errcode)
+
+{
+  int width, height, colors, bps;
+  get_mem_image_format(&width, &height, &colors, &bps);
+  int stride = width * (bps / 8) * colors;
+  unsigned ds = height * stride;
+  libraw_processed_image_t *ret = (libraw_processed_image_t *)::malloc(
+      sizeof(libraw_processed_image_t) + ds);
+  if (!ret)
+  {
+    if (errcode)
+      *errcode = ENOMEM;
+    return NULL;
+  }
+  memset(ret, 0, sizeof(libraw_processed_image_t));
+
+  // metadata init
+  ret->type = LIBRAW_IMAGE_BITMAP;
+  ret->height = height;
+  ret->width = width;
+  ret->colors = colors;
+  ret->bits = bps;
+  ret->data_size = ds;
+  copy_mem_image(ret->data, stride, 0);
+
+  return ret;
+}
+
+void LibRaw::dcraw_clear_mem(libraw_processed_image_t *p)
+{
+  if (p)
+    ::free(p);
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/postprocessing/postprocessing_aux.cpp libkdcraw/libkdcraw/libraw/src/postprocessing/postprocessing_aux.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/postprocessing/postprocessing_aux.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/postprocessing/postprocessing_aux.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,410 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+void LibRaw::hat_transform(float *temp, float *base, int st, int size, int sc)
+{
+  int i;
+  for (i = 0; i < sc; i++)
+    temp[i] = 2 * base[st * i] + base[st * (sc - i)] + base[st * (i + sc)];
+  for (; i + sc < size; i++)
+    temp[i] = 2 * base[st * i] + base[st * (i - sc)] + base[st * (i + sc)];
+  for (; i < size; i++)
+    temp[i] = 2 * base[st * i] + base[st * (i - sc)] +
+              base[st * (2 * size - 2 - (i + sc))];
+}
+
+#if !defined(LIBRAW_USE_OPENMP)
+void LibRaw::wavelet_denoise()
+{
+  float *fimg = 0, *temp, thold, mul[2], avg, diff;
+  int scale = 1, size, lev, hpass, lpass, row, col, nc, c, i, wlast, blk[2];
+  ushort *window[4];
+  static const float noise[] = {0.8002, 0.2735, 0.1202, 0.0585,
+                                0.0291, 0.0152, 0.0080, 0.0044};
+
+  while (maximum << scale < 0x10000)
+    scale++;
+  maximum <<= --scale;
+  black <<= scale;
+  FORC4 cblack[c] <<= scale;
+  if ((size = iheight * iwidth) < 0x15550000)
+    fimg = (float *)malloc((size * 3 + iheight + iwidth) * sizeof *fimg);
+  merror(fimg, "wavelet_denoise()");
+  temp = fimg + size * 3;
+  if ((nc = colors) == 3 && filters)
+    nc++;
+  FORC(nc)
+  { /* denoise R,G1,B,G3 individually */
+    for (i = 0; i < size; i++)
+      fimg[i] = 256 * sqrt((double)(image[i][c] << scale));
+    for (hpass = lev = 0; lev < 5; lev++)
+    {
+      lpass = size * ((lev & 1) + 1);
+      for (row = 0; row < iheight; row++)
+      {
+        hat_transform(temp, fimg + hpass + row * iwidth, 1, iwidth, 1 << lev);
+        for (col = 0; col < iwidth; col++)
+          fimg[lpass + row * iwidth + col] = temp[col] * 0.25;
+      }
+      for (col = 0; col < iwidth; col++)
+      {
+        hat_transform(temp, fimg + lpass + col, iwidth, iheight, 1 << lev);
+        for (row = 0; row < iheight; row++)
+          fimg[lpass + row * iwidth + col] = temp[row] * 0.25;
+      }
+      thold = threshold * noise[lev];
+      for (i = 0; i < size; i++)
+      {
+        fimg[hpass + i] -= fimg[lpass + i];
+        if (fimg[hpass + i] < -thold)
+          fimg[hpass + i] += thold;
+        else if (fimg[hpass + i] > thold)
+          fimg[hpass + i] -= thold;
+        else
+          fimg[hpass + i] = 0;
+        if (hpass)
+          fimg[i] += fimg[hpass + i];
+      }
+      hpass = lpass;
+    }
+    for (i = 0; i < size; i++)
+      image[i][c] = CLIP(SQR(fimg[i] + fimg[lpass + i]) / 0x10000);
+  }
+  if (filters && colors == 3)
+  { /* pull G1 and G3 closer together */
+    for (row = 0; row < 2; row++)
+    {
+      mul[row] = 0.125 * pre_mul[FC(row + 1, 0) | 1] / pre_mul[FC(row, 0) | 1];
+      blk[row] = cblack[FC(row, 0) | 1];
+    }
+    for (i = 0; i < 4; i++)
+      window[i] = (ushort *)fimg + width * i;
+    for (wlast = -1, row = 1; row < height - 1; row++)
+    {
+      while (wlast < row + 1)
+      {
+        for (wlast++, i = 0; i < 4; i++)
+          window[(i + 3) & 3] = window[i];
+        for (col = FC(wlast, 1) & 1; col < width; col += 2)
+          window[2][col] = BAYER(wlast, col);
+      }
+      thold = threshold / 512;
+      for (col = (FC(row, 0) & 1) + 1; col < width - 1; col += 2)
+      {
+        avg = (window[0][col - 1] + window[0][col + 1] + window[2][col - 1] +
+               window[2][col + 1] - blk[~row & 1] * 4) *
+                  mul[row & 1] +
+              (window[1][col] + blk[row & 1]) * 0.5;
+        avg = avg < 0 ? 0 : sqrt(avg);
+        diff = sqrt((double)BAYER(row, col)) - avg;
+        if (diff < -thold)
+          diff += thold;
+        else if (diff > thold)
+          diff -= thold;
+        else
+          diff = 0;
+        BAYER(row, col) = CLIP(SQR(avg + diff) + 0.5);
+      }
+    }
+  }
+  free(fimg);
+}
+#else /* LIBRAW_USE_OPENMP */
+void LibRaw::wavelet_denoise()
+{
+  float *fimg = 0, *temp, thold, mul[2], avg, diff;
+  int scale = 1, size, lev, hpass, lpass, row, col, nc, c, i, wlast, blk[2];
+  ushort *window[4];
+  static const float noise[] = {0.8002, 0.2735, 0.1202, 0.0585,
+                                0.0291, 0.0152, 0.0080, 0.0044};
+
+  while (maximum << scale < 0x10000)
+    scale++;
+  maximum <<= --scale;
+  black <<= scale;
+  FORC4 cblack[c] <<= scale;
+  if ((size = iheight * iwidth) < 0x15550000)
+    fimg = (float *)malloc((size * 3 + iheight + iwidth) * sizeof *fimg);
+  merror(fimg, "wavelet_denoise()");
+  temp = fimg + size * 3;
+  if ((nc = colors) == 3 && filters)
+    nc++;
+#pragma omp parallel default(shared) private(                                  \
+    i, col, row, thold, lev, lpass, hpass, temp, c) firstprivate(scale, size)
+  {
+#pragma omp critical /* LibRaw's malloc is not local thread-safe */
+    temp = (float *)malloc((iheight + iwidth) * sizeof *fimg);
+    FORC(nc)
+    { /* denoise R,G1,B,G3 individually */
+#pragma omp for
+      for (i = 0; i < size; i++)
+        fimg[i] = 256 * sqrt((double)(image[i][c] << scale));
+      for (hpass = lev = 0; lev < 5; lev++)
+      {
+        lpass = size * ((lev & 1) + 1);
+#pragma omp for
+        for (row = 0; row < iheight; row++)
+        {
+          hat_transform(temp, fimg + hpass + row * iwidth, 1, iwidth, 1 << lev);
+          for (col = 0; col < iwidth; col++)
+            fimg[lpass + row * iwidth + col] = temp[col] * 0.25;
+        }
+#pragma omp for
+        for (col = 0; col < iwidth; col++)
+        {
+          hat_transform(temp, fimg + lpass + col, iwidth, iheight, 1 << lev);
+          for (row = 0; row < iheight; row++)
+            fimg[lpass + row * iwidth + col] = temp[row] * 0.25;
+        }
+        thold = threshold * noise[lev];
+#pragma omp for
+        for (i = 0; i < size; i++)
+        {
+          fimg[hpass + i] -= fimg[lpass + i];
+          if (fimg[hpass + i] < -thold)
+            fimg[hpass + i] += thold;
+          else if (fimg[hpass + i] > thold)
+            fimg[hpass + i] -= thold;
+          else
+            fimg[hpass + i] = 0;
+          if (hpass)
+            fimg[i] += fimg[hpass + i];
+        }
+        hpass = lpass;
+      }
+#pragma omp for
+      for (i = 0; i < size; i++)
+        image[i][c] = CLIP(SQR(fimg[i] + fimg[lpass + i]) / 0x10000);
+    }
+#pragma omp critical
+    free(temp);
+  } /* end omp parallel */
+  /* the following loops are hard to parallize, no idea yes,
+   * problem is wlast which is carrying dependency
+   * second part should be easyer, but did not yet get it right.
+   */
+  if (filters && colors == 3)
+  { /* pull G1 and G3 closer together */
+    for (row = 0; row < 2; row++)
+    {
+      mul[row] = 0.125 * pre_mul[FC(row + 1, 0) | 1] / pre_mul[FC(row, 0) | 1];
+      blk[row] = cblack[FC(row, 0) | 1];
+    }
+    for (i = 0; i < 4; i++)
+      window[i] = (ushort *)fimg + width * i;
+    for (wlast = -1, row = 1; row < height - 1; row++)
+    {
+      while (wlast < row + 1)
+      {
+        for (wlast++, i = 0; i < 4; i++)
+          window[(i + 3) & 3] = window[i];
+        for (col = FC(wlast, 1) & 1; col < width; col += 2)
+          window[2][col] = BAYER(wlast, col);
+      }
+      thold = threshold / 512;
+      for (col = (FC(row, 0) & 1) + 1; col < width - 1; col += 2)
+      {
+        avg = (window[0][col - 1] + window[0][col + 1] + window[2][col - 1] +
+               window[2][col + 1] - blk[~row & 1] * 4) *
+                  mul[row & 1] +
+              (window[1][col] + blk[row & 1]) * 0.5;
+        avg = avg < 0 ? 0 : sqrt(avg);
+        diff = sqrt((double)BAYER(row, col)) - avg;
+        if (diff < -thold)
+          diff += thold;
+        else if (diff > thold)
+          diff -= thold;
+        else
+          diff = 0;
+        BAYER(row, col) = CLIP(SQR(avg + diff) + 0.5);
+      }
+    }
+  }
+  free(fimg);
+}
+
+#endif
+void LibRaw::median_filter()
+{
+  ushort(*pix)[4];
+  int pass, c, i, j, k, med[9];
+  static const uchar opt[] = /* Optimal 9-element median search */
+      {1, 2, 4, 5, 7, 8, 0, 1, 3, 4, 6, 7, 1, 2, 4, 5, 7, 8, 0,
+       3, 5, 8, 4, 7, 3, 6, 1, 4, 2, 5, 4, 7, 4, 2, 6, 4, 4, 2};
+
+  for (pass = 1; pass <= med_passes; pass++)
+  {
+    RUN_CALLBACK(LIBRAW_PROGRESS_MEDIAN_FILTER, pass - 1, med_passes);
+    for (c = 0; c < 3; c += 2)
+    {
+      for (pix = image; pix < image + width * height; pix++)
+        pix[0][3] = pix[0][c];
+      for (pix = image + width; pix < image + width * (height - 1); pix++)
+      {
+        if ((pix - image + 1) % width < 2)
+          continue;
+        for (k = 0, i = -width; i <= width; i += width)
+          for (j = i - 1; j <= i + 1; j++)
+            med[k++] = pix[j][3] - pix[j][1];
+        for (i = 0; i < int(sizeof opt); i += 2)
+          if (med[opt[i]] > med[opt[i + 1]])
+            SWAP(med[opt[i]], med[opt[i + 1]]);
+        pix[0][c] = CLIP(med[4] + pix[0][1]);
+      }
+    }
+  }
+}
+
+void LibRaw::blend_highlights()
+{
+  int clip = INT_MAX, row, col, c, i, j;
+  static const float trans[2][4][4] = {
+      {{1, 1, 1}, {1.7320508, -1.7320508, 0}, {-1, -1, 2}},
+      {{1, 1, 1, 1}, {1, -1, 1, -1}, {1, 1, -1, -1}, {1, -1, -1, 1}}};
+  static const float itrans[2][4][4] = {
+      {{1, 0.8660254, -0.5}, {1, -0.8660254, -0.5}, {1, 0, 1}},
+      {{1, 1, 1, 1}, {1, -1, 1, -1}, {1, 1, -1, -1}, {1, -1, -1, 1}}};
+  float cam[2][4], lab[2][4], sum[2], chratio;
+
+  if ((unsigned)(colors - 3) > 1)
+    return;
+  RUN_CALLBACK(LIBRAW_PROGRESS_HIGHLIGHTS, 0, 2);
+  FORCC if (clip > (i = 65535 * pre_mul[c])) clip = i;
+  for (row = 0; row < height; row++)
+    for (col = 0; col < width; col++)
+    {
+      FORCC if (image[row * width + col][c] > clip) break;
+      if (c == colors)
+        continue;
+      FORCC
+      {
+        cam[0][c] = image[row * width + col][c];
+        cam[1][c] = MIN(cam[0][c], clip);
+      }
+      for (i = 0; i < 2; i++)
+      {
+        FORCC for (lab[i][c] = j = 0; j < colors; j++) lab[i][c] +=
+            trans[colors - 3][c][j] * cam[i][j];
+        for (sum[i] = 0, c = 1; c < colors; c++)
+          sum[i] += SQR(lab[i][c]);
+      }
+      chratio = sqrt(sum[1] / sum[0]);
+      for (c = 1; c < colors; c++)
+        lab[0][c] *= chratio;
+      FORCC for (cam[0][c] = j = 0; j < colors; j++) cam[0][c] +=
+          itrans[colors - 3][c][j] * lab[0][j];
+      FORCC image[row * width + col][c] = cam[0][c] / colors;
+    }
+  RUN_CALLBACK(LIBRAW_PROGRESS_HIGHLIGHTS, 1, 2);
+}
+
+#define SCALE (4 >> shrink)
+void LibRaw::recover_highlights()
+{
+  float *map, sum, wgt, grow;
+  int hsat[4], count, spread, change, val, i;
+  unsigned high, wide, mrow, mcol, row, col, kc, c, d, y, x;
+  ushort *pixel;
+  static const signed char dir[8][2] = {{-1, -1}, {-1, 0}, {-1, 1}, {0, 1},
+                                        {1, 1},   {1, 0},  {1, -1}, {0, -1}};
+
+  grow = pow(2.0, 4 - highlight);
+  FORC(unsigned(colors)) hsat[c] = 32000 * pre_mul[c];
+  for (kc = 0, c = 1; c < (unsigned)colors; c++)
+    if (pre_mul[kc] < pre_mul[c])
+      kc = c;
+  high = height / SCALE;
+  wide = width / SCALE;
+  map = (float *)calloc(high, wide * sizeof *map);
+  merror(map, "recover_highlights()");
+  FORC(unsigned(colors)) if (c != kc)
+  {
+    RUN_CALLBACK(LIBRAW_PROGRESS_HIGHLIGHTS, c - 1, colors - 1);
+    memset(map, 0, high * wide * sizeof *map);
+    for (mrow = 0; mrow < high; mrow++)
+      for (mcol = 0; mcol < wide; mcol++)
+      {
+        sum = wgt = count = 0;
+        for (row = mrow * SCALE; row < (mrow + 1) * SCALE; row++)
+          for (col = mcol * SCALE; col < (mcol + 1) * SCALE; col++)
+          {
+            pixel = image[row * width + col];
+            if (pixel[c] / hsat[c] == 1 && pixel[kc] > 24000)
+            {
+              sum += pixel[c];
+              wgt += pixel[kc];
+              count++;
+            }
+          }
+        if (count == SCALE * SCALE)
+          map[mrow * wide + mcol] = sum / wgt;
+      }
+    for (spread = 32 / grow; spread--;)
+    {
+      for (mrow = 0; mrow < high; mrow++)
+        for (mcol = 0; mcol < wide; mcol++)
+        {
+          if (map[mrow * wide + mcol])
+            continue;
+          sum = count = 0;
+          for (d = 0; d < 8; d++)
+          {
+            y = mrow + dir[d][0];
+            x = mcol + dir[d][1];
+            if (y < high && x < wide && map[y * wide + x] > 0)
+            {
+              sum += (1 + (d & 1)) * map[y * wide + x];
+              count += 1 + (d & 1);
+            }
+          }
+          if (count > 3)
+            map[mrow * wide + mcol] = -(sum + grow) / (count + grow);
+        }
+      for (change = i = 0; i < int(high * wide); i++)
+        if (map[i] < 0)
+        {
+          map[i] = -map[i];
+          change = 1;
+        }
+      if (!change)
+        break;
+    }
+    for (i = 0; i < int(high * wide); i++)
+      if (map[i] == 0)
+        map[i] = 1;
+    for (mrow = 0; mrow < high; mrow++)
+      for (mcol = 0; mcol < wide; mcol++)
+      {
+        for (row = mrow * SCALE; row < (mrow + 1) * SCALE; row++)
+          for (col = mcol * SCALE; col < (mcol + 1) * SCALE; col++)
+          {
+            pixel = image[row * width + col];
+            if (pixel[c] / hsat[c] > 1)
+            {
+              val = pixel[kc] * map[mrow * wide + mcol];
+              if (pixel[c] < val)
+                pixel[c] = CLIP(val);
+            }
+          }
+      }
+  }
+  free(map);
+}
+#undef SCALE
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/postprocessing/postprocessing_ph.cpp libkdcraw/libkdcraw/libraw/src/postprocessing/postprocessing_ph.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/postprocessing/postprocessing_ph.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/postprocessing/postprocessing_ph.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,31 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ Placehoder functions to build LibRaw w/o postprocessing tools
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/libraw_cxx_defs.h"
+
+int LibRaw::dcraw_process(void)
+{
+  return LIBRAW_NOT_IMPLEMENTED;
+}
+
+void LibRaw::fuji_rotate() {}
+void LibRaw::convert_to_rgb_loop(float out_cam[3][4]) {}
+libraw_processed_image_t *LibRaw::dcraw_make_mem_image(int *) {
+  return NULL;
+}
+libraw_processed_image_t *LibRaw::dcraw_make_mem_thumb(int *){ return NULL;}
+void LibRaw::lin_interpolate_loop(int *code, int size) {}
+void LibRaw::scale_colors_loop(float scale_mul[4]) {}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/postprocessing/postprocessing_utils.cpp libkdcraw/libkdcraw/libraw/src/postprocessing/postprocessing_utils.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/postprocessing/postprocessing_utils.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/postprocessing/postprocessing_utils.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,190 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/libraw_cxx_defs.h"
+
+#define TBLN 65535
+
+void LibRaw::exp_bef(float shift, float smooth)
+{
+  // params limits
+  if (shift > 8)
+    shift = 8;
+  if (shift < 0.25)
+    shift = 0.25;
+  if (smooth < 0.0)
+    smooth = 0.0;
+  if (smooth > 1.0)
+    smooth = 1.0;
+
+  unsigned short *lut = (ushort *)malloc((TBLN + 1) * sizeof(unsigned short));
+
+  if (shift <= 1.0)
+  {
+    for (int i = 0; i <= TBLN; i++)
+      lut[i] = (unsigned short)((float)i * shift);
+  }
+  else
+  {
+    float x1, x2, y1, y2;
+
+    float cstops = log(shift) / log(2.0f);
+    float room = cstops * 2;
+    float roomlin = powf(2.0f, room);
+    x2 = (float)TBLN;
+    x1 = (x2 + 1) / roomlin - 1;
+    y1 = x1 * shift;
+    y2 = x2 * (1 + (1 - smooth) * (shift - 1));
+    float sq3x = powf(x1 * x1 * x2, 1.0f / 3.0f);
+    float B = (y2 - y1 + shift * (3 * x1 - 3.0f * sq3x)) /
+              (x2 + 2.0f * x1 - 3.0f * sq3x);
+    float A = (shift - B) * 3.0f * powf(x1 * x1, 1.0f / 3.0f);
+    float CC = y2 - A * powf(x2, 1.0f / 3.0f) - B * x2;
+    for (int i = 0; i <= TBLN; i++)
+    {
+      float X = (float)i;
+      float Y = A * powf(X, 1.0f / 3.0f) + B * X + CC;
+      if (i < x1)
+        lut[i] = (unsigned short)((float)i * shift);
+      else
+        lut[i] = Y < 0 ? 0 : (Y > TBLN ? TBLN : (unsigned short)(Y));
+    }
+  }
+  for (int i = 0; i < S.height * S.width; i++)
+  {
+    imgdata.image[i][0] = lut[imgdata.image[i][0]];
+    imgdata.image[i][1] = lut[imgdata.image[i][1]];
+    imgdata.image[i][2] = lut[imgdata.image[i][2]];
+    imgdata.image[i][3] = lut[imgdata.image[i][3]];
+  }
+
+  if (C.data_maximum <= TBLN)
+    C.data_maximum = lut[C.data_maximum];
+  if (C.maximum <= TBLN)
+    C.maximum = lut[C.maximum];
+  free(lut);
+}
+
+void LibRaw::convert_to_rgb_loop(float out_cam[3][4])
+{
+  int row, col, c;
+  float out[3];
+  ushort *img;
+  memset(libraw_internal_data.output_data.histogram, 0,
+         sizeof(int) * LIBRAW_HISTOGRAM_SIZE * 4);
+  if (libraw_internal_data.internal_output_params.raw_color)
+  {
+    for (img = imgdata.image[0], row = 0; row < S.height; row++)
+    {
+      for (col = 0; col < S.width; col++, img += 4)
+      {
+        for (c = 0; c < imgdata.idata.colors; c++)
+        {
+          libraw_internal_data.output_data.histogram[c][img[c] >> 3]++;
+        }
+      }
+    }
+  }
+  else if (imgdata.idata.colors == 3)
+  {
+    for (img = imgdata.image[0], row = 0; row < S.height; row++)
+    {
+      for (col = 0; col < S.width; col++, img += 4)
+      {
+        out[0] = out_cam[0][0] * img[0] + out_cam[0][1] * img[1] +
+                 out_cam[0][2] * img[2];
+        out[1] = out_cam[1][0] * img[0] + out_cam[1][1] * img[1] +
+                 out_cam[1][2] * img[2];
+        out[2] = out_cam[2][0] * img[0] + out_cam[2][1] * img[1] +
+                 out_cam[2][2] * img[2];
+        img[0] = CLIP((int)out[0]);
+        img[1] = CLIP((int)out[1]);
+        img[2] = CLIP((int)out[2]);
+        libraw_internal_data.output_data.histogram[0][img[0] >> 3]++;
+        libraw_internal_data.output_data.histogram[1][img[1] >> 3]++;
+        libraw_internal_data.output_data.histogram[2][img[2] >> 3]++;
+      }
+    }
+  }
+  else if (imgdata.idata.colors == 4)
+  {
+    for (img = imgdata.image[0], row = 0; row < S.height; row++)
+    {
+      for (col = 0; col < S.width; col++, img += 4)
+      {
+        out[0] = out_cam[0][0] * img[0] + out_cam[0][1] * img[1] +
+                 out_cam[0][2] * img[2] + out_cam[0][3] * img[3];
+        out[1] = out_cam[1][0] * img[0] + out_cam[1][1] * img[1] +
+                 out_cam[1][2] * img[2] + out_cam[1][3] * img[3];
+        out[2] = out_cam[2][0] * img[0] + out_cam[2][1] * img[1] +
+                 out_cam[2][2] * img[2] + out_cam[2][3] * img[3];
+        img[0] = CLIP((int)out[0]);
+        img[1] = CLIP((int)out[1]);
+        img[2] = CLIP((int)out[2]);
+        libraw_internal_data.output_data.histogram[0][img[0] >> 3]++;
+        libraw_internal_data.output_data.histogram[1][img[1] >> 3]++;
+        libraw_internal_data.output_data.histogram[2][img[2] >> 3]++;
+        libraw_internal_data.output_data.histogram[3][img[3] >> 3]++;
+      }
+    }
+  }
+}
+
+void LibRaw::scale_colors_loop(float scale_mul[4])
+{
+  unsigned size = S.iheight * S.iwidth;
+
+  if (C.cblack[4] && C.cblack[5])
+  {
+    int val;
+    for (unsigned i = 0; i < size; i++)
+    {
+      for (unsigned c = 0; c < 4; c++)
+      {
+        if (!(val = imgdata.image[i][c])) continue;
+        val -= C.cblack[6 + i / S.iwidth % C.cblack[4] * C.cblack[5] +
+                        i % S.iwidth % C.cblack[5]];
+        val -= C.cblack[c];
+        val *= scale_mul[c];
+        imgdata.image[i][c] = CLIP(val);
+      }
+    }
+  }
+  else if (C.cblack[0] || C.cblack[1] || C.cblack[2] || C.cblack[3])
+  {
+    for (unsigned i = 0; i < size; i++)
+    {
+      for (unsigned c = 0; c < 4; c++)
+      {
+        int val = imgdata.image[i][c];
+        if (!val) continue;
+        val -= C.cblack[c];
+        val *= scale_mul[c];
+        imgdata.image[i][c] = CLIP(val);
+      }
+    }
+  }
+  else // BL is zero
+  {
+    for (unsigned i = 0; i < size; i++)
+    {
+      for (unsigned c = 0; c < 4; c++)
+      {
+        int val = imgdata.image[i][c];
+        val *= scale_mul[c];
+        imgdata.image[i][c] = CLIP(val);
+      }
+    }
+  }
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/postprocessing/postprocessing_utils_dcrdefs.cpp libkdcraw/libkdcraw/libraw/src/postprocessing/postprocessing_utils_dcrdefs.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/postprocessing/postprocessing_utils_dcrdefs.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/postprocessing/postprocessing_utils_dcrdefs.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,305 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+void LibRaw::convert_to_rgb()
+{
+  int i, j, k;
+  size_t prof_desc_len;
+  char *prof_desc;
+  float out_cam[3][4];
+  double num, inverse[3][3];
+  static const double(*out_rgb[])[3] = {
+      LibRaw_constants::rgb_rgb,  LibRaw_constants::adobe_rgb,
+      LibRaw_constants::wide_rgb, LibRaw_constants::prophoto_rgb,
+      LibRaw_constants::xyz_rgb,  LibRaw_constants::aces_rgb};
+  static const char *name[] = {"sRGB",          "Adobe RGB (1998)",
+                               "WideGamut D65", "ProPhoto D65",
+                               "XYZ",           "ACES"};
+  static const unsigned phead[] = {
+      1024, 0, 0x2100000,  0x6d6e7472, 0x52474220, 0x58595a20, 0,
+      0,    0, 0x61637370, 0,          0,          0x6e6f6e65, 0,
+      0,    0, 0,          0xf6d6,     0x10000,    0xd32d};
+  unsigned pbody[] = {10,         0x63707274, 0,  36, /* cprt */
+                      0x64657363, 0,          60,     /* desc, len is strlen(longest_string) + 12 */
+                      0x77747074, 0,          20,     /* wtpt */
+                      0x626b7074, 0,          20,     /* bkpt */
+                      0x72545243, 0,          14,     /* rTRC */
+                      0x67545243, 0,          14,     /* gTRC */
+                      0x62545243, 0,          14,     /* bTRC */
+                      0x7258595a, 0,          20,     /* rXYZ */
+                      0x6758595a, 0,          20,     /* gXYZ */
+                      0x6258595a, 0,          20};    /* bXYZ */
+  static const unsigned pwhite[] = {0xf351, 0x10000, 0x116cc};
+  unsigned pcurve[] = {0x63757276, 0, 1, 0x1000000};
+
+  RUN_CALLBACK(LIBRAW_PROGRESS_CONVERT_RGB, 0, 2);
+
+  prof_desc_len = snprintf(NULL, 0, "%s gamma %g toe slope %g", name[output_color - 1], floorf(1000.f/gamm[0]+.5f)/1000.f, floorf(gamm[1]*1000.0f+.5f)/1000.f) + 1;
+  prof_desc = (char *)malloc(prof_desc_len);
+  sprintf(prof_desc, "%s gamma %g toe slope %g", name[output_color - 1], floorf(1000.f/gamm[0]+.5f)/1000.f, floorf(gamm[1]*1000.0f+.5f)/1000.f);
+
+  gamma_curve(gamm[0], gamm[1], 0, 0);
+  memcpy(out_cam, rgb_cam, sizeof out_cam);
+  raw_color |= colors == 1 || output_color < 1 || output_color > 6;
+  if (!raw_color)
+  {
+    oprof = (unsigned *)calloc(phead[0], 1);
+    merror(oprof, "convert_to_rgb()");
+    memcpy(oprof, phead, sizeof phead);
+    if (output_color == 5)
+      oprof[4] = oprof[5];
+    oprof[0] = 132 + 12 * pbody[0];
+    for (i = 0; i < (int)pbody[0]; i++)
+    {
+      oprof[oprof[0] / 4] = i ? (i > 1 ? 0x58595a20 : 0x64657363) : 0x74657874;
+      pbody[i * 3 + 2] = oprof[0];
+      oprof[0] += (pbody[i * 3 + 3] + 3) & -4;
+    }
+    memcpy(oprof + 32, pbody, sizeof pbody);
+    oprof[pbody[5] / 4 + 2] = prof_desc_len + 1;
+    memcpy((char *)oprof + pbody[8] + 8, pwhite, sizeof pwhite);
+    pcurve[3] = (short)(256 / gamm[5] + 0.5) << 16;
+    for (i = 4; i < 7; i++)
+      memcpy((char *)oprof + pbody[i * 3 + 2], pcurve, sizeof pcurve);
+    pseudoinverse((double(*)[3])out_rgb[output_color - 1], inverse, 3);
+    for (i = 0; i < 3; i++)
+      for (j = 0; j < 3; j++)
+      {
+        for (num = k = 0; k < 3; k++)
+          num += LibRaw_constants::xyzd50_srgb[i][k] * inverse[j][k];
+        oprof[pbody[j * 3 + 23] / 4 + i + 2] = num * 0x10000 + 0.5;
+      }
+    for (i = 0; i < (int)phead[0] / 4; i++)
+      oprof[i] = htonl(oprof[i]);
+    strcpy((char *)oprof + pbody[2] + 8, "auto-generated by dcraw");
+    strcpy((char *)oprof + pbody[5] + 12, prof_desc);
+    for (i = 0; i < 3; i++)
+      for (j = 0; j < colors; j++)
+        for (out_cam[i][j] = k = 0; k < 3; k++)
+          out_cam[i][j] += out_rgb[output_color - 1][i][k] * rgb_cam[k][j];
+  }
+  convert_to_rgb_loop(out_cam);
+
+  if (colors == 4 && output_color)
+    colors = 3;
+
+  RUN_CALLBACK(LIBRAW_PROGRESS_CONVERT_RGB, 1, 2);
+}
+
+void LibRaw::scale_colors()
+{
+  unsigned bottom, right, size, row, col, ur, uc, i, x, y, c, sum[8];
+  int val;
+  double dsum[8], dmin, dmax;
+  float scale_mul[4], fr, fc;
+  ushort *img = 0, *pix;
+
+  RUN_CALLBACK(LIBRAW_PROGRESS_SCALE_COLORS, 0, 2);
+
+  if (user_mul[0])
+    memcpy(pre_mul, user_mul, sizeof pre_mul);
+  if (use_auto_wb || (use_camera_wb &&
+      (cam_mul[0] < -0.5  // LibRaw 0.19 and older: fallback to auto only if cam_mul[0] is set to -1
+          || (cam_mul[0] <= 0.00001f  // New default: fallback to auto if no cam_mul parsed from metadata
+              && !(imgdata.params.raw_processing_options & LIBRAW_PROCESSING_CAMERAWB_FALLBACK_TO_DAYLIGHT))
+          )))
+  {
+    memset(dsum, 0, sizeof dsum);
+    bottom = MIN(greybox[1] + greybox[3], height);
+    right = MIN(greybox[0] + greybox[2], width);
+    for (row = greybox[1]; row < bottom; row += 8)
+      for (col = greybox[0]; col < right; col += 8)
+      {
+        memset(sum, 0, sizeof sum);
+        for (y = row; y < row + 8 && y < bottom; y++)
+          for (x = col; x < col + 8 && x < right; x++)
+            FORC4
+            {
+              if (filters)
+              {
+                c = fcol(y, x);
+                val = BAYER2(y, x);
+              }
+              else
+                val = image[y * width + x][c];
+              if (val > (int)maximum - 25)
+                goto skip_block;
+              if ((val -= cblack[c]) < 0)
+                val = 0;
+              sum[c] += val;
+              sum[c + 4]++;
+              if (filters)
+                break;
+            }
+        FORC(8) dsum[c] += sum[c];
+      skip_block:;
+      }
+    FORC4 if (dsum[c]) pre_mul[c] = dsum[c + 4] / dsum[c];
+  }
+  if (use_camera_wb && cam_mul[0] > 0.00001f)
+  {
+    memset(sum, 0, sizeof sum);
+    for (row = 0; row < 8; row++)
+      for (col = 0; col < 8; col++)
+      {
+        c = FC(row, col);
+        if ((val = white[row][col] - cblack[c]) > 0)
+          sum[c] += val;
+        sum[c + 4]++;
+      }
+    if (imgdata.color.as_shot_wb_applied)
+    {
+      // Nikon sRAW: camera WB already applied:
+      pre_mul[0] = pre_mul[1] = pre_mul[2] = pre_mul[3] = 1.0;
+    }
+    else if (sum[0] && sum[1] && sum[2] && sum[3])
+      FORC4 pre_mul[c] = (float)sum[c + 4] / sum[c];
+    else if (cam_mul[0] > 0.00001f && cam_mul[2] > 0.00001f)
+      memcpy(pre_mul, cam_mul, sizeof pre_mul);
+    else
+    {
+      imgdata.process_warnings |= LIBRAW_WARN_BAD_CAMERA_WB;
+    }
+  }
+  // Nikon sRAW, daylight
+  if (imgdata.color.as_shot_wb_applied && !use_camera_wb && !use_auto_wb &&
+      cam_mul[0] > 0.00001f && cam_mul[1] > 0.00001f && cam_mul[2] > 0.00001f)
+  {
+    for (c = 0; c < 3; c++)
+      pre_mul[c] /= cam_mul[c];
+  }
+  if (pre_mul[1] == 0)
+    pre_mul[1] = 1;
+  if (pre_mul[3] == 0)
+    pre_mul[3] = colors < 4 ? pre_mul[1] : 1;
+  if (threshold)
+    wavelet_denoise();
+  maximum -= black;
+  for (dmin = DBL_MAX, dmax = c = 0; c < 4; c++)
+  {
+    if (dmin > pre_mul[c])
+      dmin = pre_mul[c];
+    if (dmax < pre_mul[c])
+      dmax = pre_mul[c];
+  }
+  if (!highlight)
+    dmax = dmin;
+  if (dmax > 0.00001 && maximum > 0)
+    FORC4 scale_mul[c] = (pre_mul[c] /= dmax) * 65535.0 / maximum;
+  else
+    FORC4 scale_mul[c] = 1.0;
+
+  if (filters > 1000 && (cblack[4] + 1) / 2 == 1 && (cblack[5] + 1) / 2 == 1)
+  {
+    FORC4 cblack[FC(c / 2, c % 2)] +=
+        cblack[6 + c / 2 % cblack[4] * cblack[5] + c % 2 % cblack[5]];
+    cblack[4] = cblack[5] = 0;
+  }
+  size = iheight * iwidth;
+  scale_colors_loop(scale_mul);
+  if ((aber[0] != 1 || aber[2] != 1) && colors == 3)
+  {
+    for (c = 0; c < 4; c += 2)
+    {
+      if (aber[c] == 1)
+        continue;
+      img = (ushort *)malloc(size * sizeof *img);
+      merror(img, "scale_colors()");
+      for (i = 0; i < size; i++)
+        img[i] = image[i][c];
+      for (row = 0; row < iheight; row++)
+      {
+        ur = fr = (row - iheight * 0.5) * aber[c] + iheight * 0.5;
+        if (ur > (unsigned)iheight - 2)
+          continue;
+        fr -= ur;
+        for (col = 0; col < iwidth; col++)
+        {
+          uc = fc = (col - iwidth * 0.5) * aber[c] + iwidth * 0.5;
+          if (uc > (unsigned)iwidth - 2)
+            continue;
+          fc -= uc;
+          pix = img + ur * iwidth + uc;
+          image[row * iwidth + col][c] =
+              (pix[0] * (1 - fc) + pix[1] * fc) * (1 - fr) +
+              (pix[iwidth] * (1 - fc) + pix[iwidth + 1] * fc) * fr;
+        }
+      }
+      free(img);
+    }
+  }
+  RUN_CALLBACK(LIBRAW_PROGRESS_SCALE_COLORS, 1, 2);
+}
+
+// green equilibration
+void LibRaw::green_matching()
+{
+  int i, j;
+  double m1, m2, c1, c2;
+  int o1_1, o1_2, o1_3, o1_4;
+  int o2_1, o2_2, o2_3, o2_4;
+  ushort(*img)[4];
+  const int margin = 3;
+  int oj = 2, oi = 2;
+  float f;
+  const float thr = 0.01f;
+  if (half_size || shrink)
+    return;
+  if (FC(oj, oi) != 3)
+    oj++;
+  if (FC(oj, oi) != 3)
+    oi++;
+  if (FC(oj, oi) != 3)
+    oj--;
+
+  img = (ushort(*)[4])calloc(height * width, sizeof *image);
+  merror(img, "green_matching()");
+  memcpy(img, image, height * width * sizeof *image);
+
+  for (j = oj; j < height - margin; j += 2)
+    for (i = oi; i < width - margin; i += 2)
+    {
+      o1_1 = img[(j - 1) * width + i - 1][1];
+      o1_2 = img[(j - 1) * width + i + 1][1];
+      o1_3 = img[(j + 1) * width + i - 1][1];
+      o1_4 = img[(j + 1) * width + i + 1][1];
+      o2_1 = img[(j - 2) * width + i][3];
+      o2_2 = img[(j + 2) * width + i][3];
+      o2_3 = img[j * width + i - 2][3];
+      o2_4 = img[j * width + i + 2][3];
+
+      m1 = (o1_1 + o1_2 + o1_3 + o1_4) / 4.0;
+      m2 = (o2_1 + o2_2 + o2_3 + o2_4) / 4.0;
+
+      c1 = (abs(o1_1 - o1_2) + abs(o1_1 - o1_3) + abs(o1_1 - o1_4) +
+            abs(o1_2 - o1_3) + abs(o1_3 - o1_4) + abs(o1_2 - o1_4)) /
+           6.0;
+      c2 = (abs(o2_1 - o2_2) + abs(o2_1 - o2_3) + abs(o2_1 - o2_4) +
+            abs(o2_2 - o2_3) + abs(o2_3 - o2_4) + abs(o2_2 - o2_4)) /
+           6.0;
+      if ((img[j * width + i][3] < maximum * 0.95) && (c1 < maximum * thr) &&
+          (c2 < maximum * thr))
+      {
+        f = image[j * width + i][3] * m1 / m2;
+        image[j * width + i][3] = f > 0xffff ? 0xffff : f;
+      }
+    }
+  free(img);
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/preprocessing/ext_preprocess.cpp libkdcraw/libkdcraw/libraw/src/preprocessing/ext_preprocess.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/preprocessing/ext_preprocess.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/preprocessing/ext_preprocess.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,130 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_fileio_defs.h"
+
+/*
+   Search from the current directory up to the root looking for
+   a ".badpixels" file, and fix those pixels now.
+ */
+void LibRaw::bad_pixels(const char *cfname)
+{
+  FILE *fp = NULL;
+  char *cp, line[128];
+  int time, row, col, r, c, rad, tot, n;
+
+  if (!filters)
+    return;
+  RUN_CALLBACK(LIBRAW_PROGRESS_BAD_PIXELS, 0, 2);
+  if (cfname)
+    fp = fopen(cfname, "r");
+  if (!fp)
+  {
+    imgdata.process_warnings |= LIBRAW_WARN_NO_BADPIXELMAP;
+    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) && fcol(r, c) == fcol(row, col))
+          {
+            tot += BAYER2(r, c);
+            n++;
+          }
+    if (n > 0)
+      BAYER2(row, col) = tot / n;
+  }
+  fclose(fp);
+  RUN_CALLBACK(LIBRAW_PROGRESS_BAD_PIXELS, 1, 2);
+}
+
+void LibRaw::subtract(const char *fname)
+{
+  FILE *fp;
+  int dim[3] = {0, 0, 0}, comment = 0, number = 0, error = 0, nd = 0, c, row,
+      col;
+  ushort *pixel;
+  RUN_CALLBACK(LIBRAW_PROGRESS_DARK_FRAME, 0, 2);
+
+  if (!(fp = fopen(fname, "rb")))
+  {
+    imgdata.process_warnings |= LIBRAW_WARN_BAD_DARKFRAME_FILE;
+    return;
+  }
+  if (fgetc(fp) != 'P' || fgetc(fp) != '5')
+    error = 1;
+  while (!error && nd < 3 && (c = fgetc(fp)) != EOF)
+  {
+    if (c == '#')
+      comment = 1;
+    if (c == '\n')
+      comment = 0;
+    if (comment)
+      continue;
+    if (isdigit(c))
+      number = 1;
+    if (number)
+    {
+      if (isdigit(c))
+        dim[nd] = dim[nd] * 10 + c - '0';
+      else if (isspace(c))
+      {
+        number = 0;
+        nd++;
+      }
+      else
+        error = 1;
+    }
+  }
+  if (error || nd < 3)
+  {
+    fclose(fp);
+    return;
+  }
+  else if (dim[0] != width || dim[1] != height || dim[2] != 65535)
+  {
+    imgdata.process_warnings |= LIBRAW_WARN_BAD_DARKFRAME_DIM;
+    fclose(fp);
+    return;
+  }
+  pixel = (ushort *)calloc(width, sizeof *pixel);
+  merror(pixel, "subtract()");
+  for (row = 0; row < height; row++)
+  {
+    fread(pixel, 2, width, fp);
+    for (col = 0; col < width; col++)
+      BAYER(row, col) = MAX(BAYER(row, col) - ntohs(pixel[col]), 0);
+  }
+  free(pixel);
+  fclose(fp);
+  memset(cblack, 0, sizeof cblack);
+  black = 0;
+  RUN_CALLBACK(LIBRAW_PROGRESS_DARK_FRAME, 1, 2);
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/preprocessing/preprocessing_ph.cpp libkdcraw/libkdcraw/libraw/src/preprocessing/preprocessing_ph.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/preprocessing/preprocessing_ph.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/preprocessing/preprocessing_ph.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,23 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ Placehoder functions to build LibRaw w/o postprocessing
+ and preprocessing calls
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/libraw_cxx_defs.h"
+
+void LibRaw::copy_fuji_uncropped(unsigned short cblack[4],
+				 unsigned short *dmaxp) {}
+void LibRaw::copy_bayer(unsigned short cblack[4], unsigned short *dmaxp){}
+void LibRaw::raw2image_start(){}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/preprocessing/raw2image.cpp libkdcraw/libkdcraw/libraw/src/preprocessing/raw2image.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/preprocessing/raw2image.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/preprocessing/raw2image.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,558 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/libraw_cxx_defs.h"
+
+void LibRaw::raw2image_start()
+{
+  // restore color,sizes and internal data into raw_image fields
+  memmove(&imgdata.color, &imgdata.rawdata.color, sizeof(imgdata.color));
+  memmove(&imgdata.sizes, &imgdata.rawdata.sizes, sizeof(imgdata.sizes));
+  memmove(&imgdata.idata, &imgdata.rawdata.iparams, sizeof(imgdata.idata));
+  memmove(&libraw_internal_data.internal_output_params,
+          &imgdata.rawdata.ioparams,
+          sizeof(libraw_internal_data.internal_output_params));
+
+  if (O.user_flip >= 0)
+    S.flip = O.user_flip;
+
+  switch ((S.flip + 3600) % 360)
+  {
+  case 270:
+    S.flip = 5;
+    break;
+  case 180:
+    S.flip = 3;
+    break;
+  case 90:
+    S.flip = 6;
+    break;
+  }
+
+  // adjust for half mode!
+  IO.shrink =
+      P1.filters &&
+      (O.half_size || ((O.threshold || O.aber[0] != 1 || O.aber[2] != 1)));
+
+  S.iheight = (S.height + IO.shrink) >> IO.shrink;
+  S.iwidth = (S.width + IO.shrink) >> IO.shrink;
+}
+
+int LibRaw::raw2image(void)
+{
+
+  CHECK_ORDER_LOW(LIBRAW_PROGRESS_LOAD_RAW);
+
+  try
+  {
+    raw2image_start();
+
+    if (is_phaseone_compressed() && imgdata.rawdata.raw_alloc)
+    {
+      phase_one_allocate_tempbuffer();
+      int rc = phase_one_subtract_black((ushort *)imgdata.rawdata.raw_alloc,
+                                        imgdata.rawdata.raw_image);
+      if (rc == 0)
+        rc = phase_one_correct();
+      if (rc != 0)
+      {
+        phase_one_free_tempbuffer();
+        return rc;
+      }
+    }
+
+    // free and re-allocate image bitmap
+    if (imgdata.image)
+    {
+      imgdata.image = (ushort(*)[4])realloc(
+          imgdata.image, S.iheight * S.iwidth * sizeof(*imgdata.image));
+      memset(imgdata.image, 0, S.iheight * S.iwidth * sizeof(*imgdata.image));
+    }
+    else
+      imgdata.image =
+          (ushort(*)[4])calloc(S.iheight * S.iwidth, sizeof(*imgdata.image));
+
+    merror(imgdata.image, "raw2image()");
+
+    libraw_decoder_info_t decoder_info;
+    get_decoder_info(&decoder_info);
+
+    // Move saved bitmap to imgdata.image
+    if ((imgdata.idata.filters || P1.colors == 1) && imgdata.rawdata.raw_image)
+    {
+      if (IO.fuji_width)
+      {
+        unsigned r, c;
+        int row, col;
+        for (row = 0; row < S.raw_height - S.top_margin * 2; row++)
+        {
+          for (col = 0;
+               col < IO.fuji_width
+                         << !libraw_internal_data.unpacker_data.fuji_layout;
+               col++)
+          {
+            if (libraw_internal_data.unpacker_data.fuji_layout)
+            {
+              r = IO.fuji_width - 1 - col + (row >> 1);
+              c = col + ((row + 1) >> 1);
+            }
+            else
+            {
+              r = IO.fuji_width - 1 + row - (col >> 1);
+              c = row + ((col + 1) >> 1);
+            }
+            if (r < S.height && c < S.width)
+              imgdata.image[((r) >> IO.shrink) * S.iwidth + ((c) >> IO.shrink)]
+                           [FC(r, c)] =
+                  imgdata.rawdata
+                      .raw_image[(row + S.top_margin) * S.raw_pitch / 2 +
+                                 (col + S.left_margin)];
+          }
+        }
+      }
+      else
+      {
+        int row, col;
+        for (row = 0; row < S.height; row++)
+          for (col = 0; col < S.width; col++)
+            imgdata.image[((row) >> IO.shrink) * S.iwidth +
+                          ((col) >> IO.shrink)][fcol(row, col)] =
+                imgdata.rawdata
+                    .raw_image[(row + S.top_margin) * S.raw_pitch / 2 +
+                               (col + S.left_margin)];
+      }
+    }
+    else // if(decoder_info.decoder_flags & LIBRAW_DECODER_LEGACY)
+    {
+      if (imgdata.rawdata.color4_image)
+      {
+        if (S.width * 8 == S.raw_pitch)
+          memmove(imgdata.image, imgdata.rawdata.color4_image,
+                  S.width * S.height * sizeof(*imgdata.image));
+        else
+        {
+          for (int row = 0; row < S.height; row++)
+            memmove(&imgdata.image[row * S.width],
+                    &imgdata.rawdata
+                         .color4_image[(row + S.top_margin) * S.raw_pitch / 8 +
+                                       S.left_margin],
+                    S.width * sizeof(*imgdata.image));
+        }
+      }
+      else if (imgdata.rawdata.color3_image)
+      {
+        unsigned char *c3image = (unsigned char *)imgdata.rawdata.color3_image;
+        for (int row = 0; row < S.height; row++)
+        {
+          ushort(*srcrow)[3] =
+              (ushort(*)[3]) & c3image[(row + S.top_margin) * S.raw_pitch];
+          ushort(*dstrow)[4] = (ushort(*)[4]) & imgdata.image[row * S.width];
+          for (int col = 0; col < S.width; col++)
+          {
+            for (int c = 0; c < 3; c++)
+              dstrow[col][c] = srcrow[S.left_margin + col][c];
+            dstrow[col][3] = 0;
+          }
+        }
+      }
+      else
+      {
+        // legacy decoder, but no data?
+        throw LIBRAW_EXCEPTION_DECODE_RAW;
+      }
+    }
+
+    // Free PhaseOne separate copy allocated at function start
+    if (is_phaseone_compressed())
+    {
+      phase_one_free_tempbuffer();
+    }
+    // hack - clear later flags!
+
+    if (load_raw == &LibRaw::canon_600_load_raw && S.width < S.raw_width)
+    {
+      canon_600_correct();
+    }
+
+    imgdata.progress_flags =
+        LIBRAW_PROGRESS_START | LIBRAW_PROGRESS_OPEN |
+        LIBRAW_PROGRESS_RAW2_IMAGE | LIBRAW_PROGRESS_IDENTIFY |
+        LIBRAW_PROGRESS_SIZE_ADJUST | LIBRAW_PROGRESS_LOAD_RAW;
+    return 0;
+  }
+  catch (LibRaw_exceptions err)
+  {
+    EXCEPTION_HANDLER(err);
+  }
+}
+
+void LibRaw::copy_fuji_uncropped(unsigned short cblack[4],
+                                 unsigned short *dmaxp)
+{
+  int row;
+#if defined(LIBRAW_USE_OPENMP)
+#pragma omp parallel for default(shared)
+#endif
+  for (row = 0; row < int(S.raw_height) - int(S.top_margin) * 2; row++)
+  {
+    int col;
+    unsigned short ldmax = 0;
+    for (col = 0;
+         col < IO.fuji_width << !libraw_internal_data.unpacker_data.fuji_layout
+         && col + int(S.left_margin) < int(S.raw_width);
+         col++)
+    {
+      unsigned r, c;
+      if (libraw_internal_data.unpacker_data.fuji_layout)
+      {
+        r = IO.fuji_width - 1 - col + (row >> 1);
+        c = col + ((row + 1) >> 1);
+      }
+      else
+      {
+        r = IO.fuji_width - 1 + row - (col >> 1);
+        c = row + ((col + 1) >> 1);
+      }
+      if (r < S.height && c < S.width)
+      {
+        unsigned short val =
+            imgdata.rawdata.raw_image[(row + S.top_margin) * S.raw_pitch / 2 +
+                                      (col + S.left_margin)];
+        int cc = FC(r, c);
+        if (val > cblack[cc])
+        {
+          val -= cblack[cc];
+          if (val > ldmax)
+            ldmax = val;
+        }
+        else
+          val = 0;
+        imgdata.image[((r) >> IO.shrink) * S.iwidth + ((c) >> IO.shrink)][cc] =
+            val;
+      }
+    }
+#if defined(LIBRAW_USE_OPENMP)
+#pragma omp critical(dataupdate)
+#endif
+    {
+      if (*dmaxp < ldmax)
+        *dmaxp = ldmax;
+    }
+  }
+}
+
+void LibRaw::copy_bayer(unsigned short cblack[4], unsigned short *dmaxp)
+{
+  // Both cropped and uncropped
+  int row;
+  int maxHeight = MIN(S.height,S.raw_height-S.top_margin);
+#if defined(LIBRAW_USE_OPENMP)
+#pragma omp parallel for default(shared)
+#endif
+  for (row = 0; row < maxHeight ; row++)
+  {
+    int col;
+    unsigned short ldmax = 0;
+    for (col = 0; col < S.width && col + S.left_margin < S.raw_width; col++)
+    {
+      unsigned short val =
+          imgdata.rawdata.raw_image[(row + S.top_margin) * S.raw_pitch / 2 +
+                                    (col + S.left_margin)];
+      int cc = fcol(row, col);
+      if (val > cblack[cc])
+      {
+        val -= cblack[cc];
+        if (val > ldmax)
+          ldmax = val;
+      }
+      else
+        val = 0;
+      imgdata
+          .image[((row) >> IO.shrink) * S.iwidth + ((col) >> IO.shrink)][cc] =
+          val;
+    }
+#if defined(LIBRAW_USE_OPENMP)
+#pragma omp critical(dataupdate)
+#endif
+    {
+      if (*dmaxp < ldmax)
+        *dmaxp = ldmax;
+    }
+  }
+}
+
+int LibRaw::raw2image_ex(int do_subtract_black)
+{
+
+  CHECK_ORDER_LOW(LIBRAW_PROGRESS_LOAD_RAW);
+
+  try
+  {
+    raw2image_start();
+
+    // Compressed P1 files with bl data!
+    if (is_phaseone_compressed() && imgdata.rawdata.raw_alloc)
+    {
+      phase_one_allocate_tempbuffer();
+      int rc = phase_one_subtract_black((ushort *)imgdata.rawdata.raw_alloc,
+                                        imgdata.rawdata.raw_image);
+      if (rc == 0)
+        rc = phase_one_correct();
+      if (rc != 0)
+      {
+        phase_one_free_tempbuffer();
+        return rc;
+      }
+    }
+
+    // process cropping
+    int do_crop = 0;
+    if (~O.cropbox[2] && ~O.cropbox[3])
+    {
+      int crop[4], c, filt;
+      for (int c = 0; c < 4; c++)
+      {
+        crop[c] = O.cropbox[c];
+        if (crop[c] < 0)
+          crop[c] = 0;
+      }
+
+      if (IO.fuji_width && imgdata.idata.filters >= 1000)
+      {
+        crop[0] = (crop[0] / 4) * 4;
+        crop[1] = (crop[1] / 4) * 4;
+        if (!libraw_internal_data.unpacker_data.fuji_layout)
+        {
+          crop[2] *= sqrt(2.0);
+          crop[3] /= sqrt(2.0);
+        }
+        crop[2] = (crop[2] / 4 + 1) * 4;
+        crop[3] = (crop[3] / 4 + 1) * 4;
+      }
+      else if (imgdata.idata.filters == 1)
+      {
+        crop[0] = (crop[0] / 16) * 16;
+        crop[1] = (crop[1] / 16) * 16;
+      }
+      else if (imgdata.idata.filters == LIBRAW_XTRANS)
+      {
+        crop[0] = (crop[0] / 6) * 6;
+        crop[1] = (crop[1] / 6) * 6;
+      }
+      do_crop = 1;
+
+      crop[2] = MIN(crop[2], (signed)S.width - crop[0]);
+      crop[3] = MIN(crop[3], (signed)S.height - crop[1]);
+      if (crop[2] <= 0 || crop[3] <= 0)
+        throw LIBRAW_EXCEPTION_BAD_CROP;
+
+      // adjust sizes!
+      S.left_margin += crop[0];
+      S.top_margin += crop[1];
+      S.width = crop[2];
+      S.height = crop[3];
+
+      S.iheight = (S.height + IO.shrink) >> IO.shrink;
+      S.iwidth = (S.width + IO.shrink) >> IO.shrink;
+      if (!IO.fuji_width && imgdata.idata.filters &&
+          imgdata.idata.filters >= 1000)
+      {
+        for (filt = c = 0; c < 16; c++)
+          filt |= FC((c >> 1) + (crop[1]), (c & 1) + (crop[0])) << c * 2;
+        imgdata.idata.filters = filt;
+      }
+    }
+
+    int alloc_width = S.iwidth;
+    int alloc_height = S.iheight;
+
+    if (IO.fuji_width && do_crop)
+    {
+      int IO_fw = S.width >> !libraw_internal_data.unpacker_data.fuji_layout;
+      int t_alloc_width =
+          (S.height >> libraw_internal_data.unpacker_data.fuji_layout) + IO_fw;
+      int t_alloc_height = t_alloc_width - 1;
+      alloc_height = (t_alloc_height + IO.shrink) >> IO.shrink;
+      alloc_width = (t_alloc_width + IO.shrink) >> IO.shrink;
+    }
+    int alloc_sz = alloc_width * alloc_height;
+
+    if (imgdata.image)
+    {
+      imgdata.image = (ushort(*)[4])realloc(imgdata.image,
+                                            alloc_sz * sizeof(*imgdata.image));
+      memset(imgdata.image, 0, alloc_sz * sizeof(*imgdata.image));
+    }
+    else
+      imgdata.image = (ushort(*)[4])calloc(alloc_sz, sizeof(*imgdata.image));
+    merror(imgdata.image, "raw2image_ex()");
+
+    libraw_decoder_info_t decoder_info;
+    get_decoder_info(&decoder_info);
+
+    // Adjust black levels
+    unsigned short cblack[4] = {0, 0, 0, 0};
+    unsigned short dmax = 0;
+    if (do_subtract_black)
+    {
+      adjust_bl();
+      for (int i = 0; i < 4; i++)
+        cblack[i] = (unsigned short)C.cblack[i];
+    }
+
+    // Move saved bitmap to imgdata.image
+    if ((imgdata.idata.filters || P1.colors == 1) && imgdata.rawdata.raw_image)
+    {
+      if (IO.fuji_width)
+      {
+        if (do_crop)
+        {
+          IO.fuji_width =
+              S.width >> !libraw_internal_data.unpacker_data.fuji_layout;
+          int IO_fwidth =
+              (S.height >> libraw_internal_data.unpacker_data.fuji_layout) +
+              IO.fuji_width;
+          int IO_fheight = IO_fwidth - 1;
+
+          int row, col;
+          for (row = 0; row < S.height; row++)
+          {
+            for (col = 0; col < S.width; col++)
+            {
+              int r, c;
+              if (libraw_internal_data.unpacker_data.fuji_layout)
+              {
+                r = IO.fuji_width - 1 - col + (row >> 1);
+                c = col + ((row + 1) >> 1);
+              }
+              else
+              {
+                r = IO.fuji_width - 1 + row - (col >> 1);
+                c = row + ((col + 1) >> 1);
+              }
+
+              unsigned short val =
+                  imgdata.rawdata
+                      .raw_image[(row + S.top_margin) * S.raw_pitch / 2 +
+                                 (col + S.left_margin)];
+              int cc = FCF(row, col);
+              if (val > cblack[cc])
+              {
+                val -= cblack[cc];
+                if (dmax < val)
+                  dmax = val;
+              }
+              else
+                val = 0;
+              imgdata.image[((r) >> IO.shrink) * alloc_width +
+                            ((c) >> IO.shrink)][cc] = val;
+            }
+          }
+          S.height = IO_fheight;
+          S.width = IO_fwidth;
+          S.iheight = (S.height + IO.shrink) >> IO.shrink;
+          S.iwidth = (S.width + IO.shrink) >> IO.shrink;
+          S.raw_height -= 2 * S.top_margin;
+        }
+        else
+        {
+          copy_fuji_uncropped(cblack, &dmax);
+        }
+      } // end Fuji
+      else
+      {
+        copy_bayer(cblack, &dmax);
+      }
+    }
+    else // if(decoder_info.decoder_flags & LIBRAW_DECODER_LEGACY)
+    {
+      if (imgdata.rawdata.color4_image)
+      {
+        if (S.raw_pitch != S.width * 8)
+        {
+          for (int row = 0; row < S.height && row + S.top_margin < S.raw_height;
+               row++)
+            memmove(&imgdata.image[row * S.width],
+                    &imgdata.rawdata
+                         .color4_image[(row + S.top_margin) * S.raw_pitch / 8 +
+                                       S.left_margin],
+                    MIN(S.width, S.raw_width - S.left_margin) *
+                        sizeof(*imgdata.image));
+        }
+        else
+        {
+          // legacy is always 4channel and not shrinked!
+          memmove(imgdata.image, imgdata.rawdata.color4_image,
+                  MAX(0,MIN(S.raw_width - S.left_margin, S.width)) *
+                      MAX(0,MIN(S.raw_height - S.top_margin, S.height)) *
+                      sizeof(*imgdata.image));
+        }
+      }
+      else if (imgdata.rawdata.color3_image)
+      {
+        unsigned char *c3image = (unsigned char *)imgdata.rawdata.color3_image;
+        for (int row = 0; row < S.height && row + S.top_margin < S.raw_height;
+             row++)
+        {
+          ushort(*srcrow)[3] =
+              (ushort(*)[3]) & c3image[(row + S.top_margin) * S.raw_pitch];
+          ushort(*dstrow)[4] = (ushort(*)[4]) & imgdata.image[row * S.width];
+          for (int col = 0; col < S.width && col + S.left_margin < S.raw_width;
+               col++)
+          {
+            for (int c = 0; c < 3; c++)
+              dstrow[col][c] = srcrow[S.left_margin + col][c];
+            dstrow[col][3] = 0;
+          }
+        }
+      }
+      else
+      {
+        // legacy decoder, but no data?
+        throw LIBRAW_EXCEPTION_DECODE_RAW;
+      }
+    }
+
+    // Free PhaseOne separate copy allocated at function start
+    if (is_phaseone_compressed())
+    {
+      phase_one_free_tempbuffer();
+    }
+    if (load_raw == &LibRaw::canon_600_load_raw && S.width < S.raw_width)
+    {
+      canon_600_correct();
+    }
+
+    if (do_subtract_black)
+    {
+      C.data_maximum = (int)dmax;
+      C.maximum -= C.black;
+      //        ZERO(C.cblack);
+      C.cblack[0] = C.cblack[1] = C.cblack[2] = C.cblack[3] = 0;
+      C.black = 0;
+    }
+
+    // hack - clear later flags!
+    imgdata.progress_flags =
+        LIBRAW_PROGRESS_START | LIBRAW_PROGRESS_OPEN |
+        LIBRAW_PROGRESS_RAW2_IMAGE | LIBRAW_PROGRESS_IDENTIFY |
+        LIBRAW_PROGRESS_SIZE_ADJUST | LIBRAW_PROGRESS_LOAD_RAW;
+    return 0;
+  }
+  catch (LibRaw_exceptions err)
+  {
+    EXCEPTION_HANDLER(err);
+  }
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/preprocessing/subtract_black.cpp libkdcraw/libkdcraw/libraw/src/preprocessing/subtract_black.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/preprocessing/subtract_black.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/preprocessing/subtract_black.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,91 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/libraw_cxx_defs.h"
+
+int LibRaw::subtract_black()
+{
+  adjust_bl();
+  return subtract_black_internal();
+}
+
+int LibRaw::subtract_black_internal()
+{
+  CHECK_ORDER_LOW(LIBRAW_PROGRESS_RAW2_IMAGE);
+
+  try
+  {
+    if (!is_phaseone_compressed() &&
+        (C.cblack[0] || C.cblack[1] || C.cblack[2] || C.cblack[3] ||
+         (C.cblack[4] && C.cblack[5])))
+    {
+      int cblk[4], i;
+      for (i = 0; i < 4; i++)
+        cblk[i] = C.cblack[i];
+
+      int size = S.iheight * S.iwidth;
+      int dmax = 0;
+      if (C.cblack[4] && C.cblack[5])
+      {
+        for (unsigned i = 0; i < (unsigned)size; i++)
+        {
+          for (unsigned c = 0; c < 4; c++)
+          {
+            int val = imgdata.image[i][c];
+            val -= C.cblack[6 + i / S.iwidth % C.cblack[4] * C.cblack[5] +
+                            i % S.iwidth % C.cblack[5]];
+            val -= cblk[c];
+            imgdata.image[i][c] = CLIP(val);
+            if (dmax < val) dmax = val;
+          }
+        }
+      }
+      else
+      {
+        for (unsigned i = 0; i < (unsigned)size; i++)
+        {
+          for (unsigned c = 0; c < 4; c++)
+          {
+            int val = imgdata.image[i][c];
+            val -= cblk[c];
+            imgdata.image[i][c] = CLIP(val);
+            if (dmax < val) dmax = val;
+          }
+        }
+      }
+      C.data_maximum = dmax & 0xffff;
+      C.maximum -= C.black;
+      ZERO(C.cblack); // Yeah, we used cblack[6+] values too!
+      C.black = 0;
+    }
+    else
+    {
+      // Nothing to Do, maximum is already calculated, black level is 0, so no
+      // change only calculate channel maximum;
+      int idx;
+      ushort *p = (ushort *)imgdata.image;
+      int dmax = 0;
+      for (idx = 0; idx < S.iheight * S.iwidth * 4; idx++)
+        if (dmax < p[idx])
+          dmax = p[idx];
+      C.data_maximum = dmax;
+    }
+    return 0;
+  }
+  catch (LibRaw_exceptions err)
+  {
+    EXCEPTION_HANDLER(err);
+  }
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/tables/cameralist.cpp libkdcraw/libkdcraw/libraw/src/tables/cameralist.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/tables/cameralist.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/tables/cameralist.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,1187 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/libraw_cxx_defs.h"
+#ifdef USE_RAWSPEED
+/* we need separate file for that */
+#include "../../RawSpeed/rawspeed_xmldata.cpp"
+const int RAWSPEED_DATA_COUNT =
+    (sizeof(_rawspeed_data_xml) / sizeof(_rawspeed_data_xml[0]));
+#endif
+// clang-format off
+// Supported cameras:
+static const char *static_camera_list[] = {
+	"Adobe Digital Negative (DNG)",
+	"AgfaPhoto DC-833m",
+	"Alcatel 5035D",
+	"Apple iPad Pro",
+	"Apple iPhone SE",
+	"Apple iPhone 6s",
+	"Apple iPhone 6 plus",
+	"Apple iPhone 7",
+	"Apple iPhone 7 plus",
+	"Apple iPhone 8",
+	"Apple iPhone 8 plus",
+	"Apple iPhone X",
+	"Apple QuickTake 100",
+	"Apple QuickTake 150",
+	"Apple QuickTake 200",
+	"ARRI ALEXA",
+	"ARRI ALEXA65",
+	"ARRI ALEXA LF",
+	"ARRI ALEXA XT",
+	"ARRI ALEXA SXT",
+	"ASUS ZenPhone4",
+	"ASUS ZenPhone6",
+	"AVT F-080C",
+	"AVT F-145C",
+	"AVT F-201C",
+	"AVT F-510C",
+	"AVT F-810C",
+	"Baumer TXG14",
+	"BlackMagic Cinema Camera",
+	"BlackMagic Micro Cinema Camera",
+	"BlackMagic Pocket Cinema Camera",
+	"BlackMagic Production Camera 4k",
+	"BlackMagic URSA",
+	"BlackMagic URSA Mini 4k",
+	"BlackMagic URSA Mini 4.6k",
+	"BlackMagic URSA Mini Pro 4.6k",
+	"BQ Aquarius U",
+	"Canon PowerShot 600",
+	"Canon PowerShot A5",
+	"Canon PowerShot A5 Zoom",
+	"Canon PowerShot A50",
+	"Canon PowerShot A410 (CHDK hack)",
+	"Canon PowerShot A460 (CHDK hack)",
+	"Canon PowerShot A470 (CHDK hack)",
+	"Canon PowerShot A480 (CHDK hack)",
+	"Canon PowerShot A530 (CHDK hack)",
+	"Canon PowerShot A540 (CHDK hack)",
+	"Canon PowerShot A550 (CHDK hack)",
+	"Canon PowerShot A560 (CHDK hack)",
+	"Canon PowerShot A570 (CHDK hack)",
+	"Canon PowerShot A590 (CHDK hack)",
+	"Canon PowerShot A610 (CHDK hack)",
+	"Canon PowerShot A620 (CHDK hack)",
+	"Canon PowerShot A630 (CHDK hack)",
+	"Canon PowerShot A640 (CHDK hack)",
+	"Canon PowerShot A650 (CHDK hack)",
+	"Canon PowerShot A710 IS (CHDK hack)",
+	"Canon PowerShot A720 IS (CHDK hack)",
+	"Canon PowerShot A3300 IS (CHDK hack)",
+	"Canon PowerShot D10 (CHDK hack)",
+	"Canon PowerShot ELPH 130 IS (CHDK hack)",
+	"Canon PowerShot ELPH 160 IS (CHDK hack)",
+	"Canon PowerShot Pro70",
+	"Canon PowerShot Pro90 IS",
+	"Canon PowerShot Pro1",
+	"Canon PowerShot G1",
+	"Canon PowerShot G1 X",
+	"Canon PowerShot G1 X Mark II",
+	"Canon PowerShot G1 X Mark III",
+	"Canon PowerShot G2",
+	"Canon PowerShot G3",
+	"Canon PowerShot G3 X",
+	"Canon PowerShot G5",
+	"Canon PowerShot G5 X",
+	"Canon PowerShot G5 X Mark II",
+	"Canon PowerShot G6",
+	"Canon PowerShot G7 (CHDK hack)",
+	"Canon PowerShot G7 X",
+	"Canon PowerShot G7 X Mark II",
+	"Canon PowerShot G7 X Mark III",
+	"Canon PowerShot G9",
+	"Canon PowerShot G9 X",
+	"Canon PowerShot G9 X Mark II",
+	"Canon PowerShot G10",
+	"Canon PowerShot G11",
+	"Canon PowerShot G12",
+	"Canon PowerShot G15",
+	"Canon PowerShot G16",
+	"Canon PowerShot S2 IS (CHDK hack)",
+	"Canon PowerShot S3 IS (CHDK hack)",
+	"Canon PowerShot S5 IS (CHDK hack)",
+	"Canon PowerShot SD300 (CHDK hack)",
+	"Canon PowerShot SD750 (CHDK hack)",
+	"Canon PowerShot SD950 (CHDK hack)",
+	"Canon PowerShot S30",
+	"Canon PowerShot S40",
+	"Canon PowerShot S45",
+	"Canon PowerShot S50",
+	"Canon PowerShot S60",
+	"Canon PowerShot S70",
+	"Canon PowerShot S90",
+	"Canon PowerShot S95",
+	"Canon PowerShot S100",
+	"Canon PowerShot S110",
+	"Canon PowerShot S120",
+	"Canon PowerShot SX1 IS",
+	"Canon PowerShot SX50 HS",
+	"Canon PowerShot SX60 HS",
+	"Canon PowerShot SX70 HS",
+	"Canon PowerShot SX100 IS (CHDK hack)",
+	"Canon PowerShot SX110 IS (CHDK hack)",
+	"Canon PowerShot SX120 IS (CHDK hack)",
+	"Canon PowerShot SX130 IS (CHDK hack)",
+	"Canon PowerShot SX160 IS (CHDK hack)",
+	"Canon PowerShot SX220 HS (CHDK hack)",
+	"Canon PowerShot SX510 HS (CHDK hack)",
+	"Canon PowerShot SX10 IS (CHDK hack)",
+	"Canon PowerShot SX20 IS (CHDK hack)",
+	"Canon PowerShot SX30 IS (CHDK hack)",
+	"Canon PowerShot IXUS 95 (CHDK hack)",
+	"Canon PowerShot IXUS 160 (CHDK hack)",
+	"Canon PowerShot IXUS 900Ti (CHDK hack)",
+	"Canon EOS R",
+	"Canon EOS RP",
+	"Canon EOS D30",
+	"Canon EOS D60",
+	"Canon EOS 5DS",
+	"Canon EOS 5DS R",
+	"Canon EOS 5D",
+	"Canon EOS 5D Mark II",
+	"Canon EOS 5D Mark III",
+	"Canon EOS 5D Mark IV",
+	"Canon EOS 6D",
+	"Canon EOS 6D Mark II",
+	"Canon EOS 7D",
+	"Canon EOS 7D Mark II",
+	"Canon EOS 10D",
+	"Canon EOS 20D",
+	"Canon EOS 20Da",
+	"Canon EOS 30D",
+	"Canon EOS 40D",
+	"Canon EOS 50D",
+	"Canon EOS 60D",
+	"Canon EOS 60Da",
+	"Canon EOS 70D",
+	"Canon EOS 77D / 9000D",
+	"Canon EOS 80D",
+	"Canon EOS 90D",
+	"Canon EOS 100D / Rebel SL1 / Kiss X7",
+	"Canon EOS 200D / Rebel SL2 / Kiss X9",
+	"Canon EOS 250D / 200D II / Rebel SL3 / Kiss X10",
+	"Canon EOS 300D / Digital Rebel / Kiss Digital",
+	"Canon EOS 350D / Digital Rebel XT / Kiss Digital N",
+	"Canon EOS 400D / Digital Rebel XTi / Kiss Digital X",
+	"Canon EOS 450D / Digital Rebel XSi / Kiss X2",
+	"Canon EOS 500D / Rebel T1i / Kiss X3",
+	"Canon EOS 550D / Rebel T2i / Kiss X4",
+	"Canon EOS 600D / Rebel T3i / Kiss X5",
+	"Canon EOS 650D / Rebel T4i / Kiss X6i",
+	"Canon EOS 700D / Rebel T5i / Kiss X7i",
+	"Canon EOS 750D / Rebel T6i / Kiss X8i",
+	"Canon EOS 760D / Rebel T6S / 8000D",
+	"Canon EOS 800D / Rebel T7i / Kiss X9i",
+	"Canon EOS 1000D / Digital Rebel XS / Kiss F",
+	"Canon EOS 1100D / Rebel T3 / Kiss X50",
+	"Canon EOS 1200D / Kiss X70 / REBEL T5 / Hi",
+	"Canon EOS 1300D / Rebel T6 / Kiss X80",
+	"Canon EOS 1500D / 2000D / Rebel T7 / Kiss X90",
+	"Canon EOS 3000D / 4000D / Rebel T100",
+	"Canon EOS C500",
+	"Canon EOS D2000",
+	"Canon EOS M",
+	"Canon EOS M2",
+	"Canon EOS M3",
+	"Canon EOS M5",
+	"Canon EOS M6",
+	"Canon EOS M6 Mark II",
+	"Canon EOS M10",
+	"Canon EOS M50 / Kiss M",
+	"Canon EOS M100",
+	"Canon EOS M200",
+	"Canon EOS-1D C",
+	"Canon EOS-1D X",
+	"Canon EOS-1D X Mark II",
+	"Canon EOS-1D X Mark III (lossless compressed only)",
+	"Canon EOS-1D",
+	"Canon EOS-1D Mark II",
+	"Canon EOS-1D Mark II N",
+	"Canon EOS-1D Mark III",
+	"Canon EOS-1D Mark IV",
+	"Canon EOS-1DS",
+	"Canon EOS-1Ds Mark II",
+	"Canon EOS-1Ds Mark III",
+	"Casio QV-2000UX (secret menu hack)",
+	"Casio QV-3000EX (secret menu hack)",
+	"Casio QV-3500EX (secret menu hack)",
+	"Casio QV-4000 (secret menu hack)",
+	"Casio QV-5700 (secret menu hack)",
+	"Casio QV-R41",
+	"Casio QV-R51",
+	"Casio QV-R61",
+	"Casio EX-F1",
+	"Casio EX-FC300S",
+	"Casio EX-FC400S",
+	"Casio EX-FH20",
+	"Casio EX-FH25",
+	"Casio EX-FH100",
+	"Casio EX-S20 / M20",
+	"Casio EX-S100",
+	"Casio EX-Z4",
+	"Casio EX-Z50",
+	"Casio EX-Z500",
+	"Casio EX-Z55",
+	"Casio EX-Z60",
+	"Casio EX-Z75",
+	"Casio EX-Z750",
+	"Casio EX-Z8",
+	"Casio EX-Z850",
+	"Casio EX-Z1050",
+	"Casio EX-ZR100",
+	"Casio EX-Z1080",
+	"Casio EX-ZR700",
+	"Casio EX-ZR710",
+	"Casio EX-ZR750",
+	"Casio EX-ZR800",
+	"Casio EX-ZR850",
+	"Casio EX-ZR1000",
+	"Casio EX-ZR1100",
+	"Casio EX-ZR1200",
+	"Casio EX-ZR1300",
+	"Casio EX-ZR1500",
+	"Casio EX-ZR3000",
+	"Casio EX-ZR3100",
+	"Casio EX-ZR3200",
+	"Casio EX-ZR3500",
+	"Casio EX-ZR3600",
+	"Casio EX-ZR3700",
+	"Casio EX-ZR4000 / 5000",
+	"Casio EX-ZR4100 / 5100",
+	"Casio EX-100",
+	"Casio EX-100F",
+	"Casio EX-100PRO",
+	"Casio EX-10",
+	"Casio EX-P505 (secret menu hack)",
+	"Casio EX-P600 (secret menu hack)",
+	"Casio EX-P700 (secret menu hack)",
+	"CLAUSS pix500",
+	"Contax N Digital",
+	"Creative PC-CAM 600",
+	"Digital Bolex D16",
+	"Digital Bolex D16M",
+	"DJI 4384x3288",
+	"DJI Mavic Air",
+	"DJI Mavic Air2",
+	"DJI Osmo Action",
+	"DJI Phantom4 Pro/Pro+",
+	"DJI Zenmuse X5",
+	"DJI Zenmuse X5R",
+	"DXO One",
+	"Epson R-D1",
+	"Epson R-D1s",
+	"Epson R-D1x",
+	"Eyedeas E1",
+	"Foculus 531C",
+	"FujiFilm DBP for GX680 / DX-2000",
+	"FujiFilm E550",
+	"FujiFilm E900",
+	"FujiFilm F500EXR / F505EXR",
+	"FujiFilm F550EXR",
+	"FujiFilm F600EXR / F605EXR",
+	"FujiFilm F700",
+	"FujiFilm F710",
+	"FujiFilm F770EXR / F775EXR",
+	"FujiFilm F800EXR",
+	"FujiFilm F810",
+	"FujiFilm F900EXR",
+	"FujiFilm S2Pro",
+	"FujiFilm S3Pro",
+	"FujiFilm S5Pro",
+	"FujiFilm S20Pro",
+	"FujiFilm S1",
+	"FujiFilm S100FS",
+	"FujiFilm S5000",
+	"FujiFilm S5100 / S5500",
+	"FujiFilm S5200 / S5600",
+	"FujiFilm S6000fd / S6500fd",
+	"FujiFilm S7000",
+	"FujiFilm S9000 / S9500",
+	"FujiFilm S9100 / S9600",
+	"FujiFilm S200EXR / S205EXR",
+	"FujiFilm SL1000",
+	"FujiFilm HS10/HS11",
+	"FujiFilm HS20EXR / HS22EXR",
+	"FujiFilm HS30EXR / HS33EXR / HS35EXR",
+	"FujiFilm HS50EXR",
+	"FujiFilm GFX 50S",
+	"FujiFilm GFX 50R",
+	"FujiFilm GFX 100",
+	"FujiFilm X-Pro1",
+	"FujiFilm X-Pro2",
+	"FujiFilm X-Pro3",
+	"FujiFilm X-S1",
+	"FujiFilm XQ1",
+	"FujiFilm XQ2",
+	"FujiFilm X100",
+	"FujiFilm X100f",
+	"FujiFilm X100S",
+	"FujiFilm X100T",
+	"FujiFilm X100V",
+	"FujiFilm X10",
+	"FujiFilm X20",
+	"FujiFilm X30",
+	"FujiFilm X70",
+	"FujiFilm X-A1",
+	"FujiFilm X-A2",
+	"FujiFilm X-A3",
+	"FujiFilm X-A5",
+	"FujiFilm X-A7",
+	"FujiFilm X-A10",
+	"FujiFilm X-A20",
+	"FujiFilm X-E1",
+	"FujiFilm X-E2",
+	"FujiFilm X-E2S",
+	"FujiFilm X-E3",
+	"FujiFilm X-M1",
+	"FujiFilm XF1",
+	"FujiFilm XF10",
+	"FujiFilm X-H1",
+	"FujiFilm X-T1",
+	"FujiFilm X-T1 Graphite Silver",
+	"FujiFilm X-T2",
+	"FujiFilm X-T3",
+	"FujiFilm X-T4 (uncompressed and lossless compressed only)",
+	"FujiFilm X-T10",
+	"FujiFilm X-T20",
+	"FujiFilm X-T30",
+	"FujiFilm X-T100",
+	"FujiFilm X-T200",
+	"FujiFilm IS-1",
+	"Gione E7",
+	"GITUP GIT2",
+	"GITUP GIT2P",
+	"GITUP G3 DUO (16:9 mode only)",
+	"Google Pixel",
+	"Google Pixel XL",
+	"Google Pixel 3a",
+	"Google Pixel 4 XL",
+#ifdef  USE_GPRSDK
+	"GoPro Fusion",
+	"GoPro HERO5",
+	"GoPro HERO6",
+	"GoPro HERO7",
+	"GoPro HERO8",
+#endif
+	"Hasselblad H2D-22",
+	"Hasselblad H2D-39",
+	"Hasselblad H3DII-22",
+	"Hasselblad H3DII-31",
+	"Hasselblad H3DII-39",
+	"Hasselblad H3DII-50",
+	"Hasselblad H3D-22",
+	"Hasselblad H3D-31",
+	"Hasselblad H3D-39",
+	"Hasselblad H4D-60",
+	"Hasselblad H4D-50",
+	"Hasselblad H4D-40",
+	"Hasselblad H4D-31",
+	"Hasselblad H5D-60",
+	"Hasselblad H5D-50",
+	"Hasselblad H5D-50c",
+	"Hasselblad H5D-40",
+	"Hasselblad H6D-100c",
+	"Hasselblad A6D-100c", // Aerial camera
+	"Hasselblad CFV",
+	"Hasselblad CFV-50",
+	"Hasselblad CFH",
+	"Hasselblad CF-22",
+	"Hasselblad CF-31",
+	"Hasselblad CF-39",
+	"Hasselblad V96C",
+	"Hasselblad L1D-20c (DJI Mavic 2 Pro)",
+	"Hasselblad Lusso",
+	"Hasselblad Lunar",
+	"Hasselblad True Zoom",
+	"Hasselblad Stellar",
+	"Hasselblad Stellar II",
+	"Hasselblad HV",
+	"Hasselblad X1D",
+	"Hasselblad X1D II 50C",
+	"HTC UltraPixel",
+	"HTC MyTouch 4G",
+	"HTC One (A9)",
+	"HTC One (M9)",
+	"HTC 10",
+	"HTC U12",
+	"Huawei P8 Lite (PRA-LX1)",
+	"Huawei P9 (EVA-L09/AL00)",
+	"Huawei P10 (VTR-L09)",
+	"Huawei P10+ (VKY-L09)",
+	"Huawei P10 Lite (WAS-LX1A)",
+	"Huawei P20 (EML-L09)",
+	"Huawei P20 Pro (CLT-L29/L09)",
+	"Huawei P30 Pro (VOG-L29)",
+	"Huawei Honor6a",
+	"Huawei Honor7a pro",
+	"Huawei Honor8 (FRD-L09)",
+	"Huawei Honor9",
+	"Huawei Honor10",
+	"Huawei Honor20",
+	"Huawei Honor View 10 (BKL-L09)",
+	"Huawei Honor View 20 (PCT-L29)",
+	"Huawei Mate8 (NXT-L29)",
+	"Huawei Mate10 (BLA-L29)",
+	"Huawei Mate20 Pro (LYA-L29)",
+	"Imacon Ixpress 96, 96C",
+	"Imacon Ixpress 384, 384C (single shot only)",
+	"Imacon Ixpress 132C",
+	"Imacon Ixpress 528C (single shot only)",
+	"ISG 2020x1520",
+	"Ikonoskop A-Cam dII Panchromatic",
+	"Ikonoskop A-Cam dII",
+	"Kinefinity KineMINI",
+	"Kinefinity KineRAW Mini",
+	"Kinefinity KineRAW S35",
+	"Kodak DC20",
+	"Kodak DC25",
+	"Kodak DC40",
+	"Kodak DC50",
+	"Kodak DC120",
+	"Kodak DCS200",
+	"Kodak DCS315C",
+	"Kodak DCS330C",
+	"Kodak DCS420",
+	"Kodak DCS460",
+	"Kodak DCS460M",
+	"Kodak DCS460",
+	"Kodak DCS520C",
+	"Kodak DCS560C",
+	"Kodak DCS620C",
+	"Kodak DCS620X",
+	"Kodak DCS660C",
+	"Kodak DCS660M",
+	"Kodak DCS720X",
+	"Kodak DCS760C",
+	"Kodak DCS760M",
+	"Kodak EOSDCS1",
+	"Kodak EOSDCS3",
+	"Kodak NC2000",
+	"Kodak ProBack",
+	"Kodak PB645C",
+	"Kodak PB645H",
+	"Kodak PB645M",
+	"Kodak DCS Pro 14n",
+	"Kodak DCS Pro 14nx",
+	"Kodak DCS Pro SLR/c",
+	"Kodak DCS Pro SLR/n",
+	"Kodak C330",
+	"Kodak C603",
+	"Kodak P850",
+	"Kodak P880",
+	"Kodak PIXPRO AZ901",
+	"Kodak PIXPRO S-1",
+	"Kodak Z980",
+	"Kodak Z981",
+	"Kodak Z990",
+	"Kodak Z1015",
+	"Kodak KAI-0340",
+	"Konica KD-400Z",
+	"Konica KD-510Z",
+	"Leaf AFi 5",
+	"Leaf AFi 6",
+	"Leaf AFi 7",
+	"Leaf AFi-II 6",
+	"Leaf AFi-II 7",
+	"Leaf AFi-II 10",
+	"Leaf AFi-II 10R",
+	"Leaf Aptus-II 5",
+	"Leaf Aptus-II 6",
+	"Leaf Aptus-II 7",
+	"Leaf Aptus-II 8",
+	"Leaf Aptus-II 10",
+	"Leaf Aptus-II 12",
+	"Leaf Aptus-II 12R",
+	"Leaf Aptus 17",
+	"Leaf Aptus 22",
+	"Leaf Aptus 54S",
+	"Leaf Aptus 65",
+	"Leaf Aptus 65S",
+	"Leaf Aptus 75",
+	"Leaf Aptus 75S",
+	"Leaf Cantare",
+	"Leaf Cantare XY",
+	"Leaf CatchLight",
+	"Leaf CMost",
+	"Leaf Credo 40",
+	"Leaf Credo 50",
+	"Leaf Credo 60",
+	"Leaf Credo 80 (low compression mode only)",
+	"Leaf DCB-II",
+	"Leaf Valeo 6",
+	"Leaf Valeo 11",
+	"Leaf Valeo 17",
+	"Leaf Valeo 17wi",
+	"Leaf Valeo 22",
+	"Leaf Valeo 22wi",
+	"Leaf Volare",
+	"Lenovo a820",
+	"Leica C (Typ 112)",
+	"Leica CL",
+	"Leica C-Lux / CAM-DC25",
+	"Leica Digilux 2",
+	"Leica Digilux 3",
+	"Leica Digital-Modul-R",
+	"Leica D-LUX2",
+	"Leica D-LUX3",
+	"Leica D-LUX4",
+	"Leica D-LUX5",
+	"Leica D-LUX6",
+	"Leica D-LUX7",
+	"Leica D-Lux (Typ 109)",
+	"Leica M8",
+	"Leica M8.2",
+	"Leica M9",
+	"Leica M10",
+	"Leica M10-D",
+	"Leica M10-P",
+	"Leica M10 Monochrom",
+	"Leica M (Typ 240)",
+	"Leica M (Typ 262)",
+	"Leica Monochrom (Typ 240)",
+	"Leica Monochrom (Typ 246)",
+	"Leica M-D (Typ 262)",
+	"Leica M-E",
+	"Leica M-P",
+	"Leica R8",
+	"Leica Q (Typ 116)",
+	"Leica Q-P",
+	"Leica Q2",
+	"Leica S",
+	"Leica S2",
+	"Leica S (Typ 007)",
+	"Leica SL (Typ 601)",
+	"Leica SL2",
+	"Leica T (Typ 701)",
+	"Leica TL",
+	"Leica TL2",
+	"Leica X1",
+	"Leica X (Typ 113)",
+	"Leica X2",
+	"Leica X-E (Typ 102)",
+	"Leica X-U (Typ 113)",
+	"Leica V-LUX1",
+	"Leica V-LUX2",
+	"Leica V-LUX3",
+	"Leica V-LUX4",
+	"Leica V-LUX5",
+	"Leica V-Lux (Typ 114)",
+	"Leica X VARIO (Typ 107)",
+	"LG G3",
+	"LG G4",
+	"LG G6",
+	"LG V20 (F800K)",
+	"LG VS995",
+	"Logitech Fotoman Pixtura",
+	"Mamiya ZD",
+	"Matrix 4608x3288",
+	"Meizy MX4",
+	"Micron 2010",
+	"Minolta RD175 / Agfa ActionCam",
+	"Minolta DiMAGE 5",
+	"Minolta DiMAGE 7",
+	"Minolta DiMAGE 7i",
+	"Minolta DiMAGE 7Hi",
+	"Minolta DiMAGE A1",
+	"Minolta DiMAGE A2",
+	"Minolta DiMAGE A200",
+	"Minolta DiMAGE G400",
+	"Minolta DiMAGE G500",
+	"Minolta DiMAGE G530",
+	"Minolta DiMAGE G600",
+	"Minolta DiMAGE Z2",
+	"Minolta Alpha/Dynax/Maxxum 5D",
+	"Minolta Alpha/Dynax/Maxxum 7D",
+	"Motorola PIXL",
+	"Motorola Moto G (5S)",
+	"Motorola Moto G7 Play",
+	"Nikon D1",
+	"Nikon D1H",
+	"Nikon D1X",
+	"Nikon D2H",
+	"Nikon D2Hs",
+	"Nikon D2X",
+	"Nikon D2Xs",
+	"Nikon D3",
+	"Nikon D3s",
+	"Nikon D3X",
+	"Nikon D4",
+	"Nikon D4s",
+	"Nikon D40",
+	"Nikon D40X",
+	"Nikon D5",
+	"Nikon D50",
+	"Nikon D5",
+	"Nikon D60",
+	"Nikon D70",
+	"Nikon D70s",
+	"Nikon D80",
+	"Nikon D90",
+	"Nikon D100",
+	"Nikon D200",
+	"Nikon D300",
+	"Nikon D300s",
+	"Nikon D500",
+	"Nikon D600",
+	"Nikon D610",
+	"Nikon D700",
+	"Nikon D750",
+	"Nikon D780",
+	"Nikon D800",
+	"Nikon D800E",
+	"Nikon D810",
+	"Nikon D810A",
+	"Nikon D850",
+	"Nikon D3000",
+	"Nikon D3100",
+	"Nikon D3200",
+	"Nikon D3300",
+	"Nikon D3400",
+	"Nikon D3500",
+	"Nikon D5000",
+	"Nikon D5100",
+	"Nikon D5200",
+	"Nikon D5300",
+	"Nikon D5500",
+	"Nikon D5600",
+	"Nikon D7000",
+	"Nikon D7100",
+	"Nikon D7200",
+	"Nikon D7500",
+	"Nikon Df",
+	"Nikon Z 6",
+	"Nikon Z 7",
+	"Nikon Z 50",
+	"Nikon 1 AW1",
+	"Nikon 1 J1",
+	"Nikon 1 J2",
+	"Nikon 1 J3",
+	"Nikon 1 J4",
+	"Nikon 1 J5",
+	"Nikon 1 S1",
+	"Nikon 1 S2",
+	"Nikon 1 V1",
+	"Nikon 1 V2",
+	"Nikon 1 V3",
+	"Nikon Coolpix 700 (\"DIAG RAW\" hack)",
+	"Nikon Coolpix 800 (\"DIAG RAW\" hack)",
+	"Nikon Coolpix 880 (\"DIAG RAW\" hack)",
+	"Nikon Coolpix 900 (\"DIAG RAW\" hack)",
+	"Nikon Coolpix 950 (\"DIAG RAW\" hack)",
+	"Nikon Coolpix 990 (\"DIAG RAW\" hack)",
+	"Nikon Coolpix 995 (\"DIAG RAW\" hack)",
+	"Nikon Coolpix 2100 (\"DIAG RAW\" hack)",
+	"Nikon Coolpix 2500 (\"DIAG RAW\" hack)",
+	"Nikon Coolpix 3200 (\"DIAG RAW\" hack)",
+	"Nikon Coolpix 3700 (\"DIAG RAW\" hack)",
+	"Nikon Coolpix 4300 (\"DIAG RAW\" hack)",
+	"Nikon Coolpix 4500 (\"DIAG RAW\" hack)",
+	"Nikon Coolpix 5000",
+	"Nikon Coolpix 5400",
+	"Nikon Coolpix 5700",
+	"Nikon Coolpix 8400",
+	"Nikon Coolpix 8700",
+	"Nikon Coolpix 8800",
+	"Nikon Coolpix A",
+	"Nikon Coolpix A1000",
+	"Nikon Coolpix B700",
+	"Nikon Coolpix P330",
+	"Nikon Coolpix P340",
+	"Nikon Coolpix P950",
+	"Nikon Coolpix P6000",
+	"Nikon Coolpix P1000",
+	"Nikon Coolpix P7000",
+	"Nikon Coolpix P7100",
+	"Nikon Coolpix P7700",
+	"Nikon Coolpix P7800",
+	"Nikon Coolpix S6 (\"DIAG RAW\" hack)",
+	"Nikon Coolscan NEF",
+	"Nokia 7 Plus",
+	"Nokia N95",
+	"Nokia X2",
+	"Nokia 1200x1600",
+	"Nokia Lumia 930",
+	"Nokia Lumia 950 XL",
+	"Nokia Lumia 1020",
+	"Nokia Lumia 1520",
+	"Olympus AIR A01",
+	"Olympus C-3030Z",
+	"Olympus C-5050Z",
+	"Olympus C-5060WZ",
+	"Olympus C-7070WZ",
+	"Olympus C-70Z / C-7000Z",
+	"Olympus C-740UZ",
+	"Olympus C-770UZ",
+	"Olympus C-8080WZ",
+	"Olympus X200 / D-560Z / C-350Z",
+	"Olympus E-1",
+	"Olympus E-3",
+	"Olympus E-5",
+	"Olympus E-10",
+	"Olympus E-20 / E-20N / E-20P",
+	"Olympus E-30",
+	"Olympus E-300",
+	"Olympus E-330",
+	"Olympus E-400",
+	"Olympus E-410",
+	"Olympus E-420",
+	"Olympus E-450",
+	"Olympus E-500",
+	"Olympus E-510",
+	"Olympus E-520",
+	"Olympus E-600",
+	"Olympus E-620",
+	"Olympus E-P1",
+	"Olympus E-P2",
+	"Olympus E-P3",
+	"Olympus E-P5",
+	"Olympus E-PL1",
+	"Olympus E-PL1s",
+	"Olympus E-PL2",
+	"Olympus E-PL3",
+	"Olympus E-PL5",
+	"Olympus E-PL6",
+	"Olympus E-PL7",
+	"Olympus E-PL8",
+	"Olympus E-PL9",
+	"Olympus E-PL10",
+	"Olympus E-PM1",
+	"Olympus E-PM2",
+	"Olympus E-M1",
+	"Olympus E-M1 Mark II",
+	"Olympus E-M1 Mark III",
+	"Olympus E-M1X",
+	"Olympus E-M10",
+	"Olympus E-M10 Mark II",
+	"Olympus E-M10 Mark III",
+	"Olympus E-M5",
+	"Olympus E-M5 Mark II",
+	"Olympus E-M5 Mark III",
+	"Olympus Pen-F",
+	"Olympus SP-310",
+	"Olympus SP-320",
+	"Olympus SP-350",
+	"Olympus SP-500UZ",
+	"Olympus SP-510UZ",
+	"Olympus SP-550UZ",
+	"Olympus SP-560UZ",
+	"Olympus SP-565UZ",
+	"Olympus SP-570UZ",
+	"Olympus Stylus 1",
+	"Olympus Stylus 1s",
+	"Olympus SH-2",
+	"Olympus SH-3",
+	"Olympus TG-4",
+	"Olympus TG-5",
+	"Olympus TG-6",
+	"Olympus XZ-1",
+	"Olympus XZ-2",
+	"Olympus XZ-10",
+	"OmniVision 4688",
+	"OmniVision OV5647",
+	"OmniVision OV5648",
+	"OmniVision OV8850",
+	"OmniVision 13860",
+	"OnePlus 6T",
+	"OnePlus One",
+	"OnePlus A3303",
+	"OnePlus A5000",
+	"Panasonic DMC-CM1",
+	"Panasonic DMC-FZ8",
+	"Panasonic DMC-FZ18",
+	"Panasonic DMC-FZ28",
+	"Panasonic DMC-FZ30",
+	"Panasonic DMC-FZ35 / FZ38",
+	"Panasonic DMC-FZ40 / FZ42 / FZ45",
+	"Panasonic DMC-FZ50",
+	"Panasonic DMC-FZ70 / FZ72",
+	"Panasonic DC-FZ80 / FZ81 / FZ82 / FZ83 / FZ85",
+	"Panasonic DMC-FZ100",
+	"Panasonic DMC-FZ150",
+	"Panasonic DMC-FZ200",
+	"Panasonic DMC-FZ300 / FZ330",
+	"Panasonic DMC-FZ1000",
+	"Panasonic DC-FZ1000 II / FZ1000M2 / DC-FZ10002",
+	"Panasonic DMC-FZ2000 / FZ2500 / FZH1",
+	"Panasonic DMC-FX150 / FX180",
+	"Panasonic DMC-G1",
+	"Panasonic DMC-G10",
+	"Panasonic DMC-G2",
+	"Panasonic DMC-G3",
+	"Panasonic DMC-G5",
+	"Panasonic DMC-G6",
+	"Panasonic DMC-G7 / G70",
+	"Panasonic DMC-G8 / G80 / G81 / G85",
+	"Panasonic DC-G9",
+	"Panasonic DC-G90 / G95 / G91 / G99",
+	"Panasonic DMC-GF1",
+	"Panasonic DMC-GF2",
+	"Panasonic DMC-GF3",
+	"Panasonic DMC-GF5",
+	"Panasonic DMC-GF6",
+	"Panasonic DMC-GF7",
+	"Panasonic DC-GF10 / GF90",
+	"Panasonic DMC-GH1",
+	"Panasonic DMC-GH2",
+	"Panasonic DMC-GH3",
+	"Panasonic DMC-GH4",
+	"Panasonic AG-GH4",
+	"Panasonic DC-GH5",
+	"Panasonic DC-GH5S",
+	"Panasonic DMC-GM1",
+	"Panasonic DMC-GM1s",
+	"Panasonic DMC-GM5",
+	"Panasonic DMC-GX1",
+	"Panasonic DMC-GX7",
+	"Panasonic DMC-GX8",
+	"Panasonic DC-GX9 / GX7mkIII",
+	"Panasonic DMC-GX80 / GX85, DMC-GX7mkII",
+	"Panasonic DC-GX800 / GX850, DC-GF9",
+	"Panasonic DMC-L1",
+	"Panasonic DMC-L10",
+	"Panasonic DMC-LC1",
+	"Panasonic DMC-LF1",
+	"Panasonic DMC-LX1",
+	"Panasonic DMC-LX2",
+	"Panasonic DMC-LX3",
+	"Panasonic DMC-LX5",
+	"Panasonic DMC-LX7",
+	"Panasonic DMC-LX9 / LX10 / LX15",
+	"Panasonic DMC-LX100",
+	"Panasonic DC-LX100M2",
+	"Panasonic DC-S1",
+	"Panasonic DC-S1H",
+	"Panasonic DC-S1R",
+	"Panasonic DMC-ZS40, DMC-TZ60 / TZ61",
+	"Panasonic DMC-ZS50, DMC-TZ70 / TZ71",
+	"Panasonic DMC-ZS60, DMC-TZ80 / TZ81 / TZ82 / TZ85",
+	"Panasonic DC-ZS70, DC-TZ90 / TZ91 / TZ92 / TZ93",
+	"Panasonic DC-ZS80, DC-TZ95 / TZ96 / TZ97",
+	"Panasonic DMC-ZS100 / ZS110, DMC-TZ100 / TZ101 / TZ110, DMC-TX1",
+	"Panasonic DC-ZS200 / ZS220, DC-TZ200 / TZ202 / TZ220, DC-TX2",
+	"PARROT Anafi",
+	"PARROT Bebop 2",
+	"PARROT Bebop Drone",
+	"Pentax *ist D",
+	"Pentax *ist DL",
+	"Pentax *ist DL2",
+	"Pentax *ist DS",
+	"Pentax *ist DS2",
+	"Pentax K10D",
+	"Pentax K20D",
+	"Pentax K100D",
+	"Pentax K100D Super",
+	"Pentax K110D",
+	"Pentax K200D",
+	"Pentax K2000/K-m",
+	"Pentax KP",
+	"Pentax K-x",
+	"Pentax K-r",
+	"Pentax K-01",
+	"Pentax K-1",
+	"Pentax K-1 Mark II",
+	"Pentax K-3",
+	"Pentax K-3 II",
+	"Pentax K-30",
+	"Pentax K-5",
+	"Pentax K-5 II",
+	"Pentax K-5 IIs",
+	"Pentax K-50",
+	"Pentax K-500",
+	"Pentax K-7",
+	"Pentax K-70",
+	"Pentax K-S1",
+	"Pentax K-S2",
+	"Pentax MX-1",
+	"Pentax Q",
+	"Pentax Q7",
+	"Pentax Q10",
+	"Pentax QS-1",
+	"Pentax Optio S (secret menu or hack)",
+	"Pentax Optio S4 (secret menu or hack)",
+	"Pentax Optio 33WR (secret menu or hack)",
+	"Pentax Optio 750Z (secret menu or hack)",
+	"Pentax 645D",
+	"Pentax 645Z",
+	"PhaseOne IQ140",
+	"PhaseOne IQ150",
+	"PhaseOne IQ160",
+	"PhaseOne IQ180",
+	"PhaseOne IQ180 IR",
+	"PhaseOne IQ250",
+	"PhaseOne IQ260",
+	"PhaseOne IQ260 Achromatic",
+	"PhaseOne IQ280",
+	"PhaseOne IQ3 50MP",
+	"PhaseOne IQ3 60MP",
+	"PhaseOne IQ3 80MP",
+	"PhaseOne IQ3 100MP",
+	"PhaseOne IQ3 100MP Trichromatic",
+	"PhaseOne IQ4 150MP",
+	"PhaseOne LightPhase",
+	"PhaseOne Achromatic+",
+	"PhaseOne H 10",
+	"PhaseOne H 20",
+	"PhaseOne H 25",
+	"PhaseOne P 20",
+	"PhaseOne P 20+",
+	"PhaseOne P 21",
+	"PhaseOne P 25",
+	"PhaseOne P 25+",
+	"PhaseOne P 30",
+	"PhaseOne P 30+",
+	"PhaseOne P 40+",
+	"PhaseOne P 45",
+	"PhaseOne P 45+",
+	"PhaseOne P 65",
+	"PhaseOne P 65+",
+	"Photron BC2-HD",
+	"Pixelink A782",
+#ifdef USE_X3FTOOLS
+	"Polaroid x530",
+#endif
+	"RaspberryPi Camera",
+	"RaspberryPi Camera V2",
+	"Ricoh GR",
+	"Realme 3 Pro",
+	"Ricoh GR II",
+	"Ricoh GR III",
+	"Ricoh GR Digital",
+	"Ricoh GR Digital II",
+	"Ricoh GR Digital III",
+	"Ricoh GR Digital IV",
+	"Ricoh Caplio GX100",
+	"Ricoh Caplio GX200",
+	"Ricoh GXR Mount A12",
+	"Ricoh GXR GR Lens A12 50mm F2.5 Macro",
+	"Ricoh GXR GR Lens A12 28mm F2.5",
+	"Ricoh GXR Ricoh Lens A16 24-85mm F3.5-5.5",
+	"Ricoh GXR Ricoh Lens S10 24-72mm F2.5-4.4 VC",
+	"Ricoh GXR Ricoh Lens P10 28-300 mm F3.5-5.6 VC",
+#ifndef NO_JASPER
+	"Redcode R3D format",
+#endif
+	"Rollei d530flex",
+	"RoverShot 3320af",
+	"Samsung EX1 / TL500",
+	"Samsung EX2F",
+	"Samsung GX-1L",
+	"Samsung GX-1S",
+	"Samsung GX10",
+	"Samsung GX20",
+	"Samsung Galaxy Nexus",
+	"Samsung Galaxy Note 9",
+	"Samsung Galaxy NX (EK-GN120)",
+	"Samsung Galaxy S3",
+	"Samsung Galaxy S6 (SM-G920F)",
+	"Samsung Galaxy S7",
+	"Samsung Galaxy S7 Edge",
+	"Samsung Galaxy S8 (SM-G950U)",
+	"Samsung Galaxy S9 (SM-G960F)",
+	"Samsung Galaxy S9+ (SM-G965U / 965F)",
+	"Samsung Galaxy S10 (SM-G973F)",
+	"Samsung Galaxy S10+ (SM-G975U)",
+	"Samsung NX1",
+	"Samsung NX5",
+	"Samsung NX10",
+	"Samsung NX11",
+	"Samsung NX100",
+	"Samsung NX1000",
+	"Samsung NX1100",
+	"Samsung NX20",
+	"Samsung NX200",
+	"Samsung NX210",
+	"Samsung NX2000",
+	"Samsung NX30",
+	"Samsung NX300",
+	"Samsung NX300M",
+	"Samsung NX3000",
+	"Samsung NX500",
+	"Samsung NX mini / NXF1",
+	"Samsung Pro815",
+	"Samsung WB550 / WB560 / HZ15W",
+	"Samsung WB2000 / TL350",
+	"Samsung WB5000 / HZ25W",
+	"Samsung S85 (hacked)",
+	"Samsung S850 (hacked)",
+	"Sarnoff 4096x5440",
+	"Seitz 6x17",
+	"Seitz Roundshot D3",
+	"Seitz Roundshot D2X",
+	"Seitz Roundshot D2Xs",
+	"Sigma fp",
+#ifdef USE_X3FTOOLS
+	"Sigma SD9 (raw decode only)",
+	"Sigma SD10 (raw decode only)",
+	"Sigma SD14 (raw decode only)",
+	"Sigma SD15 (raw decode only)",
+	"Sigma SD1",
+	"Sigma SD1 Merrill",
+	"Sigma DP1",
+	"Sigma DP1 Merrill",
+	"Sigma DP1S",
+	"Sigma DP1X",
+	"Sigma DP2",
+	"Sigma DP2 Merrill",
+	"Sigma DP2S",
+	"Sigma DP2X",
+	"Sigma DP3 Merrill",
+	"Sigma dp0 Quattro",
+	"Sigma dp1 Quattro",
+	"Sigma dp2 Quattro",
+	"Sigma dp3 Quattro",
+	"Sigma sd Quattro",
+	"Sigma sd Quattro H",
+#else
+		"Sigma dp0 Quattro (DNG only)",
+		"Sigma dp1 Quattro (DNG only)",
+		"Sigma dp2 Quattro (DNG only)",
+		"Sigma dp3 Quattro (DNG only)",
+		"Sigma sd Quattro (DNG only)",
+		"Sigma sd Quattro H (DNG only)",
+#endif
+	"Sinar eMotion 22",
+	"Sinar eMotion 54",
+	"Sinar eSpirit 65",
+	"Sinar eMotion 75",
+	"Sinar eVolution 75",
+	"Sinar 3072x2048 (Sinarback 23)",
+	"Sinar 4080x4080 (Sinarback 44)",
+	"Sinar 4080x5440",
+	"Sinar STI format",
+	"Sinar Sinarback 54",
+	"SMaL Ultra-Pocket 3",
+	"SMaL Ultra-Pocket 4",
+	"SMaL Ultra-Pocket 5",
+	"Sony ILCE-7 (A7)",
+	"Sony ILCE-7M2 (A7 II)",
+	"Sony ILCE-7M3 (A7 III)",
+	"Sony ILCE-7R (A7R",
+	"Sony ILCE-7RM2 (A7R II)",
+	"Sony ILCE-7RM3 (A7R III)",
+	"Sony ILCE-7RM4 (A7R IV)",
+	"Sony ILCE-7S (A7S)",
+	"Sony ILCE-7S2 (A7S II)",
+	"Sony ILCE-9 (A9)",
+	"Sony ILCE-9M2 (A9 II)",
+	"Sony ILCA-68 (A68)",
+	"Sony ILCA-77M2 (A77-II)",
+	"Sony ILCA-99M2 (A99-II)",
+	"Sony ILCE-3000 / 3500",
+	"Sony ILCE-5000",
+	"Sony ILCE-5100",
+	"Sony ILCE-6000",
+	"Sony ILCE-6100",
+	"Sony ILCE-6300",
+	"Sony ILCE-6400",
+	"Sony ILCE-6500",
+	"Sony ILCE-6600",
+	"Sony ILCE-QX1",
+	"Sony DSC-F828",
+	"Sony DSC-HX95",
+	"Sony DSC-HX99",
+	"Sony DSC-R1",
+	"Sony DSC-RX0",
+	"Sony DSC-RX0 II",
+	"Sony DSC-RX1",
+	"Sony DSC-RX1R",
+	"Sony DSC-RX1R II",
+	"Sony DSC-RX10",
+	"Sony DSC-RX10 II",
+	"Sony DSC-RX10 III",
+	"Sony DSC-RX10 IV",
+	"Sony DSC-RX100",
+	"Sony DSC-RX100 II",
+	"Sony DSC-RX100 III",
+	"Sony DSC-RX100 IV",
+	"Sony DSC-RX100 V",
+	"Sony DSC-RX100 VA",
+	"Sony DSC-RX100 VI",
+	"Sony DSC-RX100 VII",
+	"Sony DSC-V3",
+	"Sony DSLR-A100",
+	"Sony DSLR-A200",
+	"Sony DSLR-A230",
+	"Sony DSLR-A290",
+	"Sony DSLR-A300",
+	"Sony DSLR-A330",
+	"Sony DSLR-A350",
+	"Sony DSLR-A380 / A390",
+	"Sony DSLR-A450",
+	"Sony DSLR-A500",
+	"Sony DSLR-A550",
+	"Sony DSLR-A560",
+	"Sony DSLR-A580",
+	"Sony DSLR-A700",
+	"Sony DSLR-A850",
+	"Sony DSLR-A900",
+	"Sony NEX-3",
+	"Sony NEX-3N",
+	"Sony NEX-5",
+	"Sony NEX-5N",
+	"Sony NEX-5R",
+	"Sony NEX-5T",
+	"Sony NEX-6",
+	"Sony NEX-7",
+	"Sony NEX-C3",
+	"Sony NEX-F3",
+	"Sony NEX-VG20",
+	"Sony NEX-VG30",
+	"Sony NEX-VG900",
+	"Sony SLT-A33",
+	"Sony SLT-A35",
+	"Sony SLT-A37",
+	"Sony SLT-A55(V)",
+	"Sony SLT-A57",
+	"Sony SLT-A58",
+	"Sony SLT-A65(V)",
+	"Sony SLT-A77(V)",
+	"Sony SLT-A99(V)",
+	"Sony XCD-SX910CR",
+	"Sony IMX135-mipi 13mp",
+	"Sony IMX135-QCOM",
+	"Sony IMX072-mipi",
+	"Sony IMX214",
+	"Sony IMX219",
+	"Sony IMX230",
+	"Sony IMX298-mipi 16mp",
+	"Sony IMX219-mipi 8mp",
+	"Sony Xperia L",
+	"STV680 VGA",
+	"PtGrey GRAS-50S5C",
+	"JaiPulnix BB-500CL",
+	"JaiPulnix BB-500GE",
+	"SVS SVS625CL",
+	"Yi M1",
+	"YUNEEC CGO3",
+	"YUNEEC CGO3P",
+	"YUNEEC CGO4",
+	"Xiaomi MI3",
+	"Xiaomi MI 9 Lite",
+	"Xiaomi RedMi Note3 Pro",
+	"Xiaomi RedMi Note7",
+	"Xiaomi FIMI X8SE",
+	"Xiaoyi YIAC3 (YI 4k)",
+	"Zenit M",
+	NULL
+};
+// clang-format on
+
+const char **LibRaw::cameraList() { return static_camera_list; }
+int LibRaw::cameraCount()
+{
+  return (sizeof(static_camera_list) / sizeof(static_camera_list[0])) - 1;
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/tables/colorconst.cpp libkdcraw/libkdcraw/libraw/src/tables/colorconst.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/tables/colorconst.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/tables/colorconst.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,45 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/libraw_cxx_defs.h"
+
+const double LibRaw_constants::xyz_rgb[3][3] = {
+    {0.4124564, 0.3575761, 0.1804375},
+    {0.2126729, 0.7151522, 0.0721750},
+    {0.0193339, 0.1191920, 0.9503041}};
+
+const float LibRaw_constants::d65_white[3] = {0.95047f, 1.0f, 1.08883f};
+
+const double LibRaw_constants::xyzd50_srgb[3][3] = {
+    {0.436083, 0.385083, 0.143055},
+    {0.222507, 0.716888, 0.060608},
+    {0.013930, 0.097097, 0.714022}};
+const double LibRaw_constants::rgb_rgb[3][3] = {
+    {1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
+const double LibRaw_constants::adobe_rgb[3][3] = {
+    {0.715146, 0.284856, 0.000000},
+    {0.000000, 1.000000, 0.000000},
+    {0.000000, 0.041166, 0.958839}};
+const double LibRaw_constants::wide_rgb[3][3] = {
+    {0.593087, 0.404710, 0.002206},
+    {0.095413, 0.843149, 0.061439},
+    {0.011621, 0.069091, 0.919288}};
+const double LibRaw_constants::prophoto_rgb[3][3] = {
+    {0.529317, 0.330092, 0.140588},
+    {0.098368, 0.873465, 0.028169},
+    {0.016879, 0.117663, 0.865457}};
+const double LibRaw_constants::aces_rgb[3][3] = {
+    {0.432996, 0.375380, 0.189317},
+    {0.089427, 0.816523, 0.102989},
+    {0.019165, 0.118150, 0.941914}};
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/tables/colordata.cpp libkdcraw/libkdcraw/libraw/src/tables/colordata.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/tables/colordata.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/tables/colordata.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,1757 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+/*
+   All matrices are from Adobe DNG Converter unless otherwise noted.
+ */
+int LibRaw::adobe_coeff(unsigned make_idx, const char *t_model,
+                        int internal_only)
+{
+  // clang-format off
+  static const struct
+  {
+	  unsigned m_idx;
+	  const char *prefix;
+	  int t_black, t_maximum, trans[12];
+  } table[] = {
+    { LIBRAW_CAMERAMAKER_Agfa, "DC-833m", 0, 0,
+      { 11438,-3762,-1115,-2409,9914,2497,-1227,2295,5300 } }, /* DJC */
+
+    { LIBRAW_CAMERAMAKER_Apple, "QuickTake", 0, 0,
+      { 21392,-5653,-3353,2406,8010,-415,7166,1427,2078 } }, /* DJC */
+
+    { LIBRAW_CAMERAMAKER_Broadcom, "RPi IMX219", 66, 0x3ff,
+      { 5302,1083,-728,-5320,14112,1699,-863,2371,5136 } }, /* LibRaw */
+    { LIBRAW_CAMERAMAKER_Broadcom, "RPi OV5647", 16, 0x3ff,
+      { 12782,-4059,-379,-478,9066,1413,1340,1513,5176 } }, /* DJC */
+	{ LIBRAW_CAMERAMAKER_Broadcom, "Pi", 16, 0x3ff,
+	  { 12782,-4059,-379,-478,9066,1413,1340,1513,5176 } }, /* DJC */
+
+    { LIBRAW_CAMERAMAKER_Canon, "EOS D30", 0, 0,
+      { 9900,-2771,-1324,-7072,14229,3140,-2790,3344,8861 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS D60", 0, 0xfa0,
+      { 6211,-1358,-896,-8557,15766,3012,-3001,3507,8567 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 5DS", 0, 0x3c96, // same CMs: 5DS, "5DS R" */
+      { 6250,-711,-808,-5153,12794,2636,-1249,2198,5610 } }, // v.2
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 5D Mark IV", 0, 0,
+      { 6446,-366,-864,-4436,12204,2513,-952,2496,6348 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 5D Mark III", 0, 0x3c80,
+      { 6722,-635,-963,-4287,12460,2028,-908,2162,5668 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 5D Mark II", 0, 0x3cf0,
+      { 4716,603,-830,-7798,15474,2480,-1496,1937,6651 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 5D", 0, 0xe6c,
+      { 6347,-479,-972,-8297,15954,2480,-1968,2131,7649 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 6D Mark II", 0, 0x38de,
+      { 6875,-970,-932,-4691,12459,2501,-874,1953,5809 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 6D", 0, 0x3c82,
+      { 7034,-804,-1014,-4420,12564,2058,-851,1994,5758 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 77D", 0, 0,
+      { 7377,-742,-998,-4235,11981,2549,-673,1918,5538 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 7D Mark II", 0, 0x3510,
+      { 7268,-1082,-969,-4186,11839,2663,-825,2029,5839 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 7D", 0, 0x3510,
+      { 6844,-996,-856,-3876,11761,2396,-593,1772,6198 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 850D", 0, 0,
+      { 9079,-1923,-1236,-4677,12454,2492,-922,2319,5565}},
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 800D", 0, 0,
+      { 6970,-512,-968,-4425,12161,2553,-739,1982,5601 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 80D", 0, 0,
+      { 7457,-671,-937,-4849,12495,2643,-1213,2354,5492 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 10D", 0, 0xfa0,
+      { 8250,-2044,-1127,-8092,15606,2664,-2893,3453,8348 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 250D", 0, 0,
+      { 9079,-1923,-1236,-4677,12454,2492,-922,2319,5565 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 200D", 0, 0,
+      { 7377,-742,-998,-4235,11981,2549,-673,1918,5538 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 20Da", 0, 0,
+      { 14155,-5065,-1382,-6550,14633,2039,-1623,1824,6561 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 20D", 0, 0xfff,
+      { 6599,-537,-891,-8071,15783,2424,-1983,2234,7462 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 30D", 0, 0,
+      { 6257,-303,-1000,-7880,15621,2396,-1714,1904,7046 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 40D", 0, 0x3f60,
+      { 6071,-747,-856,-7653,15365,2441,-2025,2553,7315 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 50D", 0, 0x3d93,
+      { 4920,616,-593,-6493,13964,2784,-1774,3178,7005 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 60Da", 0, 0x2ff7,
+      { 17492,-7240,-2023,-1791,10323,1701,-186,1329,5406 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 60D", 0, 0x2ff7,
+      { 6719,-994,-925,-4408,12426,2211,-887,2129,6051 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 70D", 0, 0x3bc7,
+      { 7034,-804,-1014,-4420,12564,2058,-851,1994,5758 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 100D", 0, 0x350f,
+      { 6602,-841,-939,-4472,12458,2247,-975,2039,6148 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 300D", 0, 0xfa0,
+      { 8250,-2044,-1127,-8092,15606,2664,-2893,3453,8348 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 350D", 0, 0xfff,
+      { 6018,-617,-965,-8645,15881,2975,-1530,1719,7642 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 3000D", 0, 0,
+      { 6939,-1016,-866,-4428,12473,2177,-1175,2178,6162 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 400D", 0, 0xe8e,
+      { 7054,-1501,-990,-8156,15544,2812,-1278,1414,7796 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 450D", 0, 0x390d,
+      { 5784,-262,-821,-7539,15064,2672,-1982,2681,7427 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 500D", 0, 0x3479,
+      { 4763,712,-646,-6821,14399,2640,-1921,3276,6561 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 550D", 0, 0x3dd7,
+      { 6941,-1164,-857,-3825,11597,2534,-416,1540,6039 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 600D", 0, 0x3510,
+      { 6461,-907,-882,-4300,12184,2378,-819,1944,5931 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 650D", 0, 0x354d,
+      { 6602,-841,-939,-4472,12458,2247,-975,2039,6148 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 750D", 0, 0x3c00,
+      { 6362,-823,-847,-4426,12109,2616,-743,1857,5635 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 760D", 0, 0x3c00,
+      { 6362,-823,-847,-4426,12109,2616,-743,1857,5635 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 700D", 0, 0x3c00,
+      { 6602,-841,-939,-4472,12458,2247,-975,2039,6148 } },
+
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 90D", 0, 0,
+      { 11498, -3759, -1516, -5073, 12954,  2349,  -892,  1867,  6118}}, /* temp */
+
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 1000D", 0, 0xe43,
+      { 6771,-1139,-977,-7818,15123,2928,-1244,1437,7533 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 1100D", 0, 0x3510,
+      { 6444,-904,-893,-4563,12308,2535,-903,2016,6728 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 1200D", 0, 0x37c2,
+      { 6461,-907,-882,-4300,12184,2378,-819,1944,5931 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 1300D", 0, 0x37c2,
+      { 6939,-1016,-866,-4428,12473,2177,-1175,2178,6162 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS 1500D", 0, 0,
+      { 8300,-2110,-1120,-4917,12694,2482,-938,2141,5666 } }, // v.2
+
+    { LIBRAW_CAMERAMAKER_Canon, "EOS RP", 0, 0,
+      { 8608,-2097,-1178,-5425,13265,2383,-1149,2238,5680 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS R", 0, 0,
+      { 8293,-1789,-1094,-5025,12925,2327,-1199,2769,6108 } }, // v.2
+
+    { LIBRAW_CAMERAMAKER_Canon, "EOS M6 Mark II", 0, 0,
+      { 11498, -3759, -1516, -5073, 12954,  2349,  -892,  1867,  6118}}, /* temp */
+
+    { LIBRAW_CAMERAMAKER_Canon, "EOS M6", 0, 0,
+      { 8532,-701,-1167,-4095,11879,2508,-797,2424,7010 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS M50", 0, 0,
+      { 8532,-701,-1167,-4095,11879,2508,-797,2424,7010 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS M5", 0, 0,
+      { 8532,-701,-1167,-4095,11879,2508,-797,2424,7010 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS M3", 0, 0,
+      { 6362,-823,-847,-4426,12109,2616,-743,1857,5635 } },
+
+    { LIBRAW_CAMERAMAKER_Canon, "EOS M200", 0, 0, /* temp */
+      { 8532,-701,-1167,-4095,11879,2508,-797,2424,7010 } },
+
+    { LIBRAW_CAMERAMAKER_Canon, "EOS M2", 0, 0,
+      { 6400,-480,-888,-5294,13416,2047,-1296,2203,6137 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS M100", 0, 0,
+      { 8532,-701,-1167,-4095,11879,2508,-797,2424,7010 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS M10", 0, 0,
+      { 6400,-480,-888,-5294,13416,2047,-1296,2203,6137 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS M", 0, 0,
+      { 6602,-841,-939,-4472,12458,2247,-975,2039,6148 } },
+
+    { LIBRAW_CAMERAMAKER_Canon, "EOS-1Ds Mark III", 0, 0x3bb0,
+      { 5859,-211,-930,-8255,16017,2353,-1732,1887,7448 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS-1Ds Mark II", 0, 0xe80,
+      { 6517,-602,-867,-8180,15926,2378,-1618,1771,7633 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS-1D Mark IV", 0, 0x3bb0,
+      { 6014,-220,-795,-4109,12014,2361,-561,1824,5787 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS-1D Mark III", 0, 0x3bb0,
+      { 6291,-540,-976,-8350,16145,2311,-1714,1858,7326 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS-1D Mark II N", 0, 0xe80,
+      { 6240,-466,-822,-8180,15825,2500,-1801,1938,8042 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS-1D Mark II", 0, 0xe80,
+      { 6264,-582,-724,-8312,15948,2504,-1744,1919,8664 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS-1DS", 0, 0xe20,
+      { 3925,4060,-1739,-8973,16552,2545,-3287,3945,8243 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS-1D C", 0, 0x3c4e,
+      { 6847,-614,-1014,-4669,12737,2139,-1197,2488,6846 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS-1D X Mark III", 0, 0,
+      {  8971, -2022, -1242, -5405, 13249,  2380, -1280,  2483,  6072}},
+    { LIBRAW_CAMERAMAKER_Canon, "EOS-1D X Mark II", 0, 0x3c4e,
+      { 7596,-978,-967,-4808,12571,2503,-1398,2567,5752 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS-1D X", 0, 0x3c4e,
+      { 6847,-614,-1014,-4669,12737,2139,-1197,2488,6846 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS-1D", 0, 0xe20,
+      { 6806,-179,-1020,-8097,16415,1687,-3267,4236,7690 } },
+    { LIBRAW_CAMERAMAKER_Canon, "EOS C500", 853, 0,
+      { 17851,-10604,922,-7425,16662,763,-3660,3636,22278 } }, /* DJC */
+    {LIBRAW_CAMERAMAKER_Canon, "PowerShot 600", 0, 0,
+      { -3822,10019,1311,4085,-157,3386,-5341,10829,4812,-1969,10969,1126 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot A530", 0, 0,
+      { 0 } }, /* don't want the A5 matrix */
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot A50", 0, 0,
+      { -6233,10706,1825,3260,821,3980,-6512,10745,6287,-2539,12232,262 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot A5", 0, 0,
+      { -5707,10308,2002,2662,1829,4139,-6265,11063,6033,-2659,11911,593 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot G10", 0, 0,
+      { 11093,-3906,-1028,-5047,12492,2879,-1003,1750,5561 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot G11", 0, 0,
+      { 12177,-4817,-1069,-1612,9864,2049,-98,850,4471 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot G12", 0, 0,
+      { 13244,-5501,-1248,-1508,9858,1935,-270,1083,4366 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot G15", 0, 0,
+      { 7474,-2301,-567,-4056,11456,2975,-222,716,4181 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot G16", 0, 0,
+      { 8020,-2687,-682,-3704,11879,2052,-965,1921,5556 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot G1 X Mark III", 0, 0,
+      { 8532,-701,-1167,-4095,11879,2508,-797,2424,7010 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot G1 X Mark II", 0, 0,
+      { 7378,-1255,-1043,-4088,12251,2048,-876,1946,5805 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot G1 X", 0, 0,
+      { 7378,-1255,-1043,-4088,12251,2048,-876,1946,5805 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot G1", 0, 0,
+      { -5686,10300,2223,4725,-1157,4383,-6128,10783,6163,-2688,12093,604 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot G2", 0, 0,
+      { 9194,-2787,-1059,-8098,15657,2608,-2610,3064,7867 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot G3 X", 0, 0,
+      { 9701,-3857,-921,-3149,11537,1817,-786,1817,5147 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot G3", 0, 0,
+      { 9326,-2882,-1084,-7940,15447,2677,-2620,3090,7740 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot G5 X Mark II",0, 0,
+      { 11629, -5713, -914, -2706, 11090, 1842, -206, 1225, 5515 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot G5 X",0, 0,
+      { 9602,-3823,-937,-2984,11495,1675,-407,1415,5049 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot G5", 0, 0,
+      { 9869,-2972,-942,-7314,15098,2369,-1898,2536,7282 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot G6", 0, 0,
+      { 9876,-3774,-871,-7613,14807,3071,-1448,1305,7485 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot G7 X Mark III", 0, 0,
+      { 11629, -5713, -914, -2706, 11090, 1842, -206, 1225, 5515 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot G7 X Mark II", 0, 0,
+      { 9602,-3823,-937,-2984,11495,1675,-407,1415,5049 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot G7 X", 0, 0,
+      { 9602,-3823,-937,-2984,11495,1675,-407,1415,5049 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot G9 X Mark II", 0, 0,
+      { 10056,-4131,-944,-2576,11143,1625,-238,1294,5179 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot G9 X",0, 0,
+      { 9602,-3823,-937,-2984,11495,1675,-407,1415,5049 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot G9", 0, 0,
+      { 7368,-2141,-598,-5621,13254,2625,-1418,1696,5743 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot Pro1", 0, 0,
+      { 10062,-3522,-1000,-7643,15117,2730,-765,817,7322 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot Pro70", 34, 0,
+      { -5106,10695,1576,3820,53,4566,-6497,10736,6701,-3336,11887,1394 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot Pro90", 0, 0,
+      { -5912,10768,2288,4612,-989,4333,-6153,10897,5944,-2907,12288,624 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot S30", 0, 0,
+      { 10744,-3813,-1142,-7962,15966,2075,-2492,2805,7744 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot S40", 0, 0,
+      { 8606,-2573,-949,-8237,15489,2974,-2649,3076,9100 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot S45", 0, 0, // +
+      { 8251,-2410,-964,-8047,15430,2823,-2380,2824,8119 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot S50", 0, 0,
+      { 8979,-2658,-871,-7721,15500,2357,-1773,2366,6634 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot S60", 0, 0,
+      { 8794,-2482,-797,-7804,15403,2572,-1422,1996,7083 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot S70", 0, 0,
+      { 9976,-3810,-832,-7115,14463,2906,-901,989,7889 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot S90", 0, 0,
+      { 12374,-5016,-1049,-1677,9902,2078,-83,852,4683 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot S95", 0, 0,
+      { 13440,-5896,-1279,-1236,9598,1931,-180,1001,4651 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot S120", 0, 0,
+      { 6961,-1685,-695,-4625,12945,1836,-1114,2152,5518 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot S110", 0, 0,
+      { 8039,-2643,-654,-3783,11230,2930,-206,690,4194 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot S100", 0, 0,
+      { 7968,-2565,-636,-2873,10697,2513,180,667,4211 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot SX1 IS", 0, 0,
+      { 6578,-259,-502,-5974,13030,3309,-308,1058,4970 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot SX50 HS", 0, 0,
+      { 12432,-4753,-1247,-2110,10691,1629,-412,1623,4926 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot SX60 HS", 0, 0,
+      { 13161,-5451,-1344,-1989,10654,1531,-47,1271,4955 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot SX70 HS", 0, 0,
+      { 18285,-8907,-1951,-1845,10688,1323,364,1101,5139 } },
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot A3300", 0, 0,
+      { 10826,-3654,-1023,-3215,11310,1906,0,999,4960 } }, /* DJC */
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot A470", 0, 0,
+      { 12513,-4407,-1242,-2680,10276,2405,-878,2215,4734 } }, /* DJC */
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot A610", 0, 0,
+      { 15591,-6402,-1592,-5365,13198,2168,-1300,1824,5075 } }, /* DJC */
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot A620", 0, 0,
+      { 15265,-6193,-1558,-4125,12116,2010,-888,1639,5220 } }, /* DJC */
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot A630", 0, 0,
+      { 14201,-5308,-1757,-6087,14472,1617,-2191,3105,5348 } }, /* DJC */
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot A640", 0, 0,
+      { 13124,-5329,-1390,-3602,11658,1944,-1612,2863,4885 } }, /* DJC */
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot A650", 0, 0,
+      { 9427,-3036,-959,-2581,10671,1911,-1039,1982,4430 } }, /* DJC */
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot A720", 0, 0,
+      { 14573,-5482,-1546,-1266,9799,1468,-1040,1912,3810 } }, /* DJC */
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot D10", 127, 0,
+      { 14052,-5229,-1156,-1325,9420,2252,-498,1957,4116 } }, /* DJC */
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot S3 IS", 0, 0,
+      { 14062,-5199,-1446,-4712,12470,2243,-1286,2028,4836 } }, /* DJC */
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot SX110 IS", 0, 0,
+      { 14134,-5576,-1527,-1991,10719,1273,-1158,1929,3581 } }, /* DJC */
+    { LIBRAW_CAMERAMAKER_Canon, "PowerShot SX220", 0, 0,
+      { 13898,-5076,-1447,-1405,10109,1297,-244,1860,3687 } }, /* DJC */
+    { LIBRAW_CAMERAMAKER_Canon, "IXUS 160", 0, 0, // same CamID: "IXUS 160", "ELPH 160"
+      { 11657,-3781,-1136,-3544,11262,2283,-160,1219,4700 } }, /* DJC */
+
+    { LIBRAW_CAMERAMAKER_Casio, "EX-F1", 0, 0,
+      { 9084,-2016,-848,-6711,14351,2570,-1059,1725,6135 } },
+    { LIBRAW_CAMERAMAKER_Casio, "EX-FH100", 0, 0,
+      { 12771,-4179,-1558,-2149,10938,1375,-453,1751,4494 } },
+    { LIBRAW_CAMERAMAKER_Casio, "EX-S20", 0, 0,
+      { 11634,-3924,-1128,-4968,12954,2015,-1588,2648,7206 } }, /* DJC */
+    { LIBRAW_CAMERAMAKER_Casio, "EX-Z750", 0, 0,
+      { 10819,-3873,-1099,-4903,13730,1175,-1755,3751,4632 } }, /* DJC */
+    { LIBRAW_CAMERAMAKER_Casio, "EX-Z10", 128, 0xfff,
+      { 9790,-3338,-603,-2321,10222,2099,-344,1273,4799 } }, /* DJC */
+
+    { LIBRAW_CAMERAMAKER_CINE, "650", 0, 0,
+      { 3390,480,-500,-800,3610,340,-550,2336,1192 } },
+    { LIBRAW_CAMERAMAKER_CINE, "660", 0, 0,
+      { 3390,480,-500,-800,3610,340,-550,2336,1192 } },
+	  { LIBRAW_CAMERAMAKER_CINE, "", 0, 0, /* empty camera name*/
+      { 20183,-4295,-423,-3940,15330,3985,-280,4870,9800 } },
+
+    { LIBRAW_CAMERAMAKER_Contax, "N Digital", 0, 0xf1e,
+      { 7777,1285,-1053,-9280,16543,2916,-3677,5679,7060 } },
+
+    { LIBRAW_CAMERAMAKER_DXO, "ONE", 0, 0,
+      { 6596,-2079,-562,-4782,13016,1933,-970,1581,5181 } },
+
+    { LIBRAW_CAMERAMAKER_Epson, "R-D1", 0, 0, // same CMs: R-D1, R-D1s, R-D1x
+      { 6827,-1878,-732,-8429,16012,2564,-704,592,7145 } },
+
+    { LIBRAW_CAMERAMAKER_Fujifilm, "DBP for GX680", 128, 0x0fff,
+      { 12741,-4916,-1420,-8510,16791,1715,-1767,2302,7771 } }, /* temp, copy from S2Pro */
+
+    { LIBRAW_CAMERAMAKER_Fujifilm, "E550", 0, 0,
+      { 11044,-3888,-1120,-7248,15167,2208,-1531,2276,8069 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "E900", 0, 0,
+      { 9183,-2526,-1078,-7461,15071,2574,-2022,2440,8639 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "F5", 0, 0, // same CamID: F500EXR, F505EXR; different CamID: F550EXR
+      { 13690,-5358,-1474,-3369,11600,1998,-132,1554,4395 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "F6", 0, 0, // same CamID: F600EXR, F605EXR; different CamID: F660EXR
+      { 13690,-5358,-1474,-3369,11600,1998,-132,1554,4395 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "F77", 0, 0xfe9, // same CamID: F770EXR, F775EXR
+      { 13690,-5358,-1474,-3369,11600,1998,-132,1554,4395 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "F7", 0, 0, // same CMs: F700, F710EXR
+      { 10004,-3219,-1201,-7036,15047,2107,-1863,2565,7736 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "F810", 0, 0,
+      { 11044,-3888,-1120,-7248,15167,2208,-1531,2276,8069 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "F8", 0, 0, // F800EXR
+      { 13690,-5358,-1474,-3369,11600,1998,-132,1554,4395 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "F900EXR", 0, 0,
+      { 12085,-4727,-953,-3257,11489,2002,-511,2046,4592 } },
+
+    { LIBRAW_CAMERAMAKER_Fujifilm, "GFX 100", 0, 0,
+      { 16212,-8423,-1583,-4336,12583,1937,-195,726,6199 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "GFX 50", 0, 0, // same CMs: "GFX 50S", "GFX 50R"
+      { 11756,-4754,-874,-3056,11045,2305,-381,1457,6006 } },
+
+    { LIBRAW_CAMERAMAKER_Fujifilm, "HS10 HS11", 0, 0xf68,
+      { 12440,-3954,-1183,-1123,9674,1708,-83,1614,4086 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "HS2", 0, 0, // same CamID: HS20EXR, HS22EXR
+      { 13690,-5358,-1474,-3369,11600,1998,-132,1554,4395 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "HS3", 0, 0, // same CamID: HS30EXR, HS33EXR, HS35EXR
+      { 13690,-5358,-1474,-3369,11600,1998,-132,1554,4395 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "HS50EXR", 0, 0,
+      { 12085,-4727,-953,-3257,11489,2002,-511,2046,4592 } },
+
+    { LIBRAW_CAMERAMAKER_Fujifilm, "IS-1", 0, 0,
+      { 21461,-10807,-1441,-2332,10599,1999,289,875,7703 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "IS Pro", 0, 0,
+      { 12300,-5110,-1304,-9117,17143,1998,-1947,2448,8100 } },
+
+    { LIBRAW_CAMERAMAKER_Fujifilm, "S5000", 0, 0,
+      { 8754,-2732,-1019,-7204,15069,2276,-1702,2334,6982 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "S5100", 0, 0,
+      { 11940,-4431,-1255,-6766,14428,2542,-993,1165,7421 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "S5200", 0, 0, // same CamID: S5200, S5600
+      { 9636,-2804,-988,-7442,15040,2589,-1803,2311,8621 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "S6", 0, 0, // same CamID: S6000fd, S6500fd
+      { 12628,-4887,-1401,-6861,14996,1962,-2198,2782,7091 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "S7000", 0, 0,
+      { 10190,-3506,-1312,-7153,15051,2238,-2003,2399,7505 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "S9000", 0, 0, // same CamID: S9000, S9500
+      { 10491,-3423,-1145,-7385,15027,2538,-1809,2275,8692 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "S9100", 0, 0, // same CamID: S9100, S9600
+      { 12343,-4515,-1285,-7165,14899,2435,-1895,2496,8800 } },
+
+    { LIBRAW_CAMERAMAKER_Fujifilm, "S100FS", 514, 0,
+      { 11521,-4355,-1065,-6524,13767,3058,-1466,1984,6045 } },
+
+    { LIBRAW_CAMERAMAKER_Fujifilm, "S20Pro", 0, 0,
+      { 10004,-3219,-1201,-7036,15047,2107,-1863,2565,7736 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "S20", 512, 0x3fff, // same CamID: S200EXR, S205EXR
+      { 11401,-4498,-1312,-5088,12751,2613,-838,1568,5941 } },
+
+    { LIBRAW_CAMERAMAKER_Fujifilm, "SL1000", 0, 0,
+      { 11705,-4262,-1107,-2282,10791,1709,-555,1713,4945 } },
+
+    { LIBRAW_CAMERAMAKER_Fujifilm, "S1", 0, 0,
+      { 12297,-4882,-1202,-2106,10691,1623,-88,1312,4790 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "S2Pro", 128, 0,
+      { 12741,-4916,-1420,-8510,16791,1715,-1767,2302,7771 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "S3Pro", 0, 0,
+      { 11807,-4612,-1294,-8927,16968,1988,-2120,2741,8006 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "S5Pro", 0, 0,
+      { 12300,-5110,-1304,-9117,17143,1998,-1947,2448,8100 } },
+
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X100F", 0, 0,
+      { 11434,-4948,-1210,-3746,12042,1903,-666,1479,5235 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X100S", 0, 0,
+      { 10592,-4262,-1008,-3514,11355,2465,-870,2025,6386 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X100T", 0, 0,
+      { 10592,-4262,-1008,-3514,11355,2465,-870,2025,6386 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X100V", 0, 0,
+      { 13426,-6334,-1177,-4244,12136,2371,580,1303,5980 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X100", 0, 0,
+      { 12161,-4457,-1069,-5034,12874,2400,-795,1724,6904 } },
+
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X10", 0, 0,
+      { 13509,-6199,-1254,-4430,12733,1865,-331,1441,5022 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X20", 0, 0,
+      { 11768,-4971,-1133,-4904,12927,2183,-480,1723,4605 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X30", 0, 0,
+      { 12328,-5256,-1144,-4469,12927,1675,-87,1291,4351 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X70", 0, 0,
+      { 10450,-4329,-878,-3217,11105,2421,-752,1758,6519 } },
+
+    { LIBRAW_CAMERAMAKER_Fujifilm, "XF10", 0, 0,
+      { 11673,-4760,-1041,-3988,12058,2166,-771,1417,5569 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "XF1", 0, 0,
+      { 13509,-6199,-1254,-4430,12733,1865,-331,1441,5022 } },
+
+    { LIBRAW_CAMERAMAKER_Fujifilm, "XQ", 0, 0,  // same CMs: XQ1, XQ2
+      { 9252,-2704,-1064,-5893,14265,1717,-1101,2341,4349 } },
+
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X-Pro1", 0, 0,
+      { 10413,-3996,-993,-3721,11640,2361,-733,1540,6011 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X-Pro2", 0, 0,
+      { 11434,-4948,-1210,-3746,12042,1903,-666,1479,5235 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X-Pro3", 0, 0,
+      { 13426,-6334,-1177,-4244,12136,2371,580,1303,5980 } },
+
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X-A10", 0, 0,
+      { 11540,-4999,-991,-2949,10963,2278,-382,1049,5605} },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X-A20", 0, 0,
+      { 11540,-4999,-991,-2949,10963,2278,-382,1049,5605} },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X-A1", 0, 0,
+      { 11086,-4555,-839,-3512,11310,2517,-815,1341,5940 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X-A2", 0, 0,
+      { 10763,-4560,-917,-3346,11311,2322,-475,1135,5843 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X-A3", 0, 0,
+      { 12407,-5222,-1086,-2971,11116,2120,-294,1029,5284 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X-A5", 0, 0,
+      { 11673,-4760,-1041,-3988,12058,2166,-771,1417,5569 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X-A7", 0, 0,
+      { 15055,-7391,-1274,-4062,12071,2238,-610,1217,6147 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X-E1", 0, 0,
+      { 10413,-3996,-993,-3721,11640,2361,-733,1540,6011 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X-E2S", 0, 0,
+      { 11562,-5118,-961,-3022,11007,2311,-525,1569,6097 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X-E2", 0, 0,
+      { 8458,-2451,-855,-4597,12447,2407,-1475,2482,6526 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X-E3", 0, 0,
+      { 11434,-4948,-1210,-3746,12042,1903,-666,1479,5235 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X-H1", 0, 0,
+      { 11434,-4948,-1210,-3746,12042,1903,-666,1479,5235 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X-M1", 0, 0,
+      { 10413,-3996,-993,-3721,11640,2361,-733,1540,6011 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X-S1", 0, 0,
+      { 13509,-6199,-1254,-4430,12733,1865,-331,1441,5022 } },
+
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X-T100", 0, 0,
+      { 11673,-4760,-1041,-3988,12058,2166,-771,1417,5569 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X-T1", 0, 0, /* same CMs: X-T1, "X-T1IR", "X-T1 IR", X-T10 */
+      { 8458,-2451,-855,-4597,12447,2407,-1475,2482,6526 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X-T200", 0, 0,
+      { 15055,-7391,-1274,-4062,12071,2238,-610,1217,6147 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X-T2", 0, 0,  // same CMs: X-T2, X-T20
+      { 11434,-4948,-1210,-3746,12042,1903,-666,1479,5235 } },
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X-T3", 0, 0,   // same CMs: X-T3, X-T30
+      { 13426,-6334,-1177,-4244,12136,2371,580,1303,5980 } }, // v.2
+    { LIBRAW_CAMERAMAKER_Fujifilm, "X-T4", 0, 0,
+      { 13426,-6334,-1177,-4244,12136,2371,580,1303,5980 } },
+
+    { LIBRAW_CAMERAMAKER_GITUP, "G3DUO", 130, 62000,
+       { 8489,-2583,-1036,-8051,15583,2643,-1307,1407,7354 } },
+
+    { LIBRAW_CAMERAMAKER_GITUP, "GIT2P", 4160, 0,
+      { 8489,-2583,-1036,-8051,15583,2643,-1307,1407,7354 } },
+    { LIBRAW_CAMERAMAKER_GITUP, "GIT2", 3200, 0,
+      { 8489,-2583,-1036,-8051,15583,2643,-1307,1407,7354 } },
+
+    { LIBRAW_CAMERAMAKER_GoPro, "HERO5 Black", 0, 0,
+	    { 10344,-4210,-620,-2315,10625,1948,93,1058,5541 } },
+
+    {LIBRAW_CAMERAMAKER_Hasselblad, "L1D-20c", 0, 0,
+      {  7310, -2746,  -646, -2991, 10847,  2469,   163,   585,  6324}},
+
+    { LIBRAW_CAMERAMAKER_Hasselblad, "16-Uncoated-3FR", 0, 0,
+      {  8519, -3260,  -280, -5081, 13459,  1738, -1449,  2960,  7809}},
+    { LIBRAW_CAMERAMAKER_Hasselblad, "16-Uncoated-FFF", 0, 0,
+      {  8068, -2959,  -108, -5788, 13608,  2389, -1002,  2237,  8162}},
+    { LIBRAW_CAMERAMAKER_Hasselblad, "16-Uncoated", 0, 0,
+      { 8519,-3260,-280,-5081,13459,1738,-1449,2960,7809 } },
+
+    { LIBRAW_CAMERAMAKER_Hasselblad, "22-Uncoated-3FR", 0, 0,
+      {  8523, -3257,  -280, -5078, 13458,  1743, -1449,  2961,  7809}},
+    { LIBRAW_CAMERAMAKER_Hasselblad, "22-Uncoated-FFF", 0, 0,
+      {  8068, -2959,  -108, -5788, 13608,  2389, -1002,  2237,  8162}},
+    { LIBRAW_CAMERAMAKER_Hasselblad, "22-Uncoated", 0, 0,
+      { 8519,-3260,-280,-5081,13459,1738,-1449,2960,7809 } },
+
+    {LIBRAW_CAMERAMAKER_Hasselblad, "31-Uncoated-FFF", 0, 0,
+      {  5155, -1201,   200, -5841, 13197,  2950, -1101,  2317,  6988}},
+    {LIBRAW_CAMERAMAKER_Hasselblad, "31-Uncoated", 0, 0,
+      {  5458, -1448,   145, -4479, 12338,  2401, -1659,  3086,  6710}},
+
+    {LIBRAW_CAMERAMAKER_Hasselblad, "39-Uncoated-3FR", 0, 0,
+      {  3904,  -100,   262, -4318, 12407,  2128, -1598,  3594,  6233}},
+    {LIBRAW_CAMERAMAKER_Hasselblad, "39-Uncoated-FFF", 0, 0,
+      {  4739,  -932,   295, -4829, 12220,  2952, -1027,  2341,  7083}},
+    {LIBRAW_CAMERAMAKER_Hasselblad, "39-Uncoated", 0, 0,
+      {  3894,  -110,   287, -4672, 12610,  2295, -2092,  4100,  6196}},
+
+    { LIBRAW_CAMERAMAKER_Hasselblad, "39-Coated-3FR", 0, 0,
+      {  5427, -1147,   173, -3834, 12073,  1969, -1444,  3320,  5621}},
+    { LIBRAW_CAMERAMAKER_Hasselblad, "39-Coated-FFF", 0, 0,
+      {  5323, -1233,   399, -4926, 12362,  2894,  -856,  2471,  5961}},
+    { LIBRAW_CAMERAMAKER_Hasselblad, "39-Coated", 0, 0,
+      { 3857,452,-46,-6008,14477,1596,-2627,4481,5718 } },
+
+    {LIBRAW_CAMERAMAKER_Hasselblad, "40-Coated5-3FR", 0, 0,
+      {  7014, -2067,  -540, -4821, 13016,  1980, -1663,  3089,  6940}},
+    {LIBRAW_CAMERAMAKER_Hasselblad, "40-Coated5-FFF", 0, 0,
+      {  5963, -1357,  -172, -5439, 12762,  3007,  -964,  2222,  7172}},
+    {LIBRAW_CAMERAMAKER_Hasselblad, "40-Coated5", 0, 0,
+      {  6159, -1402,  -177, -5439, 12762,  3007,  -955,  2200,  7104}},
+
+    { LIBRAW_CAMERAMAKER_Hasselblad, "40-Coated-3FR", 0, 0,
+      {  6550, -1681,  -399, -4626, 12598,  2257, -1807,  3354,  6486}},
+    { LIBRAW_CAMERAMAKER_Hasselblad, "40-Coated-FFF", 0, 0,
+      {  6041, -1375,  -174, -5439, 10000,  3007,  -930,  2145,  6923}},
+    { LIBRAW_CAMERAMAKER_Hasselblad, "40-Coated", 0, 0,
+	    { 6159,-1402,-177,-5439,12762,3007,-955,2200,7104 } },
+
+    { LIBRAW_CAMERAMAKER_Hasselblad, "50-Coated5-3FR", 0, 0,
+      {  5707,  -693,  -382, -4285, 12669,  1773, -1615,  3519,  5410}},
+    { LIBRAW_CAMERAMAKER_Hasselblad, "50-Coated5-FFF", 0, 0,
+      {  5263,  -612,    39, -4950, 12426,  2843,  -935,  2423,  5941}},
+    { LIBRAW_CAMERAMAKER_Hasselblad, "50-Coated5", 0, 0,
+      {  5656,  -659,  -346, -3923, 12306,  1791, -1602,  3509,  5442}},
+
+    { LIBRAW_CAMERAMAKER_Hasselblad, "50-Coated-3FR", 0, 0,
+      {  5656,  -659,  -346, -3923, 12305,  1790, -1602,  3509,  5442}},
+    { LIBRAW_CAMERAMAKER_Hasselblad, "50-Coated-FFF", 0, 0,
+      {  5280,  -614,    39, -4950, 12426,  2843,  -939,  2434,  5968}},
+    { LIBRAW_CAMERAMAKER_Hasselblad, "50-Coated", 0, 0,
+	    { 5656,-659,-346,-3923,12306,1791,-1602,3509,5442 } },
+
+    { LIBRAW_CAMERAMAKER_Hasselblad, "50-15-Coated5-II-3FR", 0, 0,
+      { 10887, -6152,  1034, -3564, 12412,  4224,    63,   626, 10123}},
+    { LIBRAW_CAMERAMAKER_Hasselblad, "50-15-Coated5-II-FFF", 0, 0,
+      {  4932,  -835,   141, -4878, 11868,  3437, -1138,  1961,  7067}},
+    { LIBRAW_CAMERAMAKER_Hasselblad, "50-15-Coated5-II", 0, 0,
+      {  8737, -4937,   830, -2860,  9961,  3390,    51,   502,  8124}},
+
+    { LIBRAW_CAMERAMAKER_Hasselblad, "50-15-Coated5", 0, 0,
+	    { 4932,-835,141,-4878,11868,3437,-1138,1961,7067 } },
+
+    { LIBRAW_CAMERAMAKER_Hasselblad, "60-Coated-3FR", 0, 0,
+      {  9296,   336, -1088, -6442, 14323,  2289, -1433,  2942,  5756}},
+    { LIBRAW_CAMERAMAKER_Hasselblad, "60-Coated", 0, 0,
+	    { 9662,-684,-279,-4903,12293,2950,-344,1669,6024 } },
+
+    { LIBRAW_CAMERAMAKER_Hasselblad, "100-17-Coated5", 0, 0,
+      {  5110, -1357,  -308, -5573, 12835,  3077, -1279,  2025,  7010}},
+
+    { LIBRAW_CAMERAMAKER_HTC, "One A9", 64, 1023,
+      { 101,-20,-2,-11,145,41,-24,1,56 } }, /* this is FM1 transposed */
+
+    { LIBRAW_CAMERAMAKER_Imacon, "Ixpress", 0, 0,
+      { 7025,-1415,-704,-5188,13765,1424,-1248,2742,6038 } }, /* DJC */
+
+    { LIBRAW_CAMERAMAKER_Kodak, "NC2000", 0, 0, // AP Nikon
+      { 13891,-6055,-803,-465,9919,642,2121,82,1291 } },
+    { LIBRAW_CAMERAMAKER_Kodak, "DCS315C", -8, 0,
+      { 17523,-4827,-2510,756,8546,-137,6113,1649,2250 } },
+    { LIBRAW_CAMERAMAKER_Kodak, "DCS330C", -8, 0,
+      { 20620,-7572,-2801,-103,10073,-396,3551,-233,2220 } },
+    { LIBRAW_CAMERAMAKER_Kodak, "DCS420", 0, 0,
+      { 10868,-1852,-644,-1537,11083,484,2343,628,2216 } },
+    { LIBRAW_CAMERAMAKER_Kodak, "DCS46", 0, 0, // same CM as EOSDCS1 and DCS465 DB
+      { 10592,-2206,-967,-1944,11685,230,2206,670,1273 } },
+    { LIBRAW_CAMERAMAKER_Kodak, "DCS520C", -178, 0, // same CamID: DCS520C, "EOS D2000C"
+      { 24542,-10860,-3401,-1490,11370,-297,2858,-605,3225 } },
+    { LIBRAW_CAMERAMAKER_Kodak, "DCS560C", -177, 0, // same CamID: DCS560C, "EOS D6000C"
+      { 20482,-7172,-3125,-1033,10410,-285,2542,226,3136 } },
+    { LIBRAW_CAMERAMAKER_Kodak, "DCS620C", -177, 0,
+      { 23617,-10175,-3149,-2054,11749,-272,2586,-489,3453 } },
+    { LIBRAW_CAMERAMAKER_Kodak, "DCS620X", -176, 0,
+      { 13095,-6231,154,12221,-21,-2137,895,4602,2258 } },
+    { LIBRAW_CAMERAMAKER_Kodak, "DCS660C", -173, 0,
+      { 18244,-6351,-2739,-791,11193,-521,3711,-129,2802 } },
+    { LIBRAW_CAMERAMAKER_Kodak, "DCS720X", 0, 0,
+      { 11775,-5884,950,9556,1846,-1286,-1019,6221,2728 } },
+    { LIBRAW_CAMERAMAKER_Kodak, "DCS760C", 0, 0,
+      { 16623,-6309,-1411,-4344,13923,323,2285,274,2926 } },
+    { LIBRAW_CAMERAMAKER_Kodak, "DCS Pro SLR", 0, 0,
+      { 5494,2393,-232,-6427,13850,2846,-1876,3997,5445 } },
+    { LIBRAW_CAMERAMAKER_Kodak, "DCS Pro 14nx", 0, 0,
+      { 5494,2393,-232,-6427,13850,2846,-1876,3997,5445 } },
+    { LIBRAW_CAMERAMAKER_Kodak, "DCS Pro 14", 0, 0, // same CamID: "DCS Pro 14N", "Photo Control Camerz ZDS 14"
+      { 7791,3128,-776,-8588,16458,2039,-2455,4006,6198 } },
+    { LIBRAW_CAMERAMAKER_Kodak, "EOSDCS1", 0, 0,
+      { 10592,-2206,-967,-1944,11685,230,2206,670,1273 } },
+    { LIBRAW_CAMERAMAKER_Kodak, "EOSDCS3", 0, 0,
+      { 9898,-2700,-940,-2478,12219,206,1985,634,1031 } },
+    { LIBRAW_CAMERAMAKER_Kodak, "ProBack645", 0, 0,
+      { 16414,-6060,-1470,-3555,13037,473,2545,122,4948 } },
+    { LIBRAW_CAMERAMAKER_Kodak, "ProBack", 0, 0,
+      { 21179,-8316,-2918,-915,11019,-165,3477,-180,4210 } },
+
+    {LIBRAW_CAMERAMAKER_Kodak, "PIXPRO AZ901", 0, 0,	// dng
+      { 21875, -8006, -2558,   634,  8194,  1104,  1535,   951,  6969}},
+    { LIBRAW_CAMERAMAKER_Kodak, "P712", 0, 3963,
+      { 9658,-3314,-823,-5163,12695,2768,-1342,1843,6044 } },
+    { LIBRAW_CAMERAMAKER_Kodak, "P850", 0, 3964,
+      { 10511,-3836,-1102,-6946,14587,2558,-1481,1792,6246 } },
+    { LIBRAW_CAMERAMAKER_Kodak, "P880", 0, 3963,
+      { 12805,-4662,-1376,-7480,15267,2360,-1626,2194,7904 } },
+    { LIBRAW_CAMERAMAKER_Kodak, "Z980", 0, 0,
+      { 11313,-3559,-1101,-3893,11891,2257,-1214,2398,4908 } },
+    { LIBRAW_CAMERAMAKER_Kodak, "Z981", 0, 0,
+      { 12729,-4717,-1188,-1367,9187,2582,274,860,4411 } },
+    { LIBRAW_CAMERAMAKER_Kodak, "Z990", 0, 0xfed,
+      { 11749,-4048,-1309,-1867,10572,1489,-138,1449,4522 } },
+    { LIBRAW_CAMERAMAKER_Kodak, "Z1015", 0, 0xef1,
+      { 11265,-4286,-992,-4694,12343,2647,-1090,1523,5447 } },
+
+    {LIBRAW_CAMERAMAKER_Leaf, "AFi 54S", 0, 0,
+      {  8236,  1746, -1313, -8251, 15953,  2428, -3672,  5786,  5771}},
+    {LIBRAW_CAMERAMAKER_Leaf, "AFi 65S", 0, 0,
+      {  7914,  1414, -1190, -8776, 16582,  2280, -2811,  4605,  5562}},
+    {LIBRAW_CAMERAMAKER_Leaf, "AFi 75S", 0, 0,
+      {  7914,  1414, -1190, -8776, 16582,  2280, -2811,  4605,  5562}},
+    {LIBRAW_CAMERAMAKER_Leaf, "Aptus 17", 0, 0,
+      {  8236,  1746, -1313, -8251, 15953,  2428, -3672,  5786,  5771}},
+    {LIBRAW_CAMERAMAKER_Leaf, "Aptus 22", 0, 0,
+      {  8236,  1746, -1313, -8251, 15953,  2428, -3672,  5786,  5771}},
+    {LIBRAW_CAMERAMAKER_Leaf, "Aptus 54S", 0, 0,
+      {  8236,  1746, -1313, -8251, 15953,  2428, -3672,  5786,  5771}},
+    {LIBRAW_CAMERAMAKER_Leaf, "Aptus 65S", 0, 0,
+      {  7914,  1414, -1190, -8776, 16582,  2280, -2811,  4605,  5562}},
+    {LIBRAW_CAMERAMAKER_Leaf, "Aptus 65", 0, 0,
+      {  7914,  1414, -1190, -8776, 16582,  2280, -2811,  4605,  5562}},
+    {LIBRAW_CAMERAMAKER_Leaf, "Aptus 75S", 0, 0,
+      {  7914,  1414, -1190, -8776, 16582,  2280, -2811,  4605,  5562}},
+    {LIBRAW_CAMERAMAKER_Leaf, "Aptus 75", 0, 0,
+      {  7914,  1414, -1190, -8776, 16582,  2280, -2811,  4605,  5562}},
+    {LIBRAW_CAMERAMAKER_Leaf, "C-Most", 0, 0,
+      {  3952,  2188,   449, -6701, 14584,  2275, -4536,  7349,  6535}},
+    {LIBRAW_CAMERAMAKER_Leaf, "Credo 40", 0, 0,
+      {  8035,   435,  -962, -6001, 13872,  2320, -1159,  3065,  5434}},
+    {LIBRAW_CAMERAMAKER_Leaf, "Credo 50", 0, 0, // emb
+      { 10325,   845,  -604, -4113, 13385,   481, -1791,  4163,  6924}},
+    {LIBRAW_CAMERAMAKER_Leaf, "Credo 60", 0, 0,
+      {  8035,   435,  -962, -6001, 13872,  2320, -1159,  3065,  5434}},
+    {LIBRAW_CAMERAMAKER_Leaf, "Credo 80", 0, 0,
+      {  6294,   686,  -712, -5435, 13417,  2211, -1006,  2435,  5042}},
+    {LIBRAW_CAMERAMAKER_Leaf, "Valeo 11", 0, 0,
+      {  8236,  1746, -1313, -8251, 15953,  2428, -3672,  5786,  5771}},
+    {LIBRAW_CAMERAMAKER_Leaf, "Valeo 17", 0, 0,
+      {  8236,  1746, -1313, -8251, 15953,  2428, -3672,  5786,  5771}},
+    {LIBRAW_CAMERAMAKER_Leaf, "Valeo 22", 0, 0,
+      {  8236,  1746, -1313, -8251, 15953,  2428, -3672,  5786,  5771}},
+    {LIBRAW_CAMERAMAKER_Leaf, "Valeo 6", 0, 0,
+      {  3952,  2188,   449, -6701, 14584,  2275, -4536,  7349,  6535}},
+
+//     { LIBRAW_CAMERAMAKER_Leaf, "AFi-II 6", 0, 0,
+    { LIBRAW_CAMERAMAKER_Leaf, "AFi-II 7", 0, 0,
+      { 7691,-108,-339,-6185,13627,2833,-2046,3899,5952 } },
+    { LIBRAW_CAMERAMAKER_Leaf, "AFi-II 10", 0, 0,
+      { 6719,1147,-148,-6929,14061,3176,-1781,3343,5424 } },
+
+    { LIBRAW_CAMERAMAKER_Leaf, "Aptus-II 5", 0, 0,
+      { 7914,1414,-1190,-8777,16582,2280,-2811,4605,5562 } },
+    { LIBRAW_CAMERAMAKER_Leaf, "Aptus-II 6", 0, 0,
+      { 7989,-113,-352,-6185,13627,2833,-2028,3866,5901 } },
+    { LIBRAW_CAMERAMAKER_Leaf, "Aptus-II 7", 0, 0,
+      { 8209,-116,-362,-6185,13627,2833,-1962,3740,5709 } },
+    { LIBRAW_CAMERAMAKER_Leaf, "Aptus-II 8", 0, 0,
+      { 7361,1257,-163,-6929,14061,3176,-1839,3454,5603 } },
+    { LIBRAW_CAMERAMAKER_Leaf, "Aptus-II 10R", 0, 0,
+      { 7167,1224,-158,-6929,14061,3176,-1826,3429,5562 } },
+    { LIBRAW_CAMERAMAKER_Leaf, "Aptus-II 10", 0, 0,
+      { 7527,1285,-166,-6929,14061,3176,-1995,3747,6077 } },
+//     { LIBRAW_CAMERAMAKER_Leaf, "Aptus-II 12R", 0, 0,
+    { LIBRAW_CAMERAMAKER_Leaf, "Aptus-II 12", 0, 0,
+      { 7361,1257,-163,-6929,14061,3176,-1695,3182,5162 } },
+
+    { LIBRAW_CAMERAMAKER_Leica, "CL", 0, 0,
+      { 7743,-2896,-921,-4211,12271,2169,-697,1562,5491 } },
+
+    { LIBRAW_CAMERAMAKER_Leica, "M8", 0, 0,
+      { 7675,-2196,-305,-5860,14119,1856,-2425,4006,6578 } },
+    { LIBRAW_CAMERAMAKER_Leica, "M9", 0, 0,
+      { 6687,-1751,-291,-3556,11373,2492,-548,2204,7146 } },
+    { LIBRAW_CAMERAMAKER_Leica, "M10", 0, 0, // same CMs: M10, M10-D, M10-P
+      { 9090,-3342,-740,-4006,13456,493,-569,2266,6871 } },
+    { LIBRAW_CAMERAMAKER_Leica, "M (Typ 2", 0, 0, // same CMs: "M (Typ 240)", "M (Typ 262)", "M-D (Typ 262)"
+      { 7199,-2140,-712,-4005,13327,649,-810,2521,6673 } },
+
+    { LIBRAW_CAMERAMAKER_Leica, "Q (Typ 116)", 0, 0,
+      { 10068,-4043,-1068,-5319,14268,1044,-765,1701,6522 } },
+    { LIBRAW_CAMERAMAKER_Leica, "Q2", 0, 0,
+      { 12312,-5440,-1307,-6408,15499,824,-1075,1677,7220 } },
+
+    { LIBRAW_CAMERAMAKER_Leica, "SL (Typ 601)", 0, 0,
+      { 11865,-4523,-1441,-5423,14458,935,-1587,2687,4830 } },
+    { LIBRAW_CAMERAMAKER_Leica, "S (Typ 007)", 0, 0,
+      { 6063,-2234,-231,-5210,13787,1500,-1043,2866,6997 } },
+    { LIBRAW_CAMERAMAKER_Leica, "S2", 0, 0,
+      { 5627,-721,-447,-4423,12456,2192,-1048,2948,7379 } },
+    { LIBRAW_CAMERAMAKER_Leica, "S3", 0, 0,
+//      { 5147,-1464,-318,-5374,13263,2325,-1425,2918,6450 } },
+      { 5092,-1630,-470,-6313,14297,2170,-1603,3135,5982 } },
+    {LIBRAW_CAMERAMAKER_Leica, "S", 0, 0, // same CMs: "S-E (Typ 006)", "S (Typ 006)"
+      { 5749,-1072,-382,-4274,12432,2048,-1166,3104,7105 } },
+
+    { LIBRAW_CAMERAMAKER_Leica, "TL2", 0, 0,
+      { 6375,-2062,-732,-4878,12838,2262,-877,1705,6204 } },
+    { LIBRAW_CAMERAMAKER_Leica, "T", 0, 0, // same CMs: TL, "T (Typ 701)"
+      { 6295,-1679,-475,-5586,13046,2837,-1410,1889,7075 } },
+
+    { LIBRAW_CAMERAMAKER_Leica, "X2", 0, 0,
+      { 8336,-2853,-699,-4425,11989,2760,-954,1625,6396 } },
+    { LIBRAW_CAMERAMAKER_Leica, "X1", 0, 0,
+      { 9055,-2611,-666,-4906,12652,2519,-555,1384,7417 } },
+    { LIBRAW_CAMERAMAKER_Leica, "X", 0, 0, /*  same CMs: "X (Typ 113)", "X-U (Typ 113)", XV, "X Vario (Typ 107)" */
+      { 9062,-3198,-828,-4065,11772,2603,-761,1468,6458 } },
+
+    { LIBRAW_CAMERAMAKER_Mamiya,"ZD", 0, 0,
+      { 7645,2579,-1363,-8689,16717,2015,-3712,5941,5961 } },
+
+    { LIBRAW_CAMERAMAKER_Micron, "2010", 110, 0,
+      { 16695,-3761,-2151,155,9682,163,3433,951,4904 } }, /* DJC */
+
+    { LIBRAW_CAMERAMAKER_Minolta, "DiMAGE 5", 0, 0xf7d,
+      { 9117,-3063,-973,-7949,15763,2306,-2752,3136,8093 } },
+    { LIBRAW_CAMERAMAKER_Minolta, "DiMAGE 7Hi", 0, 0xf7d,
+      { 11555,-4064,-1256,-7903,15633,2409,-2811,3320,7358 } },
+    { LIBRAW_CAMERAMAKER_Minolta, "DiMAGE 7i", 0, 0xf7d,
+      { 11050,-3791,-1199,-7875,15585,2434,-2797,3359,7560 } },
+    { LIBRAW_CAMERAMAKER_Minolta, "DiMAGE 7", 0, 0xf7d,
+      { 9258,-2879,-1008,-8076,15847,2351,-2806,3280,7821 } },
+    { LIBRAW_CAMERAMAKER_Minolta, "DiMAGE A1", 0, 0xf8b,
+      { 9274,-2548,-1167,-8220,16324,1943,-2273,2721,8340 } },
+    { LIBRAW_CAMERAMAKER_Minolta, "DiMAGE A200", 0, 0,
+      { 8560,-2487,-986,-8112,15535,2771,-1209,1324,7743 } },
+    { LIBRAW_CAMERAMAKER_Minolta, "DiMAGE A2", 0, 0xf8f,
+      { 9097,-2726,-1053,-8073,15506,2762,-966,981,7763 } },
+    { LIBRAW_CAMERAMAKER_Minolta, "DiMAGE Z2", 0, 0,
+      { 11280,-3564,-1370,-4655,12374,2282,-1423,2168,5396 } }, /* DJC */
+    { LIBRAW_CAMERAMAKER_Minolta, "DG-5D", 0, 0xffb, // same CamID: "ALPHA 5D", "DYNAX 5D", "MAXXUM 5D", "Alpha Sweet Digital"
+      { 10284,-3283,-1086,-7957,15762,2316,-829,882,6644 } },
+    { LIBRAW_CAMERAMAKER_Minolta, "DG-7D", 0, 0xffb, // same CamID: "ALPHA 7D", "DYNAX 7D", "MAXXUM 7D"
+      { 10239,-3104,-1099,-8037,15727,2451,-927,925,6871 } },
+
+    { LIBRAW_CAMERAMAKER_Motorola, "PIXL", 0, 0,
+      { 8898,-989,-1033,-3292,11619,1674,-661,3178,5216 } }, /* DJC */
+
+    { LIBRAW_CAMERAMAKER_Nikon, "1 AW1", 0, 0,
+      { 6588,-1305,-693,-3277,10987,2634,-355,2016,5106 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "1 J3", 0, 0,
+      { 6588,-1305,-693,-3277,10987,2634,-355,2016,5106 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "1 J4", 0, 0,
+      { 5958,-1559,-571,-4021,11453,2939,-634,1548,5087 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "1 J5", 0, 0,
+      { 7520,-2518,-645,-3844,12102,1945,-913,2249,6835 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "1 S2", -200, 0,
+      { 6612,-1342,-618,-3338,11055,2623,-174,1792,5075 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "1 V2", 0, 0,
+      { 6588,-1305,-693,-3277,10987,2634,-355,2016,5106 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "1 V3", -200, 0,
+      { 5958,-1559,-571,-4021,11453,2939,-634,1548,5087 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "1 ", 0, 0,      /* same CMs: "1 J1", "1 J2", "1 S1", "1 V1" */
+      { 8994,-2667,-865,-4594,12324,2552,-699,1786,6260 } },
+
+    { LIBRAW_CAMERAMAKER_Nikon, "COOLPIX 2100", 0, 0, // a.k.a. E2100
+      { 13142,-4152,-1596,-4655,12374,2282,-1769,2696,6711 } }, /* DJC, copied from Z2, new white balance */
+    { LIBRAW_CAMERAMAKER_Nikon, "COOLPIX 2500", 0, 0, // a.k.a. E2500, possibly same CM as for E5000
+      { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "COOLPIX 3200", 0, 0, // a.k.a. E3200
+      { 9846,-2085,-1019,-3278,11109,2170,-774,2134,5745 } }, /* DJC */
+    { LIBRAW_CAMERAMAKER_Nikon, "COOLPIX 4300", 0, 0, // a.k.a. E4300
+      { 11280,-3564,-1370,-4655,12374,2282,-1423,2168,5396 } }, /* DJC, copied from Minolta DiMAGE Z2 */
+    { LIBRAW_CAMERAMAKER_Nikon, "COOLPIX 4500", 0, 0, // a.k.a. E4500, possibly same CM as for E5000
+      { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "COOLPIX 5000", 0, 0, // a.k.a. E5000
+      { -6678,12805,2248,5725,-499,3375,-5903,10713,6034,-270,9976,134 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "COOLPIX 5400", 0, 0, // a.k.a. E5400
+      { 9349,-2988,-1001,-7918,15766,2266,-2097,2680,6839 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "COOLPIX 5700", 0, 0, // a.k.a. E5700
+      { -6475,12496,2428,5409,-16,3180,-5965,10912,5866,-177,9918,248 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "COOLPIX 8400", 0, 0, // a.k.a. E8400
+      { 7842,-2320,-992,-8154,15718,2599,-1098,1342,7560 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "COOLPIX 8700", 0, 0, // a.k.a. E8700
+      { 8489,-2583,-1036,-8051,15583,2643,-1307,1407,7354 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "COOLPIX 8800", 0, 0, // a.k.a. E8800
+      { 7971,-2314,-913,-8451,15762,2894,-1442,1520,7610 } },
+
+    { LIBRAW_CAMERAMAKER_Nikon, "COOLPIX 700", 0, 0x3dd, // a.k.a. E700
+      { -3746,10611,1665,9621,-1734,2114,-2389,7082,3064,3406,6116,-244 } }, /* DJC */
+    { LIBRAW_CAMERAMAKER_Nikon, "COOLPIX 800", 0, 0x3dd, // a.k.a. E800
+      { -3746,10611,1665,9621,-1734,2114,-2389,7082,3064,3406,6116,-244 } }, /* DJC */
+    { LIBRAW_CAMERAMAKER_Nikon, "COOLPIX 950", 0, 0x3dd, // a.k.a. E950
+      { -3746,10611,1665,9621,-1734,2114,-2389,7082,3064,3406,6116,-244 } }, /* DJC */
+    { LIBRAW_CAMERAMAKER_Nikon, "COOLPIX 995", 0, 0,     // a.k.a. E995
+      { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } }, /* DJC, copied from E5000 */
+
+    { LIBRAW_CAMERAMAKER_Nikon, "COOLPIX A1000", 0, 0,
+      { 10601,-3487,-1127,-2931,11443,1676,-587,1740,5278 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "COOLPIX B700", 0, 0,
+      { 14387,-6014,-1299,-1357,9975,1616,467,1047,4744 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "COOLPIX A", 0, 0,
+      { 8198,-2239,-724,-4871,12389,2798,-1043,2050,7181 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "COOLPIX P1000", 0, 0,
+      { 14294,-6116,-1333,-1628,10219,1637,-14,1158,5022 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "COOLPIX P6000", 0, 0,
+      { 9698,-3367,-914,-4706,12584,2368,-837,968,5801 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "COOLPIX P7000", 0, 0,
+      { 11432,-3679,-1111,-3169,11239,2202,-791,1380,4455 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "COOLPIX P7100", 0, 0,
+      { 11053,-4269,-1024,-1976,10182,2088,-526,1263,4469 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "COOLPIX P7700", -3200, 0, // same CamID: "COOLPIX P7700", "COOLPIX Deneb"
+      { 10321,-3920,-931,-2750,11146,1824,-442,1545,5539 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "COOLPIX P7800", -3200, 0, // same CamID: "COOLPIX P7800", "COOLPIX Kalon"
+      { 10321,-3920,-931,-2750,11146,1824,-442,1545,5539 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "COOLPIX P330", -200, 0,
+      { 10321,-3920,-931,-2750,11146,1824,-442,1545,5539 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "COOLPIX P340", -200, 0,
+      { 10321,-3920,-931,-2750,11146,1824,-442,1545,5539 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "Coolpix P950", 0, 0,
+      { 13307, -5641, -1290, -2048, 10581,  1689,   -64,  1222,  5176}},
+
+    { LIBRAW_CAMERAMAKER_Nikon, "D3000", 0, 0,
+      { 8736,-2458,-935,-9075,16894,2251,-1354,1242,8263 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D3100", 0, 0,
+      { 7911,-2167,-813,-5327,13150,2408,-1288,2483,7968 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D3200", 0, 0xfb9,
+      { 7013,-1408,-635,-5268,12902,2640,-1470,2801,7379 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D3300", 0, 0,
+      { 6988,-1384,-714,-5631,13410,2447,-1485,2204,7318 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D3400", 0, 0,
+      { 6988,-1384,-714,-5631,13410,2447,-1485,2204,7318 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D3500", 0, 0,
+      { 8821,-2938,-785,-4178,12142,2287,-824,1651,6860 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D5000", 0, 0xf00,
+      { 7309,-1403,-519,-8474,16008,2622,-2433,2826,8064 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D5100", 0, 0x3de6,
+      { 8198,-2239,-724,-4871,12389,2798,-1043,2050,7181 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D5200", 0, 0,
+      { 8322,-3112,-1047,-6367,14342,2179,-988,1638,6394 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D5300", 0, 0,
+      { 6988,-1384,-714,-5631,13410,2447,-1485,2204,7318 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D5500", 0, 0,
+      { 8821,-2938,-785,-4178,12142,2287,-824,1651,6860 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D5600", 0, 0,
+      { 8821,-2938,-785,-4178,12142,2287,-824,1651,6860 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D7000", 0, 0,
+      { 8198,-2239,-724,-4871,12389,2798,-1043,2050,7181 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D7100", 0, 0,
+      { 8322,-3112,-1047,-6367,14342,2179,-988,1638,6394 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D7200", 0, 0,
+      { 8322,-3112,-1047,-6367,14342,2179,-988,1638,6394 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D7500", 0, 0,
+      { 8813,-3210,-1036,-4703,12868,2021,-1054,1940,6129 } },
+
+    { LIBRAW_CAMERAMAKER_Nikon, "D100", 0, 0,
+      { 5902,-933,-782,-8983,16719,2354,-1402,1455,6464 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D200", 0, 0xfbc,
+      { 8367,-2248,-763,-8758,16447,2422,-1527,1550,8053 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D300", 0, 0,    // same CMs: D300, D300s
+      { 9030,-1992,-715,-8465,16302,2255,-2689,3217,8069 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D500", 0, 0,
+      { 8813,-3210,-1036,-4703,12868,2021,-1054,1940,6129 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D600", 0, 0x3e07,
+      { 8178,-2245,-609,-4857,12394,2776,-1207,2086,7298 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D610",0, 0,
+      { 8178,-2245,-609,-4857,12394,2776,-1207,2086,7298 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D700", 0, 0,
+      { 8139,-2171,-663,-8747,16541,2295,-1925,2008,8093 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D750", -600, 0,
+      { 9020,-2890,-715,-4535,12436,2348,-934,1919,7086 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D780", -600, 0,
+      { 9943,-3269,-839,-5323,13269,2259,-1198,2083,7557 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D800", 0, 0,    // same CMs: D800, D800E
+      { 7866,-2108,-555,-4869,12483,2681,-1176,2069,7501 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D810A", 0, 0,
+      { 11973,-5685,-888,-1965,10326,1901,-115,1123,7169 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D810", 0, 0,
+      { 9369,-3195,-791,-4488,12430,2301,-893,1796,6872 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D850", 0, 0,
+      { 10405,-3755,-1270,-5461,13787,1793,-1040,2015,6785 } },
+
+    { LIBRAW_CAMERAMAKER_Nikon, "D40X", 0, 0,
+      { 8819,-2543,-911,-9025,16928,2151,-1329,1213,8449 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D40", 0, 0,
+      { 6992,-1668,-806,-8138,15748,2543,-874,850,7897 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D50", 0, 0,
+      { 7732,-2422,-789,-8238,15884,2498,-859,783,7330 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D60", 0, 0,
+      { 8736,-2458,-935,-9075,16894,2251,-1354,1242,8263 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D70", 0, 0,     // same CMs: D70, D70s
+      { 7732,-2422,-789,-8238,15884,2498,-859,783,7330 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D80", 0, 0,
+      { 8629,-2410,-883,-9055,16940,2171,-1490,1363,8520 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D90", 0, 0xf00,
+      { 7309,-1403,-519,-8474,16008,2622,-2434,2826,8064 } },
+
+    { LIBRAW_CAMERAMAKER_Nikon, "D1H", 0, 0,
+      { 7659,-2238,-935,-8942,16969,2004,-2701,3051,8690 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D1X", 0, 0,
+      { 7702,-2245,-975,-9114,17242,1875,-2679,3055,8521 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D1", 0, 0,
+//      { 16772,-4726,-2141,-7611,15713,1972,-2846,3494,9521 } }, /* multiplied by 2.218750, 1.0, 1.148438 */
+      { 7637,-2199,-974,-9109,17099,2043,-2822,3306,8372 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D2H", 0, 0,     // same CMs: D2H, D2Hs
+      { 5733,-911,-629,-7967,15987,2055,-3050,4013,7048 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D2X", 0, 0,     // same CMs: D2X, D2Xs
+      { 10231,-2768,-1254,-8302,15900,2551,-797,681,7148 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D3S", 0, 0,
+      { 8828,-2406,-694,-4874,12603,2541,-660,1509,7587 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D3X", 0, 0,
+      { 7171,-1986,-648,-8085,15555,2718,-2170,2512,7457 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D3", 0, 0,
+      { 8139,-2171,-663,-8747,16541,2295,-1925,2008,8093 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D4", 0, 0,      // same CMs: D4, D4S (and Df)
+      { 8598,-2848,-857,-5618,13606,2195,-1002,1773,7137 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D5", 0, 0,
+      { 9200,-3522,-992,-5755,13803,2117,-753,1486,6338 } },
+    { LIBRAW_CAMERAMAKER_Nikon, "D6", 0, 0,
+      { 9028,-3423,-1035,-6321,14265,2217,-1013,1683,6928}},
+    { LIBRAW_CAMERAMAKER_Nikon, "Df", 0, 0,
+      { 8598,-2848,-857,-5618,13606,2195,-1002,1773,7137 } },
+
+    { LIBRAW_CAMERAMAKER_Nikon, "Z 50", 0, 0,
+       { 11640,-4829,-1079,-5107,13006,2325,-972,1711,7380}},
+    { LIBRAW_CAMERAMAKER_Nikon, "Z 6", 0, 0,
+      { 9943,-3269,-839,-5323,13269,2259,-1198,2083,7557 } }, // v.2
+    { LIBRAW_CAMERAMAKER_Nikon, "Z 7", 0, 0,
+      { 10405,-3755,-1270,-5461,13787,1793,-1040,2015,6785 } },
+
+    { LIBRAW_CAMERAMAKER_Olympus, "AIR A01", 0, 0xfe1,
+      { 8992,-3093,-639,-2563,10721,2122,-437,1270,5473 } },
+
+    { LIBRAW_CAMERAMAKER_Olympus, "C-5050Z", 0, 0,
+      { 10633,-3234,-1285,-7460,15570,1967,-1917,2510,6299 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "C-5060WZ", 0, 0,
+      { 10445,-3362,-1307,-7662,15690,2058,-1135,1176,7602 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "C-7070WZ", 0, 0,
+      { 10252,-3531,-1095,-7114,14850,2436,-1451,1723,6365 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "C-7000Z", 0, 0,
+      { 10793,-3791,-1146,-7498,15177,2488,-1390,1577,7321 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "C-8080WZ", 0, 0,
+      { 8606,-2509,-1014,-8238,15714,2703,-942,979,7760 } },
+
+    { LIBRAW_CAMERAMAKER_Olympus, "E-300", 0, 0,
+      { 7828,-1761,-348,-5788,14071,1830,-2853,4518,6557 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-330", 0, 0,
+      { 8961,-2473,-1084,-7979,15990,2067,-2319,3035,8249 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-400", 0, 0,
+      { 6169,-1483,-21,-7107,14761,2536,-2904,3580,8568 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-410", 0, 0xf6a,
+      { 8856,-2582,-1026,-7761,15766,2082,-2009,2575,7469 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-420", 0, 0xfd7,
+      { 8746,-2425,-1095,-7594,15612,2073,-1780,2309,7416 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-450", 0, 0xfd2,
+      { 8745,-2425,-1095,-7594,15613,2073,-1780,2309,7416 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-500", 0, 0,
+      { 8136,-1968,-299,-5481,13742,1871,-2556,4205,6630 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-510", 0, 0xf6a,
+      { 8785,-2529,-1033,-7639,15624,2112,-1783,2300,7817 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-520", 0, 0xfd2,
+      { 8344,-2322,-1020,-7596,15635,2048,-1748,2269,7287 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-600", 0, 0xfaf,
+      { 8453,-2198,-1092,-7609,15681,2008,-1725,2337,7824 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-620", 0, 0xfaf,
+      { 8453,-2198,-1092,-7609,15681,2008,-1725,2337,7824 } },
+
+    { LIBRAW_CAMERAMAKER_Olympus, "E-10", 0, 0xffc,
+      { 12970,-4703,-1433,-7466,15843,1644,-2191,2451,6668 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-20", 0, 0xffc,  // model is "E-20,E-20N,E-20P"
+      { 13414,-4950,-1517,-7166,15293,1960,-2325,2664,7212 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-30", 0, 0xfbc,
+      { 8144,-1861,-1111,-7763,15894,1929,-1865,2542,7607 } },
+
+    { LIBRAW_CAMERAMAKER_Olympus, "E-1", 0, 0,
+      { 11846,-4767,-945,-7027,15878,1089,-2699,4122,8311 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-3", 0, 0xf99,
+      { 9487,-2875,-1115,-7533,15606,2010,-1618,2100,7389 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-5", 0, 0xeec,
+      { 11200,-3783,-1325,-4576,12593,2206,-695,1742,7504 } },
+
+    { LIBRAW_CAMERAMAKER_Olympus, "E-P1", 0, 0xffd,
+      { 8343,-2050,-1021,-7715,15705,2103,-1831,2380,8235 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-P2", 0, 0xffd,
+      { 8343,-2050,-1021,-7715,15705,2103,-1831,2380,8235 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-P3", 0, 0,
+      { 7575,-2159,-571,-3722,11341,2725,-1434,2819,6271 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-P5", 0, 0,
+      { 8380,-2630,-639,-2887,10725,2496,-627,1427,5438 } },
+
+    { LIBRAW_CAMERAMAKER_Olympus, "E-PL10", 0, 0,
+      { 9197,-3190,-659,-2606,10830,2039,-458,1250,5458 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-PL1s", 0, 0,
+      { 11409,-3872,-1393,-4572,12757,2003,-709,1810,7415 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-PL1", 0, 0,
+      { 11408,-4289,-1215,-4286,12385,2118,-387,1467,7787 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-PL2", 0, 0xcf3,
+      { 15030,-5552,-1806,-3987,12387,1767,-592,1670,7023 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-PL3", 0, 0,
+      { 7575,-2159,-571,-3722,11341,2725,-1434,2819,6271 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-PL5", 0, 0xfcb,
+      { 8380,-2630,-639,-2887,10725,2496,-627,1427,5438 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-PL6", 0, 0,
+      { 8380,-2630,-639,-2887,10725,2496,-627,1427,5438 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-PL7", 0, 0,
+      { 9197,-3190,-659,-2606,10830,2039,-458,1250,5458 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-PL8", 0, 0,
+      { 9197,-3190,-659,-2606,10830,2039,-458,1250,5458 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-PL9", 0, 0,
+      { 8380,-2630,-639,-2887,10725,2496,-627,1427,5438 } },
+
+    { LIBRAW_CAMERAMAKER_Olympus, "E-PM1", 0, 0,
+      { 7575,-2159,-571,-3722,11341,2725,-1434,2819,6271 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-PM2", 0, 0,
+      { 8380,-2630,-639,-2887,10725,2496,-627,1427,5438 } },
+
+    { LIBRAW_CAMERAMAKER_Olympus, "E-M10", 0, 0,	  // Same CMs: E-M10, E-M10 Mark II, E-M10 Mark III; "CLAUSS piX 5oo"
+      { 8380,-2630,-639,-2887,10725,2496,-627,1427,5438 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-M1X", 0, 0,
+      { 11896,-5110,-1076,-3181,11378,2048,-519,1224,5166 } },
+
+    { LIBRAW_CAMERAMAKER_Olympus, "E-M1 Mark III", 0, 0,
+      { 11896,-5110,-1076,-3181,11378,2048,-519,1224,5166 } }, /* preliminary */
+
+    { LIBRAW_CAMERAMAKER_Olympus, "E-M1 Mark II", 0, 0,
+      { 9383,-3170,-763,-2457,10702,2020,-384,1236,5552 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-M1", 0, 0,
+      { 7687,-1984,-606,-4327,11928,2721,-1381,2339,6452 } },
+
+    { LIBRAW_CAMERAMAKER_Olympus, "E-M5 Mark III", 0, 0,
+      { 11896,-5110,-1076,-3181,11378,2048,-519,1224,5166 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-M5 Mark II", 0, 0,
+      { 9422,-3258,-711,-2655,10898,2015,-512,1354,5512 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "E-M5", 0, 0xfe1,
+      { 8380,-2630,-639,-2887,10725,2496,-627,1427,5438 } },
+
+    { LIBRAW_CAMERAMAKER_Olympus, "PEN-F",0, 0,
+      { 9476,-3182,-765,-2613,10958,1893,-449,1315,5268 } },
+
+    { LIBRAW_CAMERAMAKER_Olympus, "SH-2", 0, 0, // same CamID: SH-2, SH-3
+     { 10156,-3425,-1077,-2611,11177,1624,-385,1592,5080 } },
+
+    { LIBRAW_CAMERAMAKER_Olympus, "SP-350", 0, 0,
+      { 12078,-4836,-1069,-6671,14306,2578,-786,939,7418 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "SP-3", 0, 0,	// Same CMs: SP310, SP320
+      { 11766,-4445,-1067,-6901,14421,2707,-1029,1217,7572 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "SP-500UZ", 0, 0xfff,
+      { 9493,-3415,-666,-5211,12334,3260,-1548,2262,6482 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "SP-510UZ", 0, 0xffe,
+      { 10593,-3607,-1010,-5881,13127,3084,-1200,1805,6721 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "SP-550UZ", 0, 0xffe,
+      { 11597,-4006,-1049,-5432,12799,2957,-1029,1750,6516 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "SP-560UZ", 0, 0xff9,
+      { 10915,-3677,-982,-5587,12986,2911,-1168,1968,6223 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "SP-565UZ", 0, 0,
+      { 11856,-4469,-1159,-4814,12368,2756,-993,1779,5589 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "SP-570UZ", 0, 0,
+      { 11522,-4044,-1146,-4736,12172,2904,-988,1829,6039 } },
+
+    { LIBRAW_CAMERAMAKER_Olympus, "STYLUS 1",0, 0,	// Olympus "STYLUS 1 and STYLUS 1s have the same CamID, cameras are slightly different
+      { 8360,-2420,-880,-3928,12353,1739,-1381,2416,5173 } },
+
+    { LIBRAW_CAMERAMAKER_Olympus, "TG-4", 0, 0,
+     { 11426,-4159,-1126,-2066,10678,1593,-120,1327,4998 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "TG-", 0, 0, // same CMs: TG-5, TG-6
+     { 10899,-3833,-1082,-2112,10736,1575,-267,1452,5269 } },
+
+    { LIBRAW_CAMERAMAKER_Olympus, "XZ-10", 0, 0,
+      { 9777,-3483,-925,-2886,11297,1800,-602,1663,5134 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "XZ-1", 0, 0,
+      { 10901,-4095,-1074,-1141,9208,2293,-62,1417,5158 } },
+    { LIBRAW_CAMERAMAKER_Olympus, "XZ-2", 0, 0,
+      { 9777,-3483,-925,-2886,11297,1800,-602,1663,5134 } },
+
+	{ LIBRAW_CAMERAMAKER_OmniVison, "", 16, 0x3ff,
+      { 12782,-4059,-379,-478,9066,1413,1340,1513,5176 } }, /* DJC */
+
+    { LIBRAW_CAMERAMAKER_Pentax, "*istDL2", 0, 0,
+      { 10504,-2438,-1189,-8603,16207,2531,-1022,863,12242 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "*istDL", 0, 0,
+      { 10829,-2838,-1115,-8339,15817,2696,-837,680,11939 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "*istDS2", 0, 0,
+      { 10504,-2438,-1189,-8603,16207,2531,-1022,863,12242 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "*istDS", 0, 0,
+      { 10371,-2333,-1206,-8688,16231,2602,-1230,1116,11282 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "*istD", 0, 0,
+      { 9651,-2059,-1189,-8881,16512,2487,-1460,1345,10687 } },
+
+    { LIBRAW_CAMERAMAKER_Pentax, "K-01", 0, 0,
+      { 8134,-2728,-645,-4365,11987,2694,-838,1509,6498 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "K10D", 0, 0,
+      { 9679,-2965,-811,-8622,16514,2182,-975,883,9793 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "K1", 0, 0, // same CMs: K100D, "K100D Super", K110D
+      { 11095,-3157,-1324,-8377,15834,2720,-1108,947,11688 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "K20D", 0, 0,
+      { 9427,-2714,-868,-7493,16092,1373,-2199,3264,7180 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "K200D", 0, 0,
+      { 9186,-2678,-907,-8693,16517,2260,-1129,1094,8524 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "K-m", 0, 0,
+      { 9730,-2989,-970,-8527,16258,2381,-1060,970,8362 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "KP", 0, 0,
+      { 7825,-2160,-1403,-4841,13555,1349,-1559,2449,5814 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "K-x", 0, 0,
+      { 8843,-2837,-625,-5025,12644,2668,-411,1234,7410 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "K-r", 0, 0,
+      { 9895,-3077,-850,-5304,13035,2521,-883,1768,6936 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "K-1", 0, 0, // same CMs: K-1, "K-1 Mark II"
+      { 8596,-2981,-639,-4202,12046,2431,-685,1424,6122 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "K-30", 0, 0,
+      { 8134,-2728,-645,-4365,11987,2694,-838,1509,6498 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "K-3", 0, 0, // same CMs: K-3, "K-3 II"
+      { 7415,-2052,-721,-5186,12788,2682,-1446,2157,6773 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "K-500", 0, 0,
+      { 8109,-2740,-608,-4593,12175,2731,-1006,1515,6545 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "K-50", 0, 0,
+      { 8109,-2740,-608,-4593,12175,2731,-1006,1515,6545 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "K-5 II", 0, 0, // same CMs: "K-5 II" and "K-5 IIs"
+      { 8170,-2725,-639,-4440,12017,2744,-771,1465,6599 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "K-5", 0, 0,
+      { 8713,-2833,-743,-4342,11900,2772,-722,1543,6247 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "K-70", 0, 0,
+      { 8766,-3149,-747,-3976,11943,2292,-517,1259,5552 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "K-7", 0, 0,
+      { 9142,-2947,-678,-8648,16967,1663,-2224,2898,8615 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "KP", 0, 0,
+      { 8617,-3228,-1034,-4674,12821,2044,-803,1577,5728 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "K-S1", 0, 0,
+      { 8512,-3211,-787,-4167,11966,2487,-638,1288,6054 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "K-S2", 0, 0,
+      { 8662,-3280,-798,-3928,11771,2444,-586,1232,6054 } },
+
+    { LIBRAW_CAMERAMAKER_Pentax, "Q-S1", 0, 0,
+      { 12995,-5593,-1107,-1879,10139,2027,-64,1233,4919 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "Q7", 0, 0,
+      { 10901,-3938,-1025,-2743,11210,1738,-823,1805,5344 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "Q10", 0, 0,
+      { 11562,-4183,-1172,-2357,10919,1641,-582,1726,5112 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "Q", 0, 0,
+      { 11731,-4169,-1267,-2015,10727,1473,-217,1492,4870 } },
+
+    { LIBRAW_CAMERAMAKER_Pentax, "MX-1", 0, 0,
+      { 9296,-3146,-888,-2860,11287,1783,-618,1698,5151 } },
+
+    { LIBRAW_CAMERAMAKER_Pentax, "645D", 0, 0x3e00,
+      { 10646,-3593,-1158,-3329,11699,1831,-667,2874,6287 } },
+    { LIBRAW_CAMERAMAKER_Pentax, "645Z", 0, 0,
+      { 9519,-3591,-664,-4074,11725,2671,-624,1501,6653 } },
+
+
+    {LIBRAW_CAMERAMAKER_Panasonic, "DC-S1R", 0, 0,
+      { 11822,-5321,-1249,-5958,15114,766,-614,1264,7043 } },
+    {LIBRAW_CAMERAMAKER_Panasonic, "DC-S1H", 0, 0,
+      {  9397, -3719,  -805, -5425, 13326,  2309,  -972,  1715,  6034}},
+    {LIBRAW_CAMERAMAKER_Panasonic, "DC-S1", 0, 0,
+      { 9744,-3905,-779,-4899,12807,2324,-798,1630,5827 } },
+
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-CM1", -15, 0, // same CMs: DMC-CM1, DMC-CM10
+      { 8770,-3194,-820,-2871,11281,1803,-513,1552,4434 } },
+
+    { LIBRAW_CAMERAMAKER_Panasonic, "DC-FZ1000M2", -15, 0,
+      { 9803,-4185,-992,-4066,12578,1628,-838,1824,5288 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-FZ1000", -15, 0,
+      { 7830,-2696,-763,-3325,11667,1866,-641,1712,4824 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-FZ2500", -15, 0,
+      { 7386,-2443,-743,-3437,11864,1757,-608,1660,4766 } },
+
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-FZ100", -15, 0xfff,
+      { 16197,-6146,-1761,-2393,10765,1869,366,2238,5248 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-FZ150", -15, 0xfff,
+      { 11904,-4541,-1189,-2355,10899,1662,-296,1586,4289 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-FZ200", -15, 0xfff,
+      { 8112,-2563,-740,-3730,11784,2197,-941,2075,4933 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-FZ300", -15, 0xfff,
+      { 8378,-2798,-769,-3068,11410,1877,-538,1792,4623 } },
+
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-FZ18", 0, 0,
+      { 9932,-3060,-935,-5809,13331,2753,-1267,2155,5575 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-FZ28", -15, 0xf96,
+      { 10109,-3488,-993,-5412,12812,2916,-1305,2140,5543 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-FZ30", 0, 0xf94,
+      { 10976,-4029,-1141,-7918,15491,2600,-1670,2071,8246 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-FZ35", -15, 0,
+      { 9938,-2780,-890,-4604,12393,2480,-1117,2304,4620 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-FZ40", -15, 0,
+      { 13639,-5535,-1371,-1698,9633,2430,316,1152,4108 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-FZ50", 0, 0,
+      { 7906,-2709,-594,-6231,13351,3220,-1922,2631,6537 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-FZ70", -15, 0,
+      { 11532,-4324,-1066,-2375,10847,1749,-564,1699,4351 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DC-FZ80", -15, 0,
+      { 8550,-2908,-842,-3195,11529,1881,-338,1603,4631 } },
+
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-FZ8", 0, 0xf7f,
+      { 8986,-2755,-802,-6341,13575,3077,-1476,2144,6379 } },
+
+
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-L10", -15, 0xf96,
+      { 8025,-1942,-1050,-7920,15904,2100,-2456,3005,7039 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-L1", 0, 0xf7f,
+      { 8054,-1885,-1025,-8349,16367,2040,-2805,3542,7629 } },
+
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-LC1", 0, 0,
+      { 11340,-4069,-1275,-7555,15266,2448,-2960,3426,7685 } },
+
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-LF1", -15, 0,
+      { 9379,-3267,-816,-3227,11560,1881,-926,1928,5340 } },
+
+    { LIBRAW_CAMERAMAKER_Panasonic, "DC-LX100M2", -15, 0,
+      { 8585,-3127,-833,-4005,12250,1953,-650,1494,4862 } }, // v.2
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-LX100", -15, 0,
+      { 8844,-3538,-768,-3709,11762,2200,-698,1792,5220 } },
+
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-LX1", 0, 0xf7f,
+      { 10704,-4187,-1230,-8314,15952,2501,-920,945,8927 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-LX2", 0, 0,
+      { 8048,-2810,-623,-6450,13519,3272,-1700,2146,7049 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-LX3", -15, 0,
+      { 8128,-2668,-655,-6134,13307,3161,-1782,2568,6083 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-LX5", -15, 0,
+      { 10909,-4295,-948,-1333,9306,2399,22,1738,4582 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-LX7", -15, 0,
+      { 10148,-3743,-991,-2837,11366,1659,-701,1893,4899 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-LX9", -15, 0,
+      { 7790,-2736,-755,-3452,11870,1769,-628,1647,4898 } },
+
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-FX150", -15, 0xfff,
+      { 9082,-2907,-925,-6119,13377,3058,-1797,2641,5609 } },
+
+    { LIBRAW_CAMERAMAKER_Panasonic, "DC-G99", -15, 0,
+      { 9657,-3963,-748,-3361,11378,2258,-568,1415,5158 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-G10", 0, 0,
+      { 10113,-3400,-1114,-4765,12683,2317,-377,1437,6710 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-G1", -15, 0xf94,
+      { 8199,-2065,-1056,-8124,16156,2033,-2458,3022,7220 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-G2", -15, 0xf3c,
+      { 10113,-3400,-1114,-4765,12683,2317,-377,1437,6710 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-G3", -15, 0xfff,
+      { 6763,-1919,-863,-3868,11515,2684,-1216,2387,5879 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-G5", -15, 0xfff,
+      { 7798,-2562,-740,-3879,11584,2613,-1055,2248,5434 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-G6", -15, 0xfff,
+      { 8294,-2891,-651,-3869,11590,2595,-1183,2267,5352 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-G7", -15, 0xfff,
+      { 7610,-2780,-576,-4614,12195,2733,-1375,2393,6490 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-G8", -15, 0xfff,
+      { 7610,-2780,-576,-4614,12195,2733,-1375,2393,6490 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DC-G9", -15, 0,
+      { 7685,-2375,-634,-3687,11700,2249,-748,1546,5111 } },
+
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-GH1", -15, 0xf92,
+      { 6299,-1466,-532,-6535,13852,2969,-2331,3112,5984 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-GH2", -15, 0xf95,
+      { 7780,-2410,-806,-3913,11724,2484,-1018,2390,5298 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-GH3", -15, 0,
+      { 6559,-1752,-491,-3672,11407,2586,-962,1875,5130 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-GH4", -15, 0,
+      { 7122,-2108,-512,-3155,11201,2231,-541,1423,5045 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DC-GH5s", -15, 0,
+      { 6929,-2355,-708,-4192,12534,1828,-1097,1989,5195 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DC-GH5", -15, 0,
+      { 7641,-2336,-605,-3218,11299,2187,-485,1338,5121 } },
+
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-GM1", -15, 0,
+      { 6770,-1895,-744,-5232,13145,2303,-1664,2691,5703 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-GM5", -15, 0,
+      { 8238,-3244,-679,-3921,11814,2384,-836,2022,5852 } },
+
+    { LIBRAW_CAMERAMAKER_Panasonic, "DC-GF10", -15, 0,
+      { 7610,-2780,-576,-4614,12195,2733,-1375,2393,6490 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-GF1", -15, 0xf92,
+      { 7888,-1902,-1011,-8106,16085,2099,-2353,2866,7330 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-GF2", -15, 0xfff,
+      { 7888,-1902,-1011,-8106,16085,2099,-2353,2866,7330 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-GF3", -15, 0xfff,
+      { 9051,-2468,-1204,-5212,13276,2121,-1197,2510,6890 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-GF5", -15, 0xfff,
+      { 8228,-2945,-660,-3938,11792,2430,-1094,2278,5793 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-GF6", -15, 0,
+      { 8130,-2801,-946,-3520,11289,2552,-1314,2511,5791 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-GF7", -15, 0,
+      { 7610,-2780,-576,-4614,12195,2733,-1375,2393,6490 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-GF8", -15, 0,
+      { 7610,-2780,-576,-4614,12195,2733,-1375,2393,6490 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DC-GF9", -15, 0,
+      { 7610,-2780,-576,-4614,12195,2733,-1375,2393,6490 } },
+
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-GX85", -15, 0,
+      { 7771,-3020,-629,-4029,11950,2345,-821,1977,6119 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-GX1", -15, 0,
+      { 6763,-1919,-863,-3868,11515,2684,-1216,2387,5879 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-GX7", -15,0,
+      { 7610,-2780,-576,-4614,12195,2733,-1375,2393,6490 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-GX8", -15,0,
+      { 7564,-2263,-606,-3148,11239,2177,-540,1435,4853 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DC-GX9", -15, 0,
+      { 7564,-2263,-606,-3148,11239,2177,-540,1435,4853 } },
+
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-ZS100", -15, 0,
+      { 7790,-2736,-755,-3452,11870,1769,-628,1647,4898 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DC-ZS200", -15, 0,
+      { 7790,-2736,-755,-3452,11870,1769,-628,1647,4898 } },
+
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-ZS40", -15, 0,
+      { 8607,-2822,-808,-3755,11930,2049,-820,2060,5224 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-ZS50", -15, 0,
+      { 8802,-3135,-789,-3151,11468,1904,-550,1745,4810 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DMC-ZS60", -15, 0,
+      { 8550,-2908,-842,-3195,11529,1881,-338,1603,4631 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DC-ZS70", -15, 0,
+      { 9052,-3117,-883,-3045,11346,1927,-205,1520,4730 } },
+    { LIBRAW_CAMERAMAKER_Panasonic, "DC-ZS80", -15, 0,
+      { 12194,-5340,-1329,-3035,11394,1858,-50,1418,5219 } },
+
+    { LIBRAW_CAMERAMAKER_PhaseOne, "H20", 0, 0,
+      { 3906,1422,-467,-9953,18472,1365,-3307,4496,6406 } },
+    { LIBRAW_CAMERAMAKER_PhaseOne, "H25", 0, 0,
+      { 2905,732,-237,-8134,16626,1476,-3038,4253,7517 } },
+    { LIBRAW_CAMERAMAKER_PhaseOne, "IQ4 150MP", 0, 0,
+      {  6644, -2257,  -804, -6459, 14562,  2019, -1221,  1876,  6411}},
+    { LIBRAW_CAMERAMAKER_PhaseOne, "IQ140", 0, 0,
+      { 8035,435,-962,-6001,13872,2320,-1159,3065,5434 } },
+    { LIBRAW_CAMERAMAKER_PhaseOne, "IQ150", 0, 0,
+      {10325,845,-604,-4113,13385,481,-1791,4163,6924}}, /* temp */ /* emb */
+//      { 3984,0,0,0,10000,0,0,0,7666 } },
+    { LIBRAW_CAMERAMAKER_PhaseOne, "IQ160", 0, 0,
+      { 8035,435,-962,-6001,13872,2320,-1159,3065,5434 } },
+    { LIBRAW_CAMERAMAKER_PhaseOne, "IQ180", 0, 0,
+      { 6294,686,-712,-5435,13417,2211,-1006,2435,5042 } },
+
+    { LIBRAW_CAMERAMAKER_PhaseOne, "IQ250",0, 0,
+//    {3984,0,0,0,10000,0,0,0,7666}},
+      {10325,845,-604,-4113,13385,481,-1791,4163,6924}}, /* emb */
+    { LIBRAW_CAMERAMAKER_PhaseOne, "IQ260", 0, 0,
+      { 8035,435,-962,-6001,13872,2320,-1159,3065,5434 } },
+    { LIBRAW_CAMERAMAKER_PhaseOne, "IQ280", 0, 0,
+      { 6294,686,-712,-5435,13417,2211,-1006,2435,5042 } },
+
+    { LIBRAW_CAMERAMAKER_PhaseOne, "IQ3 100MP", 0, 0,
+//    {2423,0,0,0,9901,0,0,0,7989}},
+      { 10999,354,-742,-4590,13342,937,-1060,2166,8120} }, /* emb */
+    { LIBRAW_CAMERAMAKER_PhaseOne, "IQ3 50MP", 0, 0,
+//      { 3984,0,0,0,10000,0,0,0,7666 } },
+      {10058,1079,-587,-4135,12903,944,-916,2726,7480}}, /* emb */
+    { LIBRAW_CAMERAMAKER_PhaseOne, "IQ3 60MP", 0, 0,
+      { 8035,435,-962,-6001,13872,2320,-1159,3065,5434 } },
+    { LIBRAW_CAMERAMAKER_PhaseOne, "IQ3 80MP", 0, 0,
+      { 6294,686,-712,-5435,13417,2211,-1006,2435,5042 } },
+
+    { LIBRAW_CAMERAMAKER_PhaseOne, "P21", 0, 0,
+      { 6516,-2050,-507,-8217,16703,1479,-3492,4741,8489 } },
+    { LIBRAW_CAMERAMAKER_PhaseOne, "P30", 0, 0,
+      { 4516,-244,-36,-7020,14976,2174,-3206,4670,7087 } },
+    { LIBRAW_CAMERAMAKER_PhaseOne, "P40", 0, 0,
+      { 8035,435,-962,-6001,13872,2320,-1159,3065,5434 } },
+    { LIBRAW_CAMERAMAKER_PhaseOne, "P45", 0, 0,
+      { 5053,-24,-117,-5685,14077,1703,-2619,4491,5850 } },
+    { LIBRAW_CAMERAMAKER_PhaseOne, "P65", 0, 0,
+      { 8035,435,-962,-6001,13872,2320,-1159,3065,5434 } },
+    { LIBRAW_CAMERAMAKER_PhaseOne, "P2", 0, 0,
+      { 2905,732,-237,-8134,16626,1476,-3038,4253,7517 } },
+
+    { LIBRAW_CAMERAMAKER_Photron, "BC2-HD", 0, 0,
+      { 14603,-4122,-528,-1810,9794,2017,-297,2763,5936 } }, /* DJC */
+
+	  { LIBRAW_CAMERAMAKER_Polaroid, "x530", 0, 0,
+      { 13458,-2556,-510,-5444,15081,205,0,0,12120 } },
+
+	  { LIBRAW_CAMERAMAKER_RED, "One", 704, 0xffff,
+      { 21014,-7891,-2613,-3056,12201,856,-2203,5125,8042 } }, /* DJC */
+
+    { LIBRAW_CAMERAMAKER_Ricoh, "S10 24-72mm F2.5-4.4 VC", 0, 0,
+      { 10531,-4043,-878,-2038,10270,2052,-107,895,4577 } },
+    { LIBRAW_CAMERAMAKER_Ricoh, "GR A12 50mm F2.5 MACRO", 0, 0,
+      { 8849,-2560,-689,-5092,12831,2520,-507,1280,7104 } },
+    { LIBRAW_CAMERAMAKER_Ricoh, "GR DIGITAL 2", 0, 0,
+      { 8846,-2704,-729,-5265,12708,2871,-1471,1955,6218 } },
+    { LIBRAW_CAMERAMAKER_Ricoh, "GR DIGITAL 3", 0, 0,
+      { 8170,-2496,-655,-5147,13056,2312,-1367,1859,5265 } },
+    { LIBRAW_CAMERAMAKER_Ricoh, "GR DIGITAL 4", 0, 0,
+      { 8771,-3151,-837,-3097,11015,2389,-703,1343,4924 } },
+    { LIBRAW_CAMERAMAKER_Ricoh, "GR III", 0, 0,
+      { 6127,-1777,-585,-5913,13699,2428,-1088,1780,6017 } },
+    { LIBRAW_CAMERAMAKER_Ricoh, "GR II", 0, 0,
+      { 5329,-1459,-390,-5407,12930,2768,-1119,1772,6046 } },
+    { LIBRAW_CAMERAMAKER_Ricoh, "GR", 0, 0,
+      { 5329,-1459,-390,-5407,12930,2768,-1119,1772,6046 } },
+    { LIBRAW_CAMERAMAKER_Ricoh, "GX200", 0, 0,
+      { 8040,-2368,-626,-4659,12543,2363,-1125,1581,5660 } },
+    { LIBRAW_CAMERAMAKER_Ricoh, "GXR Mount A12", 0, 0,
+      { 7834,-2182,-739,-5453,13409,2241,-952,2005,6620 } },
+    { LIBRAW_CAMERAMAKER_Ricoh, "GXR A12 50mm", 0, 0,
+      { 8849,-2560,-689,-5092,12831,2520,-507,1280,7104 } },
+    { LIBRAW_CAMERAMAKER_Ricoh, "GXR A12 28mm", 0, 0,
+      { 10228,-3159,-933,-5304,13158,2371,-943,1873,6685 } },
+    { LIBRAW_CAMERAMAKER_Ricoh, "GXR A16", 0, 0,
+      { 7837,-2538,-730,-4370,12184,2461,-868,1648,5830 } },
+    { LIBRAW_CAMERAMAKER_Ricoh, "GXR P10", 0, 0,
+      { 13168,-5128,-1663,-3006,11569,1611,-373,1244,4907 } },
+    { LIBRAW_CAMERAMAKER_Ricoh, "GXR S10", 0, 0,
+      { 8963,-2926,-754,-4881,12921,2164,-1464,1944,4901 } },
+
+    { LIBRAW_CAMERAMAKER_Samsung, "EX1", 0, 0x3e00,
+      { 8898,-2498,-994,-3144,11328,2066,-760,1381,4576 } },
+    { LIBRAW_CAMERAMAKER_Samsung, "EX2F", 0, 0x7ff,
+      { 10648,-3897,-1055,-2022,10573,1668,-492,1611,4742 } },
+//    { LIBRAW_CAMERAMAKER_Samsung, "GX20", 0, 0,
+//      { 23213,-14575,-4840,-7077,16564,316,385,-1656,9398 } }, // Adobe DNG
+//      { 27717,-17403,-5779,-8450,19778,377,459,1978,11221 } }, // Samsung DNG
+//      { 9427,-2714,-868,-7493,16092,1373,-2199,3264,7180 } },  // Adobe DCP
+
+//    { LIBRAW_CAMERAMAKER_Samsung, "Galaxy S6 Edge Rear Camera", 0, 0,
+//    { LIBRAW_CAMERAMAKER_Samsung, "Galaxy S6 Rear Camera", 0, 0,
+    { LIBRAW_CAMERAMAKER_Samsung, "Galaxy S6", 0, 0, // same CMs: "Galaxy S6", "Galaxy S6 Edge"
+      { 13699,-5767,-1384,-4449,13879,499,-467,1691,5892 } },
+
+//    { LIBRAW_CAMERAMAKER_Samsung, "Galaxy S7 Edge Rear Camera", 0, 0,
+//    { LIBRAW_CAMERAMAKER_Samsung, "Galaxy S7 Rear Camera", 0, 0,
+    { LIBRAW_CAMERAMAKER_Samsung, "Galaxy S7", 0, 0, // same CMs: "Galaxy S7", "Galaxy S7 Edge"
+      { 9927,-3704,-1024,-3935,12758,1257,-389,1512,4993 } },
+
+//    { LIBRAW_CAMERAMAKER_Samsung, "Galaxy S8+ Rear Camera", 0, 0,
+//    { LIBRAW_CAMERAMAKER_Samsung, "Galaxy S8 Rear Camera", 0, 0,
+    { LIBRAW_CAMERAMAKER_Samsung, "Galaxy S8", 0, 0, // same CMs: "Galaxy S8", "Galaxy S8+"
+      { 9927,-3704,-1024,-3935,12758,1257,-389,1512,4993 } },
+
+//    { LIBRAW_CAMERAMAKER_Samsung, "Galaxy S9+ Rear Camera", 0, 0,
+//    { LIBRAW_CAMERAMAKER_Samsung, "Galaxy S9 Rear Camera", 0, 0,
+    { LIBRAW_CAMERAMAKER_Samsung, "Galaxy S9", 0, 0, // same CMs: "Galaxy S9", "Galaxy S9+"
+      { 13292,-6142,-1268,-4095,12890,1283,-557,1930,5163 } },
+//    { LIBRAW_CAMERAMAKER_Samsung, "Galaxy Note 9 Rear Telephoto Camera", 0, 0,
+    { LIBRAW_CAMERAMAKER_Samsung, "Galaxy Note 9 Rear Camera", 0, 0,
+      { 13292,-6142,-1268,-4095,12890,1283,-557,1930,5163 } },
+
+    { LIBRAW_CAMERAMAKER_Samsung, "NX U", 0, 0,
+      { 7557,-2522,-739,-4679,12949,1894,-840,1777,5311 } },
+    { LIBRAW_CAMERAMAKER_Samsung, "NX3300", 0, 0,
+      { 8060,-2933,-761,-4504,12890,1762,-630,1489,5227 } },
+    { LIBRAW_CAMERAMAKER_Samsung, "NX3000", 0, 0,
+      { 8060,-2933,-761,-4504,12890,1762,-630,1489,5227 } },
+    { LIBRAW_CAMERAMAKER_Samsung, "NX30", 0, 0, // same CMs: NX30, NX300, NX300M
+      { 7557,-2522,-739,-4679,12949,1894,-840,1777,5311 } },
+    { LIBRAW_CAMERAMAKER_Samsung, "NX2000", 0, 0,
+      { 7557,-2522,-739,-4679,12949,1894,-840,1777,5311 } },
+    { LIBRAW_CAMERAMAKER_Samsung, "NX2", 0, 0xfff, // same CMs: NX20, NX200, NX210
+      { 6933,-2268,-753,-4921,13387,1647,-803,1641,6096 } },
+    { LIBRAW_CAMERAMAKER_Samsung, "NX1000", 0, 0,
+      { 6933,-2268,-753,-4921,13387,1647,-803,1641,6096 } },
+    { LIBRAW_CAMERAMAKER_Samsung, "NX1100", 0, 0,
+      { 6933,-2268,-753,-4921,13387,1647,-803,1641,6096 } },
+    { LIBRAW_CAMERAMAKER_Samsung, "NX11", 0, 0,
+      { 10332,-3234,-1168,-6111,14639,1520,-1352,2647,8331 } },
+    { LIBRAW_CAMERAMAKER_Samsung, "NX10", 0, 0, // same CMs: NX10, NX100
+      { 10332,-3234,-1168,-6111,14639,1520,-1352,2647,8331 } },
+    { LIBRAW_CAMERAMAKER_Samsung, "NX500", 0, 0,
+      { 10686,-4042,-1052,-3595,13238,276,-464,1259,5931 } },
+    { LIBRAW_CAMERAMAKER_Samsung, "NX5", 0, 0,
+      { 10332,-3234,-1168,-6111,14639,1520,-1352,2647,8331 } },
+    { LIBRAW_CAMERAMAKER_Samsung, "NX1", 0, 0,
+      { 10686,-4042,-1052,-3595,13238,276,-464,1259,5931 } },
+    { LIBRAW_CAMERAMAKER_Samsung, "NX mini", 0, 0,
+      { 5222,-1196,-550,-6540,14649,2009,-1666,2819,5657 } },
+
+    { LIBRAW_CAMERAMAKER_Samsung, "WB2000", 0, 0xfff,
+      { 12093,-3557,-1155,-1000,9534,1733,-22,1787,4576 } },
+    { LIBRAW_CAMERAMAKER_Samsung, "WB5000", 0, 0,
+      {  7675, -2195,  -305, -5860, 14118,  1857, -2425,  4007,  6578}},
+    { LIBRAW_CAMERAMAKER_Samsung, "S85", 0, 0,
+      { 11885,-3968,-1473,-4214,12299,1916,-835,1655,5549 } }, /* DJC */
+
+// Foveon: LibRaw color data
+    { LIBRAW_CAMERAMAKER_Sigma, "dp0 Quattro", 2047, 0,
+      { 13801,-3390,-1016,5535,3802,877,1848,4245,3730 } },
+    { LIBRAW_CAMERAMAKER_Sigma, "dp1 Quattro", 2047, 0,
+      { 13801,-3390,-1016,5535,3802,877,1848,4245,3730 } },
+    { LIBRAW_CAMERAMAKER_Sigma, "dp2 Quattro", 2047, 0,
+      { 13801,-3390,-1016,5535,3802,877,1848,4245,3730 } },
+    { LIBRAW_CAMERAMAKER_Sigma, "dp3 Quattro", 2047, 0,
+      { 13801,-3390,-1016,5535,3802,877,1848,4245,3730 } },
+    { LIBRAW_CAMERAMAKER_Sigma, "sd Quattro H", 256, 0,
+      { 1295,108,-311,256,828,-65,-28,750,254 } }, /* temp */
+    { LIBRAW_CAMERAMAKER_Sigma, "sd Quattro", 2047, 0,
+      { 1295,108,-311,256,828,-65,-28,750,254 } }, /* temp */
+    { LIBRAW_CAMERAMAKER_Sigma, "SD9", 15, 4095,
+      { 13564,-2537,-751,-5465,15154,194,-67,116,10425 } },
+    { LIBRAW_CAMERAMAKER_Sigma, "SD10", 15, 16383,
+      { 6787,-1682,575,-3091,8357,160,217,-369,12314 } },
+    { LIBRAW_CAMERAMAKER_Sigma, "SD14", 15, 16383,
+      { 13589,-2509,-739,-5440,15104,193,-61,105,10554 } },
+    { LIBRAW_CAMERAMAKER_Sigma, "SD15", 15, 4095,
+      { 13556,-2537,-730,-5462,15144,195,-61,106,10577 } },
+// Merrills + SD1
+    { LIBRAW_CAMERAMAKER_Sigma, "SD1", 31, 4095,
+      { 5133,-1895,-353,4978,744,144,3837,3069,2777 } }, /* LibRaw */
+    { LIBRAW_CAMERAMAKER_Sigma, "DP1 Merrill", 31, 4095,
+      { 5133,-1895,-353,4978,744,144,3837,3069,2777 } }, /* LibRaw */
+    { LIBRAW_CAMERAMAKER_Sigma, "DP2 Merrill", 31, 4095,
+      { 5133,-1895,-353,4978,744,144,3837,3069,2777 } }, /* LibRaw */
+    { LIBRAW_CAMERAMAKER_Sigma, "DP3 Merrill", 31, 4095,
+      { 5133,-1895,-353,4978,744,144,3837,3069,2777 } }, /* LibRaw */
+// Sigma DP (non-Merrill Versions)
+    { LIBRAW_CAMERAMAKER_Sigma, "DP1X", 0, 4095,
+      { 13704,-2452,-857,-5413,15073,186,-89,151,9820 } },
+    { LIBRAW_CAMERAMAKER_Sigma, "DP1", 0, 4095,
+      { 12774,-2591,-394,-5333,14676,207,15,-21,12127 } },
+    { LIBRAW_CAMERAMAKER_Sigma, "DP", 0, 4095,
+      //  { 7401,-1169,-567,2059,3769,1510,664,3367,5328 } },
+      { 13100,-3638,-847,6855,2369,580,2723,3218,3251 } }, /* LibRaw */
+
+	{ LIBRAW_CAMERAMAKER_Sinar, "", 0, 0,
+      { 16442,-2956,-2422,-2877,12128,750,-1136,6066,4559 } }, /* DJC */
+
+    { LIBRAW_CAMERAMAKER_Sony, "DSC-F828", 0, 0,
+      { 7924,-1910,-777,-8226,15459,2998,-1517,2199,6818,-7242,11401,3481 } },
+    { LIBRAW_CAMERAMAKER_Sony, "DSC-R1", 0, 0,
+      { 8512,-2641,-694,-8042,15670,2526,-1821,2117,7414 } },
+    { LIBRAW_CAMERAMAKER_Sony, "DSC-V3", 0, 0,
+      { 7511,-2571,-692,-7894,15088,3060,-948,1111,8128 } },
+
+    { LIBRAW_CAMERAMAKER_Sony, "DSC-HX9", -800, 0, // same CMs: DSC-HX95, DSC-HX99
+      { 13076,-5686,-1481,-4027,12851,1251,-167,725,4937 } },
+
+    { LIBRAW_CAMERAMAKER_Sony, "DSC-RX100M7", -800, 0,
+        {10315, -4390, -937, -4859, 12734, 2365, -734, 1537, 5997 } },
+    { LIBRAW_CAMERAMAKER_Sony, "DSC-RX100M6", -800, 0,
+      { 7325,-2321,-596,-3494,11674,2055,-668,1562,5031 } },
+    { LIBRAW_CAMERAMAKER_Sony, "DSC-RX100M5A", -800, 0,
+      { 11176,-4700,-965,-4004,12184,2032,-763,1726,5876 } },
+    { LIBRAW_CAMERAMAKER_Sony, "DSC-RX100M", -800, 0, // same CMs: DSC-RX100M2, DSC-RX100M3, DSC-RX100M4, DSC-RX100M5
+      { 6596,-2079,-562,-4782,13016,1933,-970,1581,5181 } },
+    { LIBRAW_CAMERAMAKER_Sony, "DSC-RX100", 0, 0,
+      { 8651,-2754,-1057,-3464,12207,1373,-568,1398,4434 } },
+    { LIBRAW_CAMERAMAKER_Sony, "DSC-RX10M4", -800, 0,
+      { 7699,-2566,-629,-2967,11270,1928,-378,1286,4807 } },
+    { LIBRAW_CAMERAMAKER_Sony, "DSC-RX10",0, 0, // same CMs: DSC-RX10, DSC-RX10M2, DSC-RX10M3
+      { 6679,-1825,-745,-5047,13256,1953,-1580,2422,5183 } },
+    { LIBRAW_CAMERAMAKER_Sony, "DSC-RX1RM2", 0, 0,
+      { 6629,-1900,-483,-4618,12349,2550,-622,1381,6514 } },
+    { LIBRAW_CAMERAMAKER_Sony, "DSC-RX1R", 0, 0,
+      { 6344,-1612,-462,-4863,12477,2681,-865,1786,6899 } },
+    { LIBRAW_CAMERAMAKER_Sony, "DSC-RX1", 0, 0,
+      { 6344,-1612,-462,-4863,12477,2681,-865,1786,6899 } },
+
+    { LIBRAW_CAMERAMAKER_Sony, "DSC-RX0", -800, 0, // same CMs: DSC-RX0, DSC-RX0M2
+      { 9396,-3507,-843,-2497,11111,1572,-343,1355,5089 } },
+
+    { LIBRAW_CAMERAMAKER_Sony, "DSLR-A100", 0, 0xfeb,
+      { 9437,-2811,-774,-8405,16215,2290,-710,596,7181 } },
+    { LIBRAW_CAMERAMAKER_Sony, "DSLR-A290", 0, 0,
+      { 6038,-1484,-579,-9145,16746,2512,-875,746,7218 } },
+    { LIBRAW_CAMERAMAKER_Sony, "DSLR-A2", 0, 0, // same CMs: DSLR-A200, DSLR-A230
+      { 9847,-3091,-928,-8485,16345,2225,-715,595,7103 } },
+    { LIBRAW_CAMERAMAKER_Sony, "DSLR-A300", 0, 0,
+      { 9847,-3091,-928,-8485,16345,2225,-715,595,7103 } },
+    { LIBRAW_CAMERAMAKER_Sony, "DSLR-A330", 0, 0,
+      { 9847,-3091,-929,-8485,16346,2225,-714,595,7103 } },
+    { LIBRAW_CAMERAMAKER_Sony, "DSLR-A3", 0, 0, // same CMs: DSLR-A350, DSLR-A380, DSLR-A390
+      { 6038,-1484,-579,-9145,16746,2512,-875,746,7218 } },
+    { LIBRAW_CAMERAMAKER_Sony, "DSLR-A450", 0, 0, // close to 16596 if arw is 14-bit
+      { 4950,-580,-103,-5228,12542,3029,-709,1435,7371 } },
+    { LIBRAW_CAMERAMAKER_Sony, "DSLR-A580", 0, 16596,
+      { 5932,-1492,-411,-4813,12285,2856,-741,1524,6739 } },
+    { LIBRAW_CAMERAMAKER_Sony, "DSLR-A500", 0, 16596,
+      { 6046,-1127,-278,-5574,13076,2786,-691,1419,7625 } },
+    { LIBRAW_CAMERAMAKER_Sony, "DSLR-A5", 0, 16596,  // same CMs: DSLR-A550, DSLR-A560
+      { 4950,-580,-103,-5228,12542,3029,-709,1435,7371 } },
+    { LIBRAW_CAMERAMAKER_Sony, "DSLR-A700", 0, 0,
+      { 5775,-805,-359,-8574,16295,2391,-1943,2341,7249 } },
+    { LIBRAW_CAMERAMAKER_Sony, "DSLR-A850", 0, 0,
+      { 5413,-1162,-365,-5665,13098,2866,-608,1179,8440 } },
+    { LIBRAW_CAMERAMAKER_Sony, "DSLR-A900", 0, 0,
+      { 5209,-1072,-397,-8845,16120,2919,-1618,1803,8654 } },
+
+    { LIBRAW_CAMERAMAKER_Sony, "ILCA-68", 0, 0,
+      { 6435,-1903,-536,-4722,12449,2550,-663,1363,6517 } },
+    { LIBRAW_CAMERAMAKER_Sony, "ILCA-77M2", 0, 0,
+      { 5991,-1732,-443,-4100,11989,2381,-704,1467,5992 } },
+    { LIBRAW_CAMERAMAKER_Sony, "ILCA-99M2", 0, 0,
+      { 6660,-1918,-471,-4613,12398,2485,-649,1433,6447 } },
+    { LIBRAW_CAMERAMAKER_Sony, "ILCE-9", 0, 0,
+      { 6389,-1703,-378,-4562,12265,2587,-670,1489,6550 } },
+    { LIBRAW_CAMERAMAKER_Sony, "ILCE-7S", 0, 0, // same CMs: ILCE-7S, ILCE-7SM2
+      { 5838,-1430,-246,-3497,11477,2297,-748,1885,5778 } },
+
+    { LIBRAW_CAMERAMAKER_Sony, "ILCE-7RM4", 0, 0,
+      {7662, -2686,-660,-5240, 12965,2530, -796, 1508, 6167 }},
+
+    { LIBRAW_CAMERAMAKER_Sony, "ILCE-7RM3", 0, 0,
+      { 6640,-1847,-503,-5238,13010,2474,-993,1673,6527 } },
+    { LIBRAW_CAMERAMAKER_Sony, "ILCE-7RM2", 0, 0,
+      { 6629,-1900,-483,-4618,12349,2550,-622,1381,6514 } },
+    { LIBRAW_CAMERAMAKER_Sony, "ILCE-7R", 0, 0,
+      { 4913,-541,-202,-6130,13513,2906,-1564,2151,7183 } },
+    { LIBRAW_CAMERAMAKER_Sony, "ILCE-7M3", 0, 0,
+      { 7374,-2389,-551,-5435,13162,2519,-1006,1795,6552 } },
+    { LIBRAW_CAMERAMAKER_Sony, "ILCE-7", 0, 0, // same CMs: ILCE-7, ILCE-7M2
+      { 5271,-712,-347,-6153,13653,2763,-1601,2366,7242 } },
+
+    { LIBRAW_CAMERAMAKER_Sony, "ILCE-6100", 0, 0,
+      { 7657,-2847,-607,-4083,11966,2389,-684,1418,5844 } },
+    { LIBRAW_CAMERAMAKER_Sony, "ILCE-6300", 0, 0,
+      { 5973,-1695,-419,-3826,11797,2293,-639,1398,5789 } },
+    { LIBRAW_CAMERAMAKER_Sony, "ILCE-6400", 0, 0,
+      { 7657,-2847,-607,-4083,11966,2389,-684,1418,5844 } },
+    { LIBRAW_CAMERAMAKER_Sony, "ILCE-6500", 0, 0,
+      { 5973,-1695,-419,-3826,11797,2293,-639,1398,5789 } },
+    { LIBRAW_CAMERAMAKER_Sony, "ILCE-6600", 0, 0,
+      { 7657,-2847,-607,-4083,11966,2389,-684,1418,5844 } },
+
+    { LIBRAW_CAMERAMAKER_Sony, "ILCE", 0, 0, // same CMs: ILCE-3000, ILCE-5000, ILCE-5100, ILCE-6000, ILCE-QX1
+      { 5991,-1456,-455,-4764,12135,2980,-707,1425,6701 } },
+    { LIBRAW_CAMERAMAKER_Sony, "MODEL-NAME", 0, 0,
+      { 5491,-1192,-363,-4951,12342,2948,-911,1722,7192 } },
+    { LIBRAW_CAMERAMAKER_Sony, "NEX-5N", 0, 0,
+      { 5991,-1456,-455,-4764,12135,2980,-707,1425,6701 } },
+    { LIBRAW_CAMERAMAKER_Sony, "NEX-5R", 0, 0,
+      { 6129,-1545,-418,-4930,12490,2743,-977,1693,6615 } },
+    { LIBRAW_CAMERAMAKER_Sony, "NEX-5T", 0, 0,
+      { 6129,-1545,-418,-4930,12490,2743,-977,1693,6615 } },
+    { LIBRAW_CAMERAMAKER_Sony, "NEX-5", 0, 0,
+      { 6549,-1550,-436,-4880,12435,2753,-854,1868,6976 } },
+    { LIBRAW_CAMERAMAKER_Sony, "NEX-3N", 0, 0,
+      { 6129,-1545,-418,-4930,12490,2743,-977,1693,6615 } },
+    { LIBRAW_CAMERAMAKER_Sony, "NEX-3", 0, 0,
+      { 6549,-1550,-436,-4880,12435,2753,-854,1868,6976 } },
+    { LIBRAW_CAMERAMAKER_Sony, "NEX-6", 0, 0,
+      { 6129,-1545,-418,-4930,12490,2743,-977,1693,6615 } },
+    { LIBRAW_CAMERAMAKER_Sony, "NEX-7", 0, 0,
+      { 5491,-1192,-363,-4951,12342,2948,-911,1722,7192 } },
+    { LIBRAW_CAMERAMAKER_Sony, "NEX-VG30", 0, 0,
+      { 6129,-1545,-418,-4930,12490,2743,-977,1693,6615 } },
+    { LIBRAW_CAMERAMAKER_Sony, "NEX-VG900", 0, 0,
+      { 6344,-1612,-462,-4863,12477,2681,-865,1786,6899 } },
+    { LIBRAW_CAMERAMAKER_Sony, "NEX", 0, 0, // same CMs: NEX-C3, NEX-F3, NEX-VG20
+      { 5991,-1456,-455,-4764,12135,2980,-707,1425,6701 } },
+    { LIBRAW_CAMERAMAKER_Sony, "SLT-A33", 0, 0,
+      { 6069,-1221,-366,-5221,12779,2734,-1024,2066,6834 } },
+    { LIBRAW_CAMERAMAKER_Sony, "SLT-A35", 0, 0,
+      { 5986,-1618,-415,-4557,11820,3120,-681,1404,6971 } },
+    { LIBRAW_CAMERAMAKER_Sony, "SLT-A37", 0, 0,
+      { 5991,-1456,-455,-4764,12135,2980,-707,1425,6701 } },
+    { LIBRAW_CAMERAMAKER_Sony, "SLT-A55", 0, 0,
+      { 5932,-1492,-411,-4813,12285,2856,-741,1524,6739 } },
+    { LIBRAW_CAMERAMAKER_Sony, "SLT-A5", 0, 0, // same CMs: SLT-A57, SLT-A58
+      { 5991,-1456,-455,-4764,12135,2980,-707,1425,6701 } },
+    { LIBRAW_CAMERAMAKER_Sony, "SLT-A65", 0, 0,
+      { 5491,-1192,-363,-4951,12342,2948,-911,1722,7192 } },
+    { LIBRAW_CAMERAMAKER_Sony, "SLT-A77", 0, 0,
+      { 5491,-1192,-363,-4951,12342,2948,-911,1722,7192 } },
+    { LIBRAW_CAMERAMAKER_Sony, "SLT-A99", 0, 0,
+      { 6344,-1612,-462,-4863,12477,2681,-865,1786,6899 } },
+    { LIBRAW_CAMERAMAKER_YI, "M1", 0, 0,
+      { 7712,-2059,-653,-3882,11494,2726,-710,1332,5958 } },
+  };
+  // clang-format on
+
+  double cam_xyz[4][3];
+  //char name[130];
+  int i, j;
+
+  if (colors > 4 || colors < 1)
+    return 1;
+
+  int bl4 = (cblack[0] + cblack[1] + cblack[2] + cblack[3]) / 4, bl64 = 0;
+  if (cblack[4] * cblack[5] > 0)
+  {
+    for (unsigned c = 0; c < 4096 && c < cblack[4] * cblack[5]; c++)
+      bl64 += cblack[c + 6];
+    bl64 /= cblack[4] * cblack[5];
+  }
+  int rblack = black + bl4 + bl64;
+
+  for (i = 0; i < int(sizeof table / sizeof *table); i++)
+  {
+	  if (table[i].m_idx == make_idx)
+	  {
+		  unsigned l = strlen(table[i].prefix);
+		  if (!l ||  !strncasecmp(t_model, table[i].prefix, l))
+		  {
+			  if (!dng_version)
+			  {
+				  if (table[i].t_black > 0)
+				  {
+					  black = (ushort)table[i].t_black;
+					  memset(cblack, 0, sizeof(cblack));
+				  }
+				  else if (table[i].t_black < 0 && rblack == 0)
+				  {
+					  black = (ushort)(-table[i].t_black);
+					  memset(cblack, 0, sizeof(cblack));
+				  }
+				  if (table[i].t_maximum)
+					  maximum = (ushort)table[i].t_maximum;
+			  }
+			  if (table[i].trans[0])
+			  {
+				  for (raw_color = j = 0; j < 12; j++)
+					  if (internal_only)
+						  imgdata.color.cam_xyz[j / 3][j % 3] = table[i].trans[j] / 10000.0;
+					  else
+						  imgdata.color.cam_xyz[j / 3][j % 3] = ((double *)cam_xyz)[j] =
+						  table[i].trans[j] / 10000.0;
+				  if (!internal_only)
+					  cam_xyz_coeff(rgb_cam, cam_xyz);
+			  }
+			  return 1; // CM found
+		  }
+	  }
+  }
+  return 0; // CM not found
+}
+void LibRaw::simple_coeff(int index)
+{
+  static const float table[][12] = {
+      /* index 0 -- all Foveon cameras */
+      {1.4032, -0.2231, -0.1016, -0.5263, 1.4816, 0.017, -0.0112, 0.0183,
+       0.9113},
+      /* index 1 -- Kodak DC20 and DC25 */
+      {2.25, 0.75, -1.75, -0.25, -0.25, 0.75, 0.75, -0.25, -0.25, -1.75, 0.75,
+       2.25},
+      /* index 2 -- Logitech Fotoman Pixtura */
+      {1.893, -0.418, -0.476, -0.495, 1.773, -0.278, -1.017, -0.655, 2.672},
+      /* index 3 -- Nikon E880, E900, and E990 */
+      {-1.936280, 1.800443, -1.448486, 2.584324, 1.405365, -0.524955, -0.289090,
+       0.408680, -1.204965, 1.082304, 2.941367, -1.818705}};
+  int i, c;
+
+  for (raw_color = i = 0; i < 3; i++)
+    FORCC rgb_cam[i][c] = table[index][i * MIN(colors,4) + c];
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/tables/wblists.cpp libkdcraw/libkdcraw/libraw/src/tables/wblists.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/tables/wblists.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/tables/wblists.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,210 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+
+#define _ARR_SZ(a) (sizeof(a)/sizeof(a[0]))
+
+static const int _tagtype_dataunit_bytes [19] = {
+    1, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8, 4, 2, 8, 8, 8, 8
+};
+
+libraw_static_table_t LibRaw::tagtype_dataunit_bytes(_tagtype_dataunit_bytes, _ARR_SZ(_tagtype_dataunit_bytes));
+
+static const int _Canon_wbi2std[] = { // Canon WB index to standard indexes
+//      std. number                wbi - Canon number
+    LIBRAW_WBI_Auto,             // 0
+    LIBRAW_WBI_Daylight,         // 1
+    LIBRAW_WBI_Cloudy,           // 2
+    LIBRAW_WBI_Tungsten,         // 3
+    LIBRAW_WBI_Fluorescent,      // 4
+    LIBRAW_WBI_Flash,            // 5
+    LIBRAW_WBI_Custom,           // 6
+    LIBRAW_WBI_BW,               // 7
+    LIBRAW_WBI_Shade,            // 8
+    LIBRAW_WBI_Kelvin,           // 9
+    LIBRAW_WBI_PC_Set1,          // 10
+    LIBRAW_WBI_PC_Set2,          // 11
+    LIBRAW_WBI_PC_Set3,          // 12
+    LIBRAW_WBI_Unknown,          // 13, unlucky number "13", not used
+    LIBRAW_WBI_FluorescentHigh,  // 14
+    LIBRAW_WBI_Custom1,          // 15
+    LIBRAW_WBI_Custom2,          // 16
+    LIBRAW_WBI_Underwater,       // 17, last one for older PowerShot models
+    LIBRAW_WBI_Custom3,          // 18
+    LIBRAW_WBI_Custom4,          // 19
+    LIBRAW_WBI_PC_Set4,          // 20
+    LIBRAW_WBI_PC_Set5,          // 21
+    LIBRAW_WBI_Unknown,          // 22
+    LIBRAW_WBI_Auto1             // 23
+};
+
+libraw_static_table_t LibRaw::Canon_wbi2std(_Canon_wbi2std, _ARR_SZ(_Canon_wbi2std));
+
+static const int _Canon_KeyIsZero_Len2048_linenums_2_StdWBi[] = { // Appendix A: G2, S30, S40; G3, G5, S45, S50
+  LIBRAW_WBI_Custom1,
+  LIBRAW_WBI_Custom2,
+  LIBRAW_WBI_Daylight,
+  LIBRAW_WBI_Cloudy,
+  LIBRAW_WBI_Tungsten,
+  LIBRAW_WBI_Fluorescent,
+  LIBRAW_WBI_Unknown, // ? FluorescentHigh, Shade, Custom, Kelvin
+  LIBRAW_WBI_Flash
+};
+
+libraw_static_table_t LibRaw::Canon_KeyIsZero_Len2048_linenums_2_StdWBi(_Canon_KeyIsZero_Len2048_linenums_2_StdWBi,
+    _ARR_SZ(_Canon_KeyIsZero_Len2048_linenums_2_StdWBi));
+
+static const int _Canon_KeyIs0x0410_Len3072_linenums_2_StdWBi[] = { // G6, S60, S70; offset +16
+  LIBRAW_WBI_Custom1,
+  LIBRAW_WBI_Custom2,
+  LIBRAW_WBI_Daylight,
+  LIBRAW_WBI_Cloudy,
+  LIBRAW_WBI_Tungsten,
+  LIBRAW_WBI_Fluorescent,
+  LIBRAW_WBI_FluorescentHigh, // LIBRAW_WBI_Unknown, // ? FluorescentHigh, Shade, Custom, Kelvin
+  LIBRAW_WBI_Unknown,
+  LIBRAW_WBI_Underwater, // LIBRAW_WBI_Unknown,
+  LIBRAW_WBI_Unknown,
+  LIBRAW_WBI_Flash
+};
+
+libraw_static_table_t LibRaw::Canon_KeyIs0x0410_Len3072_linenums_2_StdWBi(_Canon_KeyIs0x0410_Len3072_linenums_2_StdWBi,
+    _ARR_SZ(_Canon_KeyIs0x0410_Len3072_linenums_2_StdWBi));
+
+static const int _Canon_KeyIs0x0410_Len2048_linenums_2_StdWBi[] = { // Pro1; offset +8
+  LIBRAW_WBI_Custom1,
+  LIBRAW_WBI_Custom2,
+  LIBRAW_WBI_Daylight,
+  LIBRAW_WBI_Cloudy,
+  LIBRAW_WBI_Tungsten,
+  LIBRAW_WBI_Fluorescent,
+  LIBRAW_WBI_Unknown,
+  LIBRAW_WBI_Flash, // LIBRAW_WBI_Unknown,
+  LIBRAW_WBI_Unknown,
+  LIBRAW_WBI_Unknown,
+  LIBRAW_WBI_Unknown // LIBRAW_WBI_Flash
+};
+
+libraw_static_table_t LibRaw::Canon_KeyIs0x0410_Len2048_linenums_2_StdWBi(_Canon_KeyIs0x0410_Len2048_linenums_2_StdWBi,
+    _ARR_SZ(_Canon_KeyIs0x0410_Len2048_linenums_2_StdWBi));
+
+static const int _Canon_G9_linenums_2_StdWBi[] = {
+    LIBRAW_WBI_Auto,
+    LIBRAW_WBI_Daylight,
+    LIBRAW_WBI_Cloudy,
+    LIBRAW_WBI_Tungsten,
+    LIBRAW_WBI_Fluorescent,
+    LIBRAW_WBI_FluorescentHigh,
+    LIBRAW_WBI_Flash,
+    LIBRAW_WBI_Underwater,
+    LIBRAW_WBI_Custom1,
+    LIBRAW_WBI_Custom2
+};
+libraw_static_table_t LibRaw::Canon_G9_linenums_2_StdWBi(_Canon_G9_linenums_2_StdWBi, _ARR_SZ(_Canon_G9_linenums_2_StdWBi));
+
+static const int _Canon_D30_linenums_2_StdWBi[] = {
+    LIBRAW_WBI_Daylight,
+    LIBRAW_WBI_Cloudy,
+    LIBRAW_WBI_Tungsten,
+    LIBRAW_WBI_Fluorescent,
+    LIBRAW_WBI_Flash,
+    LIBRAW_WBI_Custom
+};
+libraw_static_table_t LibRaw::Canon_D30_linenums_2_StdWBi(_Canon_D30_linenums_2_StdWBi, _ARR_SZ(_Canon_D30_linenums_2_StdWBi));
+
+static const int _Fuji_wb_list1[] = {
+    LIBRAW_WBI_FineWeather, LIBRAW_WBI_Shade, LIBRAW_WBI_FL_D,
+    LIBRAW_WBI_FL_L,        LIBRAW_WBI_FL_W,  LIBRAW_WBI_Tungsten
+};
+libraw_static_table_t LibRaw::Fuji_wb_list1(_Fuji_wb_list1, _ARR_SZ(_Fuji_wb_list1));
+
+static const int _FujiCCT_K[31] = {
+    2500, 2550, 2650, 2700, 2800, 2850, 2950, 3000, 3100, 3200, 3300,
+    3400, 3600, 3700, 3800, 4000, 4200, 4300, 4500, 4800, 5000, 5300,
+    5600, 5900, 6300, 6700, 7100, 7700, 8300, 9100, 10000
+};
+libraw_static_table_t LibRaw::FujiCCT_K(_FujiCCT_K, _ARR_SZ(_FujiCCT_K));
+
+static const int _Fuji_wb_list2[] = {
+    LIBRAW_WBI_Auto,  0,  LIBRAW_WBI_Custom,   6,  LIBRAW_WBI_FineWeather, 1,
+    LIBRAW_WBI_Shade, 8,  LIBRAW_WBI_FL_D,     10, LIBRAW_WBI_FL_L,        11,
+    LIBRAW_WBI_FL_W,  12, LIBRAW_WBI_Tungsten, 2,  LIBRAW_WBI_Underwater,  35,
+    LIBRAW_WBI_Ill_A, 82, LIBRAW_WBI_D65,      83
+};
+libraw_static_table_t LibRaw::Fuji_wb_list2(_Fuji_wb_list2, _ARR_SZ(_Fuji_wb_list2));
+
+static const int _Pentax_wb_list1[] = {
+    LIBRAW_WBI_Daylight, LIBRAW_WBI_Shade,
+    LIBRAW_WBI_Cloudy,   LIBRAW_WBI_Tungsten,
+    LIBRAW_WBI_FL_D,     LIBRAW_WBI_FL_N,
+    LIBRAW_WBI_FL_W,     LIBRAW_WBI_Flash
+};
+libraw_static_table_t LibRaw::Pentax_wb_list1(_Pentax_wb_list1, _ARR_SZ(_Pentax_wb_list1));
+
+static const int _Pentax_wb_list2[] = {
+    LIBRAW_WBI_Daylight, LIBRAW_WBI_Shade, LIBRAW_WBI_Cloudy,
+    LIBRAW_WBI_Tungsten, LIBRAW_WBI_FL_D,  LIBRAW_WBI_FL_N,
+    LIBRAW_WBI_FL_W,     LIBRAW_WBI_Flash, LIBRAW_WBI_FL_L
+};
+libraw_static_table_t LibRaw::Pentax_wb_list2(_Pentax_wb_list2, _ARR_SZ(_Pentax_wb_list2));
+
+
+static const int _Oly_wb_list1[] = {
+    LIBRAW_WBI_Shade,    LIBRAW_WBI_Cloudy, LIBRAW_WBI_FineWeather,
+    LIBRAW_WBI_Tungsten, LIBRAW_WBI_Sunset, LIBRAW_WBI_FL_D,
+    LIBRAW_WBI_FL_N,     LIBRAW_WBI_FL_W,   LIBRAW_WBI_FL_WW
+};
+libraw_static_table_t LibRaw::Oly_wb_list1(_Oly_wb_list1, _ARR_SZ(_Oly_wb_list1));
+
+static const int _Oly_wb_list2[] = {
+    LIBRAW_WBI_Auto, 0,
+    LIBRAW_WBI_Tungsten, 3000,
+    0x100, 3300,
+    0x100, 3600,
+    0x100, 3900,
+    LIBRAW_WBI_FL_W, 4000,
+    0x100, 4300,
+    LIBRAW_WBI_FL_D, 4500,
+    0x100, 4800,
+    LIBRAW_WBI_FineWeather, 5300,
+    LIBRAW_WBI_Cloudy, 6000,
+    LIBRAW_WBI_FL_N, 6600,
+    LIBRAW_WBI_Shade, 7500,
+    LIBRAW_WBI_Custom1, 0,
+    LIBRAW_WBI_Custom2, 0,
+    LIBRAW_WBI_Custom3, 0,
+    LIBRAW_WBI_Custom4, 0
+};
+libraw_static_table_t LibRaw::Oly_wb_list2(_Oly_wb_list2, _ARR_SZ(_Oly_wb_list2));
+
+static const int _Sony_SRF_wb_list[] = {
+    LIBRAW_WBI_Daylight, LIBRAW_WBI_Cloudy, LIBRAW_WBI_Fluorescent,
+    LIBRAW_WBI_Tungsten, LIBRAW_WBI_Flash
+};
+libraw_static_table_t LibRaw::Sony_SRF_wb_list(_Sony_SRF_wb_list, _ARR_SZ(_Sony_SRF_wb_list));
+
+static const int _Sony_SR2_wb_list[] = {
+    LIBRAW_WBI_Daylight, LIBRAW_WBI_Cloudy, LIBRAW_WBI_Tungsten, LIBRAW_WBI_Flash,
+    4500, LIBRAW_WBI_Unknown, LIBRAW_WBI_Fluorescent
+};
+libraw_static_table_t LibRaw::Sony_SR2_wb_list(_Sony_SR2_wb_list, _ARR_SZ(_Sony_SR2_wb_list));
+
+static const int _Sony_SR2_wb_list1[] = {
+    LIBRAW_WBI_Daylight, LIBRAW_WBI_Cloudy, LIBRAW_WBI_Tungsten, LIBRAW_WBI_Flash,
+    4500, LIBRAW_WBI_Shade, LIBRAW_WBI_FL_W, LIBRAW_WBI_FL_N, LIBRAW_WBI_FL_D,
+    LIBRAW_WBI_FL_L, 8500, 6000, 3200, 2500
+};
+libraw_static_table_t LibRaw::Sony_SR2_wb_list1(_Sony_SR2_wb_list1, _ARR_SZ(_Sony_SR2_wb_list1));
\ No newline at end of file
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/utils/curves.cpp libkdcraw/libkdcraw/libraw/src/utils/curves.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/utils/curves.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/utils/curves.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,151 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+void LibRaw::cubic_spline(const int *x_, const int *y_, const int len)
+{
+  float **A, *b, *c, *d, *x, *y;
+  int i, j;
+
+  A = (float **)calloc(((2 * len + 4) * sizeof **A + sizeof *A), 2 * len);
+  if (!A)
+    return;
+  A[0] = (float *)(A + 2 * len);
+  for (i = 1; i < 2 * len; i++)
+    A[i] = A[0] + 2 * len * i;
+  y = len + (x = i + (d = i + (c = i + (b = A[0] + i * i))));
+  for (i = 0; i < len; i++)
+  {
+    x[i] = x_[i] / 65535.0;
+    y[i] = y_[i] / 65535.0;
+  }
+  for (i = len - 1; i > 0; i--)
+  {
+    b[i] = (y[i] - y[i - 1]) / (x[i] - x[i - 1]);
+    d[i - 1] = x[i] - x[i - 1];
+  }
+  for (i = 1; i < len - 1; i++)
+  {
+    A[i][i] = 2 * (d[i - 1] + d[i]);
+    if (i > 1)
+    {
+      A[i][i - 1] = d[i - 1];
+      A[i - 1][i] = d[i - 1];
+    }
+    A[i][len - 1] = 6 * (b[i + 1] - b[i]);
+  }
+  for (i = 1; i < len - 2; i++)
+  {
+    float v = A[i + 1][i] / A[i][i];
+    for (j = 1; j <= len - 1; j++)
+      A[i + 1][j] -= v * A[i][j];
+  }
+  for (i = len - 2; i > 0; i--)
+  {
+    float acc = 0;
+    for (j = i; j <= len - 2; j++)
+      acc += A[i][j] * c[j];
+    c[i] = (A[i][len - 1] - acc) / A[i][i];
+  }
+  for (i = 0; i < 0x10000; i++)
+  {
+    float x_out = (float)(i / 65535.0);
+    float y_out = 0;
+    for (j = 0; j < len - 1; j++)
+    {
+      if (x[j] <= x_out && x_out <= x[j + 1])
+      {
+        float v = x_out - x[j];
+        y_out = y[j] +
+                ((y[j + 1] - y[j]) / d[j] -
+                 (2 * d[j] * c[j] + c[j + 1] * d[j]) / 6) *
+                    v +
+                (c[j] * 0.5) * v * v +
+                ((c[j + 1] - c[j]) / (6 * d[j])) * v * v * v;
+      }
+    }
+    curve[i] = y_out < 0.0
+                   ? 0
+                   : (y_out >= 1.0 ? 65535 : (ushort)(y_out * 65535.0 + 0.5));
+  }
+  free(A);
+}
+void LibRaw::gamma_curve(double pwr, double ts, int mode, int imax)
+{
+  int i;
+  double g[6], bnd[2] = {0, 0}, r;
+
+  g[0] = pwr;
+  g[1] = ts;
+  g[2] = g[3] = g[4] = 0;
+  bnd[g[1] >= 1] = 1;
+  if (g[1] && (g[1] - 1) * (g[0] - 1) <= 0)
+  {
+    for (i = 0; i < 48; i++)
+    {
+      g[2] = (bnd[0] + bnd[1]) / 2;
+      if (g[0])
+        bnd[(pow(g[2] / g[1], -g[0]) - 1) / g[0] - 1 / g[2] > -1] = g[2];
+      else
+        bnd[g[2] / exp(1 - 1 / g[2]) < g[1]] = g[2];
+    }
+    g[3] = g[2] / g[1];
+    if (g[0])
+      g[4] = g[2] * (1 / g[0] - 1);
+  }
+  if (g[0])
+    g[5] = 1 / (g[1] * SQR(g[3]) / 2 - g[4] * (1 - g[3]) +
+                (1 - pow(g[3], 1 + g[0])) * (1 + g[4]) / (1 + g[0])) -
+           1;
+  else
+    g[5] = 1 / (g[1] * SQR(g[3]) / 2 + 1 - g[2] - g[3] -
+                g[2] * g[3] * (log(g[3]) - 1)) -
+           1;
+  if (!mode--)
+  {
+    memcpy(gamm, g, sizeof gamm);
+    return;
+  }
+  for (i = 0; i < 0x10000; i++)
+  {
+    curve[i] = 0xffff;
+    if ((r = (double)i / imax) < 1)
+      curve[i] =
+          0x10000 *
+          (mode ? (r < g[3] ? r * g[1]
+                            : (g[0] ? pow(r, g[0]) * (1 + g[4]) - g[4]
+                                    : log(r) * g[2] + 1))
+                : (r < g[2] ? r / g[1]
+                            : (g[0] ? pow((r + g[4]) / (1 + g[4]), 1 / g[0])
+                                    : exp((r - 1) / g[2]))));
+  }
+}
+
+void LibRaw::linear_table(unsigned len)
+{
+  int i;
+  if (len > 0x10000)
+    len = 0x10000;
+  else if (len < 1)
+    return;
+  read_shorts(curve, len);
+  for (i = len; i < 0x10000; i++)
+    curve[i] = curve[i - 1];
+  maximum = curve[len < 0x1000 ? 0xfff : len - 1];
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/utils/decoder_info.cpp libkdcraw/libkdcraw/libraw/src/utils/decoder_info.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/utils/decoder_info.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/utils/decoder_info.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,389 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+#include "../../internal/libraw_cxx_defs.h"
+
+const char *LibRaw::unpack_function_name()
+{
+  libraw_decoder_info_t decoder_info;
+  get_decoder_info(&decoder_info);
+  return decoder_info.decoder_name;
+}
+
+int LibRaw::get_decoder_info(libraw_decoder_info_t *d_info)
+{
+  if (!d_info)
+    return LIBRAW_UNSPECIFIED_ERROR;
+  d_info->decoder_name = 0;
+  d_info->decoder_flags = 0;
+  if (!load_raw)
+    return LIBRAW_OUT_OF_ORDER_CALL;
+
+  // dcraw.c names order
+  if (load_raw == &LibRaw::android_tight_load_raw)
+  {
+    d_info->decoder_name = "android_tight_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_FIXEDMAXC;
+  }
+  else if (load_raw == &LibRaw::android_loose_load_raw)
+  {
+    d_info->decoder_name = "android_loose_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_FIXEDMAXC;
+  }
+  else if (load_raw == &LibRaw::float_dng_load_raw_placeholder)
+  {
+    d_info->decoder_name = "float_dng_load_raw_placeholder()";
+  }
+  else if (load_raw == &LibRaw::vc5_dng_load_raw_placeholder)
+  {
+      d_info->decoder_name = "vc5_dng_load_raw_placeholder()";
+  }
+  else if (load_raw == &LibRaw::canon_600_load_raw)
+  {
+    d_info->decoder_name = "canon_600_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_FIXEDMAXC;
+  }
+  else if (load_raw == &LibRaw::fuji_compressed_load_raw)
+  {
+    d_info->decoder_name = "fuji_compressed_load_raw()";
+  }
+  else if (load_raw == &LibRaw::fuji_14bit_load_raw)
+  {
+    d_info->decoder_name = "fuji_14bit_load_raw()";
+  }
+  else if (load_raw == &LibRaw::canon_load_raw)
+  {
+    d_info->decoder_name = "canon_load_raw()";
+  }
+  else if (load_raw == &LibRaw::lossless_jpeg_load_raw)
+  {
+    d_info->decoder_name = "lossless_jpeg_load_raw()";
+    d_info->decoder_flags =
+        LIBRAW_DECODER_HASCURVE | LIBRAW_DECODER_TRYRAWSPEED;
+  }
+  else if (load_raw == &LibRaw::canon_sraw_load_raw)
+  {
+    d_info->decoder_name = "canon_sraw_load_raw()";
+    // d_info->decoder_flags = LIBRAW_DECODER_TRYRAWSPEED;
+  }
+  else if (load_raw == &LibRaw::crxLoadRaw)
+  {
+    d_info->decoder_name = "crxLoadRaw()";
+  }
+  else if (load_raw == &LibRaw::lossless_dng_load_raw)
+  {
+    d_info->decoder_name = "lossless_dng_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_HASCURVE |
+                            LIBRAW_DECODER_TRYRAWSPEED |
+                            LIBRAW_DECODER_ADOBECOPYPIXEL;
+  }
+  else if (load_raw == &LibRaw::packed_dng_load_raw)
+  {
+    d_info->decoder_name = "packed_dng_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_HASCURVE |
+                            LIBRAW_DECODER_TRYRAWSPEED |
+                            LIBRAW_DECODER_ADOBECOPYPIXEL;
+  }
+  else if (load_raw == &LibRaw::pentax_load_raw)
+  {
+    d_info->decoder_name = "pentax_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_TRYRAWSPEED;
+  }
+  else if (load_raw == &LibRaw::nikon_load_raw)
+  {
+    d_info->decoder_name = "nikon_load_raw()";
+    d_info->decoder_flags =
+        LIBRAW_DECODER_TRYRAWSPEED | LIBRAW_DECODER_HASCURVE;
+  }
+  else if (load_raw == &LibRaw::nikon_coolscan_load_raw)
+  {
+    d_info->decoder_name = "nikon_coolscan_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_FIXEDMAXC;
+  }
+  else if (load_raw == &LibRaw::nikon_load_sraw)
+  {
+    d_info->decoder_name = "nikon_load_sraw()";
+    d_info->decoder_flags = LIBRAW_DECODER_HASCURVE | LIBRAW_DECODER_FIXEDMAXC;
+  }
+  else if (load_raw == &LibRaw::nikon_yuv_load_raw)
+  {
+    d_info->decoder_name = "nikon_load_yuv_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_HASCURVE | LIBRAW_DECODER_FIXEDMAXC;
+  }
+  else if (load_raw == &LibRaw::rollei_load_raw)
+  {
+    // UNTESTED
+    d_info->decoder_name = "rollei_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_FIXEDMAXC;
+  }
+  else if (load_raw == &LibRaw::phase_one_load_raw)
+  {
+    d_info->decoder_name = "phase_one_load_raw()";
+  }
+  else if (load_raw == &LibRaw::phase_one_load_raw_c)
+  {
+    d_info->decoder_name = "phase_one_load_raw_c()";
+  }
+  else if (load_raw == &LibRaw::hasselblad_load_raw)
+  {
+    d_info->decoder_name = "hasselblad_load_raw()";
+  }
+  else if (load_raw == &LibRaw::leaf_hdr_load_raw)
+  {
+    d_info->decoder_name = "leaf_hdr_load_raw()";
+  }
+  else if (load_raw == &LibRaw::unpacked_load_raw)
+  {
+    d_info->decoder_name = "unpacked_load_raw()";
+	d_info->decoder_flags = LIBRAW_DECODER_FLATDATA;
+  }
+  else if (load_raw == &LibRaw::unpacked_load_raw_reversed)
+  {
+    d_info->decoder_name = "unpacked_load_raw_reversed()";
+    d_info->decoder_flags = LIBRAW_DECODER_FIXEDMAXC;
+  }
+  else if (load_raw == &LibRaw::sinar_4shot_load_raw)
+  {
+    // UNTESTED
+    d_info->decoder_name = "sinar_4shot_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_SINAR4SHOT;
+  }
+  else if (load_raw == &LibRaw::imacon_full_load_raw)
+  {
+    d_info->decoder_name = "imacon_full_load_raw()";
+  }
+  else if (load_raw == &LibRaw::hasselblad_full_load_raw)
+  {
+    d_info->decoder_name = "hasselblad_full_load_raw()";
+  }
+  else if (load_raw == &LibRaw::packed_load_raw)
+  {
+    d_info->decoder_name = "packed_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_TRYRAWSPEED;
+  }
+  else if (load_raw == &LibRaw::broadcom_load_raw)
+  {
+    // UNTESTED
+    d_info->decoder_name = "broadcom_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_FIXEDMAXC;
+  }
+  else if (load_raw == &LibRaw::nokia_load_raw)
+  {
+    // UNTESTED
+    d_info->decoder_name = "nokia_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_FIXEDMAXC;
+  }
+  else if (load_raw == &LibRaw::canon_rmf_load_raw)
+  {
+    // UNTESTED
+    d_info->decoder_name = "canon_rmf_load_raw()";
+  }
+  else if (load_raw == &LibRaw::panasonic_load_raw)
+  {
+    d_info->decoder_name = "panasonic_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_TRYRAWSPEED;
+  }
+  else if (load_raw == &LibRaw::panasonicC6_load_raw)
+  {
+    d_info->decoder_name = "panasonicC6_load_raw()";
+  }
+  else if (load_raw == &LibRaw::panasonicC7_load_raw)
+  {
+    d_info->decoder_name = "panasonicC7_load_raw()";
+  }
+  else if (load_raw == &LibRaw::olympus_load_raw)
+  {
+    d_info->decoder_name = "olympus_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_TRYRAWSPEED;
+  }
+  else if (load_raw == &LibRaw::minolta_rd175_load_raw)
+  {
+    // UNTESTED
+    d_info->decoder_name = "minolta_rd175_load_raw()";
+  }
+  else if (load_raw == &LibRaw::quicktake_100_load_raw)
+  {
+    // UNTESTED
+    d_info->decoder_name = "quicktake_100_load_raw()";
+  }
+  else if (load_raw == &LibRaw::kodak_radc_load_raw)
+  {
+    d_info->decoder_name = "kodak_radc_load_raw()";
+  }
+  else if (load_raw == &LibRaw::kodak_jpeg_load_raw)
+  {
+    // UNTESTED + RBAYER
+    d_info->decoder_name = "kodak_jpeg_load_raw()";
+  }
+  else if (load_raw == &LibRaw::lossy_dng_load_raw)
+  {
+    // Check rbayer
+    d_info->decoder_name = "lossy_dng_load_raw()";
+    d_info->decoder_flags =
+        LIBRAW_DECODER_TRYRAWSPEED | LIBRAW_DECODER_HASCURVE;
+  }
+  else if (load_raw == &LibRaw::kodak_dc120_load_raw)
+  {
+    d_info->decoder_name = "kodak_dc120_load_raw()";
+  }
+  else if (load_raw == &LibRaw::eight_bit_load_raw)
+  {
+    d_info->decoder_name = "eight_bit_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_HASCURVE | LIBRAW_DECODER_FIXEDMAXC;
+  }
+  else if (load_raw == &LibRaw::kodak_c330_load_raw)
+  {
+    d_info->decoder_name = "kodak_yrgb_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_HASCURVE | LIBRAW_DECODER_FIXEDMAXC;
+  }
+  else if (load_raw == &LibRaw::kodak_c603_load_raw)
+  {
+    d_info->decoder_name = "kodak_yrgb_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_HASCURVE | LIBRAW_DECODER_FIXEDMAXC;
+  }
+  else if (load_raw == &LibRaw::kodak_262_load_raw)
+  {
+    d_info->decoder_name = "kodak_262_load_raw()"; // UNTESTED!
+    d_info->decoder_flags = LIBRAW_DECODER_HASCURVE | LIBRAW_DECODER_FIXEDMAXC;
+  }
+  else if (load_raw == &LibRaw::kodak_65000_load_raw)
+  {
+    d_info->decoder_name = "kodak_65000_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_HASCURVE;
+  }
+  else if (load_raw == &LibRaw::kodak_ycbcr_load_raw)
+  {
+    // UNTESTED
+    d_info->decoder_name = "kodak_ycbcr_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_HASCURVE | LIBRAW_DECODER_FIXEDMAXC;
+  }
+  else if (load_raw == &LibRaw::kodak_rgb_load_raw)
+  {
+    // UNTESTED
+    d_info->decoder_name = "kodak_rgb_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_FIXEDMAXC;
+  }
+  else if (load_raw == &LibRaw::sony_load_raw)
+  {
+    d_info->decoder_name = "sony_load_raw()";
+  }
+  else if (load_raw == &LibRaw::sony_arw_load_raw)
+  {
+    d_info->decoder_name = "sony_arw_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_TRYRAWSPEED;
+  }
+  else if (load_raw == &LibRaw::sony_arw2_load_raw)
+  {
+    d_info->decoder_name = "sony_arw2_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_HASCURVE |
+                            LIBRAW_DECODER_TRYRAWSPEED |
+                            LIBRAW_DECODER_SONYARW2;
+  }
+  else if (load_raw == &LibRaw::sony_arq_load_raw)
+  {
+    d_info->decoder_name = "sony_arq_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_LEGACY_WITH_MARGINS | LIBRAW_DECODER_FLATDATA | LIBRAW_DECODER_FLAT_BG2_SWAPPED;
+  }
+  else if (load_raw == &LibRaw::samsung_load_raw)
+  {
+    d_info->decoder_name = "samsung_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_TRYRAWSPEED;
+  }
+  else if (load_raw == &LibRaw::samsung2_load_raw)
+  {
+    d_info->decoder_name = "samsung2_load_raw()";
+  }
+  else if (load_raw == &LibRaw::samsung3_load_raw)
+  {
+    d_info->decoder_name = "samsung3_load_raw()";
+  }
+  else if (load_raw == &LibRaw::smal_v6_load_raw)
+  {
+    // UNTESTED
+    d_info->decoder_name = "smal_v6_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_FIXEDMAXC;
+  }
+  else if (load_raw == &LibRaw::smal_v9_load_raw)
+  {
+    // UNTESTED
+    d_info->decoder_name = "smal_v9_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_FIXEDMAXC;
+  }
+  else if (load_raw == &LibRaw::redcine_load_raw)
+  {
+    d_info->decoder_name = "redcine_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_HASCURVE;
+  }
+  else if (load_raw == &LibRaw::x3f_load_raw)
+  {
+    d_info->decoder_name = "x3f_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_OWNALLOC | LIBRAW_DECODER_FIXEDMAXC |
+                            LIBRAW_DECODER_LEGACY_WITH_MARGINS;
+  }
+  else if (load_raw == &LibRaw::pentax_4shot_load_raw)
+  {
+    d_info->decoder_name = "pentax_4shot_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_OWNALLOC;
+  }
+  else if (load_raw == &LibRaw::deflate_dng_load_raw)
+  {
+    d_info->decoder_name = "deflate_dng_load_raw()";
+    d_info->decoder_flags = LIBRAW_DECODER_OWNALLOC;
+  }
+  else if (load_raw == &LibRaw::nikon_load_striped_packed_raw)
+  {
+    d_info->decoder_name = "nikon_load_striped_packed_raw()";
+  }
+  else if (load_raw == &LibRaw::nikon_load_padded_packed_raw)
+  {
+    d_info->decoder_name = "nikon_load_padded_packed_raw()";
+  }
+  else if (load_raw == &LibRaw::nikon_14bit_load_raw)
+  {
+    d_info->decoder_name = "nikon_14bit_load_raw()";
+  }
+  /* -- added 07/02/18 -- */
+  else if (load_raw == &LibRaw::unpacked_load_raw_fuji_f700s20)
+  {
+    d_info->decoder_name = "unpacked_load_raw_fuji_f700s20()";
+  }
+  else if (load_raw == &LibRaw::unpacked_load_raw_FujiDBP)
+  {
+    d_info->decoder_name = "unpacked_load_raw_FujiDBP()";
+  }
+#ifdef USE_6BY9RPI
+  else if (load_raw == &LibRaw::rpi_load_raw8)
+  {
+	d_info->decoder_name = "rpi_load_raw8";
+  }
+  else if (load_raw == &LibRaw::rpi_load_raw12)
+  {
+	d_info->decoder_name = "rpi_load_raw12";
+  }
+  else if (load_raw == &LibRaw::rpi_load_raw14)
+  {
+	d_info->decoder_name = "rpi_load_raw14";
+  }
+  else if (load_raw == &LibRaw::rpi_load_raw16)
+  {
+	d_info->decoder_name = "rpi_load_raw16";
+  }
+#endif
+  else
+  {
+    d_info->decoder_name = "Unknown unpack function";
+    d_info->decoder_flags = LIBRAW_DECODER_NOTSET;
+  }
+  return LIBRAW_SUCCESS;
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/utils/init_close_utils.cpp libkdcraw/libkdcraw/libraw/src/utils/init_close_utils.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/utils/init_close_utils.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/utils/init_close_utils.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,269 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/libraw_cxx_defs.h"
+
+static void cleargps(libraw_gps_info_t *q)
+{
+  for (int i = 0; i < 3; i++)
+    q->latitude[i] = q->longitude[i] = q->gpstimestamp[i] = 0.f;
+  q->altitude = 0.f;
+  q->altref = q->latref = q->longref = q->gpsstatus = q->gpsparsed = 0;
+}
+
+LibRaw::LibRaw(unsigned int flags) : memmgr(1024)
+{
+  double aber[4] = {1, 1, 1, 1};
+  double gamm[6] = {0.45, 4.5, 0, 0, 0, 0};
+  unsigned greybox[4] = {0, 0, UINT_MAX, UINT_MAX};
+  unsigned cropbox[4] = {0, 0, UINT_MAX, UINT_MAX};
+  ZERO(imgdata);
+
+  cleargps(&imgdata.other.parsed_gps);
+  ZERO(libraw_internal_data);
+  ZERO(callbacks);
+
+  _rawspeed_camerameta = _rawspeed_decoder = NULL;
+  dnghost = NULL;
+  dngnegative = NULL;
+  dngimage = NULL;
+  _x3f_data = NULL;
+
+#ifdef USE_RAWSPEED
+  CameraMetaDataLR *camerameta =
+      make_camera_metadata(); // May be NULL in case of exception in
+                              // make_camera_metadata()
+  _rawspeed_camerameta = static_cast<void *>(camerameta);
+#endif
+  callbacks.mem_cb = (flags & LIBRAW_OPIONS_NO_MEMERR_CALLBACK)
+                         ? NULL
+                         : &default_memory_callback;
+  callbacks.data_cb = (flags & LIBRAW_OPIONS_NO_DATAERR_CALLBACK)
+                          ? NULL
+                          : &default_data_callback;
+  callbacks.exif_cb = NULL; // no default callback
+  callbacks.pre_identify_cb = NULL;
+  callbacks.post_identify_cb = NULL;
+  callbacks.pre_subtractblack_cb = callbacks.pre_scalecolors_cb =
+      callbacks.pre_preinterpolate_cb = callbacks.pre_interpolate_cb =
+          callbacks.interpolate_bayer_cb = callbacks.interpolate_xtrans_cb =
+              callbacks.post_interpolate_cb = callbacks.pre_converttorgb_cb =
+                  callbacks.post_converttorgb_cb = NULL;
+
+  memmove(&imgdata.params.aber, &aber, sizeof(aber));
+  memmove(&imgdata.params.gamm, &gamm, sizeof(gamm));
+  memmove(&imgdata.params.greybox, &greybox, sizeof(greybox));
+  memmove(&imgdata.params.cropbox, &cropbox, sizeof(cropbox));
+
+  imgdata.params.bright = 1;
+  imgdata.params.use_camera_matrix = 1;
+  imgdata.params.user_flip = -1;
+  imgdata.params.user_black = -1;
+  imgdata.params.user_cblack[0] = imgdata.params.user_cblack[1] =
+      imgdata.params.user_cblack[2] = imgdata.params.user_cblack[3] = -1000001;
+  imgdata.params.user_sat = -1;
+  imgdata.params.user_qual = -1;
+  imgdata.params.output_color = 1;
+  imgdata.params.output_bps = 8;
+  imgdata.params.use_fuji_rotate = 1;
+  imgdata.params.exp_shift = 1.0;
+  imgdata.params.auto_bright_thr = LIBRAW_DEFAULT_AUTO_BRIGHTNESS_THRESHOLD;
+  imgdata.params.adjust_maximum_thr = LIBRAW_DEFAULT_ADJUST_MAXIMUM_THRESHOLD;
+  imgdata.params.use_rawspeed = 1;
+  imgdata.params.use_dngsdk = LIBRAW_DNG_DEFAULT;
+  imgdata.params.no_auto_scale = 0;
+  imgdata.params.no_interpolation = 0;
+  imgdata.params.raw_processing_options = LIBRAW_PROCESSING_DP2Q_INTERPOLATERG |
+                                          LIBRAW_PROCESSING_DP2Q_INTERPOLATEAF |
+                                          LIBRAW_PROCESSING_CONVERTFLOAT_TO_INT;
+  imgdata.params.sony_arw2_posterization_thr = 0;
+  imgdata.params.max_raw_memory_mb = LIBRAW_MAX_ALLOC_MB_DEFAULT;
+  imgdata.params.green_matching = 0;
+  imgdata.params.custom_camera_strings = 0;
+  imgdata.params.coolscan_nef_gamma = 1.0f;
+  imgdata.parent_class = this;
+  imgdata.progress_flags = 0;
+  imgdata.color.dng_levels.baseline_exposure = -999.f;
+  imgdata.color.dng_levels.LinearResponseLimit = 1.0f;
+  MN.hasselblad.nIFD_CM[0] =
+    MN.hasselblad.nIFD_CM[1] = -1;
+  MN.kodak.ISOCalibrationGain = 1.0f;
+  _exitflag = 0;
+  tls = new LibRaw_TLS;
+  tls->init();
+}
+
+LibRaw::~LibRaw()
+{
+  recycle();
+  delete tls;
+#ifdef USE_RAWSPEED
+  if (_rawspeed_camerameta)
+  {
+    CameraMetaDataLR *cmeta =
+        static_cast<CameraMetaDataLR *>(_rawspeed_camerameta);
+    delete cmeta;
+    _rawspeed_camerameta = NULL;
+  }
+#endif
+}
+
+void x3f_clear(void *);
+
+void LibRaw::recycle()
+{
+  recycle_datastream();
+#define FREE(a)                                                                \
+  do                                                                           \
+  {                                                                            \
+    if (a)                                                                     \
+    {                                                                          \
+      free(a);                                                                 \
+      a = NULL;                                                                \
+    }                                                                          \
+  } while (0)
+
+  FREE(imgdata.image);
+
+  FREE(imgdata.thumbnail.thumb);
+  FREE(libraw_internal_data.internal_data.meta_data);
+  FREE(libraw_internal_data.output_data.histogram);
+  FREE(libraw_internal_data.output_data.oprof);
+  FREE(imgdata.color.profile);
+  FREE(imgdata.rawdata.ph1_cblack);
+  FREE(imgdata.rawdata.ph1_rblack);
+  FREE(imgdata.rawdata.raw_alloc);
+  FREE(imgdata.idata.xmpdata);
+
+#undef FREE
+
+  ZERO(imgdata.sizes);
+  imgdata.sizes.raw_inset_crop.cleft = 0xffff;
+  imgdata.sizes.raw_inset_crop.ctop = 0xffff;
+
+  ZERO(imgdata.idata);
+  ZERO(MN);
+  ZERO(imgdata.color);
+  ZERO(imgdata.other);
+  ZERO(imgdata.thumbnail);
+  ZERO(imgdata.rawdata);
+  cleargps(&imgdata.other.parsed_gps);
+  imgdata.color.dng_levels.baseline_exposure = -999.f;
+  imgdata.color.dng_levels.LinearResponseLimit = 1.f;
+
+  MN.canon.SensorLeftBorder = -1;
+  MN.canon.SensorTopBorder = -1;
+
+  MN.nikon.SensorHighSpeedCrop.cleft = 0xffff;
+  MN.nikon.SensorHighSpeedCrop.ctop = 0xffff;
+
+  MN.fuji.WB_Preset = 0xffff;
+  MN.fuji.ExpoMidPointShift = -999.f;
+  MN.fuji.DynamicRange = 0xffff;
+  MN.fuji.FilmMode = 0xffff;
+  MN.fuji.DynamicRangeSetting = 0xffff;
+  MN.fuji.DevelopmentDynamicRange = 0xffff;
+  MN.fuji.AutoDynamicRange = 0xffff;
+  MN.fuji.DRangePriority = 0xffff;
+  MN.fuji.FocusMode = 0xffff;
+  MN.fuji.DriveMode = -1;
+  MN.fuji.AFMode = 0xffff;
+  MN.fuji.FocusPixel[0] = MN.fuji.FocusPixel[1] = 0xffff;
+  MN.fuji.ImageStabilization[0] =
+      MN.fuji.ImageStabilization[1] =
+      MN.fuji.ImageStabilization[2] = 0xffff;
+
+  MN.samsung.ColorSpace[0] = MN.samsung.ColorSpace[1] = -1;
+
+  MN.sony.CameraType = 0xffff;
+  MN.sony.real_iso_offset = 0xffff;
+  MN.sony.ImageCount3_offset = 0xffff;
+  MN.sony.ElectronicFrontCurtainShutter = 0xffff;
+  MN.sony.MinoltaCamID = 0xffffffff;
+  MN.sony.RAWFileType = 0xffff;
+  MN.sony.AFAreaModeSetting = 0xff;
+  MN.sony.FlexibleSpotPosition[0] =
+      MN.sony.FlexibleSpotPosition[1] = 0xffff;
+  MN.sony.AFPointSelected = 0xff;
+  MN.sony.LongExposureNoiseReduction = 0xffffffff;
+  MN.sony.Quality = 0xffffffff;
+  MN.sony.HighISONoiseReduction = 0xffff;
+  MN.sony.SonyRawFileType = 0xffff;
+
+  MN.kodak.BlackLevelTop = 0xffff;
+  MN.kodak.BlackLevelBottom = 0xffff;
+  MN.kodak.ISOCalibrationGain = 1.0f;
+
+  MN.hasselblad.nIFD_CM[0] = MN.hasselblad.nIFD_CM[1] = -1;
+
+  imgdata.color.dng_color[0].illuminant =
+      imgdata.color.dng_color[1].illuminant = LIBRAW_WBI_None;
+
+  for (int i = 0; i < 4; i++)
+    imgdata.color.dng_levels.analogbalance[i] = 1.0f;
+
+  ZERO(libraw_internal_data);
+  ZERO(imgdata.lens);
+  imgdata.lens.makernotes.FocalUnits = 1;
+  imgdata.lens.makernotes.LensID = LIBRAW_LENS_NOT_SET;
+  ZERO(imgdata.shootinginfo);
+  imgdata.shootinginfo.DriveMode = -1;
+  imgdata.shootinginfo.FocusMode = -1;
+  imgdata.shootinginfo.MeteringMode = -1;
+  imgdata.shootinginfo.AFPoint = -1;
+  imgdata.shootinginfo.ExposureMode = -1;
+  imgdata.shootinginfo.ExposureProgram = -1;
+  imgdata.shootinginfo.ImageStabilization = -1;
+
+  _exitflag = 0;
+#ifdef USE_RAWSPEED
+  if (_rawspeed_decoder)
+  {
+    RawSpeed::RawDecoder *d =
+        static_cast<RawSpeed::RawDecoder *>(_rawspeed_decoder);
+    delete d;
+  }
+  _rawspeed_decoder = 0;
+#endif
+#ifdef USE_DNGSDK
+  if (dngnegative)
+  {
+    dng_negative *ng = (dng_negative *)dngnegative;
+    delete ng;
+    dngnegative = 0;
+  }
+  if(dngimage)
+  {
+      dng_image *dimage = (dng_image*)dngimage;
+      delete dimage;
+      dngimage = 0;
+  }
+#endif
+#ifdef USE_X3FTOOLS
+  if (_x3f_data)
+  {
+    x3f_clear(_x3f_data);
+    _x3f_data = 0;
+  }
+#endif
+  memmgr.cleanup();
+
+  imgdata.thumbnail.tformat = LIBRAW_THUMBNAIL_UNKNOWN;
+  imgdata.progress_flags = 0;
+
+  load_raw = thumb_load_raw = 0;
+
+  tls->init();
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/utils/open.cpp libkdcraw/libkdcraw/libraw/src/utils/open.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/utils/open.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/utils/open.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,1032 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/libraw_cxx_defs.h"
+#include "../../internal/libraw_cameraids.h"
+
+int LibRaw::open_file(const char *fname, INT64 max_buf_size)
+{
+	int big = 0;
+	if (max_buf_size == LIBRAW_OPEN_BIGFILE)
+		big = 1;
+	else if (max_buf_size == LIBRAW_OPEN_FILE)
+		big = 0;
+	else
+	{
+#ifndef LIBRAW_WIN32_CALLS
+		struct stat st;
+		if (stat(fname, &st))
+			return LIBRAW_IO_ERROR;
+		big = (st.st_size > max_buf_size) ? 1 : 0;
+#else
+		struct _stati64 st;
+		if (_stati64(fname, &st))
+			return LIBRAW_IO_ERROR;
+		big = (st.st_size > max_buf_size) ? 1 : 0;
+#endif
+	}
+
+  LibRaw_abstract_datastream *stream;
+  try
+  {
+    if (big)
+      stream = new LibRaw_bigfile_datastream(fname);
+    else
+      stream = new LibRaw_file_datastream(fname);
+  }
+
+  catch (const std::bad_alloc& )
+  {
+    recycle();
+    return LIBRAW_UNSUFFICIENT_MEMORY;
+  }
+  if (!stream->valid())
+  {
+    delete stream;
+    return LIBRAW_IO_ERROR;
+  }
+  ID.input_internal = 0; // preserve from deletion on error
+  int ret = open_datastream(stream);
+  if (ret == LIBRAW_SUCCESS)
+  {
+    ID.input_internal = 1; // flag to delete datastream on recycle
+  }
+  else
+  {
+    delete stream;
+    ID.input_internal = 0;
+  }
+  return ret;
+}
+
+#if defined(WIN32) || defined(_WIN32)
+#ifndef LIBRAW_WIN32_UNICODEPATHS
+int LibRaw::open_file(const wchar_t *, INT64)
+{
+  return LIBRAW_NOT_IMPLEMENTED;
+}
+#else
+int LibRaw::open_file(const wchar_t *fname, INT64 max_buf_size)
+{
+	int big = 0;
+	if (max_buf_size == LIBRAW_OPEN_BIGFILE)
+		big = 1;
+	else if (max_buf_size == LIBRAW_OPEN_FILE)
+		big = 0;
+	else
+	{
+		struct _stati64 st;
+		if (_wstati64(fname, &st))
+			return LIBRAW_IO_ERROR;
+		big = (st.st_size > max_buf_size) ? 1 : 0;
+	}
+
+  LibRaw_abstract_datastream *stream;
+  try
+  {
+    if (big)
+      stream = new LibRaw_bigfile_datastream(fname);
+    else
+      stream = new LibRaw_file_datastream(fname);
+  }
+
+  catch (std::bad_alloc)
+  {
+    recycle();
+    return LIBRAW_UNSUFFICIENT_MEMORY;
+  }
+  if (!stream->valid())
+  {
+    delete stream;
+    return LIBRAW_IO_ERROR;
+  }
+  ID.input_internal = 0; // preserve from deletion on error
+  int ret = open_datastream(stream);
+  if (ret == LIBRAW_SUCCESS)
+  {
+    ID.input_internal = 1; // flag to delete datastream on recycle
+  }
+  else
+  {
+    delete stream;
+    ID.input_internal = 0;
+  }
+  return ret;
+}
+#endif
+#endif
+
+int LibRaw::open_buffer(void *buffer, size_t size)
+{
+  // this stream will close on recycle()
+  if (!buffer || buffer == (void *)-1)
+    return LIBRAW_IO_ERROR;
+
+  LibRaw_buffer_datastream *stream;
+  try
+  {
+    stream = new LibRaw_buffer_datastream(buffer, size);
+  }
+  catch (const std::bad_alloc& )
+  {
+    recycle();
+    return LIBRAW_UNSUFFICIENT_MEMORY;
+  }
+  if (!stream->valid())
+  {
+    delete stream;
+    return LIBRAW_IO_ERROR;
+  }
+  ID.input_internal = 0; // preserve from deletion on error
+  int ret = open_datastream(stream);
+  if (ret == LIBRAW_SUCCESS)
+  {
+    ID.input_internal = 1; // flag to delete datastream on recycle
+  }
+  else
+  {
+    delete stream;
+    ID.input_internal = 0;
+  }
+  return ret;
+}
+
+int LibRaw::open_bayer(unsigned char *buffer, unsigned datalen,
+                       ushort _raw_width, ushort _raw_height,
+                       ushort _left_margin, ushort _top_margin,
+                       ushort _right_margin, ushort _bottom_margin,
+                       unsigned char procflags, unsigned char bayer_pattern,
+                       unsigned unused_bits, unsigned otherflags,
+                       unsigned black_level)
+{
+  // this stream will close on recycle()
+  if (!buffer || buffer == (void *)-1)
+    return LIBRAW_IO_ERROR;
+
+  LibRaw_buffer_datastream *stream;
+  try
+  {
+    stream = new LibRaw_buffer_datastream(buffer, datalen);
+  }
+  catch (const std::bad_alloc& )
+  {
+    recycle();
+    return LIBRAW_UNSUFFICIENT_MEMORY;
+  }
+  if (!stream->valid())
+  {
+    delete stream;
+    return LIBRAW_IO_ERROR;
+  }
+  ID.input = stream;
+  SET_PROC_FLAG(LIBRAW_PROGRESS_OPEN);
+  // From identify
+  initdata();
+  strcpy(imgdata.idata.make, "BayerDump");
+  snprintf(imgdata.idata.model, sizeof(imgdata.idata.model) - 1,
+           "%u x %u pixels", _raw_width, _raw_height);
+  S.flip = procflags >> 2;
+  libraw_internal_data.internal_output_params.zero_is_bad = procflags & 2;
+  libraw_internal_data.unpacker_data.data_offset = 0;
+  S.raw_width = _raw_width;
+  S.raw_height = _raw_height;
+  S.left_margin = _left_margin;
+  S.top_margin = _top_margin;
+  S.width = S.raw_width - S.left_margin - _right_margin;
+  S.height = S.raw_height - S.top_margin - _bottom_margin;
+
+  imgdata.idata.filters = 0x1010101 * bayer_pattern;
+  imgdata.idata.colors =
+      4 - !((imgdata.idata.filters & imgdata.idata.filters >> 1) & 0x5555);
+  libraw_internal_data.unpacker_data.load_flags = otherflags;
+  switch (libraw_internal_data.unpacker_data.tiff_bps =
+              (datalen)*8 / (S.raw_width * S.raw_height))
+  {
+  case 8:
+    load_raw = &LibRaw::eight_bit_load_raw;
+    break;
+  case 10:
+    if ((datalen) / S.raw_height * 3 >= S.raw_width * 4)
+    {
+      load_raw = &LibRaw::android_loose_load_raw;
+      break;
+    }
+    else if (libraw_internal_data.unpacker_data.load_flags & 1)
+    {
+      load_raw = &LibRaw::android_tight_load_raw;
+      break;
+    }
+  case 12:
+    libraw_internal_data.unpacker_data.load_flags |= 128;
+    load_raw = &LibRaw::packed_load_raw;
+    break;
+  case 16:
+    libraw_internal_data.unpacker_data.order =
+        0x4949 | 0x404 * (libraw_internal_data.unpacker_data.load_flags & 1);
+    libraw_internal_data.unpacker_data.tiff_bps -=
+        libraw_internal_data.unpacker_data.load_flags >> 4;
+    libraw_internal_data.unpacker_data.tiff_bps -=
+        libraw_internal_data.unpacker_data.load_flags =
+            libraw_internal_data.unpacker_data.load_flags >> 1 & 7;
+    load_raw = &LibRaw::unpacked_load_raw;
+  }
+  C.maximum =
+      (1 << libraw_internal_data.unpacker_data.tiff_bps) - (1 << unused_bits);
+  C.black = black_level;
+  S.iwidth = S.width;
+  S.iheight = S.height;
+  imgdata.idata.colors = 3;
+  imgdata.idata.filters |= ((imgdata.idata.filters >> 2 & 0x22222222) |
+                            (imgdata.idata.filters << 2 & 0x88888888)) &
+                           imgdata.idata.filters << 1;
+
+  imgdata.idata.raw_count = 1;
+  for (int i = 0; i < 4; i++)
+    imgdata.color.pre_mul[i] = 1.0;
+
+  strcpy(imgdata.idata.cdesc, "RGBG");
+
+  ID.input_internal = 1;
+  SET_PROC_FLAG(LIBRAW_PROGRESS_IDENTIFY);
+  return LIBRAW_SUCCESS;
+}
+
+struct foveon_data_t
+{
+  const char *make;
+  const char *model;
+  const int raw_width, raw_height;
+  const int white;
+  const int left_margin, top_margin;
+  const int width, height;
+} foveon_data[] = {
+    {"Sigma", "SD9", 2304, 1531, 12000, 20, 8, 2266, 1510},
+    {"Sigma", "SD9", 1152, 763, 12000, 10, 2, 1132, 755},
+    {"Sigma", "SD10", 2304, 1531, 12000, 20, 8, 2266, 1510},
+    {"Sigma", "SD10", 1152, 763, 12000, 10, 2, 1132, 755},
+    {"Sigma", "SD14", 2688, 1792, 14000, 18, 12, 2651, 1767},
+    {"Sigma", "SD14", 2688, 896, 14000, 18, 6, 2651, 883}, // 2/3
+    {"Sigma", "SD14", 1344, 896, 14000, 9, 6, 1326, 883},  // 1/2
+    {"Sigma", "SD15", 2688, 1792, 2900, 18, 12, 2651, 1767},
+    {"Sigma", "SD15", 2688, 896, 2900, 18, 6, 2651, 883}, // 2/3 ?
+    {"Sigma", "SD15", 1344, 896, 2900, 9, 6, 1326, 883},  // 1/2 ?
+    {"Sigma", "DP1", 2688, 1792, 2100, 18, 12, 2651, 1767},
+    {"Sigma", "DP1", 2688, 896, 2100, 18, 6, 2651, 883}, // 2/3 ?
+    {"Sigma", "DP1", 1344, 896, 2100, 9, 6, 1326, 883},  // 1/2 ?
+    {"Sigma", "DP1S", 2688, 1792, 2200, 18, 12, 2651, 1767},
+    {"Sigma", "DP1S", 2688, 896, 2200, 18, 6, 2651, 883}, // 2/3
+    {"Sigma", "DP1S", 1344, 896, 2200, 9, 6, 1326, 883},  // 1/2
+    {"Sigma", "DP1X", 2688, 1792, 3560, 18, 12, 2651, 1767},
+    {"Sigma", "DP1X", 2688, 896, 3560, 18, 6, 2651, 883}, // 2/3
+    {"Sigma", "DP1X", 1344, 896, 3560, 9, 6, 1326, 883},  // 1/2
+    {"Sigma", "DP2", 2688, 1792, 2326, 13, 16, 2651, 1767},
+    {"Sigma", "DP2", 2688, 896, 2326, 13, 8, 2651, 883}, // 2/3 ??
+    {"Sigma", "DP2", 1344, 896, 2326, 7, 8, 1325, 883},  // 1/2 ??
+    {"Sigma", "DP2S", 2688, 1792, 2300, 18, 12, 2651, 1767},
+    {"Sigma", "DP2S", 2688, 896, 2300, 18, 6, 2651, 883}, // 2/3
+    {"Sigma", "DP2S", 1344, 896, 2300, 9, 6, 1326, 883},  // 1/2
+    {"Sigma", "DP2X", 2688, 1792, 2300, 18, 12, 2651, 1767},
+    {"Sigma", "DP2X", 2688, 896, 2300, 18, 6, 2651, 883},           // 2/3
+    {"Sigma", "DP2X", 1344, 896, 2300, 9, 6, 1325, 883},            // 1/2
+    {"Sigma", "SD1", 4928, 3264, 3900, 12, 52, 4807, 3205},         // Full size
+    {"Sigma", "SD1", 4928, 1632, 3900, 12, 26, 4807, 1603},         // 2/3 size
+    {"Sigma", "SD1", 2464, 1632, 3900, 6, 26, 2403, 1603},          // 1/2 size
+    {"Sigma", "SD1 Merrill", 4928, 3264, 3900, 12, 52, 4807, 3205}, // Full size
+    {"Sigma", "SD1 Merrill", 4928, 1632, 3900, 12, 26, 4807, 1603}, // 2/3 size
+    {"Sigma", "SD1 Merrill", 2464, 1632, 3900, 6, 26, 2403, 1603},  // 1/2 size
+    {"Sigma", "DP1 Merrill", 4928, 3264, 3900, 12, 0, 4807, 3205},
+    {"Sigma", "DP1 Merrill", 2464, 1632, 3900, 12, 0, 2403, 1603}, // 1/2 size
+    {"Sigma", "DP1 Merrill", 4928, 1632, 3900, 12, 0, 4807, 1603}, // 2/3 size
+    {"Sigma", "DP2 Merrill", 4928, 3264, 3900, 12, 0, 4807, 3205},
+    {"Sigma", "DP2 Merrill", 2464, 1632, 3900, 12, 0, 2403, 1603}, // 1/2 size
+    {"Sigma", "DP2 Merrill", 4928, 1632, 3900, 12, 0, 4807, 1603}, // 2/3 size
+    {"Sigma", "DP3 Merrill", 4928, 3264, 3900, 12, 0, 4807, 3205},
+    {"Sigma", "DP3 Merrill", 2464, 1632, 3900, 12, 0, 2403, 1603}, // 1/2 size
+    {"Sigma", "DP3 Merrill", 4928, 1632, 3900, 12, 0, 4807, 1603}, // 2/3 size
+    {"Polaroid", "x530", 1440, 1088, 2700, 10, 13, 1419, 1059},
+    // dp2 Q
+    {"Sigma", "dp3 Quattro", 5888, 3776, 16383, 204, 76, 5446,
+     3624}, // full size, new fw ??
+    {"Sigma", "dp3 Quattro", 5888, 3672, 16383, 204, 24, 5446,
+     3624}, // full size
+    {"Sigma", "dp3 Quattro", 2944, 1836, 16383, 102, 12, 2723,
+     1812}, // half size
+    {"Sigma", "dp3 Quattro", 2944, 1888, 16383, 102, 38, 2723,
+     1812}, // half size, new fw??
+
+    {"Sigma", "dp2 Quattro", 5888, 3776, 16383, 204, 76, 5446,
+     3624}, // full size, new fw
+    {"Sigma", "dp2 Quattro", 5888, 3672, 16383, 204, 24, 5446,
+     3624}, // full size
+    {"Sigma", "dp2 Quattro", 2944, 1836, 16383, 102, 12, 2723,
+     1812}, // half size
+    {"Sigma", "dp2 Quattro", 2944, 1888, 16383, 102, 38, 2723,
+     1812}, // half size, new fw
+
+    {"Sigma", "dp1 Quattro", 5888, 3776, 16383, 204, 76, 5446,
+     3624}, // full size, new fw??
+    {"Sigma", "dp1 Quattro", 5888, 3672, 16383, 204, 24, 5446,
+     3624}, // full size
+    {"Sigma", "dp1 Quattro", 2944, 1836, 16383, 102, 12, 2723,
+     1812}, // half size
+    {"Sigma", "dp1 Quattro", 2944, 1888, 16383, 102, 38, 2723,
+     1812}, // half size, new fw
+
+    {"Sigma", "dp0 Quattro", 5888, 3776, 16383, 204, 76, 5446,
+     3624}, // full size, new fw??
+    {"Sigma", "dp0 Quattro", 5888, 3672, 16383, 204, 24, 5446,
+     3624}, // full size
+    {"Sigma", "dp0 Quattro", 2944, 1836, 16383, 102, 12, 2723,
+     1812}, // half size
+    {"Sigma", "dp0 Quattro", 2944, 1888, 16383, 102, 38, 2723,
+     1812}, // half size, new fw
+    // Sigma sd Quattro
+    {"Sigma", "sd Quattro", 5888, 3776, 16383, 204, 76, 5446,
+     3624}, // full size
+    {"Sigma", "sd Quattro", 2944, 1888, 16383, 102, 38, 2723,
+     1812}, // half size
+    // Sd Quattro H
+    {"Sigma", "sd Quattro H", 6656, 4480, 4000, 224, 160, 6208,
+     4160}, // full size
+    {"Sigma", "sd Quattro H", 3328, 2240, 4000, 112, 80, 3104,
+     2080},                                                        // half size
+    {"Sigma", "sd Quattro H", 5504, 3680, 4000, 0, 4, 5496, 3668}, // full size
+    {"Sigma", "sd Quattro H", 2752, 1840, 4000, 0, 2, 2748, 1834}, // half size
+};
+const int foveon_count = sizeof(foveon_data) / sizeof(foveon_data[0]);
+
+int LibRaw::open_datastream(LibRaw_abstract_datastream *stream)
+{
+
+  if (!stream)
+    return ENOENT;
+  if (!stream->valid())
+    return LIBRAW_IO_ERROR;
+  recycle();
+  if (callbacks.pre_identify_cb)
+  {
+    int r = (callbacks.pre_identify_cb)(this);
+    if (r == 1)
+      goto final;
+  }
+
+  try
+  {
+	  ID.input = stream;
+	  SET_PROC_FLAG(LIBRAW_PROGRESS_OPEN);
+
+	  identify();
+
+	  imgdata.lens.Lens[sizeof(imgdata.lens.Lens) - 1] = 0; // make sure lens is 0-terminated
+
+	  if (callbacks.post_identify_cb)
+		  (callbacks.post_identify_cb)(this);
+
+#define isRIC imgdata.sizes.raw_inset_crop
+
+	  if (!imgdata.idata.dng_version && makeIs(LIBRAW_CAMERAMAKER_Fujifilm)
+		  && (!strcmp(imgdata.idata.normalized_model, "S3Pro")
+			  || !strcmp(imgdata.idata.normalized_model, "S5Pro")
+			  || !strcmp(imgdata.idata.normalized_model, "S2Pro")))
+	  {
+		  isRIC.cleft = isRIC.ctop = 0xffff;
+		  isRIC.cwidth = isRIC.cheight = 0;
+	  }
+
+      // Wipe out non-standard WB
+      if (!imgdata.idata.dng_version &&
+          (makeIs(LIBRAW_CAMERAMAKER_Sony) && !strcmp(imgdata.idata.normalized_model, "DSC-F828"))
+          && !(imgdata.params.raw_processing_options & LIBRAW_PROCESSING_PROVIDE_NONSTANDARD_WB))
+      {
+          for (int i = 0; i < 4; i++) imgdata.color.cam_mul[i] = (i == 1);
+          memset(imgdata.color.WB_Coeffs, 0, sizeof(imgdata.color.WB_Coeffs));
+          memset(imgdata.color.WBCT_Coeffs, 0, sizeof(imgdata.color.WBCT_Coeffs));
+      }
+
+	  if (load_raw == &LibRaw::nikon_load_raw)
+		  nikon_read_curve();
+
+	  if (load_raw == &LibRaw::lossless_jpeg_load_raw &&
+		  imgdata.makernotes.canon.RecordMode && makeIs(LIBRAW_CAMERAMAKER_Kodak) &&
+		  /* Not normalized models here, it is intentional */
+		  (!strncasecmp(imgdata.idata.model, "EOS D2000", 9) || // if we want something different for B&W cameras,
+			  !strncasecmp(imgdata.idata.model, "EOS D6000", 9)))  // it's better to compare with CamIDs
+	  {
+		  imgdata.color.black = 0;
+		  imgdata.color.maximum = 4501;
+		  memset(imgdata.color.cblack, 0, sizeof(imgdata.color.cblack));
+		  memset(imgdata.sizes.mask, 0, sizeof(imgdata.sizes.mask));
+		  imgdata.sizes.mask[0][3] = 1; // to skip mask re-calc
+		  libraw_internal_data.unpacker_data.load_flags |= 512;
+	  }
+
+	  if (load_raw == &LibRaw::panasonic_load_raw)
+	  {
+		  if (libraw_internal_data.unpacker_data.pana_encoding == 6 ||
+			  libraw_internal_data.unpacker_data.pana_encoding == 7)
+		  {
+			  for (int i = 0; i < 3; i++)
+				  imgdata.color.cblack[i] =
+				  libraw_internal_data.internal_data.pana_black[i];
+			  imgdata.color.cblack[3] = imgdata.color.cblack[1];
+			  imgdata.color.cblack[4] = imgdata.color.cblack[5] = 0;
+			  imgdata.color.black = 0;
+			  imgdata.color.maximum =
+				  MAX(imgdata.color.linear_max[0],
+					  MAX(imgdata.color.linear_max[1], imgdata.color.linear_max[2]));
+		  }
+
+		  if (libraw_internal_data.unpacker_data.pana_encoding == 6)
+		  {
+			  int rowbytes = imgdata.sizes.raw_width / 11 * 16;
+			  if ((imgdata.sizes.raw_width % 11) == 0 &&
+				  (INT64(imgdata.sizes.raw_height) * rowbytes ==
+					  INT64(libraw_internal_data.unpacker_data.data_size)))
+				  load_raw = &LibRaw::panasonicC6_load_raw;
+			  else
+				  imgdata.idata.raw_count = 0; // incorrect size
+		  }
+		  else if (libraw_internal_data.unpacker_data.pana_encoding == 7)
+		  {
+			  int pixperblock =
+				  libraw_internal_data.unpacker_data.pana_bpp == 14 ? 9 : 10;
+			  int rowbytes = imgdata.sizes.raw_width / pixperblock * 16;
+			  if ((imgdata.sizes.raw_width % pixperblock) == 0 &&
+				  (INT64(imgdata.sizes.raw_height) * rowbytes ==
+					  INT64(libraw_internal_data.unpacker_data.data_size)))
+				  load_raw = &LibRaw::panasonicC7_load_raw;
+			  else
+				  imgdata.idata.raw_count = 0; // incorrect size
+		  }
+	  }
+
+#define NIKON_14BIT_SIZE(rw, rh)                                               \
+  (((unsigned)(ceilf((float)(rw * 7 / 4) / 16.0)) * 16) * rh)
+
+	  // Ugly hack, replace with proper data/line size for different
+	  // cameras/format when available
+	  if (makeIs(LIBRAW_CAMERAMAKER_Nikon)
+		  && !strncasecmp(imgdata.idata.model, "Z", 1) &&
+		  NIKON_14BIT_SIZE(imgdata.sizes.raw_width, imgdata.sizes.raw_height) ==
+		  libraw_internal_data.unpacker_data.data_size)
+	  {
+		  load_raw = &LibRaw::nikon_14bit_load_raw;
+	  }
+#undef NIKON_14BIT_SIZE
+
+	  // Linear max from 14-bit camera, but on 12-bit data?
+	  if (makeIs(LIBRAW_CAMERAMAKER_Sony) &&
+		  imgdata.color.maximum > 0 &&
+		  imgdata.color.linear_max[0] > (long)imgdata.color.maximum &&
+		  imgdata.color.linear_max[0] <= (long)imgdata.color.maximum * 4)
+		  for (int c = 0; c < 4; c++)
+			  imgdata.color.linear_max[c] /= 4;
+
+	  if (makeIs(LIBRAW_CAMERAMAKER_Canon))
+	  {
+#define imC imgdata.makernotes.canon
+		  if (imC.SensorLeftBorder != -1)
+		  { // tag 0x00e0 SensorInfo was parsed
+			  if (isRIC.aspect != LIBRAW_IMAGE_ASPECT_UNKNOWN)
+			  { // tag 0x009a AspectInfo was parsed
+				  isRIC.cleft += imC.SensorLeftBorder;
+				  isRIC.ctop += imC.SensorTopBorder;
+			  }
+			  else
+			  {
+				  isRIC.cleft = imC.SensorLeftBorder;
+				  isRIC.ctop = imC.SensorTopBorder;
+				  isRIC.cwidth = imC.SensorRightBorder - imC.SensorLeftBorder + 1;
+				  isRIC.cheight = imC.SensorBottomBorder - imC.SensorTopBorder + 1;
+			  }
+		  }
+		  else
+		  {
+			  if (isRIC.aspect != LIBRAW_IMAGE_ASPECT_UNKNOWN)
+			  {
+			  }
+			  else
+			  { // Canon PowerShot S2 IS
+			  }
+		  }
+#undef isRIC
+#undef imC
+	  }
+
+	  if (makeIs(LIBRAW_CAMERAMAKER_Canon) &&
+		  (load_raw == &LibRaw::canon_sraw_load_raw) &&
+		  imgdata.sizes.raw_width > 0)
+	  {
+		  float ratio =
+			  float(imgdata.sizes.raw_height) / float(imgdata.sizes.raw_width);
+		  if ((ratio < 0.57 || ratio > 0.75) &&
+			  imgdata.makernotes.canon.SensorHeight > 1 &&
+			  imgdata.makernotes.canon.SensorWidth > 1)
+		  {
+			  imgdata.sizes.raw_width = imgdata.makernotes.canon.SensorWidth;
+			  imgdata.sizes.left_margin = imgdata.makernotes.canon.SensorLeftBorder;
+			  imgdata.sizes.iwidth = imgdata.sizes.width =
+				  imgdata.makernotes.canon.SensorRightBorder -
+				  imgdata.makernotes.canon.SensorLeftBorder + 1;
+			  imgdata.sizes.raw_height = imgdata.makernotes.canon.SensorHeight;
+			  imgdata.sizes.top_margin = imgdata.makernotes.canon.SensorTopBorder;
+			  imgdata.sizes.iheight = imgdata.sizes.height =
+				  imgdata.makernotes.canon.SensorBottomBorder -
+				  imgdata.makernotes.canon.SensorTopBorder + 1;
+			  libraw_internal_data.unpacker_data.load_flags |=
+				  256; // reset width/height in canon_sraw_load_raw()
+			  imgdata.sizes.raw_pitch = 8 * imgdata.sizes.raw_width;
+		  }
+		  else if (imgdata.sizes.raw_width == 4032 &&
+			  imgdata.sizes.raw_height == 3402 &&
+			  !strcasecmp(imgdata.idata.model, "EOS 80D")) // 80D hardcoded
+		  {
+			  imgdata.sizes.raw_width = 4536;
+			  imgdata.sizes.left_margin = 28;
+			  imgdata.sizes.iwidth = imgdata.sizes.width =
+				  imgdata.sizes.raw_width - imgdata.sizes.left_margin;
+			  imgdata.sizes.raw_height = 3024;
+			  imgdata.sizes.top_margin = 8;
+			  imgdata.sizes.iheight = imgdata.sizes.height =
+				  imgdata.sizes.raw_height - imgdata.sizes.top_margin;
+			  libraw_internal_data.unpacker_data.load_flags |= 256;
+			  imgdata.sizes.raw_pitch = 8 * imgdata.sizes.raw_width;
+		  }
+	  }
+
+#ifdef USE_DNGSDK
+	  if (imgdata.idata.dng_version
+		  &&libraw_internal_data.unpacker_data.tiff_compress == 34892
+		  && libraw_internal_data.unpacker_data.tiff_bps == 8
+		  && libraw_internal_data.unpacker_data.tiff_samples == 3
+		  && load_raw == &LibRaw::lossy_dng_load_raw)
+	  {
+		  // Data should be linearized by DNG SDK
+		  C.black = 0;
+		  memset(C.cblack, 0, sizeof(C.cblack));
+	  }
+#endif
+
+	  // XTrans Compressed?
+	  if (!imgdata.idata.dng_version &&
+		  makeIs(LIBRAW_CAMERAMAKER_Fujifilm) &&
+		  (load_raw == &LibRaw::unpacked_load_raw))
+	  {
+		  if (imgdata.sizes.raw_width * (imgdata.sizes.raw_height * 2ul) !=
+			  libraw_internal_data.unpacker_data.data_size)
+		  {
+			  if ((imgdata.sizes.raw_width * (imgdata.sizes.raw_height * 7ul)) / 4 ==
+				  libraw_internal_data.unpacker_data.data_size)
+				  load_raw = &LibRaw::fuji_14bit_load_raw;
+			  else
+				  parse_fuji_compressed_header();
+		  }
+		  if (imgdata.idata.filters == 9)
+		  {
+			  // Adjust top/left margins for X-Trans
+			  int newtm = imgdata.sizes.top_margin % 6
+				  ? (imgdata.sizes.top_margin / 6 + 1) * 6
+				  : imgdata.sizes.top_margin;
+			  int newlm = imgdata.sizes.left_margin % 6
+				  ? (imgdata.sizes.left_margin / 6 + 1) * 6
+				  : imgdata.sizes.left_margin;
+			  if (newtm != imgdata.sizes.top_margin ||
+				  newlm != imgdata.sizes.left_margin)
+			  {
+				  imgdata.sizes.height -= (newtm - imgdata.sizes.top_margin);
+				  imgdata.sizes.top_margin = newtm;
+				  imgdata.sizes.width -= (newlm - imgdata.sizes.left_margin);
+				  imgdata.sizes.left_margin = newlm;
+				  for (int c1 = 0; c1 < 6; c1++)
+					  for (int c2 = 0; c2 < 6; c2++)
+						  imgdata.idata.xtrans[c1][c2] = imgdata.idata.xtrans_abs[c1][c2];
+			  }
+		  }
+	  }
+	  if (!libraw_internal_data.internal_output_params.fuji_width
+		  && imgdata.idata.filters >= 1000
+		  && ((imgdata.sizes.top_margin % 2) || (imgdata.sizes.left_margin % 2)))
+	  {
+		  int crop[2] = { 0,0 };
+		  unsigned filt;
+		  int c;
+		  if (imgdata.sizes.top_margin % 2)
+		  {
+			  imgdata.sizes.top_margin += 1;
+			  imgdata.sizes.height -= 1;
+			  crop[1] = 1;
+		  }
+		  if (imgdata.sizes.left_margin % 2)
+		  {
+			  imgdata.sizes.left_margin += 1;
+			  imgdata.sizes.width -= 1;
+			  crop[0] = 1;
+		  }
+		  for (filt = c = 0; c < 16; c++)
+			  filt |= FC((c >> 1) + (crop[1]), (c & 1) + (crop[0])) << c * 2;
+		  imgdata.idata.filters = filt;
+	  }
+
+#ifdef USE_DNGSDK
+	  if (
+		  imgdata.params.use_dngsdk &&
+		  !(imgdata.params.raw_processing_options & (LIBRAW_PROCESSING_DNG_STAGE2 | LIBRAW_PROCESSING_DNG_STAGE3 | LIBRAW_PROCESSING_DNG_DISABLEWBADJUST)))
+#endif
+	  {
+		  // Fix DNG white balance if needed: observed only for Kalpanika X3F tools produced DNGs
+		  if (imgdata.idata.dng_version && (imgdata.idata.filters == 0) &&
+			  imgdata.idata.colors > 1 && imgdata.idata.colors < 5)
+		  {
+			  float delta[4] = { 0.f, 0.f, 0.f, 0.f };
+			  int black[4];
+			  for (int c = 0; c < 4; c++)
+				  black[c] = imgdata.color.dng_levels.dng_black +
+				  imgdata.color.dng_levels.dng_cblack[c];
+			  for (int c = 0; c < imgdata.idata.colors; c++)
+				  delta[c] = imgdata.color.dng_levels.dng_whitelevel[c] - black[c];
+			  float mindelta = delta[0], maxdelta = delta[0];
+			  for (int c = 1; c < imgdata.idata.colors; c++)
+			  {
+				  if (mindelta > delta[c])
+					  mindelta = delta[c];
+				  if (maxdelta < delta[c])
+					  maxdelta = delta[c];
+			  }
+			  if (mindelta > 1 && maxdelta < (mindelta * 20)) // safety
+			  {
+				  for (int c = 0; c < imgdata.idata.colors; c++)
+				  {
+					  imgdata.color.cam_mul[c] /= (delta[c] / maxdelta);
+					  imgdata.color.pre_mul[c] /= (delta[c] / maxdelta);
+				  }
+				  imgdata.color.maximum = imgdata.color.cblack[0] + maxdelta;
+			  }
+		  }
+	  }
+
+    if (imgdata.idata.dng_version &&
+        !(imgdata.params.raw_processing_options &
+        LIBRAW_PROCESSING_USE_DNG_DEFAULT_CROP) &&
+		makeIs(LIBRAW_CAMERAMAKER_Panasonic)
+          && !strcasecmp(imgdata.idata.normalized_model, "DMC-LX100"))
+      imgdata.sizes.width = 4288;
+
+    if (imgdata.idata.dng_version &&
+        !(imgdata.params.raw_processing_options & LIBRAW_PROCESSING_USE_DNG_DEFAULT_CROP)
+	&& makeIs(LIBRAW_CAMERAMAKER_Leica)
+        && !strcasecmp(imgdata.idata.normalized_model, "SL2"))
+		imgdata.sizes.height -= 16;
+
+	if (makeIs(LIBRAW_CAMERAMAKER_Sony) &&
+        imgdata.idata.dng_version &&
+        !(imgdata.params.raw_processing_options &
+          LIBRAW_PROCESSING_USE_DNG_DEFAULT_CROP))
+    {
+      if (S.raw_width == 3984)
+        S.width = 3925;
+      else if (S.raw_width == 4288)
+        S.width = S.raw_width - 32;
+      else if (S.raw_width == 4928 && S.height < 3280)
+        S.width = S.raw_width - 8;
+      else if (S.raw_width == 5504)
+        S.width = S.raw_width - (S.height > 3664 ? 8 : 32);
+    }
+
+	if (makeIs(LIBRAW_CAMERAMAKER_Sony) &&
+        !imgdata.idata.dng_version)
+    {
+        if(load_raw ==&LibRaw::sony_arq_load_raw)
+        {
+            if(S.raw_width > 12000) // A7RM4 16x, both APS-C and APS
+                S.width = S.raw_width - 64;
+            else // A7RM3/M4 4x merge
+                S.width = S.raw_width - 32;
+        }
+
+      if (((!strncasecmp(imgdata.idata.model, "ILCE-7RM", 8) ||
+            !strcasecmp(imgdata.idata.model, "ILCA-99M2")) &&
+           (S.raw_width == 5216 || S.raw_width == 6304)) // A7RM2/M3/A99M2 in APS mode; A7RM4 in APS-C
+          ||
+          (!strcasecmp(imgdata.idata.model, "ILCE-7R") && S.raw_width >= 4580 &&
+           S.raw_width < 5020) // A7R in crop mode, no samples, so size est.
+          || (!strcasecmp(imgdata.idata.model, "ILCE-7") &&
+              S.raw_width == 3968) // A7 in crop mode
+          ||
+          ((!strncasecmp(imgdata.idata.model, "ILCE-7M", 7) ||
+            !strcasecmp(imgdata.idata.model, "ILCE-9") ||
+#if 0
+            !strcasecmp(imgdata.idata.model,
+                        "SLT-A99V")) // Does SLT-A99 also have APS-C mode??
+#endif
+           (mnCamID == SonyID_SLT_A99)) // 2 reasons: some cameras are SLT-A99, no 'V'; some are Hasselblad HV
+           && S.raw_width > 3750 &&
+           S.raw_width < 4120) // A7M2, A7M3, AA9, most likely APS-C raw_width
+                               // is 3968 (same w/ A7), but no samples, so guess
+          || (!strncasecmp(imgdata.idata.model, "ILCE-7S", 7) &&
+              S.raw_width == 2816) // A7S2=> exact, hope it works for A7S-I too
+      )
+        S.width = S.raw_width - 32;
+    }
+
+
+    // FIXME: it is possible that DNG contains 4x main frames + some previews; in this case imgdata.idata.raw_count will be greater than 4
+	if (makeIs(LIBRAW_CAMERAMAKER_Pentax) &&
+        /*!strcasecmp(imgdata.idata.model,"K-3 II")  &&*/
+            imgdata.idata.raw_count == 4 &&
+        (imgdata.params.raw_processing_options &
+         LIBRAW_PROCESSING_PENTAX_PS_ALLFRAMES))
+    {
+      imgdata.idata.raw_count = 1;
+      imgdata.idata.filters = 0;
+      imgdata.idata.colors = 4;
+      imgdata.sizes.top_margin+=2;
+      imgdata.sizes.left_margin+=2;
+      imgdata.sizes.width-=4;
+      imgdata.sizes.height-=4;
+      IO.mix_green = 1;
+      pentax_component_load_raw = load_raw;
+      load_raw = &LibRaw::pentax_4shot_load_raw;
+    }
+
+	if (!imgdata.idata.dng_version && makeIs(LIBRAW_CAMERAMAKER_Leaf) &&
+        !strcmp(imgdata.idata.model, "Credo 50"))
+    {
+      imgdata.color.pre_mul[0] = 1.f / 0.3984f;
+      imgdata.color.pre_mul[2] = 1.f / 0.7666f;
+      imgdata.color.pre_mul[1] = imgdata.color.pre_mul[3] = 1.0;
+    }
+
+	if (!imgdata.idata.dng_version && makeIs(LIBRAW_CAMERAMAKER_Fujifilm) &&
+        (!strncmp(imgdata.idata.model, "S20Pro", 6) ||
+         !strncmp(imgdata.idata.model, "F700", 4)))
+    {
+      imgdata.sizes.raw_width /= 2;
+      load_raw = &LibRaw::unpacked_load_raw_fuji_f700s20;
+    }
+
+    if (load_raw == &LibRaw::packed_load_raw &&
+		makeIs(LIBRAW_CAMERAMAKER_Nikon) &&
+        !libraw_internal_data.unpacker_data.load_flags &&
+        (!strncasecmp(imgdata.idata.model, "D810", 4) ||
+         !strcasecmp(imgdata.idata.model, "D4S")) &&
+        libraw_internal_data.unpacker_data.data_size * 2 ==
+            imgdata.sizes.raw_height * imgdata.sizes.raw_width * 3)
+    {
+      libraw_internal_data.unpacker_data.load_flags = 80;
+    }
+    // Adjust BL for Sony A900/A850
+    if (load_raw == &LibRaw::packed_load_raw &&
+		makeIs(LIBRAW_CAMERAMAKER_Sony)) // 12 bit sony, but metadata may be for 14-bit range
+    {
+      if (C.maximum > 4095)
+        C.maximum = 4095;
+      if (C.black > 256 || C.cblack[0] > 256)
+      {
+        C.black /= 4;
+        for (int c = 0; c < 4; c++)
+          C.cblack[c] /= 4;
+        for (unsigned c = 0; c < C.cblack[4] * C.cblack[5]; c++)
+          C.cblack[6 + c] /= 4;
+      }
+    }
+
+	if (load_raw == &LibRaw::nikon_yuv_load_raw) // Is it Nikon sRAW?
+    {
+      load_raw = &LibRaw::nikon_load_sraw;
+      C.black = 0;
+      memset(C.cblack, 0, sizeof(C.cblack));
+      imgdata.idata.filters = 0;
+      libraw_internal_data.unpacker_data.tiff_samples = 3;
+      imgdata.idata.colors = 3;
+      double beta_1 = -5.79342238397656E-02;
+      double beta_2 = 3.28163551282665;
+      double beta_3 = -8.43136004842678;
+      double beta_4 = 1.03533181861023E+01;
+      for (int i = 0; i <= 3072; i++)
+      {
+        double x = (double)i / 3072.;
+        double y = (1. - exp(-beta_1 * x - beta_2 * x * x - beta_3 * x * x * x -
+                             beta_4 * x * x * x * x));
+        if (y < 0.)
+          y = 0.;
+        imgdata.color.curve[i] = (y * 16383.);
+      }
+      for (int i = 0; i < 3; i++)
+        for (int j = 0; j < 4; j++)
+          imgdata.color.rgb_cam[i][j] = float(i == j);
+    }
+    // Adjust BL for Nikon 12bit
+    if ((load_raw == &LibRaw::nikon_load_raw ||
+         load_raw == &LibRaw::packed_load_raw ||
+         load_raw == &LibRaw::nikon_load_padded_packed_raw) &&
+		 makeIs(LIBRAW_CAMERAMAKER_Nikon) &&
+        strncmp(imgdata.idata.model, "COOLPIX", 7) &&
+        libraw_internal_data.unpacker_data.tiff_bps == 12)
+    {
+      C.maximum = 4095;
+      C.black /= 4;
+      for (int c = 0; c < 4; c++)
+        C.cblack[c] /= 4;
+      for (unsigned c = 0; c < C.cblack[4] * C.cblack[5]; c++)
+        C.cblack[6 + c] /= 4;
+    }
+
+    // Adjust wb_already_applied
+    if (load_raw == &LibRaw::nikon_load_sraw)
+      imgdata.color.as_shot_wb_applied =
+          LIBRAW_ASWB_APPLIED | LIBRAW_ASWB_NIKON_SRAW;
+    else if (makeIs(LIBRAW_CAMERAMAKER_Canon) &&
+             imgdata.makernotes.canon.multishot[0] >= 8 &&
+             imgdata.makernotes.canon.multishot[1] > 0)
+      imgdata.color.as_shot_wb_applied =
+          LIBRAW_ASWB_APPLIED | LIBRAW_ASWB_CANON;
+    else if (makeIs(LIBRAW_CAMERAMAKER_Nikon) &&
+             imgdata.makernotes.nikon.ExposureMode == 1)
+      imgdata.color.as_shot_wb_applied =
+          LIBRAW_ASWB_APPLIED | LIBRAW_ASWB_NIKON;
+	else if (makeIs(LIBRAW_CAMERAMAKER_Pentax) &&
+             ((imgdata.makernotes.pentax.MultiExposure & 0x01) == 1))
+      imgdata.color.as_shot_wb_applied =
+          LIBRAW_ASWB_APPLIED | LIBRAW_ASWB_PENTAX;
+    else
+      imgdata.color.as_shot_wb_applied = 0;
+
+    // Adjust Highlight Linearity limit
+    if (C.linear_max[0] < 0)
+    {
+      if (imgdata.idata.dng_version)
+      {
+        for (int c = 0; c < 4; c++)
+          C.linear_max[c] = -1 * C.linear_max[c] + imgdata.color.cblack[c + 6];
+      }
+      else
+      {
+        for (int c = 0; c < 4; c++)
+          C.linear_max[c] = -1 * C.linear_max[c] + imgdata.color.cblack[c];
+      }
+    }
+
+	if (makeIs(LIBRAW_CAMERAMAKER_Nikon) &&
+		(!C.linear_max[0]) && (C.maximum > 1024) && (load_raw != &LibRaw::nikon_load_sraw))
+    {
+      C.linear_max[0] = C.linear_max[1] = C.linear_max[2] = C.linear_max[3] =
+          (long)((float)(C.maximum) / 1.07f);
+    }
+
+    // Correct WB for Samsung GX20
+	if (
+#if 0
+        /* GX20 should be corrected, but K20 is not */
+        makeIs(LIBRAW_CAMERAMAKER_Pentax) &&
+		!strcasecmp(imgdata.idata.normalized_model, "K20D")
+#endif
+#if 0
+		!strcasecmp(imgdata.idata.make, "Samsung") &&
+        !strcasecmp(imgdata.idata.model, "GX20")
+#endif
+    makeIs(LIBRAW_CAMERAMAKER_Pentax) &&
+    (mnCamID == PentaxID_GX20) // Samsung rebranding
+		)
+    {
+      for (int cnt = LIBRAW_WBI_Unknown; cnt <= LIBRAW_WBI_StudioTungsten; cnt++) {
+        if (C.WB_Coeffs[cnt][1]) {
+          C.WB_Coeffs[cnt][0] = (int)((float)(C.WB_Coeffs[cnt][0]) * 1.0503f);
+          C.WB_Coeffs[cnt][2] = (int)((float)(C.WB_Coeffs[cnt][2]) * 2.2867f);
+        }
+      }
+      for (int cnt = 0; cnt < 64; cnt++) {
+        if (C.WBCT_Coeffs[cnt][0] > 0.0f) {
+          C.WBCT_Coeffs[cnt][1] *= 1.0503f;
+          C.WBCT_Coeffs[cnt][3] *= 2.2867f;
+        }
+      }
+      for(int cnt = 0; cnt < 4; cnt++)
+        imgdata.color.pre_mul[cnt] =
+          C.WB_Coeffs[LIBRAW_WBI_Daylight][cnt];
+    }
+
+    // Adjust BL for Panasonic
+    if (load_raw == &LibRaw::panasonic_load_raw &&
+		makeIs(LIBRAW_CAMERAMAKER_Panasonic) &&
+        ID.pana_black[0] && ID.pana_black[1] && ID.pana_black[2])
+    {
+      if (libraw_internal_data.unpacker_data.pana_encoding == 5)
+        libraw_internal_data.internal_output_params.zero_is_bad = 0;
+      C.black = 0;
+      int add = libraw_internal_data.unpacker_data.pana_encoding == 4 ? 15 : 0;
+      C.cblack[0] = ID.pana_black[0] + add;
+      C.cblack[1] = C.cblack[3] = ID.pana_black[1] + add;
+      C.cblack[2] = ID.pana_black[2] + add;
+      unsigned i = C.cblack[3];
+      for (int c = 0; c < 3; c++)
+        if (i > C.cblack[c])
+          i = C.cblack[c];
+      for (int c = 0; c < 4; c++)
+        C.cblack[c] -= i;
+      C.black = i;
+    }
+
+    // Adjust sizes for X3F processing
+#ifdef USE_X3FTOOLS
+	if (load_raw == &LibRaw::x3f_load_raw)
+    {
+      for (int i = 0; i < foveon_count; i++)
+        if (!strcasecmp(imgdata.idata.make, foveon_data[i].make) &&
+            !strcasecmp(imgdata.idata.model, foveon_data[i].model) &&
+            imgdata.sizes.raw_width == foveon_data[i].raw_width &&
+            imgdata.sizes.raw_height == foveon_data[i].raw_height)
+        {
+          imgdata.sizes.top_margin = foveon_data[i].top_margin;
+          imgdata.sizes.left_margin = foveon_data[i].left_margin;
+          imgdata.sizes.width = imgdata.sizes.iwidth = foveon_data[i].width;
+          imgdata.sizes.height = imgdata.sizes.iheight = foveon_data[i].height;
+          C.maximum = foveon_data[i].white;
+          break;
+        }
+    }
+#endif
+#if 0
+    size_t bytes = ID.input->size()-libraw_internal_data.unpacker_data.data_offset;
+    float bpp = float(bytes)/float(S.raw_width)/float(S.raw_height);
+    float bpp2 = float(bytes)/float(S.width)/float(S.height);
+    if(!strcasecmp(imgdata.idata.make,"Hasselblad") && bpp == 6.0f)
+      {
+        load_raw = &LibRaw::hasselblad_full_load_raw;
+        S.width = S.raw_width;
+        S.height = S.raw_height;
+        P1.filters = 0;
+        P1.colors=3;
+        P1.raw_count=1;
+        C.maximum=0xffff;
+      }
+#endif
+    if (C.profile_length)
+    {
+      if (C.profile)
+        free(C.profile);
+      C.profile = malloc(C.profile_length);
+      merror(C.profile, "LibRaw::open_file()");
+      ID.input->seek(ID.profile_offset, SEEK_SET);
+      ID.input->read(C.profile, C.profile_length, 1);
+    }
+
+    SET_PROC_FLAG(LIBRAW_PROGRESS_IDENTIFY);
+  }
+  catch (LibRaw_exceptions err)
+  {
+    EXCEPTION_HANDLER(err);
+  }
+  catch (const std::exception& ee)
+  {
+    EXCEPTION_HANDLER(LIBRAW_EXCEPTION_IO_CORRUPT);
+  }
+
+final:;
+
+  if (P1.raw_count < 1)
+    return LIBRAW_FILE_UNSUPPORTED;
+
+  write_fun = &LibRaw::write_ppm_tiff;
+
+  if (load_raw == &LibRaw::kodak_ycbcr_load_raw)
+  {
+    S.height += S.height & 1;
+    S.width += S.width & 1;
+  }
+
+  IO.shrink =
+      P1.filters &&
+      (O.half_size || ((O.threshold || O.aber[0] != 1 || O.aber[2] != 1)));
+  if (IO.shrink && P1.filters >= 1000)
+  {
+    S.width &= 65534;
+    S.height &= 65534;
+  }
+
+  S.iheight = (S.height + IO.shrink) >> IO.shrink;
+  S.iwidth = (S.width + IO.shrink) >> IO.shrink;
+
+  // Save color,sizes and internal data into raw_image fields
+  memmove(&imgdata.rawdata.color, &imgdata.color, sizeof(imgdata.color));
+  memmove(&imgdata.rawdata.sizes, &imgdata.sizes, sizeof(imgdata.sizes));
+  memmove(&imgdata.rawdata.iparams, &imgdata.idata, sizeof(imgdata.idata));
+  memmove(&imgdata.rawdata.ioparams,
+          &libraw_internal_data.internal_output_params,
+          sizeof(libraw_internal_data.internal_output_params));
+
+  SET_PROC_FLAG(LIBRAW_PROGRESS_SIZE_ADJUST);
+
+  return LIBRAW_SUCCESS;
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/utils/phaseone_processing.cpp libkdcraw/libkdcraw/libraw/src/utils/phaseone_processing.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/utils/phaseone_processing.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/utils/phaseone_processing.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,102 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/libraw_cxx_defs.h"
+
+void LibRaw::phase_one_allocate_tempbuffer()
+{
+  // Allocate temp raw_image buffer
+  imgdata.rawdata.raw_image = (ushort *)malloc(S.raw_pitch * S.raw_height);
+  merror(imgdata.rawdata.raw_image, "phase_one_prepare_to_correct()");
+}
+void LibRaw::phase_one_free_tempbuffer()
+{
+  free(imgdata.rawdata.raw_image);
+  imgdata.rawdata.raw_image = (ushort *)imgdata.rawdata.raw_alloc;
+}
+
+int LibRaw::phase_one_subtract_black(ushort *src, ushort *dest)
+{
+
+  try
+  {
+    if (O.user_black < 0 && O.user_cblack[0] <= -1000000 &&
+        O.user_cblack[1] <= -1000000 && O.user_cblack[2] <= -1000000 &&
+        O.user_cblack[3] <= -1000000)
+    {
+      if (!imgdata.rawdata.ph1_cblack || !imgdata.rawdata.ph1_rblack)
+      {
+        int bl = imgdata.color.phase_one_data.t_black;
+        for (int row = 0; row < S.raw_height; row++)
+        {
+          checkCancel();
+          for (int col = 0; col < S.raw_width; col++)
+          {
+            int idx = row * S.raw_width + col;
+            int val = int(src[idx]) - bl;
+            dest[idx] = val > 0 ? val : 0;
+          }
+        }
+      }
+      else
+      {
+        int bl = imgdata.color.phase_one_data.t_black;
+        for (int row = 0; row < S.raw_height; row++)
+        {
+          checkCancel();
+          for (int col = 0; col < S.raw_width; col++)
+          {
+            int idx = row * S.raw_width + col;
+            int val =
+                int(src[idx]) - bl +
+                imgdata.rawdata
+                    .ph1_cblack[row][col >= imgdata.rawdata.color.phase_one_data
+                                                .split_col] +
+                imgdata.rawdata
+                    .ph1_rblack[col][row >= imgdata.rawdata.color.phase_one_data
+                                                .split_row];
+            dest[idx] = val > 0 ? val : 0;
+          }
+        }
+      }
+    }
+    else // black set by user interaction
+    {
+      // Black level in cblack!
+      for (int row = 0; row < S.raw_height; row++)
+      {
+        checkCancel();
+        unsigned short cblk[16];
+        for (int cc = 0; cc < 16; cc++)
+          cblk[cc] = C.cblack[fcol(row, cc)];
+        for (int col = 0; col < S.raw_width; col++)
+        {
+          int idx = row * S.raw_width + col;
+          ushort val = src[idx];
+          ushort bl = cblk[col & 0xf];
+          dest[idx] = val > bl ? val - bl : 0;
+        }
+      }
+    }
+    return 0;
+  }
+  catch (LibRaw_exceptions err)
+  {
+    return LIBRAW_CANCELLED_BY_CALLBACK;
+  }
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/utils/read_utils.cpp libkdcraw/libkdcraw/libraw/src/utils/read_utils.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/utils/read_utils.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/utils/read_utils.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,149 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+ushort LibRaw::sget2Rev(uchar *s) // specific to some Canon Makernotes fields,
+                                  // where they have endian in reverse
+{
+  if (order == 0x4d4d) /* "II" means little-endian, and we reverse to "MM" - big
+                          endian */
+    return s[0] | s[1] << 8;
+  else /* "MM" means big-endian... */
+    return s[0] << 8 | s[1];
+}
+
+ushort LibRaw::get2()
+{
+  uchar str[2] = {0xff, 0xff};
+  fread(str, 1, 2, ifp);
+  return sget2(str);
+}
+
+unsigned LibRaw::sget4(uchar *s)
+{
+  if (order == 0x4949)
+    return s[0] | s[1] << 8 | s[2] << 16 | s[3] << 24;
+  else
+    return s[0] << 24 | s[1] << 16 | s[2] << 8 | s[3];
+}
+#define sget4(s) sget4((uchar *)s)
+
+unsigned LibRaw::get4()
+{
+  uchar str[4] = {0xff, 0xff, 0xff, 0xff};
+  fread(str, 1, 4, ifp);
+  return sget4(str);
+}
+
+unsigned LibRaw::getint(int type) { return tagtypeIs(LIBRAW_EXIFTAG_TYPE_SHORT) ? get2() : get4(); }
+
+float LibRaw::int_to_float(int i)
+{
+  union {
+    int i;
+    float f;
+  } u;
+  u.i = i;
+  return u.f;
+}
+
+double LibRaw::getreal(int type)
+{
+  union {
+    char c[8];
+    double d;
+  } u, v;
+  int i, rev;
+
+  switch (type)
+  {
+  case LIBRAW_EXIFTAG_TYPE_SHORT:
+    return (unsigned short)get2();
+  case LIBRAW_EXIFTAG_TYPE_LONG:
+    return (unsigned int)get4();
+  case LIBRAW_EXIFTAG_TYPE_RATIONAL: // (unsigned, unsigned)
+    u.d = (unsigned int)get4();
+    v.d = (unsigned int)get4();
+    return u.d / (v.d ? v.d : 1);
+  case LIBRAW_EXIFTAG_TYPE_SSHORT:
+    return (signed short)get2();
+  case LIBRAW_EXIFTAG_TYPE_SLONG:
+    return (signed int)get4();
+  case LIBRAW_EXIFTAG_TYPE_SRATIONAL: // (int, int)
+    u.d = (signed int)get4();
+    v.d = (signed int)get4();
+    return u.d / (v.d ? v.d : 1);
+  case LIBRAW_EXIFTAG_TYPE_FLOAT:
+    return int_to_float(get4());
+  case LIBRAW_EXIFTAG_TYPE_DOUBLE:
+    rev = 7 * ((order == 0x4949) == (ntohs(0x1234) == 0x1234));
+    for (i = 0; i < 8; i++)
+      u.c[i ^ rev] = fgetc(ifp);
+    return u.d;
+  default:
+    return fgetc(ifp);
+  }
+}
+
+double LibRaw::sgetreal(int type, uchar *s)
+{
+  union {
+    char c[8];
+    double d;
+  } u, v;
+  int i, rev;
+
+  switch (type)
+  {
+  case LIBRAW_EXIFTAG_TYPE_SHORT:
+    return (unsigned short)sget2(s);
+  case LIBRAW_EXIFTAG_TYPE_LONG:
+    return (unsigned int)sget4(s);
+  case LIBRAW_EXIFTAG_TYPE_RATIONAL: // (unsigned, unsigned)
+    u.d = (unsigned int)sget4(s);
+    v.d = (unsigned int)sget4(s+4);
+    return u.d / (v.d ? v.d : 1);
+  case LIBRAW_EXIFTAG_TYPE_SSHORT:
+    return (signed short)sget2(s);
+  case LIBRAW_EXIFTAG_TYPE_SLONG:
+    return (signed int)sget4(s);
+  case LIBRAW_EXIFTAG_TYPE_SRATIONAL: // (int, int)
+    u.d = (signed int)sget4(s);
+    v.d = (signed int)sget4(s+4);
+    return u.d / (v.d ? v.d : 1);
+  case LIBRAW_EXIFTAG_TYPE_FLOAT:
+    return int_to_float(sget4(s));
+  case LIBRAW_EXIFTAG_TYPE_DOUBLE:
+    rev = 7 * ((order == 0x4949) == (ntohs(0x1234) == 0x1234));
+    for (i = 0; i < 8; i++)
+      u.c[i ^ rev] = *(s+1);
+    return u.d;
+  default:
+    return *(s+1);
+  }
+}
+
+
+void LibRaw::read_shorts(ushort *pixel, unsigned count)
+{
+  if ((unsigned)fread(pixel, 2, count, ifp) < count)
+    derror();
+  if ((order == 0x4949) == (ntohs(0x1234) == 0x1234))
+    swab((char *)pixel, (char *)pixel, count * 2);
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/utils/thumb_utils.cpp libkdcraw/libkdcraw/libraw/src/utils/thumb_utils.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/utils/thumb_utils.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/utils/thumb_utils.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,313 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/libraw_cxx_defs.h"
+
+void LibRaw::kodak_thumb_loader()
+{
+  INT64 est_datasize =
+      T.theight * T.twidth / 3; // is 0.3 bytes per pixel good estimate?
+  if (ID.toffset < 0)
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+
+  if (ID.toffset + est_datasize > ID.input->size() + THUMB_READ_BEYOND)
+    throw LIBRAW_EXCEPTION_IO_EOF;
+
+  if(INT64(T.theight) * INT64(T.twidth) > 1024ULL * 1024ULL * LIBRAW_MAX_THUMBNAIL_MB)
+      throw LIBRAW_EXCEPTION_IO_CORRUPT;
+
+  if (INT64(T.theight) * INT64(T.twidth) < 64ULL)
+      throw LIBRAW_EXCEPTION_IO_CORRUPT;
+
+  // some kodak cameras
+  ushort s_height = S.height, s_width = S.width, s_iwidth = S.iwidth,
+         s_iheight = S.iheight;
+  ushort s_flags = libraw_internal_data.unpacker_data.load_flags;
+  libraw_internal_data.unpacker_data.load_flags = 12;
+  int s_colors = P1.colors;
+  unsigned s_filters = P1.filters;
+  ushort(*s_image)[4] = imgdata.image;
+
+  S.height = T.theight;
+  S.width = T.twidth;
+  P1.filters = 0;
+
+  if (thumb_load_raw == &LibRaw::kodak_ycbcr_load_raw)
+  {
+    S.height += S.height & 1;
+    S.width += S.width & 1;
+  }
+
+  imgdata.image =
+      (ushort(*)[4])calloc(S.iheight * S.iwidth, sizeof(*imgdata.image));
+  merror(imgdata.image, "LibRaw::kodak_thumb_loader()");
+
+  ID.input->seek(ID.toffset, SEEK_SET);
+  // read kodak thumbnail into T.image[]
+  try
+  {
+    (this->*thumb_load_raw)();
+  }
+  catch (...)
+  {
+    free(imgdata.image);
+    imgdata.image = s_image;
+
+    T.twidth = 0;
+    S.width = s_width;
+
+    S.iwidth = s_iwidth;
+    S.iheight = s_iheight;
+
+    T.theight = 0;
+    S.height = s_height;
+
+    T.tcolors = 0;
+    P1.colors = s_colors;
+
+    P1.filters = s_filters;
+    T.tlength = 0;
+    libraw_internal_data.unpacker_data.load_flags = s_flags;
+    return;
+  }
+
+  // from scale_colors
+  {
+    double dmax;
+    float scale_mul[4];
+    int c, val;
+    for (dmax = DBL_MAX, c = 0; c < 3; c++)
+      if (dmax > C.pre_mul[c])
+        dmax = C.pre_mul[c];
+
+    for (c = 0; c < 3; c++)
+      scale_mul[c] = (C.pre_mul[c] / dmax) * 65535.0 / C.maximum;
+    scale_mul[3] = scale_mul[1];
+
+    size_t size = S.height * S.width;
+    for (unsigned i = 0; i < size * 4; i++)
+    {
+      val = imgdata.image[0][i];
+      if (!val)
+        continue;
+      val *= scale_mul[i & 3];
+      imgdata.image[0][i] = CLIP(val);
+    }
+  }
+
+  // from convert_to_rgb
+  ushort *img;
+  int row, col;
+
+  int(*t_hist)[LIBRAW_HISTOGRAM_SIZE] =
+      (int(*)[LIBRAW_HISTOGRAM_SIZE])calloc(sizeof(*t_hist), 4);
+  merror(t_hist, "LibRaw::kodak_thumb_loader()");
+
+  float out[3], out_cam[3][4] = {{2.81761312, -1.98369181, 0.166078627, 0},
+                                 {-0.111855984, 1.73688626, -0.625030339, 0},
+                                 {-0.0379119813, -0.891268849, 1.92918086, 0}};
+
+  for (img = imgdata.image[0], row = 0; row < S.height; row++)
+    for (col = 0; col < S.width; col++, img += 4)
+    {
+      out[0] = out[1] = out[2] = 0;
+      int c;
+      for (c = 0; c < 3; c++)
+      {
+        out[0] += out_cam[0][c] * img[c];
+        out[1] += out_cam[1][c] * img[c];
+        out[2] += out_cam[2][c] * img[c];
+      }
+      for (c = 0; c < 3; c++)
+        img[c] = CLIP((int)out[c]);
+      for (c = 0; c < P1.colors; c++)
+        t_hist[c][img[c] >> 3]++;
+    }
+
+  // from gamma_lut
+  int(*save_hist)[LIBRAW_HISTOGRAM_SIZE] =
+      libraw_internal_data.output_data.histogram;
+  libraw_internal_data.output_data.histogram = t_hist;
+
+  // make curve output curve!
+  ushort *t_curve = (ushort *)calloc(sizeof(C.curve), 1);
+  merror(t_curve, "LibRaw::kodak_thumb_loader()");
+  memmove(t_curve, C.curve, sizeof(C.curve));
+  memset(C.curve, 0, sizeof(C.curve));
+  {
+    int perc, val, total, t_white = 0x2000, c;
+
+    perc = S.width * S.height * 0.01; /* 99th percentile white level */
+    if (IO.fuji_width)
+      perc /= 2;
+    if (!((O.highlight & ~2) || O.no_auto_bright))
+      for (t_white = c = 0; c < P1.colors; c++)
+      {
+        for (val = 0x2000, total = 0; --val > 32;)
+          if ((total += libraw_internal_data.output_data.histogram[c][val]) >
+              perc)
+            break;
+        if (t_white < val)
+          t_white = val;
+      }
+    gamma_curve(O.gamm[0], O.gamm[1], 2, (t_white << 3) / O.bright);
+  }
+
+  libraw_internal_data.output_data.histogram = save_hist;
+  free(t_hist);
+
+  // from write_ppm_tiff - copy pixels into bitmap
+
+  int s_flip = imgdata.sizes.flip;
+  if (imgdata.params.raw_processing_options &
+      LIBRAW_PROCESSING_NO_ROTATE_FOR_KODAK_THUMBNAILS)
+    imgdata.sizes.flip = 0;
+
+  S.iheight = S.height;
+  S.iwidth = S.width;
+  if (S.flip & 4)
+    SWAP(S.height, S.width);
+
+  if (T.thumb)
+    free(T.thumb);
+  T.thumb = (char *)calloc(S.width * S.height, P1.colors);
+  merror(T.thumb, "LibRaw::kodak_thumb_loader()");
+  T.tlength = S.width * S.height * P1.colors;
+
+  // from write_tiff_ppm
+  {
+    int soff = flip_index(0, 0);
+    int cstep = flip_index(0, 1) - soff;
+    int rstep = flip_index(1, 0) - flip_index(0, S.width);
+
+    for (int row = 0; row < S.height; row++, soff += rstep)
+    {
+      char *ppm = T.thumb + row * S.width * P1.colors;
+      for (int col = 0; col < S.width; col++, soff += cstep)
+        for (int c = 0; c < P1.colors; c++)
+          ppm[col * P1.colors + c] =
+              imgdata.color.curve[imgdata.image[soff][c]] >> 8;
+    }
+  }
+
+  memmove(C.curve, t_curve, sizeof(C.curve));
+  free(t_curve);
+
+  // restore variables
+  free(imgdata.image);
+  imgdata.image = s_image;
+
+  if (imgdata.params.raw_processing_options &
+      LIBRAW_PROCESSING_NO_ROTATE_FOR_KODAK_THUMBNAILS)
+    imgdata.sizes.flip = s_flip;
+
+  T.twidth = S.width;
+  S.width = s_width;
+
+  S.iwidth = s_iwidth;
+  S.iheight = s_iheight;
+
+  T.theight = S.height;
+  S.height = s_height;
+
+  T.tcolors = P1.colors;
+  P1.colors = s_colors;
+
+  P1.filters = s_filters;
+  libraw_internal_data.unpacker_data.load_flags = s_flags;
+}
+
+// ������� thumbnail �� �����, ������ thumb_format � ������������ � ��������
+
+int LibRaw::thumbOK(INT64 maxsz)
+{
+  if (!ID.input)
+    return 0;
+  if (!ID.toffset && !(imgdata.thumbnail.tlength > 0 &&
+                       load_raw == &LibRaw::broadcom_load_raw) // RPi
+  )
+    return 0;
+  INT64 fsize = ID.input->size();
+  if (fsize > 0x7fffffffU)
+    return 0; // No thumb for raw > 2Gb
+  int tsize = 0;
+  int tcol = (T.tcolors > 0 && T.tcolors < 4) ? T.tcolors : 3;
+  if (write_thumb == &LibRaw::jpeg_thumb)
+    tsize = T.tlength;
+  else if (write_thumb == &LibRaw::ppm_thumb)
+    tsize = tcol * T.twidth * T.theight;
+  else if (write_thumb == &LibRaw::ppm16_thumb)
+    tsize = tcol * T.twidth * T.theight *
+            ((imgdata.params.raw_processing_options &
+              LIBRAW_PROCESSING_USE_PPM16_THUMBS)
+                 ? 2
+                 : 1);
+#ifdef USE_X3FTOOLS
+  else if (write_thumb == &LibRaw::x3f_thumb_loader)
+  {
+    tsize = x3f_thumb_size();
+  }
+#endif
+  else // Kodak => no check
+    tsize = 1;
+  if (tsize < 0)
+    return 0;
+  if (maxsz > 0 && tsize > maxsz)
+    return 0;
+  return (tsize + ID.toffset <= fsize) ? 1 : 0;
+}
+
+int LibRaw::dcraw_thumb_writer(const char *fname)
+{
+  //    CHECK_ORDER_LOW(LIBRAW_PROGRESS_THUMB_LOAD);
+
+  if (!fname)
+    return ENOENT;
+
+  FILE *tfp = fopen(fname, "wb");
+
+  if (!tfp)
+    return errno;
+
+  if (!T.thumb)
+  {
+    fclose(tfp);
+    return LIBRAW_OUT_OF_ORDER_CALL;
+  }
+
+  try
+  {
+    switch (T.tformat)
+    {
+    case LIBRAW_THUMBNAIL_JPEG:
+      jpeg_thumb_writer(tfp, T.thumb, T.tlength);
+      break;
+    case LIBRAW_THUMBNAIL_BITMAP:
+      fprintf(tfp, "P6\n%d %d\n255\n", T.twidth, T.theight);
+      fwrite(T.thumb, 1, T.tlength, tfp);
+      break;
+    default:
+      fclose(tfp);
+      return LIBRAW_UNSUPPORTED_THUMBNAIL;
+    }
+    fclose(tfp);
+    return 0;
+  }
+  catch (LibRaw_exceptions err)
+  {
+    fclose(tfp);
+    EXCEPTION_HANDLER(err);
+  }
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/utils/utils_dcraw.cpp libkdcraw/libkdcraw/libraw/src/utils/utils_dcraw.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/utils/utils_dcraw.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/utils/utils_dcraw.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,337 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+int LibRaw::fcol(int row, int col)
+{
+  static const char filter[16][16] = {
+      {2, 1, 1, 3, 2, 3, 2, 0, 3, 2, 3, 0, 1, 2, 1, 0},
+      {0, 3, 0, 2, 0, 1, 3, 1, 0, 1, 1, 2, 0, 3, 3, 2},
+      {2, 3, 3, 2, 3, 1, 1, 3, 3, 1, 2, 1, 2, 0, 0, 3},
+      {0, 1, 0, 1, 0, 2, 0, 2, 2, 0, 3, 0, 1, 3, 2, 1},
+      {3, 1, 1, 2, 0, 1, 0, 2, 1, 3, 1, 3, 0, 1, 3, 0},
+      {2, 0, 0, 3, 3, 2, 3, 1, 2, 0, 2, 0, 3, 2, 2, 1},
+      {2, 3, 3, 1, 2, 1, 2, 1, 2, 1, 1, 2, 3, 0, 0, 1},
+      {1, 0, 0, 2, 3, 0, 0, 3, 0, 3, 0, 3, 2, 1, 2, 3},
+      {2, 3, 3, 1, 1, 2, 1, 0, 3, 2, 3, 0, 2, 3, 1, 3},
+      {1, 0, 2, 0, 3, 0, 3, 2, 0, 1, 1, 2, 0, 1, 0, 2},
+      {0, 1, 1, 3, 3, 2, 2, 1, 1, 3, 3, 0, 2, 1, 3, 2},
+      {2, 3, 2, 0, 0, 1, 3, 0, 2, 0, 1, 2, 3, 0, 1, 0},
+      {1, 3, 1, 2, 3, 2, 3, 2, 0, 2, 0, 1, 1, 0, 3, 0},
+      {0, 2, 0, 3, 1, 0, 0, 1, 1, 3, 3, 2, 3, 2, 2, 1},
+      {2, 1, 3, 2, 3, 1, 2, 1, 0, 3, 0, 2, 0, 2, 0, 2},
+      {0, 3, 1, 0, 0, 2, 0, 3, 2, 1, 3, 1, 1, 3, 1, 3}};
+
+  if (filters == 1)
+    return filter[(row + top_margin) & 15][(col + left_margin) & 15];
+  if (filters == 9)
+    return xtrans[(row + 6) % 6][(col + 6) % 6];
+  return FC(row, col);
+}
+
+size_t LibRaw::strnlen(const char *s, size_t n)
+{
+#if !defined(__FreeBSD__) && !defined(__OpenBSD__)
+  const char *p = (const char *)memchr(s, 0, n);
+  return (p ? p - s : n);
+#else
+  return ::strnlen(s, n);
+#endif
+}
+
+void *LibRaw::memmem(char *haystack, size_t haystacklen, char *needle,
+                     size_t needlelen)
+{
+#if !defined(__GLIBC__) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
+  char *c;
+  for (c = haystack; c <= haystack + haystacklen - needlelen; c++)
+    if (!memcmp(c, needle, needlelen))
+      return c;
+  return 0;
+#else
+  return ::memmem(haystack, haystacklen, needle, needlelen);
+#endif
+}
+
+char *LibRaw::strcasestr(char *haystack, const char *needle)
+{
+  char *c;
+  for (c = haystack; *c; c++)
+    if (!strncasecmp(c, needle, strlen(needle)))
+      return c;
+  return 0;
+}
+
+ushort LibRaw::sget2(uchar *s)
+{
+  if (order == 0x4949) /* "II" means little-endian */
+    return s[0] | s[1] << 8;
+  else /* "MM" means big-endian */
+    return s[0] << 8 | s[1];
+}
+
+void LibRaw::initdata()
+{
+  tiff_flip = flip = filters = UINT_MAX; /* unknown */
+  raw_height = raw_width = fuji_width = fuji_layout = cr2_slice[0] = 0;
+  maximum = height = width = top_margin = left_margin = 0;
+  cdesc[0] = desc[0] = artist[0] = make[0] = model[0] = model2[0] = 0;
+  iso_speed = shutter = aperture = focal_len = 0;
+  unique_id = 0ULL;
+  tiff_nifds = 0;
+  memset(tiff_ifd, 0, sizeof tiff_ifd);
+  for (int i = 0; i < LIBRAW_IFD_MAXCOUNT; i++)
+  {
+    tiff_ifd[i].dng_color[0].illuminant = tiff_ifd[i].dng_color[1].illuminant =
+        0xffff;
+    for (int c = 0; c < 4; c++)
+      tiff_ifd[i].dng_levels.analogbalance[c] = 1.0f;
+  }
+  for (int i = 0; i < 0x10000; i++)
+    curve[i] = i;
+  memset(gpsdata, 0, sizeof gpsdata);
+  memset(cblack, 0, sizeof cblack);
+  memset(white, 0, sizeof white);
+  memset(mask, 0, sizeof mask);
+  thumb_offset = thumb_length = thumb_width = thumb_height = 0;
+  load_raw = thumb_load_raw = 0;
+  write_thumb = &LibRaw::jpeg_thumb;
+  data_offset = meta_offset = meta_length = tiff_bps = tiff_compress = 0;
+  kodak_cbpp = zero_after_ff = dng_version = load_flags = 0;
+  timestamp = shot_order = tiff_samples = black = is_foveon = 0;
+  mix_green = profile_length = data_error = zero_is_bad = 0;
+  pixel_aspect = is_raw = raw_color = 1;
+  tile_width = tile_length = 0;
+  metadata_blocks = 0;
+  is_NikonTransfer = 0;
+  is_Sony = 0;
+  is_pana_raw = 0;
+  maker_index = LIBRAW_CAMERAMAKER_Unknown;
+  is_4K_RAFdata = 0;
+  FujiCropMode = 0;
+  is_PentaxRicohMakernotes = 0;
+  normalized_model[0] = 0;
+  normalized_make[0] = 0;
+  CM_found = 0;
+}
+
+void LibRaw::aRGB_coeff(double aRGB_cam[3][3])
+{
+  static const double rgb_aRGB[3][3] = {
+      {1.39828313770000, -0.3982830047, 9.64980900741708E-8},
+      {6.09219200572997E-8, 0.9999999809, 1.33230799934103E-8},
+      {2.17237099975343E-8, -0.0429383201, 1.04293828050000}};
+
+  double cmatrix_tmp[3][3] = {
+      {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}};
+  int i, j, k;
+
+  for (i = 0; i < 3; i++)
+    for (j = 0; j < 3; j++)
+    {
+      for (k = 0; k < 3; k++)
+        cmatrix_tmp[i][j] += rgb_aRGB[i][k] * aRGB_cam[k][j];
+      cmatrix[i][j] = (float)cmatrix_tmp[i][j];
+    }
+}
+
+void LibRaw::romm_coeff(float romm_cam[3][3])
+{
+  static const float rgb_romm[3][3] = /* ROMM == Kodak ProPhoto */
+      {{2.034193, -0.727420, -0.306766},
+       {-0.228811, 1.231729, -0.002922},
+       {-0.008565, -0.153273, 1.161839}};
+  int i, j, k;
+
+  for (i = 0; i < 3; i++)
+    for (j = 0; j < 3; j++)
+      for (cmatrix[i][j] = k = 0; k < 3; k++)
+        cmatrix[i][j] += rgb_romm[i][k] * romm_cam[k][j];
+}
+
+void LibRaw::remove_zeroes()
+{
+  unsigned row, col, tot, n;
+  int r, c;
+
+  RUN_CALLBACK(LIBRAW_PROGRESS_REMOVE_ZEROES, 0, 2);
+
+  for (row = 0; row < height; row++)
+    for (col = 0; col < width; col++)
+      if (BAYER(row, col) == 0)
+      {
+        tot = n = 0;
+        for (r = (int)row - 2; r <= (int)row + 2; r++)
+          for (c = (int)col - 2; c <= (int)col + 2; c++)
+            if (r >= 0 && r < height && c >= 0 && c < width &&
+                FC(r, c) == FC(row, col) && BAYER(r, c))
+              tot += (n++, BAYER(r, c));
+        if (n)
+          BAYER(row, col) = tot / n;
+      }
+  RUN_CALLBACK(LIBRAW_PROGRESS_REMOVE_ZEROES, 1, 2);
+}
+void LibRaw::crop_masked_pixels()
+{
+  int row, col;
+  unsigned c, m, zero, val;
+#define mblack imgdata.color.black_stat
+
+  if (mask[0][3] > 0)
+    goto mask_set;
+  if (load_raw == &LibRaw::canon_load_raw ||
+      load_raw == &LibRaw::lossless_jpeg_load_raw ||
+      load_raw == &LibRaw::crxLoadRaw)
+  {
+    mask[0][1] = mask[1][1] += 2;
+    mask[0][3] -= 2;
+    goto sides;
+  }
+  if (load_raw == &LibRaw::canon_600_load_raw ||
+      load_raw == &LibRaw::sony_load_raw ||
+      (load_raw == &LibRaw::eight_bit_load_raw && strncmp(model, "DC2", 3)) ||
+      load_raw == &LibRaw::kodak_262_load_raw ||
+      (load_raw == &LibRaw::packed_load_raw && (load_flags & 32)))
+  {
+  sides:
+    mask[0][0] = mask[1][0] = top_margin;
+    mask[0][2] = mask[1][2] = top_margin + height;
+    mask[0][3] += left_margin;
+    mask[1][1] += left_margin + width;
+    mask[1][3] += raw_width;
+  }
+  if (load_raw == &LibRaw::nokia_load_raw)
+  {
+    mask[0][2] = top_margin;
+    mask[0][3] = width;
+  }
+  if (load_raw == &LibRaw::broadcom_load_raw)
+  {
+    mask[0][2] = top_margin;
+    mask[0][3] = width;
+  }
+mask_set:
+  memset(mblack, 0, sizeof mblack);
+  for (zero = m = 0; m < 8; m++)
+    for (row = MAX(mask[m][0], 0); row < MIN(mask[m][2], raw_height); row++)
+      for (col = MAX(mask[m][1], 0); col < MIN(mask[m][3], raw_width); col++)
+      {
+        /* No need to subtract margins because full area and active area filters are the same */
+        c = FC(row, col);
+        mblack[c] += val = raw_image[(row)*raw_pitch / 2 + (col)];
+        mblack[4 + c]++;
+        zero += !val;
+      }
+  if (load_raw == &LibRaw::canon_600_load_raw && width < raw_width)
+  {
+    black = (mblack[0] + mblack[1] + mblack[2] + mblack[3]) /
+                MAX(1, (mblack[4] + mblack[5] + mblack[6] + mblack[7])) -
+            4;
+  }
+  else if (zero < mblack[4] && mblack[5] && mblack[6] && mblack[7])
+  {
+    FORC4 cblack[c] = mblack[c] / MAX(1, mblack[4 + c]);
+    black = cblack[4] = cblack[5] = cblack[6] = 0;
+  }
+}
+#undef mblack
+
+void LibRaw::pseudoinverse(double (*in)[3], double (*out)[3], int size)
+{
+  double work[3][6], num;
+  int i, j, k;
+
+  for (i = 0; i < 3; i++)
+  {
+    for (j = 0; j < 6; j++)
+      work[i][j] = j == i + 3;
+    for (j = 0; j < 3; j++)
+      for (k = 0; k < size && k < 4; k++)
+        work[i][j] += in[k][i] * in[k][j];
+  }
+  for (i = 0; i < 3; i++)
+  {
+    num = work[i][i];
+    for (j = 0; j < 6; j++)
+      if (fabs(num) > 0.00001f)
+        work[i][j] /= num;
+    for (k = 0; k < 3; k++)
+    {
+      if (k == i)
+        continue;
+      num = work[k][i];
+      for (j = 0; j < 6; j++)
+        work[k][j] -= work[i][j] * num;
+    }
+  }
+  for (i = 0; i < size && i < 4; i++)
+    for (j = 0; j < 3; j++)
+      for (out[i][j] = k = 0; k < 3; k++)
+        out[i][j] += work[j][k + 3] * in[i][k];
+}
+
+void LibRaw::cam_xyz_coeff(float _rgb_cam[3][4], double cam_xyz[4][3])
+{
+  double cam_rgb[4][3], inverse[4][3], num;
+  int i, j, k;
+
+  for (i = 0; i < colors && i < 4; i++) /* Multiply out XYZ colorspace */
+    for (j = 0; j < 3; j++)
+      for (cam_rgb[i][j] = k = 0; k < 3; k++)
+        cam_rgb[i][j] += cam_xyz[i][k] * LibRaw_constants::xyz_rgb[k][j];
+
+  for (i = 0; i < colors && i < 4; i++)
+  {                               /* Normalize cam_rgb so that */
+    for (num = j = 0; j < 3; j++) /* cam_rgb * (1,1,1) is (1,1,1,1) */
+      num += cam_rgb[i][j];
+    if (num > 0.00001)
+    {
+      for (j = 0; j < 3; j++)
+        cam_rgb[i][j] /= num;
+      pre_mul[i] = 1 / num;
+    }
+    else
+    {
+      for (j = 0; j < 3; j++)
+        cam_rgb[i][j] = 0.0;
+      pre_mul[i] = 1.0;
+    }
+  }
+  pseudoinverse(cam_rgb, inverse, colors);
+  for (i = 0; i < 3; i++)
+    for (j = 0; j < colors && j < 4; j++)
+      _rgb_cam[i][j] = inverse[j][i];
+}
+
+void LibRaw::tiff_get(unsigned base, unsigned *tag, unsigned *type,
+                      unsigned *len, unsigned *save)
+{
+#ifdef LIBRAW_IOSPACE_CHECK
+  INT64 pos = ftell(ifp);
+  INT64 fsize = ifp->size();
+  if (fsize < 12 || (fsize - pos) < 12)
+    throw LIBRAW_EXCEPTION_IO_EOF;
+#endif
+  *tag = get2();
+  *type = get2();
+  *len = get4();
+  *save = ftell(ifp) + 4;
+  if (*len * tagtype_dataunit_bytes[(*type <= LIBRAW_EXIFTAG_TYPE_IFD8) ? *type : 0] > 4)
+    fseek(ifp, get4() + base, SEEK_SET);
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/utils/utils_libraw.cpp libkdcraw/libkdcraw/libraw/src/utils/utils_libraw.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/utils/utils_libraw.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/utils/utils_libraw.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,607 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/libraw_cxx_defs.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+  void default_memory_callback(void *, const char *file, const char *where)
+  {
+    fprintf(stderr, "%s: Out of memory in %s\n", file ? file : "unknown file",
+            where);
+  }
+
+  void default_data_callback(void *, const char *file, const int offset)
+  {
+    if (offset < 0)
+      fprintf(stderr, "%s: Unexpected end of file\n",
+              file ? file : "unknown file");
+    else
+      fprintf(stderr, "%s: data corrupted at %d\n",
+              file ? file : "unknown file", offset);
+  }
+  const char *libraw_strerror(int e)
+  {
+    enum LibRaw_errors errorcode = (LibRaw_errors)e;
+    switch (errorcode)
+    {
+    case LIBRAW_SUCCESS:
+      return "No error";
+    case LIBRAW_UNSPECIFIED_ERROR:
+      return "Unspecified error";
+    case LIBRAW_FILE_UNSUPPORTED:
+      return "Unsupported file format or not RAW file";
+    case LIBRAW_REQUEST_FOR_NONEXISTENT_IMAGE:
+      return "Request for nonexisting image number";
+    case LIBRAW_OUT_OF_ORDER_CALL:
+      return "Out of order call of libraw function";
+    case LIBRAW_NO_THUMBNAIL:
+      return "No thumbnail in file";
+    case LIBRAW_UNSUPPORTED_THUMBNAIL:
+      return "Unsupported thumbnail format";
+    case LIBRAW_INPUT_CLOSED:
+      return "No input stream, or input stream closed";
+    case LIBRAW_MEMPOOL_OVERFLOW:
+      return "Libraw internal mempool overflowed";
+    case LIBRAW_UNSUFFICIENT_MEMORY:
+      return "Unsufficient memory";
+    case LIBRAW_DATA_ERROR:
+      return "Corrupted data or unexpected EOF";
+    case LIBRAW_IO_ERROR:
+      return "Input/output error";
+    case LIBRAW_CANCELLED_BY_CALLBACK:
+      return "Cancelled by user callback";
+    case LIBRAW_BAD_CROP:
+      return "Bad crop box";
+    case LIBRAW_TOO_BIG:
+      return "Image too big for processing";
+    default:
+      return "Unknown error code";
+    }
+  }
+
+#ifdef __cplusplus
+}
+#endif
+
+unsigned LibRaw::parse_custom_cameras(unsigned limit,
+                                      libraw_custom_camera_t table[],
+                                      char **list)
+{
+  if (!list)
+    return 0;
+  unsigned index = 0;
+  for (unsigned i = 0; i < limit; i++)
+  {
+    if (!list[i])
+      break;
+    if (strlen(list[i]) < 10)
+      continue;
+    char *string = (char *)malloc(strlen(list[i]) + 1);
+    strcpy(string, list[i]);
+    char *start = string;
+    memset(&table[index], 0, sizeof(table[0]));
+    for (int j = 0; start && j < 14; j++)
+    {
+      char *end = strchr(start, ',');
+      if (end)
+      {
+        *end = 0;
+        end++;
+      } // move to next char
+      while (isspace(*start) && *start)
+        start++; // skip leading spaces?
+      unsigned val = strtol(start, 0, 10);
+      switch (j)
+      {
+      case 0:
+        table[index].fsize = val;
+        break;
+      case 1:
+        table[index].rw = val;
+        break;
+      case 2:
+        table[index].rh = val;
+        break;
+      case 3:
+        table[index].lm = val;
+        break;
+      case 4:
+        table[index].tm = val;
+        break;
+      case 5:
+        table[index].rm = val;
+        break;
+      case 6:
+        table[index].bm = val;
+        break;
+      case 7:
+        table[index].lf = val;
+        break;
+      case 8:
+        table[index].cf = val;
+        break;
+      case 9:
+        table[index].max = val;
+        break;
+      case 10:
+        table[index].flags = val;
+        break;
+      case 11:
+        strncpy(table[index].t_make, start, sizeof(table[index].t_make) - 1);
+        break;
+      case 12:
+        strncpy(table[index].t_model, start, sizeof(table[index].t_model) - 1);
+        break;
+      case 13:
+        table[index].offset = val;
+        break;
+      default:
+        break;
+      }
+      start = end;
+    }
+    free(string);
+    if (table[index].t_make[0])
+      index++;
+  }
+  return index;
+}
+
+void LibRaw::derror()
+{
+  if (!libraw_internal_data.unpacker_data.data_error &&
+      libraw_internal_data.internal_data.input)
+  {
+    if (libraw_internal_data.internal_data.input->eof())
+    {
+      if (callbacks.data_cb)
+        (*callbacks.data_cb)(callbacks.datacb_data,
+                             libraw_internal_data.internal_data.input->fname(),
+                             -1);
+      throw LIBRAW_EXCEPTION_IO_EOF;
+    }
+    else
+    {
+      if (callbacks.data_cb)
+        (*callbacks.data_cb)(callbacks.datacb_data,
+                             libraw_internal_data.internal_data.input->fname(),
+                             libraw_internal_data.internal_data.input->tell());
+      // throw LIBRAW_EXCEPTION_IO_CORRUPT;
+    }
+  }
+  libraw_internal_data.unpacker_data.data_error++;
+}
+
+const char *LibRaw::version() { return LIBRAW_VERSION_STR; }
+int LibRaw::versionNumber() { return LIBRAW_VERSION; }
+const char *LibRaw::strerror(int p) { return libraw_strerror(p); }
+
+unsigned LibRaw::capabilities()
+{
+  unsigned ret = 0;
+#ifdef USE_RAWSPEED
+  ret |= LIBRAW_CAPS_RAWSPEED;
+#endif
+#ifdef USE_DNGSDK
+  ret |= LIBRAW_CAPS_DNGSDK;
+#ifdef USE_GPRSDK
+  ret |= LIBRAW_CAPS_GPRSDK;
+#endif
+#ifdef LIBRAW_WIN32_UNICODEPATHS
+  ret |= LIBRAW_CAPS_UNICODEPATHS;
+#endif
+#endif
+#ifdef USE_X3FTOOLS
+  ret |= LIBRAW_CAPS_X3FTOOLS;
+#endif
+#ifdef USE_6BY9RPI
+  ret |= LIBRAW_CAPS_RPI6BY9;
+#endif
+  return ret;
+}
+
+int LibRaw::is_sraw()
+{
+  return load_raw == &LibRaw::canon_sraw_load_raw ||
+         load_raw == &LibRaw::nikon_load_sraw;
+}
+int LibRaw::is_coolscan_nef()
+{
+  return load_raw == &LibRaw::nikon_coolscan_load_raw;
+}
+int LibRaw::is_jpeg_thumb()
+{
+  return thumb_load_raw == 0 && write_thumb == &LibRaw::jpeg_thumb;
+}
+
+int LibRaw::is_nikon_sraw() { return load_raw == &LibRaw::nikon_load_sraw; }
+int LibRaw::sraw_midpoint()
+{
+  if (load_raw == &LibRaw::canon_sraw_load_raw)
+    return 8192;
+  else if (load_raw == &LibRaw::nikon_load_sraw)
+    return 2048;
+  else
+    return 0;
+}
+
+void *LibRaw::malloc(size_t t)
+{
+  void *p = memmgr.malloc(t);
+  if (!p)
+    throw LIBRAW_EXCEPTION_ALLOC;
+  return p;
+}
+void *LibRaw::realloc(void *q, size_t t)
+{
+  void *p = memmgr.realloc(q, t);
+  if (!p)
+    throw LIBRAW_EXCEPTION_ALLOC;
+  return p;
+}
+
+void *LibRaw::calloc(size_t n, size_t t)
+{
+  void *p = memmgr.calloc(n, t);
+  if (!p)
+    throw LIBRAW_EXCEPTION_ALLOC;
+  return p;
+}
+void LibRaw::free(void *p) { memmgr.free(p); }
+
+void LibRaw::recycle_datastream()
+{
+  if (libraw_internal_data.internal_data.input &&
+      libraw_internal_data.internal_data.input_internal)
+  {
+    delete libraw_internal_data.internal_data.input;
+    libraw_internal_data.internal_data.input = NULL;
+  }
+  libraw_internal_data.internal_data.input_internal = 0;
+}
+void LibRaw::merror(void *ptr, const char *where)
+{
+  if (ptr)
+    return;
+  if (callbacks.mem_cb)
+    (*callbacks.mem_cb)(callbacks.memcb_data,
+                        libraw_internal_data.internal_data.input
+                            ? libraw_internal_data.internal_data.input->fname()
+                            : NULL,
+                        where);
+  throw LIBRAW_EXCEPTION_ALLOC;
+}
+
+void LibRaw::clearCancelFlag()
+{
+#ifdef _MSC_VER
+  InterlockedExchange(&_exitflag, 0);
+#else
+  __sync_fetch_and_and(&_exitflag, 0);
+#endif
+#ifdef RAWSPEED_FASTEXIT
+  if (_rawspeed_decoder)
+  {
+    RawSpeed::RawDecoder *d =
+        static_cast<RawSpeed::RawDecoder *>(_rawspeed_decoder);
+    d->resumeProcessing();
+  }
+#endif
+}
+
+void LibRaw::setCancelFlag()
+{
+#ifdef _MSC_VER
+  InterlockedExchange(&_exitflag, 1);
+#else
+  __sync_fetch_and_add(&_exitflag, 1);
+#endif
+#ifdef RAWSPEED_FASTEXIT
+  if (_rawspeed_decoder)
+  {
+    RawSpeed::RawDecoder *d =
+        static_cast<RawSpeed::RawDecoder *>(_rawspeed_decoder);
+    d->cancelProcessing();
+  }
+#endif
+}
+
+void LibRaw::checkCancel()
+{
+#ifdef _MSC_VER
+  if (InterlockedExchange(&_exitflag, 0))
+    throw LIBRAW_EXCEPTION_CANCELLED_BY_CALLBACK;
+#else
+  if (__sync_fetch_and_and(&_exitflag, 0))
+    throw LIBRAW_EXCEPTION_CANCELLED_BY_CALLBACK;
+#endif
+}
+
+int LibRaw::is_curve_linear()
+{
+  for (int i = 0; i < 0x10000; i++)
+    if (imgdata.color.curve[i] != i)
+      return 0;
+  return 1;
+}
+
+void LibRaw::free_image(void)
+{
+  if (imgdata.image)
+  {
+    free(imgdata.image);
+    imgdata.image = 0;
+    imgdata.progress_flags = LIBRAW_PROGRESS_START | LIBRAW_PROGRESS_OPEN |
+                             LIBRAW_PROGRESS_IDENTIFY |
+                             LIBRAW_PROGRESS_SIZE_ADJUST |
+                             LIBRAW_PROGRESS_LOAD_RAW;
+  }
+}
+
+int LibRaw::is_phaseone_compressed()
+{
+  return (load_raw == &LibRaw::phase_one_load_raw_c ||
+          load_raw == &LibRaw::phase_one_load_raw);
+}
+
+int LibRaw::is_canon_600() { return load_raw == &LibRaw::canon_600_load_raw; }
+const char *LibRaw::strprogress(enum LibRaw_progress p)
+{
+  switch (p)
+  {
+  case LIBRAW_PROGRESS_START:
+    return "Starting";
+  case LIBRAW_PROGRESS_OPEN:
+    return "Opening file";
+  case LIBRAW_PROGRESS_IDENTIFY:
+    return "Reading metadata";
+  case LIBRAW_PROGRESS_SIZE_ADJUST:
+    return "Adjusting size";
+  case LIBRAW_PROGRESS_LOAD_RAW:
+    return "Reading RAW data";
+  case LIBRAW_PROGRESS_REMOVE_ZEROES:
+    return "Clearing zero values";
+  case LIBRAW_PROGRESS_BAD_PIXELS:
+    return "Removing dead pixels";
+  case LIBRAW_PROGRESS_DARK_FRAME:
+    return "Subtracting dark frame data";
+  case LIBRAW_PROGRESS_FOVEON_INTERPOLATE:
+    return "Interpolating Foveon sensor data";
+  case LIBRAW_PROGRESS_SCALE_COLORS:
+    return "Scaling colors";
+  case LIBRAW_PROGRESS_PRE_INTERPOLATE:
+    return "Pre-interpolating";
+  case LIBRAW_PROGRESS_INTERPOLATE:
+    return "Interpolating";
+  case LIBRAW_PROGRESS_MIX_GREEN:
+    return "Mixing green channels";
+  case LIBRAW_PROGRESS_MEDIAN_FILTER:
+    return "Median filter";
+  case LIBRAW_PROGRESS_HIGHLIGHTS:
+    return "Highlight recovery";
+  case LIBRAW_PROGRESS_FUJI_ROTATE:
+    return "Rotating Fuji diagonal data";
+  case LIBRAW_PROGRESS_FLIP:
+    return "Flipping image";
+  case LIBRAW_PROGRESS_APPLY_PROFILE:
+    return "ICC conversion";
+  case LIBRAW_PROGRESS_CONVERT_RGB:
+    return "Converting to RGB";
+  case LIBRAW_PROGRESS_STRETCH:
+    return "Stretching image";
+  case LIBRAW_PROGRESS_THUMB_LOAD:
+    return "Loading thumbnail";
+  default:
+    return "Some strange things";
+  }
+}
+int LibRaw::adjust_sizes_info_only(void)
+{
+  CHECK_ORDER_LOW(LIBRAW_PROGRESS_IDENTIFY);
+
+  raw2image_start();
+  if (O.use_fuji_rotate)
+  {
+    if (IO.fuji_width)
+    {
+      IO.fuji_width = (IO.fuji_width - 1 + IO.shrink) >> IO.shrink;
+      S.iwidth = (ushort)(IO.fuji_width / sqrt(0.5));
+      S.iheight = (ushort)((S.iheight - IO.fuji_width) / sqrt(0.5));
+    }
+    else
+    {
+      if (S.pixel_aspect < 0.995)
+        S.iheight = (ushort)(S.iheight / S.pixel_aspect + 0.5);
+      if (S.pixel_aspect > 1.005)
+        S.iwidth = (ushort)(S.iwidth * S.pixel_aspect + 0.5);
+    }
+  }
+  SET_PROC_FLAG(LIBRAW_PROGRESS_FUJI_ROTATE);
+  if (S.flip & 4)
+  {
+    unsigned short t = S.iheight;
+    S.iheight = S.iwidth;
+    S.iwidth = t;
+    SET_PROC_FLAG(LIBRAW_PROGRESS_FLIP);
+  }
+  return 0;
+}
+int LibRaw::adjust_maximum()
+{
+  ushort real_max;
+  float auto_threshold;
+
+  if (O.adjust_maximum_thr < 0.00001)
+    return LIBRAW_SUCCESS;
+  else if (O.adjust_maximum_thr > 0.99999)
+    auto_threshold = LIBRAW_DEFAULT_ADJUST_MAXIMUM_THRESHOLD;
+  else
+    auto_threshold = O.adjust_maximum_thr;
+
+  real_max = C.data_maximum;
+  if (real_max > 0 && real_max < C.maximum &&
+      real_max > C.maximum * auto_threshold)
+  {
+    C.maximum = real_max;
+  }
+  return LIBRAW_SUCCESS;
+}
+void LibRaw::adjust_bl()
+{
+  int clear_repeat = 0;
+  if (O.user_black >= 0)
+  {
+    C.black = O.user_black;
+    clear_repeat = 1;
+  }
+  for (int i = 0; i < 4; i++)
+    if (O.user_cblack[i] > -1000000)
+    {
+      C.cblack[i] = O.user_cblack[i];
+      clear_repeat = 1;
+    }
+
+  if (clear_repeat)
+    C.cblack[4] = C.cblack[5] = 0;
+
+  // Add common part to cblack[] early
+  if (imgdata.idata.filters > 1000 && (C.cblack[4] + 1) / 2 == 1 &&
+      (C.cblack[5] + 1) / 2 == 1)
+  {
+    int clrs[4];
+    int lastg = -1, gcnt = 0;
+    for (int c = 0; c < 4; c++)
+    {
+      clrs[c] = FC(c / 2, c % 2);
+      if (clrs[c] == 1)
+      {
+        gcnt++;
+        lastg = c;
+      }
+    }
+    if (gcnt > 1 && lastg >= 0)
+      clrs[lastg] = 3;
+    for (int c = 0; c < 4; c++)
+      C.cblack[clrs[c]] +=
+          C.cblack[6 + c / 2 % C.cblack[4] * C.cblack[5] + c % 2 % C.cblack[5]];
+    C.cblack[4] = C.cblack[5] = 0;
+    // imgdata.idata.filters = sfilters;
+  }
+  else if (imgdata.idata.filters <= 1000 && C.cblack[4] == 1 &&
+           C.cblack[5] == 1) // Fuji RAF dng
+  {
+    for (int c = 0; c < 4; c++)
+      C.cblack[c] += C.cblack[6];
+    C.cblack[4] = C.cblack[5] = 0;
+  }
+  // remove common part from C.cblack[]
+  int i = C.cblack[3];
+  int c;
+  for (c = 0; c < 3; c++)
+    if (i > (int)C.cblack[c])
+      i = C.cblack[c];
+
+  for (c = 0; c < 4; c++)
+    C.cblack[c] -= i; // remove common part
+  C.black += i;
+
+  // Now calculate common part for cblack[6+] part and move it to C.black
+
+  if (C.cblack[4] && C.cblack[5])
+  {
+    i = C.cblack[6];
+    for (c = 1; c < int(C.cblack[4] * C.cblack[5]); c++)
+      if (i > int(C.cblack[6 + c]))
+        i = C.cblack[6 + c];
+    // Remove i from cblack[6+]
+    int nonz = 0;
+    for (c = 0; c < int(C.cblack[4] * C.cblack[5]); c++)
+    {
+      C.cblack[6 + c] -= i;
+      if (C.cblack[6 + c])
+        nonz++;
+    }
+    C.black += i;
+    if (!nonz)
+      C.cblack[4] = C.cblack[5] = 0;
+  }
+  for (c = 0; c < 4; c++)
+    C.cblack[c] += C.black;
+}
+int LibRaw::getwords(char *line, char *words[], int maxwords, int maxlen)
+{
+  line[maxlen - 1] = 0;
+  char *p = line;
+  int nwords = 0;
+
+  while (1)
+  {
+    while (isspace(*p))
+      p++;
+    if (*p == '\0')
+      return nwords;
+    words[nwords++] = p;
+    while (!isspace(*p) && *p != '\0')
+      p++;
+    if (*p == '\0')
+      return nwords;
+    *p++ = '\0';
+    if (nwords >= maxwords)
+      return nwords;
+  }
+}
+int LibRaw::stread(char *buf, size_t len, LibRaw_abstract_datastream *fp)
+{
+  if (len > 0)
+  {
+    int r = fp->read(buf, len, 1);
+    buf[len - 1] = 0;
+    return r;
+  }
+  else
+    return 0;
+}
+
+int LibRaw::find_ifd_by_offset(int o)
+{
+    for(unsigned i = 0; i < libraw_internal_data.identify_data.tiff_nifds && i < LIBRAW_IFD_MAXCOUNT; i++)
+        if(tiff_ifd[i].offset == o)
+            return i;
+    return -1;
+}
+
+short LibRaw::tiff_sget (unsigned save, uchar *buf, unsigned buf_len, INT64 *tag_offset,
+                         unsigned *tag_id, unsigned *tag_type, INT64 *tag_dataoffset,
+                         unsigned *tag_datalen, int *tag_dataunitlen) {
+  uchar *pos = buf + *tag_offset;
+  if ((((*tag_offset) + 12) > buf_len) || (*tag_offset < 0)) { // abnormal, tag buffer overrun
+    return -1;
+  }
+  *tag_id      = sget2(pos); pos += 2;
+  *tag_type    = sget2(pos); pos += 2;
+  *tag_datalen = sget4(pos); pos += 4;
+  *tag_dataunitlen = tagtype_dataunit_bytes[(*tag_type <= LIBRAW_EXIFTAG_TYPE_IFD8) ? *tag_type : 0];
+  if ((*tag_datalen * (*tag_dataunitlen)) > 4) {
+    *tag_dataoffset = sget4(pos) - save;
+    if ((*tag_dataoffset + *tag_datalen) > buf_len) { // abnormal, tag data buffer overrun
+      return -2;
+    }
+  } else *tag_dataoffset = *tag_offset + 8;
+  *tag_offset += 12;
+  return 0;
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/write/apply_profile.cpp libkdcraw/libkdcraw/libraw/src/write/apply_profile.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/write/apply_profile.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/write/apply_profile.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,77 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_fileio_defs.h"
+
+#ifndef NO_LCMS
+void LibRaw::apply_profile(const char *input, const char *output)
+{
+  char *prof;
+  cmsHPROFILE hInProfile = 0, hOutProfile = 0;
+  cmsHTRANSFORM hTransform;
+  FILE *fp;
+  unsigned size;
+
+  if (strcmp(input, "embed"))
+    hInProfile = cmsOpenProfileFromFile(input, "r");
+  else if (profile_length)
+  {
+    hInProfile = cmsOpenProfileFromMem(imgdata.color.profile, profile_length);
+  }
+  else
+  {
+    imgdata.process_warnings |= LIBRAW_WARN_NO_EMBEDDED_PROFILE;
+  }
+  if (!hInProfile)
+  {
+    imgdata.process_warnings |= LIBRAW_WARN_NO_INPUT_PROFILE;
+    return;
+  }
+  if (!output)
+    hOutProfile = cmsCreate_sRGBProfile();
+  else if ((fp = fopen(output, "rb")))
+  {
+    fread(&size, 4, 1, fp);
+    fseek(fp, 0, SEEK_SET);
+    oprof = (unsigned *)malloc(size = ntohl(size));
+    merror(oprof, "apply_profile()");
+    fread(oprof, 1, size, fp);
+    fclose(fp);
+    if (!(hOutProfile = cmsOpenProfileFromMem(oprof, size)))
+    {
+      free(oprof);
+      oprof = 0;
+    }
+  }
+  if (!hOutProfile)
+  {
+    imgdata.process_warnings |= LIBRAW_WARN_BAD_OUTPUT_PROFILE;
+    goto quit;
+  }
+  RUN_CALLBACK(LIBRAW_PROGRESS_APPLY_PROFILE, 0, 2);
+  hTransform = cmsCreateTransform(hInProfile, TYPE_RGBA_16, hOutProfile,
+                                  TYPE_RGBA_16, INTENT_PERCEPTUAL, 0);
+  cmsDoTransform(hTransform, image, image, width * height);
+  raw_color = 1; /* Don't use rgb_cam with a profile */
+  cmsDeleteTransform(hTransform);
+  cmsCloseProfile(hOutProfile);
+quit:
+  cmsCloseProfile(hInProfile);
+  RUN_CALLBACK(LIBRAW_PROGRESS_APPLY_PROFILE, 1, 2);
+}
+#endif
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/write/file_write.cpp libkdcraw/libkdcraw/libraw/src/write/file_write.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/write/file_write.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/write/file_write.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,289 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/dcraw_defs.h"
+
+int LibRaw::flip_index(int row, int col)
+{
+  if (flip & 4)
+    SWAP(row, col);
+  if (flip & 2)
+    row = iheight - 1 - row;
+  if (flip & 1)
+    col = iwidth - 1 - col;
+  return row * iwidth + col;
+}
+
+void LibRaw::tiff_set(struct tiff_hdr *th, ushort *ntag, ushort tag,
+                      ushort type, int count, int val)
+{
+  struct libraw_tiff_tag *tt;
+  int c;
+
+  tt = (struct libraw_tiff_tag *)(ntag + 1) + (*ntag)++;
+  tt->val.i = val;
+  if (tagtypeIs(LIBRAW_EXIFTAG_TYPE_BYTE) && count <= 4)
+    FORC(4) tt->val.c[c] = val >> (c << 3);
+  else if (tagtypeIs(LIBRAW_EXIFTAG_TYPE_ASCII))
+  {
+    count = strnlen((char *)th + val, count - 1) + 1;
+    if (count <= 4)
+      FORC(4) tt->val.c[c] = ((char *)th)[val + c];
+  }
+  else if (tagtypeIs(LIBRAW_EXIFTAG_TYPE_SHORT) && count <= 2)
+    FORC(2) tt->val.s[c] = val >> (c << 4);
+  tt->count = count;
+  tt->type = type;
+  tt->tag = tag;
+}
+
+#define TOFF(ptr) ((char *)(&(ptr)) - (char *)th)
+
+void LibRaw::tiff_head(struct tiff_hdr *th, int full)
+{
+  int c, psize = 0;
+  struct tm *t;
+
+  memset(th, 0, sizeof *th);
+  th->t_order = htonl(0x4d4d4949) >> 16;
+  th->magic = 42;
+  th->ifd = 10;
+  th->rat[0] = th->rat[2] = 300;
+  th->rat[1] = th->rat[3] = 1;
+  FORC(6) th->rat[4 + c] = 1000000;
+  th->rat[4] *= shutter;
+  th->rat[6] *= aperture;
+  th->rat[8] *= focal_len;
+  strncpy(th->t_desc, desc, 512);
+  strncpy(th->t_make, make, 64);
+  strncpy(th->t_model, model, 64);
+  strcpy(th->soft, "dcraw v" DCRAW_VERSION);
+  t = localtime(&timestamp);
+  sprintf(th->date, "%04d:%02d:%02d %02d:%02d:%02d", t->tm_year + 1900,
+          t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
+  strncpy(th->t_artist, artist, 64);
+  if (full)
+  {
+    tiff_set(th, &th->ntag, 254, 4, 1, 0);
+    tiff_set(th, &th->ntag, 256, 4, 1, width);
+    tiff_set(th, &th->ntag, 257, 4, 1, height);
+    tiff_set(th, &th->ntag, 258, 3, colors, output_bps);
+    if (colors > 2)
+      th->tag[th->ntag - 1].val.i = TOFF(th->bps);
+    FORC4 th->bps[c] = output_bps;
+    tiff_set(th, &th->ntag, 259, 3, 1, 1);
+    tiff_set(th, &th->ntag, 262, 3, 1, 1 + (colors > 1));
+  }
+  tiff_set(th, &th->ntag, 270, 2, 512, TOFF(th->t_desc));
+  tiff_set(th, &th->ntag, 271, 2, 64, TOFF(th->t_make));
+  tiff_set(th, &th->ntag, 272, 2, 64, TOFF(th->t_model));
+  if (full)
+  {
+    if (oprof)
+      psize = ntohl(oprof[0]);
+    tiff_set(th, &th->ntag, 273, 4, 1, sizeof *th + psize);
+    tiff_set(th, &th->ntag, 277, 3, 1, colors);
+    tiff_set(th, &th->ntag, 278, 4, 1, height);
+    tiff_set(th, &th->ntag, 279, 4, 1,
+             height * width * colors * output_bps / 8);
+  }
+  else
+    tiff_set(th, &th->ntag, 274, 3, 1, "12435867"[flip] - '0');
+  tiff_set(th, &th->ntag, 282, 5, 1, TOFF(th->rat[0]));
+  tiff_set(th, &th->ntag, 283, 5, 1, TOFF(th->rat[2]));
+  tiff_set(th, &th->ntag, 284, 3, 1, 1);
+  tiff_set(th, &th->ntag, 296, 3, 1, 2);
+  tiff_set(th, &th->ntag, 305, 2, 32, TOFF(th->soft));
+  tiff_set(th, &th->ntag, 306, 2, 20, TOFF(th->date));
+  tiff_set(th, &th->ntag, 315, 2, 64, TOFF(th->t_artist));
+  tiff_set(th, &th->ntag, 34665, 4, 1, TOFF(th->nexif));
+  if (psize)
+    tiff_set(th, &th->ntag, 34675, 7, psize, sizeof *th);
+  tiff_set(th, &th->nexif, 33434, 5, 1, TOFF(th->rat[4]));
+  tiff_set(th, &th->nexif, 33437, 5, 1, TOFF(th->rat[6]));
+  tiff_set(th, &th->nexif, 34855, 3, 1, iso_speed);
+  tiff_set(th, &th->nexif, 37386, 5, 1, TOFF(th->rat[8]));
+  if (gpsdata[1])
+  {
+    uchar latref[4] = { (uchar)(gpsdata[29]),0,0,0 },
+          lonref[4] = { (uchar)(gpsdata[30]),0,0,0 };
+    tiff_set(th, &th->ntag, 34853, 4, 1, TOFF(th->ngps));
+    tiff_set(th, &th->ngps, 0, 1, 4, 0x202);
+    tiff_set(th, &th->ngps, 1, 2, 2, TOFF(latref));
+    tiff_set(th, &th->ngps, 2, 5, 3, TOFF(th->gps[0]));
+    tiff_set(th, &th->ngps, 3, 2, 2, TOFF(lonref));
+    tiff_set(th, &th->ngps, 4, 5, 3, TOFF(th->gps[6]));
+    tiff_set(th, &th->ngps, 5, 1, 1, gpsdata[31]);
+    tiff_set(th, &th->ngps, 6, 5, 1, TOFF(th->gps[18]));
+    tiff_set(th, &th->ngps, 7, 5, 3, TOFF(th->gps[12]));
+    tiff_set(th, &th->ngps, 18, 2, 12, TOFF(th->gps[20]));
+    tiff_set(th, &th->ngps, 29, 2, 12, TOFF(th->gps[23]));
+    memcpy(th->gps, gpsdata, sizeof th->gps);
+  }
+}
+
+void LibRaw::jpeg_thumb_writer(FILE *tfp, char *t_humb, int t_humb_length)
+{
+  ushort exif[5];
+  struct tiff_hdr th;
+  fputc(0xff, tfp);
+  fputc(0xd8, tfp);
+  if (strcmp(t_humb + 6, "Exif"))
+  {
+    memcpy(exif, "\xff\xe1  Exif\0\0", 10);
+    exif[1] = htons(8 + sizeof th);
+    fwrite(exif, 1, sizeof exif, tfp);
+    tiff_head(&th, 0);
+    fwrite(&th, 1, sizeof th, tfp);
+  }
+  fwrite(t_humb + 2, 1, t_humb_length - 2, tfp);
+}
+void LibRaw::write_ppm_tiff()
+{
+  struct tiff_hdr th;
+  uchar *ppm;
+  ushort *ppm2;
+  int c, row, col, soff, rstep, cstep;
+  int perc, val, total, t_white = 0x2000;
+
+  perc = width * height * auto_bright_thr;
+
+  if (fuji_width)
+    perc /= 2;
+  if (!((highlight & ~2) || no_auto_bright))
+    for (t_white = c = 0; c < colors; c++)
+    {
+      for (val = 0x2000, total = 0; --val > 32;)
+        if ((total += histogram[c][val]) > perc)
+          break;
+      if (t_white < val)
+        t_white = val;
+    }
+  gamma_curve(gamm[0], gamm[1], 2, (t_white << 3) / bright);
+  iheight = height;
+  iwidth = width;
+  if (flip & 4)
+    SWAP(height, width);
+  ppm = (uchar *)calloc(width, colors * output_bps / 8);
+  ppm2 = (ushort *)ppm;
+  merror(ppm, "write_ppm_tiff()");
+  if (output_tiff)
+  {
+    tiff_head(&th, 1);
+    fwrite(&th, sizeof th, 1, ofp);
+    if (oprof)
+      fwrite(oprof, ntohl(oprof[0]), 1, ofp);
+  }
+  else if (colors > 3)
+    fprintf(
+        ofp,
+        "P7\nWIDTH %d\nHEIGHT %d\nDEPTH %d\nMAXVAL %d\nTUPLTYPE %s\nENDHDR\n",
+        width, height, colors, (1 << output_bps) - 1, cdesc);
+  else
+    fprintf(ofp, "P%d\n%d %d\n%d\n", colors / 2 + 5, width, height,
+            (1 << output_bps) - 1);
+  soff = flip_index(0, 0);
+  cstep = flip_index(0, 1) - soff;
+  rstep = flip_index(1, 0) - flip_index(0, width);
+  for (row = 0; row < height; row++, soff += rstep)
+  {
+    for (col = 0; col < width; col++, soff += cstep)
+      if (output_bps == 8)
+        FORCC ppm[col * colors + c] = curve[image[soff][c]] >> 8;
+      else
+        FORCC ppm2[col * colors + c] = curve[image[soff][c]];
+    if (output_bps == 16 && !output_tiff && htons(0x55aa) != 0x55aa)
+      swab((char *)ppm2, (char *)ppm2, width * colors * 2);
+    fwrite(ppm, colors * output_bps / 8, width, ofp);
+  }
+  free(ppm);
+}
+void LibRaw::ppm_thumb()
+{
+  char *thumb;
+  thumb_length = thumb_width * thumb_height * 3;
+  thumb = (char *)malloc(thumb_length);
+  merror(thumb, "ppm_thumb()");
+  fprintf(ofp, "P6\n%d %d\n255\n", thumb_width, thumb_height);
+  fread(thumb, 1, thumb_length, ifp);
+  fwrite(thumb, 1, thumb_length, ofp);
+  free(thumb);
+}
+
+void LibRaw::ppm16_thumb()
+{
+  unsigned i;
+  char *thumb;
+  thumb_length = thumb_width * thumb_height * 3;
+  thumb = (char *)calloc(thumb_length, 2);
+  merror(thumb, "ppm16_thumb()");
+  read_shorts((ushort *)thumb, thumb_length);
+  for (i = 0; i < thumb_length; i++)
+    thumb[i] = ((ushort *)thumb)[i] >> 8;
+  fprintf(ofp, "P6\n%d %d\n255\n", thumb_width, thumb_height);
+  fwrite(thumb, 1, thumb_length, ofp);
+  free(thumb);
+}
+
+void LibRaw::layer_thumb()
+{
+  unsigned int i;
+  int c;
+  char *thumb, map[][4] = {"012", "102"};
+
+  colors = thumb_misc >> 5 & 7;
+  thumb_length = thumb_width * thumb_height;
+  thumb = (char *)calloc(colors, thumb_length);
+  merror(thumb, "layer_thumb()");
+  fprintf(ofp, "P%d\n%d %d\n255\n", 5 + (colors >> 1), thumb_width,
+          thumb_height);
+  fread(thumb, thumb_length, colors, ifp);
+  for (i = 0; i < thumb_length; i++)
+    FORCC putc(thumb[i + thumb_length * (map[thumb_misc >> 8][c] - '0')], ofp);
+  free(thumb);
+}
+
+void LibRaw::rollei_thumb()
+{
+  unsigned i;
+  ushort *thumb;
+
+  thumb_length = thumb_width * thumb_height;
+  thumb = (ushort *)calloc(thumb_length, 2);
+  merror(thumb, "rollei_thumb()");
+  fprintf(ofp, "P6\n%d %d\n255\n", thumb_width, thumb_height);
+  read_shorts(thumb, thumb_length);
+  for (i = 0; i < thumb_length; i++)
+  {
+    putc(thumb[i] << 3, ofp);
+    putc(thumb[i] >> 5 << 2, ofp);
+    putc(thumb[i] >> 11 << 3, ofp);
+  }
+  free(thumb);
+}
+
+void LibRaw::jpeg_thumb()
+{
+  char *thumb;
+
+  thumb = (char *)malloc(thumb_length);
+  merror(thumb, "jpeg_thumb()");
+  fread(thumb, 1, thumb_length, ifp);
+  jpeg_thumb_writer(ofp, thumb, thumb_length);
+  free(thumb);
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/write/tiff_writer.cpp libkdcraw/libkdcraw/libraw/src/write/tiff_writer.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/write/tiff_writer.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/write/tiff_writer.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,68 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ LibRaw uses code from dcraw.c -- Dave Coffin's raw photo decoder,
+ dcraw.c is copyright 1997-2018 by Dave Coffin, dcoffin a cybercom o net.
+ LibRaw do not use RESTRICTED code from dcraw.c
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+#include "../../internal/libraw_cxx_defs.h"
+
+int LibRaw::dcraw_ppm_tiff_writer(const char *filename)
+{
+  CHECK_ORDER_LOW(LIBRAW_PROGRESS_LOAD_RAW);
+
+  if (!imgdata.image)
+    return LIBRAW_OUT_OF_ORDER_CALL;
+
+  if (!filename)
+    return ENOENT;
+  FILE *f = NULL;
+  if (!strcmp(filename, "-"))
+  {
+#ifdef LIBRAW_WIN32_CALLS
+    _setmode(_fileno(stdout), _O_BINARY);
+#endif
+    f = stdout;
+  }
+  else
+    f = fopen(filename, "wb");
+
+  if (!f)
+    return errno;
+
+  try
+  {
+    if (!libraw_internal_data.output_data.histogram)
+    {
+      libraw_internal_data.output_data.histogram =
+          (int(*)[LIBRAW_HISTOGRAM_SIZE])malloc(
+              sizeof(*libraw_internal_data.output_data.histogram) * 4);
+      merror(libraw_internal_data.output_data.histogram,
+             "LibRaw::dcraw_ppm_tiff_writer()");
+    }
+    libraw_internal_data.internal_data.output = f;
+    write_ppm_tiff();
+    SET_PROC_FLAG(LIBRAW_PROGRESS_FLIP);
+    libraw_internal_data.internal_data.output = NULL;
+    if (strcmp(filename, "-"))
+      fclose(f);
+    return 0;
+  }
+  catch (LibRaw_exceptions err)
+  {
+    if (strcmp(filename, "-"))
+      fclose(f);
+    EXCEPTION_HANDLER(err);
+  }
+}
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/write/write_ph.cpp libkdcraw/libkdcraw/libraw/src/write/write_ph.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/write/write_ph.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/write/write_ph.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,37 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+ Placehoder functions to build LibRaw w/o postprocessing
+ and preprocessing calls
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+
+ */
+
+
+#include "../../internal/dcraw_defs.h"
+int LibRaw::flip_index(int row, int col)
+{
+  if (flip & 4)
+    SWAP(row, col);
+  if (flip & 2)
+    row = iheight - 1 - row;
+  if (flip & 1)
+    col = iwidth - 1 - col;
+  return row * iwidth + col;
+}
+
+void LibRaw::ppm_thumb(){}
+void LibRaw::jpeg_thumb(){}
+void LibRaw::rollei_thumb(){}
+void LibRaw::jpeg_thumb_writer(FILE *tfp, char *t_humb, int t_humb_length){}
+void LibRaw::write_ppm_tiff(){}
+void LibRaw::ppm16_thumb(){}
+void LibRaw::layer_thumb(){}
\ No newline at end of file
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/x3f/x3f_parse_process.cpp libkdcraw/libkdcraw/libraw/src/x3f/x3f_parse_process.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/x3f/x3f_parse_process.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/x3f/x3f_parse_process.cpp	2022-11-07 07:46:31.738795008 +0300
@@ -0,0 +1,725 @@
+/* -*- C++ -*-
+ * Copyright 2019-2020 LibRaw LLC (info@libraw.org)
+ *
+
+ LibRaw is free software; you can redistribute it and/or modify
+ it under the terms of the one of two licenses as you choose:
+
+1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
+   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
+
+2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
+ */
+
+/* Library for accessing X3F Files
+----------------------------------------------------------------
+BSD-style License
+----------------------------------------------------------------
+
+* Copyright (c) 2010, Roland Karlsson (roland@proxel.se)
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above copyright
+*       notice, this list of conditions and the following disclaimer in the
+*       documentation and/or other materials provided with the distribution.
+*     * Neither the name of the organization nor the
+*       names of its contributors may be used to endorse or promote products
+*       derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY ROLAND KARLSSON ''AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+* DISCLAIMED. IN NO EVENT SHALL ROLAND KARLSSON BE LIABLE FOR ANY
+* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#ifdef USE_X3FTOOLS
+
+#include "../../internal/libraw_cxx_defs.h"
+
+#if defined __sun && defined DS
+#undef DS
+#endif
+#ifdef ID
+#undef ID /* used in x3f utils */
+#endif
+
+#include "../../internal/x3f_tools.h"
+
+#define Sigma_X3F 22
+
+void x3f_clear(void *p) { x3f_delete((x3f_t *)p); }
+
+static void utf2char(utf16_t *str, char *buffer, unsigned bufsz)
+{
+  if (bufsz < 1)
+    return;
+  buffer[bufsz - 1] = 0;
+  char *b = buffer;
+
+  while (*str != 0x00 && --bufsz > 0)
+  {
+    char *chr = (char *)str;
+    *b++ = *chr;
+    str++;
+  }
+  *b = 0;
+}
+
+static void *lr_memmem(const void *l, size_t l_len, const void *s, size_t s_len)
+{
+  char *cur, *last;
+  const char *cl = (const char *)l;
+  const char *cs = (const char *)s;
+
+  /* we need something to compare */
+  if (l_len == 0 || s_len == 0)
+    return NULL;
+
+  /* "s" must be smaller or equal to "l" */
+  if (l_len < s_len)
+    return NULL;
+
+  /* special case where s_len == 1 */
+  if (s_len == 1)
+    return (void *)memchr(l, (int)*cs, l_len);
+
+  /* the last position where its possible to find "s" in "l" */
+  last = (char *)cl + l_len - s_len;
+
+  for (cur = (char *)cl; cur <= last; cur++)
+    if (cur[0] == cs[0] && memcmp(cur, cs, s_len) == 0)
+      return cur;
+  return NULL;
+}
+
+void LibRaw::parse_x3f()
+{
+  x3f_t *x3f = x3f_new_from_file(libraw_internal_data.internal_data.input);
+  if (!x3f)
+    return;
+  _x3f_data = x3f;
+
+  x3f_header_t *H = NULL;
+
+  H = &x3f->header;
+  // Parse RAW size from RAW section
+  x3f_directory_entry_t *DE = x3f_get_raw(x3f);
+  if (!DE)
+    return;
+  imgdata.sizes.flip = H->rotation;
+  x3f_directory_entry_header_t *DEH = &DE->header;
+  x3f_image_data_t *ID = &DEH->data_subsection.image_data;
+  imgdata.sizes.raw_width = ID->columns;
+  imgdata.sizes.raw_height = ID->rows;
+  // Parse other params from property section
+
+  DE = x3f_get_prop(x3f);
+  if ((x3f_load_data(x3f, DE) == X3F_OK))
+  {
+    // Parse property list
+    DEH = &DE->header;
+    x3f_property_list_t *PL = &DEH->data_subsection.property_list;
+    utf16_t *datap = (utf16_t *)PL->data;
+    uint32_t maxitems = PL->data_size / sizeof(utf16_t);
+    if (PL->property_table.size != 0)
+    {
+      int i;
+      x3f_property_t *P = PL->property_table.element;
+      for (i = 0; i < PL->num_properties; i++)
+      {
+        char name[100], value[100];
+        int noffset = (P[i].name - datap);
+        int voffset = (P[i].value - datap);
+        if (noffset < 0 || noffset > maxitems || voffset < 0 ||
+            voffset > maxitems)
+          throw LIBRAW_EXCEPTION_IO_CORRUPT;
+        int maxnsize = maxitems - (P[i].name - datap);
+        int maxvsize = maxitems - (P[i].value - datap);
+        utf2char(P[i].name, name, MIN(maxnsize, sizeof(name)));
+        utf2char(P[i].value, value, MIN(maxvsize, sizeof(value)));
+        if (!strcmp(name, "ISO"))
+          imgdata.other.iso_speed = atoi(value);
+        if (!strcmp(name, "CAMMANUF"))
+          strcpy(imgdata.idata.make, value);
+        if (!strcmp(name, "CAMMODEL"))
+          strcpy(imgdata.idata.model, value);
+        if (!strcmp(name, "CAMSERIAL"))
+          strcpy(imgdata.shootinginfo.BodySerial, value);
+        if (!strcmp(name, "WB_DESC"))
+          strcpy(imgdata.color.model2, value);
+        if (!strcmp(name, "TIME"))
+          imgdata.other.timestamp = atoi(value);
+        if (!strcmp(name, "SHUTTER"))
+          imgdata.other.shutter = atof(value);
+        if (!strcmp(name, "APERTURE"))
+          imgdata.other.aperture = atof(value);
+        if (!strcmp(name, "FLENGTH"))
+          imgdata.other.focal_len = atof(value);
+        if (!strcmp(name, "FLEQ35MM"))
+          imgdata.lens.makernotes.FocalLengthIn35mmFormat = atof(value);
+        if (!strcmp(name, "IMAGERTEMP"))
+          imgdata.makernotes.common.SensorTemperature = atof(value);
+        if (!strcmp(name, "LENSARANGE"))
+        {
+          char *sp;
+          imgdata.lens.makernotes.MaxAp4CurFocal =
+              imgdata.lens.makernotes.MinAp4CurFocal = atof(value);
+          sp = strrchr(value, ' ');
+          if (sp)
+          {
+            imgdata.lens.makernotes.MinAp4CurFocal = atof(sp);
+            if (imgdata.lens.makernotes.MaxAp4CurFocal >
+                imgdata.lens.makernotes.MinAp4CurFocal)
+              my_swap(float, imgdata.lens.makernotes.MaxAp4CurFocal,
+                      imgdata.lens.makernotes.MinAp4CurFocal);
+          }
+        }
+        if (!strcmp(name, "LENSFRANGE"))
+        {
+          char *sp;
+          imgdata.lens.makernotes.MinFocal = imgdata.lens.makernotes.MaxFocal =
+              atof(value);
+          sp = strrchr(value, ' ');
+          if (sp)
+          {
+            imgdata.lens.makernotes.MaxFocal = atof(sp);
+            if ((imgdata.lens.makernotes.MaxFocal + 0.17f) <
+                imgdata.lens.makernotes.MinFocal)
+              my_swap(float, imgdata.lens.makernotes.MaxFocal,
+                      imgdata.lens.makernotes.MinFocal);
+          }
+        }
+        if (!strcmp(name, "LENSMODEL"))
+        {
+          char *sp;
+          imgdata.lens.makernotes.LensID =
+              strtol(value, &sp, 16); // atoi(value);
+          if (imgdata.lens.makernotes.LensID)
+            imgdata.lens.makernotes.LensMount = Sigma_X3F;
+        }
+      }
+      imgdata.idata.raw_count = 1;
+      load_raw = &LibRaw::x3f_load_raw;
+      imgdata.sizes.raw_pitch = imgdata.sizes.raw_width * 6;
+      imgdata.idata.is_foveon = 1;
+      libraw_internal_data.internal_output_params.raw_color =
+          1;                          // Force adobe coeff
+      imgdata.color.maximum = 0x3fff; // To be reset by color table
+      libraw_internal_data.unpacker_data.order = 0x4949;
+    }
+  }
+  else
+  {
+    // No property list
+    if (imgdata.sizes.raw_width == 5888 || imgdata.sizes.raw_width == 2944 ||
+        imgdata.sizes.raw_width == 6656 || imgdata.sizes.raw_width == 3328 ||
+        imgdata.sizes.raw_width == 5504 ||
+        imgdata.sizes.raw_width == 2752) // Quattro
+    {
+      imgdata.idata.raw_count = 1;
+      load_raw = &LibRaw::x3f_load_raw;
+      imgdata.sizes.raw_pitch = imgdata.sizes.raw_width * 6;
+      imgdata.idata.is_foveon = 1;
+      libraw_internal_data.internal_output_params.raw_color =
+          1; // Force adobe coeff
+      libraw_internal_data.unpacker_data.order = 0x4949;
+      strcpy(imgdata.idata.make, "SIGMA");
+#if 1
+      // Try to find model number in first 2048 bytes;
+      int pos = libraw_internal_data.internal_data.input->tell();
+      libraw_internal_data.internal_data.input->seek(0, SEEK_SET);
+      unsigned char buf[2048];
+      libraw_internal_data.internal_data.input->read(buf, 2048, 1);
+      libraw_internal_data.internal_data.input->seek(pos, SEEK_SET);
+      unsigned char *fnd = (unsigned char *)lr_memmem(buf, 2048, "SIGMA dp", 8);
+      unsigned char *fndsd =
+          (unsigned char *)lr_memmem(buf, 2048, "sd Quatt", 8);
+      if (fnd)
+      {
+        unsigned char *nm = fnd + 8;
+        snprintf(imgdata.idata.model, 64, "dp%c Quattro",
+                 *nm <= '9' && *nm >= '0' ? *nm : '2');
+      }
+      else if (fndsd)
+      {
+        snprintf(imgdata.idata.model, 64, "%s", fndsd);
+      }
+      else
+#endif
+          if (imgdata.sizes.raw_width == 6656 ||
+              imgdata.sizes.raw_width == 3328)
+        strcpy(imgdata.idata.model, "sd Quattro H");
+      else
+        strcpy(imgdata.idata.model, "dp2 Quattro");
+    }
+    // else
+  }
+  // Try to get thumbnail data
+  LibRaw_thumbnail_formats format = LIBRAW_THUMBNAIL_UNKNOWN;
+  if ((DE = x3f_get_thumb_jpeg(x3f)))
+  {
+    format = LIBRAW_THUMBNAIL_JPEG;
+  }
+  else if ((DE = x3f_get_thumb_plain(x3f)))
+  {
+    format = LIBRAW_THUMBNAIL_BITMAP;
+  }
+  if (DE)
+  {
+    x3f_directory_entry_header_t *DEH = &DE->header;
+    x3f_image_data_t *ID = &DEH->data_subsection.image_data;
+    imgdata.thumbnail.twidth = ID->columns;
+    imgdata.thumbnail.theight = ID->rows;
+    imgdata.thumbnail.tcolors = 3;
+    imgdata.thumbnail.tformat = format;
+    libraw_internal_data.internal_data.toffset = DE->input.offset;
+    write_thumb = &LibRaw::x3f_thumb_loader;
+  }
+  DE = x3f_get_camf(x3f);
+  if (DE && DE->input.size > 28)
+  {
+    libraw_internal_data.unpacker_data.meta_offset = DE->input.offset + 8;
+    libraw_internal_data.unpacker_data.meta_length = DE->input.size - 28;
+  }
+}
+
+INT64 LibRaw::x3f_thumb_size()
+{
+  try
+  {
+    x3f_t *x3f = (x3f_t *)_x3f_data;
+    if (!x3f)
+      return -1; // No data pointer set
+    x3f_directory_entry_t *DE = x3f_get_thumb_jpeg(x3f);
+    if (!DE)
+      DE = x3f_get_thumb_plain(x3f);
+    if (!DE)
+      return -1;
+    int64_t p = x3f_load_data_size(x3f, DE);
+    if (p < 0 || p > 0xffffffff)
+      return -1;
+    return p;
+  }
+  catch (...)
+  {
+    return -1;
+  }
+}
+
+void LibRaw::x3f_thumb_loader()
+{
+  try
+  {
+    x3f_t *x3f = (x3f_t *)_x3f_data;
+    if (!x3f)
+      return; // No data pointer set
+    x3f_directory_entry_t *DE = x3f_get_thumb_jpeg(x3f);
+    if (!DE)
+      DE = x3f_get_thumb_plain(x3f);
+    if (!DE)
+      return;
+    if (X3F_OK != x3f_load_data(x3f, DE))
+      throw LIBRAW_EXCEPTION_IO_CORRUPT;
+    x3f_directory_entry_header_t *DEH = &DE->header;
+    x3f_image_data_t *ID = &DEH->data_subsection.image_data;
+    imgdata.thumbnail.twidth = ID->columns;
+    imgdata.thumbnail.theight = ID->rows;
+    imgdata.thumbnail.tcolors = 3;
+    if (imgdata.thumbnail.tformat == LIBRAW_THUMBNAIL_JPEG)
+    {
+      imgdata.thumbnail.thumb = (char *)malloc(ID->data_size);
+      merror(imgdata.thumbnail.thumb, "LibRaw::x3f_thumb_loader()");
+      memmove(imgdata.thumbnail.thumb, ID->data, ID->data_size);
+      imgdata.thumbnail.tlength = ID->data_size;
+    }
+    else if (imgdata.thumbnail.tformat == LIBRAW_THUMBNAIL_BITMAP)
+    {
+      imgdata.thumbnail.tlength = ID->columns * ID->rows * 3;
+      imgdata.thumbnail.thumb = (char *)malloc(ID->columns * ID->rows * 3);
+      merror(imgdata.thumbnail.thumb, "LibRaw::x3f_thumb_loader()");
+      char *src0 = (char *)ID->data;
+      for (int row = 0; row < ID->rows; row++)
+      {
+        int offset = row * ID->row_stride;
+        if (offset + ID->columns * 3 > ID->data_size)
+          break;
+        char *dest = &imgdata.thumbnail.thumb[row * ID->columns * 3];
+        char *src = &src0[offset];
+        memmove(dest, src, ID->columns * 3);
+      }
+    }
+  }
+  catch (...)
+  {
+    // do nothing
+  }
+}
+
+static inline uint32_t _clampbits(int x, uint32_t n)
+{
+  uint32_t _y_temp;
+  if ((_y_temp = x >> n))
+    x = ~_y_temp >> (32 - n);
+  return x;
+}
+
+void LibRaw::x3f_dpq_interpolate_rg()
+{
+  int w = imgdata.sizes.raw_width / 2;
+  int h = imgdata.sizes.raw_height / 2;
+  unsigned short *image = (ushort *)imgdata.rawdata.color3_image;
+
+  for (int color = 0; color < 2; color++)
+  {
+    for (int y = 2; y < (h - 2); y++)
+    {
+      uint16_t *row0 =
+          &image[imgdata.sizes.raw_width * 3 * (y * 2) + color]; // dst[1]
+      uint16_t row0_3 = row0[3];
+      uint16_t *row1 =
+          &image[imgdata.sizes.raw_width * 3 * (y * 2 + 1) + color]; // dst1[1]
+      uint16_t row1_3 = row1[3];
+      for (int x = 2; x < (w - 2); x++)
+      {
+        row1[0] = row1[3] = row0[3] = row0[0];
+        row0 += 6;
+        row1 += 6;
+      }
+    }
+  }
+}
+
+#ifdef _ABS
+#undef _ABS
+#endif
+#define _ABS(a) ((a) < 0 ? -(a) : (a))
+
+#undef CLIP
+#define CLIP(value, high) ((value) > (high) ? (high) : (value))
+
+void LibRaw::x3f_dpq_interpolate_af(int xstep, int ystep, int scale)
+{
+  unsigned short *image = (ushort *)imgdata.rawdata.color3_image;
+  unsigned int rowpitch =
+      imgdata.rawdata.sizes.raw_pitch / 2; // in 16-bit words
+                                           // Interpolate single pixel
+  for (int y = 0;
+       y < imgdata.rawdata.sizes.height + imgdata.rawdata.sizes.top_margin;
+       y += ystep)
+  {
+    if (y < imgdata.rawdata.sizes.top_margin)
+      continue;
+    if (y < scale)
+      continue;
+    if (y > imgdata.rawdata.sizes.raw_height - scale)
+      break;
+    uint16_t *row0 = &image[imgdata.sizes.raw_width * 3 * y]; // Ðаша Ñтрока
+    uint16_t *row_minus =
+        &image[imgdata.sizes.raw_width * 3 * (y - scale)]; // Строка выше
+    uint16_t *row_plus =
+        &image[imgdata.sizes.raw_width * 3 * (y + scale)]; // Строка ниже
+    for (int x = 0;
+         x < imgdata.rawdata.sizes.width + imgdata.rawdata.sizes.left_margin;
+         x += xstep)
+    {
+      if (x < imgdata.rawdata.sizes.left_margin)
+        continue;
+      if (x < scale)
+        continue;
+      if (x > imgdata.rawdata.sizes.raw_width - scale)
+        break;
+      uint16_t *pixel0 = &row0[x * 3];
+      uint16_t *pixel_top = &row_minus[x * 3];
+      uint16_t *pixel_bottom = &row_plus[x * 3];
+      uint16_t *pixel_left = &row0[(x - scale) * 3];
+      uint16_t *pixel_right = &row0[(x + scale) * 3];
+      uint16_t *pixf = pixel_top;
+      if (_ABS(pixf[2] - pixel0[2]) > _ABS(pixel_bottom[2] - pixel0[2]))
+        pixf = pixel_bottom;
+      if (_ABS(pixf[2] - pixel0[2]) > _ABS(pixel_left[2] - pixel0[2]))
+        pixf = pixel_left;
+      if (_ABS(pixf[2] - pixel0[2]) > _ABS(pixel_right[2] - pixel0[2]))
+        pixf = pixel_right;
+      int blocal = pixel0[2], bnear = pixf[2];
+      if (blocal < imgdata.color.black + 16 || bnear < imgdata.color.black + 16)
+      {
+        if (pixel0[0] < imgdata.color.black)
+          pixel0[0] = imgdata.color.black;
+        if (pixel0[1] < imgdata.color.black)
+          pixel0[1] = imgdata.color.black;
+        pixel0[0] = CLIP(
+            (pixel0[0] - imgdata.color.black) * 4 + imgdata.color.black, 16383);
+        pixel0[1] = CLIP(
+            (pixel0[1] - imgdata.color.black) * 4 + imgdata.color.black, 16383);
+      }
+      else
+      {
+        float multip = float(bnear - imgdata.color.black) /
+                       float(blocal - imgdata.color.black);
+        if (pixel0[0] < imgdata.color.black)
+          pixel0[0] = imgdata.color.black;
+        if (pixel0[1] < imgdata.color.black)
+          pixel0[1] = imgdata.color.black;
+        float pixf0 = pixf[0];
+        if (pixf0 < imgdata.color.black)
+          pixf0 = imgdata.color.black;
+        float pixf1 = pixf[1];
+        if (pixf1 < imgdata.color.black)
+          pixf1 = imgdata.color.black;
+
+        pixel0[0] = CLIP(
+            ((float(pixf0 - imgdata.color.black) * multip +
+              imgdata.color.black) +
+             ((pixel0[0] - imgdata.color.black) * 3.75 + imgdata.color.black)) /
+                2,
+            16383);
+        pixel0[1] = CLIP(
+            ((float(pixf1 - imgdata.color.black) * multip +
+              imgdata.color.black) +
+             ((pixel0[1] - imgdata.color.black) * 3.75 + imgdata.color.black)) /
+                2,
+            16383);
+        // pixel0[1] = float(pixf[1]-imgdata.color.black)*multip +
+        // imgdata.color.black;
+      }
+    }
+  }
+}
+
+void LibRaw::x3f_dpq_interpolate_af_sd(int xstart, int ystart, int xend,
+                                       int yend, int xstep, int ystep,
+                                       int scale)
+{
+  unsigned short *image = (ushort *)imgdata.rawdata.color3_image;
+  unsigned int rowpitch =
+      imgdata.rawdata.sizes.raw_pitch / 2; // in 16-bit words
+  // Interpolate single pixel
+  for (int y = ystart; y <= yend && y < imgdata.rawdata.sizes.height +
+                                           imgdata.rawdata.sizes.top_margin;
+       y += ystep)
+  {
+    uint16_t *row0 = &image[imgdata.sizes.raw_width * 3 * y]; // Ðаша Ñтрока
+    uint16_t *row1 =
+        &image[imgdata.sizes.raw_width * 3 * (y + 1)]; // Ð¡Ð»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ Ñтрока
+    uint16_t *row_minus =
+        &image[imgdata.sizes.raw_width * 3 * (y - scale)]; // Строка выше
+    uint16_t *row_plus =
+        &image[imgdata.sizes.raw_width * 3 *
+               (y + scale)]; // Строка ниже AF-point (scale=2 -> ниже row1
+    uint16_t *row_minus1 = &image[imgdata.sizes.raw_width * 3 * (y - 1)];
+    for (int x = xstart; x < xend && x < imgdata.rawdata.sizes.width +
+                                             imgdata.rawdata.sizes.left_margin;
+         x += xstep)
+    {
+      uint16_t *pixel00 = &row0[x * 3]; // Current pixel
+      float sumR = 0.f, sumG = 0.f;
+      float cnt = 0.f;
+      for (int xx = -scale; xx <= scale; xx += scale)
+      {
+        sumR += row_minus[(x + xx) * 3];
+        sumR += row_plus[(x + xx) * 3];
+        sumG += row_minus[(x + xx) * 3 + 1];
+        sumG += row_plus[(x + xx) * 3 + 1];
+        cnt += 1.f;
+        if (xx)
+        {
+          cnt += 1.f;
+          sumR += row0[(x + xx) * 3];
+          sumG += row0[(x + xx) * 3 + 1];
+        }
+      }
+      pixel00[0] = sumR / 8.f;
+      pixel00[1] = sumG / 8.f;
+
+      if (scale == 2)
+      {
+        uint16_t *pixel0B = &row0[x * 3 + 3]; // right pixel
+        uint16_t *pixel1B = &row1[x * 3 + 3]; // right pixel
+        float sumG0 = 0, sumG1 = 0.f;
+        float cnt = 0.f;
+        for (int xx = -scale; xx <= scale; xx += scale)
+        {
+          sumG0 += row_minus1[(x + xx) * 3 + 2];
+          sumG1 += row_plus[(x + xx) * 3 + 2];
+          cnt += 1.f;
+          if (xx)
+          {
+            sumG0 += row0[(x + xx) * 3 + 2];
+            sumG1 += row1[(x + xx) * 3 + 2];
+            cnt += 1.f;
+          }
+        }
+        pixel0B[2] = sumG0 / cnt;
+        pixel1B[2] = sumG1 / cnt;
+      }
+
+      //			uint16_t* pixel10 = &row1[x*3]; // Pixel below current
+      //			uint16_t* pixel_bottom = &row_plus[x*3];
+    }
+  }
+}
+
+void LibRaw::x3f_load_raw()
+{
+  // already in try/catch
+  int raise_error = 0;
+  x3f_t *x3f = (x3f_t *)_x3f_data;
+  if (!x3f)
+    return; // No data pointer set
+  if (X3F_OK == x3f_load_data(x3f, x3f_get_raw(x3f)))
+  {
+    x3f_directory_entry_t *DE = x3f_get_raw(x3f);
+    x3f_directory_entry_header_t *DEH = &DE->header;
+    x3f_image_data_t *ID = &DEH->data_subsection.image_data;
+    if (!ID)
+      throw LIBRAW_EXCEPTION_IO_CORRUPT;
+    x3f_quattro_t *Q = ID->quattro;
+    x3f_huffman_t *HUF = ID->huffman;
+    x3f_true_t *TRU = ID->tru;
+    uint16_t *data = NULL;
+    if (ID->rows != S.raw_height || ID->columns != S.raw_width)
+    {
+      raise_error = 1;
+      goto end;
+    }
+    if (HUF != NULL)
+      data = HUF->x3rgb16.data;
+    if (TRU != NULL)
+      data = TRU->x3rgb16.data;
+    if (data == NULL)
+    {
+      raise_error = 1;
+      goto end;
+    }
+
+    size_t datasize = S.raw_height * S.raw_width * 3 * sizeof(unsigned short);
+    S.raw_pitch = S.raw_width * 3 * sizeof(unsigned short);
+    if (!(imgdata.rawdata.raw_alloc = malloc(datasize)))
+      throw LIBRAW_EXCEPTION_ALLOC;
+
+    imgdata.rawdata.color3_image = (ushort(*)[3])imgdata.rawdata.raw_alloc;
+    // swap R/B channels for known old cameras
+    if (!strcasecmp(P1.make, "Polaroid") && !strcasecmp(P1.model, "x530"))
+    {
+      ushort(*src)[3] = (ushort(*)[3])data;
+      for (int p = 0; p < S.raw_height * S.raw_width; p++)
+      {
+        imgdata.rawdata.color3_image[p][0] = src[p][2];
+        imgdata.rawdata.color3_image[p][1] = src[p][1];
+        imgdata.rawdata.color3_image[p][2] = src[p][0];
+      }
+    }
+    else if (HUF)
+      memmove(imgdata.rawdata.raw_alloc, data, datasize);
+    else if (TRU && (!Q || !Q->quattro_layout))
+      memmove(imgdata.rawdata.raw_alloc, data, datasize);
+    else if (TRU && Q)
+    {
+      // Move quattro data in place
+      // R/B plane
+      for (int prow = 0; prow < TRU->x3rgb16.rows && prow < S.raw_height / 2;
+           prow++)
+      {
+        ushort(*destrow)[3] =
+            (unsigned short(*)[3]) &
+            imgdata.rawdata
+                .color3_image[prow * 2 * S.raw_pitch / 3 / sizeof(ushort)][0];
+        ushort(*srcrow)[3] =
+            (unsigned short(*)[3]) & data[prow * TRU->x3rgb16.row_stride];
+        for (int pcol = 0;
+             pcol < TRU->x3rgb16.columns && pcol < S.raw_width / 2; pcol++)
+        {
+          destrow[pcol * 2][0] = srcrow[pcol][0];
+          destrow[pcol * 2][1] = srcrow[pcol][1];
+        }
+      }
+      for (int row = 0; row < Q->top16.rows && row < S.raw_height; row++)
+      {
+        ushort(*destrow)[3] =
+            (unsigned short(*)[3]) &
+            imgdata.rawdata
+                .color3_image[row * S.raw_pitch / 3 / sizeof(ushort)][0];
+        ushort(*srcrow) =
+            (unsigned short *)&Q->top16.data[row * Q->top16.columns];
+        for (int col = 0; col < Q->top16.columns && col < S.raw_width; col++)
+          destrow[col][2] = srcrow[col];
+      }
+    }
+
+#if 1
+    if (TRU && Q &&
+        (imgdata.params.raw_processing_options &
+         LIBRAW_PROCESSING_DP2Q_INTERPOLATEAF))
+    {
+      if (imgdata.sizes.raw_width == 5888 &&
+          imgdata.sizes.raw_height == 3672) // dpN Quattro normal
+      {
+        x3f_dpq_interpolate_af(32, 8, 2);
+      }
+      else if (imgdata.sizes.raw_width == 5888 &&
+               imgdata.sizes.raw_height == 3776) // sd Quattro normal raw
+      {
+        x3f_dpq_interpolate_af_sd(216, 464, imgdata.sizes.raw_width - 1, 3312,
+                                  16, 32, 2);
+      }
+      else if (imgdata.sizes.raw_width == 6656 &&
+               imgdata.sizes.raw_height == 4480) // sd Quattro H normal raw
+      {
+        x3f_dpq_interpolate_af_sd(232, 592, imgdata.sizes.raw_width - 1, 3920,
+                                  16, 32, 2);
+      }
+      else if (imgdata.sizes.raw_width == 3328 &&
+               imgdata.sizes.raw_height == 2240) // sd Quattro H half size
+      {
+        x3f_dpq_interpolate_af_sd(116, 296, imgdata.sizes.raw_width - 1, 2200,
+                                  8, 16, 1);
+      }
+      else if (imgdata.sizes.raw_width == 5504 &&
+               imgdata.sizes.raw_height == 3680) // sd Quattro H APS-C raw
+      {
+        x3f_dpq_interpolate_af_sd(8, 192, imgdata.sizes.raw_width - 1, 3185, 16,
+                                  32, 2);
+      }
+      else if (imgdata.sizes.raw_width == 2752 &&
+               imgdata.sizes.raw_height == 1840) // sd Quattro H APS-C half size
+      {
+        x3f_dpq_interpolate_af_sd(4, 96, imgdata.sizes.raw_width - 1, 1800, 8,
+                                  16, 1);
+      }
+      else if (imgdata.sizes.raw_width == 2944 &&
+               imgdata.sizes.raw_height == 1836) // dpN Quattro small raw
+      {
+        x3f_dpq_interpolate_af(16, 4, 1);
+      }
+      else if (imgdata.sizes.raw_width == 2944 &&
+               imgdata.sizes.raw_height == 1888) // sd Quattro small
+      {
+        x3f_dpq_interpolate_af_sd(108, 232, imgdata.sizes.raw_width - 1, 1656,
+                                  8, 16, 1);
+      }
+    }
+#endif
+    if (TRU && Q && Q->quattro_layout &&
+        (imgdata.params.raw_processing_options &
+         LIBRAW_PROCESSING_DP2Q_INTERPOLATERG))
+      x3f_dpq_interpolate_rg();
+  }
+  else
+    raise_error = 1;
+end:
+  if (raise_error)
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+}
+#endif
\ No newline at end of file
diff -x .git -x libkdcraw/libkdcraw/test -uNr libkdcraw-wrk/libkdcraw/libraw/src/x3f/x3f_utils_patched.cpp libkdcraw/libkdcraw/libraw/src/x3f/x3f_utils_patched.cpp
--- libkdcraw-wrk/libkdcraw/libraw/src/x3f/x3f_utils_patched.cpp	1970-01-01 03:00:00.000000000 +0300
+++ libkdcraw/libkdcraw/libraw/src/x3f/x3f_utils_patched.cpp	2022-11-07 07:46:31.742795008 +0300
@@ -0,0 +1,2123 @@
+#ifdef USE_X3FTOOLS
+
+/* Library for accessing X3F Files
+----------------------------------------------------------------
+BSD-style License
+----------------------------------------------------------------
+
+* Copyright (c) 2010, Roland Karlsson (roland@proxel.se)
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above copyright
+*       notice, this list of conditions and the following disclaimer in the
+*       documentation and/or other materials provided with the distribution.
+*     * Neither the name of the organization nor the
+*       names of its contributors may be used to endorse or promote products
+*       derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY ROLAND KARLSSON ''AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+* DISCLAIMED. IN NO EVENT SHALL ROLAND KARLSSON BE LIABLE FOR ANY
+* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#include "../../internal/libraw_cxx_defs.h"
+
+#if defined __sun && defined DS
+#undef DS
+#endif
+#ifdef ID
+#undef ID /* used in x3f utils */
+#endif
+
+#include "../../internal/x3f_tools.h"
+
+/* extern */ int legacy_offset = 0;
+/* extern */ bool_t auto_legacy_offset = 1;
+
+/* --------------------------------------------------------------------- */
+/* Reading and writing - assuming little endian in the file              */
+/* --------------------------------------------------------------------- */
+
+static int x3f_get1(LibRaw_abstract_datastream *f)
+{
+  /* Little endian file */
+  return f->get_char();
+}
+
+static int x3f_sget2(uchar *s) { return s[0] | s[1] << 8; }
+
+static int x3f_get2(LibRaw_abstract_datastream *f)
+{
+  uchar str[2] = {0xff, 0xff};
+  f->read(str, 1, 2);
+  return x3f_sget2(str);
+}
+
+unsigned x3f_sget4(uchar *s)
+{
+  return s[0] | s[1] << 8 | s[2] << 16 | s[3] << 24;
+}
+
+unsigned x3f_get4(LibRaw_abstract_datastream *f)
+{
+  uchar str[4] = {0xff, 0xff, 0xff, 0xff};
+  f->read(str, 1, 4);
+  return x3f_sget4(str);
+}
+
+#define FREE(P)                                                                \
+  do                                                                           \
+  {                                                                            \
+    free(P);                                                                   \
+    (P) = NULL;                                                                \
+  } while (0)
+
+#define PUT_GET_N(_buffer, _size, _file, _func)                                \
+  do                                                                           \
+  {                                                                            \
+    int _left = _size;                                                         \
+    while (_left != 0)                                                         \
+    {                                                                          \
+      int _cur = _file->_func(_buffer, 1, _left);                              \
+      if (_cur == 0)                                                           \
+      {                                                                        \
+        throw LIBRAW_EXCEPTION_IO_CORRUPT;                                     \
+      }                                                                        \
+      _left -= _cur;                                                           \
+    }                                                                          \
+  } while (0)
+
+#define GET1(_v)                                                               \
+  do                                                                           \
+  {                                                                            \
+    (_v) = x3f_get1(I->input.file);                                            \
+  } while (0)
+#define GET2(_v)                                                               \
+  do                                                                           \
+  {                                                                            \
+    (_v) = x3f_get2(I->input.file);                                            \
+  } while (0)
+#define GET4(_v)                                                               \
+  do                                                                           \
+  {                                                                            \
+    (_v) = x3f_get4(I->input.file);                                            \
+  } while (0)
+
+#define GET4F(_v)                                                              \
+  do                                                                           \
+  {                                                                            \
+    union {                                                                    \
+      int32_t i;                                                               \
+      float f;                                                                 \
+    } _tmp;                                                                    \
+    _tmp.i = x3f_get4(I->input.file);                                          \
+    (_v) = _tmp.f;                                                             \
+  } while (0)
+
+#define GETN(_v, _s) PUT_GET_N(_v, _s, I->input.file, read)
+
+#define GET_TABLE(_T, _GETX, _NUM, _TYPE)                                      \
+  do                                                                           \
+  {                                                                            \
+    int _i;                                                                    \
+    (_T).size = (_NUM);                                                        \
+    (_T).element =                                                             \
+        (_TYPE *)realloc((_T).element, (_NUM) * sizeof((_T).element[0]));      \
+    for (_i = 0; _i < (_T).size; _i++)                                         \
+      _GETX((_T).element[_i]);                                                 \
+  } while (0)
+
+#define GET_PROPERTY_TABLE(_T, _NUM)                                           \
+  do                                                                           \
+  {                                                                            \
+    int _i;                                                                    \
+    (_T).size = (_NUM);                                                        \
+    (_T).element = (x3f_property_t *)realloc(                                  \
+        (_T).element, (_NUM) * sizeof((_T).element[0]));                       \
+    for (_i = 0; _i < (_T).size; _i++)                                         \
+    {                                                                          \
+      GET4((_T).element[_i].name_offset);                                      \
+      GET4((_T).element[_i].value_offset);                                     \
+    }                                                                          \
+  } while (0)
+
+#define GET_TRUE_HUFF_TABLE(_T)                                                \
+  do                                                                           \
+  {                                                                            \
+    int _i;                                                                    \
+    (_T).element = NULL;                                                       \
+    for (_i = 0;; _i++)                                                        \
+    {                                                                          \
+      (_T).size = _i + 1;                                                      \
+      (_T).element = (x3f_true_huffman_element_t *)realloc(                    \
+          (_T).element, (_i + 1) * sizeof((_T).element[0]));                   \
+      GET1((_T).element[_i].code_size);                                        \
+      GET1((_T).element[_i].code);                                             \
+      if ((_T).element[_i].code_size == 0)                                     \
+        break;                                                                 \
+    }                                                                          \
+  } while (0)
+
+/* --------------------------------------------------------------------- */
+/* Allocating Huffman tree help data                                   */
+/* --------------------------------------------------------------------- */
+
+static void cleanup_huffman_tree(x3f_hufftree_t *HTP) { free(HTP->nodes); }
+
+static void new_huffman_tree(x3f_hufftree_t *HTP, int bits)
+{
+  int leaves = 1 << bits;
+
+  HTP->free_node_index = 0;
+  HTP->total_node_index = HUF_TREE_MAX_NODES(leaves);
+  HTP->nodes = (x3f_huffnode_t *)calloc(1, HUF_TREE_MAX_NODES(leaves) *
+                                               sizeof(x3f_huffnode_t));
+}
+
+/* --------------------------------------------------------------------- */
+/* Allocating TRUE engine RAW help data                                  */
+/* --------------------------------------------------------------------- */
+
+static void cleanup_true(x3f_true_t **TRUP)
+{
+  x3f_true_t *TRU = *TRUP;
+
+  if (TRU == NULL)
+    return;
+
+  FREE(TRU->table.element);
+  FREE(TRU->plane_size.element);
+  cleanup_huffman_tree(&TRU->tree);
+  FREE(TRU->x3rgb16.buf);
+
+  FREE(TRU);
+
+  *TRUP = NULL;
+}
+
+static x3f_true_t *new_true(x3f_true_t **TRUP)
+{
+  x3f_true_t *TRU = (x3f_true_t *)calloc(1, sizeof(x3f_true_t));
+
+  cleanup_true(TRUP);
+
+  TRU->table.size = 0;
+  TRU->table.element = NULL;
+  TRU->plane_size.size = 0;
+  TRU->plane_size.element = NULL;
+  TRU->tree.nodes = NULL;
+  TRU->x3rgb16.data = NULL;
+  TRU->x3rgb16.buf = NULL;
+
+  *TRUP = TRU;
+
+  return TRU;
+}
+
+static void cleanup_quattro(x3f_quattro_t **QP)
+{
+  x3f_quattro_t *Q = *QP;
+
+  if (Q == NULL)
+    return;
+
+  FREE(Q->top16.buf);
+  FREE(Q);
+
+  *QP = NULL;
+}
+
+static x3f_quattro_t *new_quattro(x3f_quattro_t **QP)
+{
+  x3f_quattro_t *Q = (x3f_quattro_t *)calloc(1, sizeof(x3f_quattro_t));
+  int i;
+
+  cleanup_quattro(QP);
+
+  for (i = 0; i < TRUE_PLANES; i++)
+  {
+    Q->plane[i].columns = 0;
+    Q->plane[i].rows = 0;
+  }
+
+  Q->unknown = 0;
+
+  Q->top16.data = NULL;
+  Q->top16.buf = NULL;
+
+  *QP = Q;
+
+  return Q;
+}
+
+/* --------------------------------------------------------------------- */
+/* Allocating Huffman engine help data                                   */
+/* --------------------------------------------------------------------- */
+
+static void cleanup_huffman(x3f_huffman_t **HUFP)
+{
+  x3f_huffman_t *HUF = *HUFP;
+
+  if (HUF == NULL)
+    return;
+
+  FREE(HUF->mapping.element);
+  FREE(HUF->table.element);
+  cleanup_huffman_tree(&HUF->tree);
+  FREE(HUF->row_offsets.element);
+  FREE(HUF->rgb8.buf);
+  FREE(HUF->x3rgb16.buf);
+  FREE(HUF);
+
+  *HUFP = NULL;
+}
+
+static x3f_huffman_t *new_huffman(x3f_huffman_t **HUFP)
+{
+  x3f_huffman_t *HUF = (x3f_huffman_t *)calloc(1, sizeof(x3f_huffman_t));
+
+  cleanup_huffman(HUFP);
+
+  /* Set all not read data block pointers to NULL */
+  HUF->mapping.size = 0;
+  HUF->mapping.element = NULL;
+  HUF->table.size = 0;
+  HUF->table.element = NULL;
+  HUF->tree.nodes = NULL;
+  HUF->row_offsets.size = 0;
+  HUF->row_offsets.element = NULL;
+  HUF->rgb8.data = NULL;
+  HUF->rgb8.buf = NULL;
+  HUF->x3rgb16.data = NULL;
+  HUF->x3rgb16.buf = NULL;
+
+  *HUFP = HUF;
+
+  return HUF;
+}
+
+/* --------------------------------------------------------------------- */
+/* Creating a new x3f structure from file                                */
+/* --------------------------------------------------------------------- */
+
+/* extern */ x3f_t *x3f_new_from_file(LibRaw_abstract_datastream *infile)
+{
+  if (!infile)
+    return NULL;
+  INT64 fsize = infile->size();
+  x3f_t *x3f = (x3f_t *)calloc(1, sizeof(x3f_t));
+  if (!x3f)
+    throw LIBRAW_EXCEPTION_ALLOC;
+  try
+  {
+    x3f_info_t *I = NULL;
+    x3f_header_t *H = NULL;
+    x3f_directory_section_t *DS = NULL;
+    int i, d;
+
+    I = &x3f->info;
+    I->error = NULL;
+    I->input.file = infile;
+    I->output.file = NULL;
+
+    /* Read file header */
+    H = &x3f->header;
+    infile->seek(0, SEEK_SET);
+    GET4(H->identifier);
+
+    if (H->identifier != X3F_FOVb)
+    {
+      free(x3f);
+      return NULL;
+    }
+
+    GET4(H->version);
+    GETN(H->unique_identifier, SIZE_UNIQUE_IDENTIFIER);
+    /* TODO: the meaning of the rest of the header for version >= 4.0 (Quattro)
+     * is unknown */
+    if (H->version < X3F_VERSION_4_0)
+    {
+      GET4(H->mark_bits);
+      GET4(H->columns);
+      GET4(H->rows);
+      GET4(H->rotation);
+      if (H->version >= X3F_VERSION_2_1)
+      {
+        int num_ext_data =
+            H->version >= X3F_VERSION_3_0 ? NUM_EXT_DATA_3_0 : NUM_EXT_DATA_2_1;
+
+        GETN(H->white_balance, SIZE_WHITE_BALANCE);
+        if (H->version >= X3F_VERSION_2_3)
+          GETN(H->color_mode, SIZE_COLOR_MODE);
+        GETN(H->extended_types, num_ext_data);
+        for (i = 0; i < num_ext_data; i++)
+          GET4F(H->extended_data[i]);
+      }
+    }
+
+    /* Go to the beginning of the directory */
+    infile->seek(-4, SEEK_END);
+    infile->seek(x3f_get4(infile), SEEK_SET);
+
+    /* Read the directory header */
+    DS = &x3f->directory_section;
+    GET4(DS->identifier);
+    GET4(DS->version);
+    GET4(DS->num_directory_entries);
+
+    if (DS->num_directory_entries > 50)
+      goto _err; // too much direntries, most likely broken file
+
+    if (DS->num_directory_entries > 0)
+    {
+      size_t size = DS->num_directory_entries * sizeof(x3f_directory_entry_t);
+      DS->directory_entry = (x3f_directory_entry_t *)calloc(1, size);
+    }
+
+    /* Traverse the directory */
+    for (d = 0; d < DS->num_directory_entries; d++)
+    {
+      x3f_directory_entry_t *DE = &DS->directory_entry[d];
+      x3f_directory_entry_header_t *DEH = &DE->header;
+      uint32_t save_dir_pos;
+
+      /* Read the directory entry info */
+      GET4(DE->input.offset);
+      GET4(DE->input.size);
+      if (DE->input.offset + DE->input.size > fsize * 2)
+        goto _err;
+
+      DE->output.offset = 0;
+      DE->output.size = 0;
+
+      GET4(DE->type);
+
+      /* Save current pos and go to the entry */
+      save_dir_pos = infile->tell();
+      infile->seek(DE->input.offset, SEEK_SET);
+
+      /* Read the type independent part of the entry header */
+      DEH = &DE->header;
+      GET4(DEH->identifier);
+      GET4(DEH->version);
+
+      /* NOTE - the tests below could be made on DE->type instead */
+
+      if (DEH->identifier == X3F_SECp)
+      {
+        x3f_property_list_t *PL = &DEH->data_subsection.property_list;
+        if (!PL)
+          goto _err;
+        /* Read the property part of the header */
+        GET4(PL->num_properties);
+        GET4(PL->character_format);
+        GET4(PL->reserved);
+        GET4(PL->total_length);
+
+        /* Set all not read data block pointers to NULL */
+        PL->data = NULL;
+        PL->data_size = 0;
+      }
+
+      if (DEH->identifier == X3F_SECi)
+      {
+        x3f_image_data_t *ID = &DEH->data_subsection.image_data;
+        if (!ID)
+          goto _err;
+        /* Read the image part of the header */
+        GET4(ID->type);
+        GET4(ID->format);
+        ID->type_format = (ID->type << 16) + (ID->format);
+        GET4(ID->columns);
+        GET4(ID->rows);
+        GET4(ID->row_stride);
+
+        /* Set all not read data block pointers to NULL */
+        ID->huffman = NULL;
+
+        ID->data = NULL;
+        ID->data_size = 0;
+      }
+
+      if (DEH->identifier == X3F_SECc)
+      {
+        x3f_camf_t *CAMF = &DEH->data_subsection.camf;
+        if (!CAMF)
+          goto _err;
+        /* Read the CAMF part of the header */
+        GET4(CAMF->type);
+        GET4(CAMF->tN.val0);
+        GET4(CAMF->tN.val1);
+        GET4(CAMF->tN.val2);
+        GET4(CAMF->tN.val3);
+
+        /* Set all not read data block pointers to NULL */
+        CAMF->data = NULL;
+        CAMF->data_size = 0;
+
+        /* Set all not allocated help pointers to NULL */
+        CAMF->table.element = NULL;
+        CAMF->table.size = 0;
+        CAMF->tree.nodes = NULL;
+        CAMF->decoded_data = NULL;
+        CAMF->decoded_data_size = 0;
+        CAMF->entry_table.element = NULL;
+        CAMF->entry_table.size = 0;
+      }
+
+      /* Reset the file pointer back to the directory */
+      infile->seek(save_dir_pos, SEEK_SET);
+    }
+
+    return x3f;
+  _err:
+    if (x3f)
+    {
+      DS = &x3f->directory_section;
+      if (DS && DS->directory_entry)
+        free(DS->directory_entry);
+      free(x3f);
+    }
+    return NULL;
+  }
+  catch (...)
+  {
+    x3f_directory_section_t *DS = &x3f->directory_section;
+    if (DS && DS->directory_entry)
+      free(DS->directory_entry);
+    free(x3f);
+    return NULL;
+  }
+}
+
+/* --------------------------------------------------------------------- */
+/* Clean up an x3f structure                                             */
+/* --------------------------------------------------------------------- */
+
+static void free_camf_entry(camf_entry_t *entry)
+{
+  FREE(entry->property_name);
+  FREE(entry->property_value);
+  FREE(entry->matrix_decoded);
+  FREE(entry->matrix_dim_entry);
+}
+
+/* extern */ x3f_return_t x3f_delete(x3f_t *x3f)
+{
+  x3f_directory_section_t *DS;
+  int d;
+
+  if (x3f == NULL)
+    return X3F_ARGUMENT_ERROR;
+
+  DS = &x3f->directory_section;
+  if (DS->num_directory_entries > 50)
+    return X3F_ARGUMENT_ERROR;
+
+  for (d = 0; d < DS->num_directory_entries; d++)
+  {
+    x3f_directory_entry_t *DE = &DS->directory_entry[d];
+    x3f_directory_entry_header_t *DEH = &DE->header;
+    if (DEH->identifier == X3F_SECp)
+    {
+      x3f_property_list_t *PL = &DEH->data_subsection.property_list;
+      if (PL)
+      {
+        int i;
+      }
+      FREE(PL->property_table.element);
+      FREE(PL->data);
+    }
+
+    if (DEH->identifier == X3F_SECi)
+    {
+      x3f_image_data_t *ID = &DEH->data_subsection.image_data;
+
+      if (ID)
+      {
+        cleanup_huffman(&ID->huffman);
+        cleanup_true(&ID->tru);
+        cleanup_quattro(&ID->quattro);
+        FREE(ID->data);
+      }
+    }
+
+    if (DEH->identifier == X3F_SECc)
+    {
+      x3f_camf_t *CAMF = &DEH->data_subsection.camf;
+      int i;
+      if (CAMF)
+      {
+        FREE(CAMF->data);
+        FREE(CAMF->table.element);
+        cleanup_huffman_tree(&CAMF->tree);
+        FREE(CAMF->decoded_data);
+        for (i = 0; i < CAMF->entry_table.size; i++)
+        {
+          free_camf_entry(&CAMF->entry_table.element[i]);
+        }
+      }
+      FREE(CAMF->entry_table.element);
+    }
+  }
+
+  FREE(DS->directory_entry);
+  FREE(x3f);
+
+  return X3F_OK;
+}
+
+/* --------------------------------------------------------------------- */
+/* Getting a reference to a directory entry                              */
+/* --------------------------------------------------------------------- */
+
+/* TODO: all those only get the first instance */
+
+static x3f_directory_entry_t *x3f_get(x3f_t *x3f, uint32_t type,
+                                      uint32_t image_type)
+{
+  x3f_directory_section_t *DS;
+  int d;
+
+  if (x3f == NULL)
+    return NULL;
+
+  DS = &x3f->directory_section;
+
+  for (d = 0; d < DS->num_directory_entries; d++)
+  {
+    x3f_directory_entry_t *DE = &DS->directory_entry[d];
+    x3f_directory_entry_header_t *DEH = &DE->header;
+
+    if (DEH->identifier == type)
+    {
+      switch (DEH->identifier)
+      {
+      case X3F_SECi:
+      {
+        x3f_image_data_t *ID = &DEH->data_subsection.image_data;
+
+        if (ID->type_format == image_type)
+          return DE;
+      }
+      break;
+      default:
+        return DE;
+      }
+    }
+  }
+
+  return NULL;
+}
+
+/* extern */ x3f_directory_entry_t *x3f_get_raw(x3f_t *x3f)
+{
+  x3f_directory_entry_t *DE;
+
+  if ((DE = x3f_get(x3f, X3F_SECi, X3F_IMAGE_RAW_HUFFMAN_X530)) != NULL)
+    return DE;
+
+  if ((DE = x3f_get(x3f, X3F_SECi, X3F_IMAGE_RAW_HUFFMAN_10BIT)) != NULL)
+    return DE;
+
+  if ((DE = x3f_get(x3f, X3F_SECi, X3F_IMAGE_RAW_TRUE)) != NULL)
+    return DE;
+
+  if ((DE = x3f_get(x3f, X3F_SECi, X3F_IMAGE_RAW_MERRILL)) != NULL)
+    return DE;
+
+  if ((DE = x3f_get(x3f, X3F_SECi, X3F_IMAGE_RAW_QUATTRO)) != NULL)
+    return DE;
+
+  if ((DE = x3f_get(x3f, X3F_SECi, X3F_IMAGE_RAW_SDQ)) != NULL)
+    return DE;
+
+  if ((DE = x3f_get(x3f, X3F_SECi, X3F_IMAGE_RAW_SDQH)) != NULL)
+    return DE;
+  if ((DE = x3f_get(x3f, X3F_SECi, X3F_IMAGE_RAW_SDQH2)) != NULL)
+    return DE;
+
+  return NULL;
+}
+
+/* extern */ x3f_directory_entry_t *x3f_get_thumb_plain(x3f_t *x3f)
+{
+  return x3f_get(x3f, X3F_SECi, X3F_IMAGE_THUMB_PLAIN);
+}
+
+/* extern */ x3f_directory_entry_t *x3f_get_thumb_huffman(x3f_t *x3f)
+{
+  return x3f_get(x3f, X3F_SECi, X3F_IMAGE_THUMB_HUFFMAN);
+}
+
+/* extern */ x3f_directory_entry_t *x3f_get_thumb_jpeg(x3f_t *x3f)
+{
+  return x3f_get(x3f, X3F_SECi, X3F_IMAGE_THUMB_JPEG);
+}
+
+/* extern */ x3f_directory_entry_t *x3f_get_camf(x3f_t *x3f)
+{
+  return x3f_get(x3f, X3F_SECc, 0);
+}
+
+/* extern */ x3f_directory_entry_t *x3f_get_prop(x3f_t *x3f)
+{
+  return x3f_get(x3f, X3F_SECp, 0);
+}
+
+/* For some obscure reason, the bit numbering is weird. It is
+   generally some kind of "big endian" style - e.g. the bit 7 is the
+   first in a byte and bit 31 first in a 4 byte int. For patterns in
+   the huffman pattern table, bit 27 is the first bit and bit 26 the
+   next one. */
+
+#define PATTERN_BIT_POS(_len, _bit) ((_len) - (_bit)-1)
+#define MEMORY_BIT_POS(_bit) PATTERN_BIT_POS(8, _bit)
+
+/* --------------------------------------------------------------------- */
+/* Huffman Decode                                                        */
+/* --------------------------------------------------------------------- */
+
+/* Make the huffman tree */
+
+#ifdef DBG_PRNT
+static char *display_code(int length, uint32_t code, char *buffer)
+{
+  int i;
+
+  for (i = 0; i < length; i++)
+  {
+    int pos = PATTERN_BIT_POS(length, i);
+    buffer[i] = ((code >> pos) & 1) == 0 ? '0' : '1';
+  }
+
+  buffer[i] = 0;
+
+  return buffer;
+}
+#endif
+
+static x3f_huffnode_t *new_node(x3f_hufftree_t *tree)
+{
+	if (tree->free_node_index >= tree->total_node_index)
+		throw LIBRAW_EXCEPTION_IO_CORRUPT;
+  x3f_huffnode_t *t = &tree->nodes[tree->free_node_index];
+
+  t->branch[0] = NULL;
+  t->branch[1] = NULL;
+  t->leaf = UNDEFINED_LEAF;
+
+  tree->free_node_index++;
+
+  return t;
+}
+
+static void add_code_to_tree(x3f_hufftree_t *tree, int length, uint32_t code,
+                             uint32_t value)
+{
+  int i;
+
+  x3f_huffnode_t *t = tree->nodes;
+
+  for (i = 0; i < length; i++)
+  {
+    int pos = PATTERN_BIT_POS(length, i);
+    int bit = (code >> pos) & 1;
+    x3f_huffnode_t *t_next = t->branch[bit];
+
+    if (t_next == NULL)
+      t_next = t->branch[bit] = new_node(tree);
+
+    t = t_next;
+  }
+
+  t->leaf = value;
+}
+
+static void populate_true_huffman_tree(x3f_hufftree_t *tree,
+                                       x3f_true_huffman_t *table)
+{
+  int i;
+
+  new_node(tree);
+
+  for (i = 0; i < table->size; i++)
+  {
+    x3f_true_huffman_element_t *element = &table->element[i];
+    uint32_t length = element->code_size;
+
+    if (length != 0)
+    {
+      /* add_code_to_tree wants the code right adjusted */
+      uint32_t code = ((element->code) >> (8 - length)) & 0xff;
+      uint32_t value = i;
+
+      add_code_to_tree(tree, length, code, value);
+
+#ifdef DBG_PRNT
+      {
+        char buffer[100];
+
+        x3f_printf(DEBUG, "H %5d : %5x : %5d : %02x %08x (%08x) (%s)\n", i, i,
+                   value, length, code, value,
+                   display_code(length, code, buffer));
+      }
+#endif
+    }
+  }
+}
+
+static void populate_huffman_tree(x3f_hufftree_t *tree, x3f_table32_t *table,
+                                  x3f_table16_t *mapping)
+{
+  int i;
+
+  new_node(tree);
+
+  for (i = 0; i < table->size; i++)
+  {
+    uint32_t element = table->element[i];
+
+    if (element != 0)
+    {
+      uint32_t length = HUF_TREE_GET_LENGTH(element);
+      uint32_t code = HUF_TREE_GET_CODE(element);
+      uint32_t value;
+
+      /* If we have a valid mapping table - then the value from the
+         mapping table shall be used. Otherwise we use the current
+         index in the table as value. */
+      if (table->size == mapping->size)
+        value = mapping->element[i];
+      else
+        value = i;
+
+      add_code_to_tree(tree, length, code, value);
+
+#ifdef DBG_PRNT
+      {
+        char buffer[100];
+
+        x3f_printf(DEBUG, "H %5d : %5x : %5d : %02x %08x (%08x) (%s)\n", i, i,
+                   value, length, code, element,
+                   display_code(length, code, buffer));
+      }
+#endif
+    }
+  }
+}
+
+#ifdef DBG_PRNT
+static void print_huffman_tree(x3f_huffnode_t *t, int length, uint32_t code)
+{
+  char buf1[100];
+  char buf2[100];
+
+  x3f_printf(DEBUG, "%*s (%s,%s) %s (%s)\n", length,
+             length < 1 ? "-" : (code & 1) ? "1" : "0",
+             t->branch[0] == NULL ? "-" : "0", t->branch[1] == NULL ? "-" : "1",
+             t->leaf == UNDEFINED_LEAF ? "-"
+                                       : (sprintf(buf1, "%x", t->leaf), buf1),
+             display_code(length, code, buf2));
+
+  code = code << 1;
+  if (t->branch[0])
+    print_huffman_tree(t->branch[0], length + 1, code + 0);
+  if (t->branch[1])
+    print_huffman_tree(t->branch[1], length + 1, code + 1);
+}
+#endif
+
+/* Help machinery for reading bits in a memory */
+
+typedef struct bit_state_s
+{
+  uint8_t *next_address;
+  uint8_t bit_offset;
+  uint8_t bits[8];
+} bit_state_t;
+
+static void set_bit_state(bit_state_t *BS, uint8_t *address)
+{
+  BS->next_address = address;
+  BS->bit_offset = 8;
+}
+
+static uint8_t get_bit(bit_state_t *BS)
+{
+  if (BS->bit_offset == 8)
+  {
+    uint8_t byte = *BS->next_address;
+    int i;
+
+    for (i = 7; i >= 0; i--)
+    {
+      BS->bits[i] = byte & 1;
+      byte = byte >> 1;
+    }
+    BS->next_address++;
+    BS->bit_offset = 0;
+  }
+
+  return BS->bits[BS->bit_offset++];
+}
+
+/* Decode use the TRUE algorithm */
+
+static int32_t get_true_diff(bit_state_t *BS, x3f_hufftree_t *HTP)
+{
+  int32_t diff;
+  x3f_huffnode_t *node = &HTP->nodes[0];
+  uint8_t bits;
+
+  while (node->branch[0] != NULL || node->branch[1] != NULL)
+  {
+    uint8_t bit = get_bit(BS);
+    x3f_huffnode_t *new_node = node->branch[bit];
+
+    node = new_node;
+    if (node == NULL)
+    {
+      /* TODO: Shouldn't this be treated as a fatal error? */
+      return 0;
+    }
+  }
+
+  bits = node->leaf;
+
+  if (bits == 0)
+    diff = 0;
+  else
+  {
+    uint8_t first_bit = get_bit(BS);
+    int i;
+
+    diff = first_bit;
+
+    for (i = 1; i < bits; i++)
+      diff = (diff << 1) + get_bit(BS);
+
+    if (first_bit == 0)
+      diff -= (1 << bits) - 1;
+  }
+
+  return diff;
+}
+
+/* This code (that decodes one of the X3F color planes, really is a
+   decoding of a compression algorithm suited for Bayer CFA data. In
+   Bayer CFA the data is divided into 2x2 squares that represents
+   (R,G1,G2,B) data. Those four positions are (in this compression)
+   treated as one data stream each, where you store the differences to
+   previous data in the stream. The reason for this is, of course,
+   that the date is more often than not near to the next data in a
+   stream that represents the same color. */
+
+/* TODO: write more about the compression */
+
+static void true_decode_one_color(x3f_image_data_t *ID, int color)
+{
+  x3f_true_t *TRU = ID->tru;
+  x3f_quattro_t *Q = ID->quattro;
+  uint32_t seed = TRU->seed[color]; /* TODO : Is this correct ? */
+  int row;
+
+  x3f_hufftree_t *tree = &TRU->tree;
+  bit_state_t BS;
+
+  int32_t row_start_acc[2][2];
+  uint32_t rows = ID->rows;
+  uint32_t cols = ID->columns;
+  x3f_area16_t *area = &TRU->x3rgb16;
+  uint16_t *dst = area->data + color;
+
+  set_bit_state(&BS, TRU->plane_address[color]);
+
+  row_start_acc[0][0] = seed;
+  row_start_acc[0][1] = seed;
+  row_start_acc[1][0] = seed;
+  row_start_acc[1][1] = seed;
+
+  if (ID->type_format == X3F_IMAGE_RAW_QUATTRO ||
+      ID->type_format == X3F_IMAGE_RAW_SDQ ||
+      ID->type_format == X3F_IMAGE_RAW_SDQH ||
+      ID->type_format == X3F_IMAGE_RAW_SDQH2)
+  {
+    rows = Q->plane[color].rows;
+    cols = Q->plane[color].columns;
+
+    if (Q->quattro_layout && color == 2)
+    {
+      area = &Q->top16;
+      dst = area->data;
+    }
+  }
+  else
+  {
+  }
+
+  if (rows != area->rows || cols < area->columns)
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+
+  for (row = 0; row < rows; row++)
+  {
+    int col;
+    bool_t odd_row = row & 1;
+    int32_t acc[2];
+
+    for (col = 0; col < cols; col++)
+    {
+      bool_t odd_col = col & 1;
+      int32_t diff = get_true_diff(&BS, tree);
+      int32_t prev = col < 2 ? row_start_acc[odd_row][odd_col] : acc[odd_col];
+      int32_t value = prev + diff;
+
+      acc[odd_col] = value;
+      if (col < 2)
+        row_start_acc[odd_row][odd_col] = value;
+
+      /* Discard additional data at the right for binned Quattro plane 2 */
+      if (col >= area->columns)
+        continue;
+
+      *dst = value;
+      dst += area->channels;
+    }
+  }
+}
+
+static void true_decode(x3f_info_t *I, x3f_directory_entry_t *DE)
+{
+  x3f_directory_entry_header_t *DEH = &DE->header;
+  x3f_image_data_t *ID = &DEH->data_subsection.image_data;
+  int color;
+
+  for (color = 0; color < 3; color++)
+  {
+    true_decode_one_color(ID, color);
+  }
+}
+
+/* Decode use the huffman tree */
+
+static int32_t get_huffman_diff(bit_state_t *BS, x3f_hufftree_t *HTP)
+{
+  int32_t diff;
+  x3f_huffnode_t *node = &HTP->nodes[0];
+
+  while (node->branch[0] != NULL || node->branch[1] != NULL)
+  {
+    uint8_t bit = get_bit(BS);
+    x3f_huffnode_t *new_node = node->branch[bit];
+
+    node = new_node;
+    if (node == NULL)
+    {
+      /* TODO: Shouldn't this be treated as a fatal error? */
+      throw LIBRAW_EXCEPTION_IO_CORRUPT;
+      return 0;
+    }
+  }
+
+  diff = node->leaf;
+
+  return diff;
+}
+
+static void huffman_decode_row(x3f_info_t *I, x3f_directory_entry_t *DE,
+                               int bits, int row, int offset, int *minimum)
+{
+  x3f_directory_entry_header_t *DEH = &DE->header;
+  x3f_image_data_t *ID = &DEH->data_subsection.image_data;
+  x3f_huffman_t *HUF = ID->huffman;
+
+  int16_t c[3] = {(int16_t)offset, (int16_t)offset, (int16_t)offset};
+  int col;
+  bit_state_t BS;
+
+  if (HUF->row_offsets.element[row] > ID->data_size - 1)
+	  throw LIBRAW_EXCEPTION_IO_CORRUPT;
+  set_bit_state(&BS, (uint8_t *)ID->data + HUF->row_offsets.element[row]);
+
+  for (col = 0; col < ID->columns; col++)
+  {
+    int color;
+
+    for (color = 0; color < 3; color++)
+    {
+      uint16_t c_fix;
+
+      c[color] += get_huffman_diff(&BS, &HUF->tree);
+      if (c[color] < 0)
+      {
+        c_fix = 0;
+        if (c[color] < *minimum)
+          *minimum = c[color];
+      }
+      else
+      {
+        c_fix = c[color];
+      }
+
+      switch (ID->type_format)
+      {
+      case X3F_IMAGE_RAW_HUFFMAN_X530:
+      case X3F_IMAGE_RAW_HUFFMAN_10BIT:
+        HUF->x3rgb16.data[3 * (row * ID->columns + col) + color] =
+            (uint16_t)c_fix;
+        break;
+      case X3F_IMAGE_THUMB_HUFFMAN:
+        HUF->rgb8.data[3 * (row * ID->columns + col) + color] = (uint8_t)c_fix;
+        break;
+      default:
+        /* TODO: Shouldn't this be treated as a fatal error? */
+        throw LIBRAW_EXCEPTION_IO_CORRUPT;
+      }
+    }
+  }
+}
+
+static void huffman_decode(x3f_info_t *I, x3f_directory_entry_t *DE, int bits)
+{
+  x3f_directory_entry_header_t *DEH = &DE->header;
+  x3f_image_data_t *ID = &DEH->data_subsection.image_data;
+
+  int row;
+  int minimum = 0;
+  int offset = legacy_offset;
+
+  for (row = 0; row < ID->rows; row++)
+    huffman_decode_row(I, DE, bits, row, offset, &minimum);
+
+  if (auto_legacy_offset && minimum < 0)
+  {
+    offset = -minimum;
+    for (row = 0; row < ID->rows; row++)
+      huffman_decode_row(I, DE, bits, row, offset, &minimum);
+  }
+}
+
+static int32_t get_simple_diff(x3f_huffman_t *HUF, uint16_t index)
+{
+  if (HUF->mapping.size == 0)
+    return index;
+  else
+    return HUF->mapping.element[index];
+}
+
+static void simple_decode_row(x3f_info_t *I, x3f_directory_entry_t *DE,
+                              int bits, int row, int row_stride)
+{
+  x3f_directory_entry_header_t *DEH = &DE->header;
+  x3f_image_data_t *ID = &DEH->data_subsection.image_data;
+  x3f_huffman_t *HUF = ID->huffman;
+
+  if (row*row_stride > ID->data_size - (ID->columns*sizeof(uint32_t)))
+	  throw LIBRAW_EXCEPTION_IO_CORRUPT;
+  uint32_t *data = (uint32_t *)((unsigned char *)ID->data + row * row_stride);
+
+  uint16_t c[3] = {0, 0, 0};
+  int col;
+
+  uint32_t mask = 0;
+
+  switch (bits)
+  {
+  case 8:
+    mask = 0x0ff;
+    break;
+  case 9:
+    mask = 0x1ff;
+    break;
+  case 10:
+    mask = 0x3ff;
+    break;
+  case 11:
+    mask = 0x7ff;
+    break;
+  case 12:
+    mask = 0xfff;
+    break;
+  default:
+    mask = 0;
+    /* TODO: Shouldn't this be treated as a fatal error? */
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+    break;
+  }
+
+  for (col = 0; col < ID->columns; col++)
+  {
+    int color;
+    uint32_t val = data[col];
+
+    for (color = 0; color < 3; color++)
+    {
+      uint16_t c_fix;
+      c[color] += get_simple_diff(HUF, (val >> (color * bits)) & mask);
+
+      switch (ID->type_format)
+      {
+      case X3F_IMAGE_RAW_HUFFMAN_X530:
+      case X3F_IMAGE_RAW_HUFFMAN_10BIT:
+        c_fix = (int16_t)c[color] > 0 ? c[color] : 0;
+
+        HUF->x3rgb16.data[3 * (row * ID->columns + col) + color] = c_fix;
+        break;
+      case X3F_IMAGE_THUMB_HUFFMAN:
+        c_fix = (int8_t)c[color] > 0 ? c[color] : 0;
+
+        HUF->rgb8.data[3 * (row * ID->columns + col) + color] = c_fix;
+        break;
+      default:
+        /* TODO: Shouldn't this be treated as a fatal error? */
+        throw LIBRAW_EXCEPTION_IO_CORRUPT;
+      }
+    }
+  }
+}
+
+static void simple_decode(x3f_info_t *I, x3f_directory_entry_t *DE, int bits,
+                          int row_stride)
+{
+  x3f_directory_entry_header_t *DEH = &DE->header;
+  x3f_image_data_t *ID = &DEH->data_subsection.image_data;
+
+  int row;
+
+  for (row = 0; row < ID->rows; row++)
+    simple_decode_row(I, DE, bits, row, row_stride);
+}
+
+/* --------------------------------------------------------------------- */
+/* Loading the data in a directory entry                                 */
+/* --------------------------------------------------------------------- */
+
+/* First you set the offset to where to start reading the data ... */
+
+static void read_data_set_offset(x3f_info_t *I, x3f_directory_entry_t *DE,
+                                 uint32_t header_size)
+{
+  uint32_t i_off = DE->input.offset + header_size;
+
+  I->input.file->seek(i_off, SEEK_SET);
+}
+
+/* ... then you read the data, block for block */
+
+static uint32_t read_data_block(void **data, x3f_info_t *I,
+                                x3f_directory_entry_t *DE, uint32_t footer)
+{
+  INT64 fpos = I->input.file->tell();
+  uint32_t size = DE->input.size + DE->input.offset - fpos - footer;
+
+  if (fpos + size > I->input.file->size())
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+
+  *data = (void *)malloc(size);
+
+  GETN(*data, size);
+
+  return size;
+}
+
+static uint32_t data_block_size(void **data, x3f_info_t *I,
+                                x3f_directory_entry_t *DE, uint32_t footer)
+{
+  uint32_t size =
+      DE->input.size + DE->input.offset - I->input.file->tell() - footer;
+  return size;
+}
+
+static void x3f_load_image_verbatim(x3f_info_t *I, x3f_directory_entry_t *DE)
+{
+  x3f_directory_entry_header_t *DEH = &DE->header;
+  x3f_image_data_t *ID = &DEH->data_subsection.image_data;
+  if (!ID->data_size)
+    ID->data_size = read_data_block(&ID->data, I, DE, 0);
+}
+
+static int32_t x3f_load_image_verbatim_size(x3f_info_t *I,
+                                            x3f_directory_entry_t *DE)
+{
+  x3f_directory_entry_header_t *DEH = &DE->header;
+  x3f_image_data_t *ID = &DEH->data_subsection.image_data;
+  return data_block_size(&ID->data, I, DE, 0);
+}
+
+static void x3f_load_property_list(x3f_info_t *I, x3f_directory_entry_t *DE)
+{
+  x3f_directory_entry_header_t *DEH = &DE->header;
+  x3f_property_list_t *PL = &DEH->data_subsection.property_list;
+  int i;
+
+  read_data_set_offset(I, DE, X3F_PROPERTY_LIST_HEADER_SIZE);
+
+  GET_PROPERTY_TABLE(PL->property_table, PL->num_properties);
+
+  if (!PL->data_size)
+    PL->data_size = read_data_block(&PL->data, I, DE, 0);
+  uint32_t maxoffset = PL->data_size / sizeof(utf16_t) -
+                       2; // at least 2 chars, value + terminating 0x0000
+
+  for (i = 0; i < PL->num_properties; i++)
+  {
+    x3f_property_t *P = &PL->property_table.element[i];
+    if (P->name_offset > maxoffset || P->value_offset > maxoffset)
+      throw LIBRAW_EXCEPTION_IO_CORRUPT;
+    P->name = ((utf16_t *)PL->data + P->name_offset);
+    P->value = ((utf16_t *)PL->data + P->value_offset);
+  }
+}
+
+static void x3f_load_true(x3f_info_t *I, x3f_directory_entry_t *DE)
+{
+  x3f_directory_entry_header_t *DEH = &DE->header;
+  x3f_image_data_t *ID = &DEH->data_subsection.image_data;
+  x3f_true_t *TRU = new_true(&ID->tru);
+  x3f_quattro_t *Q = NULL;
+  int i;
+
+  if (ID->type_format == X3F_IMAGE_RAW_QUATTRO ||
+      ID->type_format == X3F_IMAGE_RAW_SDQ ||
+      ID->type_format == X3F_IMAGE_RAW_SDQH ||
+      ID->type_format == X3F_IMAGE_RAW_SDQH2)
+  {
+    Q = new_quattro(&ID->quattro);
+
+    for (i = 0; i < TRUE_PLANES; i++)
+    {
+      GET2(Q->plane[i].columns);
+      GET2(Q->plane[i].rows);
+    }
+
+    if (Q->plane[0].rows == ID->rows / 2)
+    {
+      Q->quattro_layout = 1;
+    }
+    else if (Q->plane[0].rows == ID->rows)
+    {
+      Q->quattro_layout = 0;
+    }
+    else
+    {
+      throw LIBRAW_EXCEPTION_IO_CORRUPT;
+    }
+  }
+
+  /* Read TRUE header data */
+  GET2(TRU->seed[0]);
+  GET2(TRU->seed[1]);
+  GET2(TRU->seed[2]);
+  GET2(TRU->unknown);
+  GET_TRUE_HUFF_TABLE(TRU->table);
+
+  if (ID->type_format == X3F_IMAGE_RAW_QUATTRO ||
+      ID->type_format == X3F_IMAGE_RAW_SDQ ||
+      ID->type_format == X3F_IMAGE_RAW_SDQH ||
+      ID->type_format == X3F_IMAGE_RAW_SDQH2)
+  {
+    GET4(Q->unknown);
+  }
+
+  GET_TABLE(TRU->plane_size, GET4, TRUE_PLANES, uint32_t);
+
+  /* Read image data */
+  if (!ID->data_size)
+    ID->data_size = read_data_block(&ID->data, I, DE, 0);
+
+  /* TODO: can it be fewer than 8 bits? Maybe taken from TRU->table? */
+  new_huffman_tree(&TRU->tree, 8);
+
+  populate_true_huffman_tree(&TRU->tree, &TRU->table);
+
+#ifdef DBG_PRNT
+  print_huffman_tree(TRU->tree.nodes, 0, 0);
+#endif
+
+  TRU->plane_address[0] = (uint8_t *)ID->data;
+  for (i = 1; i < TRUE_PLANES; i++)
+    TRU->plane_address[i] = TRU->plane_address[i - 1] +
+                            (((TRU->plane_size.element[i - 1] + 15) / 16) * 16);
+
+  if ((ID->type_format == X3F_IMAGE_RAW_QUATTRO ||
+       ID->type_format == X3F_IMAGE_RAW_SDQ ||
+       ID->type_format == X3F_IMAGE_RAW_SDQH ||
+       ID->type_format == X3F_IMAGE_RAW_SDQH2) &&
+      Q->quattro_layout)
+  {
+    uint32_t columns = Q->plane[0].columns;
+    uint32_t rows = Q->plane[0].rows;
+    uint32_t channels = 3;
+    uint32_t size = columns * rows * channels;
+
+    TRU->x3rgb16.columns = columns;
+    TRU->x3rgb16.rows = rows;
+    TRU->x3rgb16.channels = channels;
+    TRU->x3rgb16.row_stride = columns * channels;
+    TRU->x3rgb16.buf = malloc(sizeof(uint16_t) * size);
+    TRU->x3rgb16.data = (uint16_t *)TRU->x3rgb16.buf;
+
+    columns = Q->plane[2].columns;
+    rows = Q->plane[2].rows;
+    channels = 1;
+    size = columns * rows * channels;
+
+    Q->top16.columns = columns;
+    Q->top16.rows = rows;
+    Q->top16.channels = channels;
+    Q->top16.row_stride = columns * channels;
+    Q->top16.buf = malloc(sizeof(uint16_t) * size);
+    Q->top16.data = (uint16_t *)Q->top16.buf;
+  }
+  else
+  {
+    uint32_t size = ID->columns * ID->rows * 3;
+
+    TRU->x3rgb16.columns = ID->columns;
+    TRU->x3rgb16.rows = ID->rows;
+    TRU->x3rgb16.channels = 3;
+    TRU->x3rgb16.row_stride = ID->columns * 3;
+    TRU->x3rgb16.buf = malloc(sizeof(uint16_t) * size);
+    TRU->x3rgb16.data = (uint16_t *)TRU->x3rgb16.buf;
+  }
+
+  true_decode(I, DE);
+}
+
+static void x3f_load_huffman_compressed(x3f_info_t *I,
+                                        x3f_directory_entry_t *DE, int bits,
+                                        int use_map_table)
+{
+  x3f_directory_entry_header_t *DEH = &DE->header;
+  x3f_image_data_t *ID = &DEH->data_subsection.image_data;
+  x3f_huffman_t *HUF = ID->huffman;
+  int table_size = 1 << bits;
+  int row_offsets_size = ID->rows * sizeof(HUF->row_offsets.element[0]);
+
+  GET_TABLE(HUF->table, GET4, table_size, uint32_t);
+
+  if (!ID->data_size)
+    ID->data_size = read_data_block(&ID->data, I, DE, row_offsets_size);
+
+  GET_TABLE(HUF->row_offsets, GET4, ID->rows, uint32_t);
+
+  new_huffman_tree(&HUF->tree, bits);
+  populate_huffman_tree(&HUF->tree, &HUF->table, &HUF->mapping);
+
+  huffman_decode(I, DE, bits);
+}
+
+static void x3f_load_huffman_not_compressed(x3f_info_t *I,
+                                            x3f_directory_entry_t *DE, int bits,
+                                            int use_map_table, int row_stride)
+{
+  x3f_directory_entry_header_t *DEH = &DE->header;
+  x3f_image_data_t *ID = &DEH->data_subsection.image_data;
+
+  if (!ID->data_size)
+    ID->data_size = read_data_block(&ID->data, I, DE, 0);
+
+  simple_decode(I, DE, bits, row_stride);
+}
+
+static void x3f_load_huffman(x3f_info_t *I, x3f_directory_entry_t *DE, int bits,
+                             int use_map_table, int row_stride)
+{
+  x3f_directory_entry_header_t *DEH = &DE->header;
+  x3f_image_data_t *ID = &DEH->data_subsection.image_data;
+  x3f_huffman_t *HUF = new_huffman(&ID->huffman);
+  uint32_t size;
+
+  if (use_map_table)
+  {
+    int table_size = 1 << bits;
+
+    GET_TABLE(HUF->mapping, GET2, table_size, uint16_t);
+  }
+
+  switch (ID->type_format)
+  {
+  case X3F_IMAGE_RAW_HUFFMAN_X530:
+  case X3F_IMAGE_RAW_HUFFMAN_10BIT:
+    size = ID->columns * ID->rows * 3;
+    HUF->x3rgb16.columns = ID->columns;
+    HUF->x3rgb16.rows = ID->rows;
+    HUF->x3rgb16.channels = 3;
+    HUF->x3rgb16.row_stride = ID->columns * 3;
+    HUF->x3rgb16.buf = malloc(sizeof(uint16_t) * size);
+    HUF->x3rgb16.data = (uint16_t *)HUF->x3rgb16.buf;
+    break;
+  case X3F_IMAGE_THUMB_HUFFMAN:
+    size = ID->columns * ID->rows * 3;
+    HUF->rgb8.columns = ID->columns;
+    HUF->rgb8.rows = ID->rows;
+    HUF->rgb8.channels = 3;
+    HUF->rgb8.row_stride = ID->columns * 3;
+    HUF->rgb8.buf = malloc(sizeof(uint8_t) * size);
+    HUF->rgb8.data = (uint8_t *)HUF->rgb8.buf;
+    break;
+  default:
+    /* TODO: Shouldn't this be treated as a fatal error? */
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+  }
+
+  if (row_stride == 0)
+    return x3f_load_huffman_compressed(I, DE, bits, use_map_table);
+  else
+    return x3f_load_huffman_not_compressed(I, DE, bits, use_map_table,
+                                           row_stride);
+}
+
+static void x3f_load_pixmap(x3f_info_t *I, x3f_directory_entry_t *DE)
+{
+  x3f_load_image_verbatim(I, DE);
+}
+
+static uint32_t x3f_load_pixmap_size(x3f_info_t *I, x3f_directory_entry_t *DE)
+{
+  return x3f_load_image_verbatim_size(I, DE);
+}
+
+static void x3f_load_jpeg(x3f_info_t *I, x3f_directory_entry_t *DE)
+{
+  x3f_load_image_verbatim(I, DE);
+}
+
+static uint32_t x3f_load_jpeg_size(x3f_info_t *I, x3f_directory_entry_t *DE)
+{
+  return x3f_load_image_verbatim_size(I, DE);
+}
+
+static void x3f_load_image(x3f_info_t *I, x3f_directory_entry_t *DE)
+{
+  x3f_directory_entry_header_t *DEH = &DE->header;
+  x3f_image_data_t *ID = &DEH->data_subsection.image_data;
+
+  if (ID->rows > 65535 || ID->columns > 65535)
+	  throw LIBRAW_EXCEPTION_IO_CORRUPT;
+
+  read_data_set_offset(I, DE, X3F_IMAGE_HEADER_SIZE);
+
+  switch (ID->type_format)
+  {
+  case X3F_IMAGE_RAW_TRUE:
+  case X3F_IMAGE_RAW_MERRILL:
+  case X3F_IMAGE_RAW_QUATTRO:
+  case X3F_IMAGE_RAW_SDQ:
+  case X3F_IMAGE_RAW_SDQH:
+  case X3F_IMAGE_RAW_SDQH2:
+    x3f_load_true(I, DE);
+    break;
+  case X3F_IMAGE_RAW_HUFFMAN_X530:
+  case X3F_IMAGE_RAW_HUFFMAN_10BIT:
+    x3f_load_huffman(I, DE, 10, 1, ID->row_stride);
+    break;
+  case X3F_IMAGE_THUMB_PLAIN:
+    x3f_load_pixmap(I, DE);
+    break;
+  case X3F_IMAGE_THUMB_HUFFMAN:
+    x3f_load_huffman(I, DE, 8, 0, ID->row_stride);
+    break;
+  case X3F_IMAGE_THUMB_JPEG:
+    x3f_load_jpeg(I, DE);
+    break;
+  default:
+    /* TODO: Shouldn't this be treated as a fatal error? */
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+  }
+}
+
+// Used only for thumbnail size estimation
+static uint32_t x3f_load_image_size(x3f_info_t *I, x3f_directory_entry_t *DE)
+{
+  x3f_directory_entry_header_t *DEH = &DE->header;
+  x3f_image_data_t *ID = &DEH->data_subsection.image_data;
+
+  read_data_set_offset(I, DE, X3F_IMAGE_HEADER_SIZE);
+
+  switch (ID->type_format)
+  {
+  case X3F_IMAGE_THUMB_PLAIN:
+    return x3f_load_pixmap_size(I, DE);
+  case X3F_IMAGE_THUMB_JPEG:
+    return x3f_load_jpeg_size(I, DE);
+    break;
+  default:
+    return 0;
+  }
+}
+
+static void x3f_load_camf_decode_type2(x3f_camf_t *CAMF)
+{
+  uint32_t key = CAMF->t2.crypt_key;
+  int i;
+
+  CAMF->decoded_data_size = CAMF->data_size;
+  CAMF->decoded_data = malloc(CAMF->decoded_data_size);
+
+  for (i = 0; i < CAMF->data_size; i++)
+  {
+    uint8_t old, _new;
+    uint32_t tmp;
+
+    old = ((uint8_t *)CAMF->data)[i];
+    key = (key * 1597 + 51749) % 244944;
+    tmp = (uint32_t)(key * ((int64_t)301593171) >> 24);
+    _new = (uint8_t)(old ^ (uint8_t)(((((key << 8) - tmp) >> 1) + tmp) >> 17));
+    ((uint8_t *)CAMF->decoded_data)[i] = _new;
+  }
+}
+
+/* NOTE: the unpacking in this code is in big respects identical to
+   true_decode_one_color(). The difference is in the output you
+   build. It might be possible to make some parts shared. NOTE ALSO:
+   This means that the meta data is obfuscated using an image
+   compression algorithm. */
+
+static void camf_decode_type4(x3f_camf_t *CAMF)
+{
+  uint32_t seed = CAMF->t4.decode_bias;
+  int row;
+
+  uint8_t *dst;
+  uint32_t dst_size = CAMF->t4.decoded_data_size;
+  uint8_t *dst_end;
+
+  bool_t odd_dst = 0;
+
+  x3f_hufftree_t *tree = &CAMF->tree;
+  bit_state_t BS;
+
+  int32_t row_start_acc[2][2];
+  uint32_t rows = CAMF->t4.block_count;
+  uint32_t cols = CAMF->t4.block_size;
+
+  CAMF->decoded_data_size = dst_size;
+
+  CAMF->decoded_data = malloc(CAMF->decoded_data_size);
+  memset(CAMF->decoded_data, 0, CAMF->decoded_data_size);
+
+  dst = (uint8_t *)CAMF->decoded_data;
+  dst_end = dst + dst_size;
+
+  set_bit_state(&BS, CAMF->decoding_start);
+
+  row_start_acc[0][0] = seed;
+  row_start_acc[0][1] = seed;
+  row_start_acc[1][0] = seed;
+  row_start_acc[1][1] = seed;
+
+  for (row = 0; row < rows; row++)
+  {
+    int col;
+    bool_t odd_row = row & 1;
+    int32_t acc[2];
+
+    /* We loop through all the columns and the rows. But the actual
+       data is smaller than that, so we break the loop when reaching
+       the end. */
+    for (col = 0; col < cols; col++)
+    {
+      bool_t odd_col = col & 1;
+      int32_t diff = get_true_diff(&BS, tree);
+      int32_t prev = col < 2 ? row_start_acc[odd_row][odd_col] : acc[odd_col];
+      int32_t value = prev + diff;
+
+      acc[odd_col] = value;
+      if (col < 2)
+        row_start_acc[odd_row][odd_col] = value;
+
+      switch (odd_dst)
+      {
+      case 0:
+        *dst++ = (uint8_t)((value >> 4) & 0xff);
+
+        if (dst >= dst_end)
+        {
+          goto ready;
+        }
+
+        *dst = (uint8_t)((value << 4) & 0xf0);
+        break;
+      case 1:
+        *dst++ |= (uint8_t)((value >> 8) & 0x0f);
+
+        if (dst >= dst_end)
+        {
+          goto ready;
+        }
+
+        *dst++ = (uint8_t)((value << 0) & 0xff);
+
+        if (dst >= dst_end)
+        {
+          goto ready;
+        }
+
+        break;
+      }
+
+      odd_dst = !odd_dst;
+    } /* end col */
+  }   /* end row */
+
+ready:;
+}
+
+static void x3f_load_camf_decode_type4(x3f_camf_t *CAMF)
+{
+  int i;
+  uint8_t *p;
+  x3f_true_huffman_element_t *element = NULL;
+
+  for (i = 0, p = (uint8_t *)CAMF->data; *p != 0; i++)
+  {
+    /* TODO: Is this too expensive ??*/
+    element = (x3f_true_huffman_element_t *)realloc(element,
+                                                    (i + 1) * sizeof(*element));
+
+    element[i].code_size = *p++;
+    element[i].code = *p++;
+  }
+
+  CAMF->table.size = i;
+  CAMF->table.element = element;
+
+  /* TODO: where does the values 28 and 32 come from? */
+#define CAMF_T4_DATA_SIZE_OFFSET 28
+#define CAMF_T4_DATA_OFFSET 32
+  CAMF->decoding_size =
+      *(uint32_t *)((unsigned char *)CAMF->data + CAMF_T4_DATA_SIZE_OFFSET);
+  CAMF->decoding_start = (uint8_t *)CAMF->data + CAMF_T4_DATA_OFFSET;
+
+  /* TODO: can it be fewer than 8 bits? Maybe taken from TRU->table? */
+  new_huffman_tree(&CAMF->tree, 8);
+
+  populate_true_huffman_tree(&CAMF->tree, &CAMF->table);
+
+#ifdef DBG_PRNT
+  print_huffman_tree(CAMF->tree.nodes, 0, 0);
+#endif
+
+  camf_decode_type4(CAMF);
+}
+
+static void camf_decode_type5(x3f_camf_t *CAMF)
+{
+  int32_t acc = CAMF->t5.decode_bias;
+
+  uint8_t *dst;
+
+  x3f_hufftree_t *tree = &CAMF->tree;
+  bit_state_t BS;
+
+  int32_t i;
+
+  CAMF->decoded_data_size = CAMF->t5.decoded_data_size;
+  CAMF->decoded_data = malloc(CAMF->decoded_data_size);
+
+  dst = (uint8_t *)CAMF->decoded_data;
+
+  set_bit_state(&BS, CAMF->decoding_start);
+
+  for (i = 0; i < CAMF->decoded_data_size; i++)
+  {
+    int32_t diff = get_true_diff(&BS, tree);
+
+    acc = acc + diff;
+    *dst++ = (uint8_t)(acc & 0xff);
+  }
+}
+
+static void x3f_load_camf_decode_type5(x3f_camf_t *CAMF)
+{
+  int i;
+  uint8_t *p;
+  x3f_true_huffman_element_t *element = NULL;
+
+  for (i = 0, p = (uint8_t *)CAMF->data; *p != 0; i++)
+  {
+    /* TODO: Is this too expensive ??*/
+    element = (x3f_true_huffman_element_t *)realloc(element,
+                                                    (i + 1) * sizeof(*element));
+
+    element[i].code_size = *p++;
+    element[i].code = *p++;
+  }
+
+  CAMF->table.size = i;
+  CAMF->table.element = element;
+
+  /* TODO: where does the values 28 and 32 come from? */
+#define CAMF_T5_DATA_SIZE_OFFSET 28
+#define CAMF_T5_DATA_OFFSET 32
+  CAMF->decoding_size =
+      *(uint32_t *)((uint8_t *)CAMF->data + CAMF_T5_DATA_SIZE_OFFSET);
+  CAMF->decoding_start = (uint8_t *)CAMF->data + CAMF_T5_DATA_OFFSET;
+
+  /* TODO: can it be fewer than 8 bits? Maybe taken from TRU->table? */
+  new_huffman_tree(&CAMF->tree, 8);
+
+  populate_true_huffman_tree(&CAMF->tree, &CAMF->table);
+
+#ifdef DBG_PRNT
+  print_huffman_tree(CAMF->tree.nodes, 0, 0);
+#endif
+
+  camf_decode_type5(CAMF);
+}
+
+static void x3f_setup_camf_text_entry(camf_entry_t *entry)
+{
+  entry->text_size = *(uint32_t *)entry->value_address;
+  entry->text = (char *)entry->value_address + 4;
+}
+
+static void x3f_setup_camf_property_entry(camf_entry_t *entry)
+{
+  int i;
+  uint8_t *e = (uint8_t *)entry->entry;
+  uint8_t *v = (uint8_t *)entry->value_address;
+  uint32_t num = entry->property_num = *(uint32_t *)v;
+  uint32_t off = *(uint32_t *)(v + 4);
+
+  entry->property_name = (char **)malloc(num * sizeof(uint8_t *));
+  entry->property_value = (uint8_t **)malloc(num * sizeof(uint8_t *));
+
+  for (i = 0; i < num; i++)
+  {
+    uint32_t name_off = off + *(uint32_t *)(v + 8 + 8 * i);
+    uint32_t value_off = off + *(uint32_t *)(v + 8 + 8 * i + 4);
+
+    entry->property_name[i] = (char *)(e + name_off);
+    entry->property_value[i] = e + value_off;
+  }
+}
+
+static void set_matrix_element_info(uint32_t type, uint32_t *size,
+                                    matrix_type_t *decoded_type)
+{
+  switch (type)
+  {
+  case 0:
+    *size = 2;
+    *decoded_type = M_INT; /* known to be true */
+    break;
+  case 1:
+    *size = 4;
+    *decoded_type = M_UINT; /* TODO: unknown ???? */
+    break;
+  case 2:
+    *size = 4;
+    *decoded_type = M_UINT; /* TODO: unknown ???? */
+    break;
+  case 3:
+    *size = 4;
+    *decoded_type = M_FLOAT; /* known to be true */
+    break;
+  case 5:
+    *size = 1;
+    *decoded_type = M_UINT; /* TODO: unknown ???? */
+    break;
+  case 6:
+    *size = 2;
+    *decoded_type = M_UINT; /* TODO: unknown ???? */
+    break;
+  default:
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+  }
+}
+
+static void get_matrix_copy(camf_entry_t *entry)
+{
+  uint32_t element_size = entry->matrix_element_size;
+  uint32_t elements = entry->matrix_elements;
+  int i, size = (entry->matrix_decoded_type == M_FLOAT ? sizeof(double)
+                                                       : sizeof(uint32_t)) *
+                elements;
+
+  entry->matrix_decoded = malloc(size);
+
+  switch (element_size)
+  {
+  case 4:
+    switch (entry->matrix_decoded_type)
+    {
+    case M_INT:
+    case M_UINT:
+      memcpy(entry->matrix_decoded, entry->matrix_data, size);
+      break;
+    case M_FLOAT:
+      for (i = 0; i < elements; i++)
+        ((double *)entry->matrix_decoded)[i] =
+            (double)((float *)entry->matrix_data)[i];
+      break;
+    default:
+      throw LIBRAW_EXCEPTION_IO_CORRUPT;
+    }
+    break;
+  case 2:
+    switch (entry->matrix_decoded_type)
+    {
+    case M_INT:
+      for (i = 0; i < elements; i++)
+        ((int32_t *)entry->matrix_decoded)[i] =
+            (int32_t)((int16_t *)entry->matrix_data)[i];
+      break;
+    case M_UINT:
+      for (i = 0; i < elements; i++)
+        ((uint32_t *)entry->matrix_decoded)[i] =
+            (uint32_t)((uint16_t *)entry->matrix_data)[i];
+      break;
+    default:
+      throw LIBRAW_EXCEPTION_IO_CORRUPT;
+    }
+    break;
+  case 1:
+    switch (entry->matrix_decoded_type)
+    {
+    case M_INT:
+      for (i = 0; i < elements; i++)
+        ((int32_t *)entry->matrix_decoded)[i] =
+            (int32_t)((int8_t *)entry->matrix_data)[i];
+      break;
+    case M_UINT:
+      for (i = 0; i < elements; i++)
+        ((uint32_t *)entry->matrix_decoded)[i] =
+            (uint32_t)((uint8_t *)entry->matrix_data)[i];
+      break;
+    default:
+      throw LIBRAW_EXCEPTION_IO_CORRUPT;
+    }
+    break;
+  default:
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+  }
+}
+
+static void x3f_setup_camf_matrix_entry(camf_entry_t *entry)
+{
+  int i;
+  int totalsize = 1;
+
+  uint8_t *e = (uint8_t *)entry->entry;
+  uint8_t *v = (uint8_t *)entry->value_address;
+  uint32_t type = entry->matrix_type = *(uint32_t *)(v + 0);
+  uint32_t dim = entry->matrix_dim = *(uint32_t *)(v + 4);
+  uint32_t off = entry->matrix_data_off = *(uint32_t *)(v + 8);
+  camf_dim_entry_t *dentry = entry->matrix_dim_entry =
+      (camf_dim_entry_t *)malloc(dim * sizeof(camf_dim_entry_t));
+
+  for (i = 0; i < dim; i++)
+  {
+    uint32_t size = dentry[i].size = *(uint32_t *)(v + 12 + 12 * i + 0);
+    dentry[i].name_offset = *(uint32_t *)(v + 12 + 12 * i + 4);
+    dentry[i].n = *(uint32_t *)(v + 12 + 12 * i + 8);
+    dentry[i].name = (char *)(e + dentry[i].name_offset);
+
+    if (dentry[i].n != i)
+    {
+    }
+
+    totalsize *= size;
+  }
+
+  set_matrix_element_info(type, &entry->matrix_element_size,
+                          &entry->matrix_decoded_type);
+  entry->matrix_data = (void *)(e + off);
+
+  entry->matrix_elements = totalsize;
+  entry->matrix_used_space = entry->entry_size - off;
+
+  /* This estimate only works for matrices above a certain size */
+  entry->matrix_estimated_element_size = entry->matrix_used_space / totalsize;
+
+  get_matrix_copy(entry);
+}
+
+static void x3f_setup_camf_entries(x3f_camf_t *CAMF)
+{
+  uint8_t *p = (uint8_t *)CAMF->decoded_data;
+  uint8_t *end = p + CAMF->decoded_data_size;
+  camf_entry_t *entry = NULL;
+  int i;
+
+  for (i = 0; p < end; i++)
+  {
+    uint32_t *p4 = (uint32_t *)p;
+
+    switch (*p4)
+    {
+    case X3F_CMbP:
+    case X3F_CMbT:
+    case X3F_CMbM:
+      break;
+    default:
+      goto stop;
+    }
+
+    /* TODO: lots of realloc - may be inefficient */
+    entry = (camf_entry_t *)realloc(entry, (i + 1) * sizeof(camf_entry_t));
+
+    /* Pointer */
+    entry[i].entry = p;
+
+    /* Header */
+    entry[i].id = *p4++;
+    entry[i].version = *p4++;
+    entry[i].entry_size = *p4++;
+    entry[i].name_offset = *p4++;
+    entry[i].value_offset = *p4++;
+
+    /* Compute adresses and sizes */
+    entry[i].name_address = (char *)(p + entry[i].name_offset);
+    entry[i].value_address = p + entry[i].value_offset;
+    entry[i].name_size = entry[i].value_offset - entry[i].name_offset;
+    entry[i].value_size = entry[i].entry_size - entry[i].value_offset;
+
+    entry[i].text_size = 0;
+    entry[i].text = NULL;
+    entry[i].property_num = 0;
+    entry[i].property_name = NULL;
+    entry[i].property_value = NULL;
+    entry[i].matrix_type = 0;
+    entry[i].matrix_dim = 0;
+    entry[i].matrix_data_off = 0;
+    entry[i].matrix_data = NULL;
+    entry[i].matrix_dim_entry = NULL;
+
+    entry[i].matrix_decoded = NULL;
+
+    switch (entry[i].id)
+    {
+    case X3F_CMbP:
+      x3f_setup_camf_property_entry(&entry[i]);
+      break;
+    case X3F_CMbT:
+      x3f_setup_camf_text_entry(&entry[i]);
+      break;
+    case X3F_CMbM:
+      x3f_setup_camf_matrix_entry(&entry[i]);
+      break;
+    }
+
+    p += entry[i].entry_size;
+  }
+
+stop:
+
+  CAMF->entry_table.size = i;
+  CAMF->entry_table.element = entry;
+}
+
+static void x3f_load_camf(x3f_info_t *I, x3f_directory_entry_t *DE)
+{
+  x3f_directory_entry_header_t *DEH = &DE->header;
+  x3f_camf_t *CAMF = &DEH->data_subsection.camf;
+
+  read_data_set_offset(I, DE, X3F_CAMF_HEADER_SIZE);
+
+  if (!CAMF->data_size)
+    CAMF->data_size = read_data_block(&CAMF->data, I, DE, 0);
+
+  switch (CAMF->type)
+  {
+  case 2: /* Older SD9-SD14 */
+    x3f_load_camf_decode_type2(CAMF);
+    break;
+  case 4: /* TRUE ... Merrill */
+    x3f_load_camf_decode_type4(CAMF);
+    break;
+  case 5: /* Quattro ... */
+    x3f_load_camf_decode_type5(CAMF);
+    break;
+  default:
+    /* TODO: Shouldn't this be treated as a fatal error? */
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+  }
+
+  if (CAMF->decoded_data != NULL)
+    x3f_setup_camf_entries(CAMF);
+  else
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+}
+
+/* extern */ x3f_return_t x3f_load_data(x3f_t *x3f, x3f_directory_entry_t *DE)
+{
+  x3f_info_t *I = &x3f->info;
+
+  if (DE == NULL)
+    return X3F_ARGUMENT_ERROR;
+
+  switch (DE->header.identifier)
+  {
+  case X3F_SECp:
+    x3f_load_property_list(I, DE);
+    break;
+  case X3F_SECi:
+    x3f_load_image(I, DE);
+    break;
+  case X3F_SECc:
+    x3f_load_camf(I, DE);
+    break;
+  default:
+    return X3F_INTERNAL_ERROR;
+  }
+  return X3F_OK;
+}
+
+/* extern */ int64_t x3f_load_data_size(x3f_t *x3f, x3f_directory_entry_t *DE)
+{
+  x3f_info_t *I = &x3f->info;
+
+  if (DE == NULL)
+    return -1;
+
+  switch (DE->header.identifier)
+  {
+  case X3F_SECi:
+    return x3f_load_image_size(I, DE);
+  default:
+    return 0;
+  }
+}
+
+/* extern */ x3f_return_t x3f_load_image_block(x3f_t *x3f,
+                                               x3f_directory_entry_t *DE)
+{
+  x3f_info_t *I = &x3f->info;
+
+  if (DE == NULL)
+    return X3F_ARGUMENT_ERROR;
+
+  switch (DE->header.identifier)
+  {
+  case X3F_SECi:
+    read_data_set_offset(I, DE, X3F_IMAGE_HEADER_SIZE);
+    x3f_load_image_verbatim(I, DE);
+    break;
+  default:
+    throw LIBRAW_EXCEPTION_IO_CORRUPT;
+    return X3F_INTERNAL_ERROR;
+  }
+
+  return X3F_OK;
+}
+
+/* --------------------------------------------------------------------- */
+/* The End                                                               */
+/* --------------------------------------------------------------------- */
+
+#endif
\ No newline at end of file

Attachment: v14_libraw_upd.tar.gz
Description: application/gzip

____________________________________________________
tde-devels mailing list -- devels@xxxxxxxxxxxxxxxxxx
To unsubscribe send an email to devels-leave@xxxxxxxxxxxxxxxxxx
Web mail archive available at https://mail.trinitydesktop.org/mailman3/hyperkitty/list/devels@xxxxxxxxxxxxxxxxxx

[Index of Archives]     [Trinity Users]     [Linux Sound]     [ALSA Users]     [ALSA Devel]     [Linux Audio Users]     [Linux Media]     [KDE]     [Kernel]     [Gimp]     [Yosemite News]     [Linux Media]     [Trinity Desktop Environment]

  Powered by Linux