/* -*- C++ -*-
 * Copyright 2019-2025 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];
  memset(tail,0,sizeof(tail));
  fseek(ifp, -int(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_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] = getrealf(LIBRAW_EXIFTAG_TYPE_FLOAT);
  cam_mul[2] = getrealf(LIBRAW_EXIFTAG_TYPE_FLOAT);
  temp = get4();
  maximum = ~((~0u) << LIM(temp, 1, 31));
  fseek(ifp, 668, SEEK_CUR);
  shutter = float(get4()) / 1000000000.f;
  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(INT64 end)
{
  unsigned size;
  INT64 save;
  char tag[4];
  if (libraw_internal_data.unpacker_data.CR3_Version-- < 1)
	  throw LIBRAW_EXCEPTION_IO_CORRUPT;

  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
    memset(tag,0,sizeof(tag));
    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 + INT64(size), SEEK_SET);
  }
}

void LibRaw::parse_smal(INT64 offset, INT64 fsize)
{
  int ver;

  fseek(ifp, offset + 2LL, SEEK_SET);
  order = 0x4949;
  ver = fgetc(ifp);
  if (ver == 6)
    fseek(ifp, 5, SEEK_CUR);
  if (INT64(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 = &LibRaw::smal_v6_load_raw;
  if (ver == 9)
    load_raw = &LibRaw::smal_v9_load_raw;
}

void LibRaw::parse_riff(int maxdepth)
{
  unsigned i, size;
  INT64 end;
  char tag[4] = {0, 0, 0, 0}, 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;
  if (maxdepth < 1)
	  throw LIBRAW_EXCEPTION_IO_CORRUPT;

  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(maxdepth-1);
  }
  else if (!memcmp(tag, "nctg", 4))
  {
    while (ftell(ifp) + 7 < end)
    {
		if (feof(ifp))
			break;
      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)
  {
    memset(date,0,sizeof(date));
    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
  {
	memset(line, 0, sizeof(line));
    if (!fgets(line, 128, ifp))
      break;
    line[127] = 0;
    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 = float(atof(val));
    if (!strcmp(line, "SPE"))
      shutter = float(atof(val));
    if (!strcmp(line, "FOCLEN"))
      focal_len = float(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_crops[0].cleft,
             &imgdata.sizes.raw_inset_crops[0].ctop,
             &imgdata.sizes.raw_inset_crops[0].cwidth,
             &imgdata.sizes.raw_inset_crops[0].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");
  thumb_format = LIBRAW_INTERNAL_THUMBNAIL_ROLLEI;
}

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();
	memset(str, 0, sizeof(str));
    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();
  thumb_format = LIBRAW_INTERNAL_THUMBNAIL_PPM;
  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)] = float(get4());
  fseek(ifp, 88, SEEK_SET);
  aperture = libraw_powf64l(2.0f, ((float)get4()) / 16.0f);
  fseek(ifp, 112, SEEK_SET);
  focal_len = float(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(INT64 offset)
{
  int len, hlen, mark;
  INT64 save;

  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 + INT64(hlen)) >= 0 &&
        (save + INT64(hlen)) <= ifp->size()) /* "HEAP" */
    {
      parse_ciff(save + hlen, len - hlen, 0);
    }
    if (parse_tiff(save + 6))
      apply_tiff();
    fseek(ifp, save + INT64(len), SEEK_SET);
  }
  return 1;
}

void LibRaw::parse_thumb_note(INT64 base, unsigned toff, unsigned tlen)
{
  unsigned entries, tag, type, len;
  INT64 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);
  raw_stride =
      ((((((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];

    if (ftell(ifp) > 22LL) // 22 bytes is minimum jpeg size
    {
        thumb_length = unsigned(ftell(ifp));
        thumb_offset = 0;
        thumb_width = thumb_height = 0;
        load_flags |= 0x4000; // flag: we have JPEG from beginning to meta_offset
    }

	// 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 - int(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
