/**********************************************************************
 * tiff_find.c                                               April 2005
 * Horms                                             horms@verge.net.au
 *
 * tiff_find
 * Traverse TIFF directory structure and print to stdout
 * Copyright (C) 2006  Horms
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 * 02111-1307  USA
 *
 **********************************************************************/

/**********************************************************************
 * Notes
 *
 * This is inteded to be used to explore TIFF files. Or more to the
 * point, to allow me to explore some NEF files to write a loader for them.
 * I still recommend using libtiff, as it is both powerful and extendable,
 * and I plan to use that for the loader.
 *
 * Test Images: 
 *  Nikon D70, firmware 2.00, NEF (Raw)
 *  *** no other images tested, probably does not work ***
 *
 * References:
 *  ftp://ftp.sgi.com/graphics/tiff/TIFF6.ps.Z
 *  http://www.exif.org/Exif2-2.PDF
 *  http://www.tidalwave.it/infoglueDeliverLive/ViewPage.action?siteNodeId=186&languageId=1&contentId=-1#5
 *  http://www.remotesensing.org/libtiff/
 *  http://www.hh.iij4u.or.jp/~tabata/nef2.html (Japanese)
 *  http://homepage3.nifty.com/kamisaka/makernote/makernote_nikon.htm (Japanese)
 *  http://home.earthlink.net/~ritter/tiff/#spec
 *
 * TODO
 *   What is in the 0th sub directory of the root IDF?
 **********************************************************************/

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <stdlib.h>

#define LOG_NAME "tiff_find"

struct tiff_header {
	unsigned short endien;
	unsigned short magic;
	unsigned   offset;

	unsigned   base;   /* Byte where the TIFF starts in the file, 
			      non-zero fo embeded tiffs */
};

enum tiff_type_id {
	TIFF_TYPE_BYTE      = 1,
	TIFF_TYPE_ASCII     = 2,
	TIFF_TYPE_SHORT     = 3,
	TIFF_TYPE_LONG      = 4,
	TIFF_TYPE_RATIONAL  = 5,
	TIFF_TYPE_SBYTE     = 6,
	TIFF_TYPE_UNDEFINED = 7,
	TIFF_TYPE_SSHORT    = 8,
	TIFF_TYPE_SLONG     = 9,
	TIFF_TYPE_SRATIONAL = 10,
	TIFF_TYPE_FLOAT     = 11,
	TIFF_TYPE_DOUBLE    = 12
};

struct tiff_type {
	enum tiff_type_id id;
	const char *str;
	int width;            /* Only 0 < 8 */
};

static const struct tiff_type tiff_base_type[] = {
	{ TIFF_TYPE_BYTE      , "byte",       1 },
	{ TIFF_TYPE_ASCII     , "ascii",      1 },
	{ TIFF_TYPE_SHORT     , "short",      2 },
	{ TIFF_TYPE_LONG      , "long",       4 },
	{ TIFF_TYPE_RATIONAL  , "rational",   8 },
	{ TIFF_TYPE_SBYTE     , "sbyte",      1 },
	{ TIFF_TYPE_UNDEFINED , "undefined",  1 },
	{ TIFF_TYPE_SSHORT    , "sshort",     2 },
	{ TIFF_TYPE_SLONG     , "slong",      4 },
	{ TIFF_TYPE_SRATIONAL , "strational", 8 },
	{ TIFF_TYPE_FLOAT     , "float",      4 },
	{ TIFF_TYPE_DOUBLE    , "double",     8 }
};

struct tiff_tag {
	unsigned id;
	const char *str;
};

/* Tiff 6.0 Tags (incomlete) */
#define TIFF_TAG_SUBFILETYPE                254
#define TIFF_TAG_IMAGEWIDTH                 256
#define TIFF_TAG_IMAGELENGTH                257
#define TIFF_TAG_BITS_PER_SAMPLE            258
#define TIFF_TAG_COMPRESSION                259
#define TIFF_TAG_PHOTOMETRIC_INTERPRETATION 262
#define TIFF_TAG_MANUFACTURER               271
#define TIFF_TAG_MODEL                      272
#define TIFF_TAG_STRIP_OFFSETS              273
#define TIFF_TAG_ORIENTATION                274
#define TIFF_TAG_SAMPLESPERPIXEL            277
#define TIFF_TAG_ROWSPERSTRIP               278
#define TIFF_TAG_BYTESPERSTRIP              279
#define TIFF_TAG_XRESOLUTION                282
#define TIFF_TAG_YRESOLUTION                283
#define TIFF_TAG_PLANARCONFIGURATION        284
#define TIFF_TAG_RESOLUTIONUNIT             296
#define TIFF_TAG_SOFTWARE                   305
#define TIFF_TAG_CREATIONDATEANDTIME        306
#define TIFF_TAG_SUBIFD                     330
#define TIFF_TAG_COLORIMETRY                532

/* EXIF Tags 2.2 (incomplete) */
#define EXIF_TAG_EXIFID                     34665
#define EXIF_TAG_DATETIMEORIGINAL           36867
#define EXIF_TAG_DATETIMEDIGITISED          36868
#define EXIF_TAG_EXPOSUREBIAS               37381
#define EXIF_TAG_METERINGMODE               37383
#define EXIF_TAG_LIGHTSOURCE                37384
#define EXIF_TAG_FLASH                      37385
#define EXIF_TAG_LENSEFOCALLENGTH           37386
#define EXIF_TAG_MAKERNOTE                  37500
#define EXIF_TAG_USERCOMMENT                37510
#define EXIF_TAG_SUBSECTIME                 37520
#define EXIF_TAG_SUBSECTIMEORIGINAL         37521
#define EXIF_TAG_SUBSECTIMEDIGITISED        37522
#define EXIF_TAG_SENSINGMETHOD              41495
#define EXIF_TAG_FILESOURCE                 41728
#define EXIF_TAG_SCEENETYPE                 41729
#define EXIF_TAG_CFAPATTERN                 41730
#define EXIF_TAG_CUSTOMRENDERED             41985
#define EXIF_TAG_EXPOSUREMODE               41986
#define EXIF_TAG_WHITEBALANCE               41987
#define EXIF_TAG_DIGITALZOOMRATIO           41988
#define EXIF_TAG_FOCALLENGTHIN35MMFILM      41989
#define EXIF_TAG_SENSECAPTURETYPE           41990
#define EXIF_TAG_GAINCONTROL                41991
#define EXIF_TAG_CONTRAST                   41992
#define EXIF_TAG_SATURATION                 41993
#define EXIF_TAG_SHARPNESS                  41994
#define EXIF_TAG_SUBJECTDISTANCERANGE       41996

/* Tags that seem to be NEF Specific */
#define NEF_TAG_CFAREPEATPATTERNDIM         33421
#define NEF_TAG_CFAPATTERNDIM               33422
#define NEF_TAG_CFAPATTERN                  37399
#define NEF_TAG_TIFFEPSTANDARDID            37398

/* Tags in the Nikon Maker Note */
#define NEF_TAG_FIRMWAREVERSION             1
#define NEF_TAG_ISO                         2
#define NEF_TAG_COLOURMODE                  3
#define NEF_TAG_QUALITY                     4
#define NEF_TAG_WHITEBALANCE                5
#define NEF_TAG_SHARPENING                  6
#define NEF_TAG_FOCUSMODE                   7
#define NEF_TAG_FLASHSETTING                8
#define NEF_TAG_AUTOFLASHMODE               9
#define NEF_TAG_WHITEBALANCEFINE           11
#define NEF_TAG_WHITEBALANCERBCOEFICIENTS  12
#define NEF_TAG_UNKNOWN13                  13
#define NEF_TAG_EXPOSUREDIFFERENCE         14
#define NEF_TAG_ISOSELECTION               15
#define NEF_TAG_DATADUMP                   16
#define NEF_TAG_THUMBNAILOFFSET            17
#define NEF_TAG_FLASHCOMPENSATION          18
#define NEF_TAG_ISOREQUESTED               19
#define NEF_TAG_NDFIMAGEBOUNDRY            22
#define NEF_TAG_UNKNOWN23                  23
#define NEF_TAG_FLASHBRACKETCOMPENSATION   24
#define NEF_TAG_AEBRACKETCOMPENSATION      25
#define NEF_TAG_TONECOMPENSATION          129
#define NEF_TAG_LENSETYPE                 131
#define NEF_TAG_LENSERANGE                132
#define NEF_TAG_FLASHTYPE                 135
#define NEF_TAG_AFFOCUSPOSITION           136
#define NEF_TAG_BRACKETING                137
#define NEF_TAG_LENSEFSTOP                139
#define NEF_TAG_CURVE                     140
#define NEF_TAG_COLOURMODE2               141
#define NEF_TAG_LIGHTINGTYPE              142
#define NEF_TAG_SCENEMODE                 143
#define NEF_TAG_LIGHTTYPE                 144
#define NEF_TAG_HUE                       146
#define NEF_TAG_FLASHUNKNOWNINFO          147
#define NEF_TAG_SATURATION                149
#define NEF_TAG_COMPRESSIONDATA           150
#define NEF_TAG_BLOCK151                  151
#define NEF_TAG_LENSEINFO                 152
#define NEF_TAG_BAYERUNITCOUNT            153
#define NEF_TAG_SENSORPIXELSIZE           154
#define NEF_TAG_CAMERASERIALNUMBER        160
#define NEF_TAG_NDFLENGTH                 164
#define NEF_TAG_SHUTTERCOUNT              167
#define NEF_TAG_UNKNOWN168                168
#define NEF_TAG_IMAGEOPTIMISATION         169
#define NEF_TAG_SATURATION2               170
#define NEF_TAG_VARIPROGRAM               171

static const struct tiff_tag idf_tag[] = {
	{ TIFF_TAG_SUBFILETYPE,                 "Subfile Type"},
	{ TIFF_TAG_IMAGEWIDTH,                  "Image Width"},
	{ TIFF_TAG_IMAGELENGTH,                 "Image Length"},
	{ TIFF_TAG_BITS_PER_SAMPLE,             "Bits Per Sample"},
	{ TIFF_TAG_COMPRESSION,                 "Compression"},
	{ TIFF_TAG_PHOTOMETRIC_INTERPRETATION,  "Photometric Interpretation"},
	{ TIFF_TAG_MANUFACTURER,                "Manufacturer"},
	{ TIFF_TAG_MODEL,                       "Model"},
	{ TIFF_TAG_STRIP_OFFSETS,               "Strip Offsets"},
	{ TIFF_TAG_ORIENTATION,                 "Orientation"},
	{ TIFF_TAG_SAMPLESPERPIXEL,             "Samples Per Pixel"},
	{ TIFF_TAG_ROWSPERSTRIP,                "Rows Per Strip"},
	{ TIFF_TAG_BYTESPERSTRIP,               "Bytes Per Strip"},
	{ TIFF_TAG_XRESOLUTION,                 "X Resolution"},
	{ TIFF_TAG_YRESOLUTION,                 "Y Resolution"},
	{ TIFF_TAG_PLANARCONFIGURATION,         "Planar Configuration"},
	{ TIFF_TAG_RESOLUTIONUNIT,              "Resolution Unit"},
	{ TIFF_TAG_SOFTWARE,                    "Software"},
	{ TIFF_TAG_CREATIONDATEANDTIME,         "Creation Date and Time"},
	{ TIFF_TAG_SUBIFD,                      "SubIFD"},
	{ TIFF_TAG_COLORIMETRY,                 "Colorimetry"},
	{ NEF_TAG_CFAREPEATPATTERNDIM,          "CFARepeatPatternDIM"},
	{ NEF_TAG_CFAPATTERNDIM,                "CFAPatternDIM"},
	{ NEF_TAG_CFAPATTERN,                   "CFAPattern"},
	{ EXIF_TAG_EXIFID,                      "EXIFID"},
	{ EXIF_TAG_DATETIMEDIGITISED,           "Date Time Digitised"},
	{ NEF_TAG_TIFFEPSTANDARDID,             "TIFF/EP StandardId"}
};

static const struct tiff_tag exif_idf_tag[] = {
	{ EXIF_TAG_DATETIMEDIGITISED,     "Date and Time Digitised" },
	{ EXIF_TAG_EXPOSUREBIAS,          "Exposure Bias" },
	{ EXIF_TAG_METERINGMODE,          "Metering Mode" },
	{ EXIF_TAG_LIGHTSOURCE,           "Light Source" },
	{ EXIF_TAG_FLASH,                 "Flash" },
	{ EXIF_TAG_LENSEFOCALLENGTH,      "Lense Focal Length" },
	{ EXIF_TAG_MAKERNOTE,             "Maker Note" },
	{ EXIF_TAG_USERCOMMENT,           "User Comment" },
	{ EXIF_TAG_SUBSECTIME,            "Sub-Second Time" },
	{ EXIF_TAG_SUBSECTIMEORIGINAL,    "Sub-Second Time Original" },
	{ EXIF_TAG_SUBSECTIMEDIGITISED,   "Sub-Second Time Digitised" },
	{ EXIF_TAG_SENSINGMETHOD,         "Sensing Method" },
	{ EXIF_TAG_FILESOURCE,            "File Source" },
	{ EXIF_TAG_SCEENETYPE,            "Sceene Type" },
	{ EXIF_TAG_CFAPATTERN,            "CFA Pattern" },
	{ EXIF_TAG_CUSTOMRENDERED,        "Custom Rendered" },
	{ EXIF_TAG_EXPOSUREMODE,          "Exposure Mode" },
	{ EXIF_TAG_WHITEBALANCE,          "White Balance" },
	{ EXIF_TAG_DIGITALZOOMRATIO,      "Digital Zoom Ratio" },
	{ EXIF_TAG_FOCALLENGTHIN35MMFILM, "Focal Length in 32mm Film" },
	{ EXIF_TAG_SENSECAPTURETYPE,      "Sense Capture Type" },
	{ EXIF_TAG_GAINCONTROL,           "Gain Control" },
	{ EXIF_TAG_CONTRAST,              "Contrast" },
	{ EXIF_TAG_SATURATION,            "Saturation" },
	{ EXIF_TAG_SHARPNESS,             "Sharpness" },
	{ EXIF_TAG_SUBJECTDISTANCERANGE,  "Subject Distance Range" }
};


static const struct tiff_tag maker_note_idf_tag[] = {
	{ NEF_TAG_FIRMWAREVERSION,           "Firmware Version" },
	{ NEF_TAG_ISO,                       "ISO" },
	{ NEF_TAG_COLOURMODE,                "Colour Mode" },
	{ NEF_TAG_QUALITY,                   "Quality" },
	{ NEF_TAG_WHITEBALANCE,              "White Balance" },
	{ NEF_TAG_SHARPENING,                "Sharpening" },
	{ NEF_TAG_FOCUSMODE,                 "Focus Mode" },
	{ NEF_TAG_FLASHSETTING,              "Flash Setting" },
	{ NEF_TAG_AUTOFLASHMODE,             "Auto Flash Mode" },
	{ NEF_TAG_WHITEBALANCEFINE,          "White Balance Fine" },
	{ NEF_TAG_WHITEBALANCERBCOEFICIENTS, "White Balance RB Coeficients" },
	{ NEF_TAG_UNKNOWN13,                 "Unknown 13" },
	{ NEF_TAG_EXPOSUREDIFFERENCE,        "Exposure Difference" },
	{ NEF_TAG_ISOSELECTION,              "ISO Selection" },
	{ NEF_TAG_DATADUMP,                  "Data Dump" },
	{ NEF_TAG_THUMBNAILOFFSET,           "Thumb Nail Offset" },
	{ NEF_TAG_FLASHCOMPENSATION,         "Flash Compensation" },
	{ NEF_TAG_ISOREQUESTED,              "ISO Requested" },
	{ NEF_TAG_NDFIMAGEBOUNDRY,           "NFD Image Boundry" },
	{ NEF_TAG_UNKNOWN23,                 "Unknown 23" },
	{ NEF_TAG_FLASHBRACKETCOMPENSATION,  "Flash Bracket Compensation" },
	{ NEF_TAG_AEBRACKETCOMPENSATION,     "AE Bracket Compensation" },
	{ NEF_TAG_TONECOMPENSATION,          "Tone Compenstation" },
	{ NEF_TAG_LENSETYPE,                 "Lense Type" },
	{ NEF_TAG_LENSERANGE,                "Lense Range" },
	{ NEF_TAG_FLASHTYPE,                 "Flash Type" },
	{ NEF_TAG_AFFOCUSPOSITION,           "AF Focus Position" },
	{ NEF_TAG_BRACKETING,                "Bracketing" },
	{ NEF_TAG_LENSEFSTOP,                "Lense F Stop" },
	{ NEF_TAG_CURVE,                     "Curve" },
	{ NEF_TAG_COLOURMODE2,               "Colour Mode 2" },
	{ NEF_TAG_LIGHTINGTYPE,              "Lighting Type" },
	{ NEF_TAG_SCENEMODE,                 "Scene Mode" },
	{ NEF_TAG_LIGHTTYPE,                 "Light Type" },
	{ NEF_TAG_HUE,                       "Hue" },
	{ NEF_TAG_FLASHUNKNOWNINFO,          "Flash Unknown Info" },
	{ NEF_TAG_SATURATION,                "Saturation" },
	{ NEF_TAG_COMPRESSIONDATA,           "Compression Data" },
	/* I believe that Block 151 relates to the encryption of
	 * whitebalance by the DX2, but I neither have a DX2, nor
	 * an image produced by it to test this. */
	{ NEF_TAG_BLOCK151,                  "Block 151" },
	{ NEF_TAG_LENSEINFO,                 "Lense Info" },
	{ NEF_TAG_BAYERUNITCOUNT,            "Bayer Unit Count" },
	{ NEF_TAG_SENSORPIXELSIZE,           "Sensor Pixel Size" },
	{ NEF_TAG_CAMERASERIALNUMBER,        "Camera Serial Number" },
	{ NEF_TAG_NDFLENGTH,                 "NFD Length" },
	{ NEF_TAG_SHUTTERCOUNT,              "Shutter Count" },
	{ NEF_TAG_UNKNOWN168,                "Unknown 168" },
	{ NEF_TAG_IMAGEOPTIMISATION,         "Image Optimisation" },
	{ NEF_TAG_SATURATION2,               "Saturation 2" },
	{ NEF_TAG_VARIPROGRAM,               "Vari Program" }
};

static int
tiff_parse(FILE *f, unsigned offset);

static int
tiff_parse_custom(FILE *f, unsigned offset, const struct tiff_tag *tag_list, 
		  size_t tag_count, unsigned no_recurse);

static int
tiff_parse_ifd(FILE *f, const struct tiff_header *hdr, unsigned suboffset,
	       unsigned no_recurse);

static int
tiff_parse_exif_ifd(FILE *f, const struct tiff_header *hdr, 
		    unsigned exifid);

static int
tiff_parse_maker_note_ifd(FILE *f, const struct tiff_header *hdr, 
			 unsigned maker_note);

static int
tiff_parse_ifd_custom(FILE *f, const struct tiff_header *hdr, 
		     unsigned suboffset, const struct tiff_tag *tag_list, 
		     size_t tag_count, unsigned no_recurse);

static size_t
tiff_read(FILE*f, unsigned char *buf, size_t size)
{
	/* int i; */
	size_t status;

	status = fread(buf, 1, size, f);
	if (status != size) {
		if (feof(f))
			fprintf(stderr, "tiff_read: fread: eof\n");
		else if (ferror(f))
			perror("tiff_read: fread");
		else
			fprintf(stderr, "tiff_read: short read request=%u "
				"read=%u\n", size, status);
	}

	/*
	for (i = 0; i < status; i++) {
		printf("%02x ", *(buf + i) & 0xff);
		if ((i % 16) == 15)
			printf("\n");
		else if ((i % 8) == 7)
			printf(" ");
	}
	printf("\n");
	*/

	return status;
}

static int
tiff_seek(FILE *f, const struct tiff_header *hdr, unsigned offset)
{
	if (hdr) {
		if (~0UL - hdr->base < offset) {
			fprintf(stderr, "tiff_seek: overflow\n");
			return -1;
		}
		offset += hdr->base;
	}
	if (fseek(f, offset, SEEK_SET) < 0) {
		perror("tiff_seek: fseek");
		return -1;
	}
	return 0;
}

/* Assumes big endian */
static unsigned 
buftol(const unsigned char *buf, const struct tiff_header *hdr)
{
	unsigned ret;

	memcpy(&ret, buf, sizeof(ret));
	if (hdr->endien == 0x4D4D)
		return ntohl(ret);
	return ret;
}

static unsigned short 
buftos(const unsigned char *buf, const struct tiff_header *hdr)
{
	unsigned short ret;

	memcpy(&ret, buf, sizeof(ret));
	if (hdr->endien == 0x4D4D)
		return ntohs(ret);
	return ret;
}

static void
idf_type_print(enum tiff_type_id type, const unsigned char *buf, 
	       const struct tiff_header *hdr)
{
	float f;
	double d;

	switch (type) 
	{
	case TIFF_TYPE_BYTE:
		printf("%u", *buf & 0xff);
		break;
	case TIFF_TYPE_ASCII: 
		if (!(*buf & 0xff))
			break;
		else if (isprint(*buf & 0xff))
			printf("%c", *buf & 0xff);
		else
			printf("\\%03o", *buf & 0xff); /* Probably illegal */
		break;
	case TIFF_TYPE_SHORT: 
		printf("%u", buftos(buf, hdr));
		break;
	case TIFF_TYPE_LONG: 
		printf("%u", buftol(buf, hdr));
		break;
	case TIFF_TYPE_RATIONAL: 
		printf("%u/%u", buftol(buf, hdr), buftol(buf+4, hdr));
		break;
	case TIFF_TYPE_SBYTE:
		printf("%d", (char)(*buf & 0xff));
		break;
	case TIFF_TYPE_UNDEFINED:
		printf("0x%02x", *buf & 0xff);
		break;
	case TIFF_TYPE_SSHORT:
		printf("%d", (short)buftos(buf, hdr));
		break;
	case TIFF_TYPE_SLONG:
		printf("%d", (int)buftol(buf, hdr));
		break;
	case TIFF_TYPE_SRATIONAL: 
		printf("%d/%d", (int)buftol(buf, hdr), 
		       (int)buftol(buf+4, hdr));
		break;
	case TIFF_TYPE_FLOAT:
		memcpy(&f, buf, 4); /* XXX: Does that work ? */
		printf("%f", f);
		break;
	case TIFF_TYPE_DOUBLE:
		memcpy(&d, buf, d); /* XXX: Does that work ? */
		printf("%lf", d);
		break;
	}
}

static unsigned
idf_type_width(enum tiff_type_id type)
{
	int i;
	for (i = 0; i < sizeof(tiff_base_type)/sizeof(tiff_base_type[0]); i++)
		if (tiff_base_type[i].id == type)
			return tiff_base_type[i].width;
	return 0;
}

static unsigned
idf_value_width(unsigned short type, unsigned count)
{
	unsigned long long res;

	res = count * idf_type_width(type);
	if (res > ~0U)
		return 0;

	return res;
}

static const char *
idf_type_str(unsigned short type)
{
	int i;
	for (i = 0; i < sizeof(tiff_base_type)/sizeof(tiff_base_type[0]); i++)
		if (tiff_base_type[i].id == type)
			return tiff_base_type[i].str;
	return "Unknown";
}

static char idf_tag_str_buf[6];
static const char *
idf_tag_str(unsigned short tag, const struct tiff_tag *tag_list, 
	    size_t tag_count)
{
	int i;

	for (i = 0; i < tag_count; i++)
		if (tag_list[i].id == tag)
			return tag_list[i].str;

	snprintf(idf_tag_str_buf, sizeof(idf_tag_str_buf), "%u", tag);
	return idf_tag_str_buf;
}

static int
tiff_parse_header(FILE *f, struct tiff_header *hdr, unsigned offset)
{
	unsigned char buf[8];
	int status = 0;

	hdr->base = offset; 

	if (tiff_seek(f, hdr, 0) < 0)
		goto error;
	if (tiff_read(f, buf, 8) != 8)
		goto error;

	printf("*** Tiff Header\n");
	printf("Byte Order: %02x%02x    ", buf[0] & 0xff, buf[1] & 0xff);
	memcpy(&hdr->endien, buf, 2);
	if (hdr->endien == 0x4949)
		printf(" (Little Endien)\n");
	else if (hdr->endien == 0x4D4D)
		printf(" (Big Endien)\n");
	else {
		printf(" (Error)\n");
		status = 1;
	}

	printf("Magic:      %02x%02x    ", buf[2] & 0xff, buf[3] & 0xff);
	if (buf[2] == 0 && buf[3] == 42)
		printf(" (42)\n");
	else {
		printf(" (Error)\n");
		status = 1;
	}
	memcpy(&hdr->magic, buf+2, 2);

	hdr->offset = buftol(buf+4, hdr);
	printf("Offset:     %02x%02x%02x%02x (%d)\n", buf[4] & 0xff, 
	       buf[5] & 0xff, buf[6] & 0xff, buf[7] & 0xff,
	       hdr->offset);

	printf("\n");

	return status;

error:
	fprintf(stderr, "Error parsing tiff header: tiff_offset=%u\n", offset);
	return -1;
}

static int
__tiff_parse_ifd_custom(FILE *f, const struct tiff_header *hdr, 
		       unsigned offset, const struct tiff_tag *tag_list, 
		       size_t tag_count, unsigned no_recurse)
{
	unsigned char buf[12];
	unsigned fields, count, value, value_width, type_width, 
		     value_offset, orignal_offset, i, j, exifid = 0,
		     maker_note = 0, *subifd = NULL, *p;
	unsigned short type, tag;

	orignal_offset = offset;
	if (tiff_seek(f, hdr, offset ) < 0)
		goto error;
	if (tiff_read(f, buf, 2) != 2)
		goto error;
	fields = buftos(buf, hdr);
	printf("** IDF at %d, %d fields\n", offset, fields);
	offset += 2;

	for (i = 0; i < fields; i++) {
		printf("\n");
		printf("Field:          %u\n", i);
		if (tiff_seek(f, hdr, offset) < 0)
			perror("fseek");
		if (tiff_read(f, buf, 12) != 12)
			goto error;
		value_offset = offset + 8;
		offset += 12;

		tag = buftos(buf, hdr);
		printf("Tag:            %02x%02x     (%u=%s)\n", buf[0], 
		       buf[1], tag, idf_tag_str(tag, tag_list, tag_count));
		type = buftos(buf+2, hdr);
		printf("Type:           %02x%02x     (%u=%s)\n", 
		       buf[2], buf[3], type, idf_type_str(type));
		count = buftol(buf+4, hdr);
		printf("Count:          %02x%02x%02x%02x (%u)\n", 
		       buf[4], buf[5], buf[5], buf[7], count);
		value = buftol(buf+8, hdr);
		value_width = idf_value_width(type, count);
		type_width = idf_type_width(type);
		switch (tag) {
		case EXIF_TAG_EXIFID:
			exifid = value;
			break;
		case EXIF_TAG_MAKERNOTE:
			maker_note = value;
			break;
		case TIFF_TAG_SUBIFD:
			if (subifd) {
				fprintf(stderr, "duplicate SUBIFD");
				goto error;
			}
			printf("found subid\n");
			subifd = malloc((count + 1) * type_width);
			if (!subifd) {
				perror("malloc");
				goto error;
			}
			subifd[count - 1] = 0;
			break;
		}
		if (value_width <= 4)
			printf("Raw Value:      %02x%02x%02x%02x\n", 
			       buf[8], buf[9], buf[10], buf[11]);
		else {
			printf("Offset:         %02x%02x%02x%02x (%u)\n", 
			       buf[8], buf[9], buf[10], buf[11], value);
			value_offset = value;
		}
		if (tag == EXIF_TAG_MAKERNOTE)
			continue;
		printf("Value:          " );
		if (tiff_seek(f, hdr, value_offset) < 0)
			goto error;
		for (j = 0; j < count; j++) {
			if (tiff_read(f, buf, type_width) != type_width)
				goto error;
			idf_type_print(type, buf, hdr);
			if (j + 1 < count && type != TIFF_TYPE_ASCII)
				printf(", ");
			if (tag == TIFF_TAG_SUBIFD)
				subifd[j] = buftol(buf, hdr);
		}
		printf("\n");
	}
	printf("\n");

	if (no_recurse)
		goto out;

	if (exifid) {
		printf("** Exifid of IDF at %d\n", orignal_offset);
		if (tiff_parse_exif_ifd(f, hdr, exifid) < 0)
			goto error;
	}

	if (maker_note) {
		printf("** Maker Note of IDF at %d\n", orignal_offset);
		if (tiff_parse_maker_note_ifd(f, hdr, maker_note) < 0)
			goto error;
	}

	if (subifd) {
		p = subifd;
		while (*p) {
			printf("** SubIFD of IDF at %d\n", orignal_offset);
			if (tiff_parse_ifd(f, hdr, *p, 0))
				goto error;
			p++;
		}
	}

out:
	return fields;

error:
	fprintf(stderr, "Error parsing ifd: tiff_offset=%u ifd_offset=%u\n", 
		hdr->base, orignal_offset);
	if (subifd)
		free(subifd);
	return -1;
}

static int
tiff_parse_ifd_custom(FILE *f, const struct tiff_header *hdr, 
		     unsigned suboffset, const struct tiff_tag *tag_list, 
		     size_t tag_count, unsigned no_recurse)
{
	unsigned char buf[4];
	unsigned offset;
	int fields;

	if (suboffset)
		offset = suboffset;
	else
		offset = hdr->offset;
	
	do {
		fields = __tiff_parse_ifd_custom(f, hdr, offset, tag_list, 
						tag_count, no_recurse);
		if (fields < 0)
			return -1;
		if (no_recurse)
			break;
		offset += fields * 12;
		if (tiff_seek(f, hdr, offset) < 0)
			return -1;
		if (tiff_read(f, buf, 4) != 4)
			return -1;
		offset = buftol(buf, hdr);
		printf("* Next Offset:   %02x%02x%02x%02x (%u%s)\n", 
		       buf[0], buf[1], buf[2], buf[3], offset,
		       offset ? "" : "=end");
		printf("\n");
	} while (offset);

	return 0;
}

static int
tiff_parse_ifd(FILE *f, const struct tiff_header *hdr, unsigned suboffset,
	       unsigned no_recurse)
{
	return tiff_parse_ifd_custom(f, hdr, suboffset, idf_tag,
  				    sizeof(idf_tag) / sizeof(idf_tag[1]),
				    no_recurse);
}

static int
tiff_parse_exif_ifd(FILE *f, const struct tiff_header *hdr, unsigned exifid)
{
	return tiff_parse_ifd_custom(f, hdr, exifid, exif_idf_tag,
  				    sizeof(exif_idf_tag) /
				    sizeof(exif_idf_tag[1]), 0);
}

static int
tiff_parse_maker_note_ifd(FILE *f, const struct tiff_header *hdr, 
			 unsigned maker_note)
{
	unsigned char buf[11];

	if (tiff_seek(f, hdr, maker_note) < 0)
		return -1;
	if (tiff_read(f, buf, 10) != 10)
		return 0;
	if (memcmp(buf, "Nikon", 5))
		return tiff_parse_ifd(f, hdr, maker_note, 0);

	buf[10] = 0;
	printf("* Embeded Tiff. ID: %s\n", buf);
	/* On the D70 at least the embeded tiff does not have
	 * a terminating IDF */
	return tiff_parse_custom(f, maker_note + 10, maker_note_idf_tag, 
				 sizeof(maker_note_idf_tag)/
				 sizeof(maker_note_idf_tag[0]), 1);
}

static int
tiff_parse_custom(FILE *f, unsigned offset, const struct tiff_tag *tag_list, 
		  size_t tag_count, unsigned no_recurse)
{
	struct tiff_header hdr;

	if (tiff_parse_header(f, &hdr, offset) < 0)
		return -1;
	if (tiff_parse_ifd_custom(f, &hdr, hdr.offset, tag_list, tag_count,
				  no_recurse) < 0)
		return -1;

	return 0;
}

static int
tiff_parse(FILE *f, unsigned offset)
{
	return tiff_parse_custom(f, offset, idf_tag, 
				 sizeof(idf_tag) / sizeof(idf_tag[1]), 0);
}

int
main(int argc, char **argv, char **environment)
{
	FILE *f = NULL;
	int status = 1;

	if (argc != 2) {
		fprintf(stderr, "usage: tiff_read FILE\n");
		goto error;
	}

	if (! (f = fopen(argv[1], "r")) ) {
		perror("main: fopen");
		goto error;
	}

	if (tiff_parse(f, 0) < 0)
		goto error;

	status = 0;
error:
	if (f)
		fclose(f);
	return status;
}

