#include "exif.h"

#include <stdlib.h>


extern struct prefs_struct prefs;
extern unsigned char* rewrite_cache;
extern unsigned long int rewrite_cache_offset;


/* some TIFF definitions */

struct tiff_ifd_type_size tiff_ifd_type_sizes[]=
{
	{	TIFF_IFD_TYPE_BYTE,			1	},
	{	TIFF_IFD_TYPE_STRING,		1	},
	{	TIFF_IFD_TYPE_USHORT,		2	},
	{	TIFF_IFD_TYPE_ULONG,		4	},
	{	TIFF_IFD_TYPE_URATIONAL,	8	},
	{	TIFF_IFD_TYPE_SBYTE,		1	},
	{	TIFF_IFD_TYPE_UNDEFINED,	8	},
	{	TIFF_IFD_TYPE_SSHORT,		2	},
	{	TIFF_IFD_TYPE_SLONG,		4	},
	{	TIFF_IFD_TYPE_SRATIONAL,	8	},
	{	TIFF_IFD_TYPE_FLOAT,		4	},
	{	TIFF_IFD_TYPE_DOUBLE,		8	},
	/* reserved */
	{	TIFF_IFD_TYPE_UNKNOWN, 0 }
};


struct exif_ifd_field exif_ifd_fields[]=
{
	{	EXIF_IFD_IMAGE_WIDTH,						TIFF_IFD_TYPE_ULONG,		  1,	"image width"	},
	{	EXIF_IFD_IMAGE_HEIGHT,						TIFF_IFD_TYPE_ULONG,		  1,	"image height"	},
	{	EXIF_IFD_BITS_PER_SAMPLE,					TIFF_IFD_TYPE_USHORT,		  3,	"bits per sample"	},
	{	EXIF_IFD_COMPRESSION,						TIFF_IFD_TYPE_USHORT,		  1,	"compression"	},
	{	EXIF_IFD_PHOTOMETRIC_INTERPRETATION,		TIFF_IFD_TYPE_USHORT,		  1,	"photometric interpretation"	},
	{	EXIF_IFD_IMAGE_DESCR,						TIFF_IFD_TYPE_STRING,		 20,	"image description" },
	{	EXIF_IFD_MAKE,								TIFF_IFD_TYPE_STRING,		  6,	"make" },
	{	EXIF_IFD_MODEL,								TIFF_IFD_TYPE_STRING,		 20,	"model" },
	{	EXIF_IFD_STRIP_OFFSETS,						TIFF_IFD_TYPE_ULONG,		  1,	"strip offsets" },
	{	EXIF_IFD_ORIENTATION,						TIFF_IFD_TYPE_USHORT,		  1,	"orientation" },
	{	EXIF_IFD_SAMPLES_PER_PIXEL,					TIFF_IFD_TYPE_USHORT,		  1,	"samples per pixel" },
	{	EXIF_IFD_ROWS_PER_STRIP,					TIFF_IFD_TYPE_ULONG,		  1,	"rows per strip" },
	{	EXIF_IFD_STRIP_BYTE_COUNT,					TIFF_IFD_TYPE_ULONG,		  1,	"strip byte count" },
	{	EXIF_IFD_XRES,								TIFF_IFD_TYPE_URATIONAL,	  1,	"x resolution" },
	{	EXIF_IFD_YRES,								TIFF_IFD_TYPE_URATIONAL,	  1,	"y resolution" },
	{	EXIF_IFD_RES_UNIT,							TIFF_IFD_TYPE_USHORT,		  1,	"resolution unit" },
	{	EXIF_IFD_SOFTWARE,							TIFF_IFD_TYPE_STRING,		 23,	"software" },
	{	EXIF_IFD_DATE_TIME,							TIFF_IFD_TYPE_STRING,		 20,	"date/time" },

	{	EXIF_IFD_JPEG_IF_OFFSET,					TIFF_IFD_TYPE_ULONG,		  1,	"JPEG IF offset" },
	{	EXIF_IFD_JPEG_IF_BYTE_COUNT,				TIFF_IFD_TYPE_ULONG,		  1,	"JPEG IF byte count" },
	{	EXIF_IFD_YCBCR_POS,							TIFF_IFD_TYPE_USHORT,		  1,	"YCbCr positioning" },

	{	EXIF_IFD_EXPOSURE_TIME,						TIFF_IFD_TYPE_URATIONAL,	  1,	"exposure time" },
	{	EXIF_IFD_F_NUMBER,							TIFF_IFD_TYPE_URATIONAL,	  1,	"F number" },
	{	EXIF_IFD_OFFSET,							TIFF_IFD_TYPE_ULONG,		  1,	"EXIF offset" },
	{	EXIF_IFD_LOCATION_OFFSET,					TIFF_IFD_TYPE_URATIONAL,	000,	"location offset" },
	{	EXIF_IFD_EXPOSURE_PROGRAM,					TIFF_IFD_TYPE_USHORT,		  1,	"exposure program" },
	{	EXIF_IFD_ISO_SPEED_RATINGS,					TIFF_IFD_TYPE_USHORT,		  1,	"ISO speed ratings" },

	{	EXIF_IFD_EXIF_VERSION,						TIFF_IFD_TYPE_UNDEFINED,	  4,	"EXIF version" },
	{	EXIF_IFD_ORIG_DATE_TIME,					TIFF_IFD_TYPE_STRING,		 20,	"original date/time" },
	{	EXIF_IFD_DIGIT_DATE_TIME,					TIFF_IFD_TYPE_STRING,		 20,	"digitized date/time" },
	{	EXIF_IFD_COMPONENT_CONFIG,					TIFF_IFD_TYPE_UNDEFINED,	  4,	"component configuration" },
	{	EXIF_IFD_COMPRESSION_RATE,					TIFF_IFD_TYPE_URATIONAL,	  1,	"compression bits/pixel" },
	{	EXIF_IFD_SHUTTER_SPEED,						TIFF_IFD_TYPE_SRATIONAL,	  1,	"shutter speed" },
	{	EXIF_IFD_APERTURE,							TIFF_IFD_TYPE_URATIONAL,	  1,	"aperture" },
	{	EXIF_IFD_BRIGHTNESS,						TIFF_IFD_TYPE_SRATIONAL,	  1,	"brightness" },
	{	EXIF_IFD_EXPOSURE_BIAS,						TIFF_IFD_TYPE_SRATIONAL,	  1,	"exposure bias" },
	{	EXIF_IFD_MAX_APERTURE,						TIFF_IFD_TYPE_URATIONAL,	  1,	"max aperture" },
	{	EXIF_IFD_SUBJECT_DISTANCE,					TIFF_IFD_TYPE_URATIONAL,	  1,	"subject distance" },
	{	EXIF_IFD_METERING_MODE,						TIFF_IFD_TYPE_USHORT,		  1,	"metering mode" },
	{	EXIF_IFD_LIGHT_SOURCE,						TIFF_IFD_TYPE_USHORT,		  1,	"light source" },
	{	EXIF_IFD_FLASH,								TIFF_IFD_TYPE_USHORT,		  1,	"flash" },
	{	EXIF_IFD_FOCAL_LENGTH,						TIFF_IFD_TYPE_URATIONAL,	  1,	"focal length" },
	{	EXIF_IFD_MAKER_NOTE,						TIFF_IFD_TYPE_UNDEFINED,	 -1,	"maker note" },
	{	EXIF_IFD_USER_COMMENT,						TIFF_IFD_TYPE_UNDEFINED,	 -1,	"user comment" },

	{	EXIF_IFD_FLASH_PIX_VERSION,					TIFF_IFD_TYPE_UNDEFINED,	  4,	"flash pix version" },
	{	EXIF_IFD_COLOR_SPACE,						TIFF_IFD_TYPE_USHORT,		  1,	"color space" },
	{	EXIF_IFD_IMAGE_WIDTH,						TIFF_IFD_TYPE_ULONG,		  1,	"EXIF image width" },
	{	EXIF_IFD_IMAGE_HEIGHT,						TIFF_IFD_TYPE_ULONG,		  1,	"EXIF image height" },
	{	EXIF_IFD_SOUND_FILE,						TIFF_IFD_TYPE_URATIONAL,	000,	"sound file" },
	{	EXIF_IFD_INTEROPE_OFFSET,					TIFF_IFD_TYPE_ULONG,		  1,	"EXIF interoperability offset" },
	{	EXIF_IFD_FOCAL_PLANE_XRES,					TIFF_IFD_TYPE_URATIONAL,	  1,	"focal plane x resolution" },
	{	EXIF_IFD_FOCAL_PLANE_YRES,					TIFF_IFD_TYPE_URATIONAL,	  1,	"focal plane y resolution" },
	{	EXIF_IFD_FOCAL_PLANE_RES_UNIT,				TIFF_IFD_TYPE_USHORT,		  1,	"focal plane res unit" },
	{	EXIF_IFD_SENSING_METHOD,					TIFF_IFD_TYPE_USHORT,		  2,	"sensing method" },
	{	EXIF_IFD_FILE_SOURCE,						TIFF_IFD_TYPE_UNDEFINED,	  1,	"file source" },
	{	EXIF_IFD_SCENE_TYPE,						TIFF_IFD_TYPE_UNDEFINED,	  1,	"scene type" },
	/* reserved */
	{	EXIF_IFD_EOT, TIFF_IFD_TYPE_UNDEFINED, 0, NULL }
};

struct exif_interope_field exif_interope_fields[]=
{
	{	EXIF_INTEROPE_INDEX,						TIFF_IFD_TYPE_STRING,		  4,	"index" },
	{	EXIF_INTEROPE_VERSION,						TIFF_IFD_TYPE_UNDEFINED,	  4,	"version" },
	{	EXIF_INTEROPE_IMAGE_WIDTH,					TIFF_IFD_TYPE_USHORT,		  1,	"image width" },
	{	EXIF_INTEROPE_IMAGE_HEIGHT,					TIFF_IFD_TYPE_USHORT,		  1,	"image height" },
	/* reserved */
	{	EXIF_INTEROPE_EOT, TIFF_IFD_TYPE_UNDEFINED, 0, NULL }
};

struct exif_location_field exif_location_fields[]=
{
	{	EXIF_LOCATION_CONTINENT,					TIFF_IFD_TYPE_STRING,		 -1,	"continent" },
	{	EXIF_LOCATION_COUNTRY,						TIFF_IFD_TYPE_STRING,		  2,	"country (ISO 3166)" },
	{	EXIF_LOCATION_COUNTRY_OTHER,				TIFF_IFD_TYPE_STRING,		 -1,	"country/other" },
	{	EXIF_LOCATION_REGION,						TIFF_IFD_TYPE_STRING,		 -1,	"region" },
	{	EXIF_LOCATION_AREA,							TIFF_IFD_TYPE_STRING,		 -1,	"area" },
	{	EXIF_LOCATION_SUB_AREA,						TIFF_IFD_TYPE_STRING,		 -1,	"sub-area" },
	{	EXIF_LOCATION_PLACE,						TIFF_IFD_TYPE_STRING,		 -1,	"place" },
	{	EXIF_LOCATION_PLACE_AREA,					TIFF_IFD_TYPE_STRING,		 -1,	"place area" },
	{	EXIF_LOCATION_LOCATION,						TIFF_IFD_TYPE_STRING,		 -1,	"location" },
	{	EXIF_LOCATION_ADDRESS,						TIFF_IFD_TYPE_STRING,		 -1,	"address" },
	{	EXIF_LOCATION_POSTAL_CODE,					TIFF_IFD_TYPE_STRING,		 -1,	"postal code" },
	{	EXIF_LOCATION_DESCRIPTION,					TIFF_IFD_TYPE_STRING,		 -1,	"description" },
	/* reserved */
	{	EXIF_LOCATION_EOT, TIFF_IFD_TYPE_UNDEFINED, 0, NULL }
};

struct exif_canon_field exif_canon_fields[]=
{
	{	EXIF_CANON_MAKER_NOTE_UNKNOWN1,				TIFF_IFD_TYPE_USHORT,		  6,	"unknown1" },
	{	EXIF_CANON_MAKER_NOTE_UNKNOWN2,				TIFF_IFD_TYPE_USHORT,		 19,	"unknown2" },
	{	EXIF_CANON_MAKER_NOTE_UNKNOWN3,				TIFF_IFD_TYPE_USHORT,		  4,	"unknown3" },
	{	EXIF_CANON_MAKER_NOTE_UNKNOWN4,				TIFF_IFD_TYPE_USHORT,		  4,	"unknown4" },
	{	EXIF_CANON_MAKER_NOTE_UNKNOWN5,				TIFF_IFD_TYPE_USHORT,		 15,	"unknown5" },
	{	EXIF_CANON_MAKER_NOTE_IMAGE_TYPE,			TIFF_IFD_TYPE_STRING,		 32,	"image type" },
	{	EXIF_CANON_MAKER_NOTE_FIRMWARE_VERSION,		TIFF_IFD_TYPE_STRING,		 24,	"firmware version" },
	{	EXIF_CANON_MAKER_NOTE_IMAGE_NUMBER,			TIFF_IFD_TYPE_ULONG,		  1,	"image number" },
	{	EXIF_CANON_MAKER_NOTE_OWNER_NAME,			TIFF_IFD_TYPE_STRING,		 32,	"owner name" },
	{	EXIF_CANON_MAKER_NOTE_UNKNOWN6,				TIFF_IFD_TYPE_ULONG,		  1,	"unknown6" },
	/* reserved */
	{	EXIF_CANON_EOT, TIFF_IFD_TYPE_UNDEFINED, 0, NULL }
};

struct exif_fuji_field exif_fuji_fields[]=
{
	{	EXIF_FUJI_MAKER_NOTE_VERSION,				TIFF_IFD_TYPE_UNDEFINED,	  4,	"version" },
	{	EXIF_FUJI_MAKER_NOTE_QUALITY,				TIFF_IFD_TYPE_STRING,		  8,	"quality" },
	{	EXIF_FUJI_MAKER_NOTE_SHARPNESS,				TIFF_IFD_TYPE_USHORT,		  1,	"sharpness" },
	{	EXIF_FUJI_MAKER_NOTE_WHITE_BALANCE,			TIFF_IFD_TYPE_USHORT,		  1,	"white balance" },
	{	EXIF_FUJI_MAKER_NOTE_FLASH_MODE,			TIFF_IFD_TYPE_USHORT,		  1,	"flash mode" },
	{	EXIF_FUJI_MAKER_NOTE_FLASH_STRENGTH,		TIFF_IFD_TYPE_SRATIONAL,	  1,	"flash strength" },
	{	EXIF_FUJI_MAKER_NOTE_MACRO,					TIFF_IFD_TYPE_USHORT,		  1,	"macro" },
	{	EXIF_FUJI_MAKER_NOTE_FOCUS_MODE,			TIFF_IFD_TYPE_USHORT,		  1,	"focus mode" },
	{	EXIF_FUJI_MAKER_NOTE_SLOW_SYNC,				TIFF_IFD_TYPE_USHORT,		  1,	"slow sync" },
	{	EXIF_FUJI_MAKER_NOTE_PICTURE_MODE,			TIFF_IFD_TYPE_USHORT,		  1,	"picture mode" },
	{	EXIF_FUJI_MAKER_NOTE_CONT_TAKE_BRACKET,		TIFF_IFD_TYPE_USHORT,		  1,	"contTake/bracket" },
	{	EXIF_FUJI_MAKER_NOTE_UNKNOWN,				TIFF_IFD_TYPE_USHORT,		  1,	"unknown" },
	{	EXIF_FUJI_MAKER_NOTE_BLUR_WARNING,			TIFF_IFD_TYPE_USHORT,		  1,	"blur warning" },
	{	EXIF_FUJI_MAKER_NOTE_FOCUS_WARNING,			TIFF_IFD_TYPE_USHORT,		  1,	"focus warning" },
	{	EXIF_FUJI_MAKER_NOTE_AE_WARNING,			TIFF_IFD_TYPE_USHORT,		  1,	"ae warning" },
	/* reserved */
	{	EXIF_FUJI_EOT, TIFF_IFD_TYPE_UNDEFINED, 0, NULL }
};

struct exif_nikon_field exif_nikon_fields[]=
{
	{	EXIF_NIKON_MAKER_NOTE_UNKNOWN1,				TIFF_IFD_TYPE_STRING,		  6,	"unknown1" },
	{	EXIF_NIKON_MAKER_NOTE_UNKNOWN2,				TIFF_IFD_TYPE_USHORT,		  1,	"unknown2" },
	{	EXIF_NIKON_MAKER_NOTE_UNKNOWN3,				TIFF_IFD_TYPE_USHORT,		  1,	"unknown3" },
	{	EXIF_NIKON_MAKER_NOTE_UNKNOWN4,				TIFF_IFD_TYPE_USHORT,		  1,	"unknown4" },
	{	EXIF_NIKON_MAKER_NOTE_UNKNOWN5,				TIFF_IFD_TYPE_USHORT,		  1,	"unknown5" },
	{	EXIF_NIKON_MAKER_NOTE_UNKNOWN6,				TIFF_IFD_TYPE_USHORT,		  1,	"unknown6" },
	{	EXIF_NIKON_MAKER_NOTE_UNKNOWN7,				TIFF_IFD_TYPE_URATIONAL,	  1,	"unknown7" },
	{	EXIF_NIKON_MAKER_NOTE_UNKNOWN8,				TIFF_IFD_TYPE_STRING,		 20,	"unknown8" },
	{	EXIF_NIKON_MAKER_NOTE_UNKNOWN9,				TIFF_IFD_TYPE_URATIONAL,	  1,	"unknown9" },
	{	EXIF_NIKON_MAKER_NOTE_UNKNOWN10,			TIFF_IFD_TYPE_USHORT,		  1,	"unknown10" },
	{	EXIF_NIKON_MAKER_NOTE_DATA_DUMP,			TIFF_IFD_TYPE_ULONG,		 30,	"data dump" },
	/* reserved */
	{	EXIF_NIKON_EOT, TIFF_IFD_TYPE_UNDEFINED, 0, NULL }
};

struct exif_sanyo_field exif_sanyo_fields[]=
{
	{	EXIF_SANYO_MAKER_NOTE_START_OFFSET,				TIFF_IFD_TYPE_USHORT,		1,	"makernote start offset" },
	{	EXIF_SANYO_MAKER_NOTE_JPEG_THUMBNAIL,			TIFF_IFD_TYPE_UNDEFINED,	-1,	"JPEG thumbnail data" },
	{	EXIF_SANYO_MAKER_NOTE_SPECIAL_MODE,				TIFF_IFD_TYPE_ULONG,		3,	"special mode" },
	{	EXIF_SANYO_MAKER_NOTE_JPEQ_QUALITY,				TIFF_IFD_TYPE_USHORT,		1,	"JPEG quality" },
	{	EXIF_SANYO_MAKER_NOTE_MACRO,					TIFF_IFD_TYPE_USHORT,		1,	"macro" },
	{	EXIF_SANYO_MAKER_NOTE_UNKNOWN1,					TIFF_IFD_TYPE_USHORT,		1,	"unknown1" },
	{	EXIF_SANYO_MAKER_NOTE_DIGITAL_ZOOM1,			TIFF_IFD_TYPE_URATIONAL,	1,	"digital zoom (1)" },
	{	EXIF_SANYO_MAKER_NOTE_UNKNOWN2,					TIFF_IFD_TYPE_URATIONAL,	1,	"unknown2" },
	{	EXIF_SANYO_MAKER_NOTE_UNKNOWN3,					TIFF_IFD_TYPE_USHORT,		6,	"unknown3" },
	{	EXIF_SANYO_MAKER_NOTE_SOFTWARE_RELEASE,			TIFF_IFD_TYPE_STRING,		-1,	"software release" },
	{	EXIF_SANYO_MAKER_NOTE_PICT_INFO,				TIFF_IFD_TYPE_STRING,		-1,	"pict info" },
	{	EXIF_SANYO_MAKER_NOTE_CAMERA_ID,				TIFF_IFD_TYPE_UNDEFINED,	32,	"camera ID" },
	{	EXIF_SANYO_MAKER_NOTE_SEQUENTIAL_SHOT_METHOD,	TIFF_IFD_TYPE_USHORT, 	 	1,	"sequential shot method" },
	{	EXIF_SANYO_MAKER_NOTE_WIDE_RANGE,				TIFF_IFD_TYPE_USHORT, 	 	1,	"wide range" },
	{	EXIF_SANYO_MAKER_NOTE_COLOUR_ADJUSTMENT_MODE,	TIFF_IFD_TYPE_USHORT, 	 	1,	"colour adjustment mode" },
	{	EXIF_SANYO_MAKER_NOTE_QUICK_SHOT,				TIFF_IFD_TYPE_USHORT, 	 	1,	"quick shot" },
	{	EXIF_SANYO_MAKER_NOTE_SELF_TIMER,				TIFF_IFD_TYPE_USHORT, 	 	1,	"self timer" },
	{	EXIF_SANYO_MAKER_NOTE_VOICE_MEMO,				TIFF_IFD_TYPE_USHORT, 	 	1,	"voice memo" },
	{	EXIF_SANYO_MAKER_NOTE_RECORD_SHUTTER_RELEASE,	TIFF_IFD_TYPE_USHORT, 	 	1,	"record shutter release" },
	{	EXIF_SANYO_MAKER_NOTE_FLICKER_REDUCE,			TIFF_IFD_TYPE_USHORT, 	 	1,	"flicker reduce" },
	{	EXIF_SANYO_MAKER_NOTE_OPTICAL_ZOOM,				TIFF_IFD_TYPE_USHORT, 	 	1,	"optical zoom" },
	{	EXIF_SANYO_MAKER_NOTE_DIGITAL_ZOOM2,			TIFF_IFD_TYPE_USHORT, 	 	1,	"digital zoom (2)" },
	{	EXIF_SANYO_MAKER_NOTE_LIGHT_SOURCE_SPECIAL,		TIFF_IFD_TYPE_USHORT, 	 	1,	"light source special" },
	{	EXIF_SANYO_MAKER_NOTE_RESAVED,					TIFF_IFD_TYPE_USHORT, 	 	1,	"resaved" },
	{	EXIF_SANYO_MAKER_NOTE_SCENE_SELECT,				TIFF_IFD_TYPE_USHORT, 	 	1,	"scene select" },
	{	EXIF_SANYO_MAKER_NOTE_MANUAL_FOCAL_DISTANCE,	TIFF_IFD_TYPE_URATIONAL,	1,	"manual focal distance" },
	{	EXIF_SANYO_MAKER_NOTE_SEQUENTIAL_SHOT_INTERVAL,	TIFF_IFD_TYPE_USHORT, 	 	1,	"sequential shot interval" },
	{	EXIF_SANYO_MAKER_NOTE_FLASH_MODE,				TIFF_IFD_TYPE_USHORT,  		1,	"flash mode" },
	{	EXIF_SANYO_MAKER_NOTE_PRINTIM_FLAGS,			TIFF_IFD_TYPE_UNDEFINED,	-1,	"printIM flags" },
	{	EXIF_SANYO_MAKER_NOTE_DATA_DUMP,				TIFF_IFD_TYPE_ULONG,		-1,	"data dump" },
	/* reserved */
	{	EXIF_SANYO_EOT, TIFF_IFD_TYPE_UNDEFINED, 0, NULL }
};


void exif_parse(unsigned char* buffer, const unsigned int bytes_left, struct parser_result* result)
{
	unsigned char* ptr;
	unsigned long int written_bytes;
	unsigned long int ifd_max;
	unsigned short int ifd_number;
	long int bytes;
	
	bool ret;

	/* exif parsing is still embryonic */
	return;

	ptr=buffer;
	bytes=bytes_left;
	written_bytes=0;
	ifd_number=0;
	ifd_max=0;
	ret=true;
	debug("Exif parser: %ld byte(s) to parse", bytes);

	/* parse Exif header */
	if (bytes<15)
	{
		ret=false;
		error("Exif parser: chunk underrun (%ld byte(s) remaining, 15 or more expected)",
				bytes);
	}
	else
	{
		char exif_marker[5];

		/* Exif marker expected */
		if (strcmp((char*)getraw(ptr, 0, (unsigned char*)exif_marker, 6),
									EXIF_MARKER_STRING)!=0)
		{
			ret=false;
			error("Exif parser: expected marker not matched");
		}
		else
		{
			bool little_endian;

			if (prefs.rewrite)
				written_bytes+=dump_rewrite(ptr, 6);
			bytes-=6;
			ptr+=6;

			/* decode the Image File Header */
			if (getlong(ptr, 0)==TIFF_BIG_ENDIAN_MARKER_STRING)
			{
				little_endian=false;
				debug("Exif parser: TIFF (big endian) marker found");
			}
			else
			if (getlong(ptr, 0)==TIFF_LITTLE_ENDIAN_MARKER_STRING)
			{
				little_endian=true;
				debug("Exif parser: TIFF (little endian) marker found");
			}
			else
			{
				ret=false;
				error("Exif parser: expected TIFF marker not matched");
			}

			if (ret)
			{
				unsigned char* base_offset;
				unsigned long int offset;

				base_offset=ptr;
				if (prefs.rewrite)
					written_bytes+=dump_rewrite(ptr, 4);
				bytes-=4;
				ptr+=4;

				/* offset of the IFD count value (should be =8) */
				offset=getrawlong(ptr, 0);
				if (prefs.rewrite)
					written_bytes+=dump_rewrite(ptr, 4);
				bytes-=4;
				ptr+=4;
				debug("Exif parser: start offset=%lu (0x%08x)", offset, offset);
				
				if (offset<8)
				{
					ret=false;
					error("Exif parser: unexpected initial IFD%lu offset (got %lu, expected >=8)",
							ifd_number, offset);
				}
				else
				{
					struct tiff_ifd_entry* ifd_entries;
					unsigned long int first_value_offset;
					unsigned long int next_ifd_offset;

					offset-=8;
					if (offset>0)
					{
						warning("Exif parser: unexpected initial IFD%lu offset (got %lu, expected 8)",
								ifd_number, offset);
						/* eat the unexpected bytes before first IFD */
						if (prefs.rewrite)
							written_bytes+=dump_rewrite(ptr, 4);
						bytes-=offset;
						ptr+=offset;
					}

					/* IFDs count */
					ifd_max=getrawword(ptr, 0);
					if (prefs.rewrite)
						written_bytes+=dump_rewrite(ptr, 2);
					bytes-=2;
					ptr+=2;
					debug("Exif parser: IFDs=%u (0x%04x)", ifd_max, ifd_max);

					if ((long int)(ifd_max*12)>bytes)
						error("Exif parser: IFD entries list size inconsistency (expected %lu*%lu=%lu, size is only %lu)",
								ifd_max, 12, ifd_max*12, bytes);

					/* TODO: allocate space to store all IFD entries so that we can
					   parse the entries list in a 2nd pass, in order to check IFD values 
					   (this would allow to remove some IFDs and to recalculate all offsets) */
					if (ifd_max>0)
					{
						ifd_entries=malloc(ifd_max*sizeof(struct tiff_ifd_entry));
						if (!ifd_entries)
							fatal_error("Exif parser: could not allocate space for Exif IFD entries list");
					}
					else
						ifd_entries=NULL;

					/* decode the IFDs */
					ifd_number=0;
					while ((bytes>0) && (ifd_number<ifd_max))
					{
						if (bytes<12)
						{
							ret=false;
							error("Exif parser: IFD%u entry underrun (%lu byte(s) left, 12 expected)",
									ifd_number, bytes, 12);
							if (prefs.rewrite)
								written_bytes+=dump_rewrite(ptr, bytes);
							bytes=0;
							ptr+=bytes;
							break;
						}

						ifd_entries[ifd_number].tag=getrawword(ptr, 0);
						ifd_entries[ifd_number].type=getrawword(ptr, 2);
						ifd_entries[ifd_number].count=getrawlong(ptr, 4);
						ifd_entries[ifd_number].value_offset=getrawlong(ptr, 8);

						if (!exif_match_tag(ifd_entries[ifd_number].tag))
							warning("Exif parser: unknown Exif tag 0x%04x",
									ifd_entries[ifd_number].tag);
						else
							debug("Exif parser: IFD%u found (tag=0x%02x, type=%u, count=%lu)",
									ifd_number,
									ifd_entries[ifd_number].tag,
									ifd_entries[ifd_number].type,
									ifd_entries[ifd_number].count);

						if (prefs.rewrite)
							written_bytes+=dump_rewrite(ptr, 12);
						bytes-=12;
						ptr+=12;
						ifd_number++;
					}

					/* offset to the next IFD if any (otherwise =0) */
					next_ifd_offset=getrawlong(ptr, 0);
					if (prefs.rewrite)
						written_bytes+=dump_rewrite(ptr, 4);
					bytes-=4;
					ptr+=4;
					debug("Exif parser: next IFD offset is %lu (0 means none)",
							next_ifd_offset);

					/* skip+rewrite values, determine if there are gaps between values
					   or between the 1st value and the IFD entries list  */
					first_value_offset=0;
					ifd_number=0;
					while ((bytes>0) && (ifd_number<ifd_max))
					{
						unsigned long int size;

						size=get_ifd_type_size(ifd_entries[ifd_number].type)
								*ifd_entries[ifd_number].count;
						if (size>4)
						{
							info("Exif parser: outline value for IFD%u (size=%lu byte(s))",
									ifd_number, size);

							/* currently matching the first outline value? */
							if (first_value_offset==0)
							{
								unsigned long int gap_size;

								/* check if there is a gap before the first outline value (rewrite it
								   if necessary */
								gap_size=(base_offset+ifd_entries[ifd_number].value_offset)-ptr;
								if (gap_size)
								{
									warning("Exif parser: there's a gap of %lu byte(s) before the first outline value from IFD%u",
											gap_size, ifd_number);
									if (prefs.rewrite)
										written_bytes+=dump_rewrite(ptr, gap_size);
									bytes-=gap_size;
									ptr+=gap_size;
								}
							}
							else
							{
								/* TODO: check if there is a gap before the next outline value
								   (rewrite it if necessary */
							}
							first_value_offset=ifd_entries[ifd_number].value_offset;

							/* TODO: dump/dump-full */

							bytes+=size;
							ptr+=size;
						}
						else
						{
							info("Exif parser: inline value for IFD%u (size=%lu byte(s))",
									ifd_number, size);
							/* TODO: make switch for Exif-specific IFDs (pointers)
							   and parse the nested chunks */

							/* TODO: dump/dump-full */
						}
						ifd_number++;
					}
					if (bytes<0)
					{
						ret=false;
						error("Exif parser: dataset underrun (overflow of %lu byte(s))",
							abs(bytes));
					}
					free(ifd_entries);
				}
			}
		}
	}
	debug("Exif parser: %u dataset(s) inspected, total length: %lu byte(s)",
			ifd_max, bytes_left-bytes);
	if (prefs.rewrite)
		debug("Exif parser: %lu byte(s) rewritten", written_bytes);
	result->parsed_bytes=bytes_left-bytes;
	result->written_bytes=written_bytes;
	result->ret=ret;
}

struct exif_ifd_field* exif_match_tag(const int tag)
{
	register unsigned int i;

	i=0;
	while (exif_ifd_fields[i].tag!=EXIF_IFD_EOT)
	{
		if (exif_ifd_fields[i].tag==tag)
			return(&exif_ifd_fields[i]);
		i++;
	}
	return(NULL);
}

void exif_dump_list(void)
{
}

unsigned int get_ifd_type_size(const enum TIFF_TYPE type)
{
	register unsigned int i;

	i=0;
	while (tiff_ifd_type_sizes[i].type!=TIFF_IFD_TYPE_UNKNOWN)
	{
		if (tiff_ifd_type_sizes[i].type==type)
			return(tiff_ifd_type_sizes[i].size);
		i++;
	}
	return(0);
}
