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);

    identify();

	if (!strcasecmp(imgdata.idata.make, "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;
		}
	}

	// XTrans Compressed?
	if (!imgdata.idata.dng_version && !strcasecmp(imgdata.idata.make, "Fujifilm") && (load_raw == &LibRaw::unpacked_load_raw) )
	{
		if (imgdata.sizes.raw_width * imgdata.sizes.raw_height * 2 != libraw_internal_data.unpacker_data.data_size)
			parse_xtrans_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 c = 0; c < 36; c++)
					imgdata.idata.xtrans[0][c] = imgdata.idata.xtrans_abs[0][c];
			}
		}
	}

    // Fix DNG white balance if needed
    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};
	for(int c = 0; c < imgdata.idata.colors ; c++ )
	  delta[c] = imgdata.color.dng_levels.dng_whitelevel[c] - imgdata.color.dng_levels.dng_blacklevel[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 &&
      (
    (!strcasecmp(imgdata.idata.make,"Leica") && !strcasecmp(imgdata.idata.model,"D-LUX (Typ 109)"))
	  ||
	  (!strcasecmp(imgdata.idata.make,"Panasonic") && !strcasecmp(imgdata.idata.model,"LX100"))
	)
       )
      imgdata.sizes.width = 4288;

	if (!strncasecmp(imgdata.idata.make, "Sony", 4) && imgdata.idata.dng_version)
	{
		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);
		else if (S.raw_width == 6048)
		{
			S.width = S.raw_width-24;
			if (strstr(imgdata.idata.model,"RX1") || strstr(imgdata.idata.model,"A99")) S.width -= 6;
		}
		else if (S.raw_width == 7392) S.width = S.raw_width-30;
		else if(S.raw_width == 8000)	S.width = S.raw_width - 32;
	}

	if(!strcasecmp(imgdata.idata.make,"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;
		IO.mix_green = 1;
		pentax_component_load_raw = load_raw;
		load_raw= &LibRaw::pentax_4shot_load_raw;
	}

	if (!imgdata.idata.dng_version && !strcmp(imgdata.idata.make, "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;
	}

	// S3Pro DNG patch
	if(imgdata.idata.dng_version && !strcmp(imgdata.idata.make,"Fujifilm") && !strcmp(imgdata.idata.model,"S3Pro") && imgdata.sizes.raw_width == 4288 )
	{
		imgdata.sizes.left_margin++;
		imgdata.sizes.width--;
	}
	if(imgdata.idata.dng_version && !strcmp(imgdata.idata.make,"Fujifilm") && !strcmp(imgdata.idata.model,"S5Pro") && imgdata.sizes.raw_width == 4288 )
	{
		imgdata.sizes.left_margin++;
		imgdata.sizes.width--;
	}
	if(!imgdata.idata.dng_version && !strcmp(imgdata.idata.make,"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 && !strcasecmp(imgdata.idata.make,"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 && !strcasecmp(imgdata.idata.make,"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(int 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)
       && !strcasecmp(imgdata.idata.make,"Nikon")
       && strncmp(imgdata.idata.model,"COOLPIX",7)
//	   && strncmp(imgdata.idata.model,"1 ",2)
       && 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(int c=0; c< C.cblack[4]*C.cblack[5];c++)
          C.cblack[6+c]/=4;
      }

    // 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  (!strcasecmp(imgdata.idata.make,"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  (!strcasecmp(imgdata.idata.make,"Samsung") && !strcasecmp(imgdata.idata.model,"GX20")) {
      C.WB_Coeffs[LIBRAW_WBI_Daylight][2] = (int) ((float) (C.WB_Coeffs[LIBRAW_WBI_Daylight][2]) * 2.56f);
      C.WB_Coeffs[LIBRAW_WBI_Shade][2] = (int) ((float) (C.WB_Coeffs[LIBRAW_WBI_Shade][2]) * 2.56f);
      C.WB_Coeffs[LIBRAW_WBI_Cloudy][2] = (int) ((float) (C.WB_Coeffs[LIBRAW_WBI_Cloudy][2]) * 2.56f);
      C.WB_Coeffs[LIBRAW_WBI_Tungsten][2] = (int) ((float) (C.WB_Coeffs[LIBRAW_WBI_Tungsten][2]) * 2.56f);
      C.WB_Coeffs[LIBRAW_WBI_FL_D][2] = (int) ((float) (C.WB_Coeffs[LIBRAW_WBI_FL_D][2]) * 2.56f);
      C.WB_Coeffs[LIBRAW_WBI_FL_N][2] = (int) ((float) (C.WB_Coeffs[LIBRAW_WBI_FL_N][2]) * 2.56f);
      C.WB_Coeffs[LIBRAW_WBI_FL_W][2] = (int) ((float) (C.WB_Coeffs[LIBRAW_WBI_FL_W][2]) * 2.56f);
      C.WB_Coeffs[LIBRAW_WBI_Flash][2] = (int) ((float) (C.WB_Coeffs[LIBRAW_WBI_Flash][2]) * 2.56f);
      for (int c=0; c<64; c++) {
        if (imgdata.color.WBCT_Coeffs[c][0] > 0.0f) {
          imgdata.color.WBCT_Coeffs[c][3] *= 2.56f;
        }
      }
    }

	// Adjust BL for Panasonic
    if(load_raw == &LibRaw::panasonic_load_raw && (!strcasecmp(imgdata.idata.make,"Panasonic") || !strcasecmp(imgdata.idata.make,"Leica") ||  !strcasecmp(imgdata.idata.make,"YUNEEC"))
       &&  ID.pana_black[0] && ID.pana_black[1] && ID.pana_black[2])
      {
        C.black=0;
        C.cblack[0] = ID.pana_black[0]+ID.pana_black[3];
        C.cblack[1] = C.cblack[3] = ID.pana_black[1]+ID.pana_black[3];
        C.cblack[2] = ID.pana_black[2]+ID.pana_black[3];
        int 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
    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;
            }
    }
#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);
    printf("RawSize: %dx%d data offset: %d data size:%d bpp: %g bpp2: %g\n",S.raw_width,S.raw_height,libraw_internal_data.unpacker_data.data_offset,bytes,bpp,bpp2);
    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;
        printf("3 channel hassy found\n");
      }
#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 (std::exception ee) {
    EXCEPTION_HANDLER(LIBRAW_EXCEPTION_IO_CORRUPT);
  }

  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) ));

  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;
}