#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 void *x3f_limited_malloc(UINT64 sz)
{
  if (sz > LIBRAW_X3F_ALLOC_LIMIT_MB * 1024ULL * 1024ULL)
    throw LIBRAW_EXCEPTION_TOOBIG;
  void *ret = malloc(sz);
  if (!ret)
    throw LIBRAW_EXCEPTION_ALLOC;
  return ret;
}

static void *x3f_limited_calloc(UINT64 n, UINT64 sz)
{
  if (sz * n > LIBRAW_X3F_ALLOC_LIMIT_MB * 1024ULL * 1024ULL)
    throw LIBRAW_EXCEPTION_TOOBIG;
  void *ret = calloc(n, sz);
  if (!ret)
    throw LIBRAW_EXCEPTION_ALLOC;
  return ret;
}

static void *x3f_limited_realloc(void *ptr, UINT64 sz)
{
  if (sz > LIBRAW_X3F_ALLOC_LIMIT_MB * 1024ULL * 1024ULL)
    throw LIBRAW_EXCEPTION_TOOBIG;
  void *ret = realloc(ptr, sz);
  if (!ret)
    throw LIBRAW_EXCEPTION_ALLOC;
  return ret;
}


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 *)x3f_limited_realloc((_T).element, (_NUM) * sizeof((_T).element[0]));      \
    for (_i = 0; _i < (int)(_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 *)x3f_limited_realloc(                                  \
        (_T).element, (_NUM) * sizeof((_T).element[0]));                       \
    for (_i = 0; _i < (int)(_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 *)x3f_limited_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 *)x3f_limited_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 *)x3f_limited_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 *)x3f_limited_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 *)x3f_limited_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 *)x3f_limited_calloc(1, sizeof(x3f_t));
  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);
    if (H->version < X3F_VERSION_4_0)
    {
      GETN(H->unique_identifier, SIZE_UNIQUE_IDENTIFIER);
      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]);
      }
    }
    else if (H->version < X3F_VERSION(5, 0))
    {
      /* TODO: the meaning of the rest of the header for version >= 4.0 
      * (Quattro) is unknown */
      infile->seek(12, SEEK_CUR);
      GETN(H->unique_identifier, SIZE_UNIQUE_IDENTIFIER);
      infile->seek(4, SEEK_CUR);
      GET4(H->columns);
      GET4(H->rows);
      GET4(H->rotation);
    }

    /* 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 *)x3f_limited_calloc(1, size);
    }

    /* Traverse the directory */
    for (d = 0; d < (int)DS->num_directory_entries; d++)
    {
      x3f_directory_entry_t *DE = &DS->directory_entry[d];
      x3f_directory_entry_header_t *DEH = &DE->header;
      INT64 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 < (int)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;
      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 < (int)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 < (int)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 < (int)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 < (int)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 < (int)rows; row++)
  {
    int col;
    bool_t odd_row = row & 1;
    int32_t acc[2];

    for (col = 0; col < (int)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 >= (int)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; /* unreachable code */
    }
  }

  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 < (int)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 < (int)ID->rows; row++)
    huffman_decode_row(I, DE, bits, row, offset, &minimum);

  if (auto_legacy_offset && minimum < 0)
  {
    offset = -minimum;
    for (row = 0; row < (int)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 > (int)(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 < (int)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] = uint8_t(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 < (int)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)
{
  uint32_t fpos = (I->input.file->tell() & 0x7fffffffU) ;
  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 *)x3f_limited_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 fpos = (I->input.file->tell() & 0x7fffffffU);
  uint32_t size =
      DE->input.size + DE->input.offset - fpos - 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 < (int)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;
    UINT64 size = UINT64(columns) * UINT64(rows) * UINT64(channels);

    TRU->x3rgb16.columns = columns;
    TRU->x3rgb16.rows = rows;
    TRU->x3rgb16.channels = channels;
    TRU->x3rgb16.row_stride = columns * channels;
    TRU->x3rgb16.buf = x3f_limited_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 = UINT64(columns) * UINT64(rows) * UINT64(channels);

    Q->top16.columns = columns;
    Q->top16.rows = rows;
    Q->top16.channels = channels;
    Q->top16.row_stride = columns * channels;
    Q->top16.buf = x3f_limited_malloc(sizeof(uint16_t) * size);
    Q->top16.data = (uint16_t *)Q->top16.buf;
  }
  else
  {
    UINT64 size = UINT64(ID->columns) * UINT64(ID->rows) * 3ULL;

    TRU->x3rgb16.columns = ID->columns;
    TRU->x3rgb16.rows = ID->rows;
    TRU->x3rgb16.channels = 3;
    TRU->x3rgb16.row_stride = ID->columns * 3;
    TRU->x3rgb16.buf = x3f_limited_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);
  UINT64 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 = UINT64(ID->columns) * UINT64(ID->rows) * 3ULL;
    HUF->x3rgb16.columns = ID->columns;
    HUF->x3rgb16.rows = ID->rows;
    HUF->x3rgb16.channels = 3;
    HUF->x3rgb16.row_stride = ID->columns * 3;
    HUF->x3rgb16.buf = x3f_limited_malloc(sizeof(uint16_t) * size);
    HUF->x3rgb16.data = (uint16_t *)HUF->x3rgb16.buf;
    break;
  case X3F_IMAGE_THUMB_HUFFMAN:
    size = UINT64(ID->columns) * UINT64(ID->rows) * 3ULL;
    HUF->rgb8.columns = ID->columns;
    HUF->rgb8.rows = ID->rows;
    HUF->rgb8.channels = 3;
    HUF->rgb8.row_stride = ID->columns * 3;
    HUF->rgb8.buf = x3f_limited_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 int32_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 int32_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 int32_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 = x3f_limited_malloc(CAMF->decoded_data_size);

  for (i = 0; i < (int)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 = x3f_limited_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 < (int)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 < (int)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 *)x3f_limited_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 = x3f_limited_malloc(CAMF->decoded_data_size);

  dst = (uint8_t *)CAMF->decoded_data;

  set_bit_state(&BS, CAMF->decoding_start);

  for (i = 0; i < (int)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 *)x3f_limited_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 **)x3f_limited_malloc(num * sizeof(uint8_t *));
  entry->property_value = (uint8_t **)x3f_limited_malloc(num * sizeof(uint8_t *));

  for (i = 0; i < (int)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 = x3f_limited_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 < (int)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 < (int)elements; i++)
        ((int32_t *)entry->matrix_decoded)[i] =
            (int32_t)((int16_t *)entry->matrix_data)[i];
      break;
    case M_UINT:
      for (i = 0; i < (int)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 < (int)elements; i++)
        ((int32_t *)entry->matrix_decoded)[i] =
            (int32_t)((int8_t *)entry->matrix_data)[i];
      break;
    case M_UINT:
      for (i = 0; i < (int)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 *)x3f_limited_malloc(dim * sizeof(camf_dim_entry_t));

  for (i = 0; i < (int)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 ((int)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 *)x3f_limited_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 addresses 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 */ int32_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; /* unreachable code*/
  }

  return X3F_OK;
}

/* --------------------------------------------------------------------- */
/* The End                                                               */
/* --------------------------------------------------------------------- */

#endif
