shun-iwasawa fdbab5
/*-------------------------------------------------------------
shun-iwasawa fdbab5
tiio_jpg_exif.cpp
shun-iwasawa fdbab5
Based on source code of a public domain software "Exif Jpeg header manipulation
shun-iwasawa fdbab5
tool (jhead)" by Matthias Wandel.
shun-iwasawa fdbab5
For now it is used only for obtaining resolution values.
shun-iwasawa fdbab5
http://www.sentex.net/~mwandel/jhead/
shun-iwasawa fdbab5
-------------------------------------------------------------*/
shun-iwasawa fdbab5
#include "tiio_jpg_exif.h"
shun-iwasawa fdbab5
#include <iostream></iostream>
shun-iwasawa fdbab5
#include <string.h></string.h>
shun-iwasawa fdbab5
shun-iwasawa fdbab5
// for debug
shun-iwasawa fdbab5
#define ShowTags 0
shun-iwasawa fdbab5
#define DumpExifMap 0
shun-iwasawa fdbab5
shun-iwasawa fdbab5
namespace {
shun-iwasawa fdbab5
shun-iwasawa fdbab5
typedef unsigned char uchar;
shun-iwasawa fdbab5
const int BytesPerFormat[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
shun-iwasawa fdbab5
//--------------------------------------------------------------------------
shun-iwasawa fdbab5
// Describes tag values
shun-iwasawa fdbab5
const int TAG_X_RESOLUTION    = 0x011A;
shun-iwasawa fdbab5
const int TAG_Y_RESOLUTION    = 0x011B;
shun-iwasawa fdbab5
const int TAG_RESOLUTION_UNIT = 0x0128;
shun-iwasawa fdbab5
#ifdef ReadAllTags
shun-iwasawa fdbab5
const int TAG_INTEROP_INDEX          = 0x0001;
shun-iwasawa fdbab5
const int TAG_INTEROP_VERSION        = 0x0002;
shun-iwasawa fdbab5
const int TAG_IMAGE_WIDTH            = 0x0100;
shun-iwasawa fdbab5
const int TAG_IMAGE_LENGTH           = 0x0101;
shun-iwasawa fdbab5
const int TAG_BITS_PER_SAMPLE        = 0x0102;
shun-iwasawa fdbab5
const int TAG_COMPRESSION            = 0x0103;
shun-iwasawa fdbab5
const int TAG_PHOTOMETRIC_INTERP     = 0x0106;
shun-iwasawa fdbab5
const int TAG_FILL_ORDER             = 0x010A;
shun-iwasawa fdbab5
const int TAG_DOCUMENT_NAME          = 0x010D;
shun-iwasawa fdbab5
const int TAG_IMAGE_DESCRIPTION      = 0x010E;
shun-iwasawa fdbab5
const int TAG_MAKE                   = 0x010F;
shun-iwasawa fdbab5
const int TAG_MODEL                  = 0x0110;
shun-iwasawa fdbab5
const int TAG_SRIP_OFFSET            = 0x0111;
shun-iwasawa fdbab5
const int TAG_ORIENTATION            = 0x0112;
shun-iwasawa fdbab5
const int TAG_SAMPLES_PER_PIXEL      = 0x0115;
shun-iwasawa fdbab5
const int TAG_ROWS_PER_STRIP         = 0x0116;
shun-iwasawa fdbab5
const int TAG_STRIP_BYTE_COUNTS      = 0x0117;
shun-iwasawa fdbab5
const int TAG_PLANAR_CONFIGURATION   = 0x011C;
shun-iwasawa fdbab5
const int TAG_TRANSFER_FUNCTION      = 0x012D;
shun-iwasawa fdbab5
const int TAG_SOFTWARE               = 0x0131;
shun-iwasawa fdbab5
const int TAG_DATETIME               = 0x0132;
shun-iwasawa fdbab5
const int TAG_ARTIST                 = 0x013B;
shun-iwasawa fdbab5
const int TAG_WHITE_POINT            = 0x013E;
shun-iwasawa fdbab5
const int TAG_PRIMARY_CHROMATICITIES = 0x013F;
shun-iwasawa fdbab5
const int TAG_TRANSFER_RANGE         = 0x0156;
shun-iwasawa fdbab5
const int TAG_JPEG_PROC              = 0x0200;
shun-iwasawa fdbab5
const int TAG_THUMBNAIL_OFFSET       = 0x0201;
shun-iwasawa fdbab5
const int TAG_THUMBNAIL_LENGTH       = 0x0202;
shun-iwasawa fdbab5
const int TAG_Y_CB_CR_COEFFICIENTS   = 0x0211;
shun-iwasawa fdbab5
const int TAG_Y_CB_CR_SUB_SAMPLING   = 0x0212;
shun-iwasawa fdbab5
const int TAG_Y_CB_CR_POSITIONING    = 0x0213;
shun-iwasawa fdbab5
const int TAG_REFERENCE_BLACK_WHITE  = 0x0214;
shun-iwasawa fdbab5
const int TAG_RELATED_IMAGE_WIDTH    = 0x1001;
shun-iwasawa fdbab5
const int TAG_RELATED_IMAGE_LENGTH   = 0x1002;
shun-iwasawa fdbab5
const int TAG_CFA_REPEAT_PATTERN_DIM = 0x828D;
shun-iwasawa fdbab5
const int TAG_CFA_PATTERN1           = 0x828E;
shun-iwasawa fdbab5
const int TAG_BATTERY_LEVEL          = 0x828F;
shun-iwasawa fdbab5
const int TAG_COPYRIGHT              = 0x8298;
shun-iwasawa fdbab5
const int TAG_EXPOSURETIME           = 0x829A;
shun-iwasawa fdbab5
const int TAG_FNUMBER                = 0x829D;
shun-iwasawa fdbab5
const int TAG_IPTC_NAA               = 0x83BB;
shun-iwasawa fdbab5
const int TAG_EXIF_OFFSET            = 0x8769;
shun-iwasawa fdbab5
const int TAG_INTER_COLOR_PROFILE    = 0x8773;
shun-iwasawa fdbab5
const int TAG_EXPOSURE_PROGRAM       = 0x8822;
shun-iwasawa fdbab5
const int TAG_SPECTRAL_SENSITIVITY   = 0x8824;
shun-iwasawa fdbab5
const int TAG_GPSINFO                = 0x8825;
shun-iwasawa fdbab5
const int TAG_ISO_EQUIVALENT         = 0x8827;
shun-iwasawa fdbab5
const int TAG_OECF                   = 0x8828;
shun-iwasawa fdbab5
const int TAG_EXIF_VERSION           = 0x9000;
shun-iwasawa fdbab5
const int TAG_DATETIME_ORIGINAL      = 0x9003;
shun-iwasawa fdbab5
const int TAG_DATETIME_DIGITIZED     = 0x9004;
shun-iwasawa fdbab5
const int TAG_COMPONENTS_CONFIG      = 0x9101;
shun-iwasawa fdbab5
const int TAG_CPRS_BITS_PER_PIXEL    = 0x9102;
shun-iwasawa fdbab5
const int TAG_SHUTTERSPEED           = 0x9201;
shun-iwasawa fdbab5
const int TAG_APERTURE               = 0x9202;
shun-iwasawa fdbab5
const int TAG_BRIGHTNESS_VALUE       = 0x9203;
shun-iwasawa fdbab5
const int TAG_EXPOSURE_BIAS          = 0x9204;
shun-iwasawa fdbab5
const int TAG_MAXAPERTURE            = 0x9205;
shun-iwasawa fdbab5
const int TAG_SUBJECT_DISTANCE       = 0x9206;
shun-iwasawa fdbab5
const int TAG_METERING_MODE          = 0x9207;
shun-iwasawa fdbab5
const int TAG_LIGHT_SOURCE           = 0x9208;
shun-iwasawa fdbab5
const int TAG_FLASH                  = 0x9209;
shun-iwasawa fdbab5
const int TAG_FOCALLENGTH            = 0x920A;
shun-iwasawa fdbab5
const int TAG_SUBJECTAREA            = 0x9214;
shun-iwasawa fdbab5
const int TAG_MAKER_NOTE             = 0x927C;
shun-iwasawa fdbab5
const int TAG_USERCOMMENT            = 0x9286;
shun-iwasawa fdbab5
const int TAG_SUBSEC_TIME            = 0x9290;
shun-iwasawa fdbab5
const int TAG_SUBSEC_TIME_ORIG       = 0x9291;
shun-iwasawa fdbab5
const int TAG_SUBSEC_TIME_DIG        = 0x9292;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
const int TAG_WINXP_TITLE = 0x9c9b;  // Windows XP - not part of exif standard.
shun-iwasawa fdbab5
const int TAG_WINXP_COMMENT =
shun-iwasawa fdbab5
    0x9c9c;                           // Windows XP - not part of exif standard.
shun-iwasawa fdbab5
const int TAG_WINXP_AUTHOR = 0x9c9d;  // Windows XP - not part of exif standard.
shun-iwasawa fdbab5
const int TAG_WINXP_KEYWORDS =
shun-iwasawa fdbab5
    0x9c9e;  // Windows XP - not part of exif standard.
shun-iwasawa fdbab5
const int TAG_WINXP_SUBJECT =
shun-iwasawa fdbab5
    0x9c9f;  // Windows XP - not part of exif standard.
shun-iwasawa fdbab5
shun-iwasawa fdbab5
const int TAG_FLASH_PIX_VERSION  = 0xA000;
shun-iwasawa fdbab5
const int TAG_COLOR_SPACE        = 0xA001;
shun-iwasawa fdbab5
const int TAG_PIXEL_X_DIMENSION  = 0xA002;
shun-iwasawa fdbab5
const int TAG_PIXEL_Y_DIMENSION  = 0xA003;
shun-iwasawa fdbab5
const int TAG_RELATED_AUDIO_FILE = 0xA004;
shun-iwasawa fdbab5
const int TAG_INTEROP_OFFSET     = 0xA005;
shun-iwasawa fdbab5
const int TAG_FLASH_ENERGY       = 0xA20B;
shun-iwasawa fdbab5
const int TAG_SPATIAL_FREQ_RESP  = 0xA20C;
shun-iwasawa fdbab5
const int TAG_FOCAL_PLANE_XRES   = 0xA20E;
shun-iwasawa fdbab5
const int TAG_FOCAL_PLANE_YRES   = 0xA20F;
shun-iwasawa fdbab5
const int TAG_FOCAL_PLANE_UNITS  = 0xA210;
shun-iwasawa fdbab5
const int TAG_SUBJECT_LOCATION   = 0xA214;
shun-iwasawa fdbab5
const int TAG_EXPOSURE_INDEX     = 0xA215;
shun-iwasawa fdbab5
const int TAG_SENSING_METHOD     = 0xA217;
shun-iwasawa fdbab5
const int TAG_FILE_SOURCE        = 0xA300;
shun-iwasawa fdbab5
const int TAG_SCENE_TYPE         = 0xA301;
shun-iwasawa fdbab5
const int TAG_CFA_PATTERN        = 0xA302;
shun-iwasawa fdbab5
const int TAG_CUSTOM_RENDERED    = 0xA401;
shun-iwasawa fdbab5
const int TAG_EXPOSURE_MODE      = 0xA402;
shun-iwasawa fdbab5
const int TAG_WHITEBALANCE       = 0xA403;
shun-iwasawa fdbab5
const int TAG_DIGITALZOOMRATIO   = 0xA404;
shun-iwasawa fdbab5
const int TAG_FOCALLENGTH_35MM   = 0xA405;
shun-iwasawa fdbab5
const int TAG_SCENE_CAPTURE_TYPE = 0xA406;
shun-iwasawa fdbab5
const int TAG_GAIN_CONTROL       = 0xA407;
shun-iwasawa fdbab5
const int TAG_CONTRAST           = 0xA408;
shun-iwasawa fdbab5
const int TAG_SATURATION         = 0xA409;
shun-iwasawa fdbab5
const int TAG_SHARPNESS          = 0xA40A;
shun-iwasawa fdbab5
const int TAG_DISTANCE_RANGE     = 0xA40C;
shun-iwasawa fdbab5
const int TAG_IMAGE_UNIQUE_ID    = 0xA420;
shun-iwasawa fdbab5
#endif
shun-iwasawa fdbab5
shun-iwasawa fdbab5
typedef struct {
shun-iwasawa fdbab5
  unsigned short Tag;
Campbell Barton e4aac6
  const char *Desc;
shun-iwasawa fdbab5
} TagTable_t;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
const TagTable_t TagTable[] = {
shun-iwasawa fdbab5
    {TAG_X_RESOLUTION, "XResolution"},
shun-iwasawa fdbab5
    {TAG_Y_RESOLUTION, "YResolution"},
shun-iwasawa fdbab5
    {TAG_RESOLUTION_UNIT, "ResolutionUnit"},
shun-iwasawa fdbab5
#ifdef ReadAllTags
shun-iwasawa fdbab5
    {TAG_INTEROP_INDEX, "InteropIndex"},
shun-iwasawa fdbab5
    {TAG_INTEROP_VERSION, "InteropVersion"},
shun-iwasawa fdbab5
    {TAG_IMAGE_WIDTH, "ImageWidth"},
shun-iwasawa fdbab5
    {TAG_IMAGE_LENGTH, "ImageLength"},
shun-iwasawa fdbab5
    {TAG_BITS_PER_SAMPLE, "BitsPerSample"},
shun-iwasawa fdbab5
    {TAG_COMPRESSION, "Compression"},
shun-iwasawa fdbab5
    {TAG_PHOTOMETRIC_INTERP, "PhotometricInterpretation"},
shun-iwasawa fdbab5
    {TAG_FILL_ORDER, "FillOrder"},
shun-iwasawa fdbab5
    {TAG_DOCUMENT_NAME, "DocumentName"},
shun-iwasawa fdbab5
    {TAG_IMAGE_DESCRIPTION, "ImageDescription"},
shun-iwasawa fdbab5
    {TAG_MAKE, "Make"},
shun-iwasawa fdbab5
    {TAG_MODEL, "Model"},
shun-iwasawa fdbab5
    {TAG_SRIP_OFFSET, "StripOffsets"},
shun-iwasawa fdbab5
    {TAG_ORIENTATION, "Orientation"},
shun-iwasawa fdbab5
    {TAG_SAMPLES_PER_PIXEL, "SamplesPerPixel"},
shun-iwasawa fdbab5
    {TAG_ROWS_PER_STRIP, "RowsPerStrip"},
shun-iwasawa fdbab5
    {TAG_STRIP_BYTE_COUNTS, "StripByteCounts"},
shun-iwasawa fdbab5
    {TAG_PLANAR_CONFIGURATION, "PlanarConfiguration"},
shun-iwasawa fdbab5
    {TAG_TRANSFER_FUNCTION, "TransferFunction"},
shun-iwasawa fdbab5
    {TAG_SOFTWARE, "Software"},
shun-iwasawa fdbab5
    {TAG_DATETIME, "DateTime"},
shun-iwasawa fdbab5
    {TAG_ARTIST, "Artist"},
shun-iwasawa fdbab5
    {TAG_WHITE_POINT, "WhitePoint"},
shun-iwasawa fdbab5
    {TAG_PRIMARY_CHROMATICITIES, "PrimaryChromaticities"},
shun-iwasawa fdbab5
    {TAG_TRANSFER_RANGE, "TransferRange"},
shun-iwasawa fdbab5
    {TAG_JPEG_PROC, "JPEGProc"},
shun-iwasawa fdbab5
    {TAG_THUMBNAIL_OFFSET, "ThumbnailOffset"},
shun-iwasawa fdbab5
    {TAG_THUMBNAIL_LENGTH, "ThumbnailLength"},
shun-iwasawa fdbab5
    {TAG_Y_CB_CR_COEFFICIENTS, "YCbCrCoefficients"},
shun-iwasawa fdbab5
    {TAG_Y_CB_CR_SUB_SAMPLING, "YCbCrSubSampling"},
shun-iwasawa fdbab5
    {TAG_Y_CB_CR_POSITIONING, "YCbCrPositioning"},
shun-iwasawa fdbab5
    {TAG_REFERENCE_BLACK_WHITE, "ReferenceBlackWhite"},
shun-iwasawa fdbab5
    {TAG_RELATED_IMAGE_WIDTH, "RelatedImageWidth"},
shun-iwasawa fdbab5
    {TAG_RELATED_IMAGE_LENGTH, "RelatedImageLength"},
shun-iwasawa fdbab5
    {TAG_CFA_REPEAT_PATTERN_DIM, "CFARepeatPatternDim"},
shun-iwasawa fdbab5
    {TAG_CFA_PATTERN1, "CFAPattern"},
shun-iwasawa fdbab5
    {TAG_BATTERY_LEVEL, "BatteryLevel"},
shun-iwasawa fdbab5
    {TAG_COPYRIGHT, "Copyright"},
shun-iwasawa fdbab5
    {TAG_EXPOSURETIME, "ExposureTime"},
shun-iwasawa fdbab5
    {TAG_FNUMBER, "FNumber"},
shun-iwasawa fdbab5
    {TAG_IPTC_NAA, "IPTC/NAA"},
shun-iwasawa fdbab5
    {TAG_EXIF_OFFSET, "ExifOffset"},
shun-iwasawa fdbab5
    {TAG_INTER_COLOR_PROFILE, "InterColorProfile"},
shun-iwasawa fdbab5
    {TAG_EXPOSURE_PROGRAM, "ExposureProgram"},
shun-iwasawa fdbab5
    {TAG_SPECTRAL_SENSITIVITY, "SpectralSensitivity"},
shun-iwasawa fdbab5
    {TAG_GPSINFO, "GPS Dir offset"},
shun-iwasawa fdbab5
    {TAG_ISO_EQUIVALENT, "ISOSpeedRatings"},
shun-iwasawa fdbab5
    {TAG_OECF, "OECF"},
shun-iwasawa fdbab5
    {TAG_EXIF_VERSION, "ExifVersion"},
shun-iwasawa fdbab5
    {TAG_DATETIME_ORIGINAL, "DateTimeOriginal"},
shun-iwasawa fdbab5
    {TAG_DATETIME_DIGITIZED, "DateTimeDigitized"},
shun-iwasawa fdbab5
    {TAG_COMPONENTS_CONFIG, "ComponentsConfiguration"},
shun-iwasawa fdbab5
    {TAG_CPRS_BITS_PER_PIXEL, "CompressedBitsPerPixel"},
shun-iwasawa fdbab5
    {TAG_SHUTTERSPEED, "ShutterSpeedValue"},
shun-iwasawa fdbab5
    {TAG_APERTURE, "ApertureValue"},
shun-iwasawa fdbab5
    {TAG_BRIGHTNESS_VALUE, "BrightnessValue"},
shun-iwasawa fdbab5
    {TAG_EXPOSURE_BIAS, "ExposureBiasValue"},
shun-iwasawa fdbab5
    {TAG_MAXAPERTURE, "MaxApertureValue"},
shun-iwasawa fdbab5
    {TAG_SUBJECT_DISTANCE, "SubjectDistance"},
shun-iwasawa fdbab5
    {TAG_METERING_MODE, "MeteringMode"},
shun-iwasawa fdbab5
    {TAG_LIGHT_SOURCE, "LightSource"},
shun-iwasawa fdbab5
    {TAG_FLASH, "Flash"},
shun-iwasawa fdbab5
    {TAG_FOCALLENGTH, "FocalLength"},
shun-iwasawa fdbab5
    {TAG_MAKER_NOTE, "MakerNote"},
shun-iwasawa fdbab5
    {TAG_USERCOMMENT, "UserComment"},
shun-iwasawa fdbab5
    {TAG_SUBSEC_TIME, "SubSecTime"},
shun-iwasawa fdbab5
    {TAG_SUBSEC_TIME_ORIG, "SubSecTimeOriginal"},
shun-iwasawa fdbab5
    {TAG_SUBSEC_TIME_DIG, "SubSecTimeDigitized"},
shun-iwasawa fdbab5
    {TAG_WINXP_TITLE, "Windows-XP Title"},
shun-iwasawa fdbab5
    {TAG_WINXP_COMMENT, "Windows-XP comment"},
shun-iwasawa fdbab5
    {TAG_WINXP_AUTHOR, "Windows-XP author"},
shun-iwasawa fdbab5
    {TAG_WINXP_KEYWORDS, "Windows-XP keywords"},
shun-iwasawa fdbab5
    {TAG_WINXP_SUBJECT, "Windows-XP subject"},
shun-iwasawa fdbab5
    {TAG_FLASH_PIX_VERSION, "FlashPixVersion"},
shun-iwasawa fdbab5
    {TAG_COLOR_SPACE, "ColorSpace"},
shun-iwasawa fdbab5
    {TAG_PIXEL_X_DIMENSION, "ExifImageWidth"},
shun-iwasawa fdbab5
    {TAG_PIXEL_Y_DIMENSION, "ExifImageLength"},
shun-iwasawa fdbab5
    {TAG_RELATED_AUDIO_FILE, "RelatedAudioFile"},
shun-iwasawa fdbab5
    {TAG_INTEROP_OFFSET, "InteroperabilityOffset"},
shun-iwasawa fdbab5
    {TAG_FLASH_ENERGY, "FlashEnergy"},
shun-iwasawa fdbab5
    {TAG_SPATIAL_FREQ_RESP, "SpatialFrequencyResponse"},
shun-iwasawa fdbab5
    {TAG_FOCAL_PLANE_XRES, "FocalPlaneXResolution"},
shun-iwasawa fdbab5
    {TAG_FOCAL_PLANE_YRES, "FocalPlaneYResolution"},
shun-iwasawa fdbab5
    {TAG_FOCAL_PLANE_UNITS, "FocalPlaneResolutionUnit"},
shun-iwasawa fdbab5
    {TAG_SUBJECT_LOCATION, "SubjectLocation"},
shun-iwasawa fdbab5
    {TAG_EXPOSURE_INDEX, "ExposureIndex"},
shun-iwasawa fdbab5
    {TAG_SENSING_METHOD, "SensingMethod"},
shun-iwasawa fdbab5
    {TAG_FILE_SOURCE, "FileSource"},
shun-iwasawa fdbab5
    {TAG_SCENE_TYPE, "SceneType"},
shun-iwasawa fdbab5
    {TAG_CFA_PATTERN, "CFA Pattern"},
shun-iwasawa fdbab5
    {TAG_CUSTOM_RENDERED, "CustomRendered"},
shun-iwasawa fdbab5
    {TAG_EXPOSURE_MODE, "ExposureMode"},
shun-iwasawa fdbab5
    {TAG_WHITEBALANCE, "WhiteBalance"},
shun-iwasawa fdbab5
    {TAG_DIGITALZOOMRATIO, "DigitalZoomRatio"},
shun-iwasawa fdbab5
    {TAG_FOCALLENGTH_35MM, "FocalLengthIn35mmFilm"},
shun-iwasawa fdbab5
    {TAG_SUBJECTAREA, "SubjectArea"},
shun-iwasawa fdbab5
    {TAG_SCENE_CAPTURE_TYPE, "SceneCaptureType"},
shun-iwasawa fdbab5
    {TAG_GAIN_CONTROL, "GainControl"},
shun-iwasawa fdbab5
    {TAG_CONTRAST, "Contrast"},
shun-iwasawa fdbab5
    {TAG_SATURATION, "Saturation"},
shun-iwasawa fdbab5
    {TAG_SHARPNESS, "Sharpness"},
shun-iwasawa fdbab5
    {TAG_DISTANCE_RANGE, "SubjectDistanceRange"},
shun-iwasawa fdbab5
    {TAG_IMAGE_UNIQUE_ID, "ImageUniqueId"},
shun-iwasawa fdbab5
#endif
shun-iwasawa fdbab5
};
shun-iwasawa fdbab5
shun-iwasawa fdbab5
const int TAG_TABLE_SIZE = (sizeof(TagTable) / sizeof(TagTable_t));
shun-iwasawa fdbab5
shun-iwasawa fdbab5
const int TRUE  = 1;
shun-iwasawa fdbab5
const int FALSE = 0;
Campbell Barton e4aac6
}  // namespace
shun-iwasawa fdbab5
shun-iwasawa fdbab5
//--------------------------------------------------------------------------
shun-iwasawa fdbab5
// Convert a 16 bit unsigned value from file's native byte order
shun-iwasawa fdbab5
//--------------------------------------------------------------------------
shun-iwasawa fdbab5
int JpgExifReader::Get16u(void *Short) {
shun-iwasawa fdbab5
  if (MotorolaOrder) {
shun-iwasawa fdbab5
    return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1];
shun-iwasawa fdbab5
  } else {
shun-iwasawa fdbab5
    return (((uchar *)Short)[1] << 8) | ((uchar *)Short)[0];
shun-iwasawa fdbab5
  }
shun-iwasawa fdbab5
}
shun-iwasawa fdbab5
shun-iwasawa fdbab5
//--------------------------------------------------------------------------
shun-iwasawa fdbab5
// Convert a 32 bit signed value from file's native byte order
shun-iwasawa fdbab5
//--------------------------------------------------------------------------
shun-iwasawa fdbab5
int JpgExifReader::Get32s(void *Long) {
shun-iwasawa fdbab5
  if (MotorolaOrder) {
shun-iwasawa fdbab5
    return (((char *)Long)[0] << 24) | (((uchar *)Long)[1] << 16) |
shun-iwasawa fdbab5
           (((uchar *)Long)[2] << 8) | (((uchar *)Long)[3] << 0);
shun-iwasawa fdbab5
  } else {
shun-iwasawa fdbab5
    return (((char *)Long)[3] << 24) | (((uchar *)Long)[2] << 16) |
shun-iwasawa fdbab5
           (((uchar *)Long)[1] << 8) | (((uchar *)Long)[0] << 0);
shun-iwasawa fdbab5
  }
shun-iwasawa fdbab5
}
shun-iwasawa fdbab5
shun-iwasawa fdbab5
//--------------------------------------------------------------------------
shun-iwasawa fdbab5
// Convert a 32 bit unsigned value from file's native byte order
shun-iwasawa fdbab5
//--------------------------------------------------------------------------
shun-iwasawa fdbab5
unsigned JpgExifReader::Get32u(void *Long) {
shun-iwasawa fdbab5
  return (unsigned)Get32s(Long) & 0xffffffff;
shun-iwasawa fdbab5
}
shun-iwasawa fdbab5
shun-iwasawa fdbab5
//--------------------------------------------------------------------------
shun-iwasawa fdbab5
// Display a number as one of its many formats
shun-iwasawa fdbab5
//--------------------------------------------------------------------------
shun-iwasawa fdbab5
void JpgExifReader::PrintFormatNumber(void *ValuePtr, int Format,
shun-iwasawa fdbab5
                                      int ByteCount) {
shun-iwasawa fdbab5
  int s, n;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
  for (n = 0; n < 16; n++) {
shun-iwasawa fdbab5
    switch (Format) {
shun-iwasawa fdbab5
    case FMT_SBYTE:
shun-iwasawa fdbab5
    case FMT_BYTE:
shun-iwasawa fdbab5
      printf("%02x", *(uchar *)ValuePtr);
shun-iwasawa fdbab5
      s = 1;
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
    case FMT_USHORT:
shun-iwasawa fdbab5
      printf("%d", Get16u(ValuePtr));
shun-iwasawa fdbab5
      s = 2;
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
    case FMT_ULONG:
shun-iwasawa fdbab5
    case FMT_SLONG:
shun-iwasawa fdbab5
      printf("%d", Get32s(ValuePtr));
shun-iwasawa fdbab5
      s = 4;
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
    case FMT_SSHORT:
shun-iwasawa fdbab5
      printf("%hd", (signed short)Get16u(ValuePtr));
shun-iwasawa fdbab5
      s = 2;
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
    case FMT_URATIONAL:
shun-iwasawa fdbab5
      printf("%u/%u", Get32s(ValuePtr), Get32s(4 + (char *)ValuePtr));
shun-iwasawa fdbab5
      s = 8;
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case FMT_SRATIONAL:
shun-iwasawa fdbab5
      printf("%d/%d", Get32s(ValuePtr), Get32s(4 + (char *)ValuePtr));
shun-iwasawa fdbab5
      s = 8;
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case FMT_SINGLE:
shun-iwasawa fdbab5
      printf("%f", (double)*(float *)ValuePtr);
shun-iwasawa fdbab5
      s = 8;
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
    case FMT_DOUBLE:
shun-iwasawa fdbab5
      printf("%f", *(double *)ValuePtr);
shun-iwasawa fdbab5
      s = 8;
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
    default:
shun-iwasawa fdbab5
      printf("Unknown format %d:", Format);
shun-iwasawa fdbab5
      return;
shun-iwasawa fdbab5
    }
shun-iwasawa fdbab5
    ByteCount -= s;
shun-iwasawa fdbab5
    if (ByteCount <= 0) break;
shun-iwasawa fdbab5
    printf(", ");
shun-iwasawa fdbab5
    ValuePtr = (void *)((char *)ValuePtr + s);
shun-iwasawa fdbab5
  }
shun-iwasawa fdbab5
  if (n >= 16) printf("...");
shun-iwasawa fdbab5
}
shun-iwasawa fdbab5
shun-iwasawa fdbab5
//--------------------------------------------------------------------------
shun-iwasawa fdbab5
// Evaluate number, be it int, rational, or float from directory.
shun-iwasawa fdbab5
//--------------------------------------------------------------------------
shun-iwasawa fdbab5
double JpgExifReader::ConvertAnyFormat(void *ValuePtr, int Format) {
shun-iwasawa fdbab5
  double Value;
shun-iwasawa fdbab5
  Value = 0;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
  switch (Format) {
shun-iwasawa fdbab5
  case FMT_SBYTE:
shun-iwasawa fdbab5
    Value = *(signed char *)ValuePtr;
shun-iwasawa fdbab5
    break;
shun-iwasawa fdbab5
  case FMT_BYTE:
shun-iwasawa fdbab5
    Value = *(uchar *)ValuePtr;
shun-iwasawa fdbab5
    break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
  case FMT_USHORT:
shun-iwasawa fdbab5
    Value = Get16u(ValuePtr);
shun-iwasawa fdbab5
    break;
shun-iwasawa fdbab5
  case FMT_ULONG:
shun-iwasawa fdbab5
    Value = Get32u(ValuePtr);
shun-iwasawa fdbab5
    break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
  case FMT_URATIONAL:
shun-iwasawa fdbab5
  case FMT_SRATIONAL: {
shun-iwasawa fdbab5
    int Num, Den;
shun-iwasawa fdbab5
    Num = Get32s(ValuePtr);
shun-iwasawa fdbab5
    Den = Get32s(4 + (char *)ValuePtr);
shun-iwasawa fdbab5
    if (Den == 0) {
shun-iwasawa fdbab5
      Value = 0;
shun-iwasawa fdbab5
    } else {
shun-iwasawa fdbab5
      if (Format == FMT_SRATIONAL) {
shun-iwasawa fdbab5
        Value = (double)Num / Den;
shun-iwasawa fdbab5
      } else {
shun-iwasawa fdbab5
        Value = (double)(unsigned)Num / (double)(unsigned)Den;
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
    }
shun-iwasawa fdbab5
    break;
shun-iwasawa fdbab5
  }
shun-iwasawa fdbab5
shun-iwasawa fdbab5
  case FMT_SSHORT:
shun-iwasawa fdbab5
    Value = (signed short)Get16u(ValuePtr);
shun-iwasawa fdbab5
    break;
shun-iwasawa fdbab5
  case FMT_SLONG:
shun-iwasawa fdbab5
    Value = Get32s(ValuePtr);
shun-iwasawa fdbab5
    break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
  // Not sure if this is correct (never seen float used in Exif format)
shun-iwasawa fdbab5
  case FMT_SINGLE:
shun-iwasawa fdbab5
    Value = (double)*(float *)ValuePtr;
shun-iwasawa fdbab5
    break;
shun-iwasawa fdbab5
  case FMT_DOUBLE:
shun-iwasawa fdbab5
    Value = *(double *)ValuePtr;
shun-iwasawa fdbab5
    break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
  default:
shun-iwasawa fdbab5
    std::cout << "Illegal format code " << Format << " in Exif header"
shun-iwasawa fdbab5
              << std::endl;
shun-iwasawa fdbab5
  }
shun-iwasawa fdbab5
  return Value;
shun-iwasawa fdbab5
}
shun-iwasawa fdbab5
shun-iwasawa fdbab5
//--------------------------------------------------------------------------
shun-iwasawa fdbab5
shun-iwasawa fdbab5
void JpgExifReader::process_EXIF(unsigned char *ExifSection,
shun-iwasawa fdbab5
                                 unsigned int length) {
shun-iwasawa fdbab5
  unsigned int FirstOffset;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
#ifdef ReadAllTags
shun-iwasawa fdbab5
  FocalplaneXRes       = 0;
shun-iwasawa fdbab5
  FocalplaneUnits      = 0;
shun-iwasawa fdbab5
  ExifImageWidth       = 0;
shun-iwasawa fdbab5
  NumOrientations      = 0;
shun-iwasawa fdbab5
  DirWithThumbnailPtrs = NULL;
shun-iwasawa fdbab5
#endif
shun-iwasawa fdbab5
shun-iwasawa fdbab5
  if (ShowTags) printf("Exif header %u bytes long\n", length);
shun-iwasawa fdbab5
shun-iwasawa fdbab5
  {  // Check the EXIF header component
shun-iwasawa fdbab5
    static uchar ExifHeader[] = "Exif\0\0";
shun-iwasawa fdbab5
    if (memcmp(ExifSection + 2, ExifHeader, 6)) {
shun-iwasawa fdbab5
      std::cout << "Incorrect Exif header" << std::endl;
shun-iwasawa fdbab5
      return;
shun-iwasawa fdbab5
    }
shun-iwasawa fdbab5
  }
shun-iwasawa fdbab5
shun-iwasawa fdbab5
  if (memcmp(ExifSection + 8, "II", 2) == 0) {
shun-iwasawa fdbab5
    if (ShowTags) printf("Exif section in Intel order\n");
shun-iwasawa fdbab5
    MotorolaOrder = 0;
shun-iwasawa fdbab5
  } else {
shun-iwasawa fdbab5
    if (memcmp(ExifSection + 8, "MM", 2) == 0) {
shun-iwasawa fdbab5
      if (ShowTags) printf("Exif section in Motorola order\n");
shun-iwasawa fdbab5
      MotorolaOrder = 1;
shun-iwasawa fdbab5
    } else {
shun-iwasawa fdbab5
      std::cout << "Invalid Exif alignment marker." << std::endl;
shun-iwasawa fdbab5
      return;
shun-iwasawa fdbab5
    }
shun-iwasawa fdbab5
  }
shun-iwasawa fdbab5
shun-iwasawa fdbab5
  // Check the next value for correctness.
shun-iwasawa fdbab5
  if (Get16u(ExifSection + 10) != 0x2a) {
shun-iwasawa fdbab5
    std::cout << "Invalid Exif start (1)" << std::endl;
shun-iwasawa fdbab5
    return;
shun-iwasawa fdbab5
  }
shun-iwasawa fdbab5
shun-iwasawa fdbab5
  FirstOffset = Get32u(ExifSection + 12);
shun-iwasawa fdbab5
  if (FirstOffset < 8 || FirstOffset > 16) {
shun-iwasawa fdbab5
    if (FirstOffset < 16 || FirstOffset > length - 16) {
shun-iwasawa fdbab5
      std::cout << "invalid offset for first Exif IFD value" << std::endl;
shun-iwasawa fdbab5
      return;
shun-iwasawa fdbab5
    }
shun-iwasawa fdbab5
    // Usually set to 8, but other values valid too.
shun-iwasawa fdbab5
    std::cout << "Suspicious offset of first Exif IFD value" << std::endl;
shun-iwasawa fdbab5
  }
shun-iwasawa fdbab5
shun-iwasawa fdbab5
  // First directory starts 16 bytes in.  All offset are relative to 8 bytes in.
shun-iwasawa fdbab5
  ProcessExifDir(ExifSection + 8 + FirstOffset, ExifSection + 8, length - 8, 0);
shun-iwasawa fdbab5
shun-iwasawa fdbab5
#ifdef ReadAllTags
shun-iwasawa fdbab5
  ImageInfo.ThumbnailAtEnd =
shun-iwasawa fdbab5
      ImageInfo.ThumbnailOffset >= ImageInfo.LargestExifOffset ? TRUE : FALSE;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
  if (DumpExifMap) {
shun-iwasawa fdbab5
    unsigned a, b;
shun-iwasawa fdbab5
    printf("Map: %05d- End of exif\n", length - 8);
shun-iwasawa fdbab5
    for (a = 0; a < length - 8; a += 10) {
shun-iwasawa fdbab5
      printf("Map: %05d ", a);
shun-iwasawa fdbab5
      for (b = 0; b < 10; b++) printf(" %02x", *(ExifSection + 8 + a + b));
shun-iwasawa fdbab5
      printf("\n");
shun-iwasawa fdbab5
    }
shun-iwasawa fdbab5
  }
shun-iwasawa fdbab5
shun-iwasawa fdbab5
  // Compute the CCD width, in millimeters.
shun-iwasawa fdbab5
  if (FocalplaneXRes != 0 && ExifImageWidth != 0) {
shun-iwasawa fdbab5
    // Note: With some cameras, its not possible to compute this correctly
shun-iwasawa fdbab5
    // because
shun-iwasawa fdbab5
    // they don't adjust the indicated focal plane resolution units when using
shun-iwasawa fdbab5
    // less
shun-iwasawa fdbab5
    // than maximum resolution, so the CCDWidth value comes out too small.
shun-iwasawa fdbab5
    // Nothing
shun-iwasawa fdbab5
    // that Jhad can do about it - its a camera problem.
shun-iwasawa fdbab5
    ImageInfo.CCDWidth =
shun-iwasawa fdbab5
        (float)(ExifImageWidth * FocalplaneUnits / FocalplaneXRes);
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    if (ImageInfo.FocalLength && ImageInfo.FocalLength35mmEquiv == 0) {
shun-iwasawa fdbab5
      // Compute 35 mm equivalent focal length based on sensor geometry if we
shun-iwasawa fdbab5
      // haven't
shun-iwasawa fdbab5
      // already got it explicitly from a tag.
shun-iwasawa fdbab5
      ImageInfo.FocalLength35mmEquiv =
shun-iwasawa fdbab5
          (int)(ImageInfo.FocalLength / ImageInfo.CCDWidth * 36 + 0.5);
shun-iwasawa fdbab5
    }
shun-iwasawa fdbab5
  }
shun-iwasawa fdbab5
#endif
shun-iwasawa fdbab5
}
shun-iwasawa fdbab5
shun-iwasawa fdbab5
//--------------------------------------------------------------------------
shun-iwasawa fdbab5
// Process one of the nested EXIF directories.
shun-iwasawa fdbab5
//--------------------------------------------------------------------------
shun-iwasawa fdbab5
void JpgExifReader::ProcessExifDir(unsigned char *DirStart,
shun-iwasawa fdbab5
                                   unsigned char *OffsetBase,
shun-iwasawa fdbab5
                                   unsigned ExifLength, int NestingLevel) {
shun-iwasawa fdbab5
  int de;
shun-iwasawa 443318
#ifdef ReadAllTags
shun-iwasawa fdbab5
  int a;
shun-iwasawa 443318
#endif
shun-iwasawa fdbab5
  int NumDirEntries;
shun-iwasawa fdbab5
  unsigned ThumbnailOffset = 0;
shun-iwasawa fdbab5
  unsigned ThumbnailSize   = 0;
shun-iwasawa fdbab5
  char IndentString[25];
shun-iwasawa fdbab5
shun-iwasawa fdbab5
  if (NestingLevel > 4) {
shun-iwasawa fdbab5
    std::cout << "Maximum Exif directory nesting exceeded (corrupt Exif header)"
shun-iwasawa fdbab5
              << std::endl;
shun-iwasawa fdbab5
    return;
shun-iwasawa fdbab5
  }
shun-iwasawa fdbab5
shun-iwasawa fdbab5
  memset(IndentString, ' ', 25);
shun-iwasawa fdbab5
  IndentString[NestingLevel * 4] = '\0';
shun-iwasawa fdbab5
shun-iwasawa fdbab5
  NumDirEntries = Get16u(DirStart);
shun-iwasawa fdbab5
#define DIR_ENTRY_ADDR(Start, Entry) (Start + 2 + 12 * (Entry))
shun-iwasawa fdbab5
shun-iwasawa fdbab5
  {
shun-iwasawa fdbab5
    unsigned char *DirEnd;
shun-iwasawa 443318
    DirEnd = DIR_ENTRY_ADDR(DirStart, static_cast<int64_t>(NumDirEntries));</int64_t>
shun-iwasawa fdbab5
    if (DirEnd + 4 > (OffsetBase + ExifLength)) {
shun-iwasawa fdbab5
      if (DirEnd + 2 == OffsetBase + ExifLength ||
shun-iwasawa fdbab5
          DirEnd == OffsetBase + ExifLength) {
shun-iwasawa fdbab5
        // Version 1.3 of jhead would truncate a bit too much.
shun-iwasawa fdbab5
        // This also caught later on as well.
shun-iwasawa fdbab5
      } else {
shun-iwasawa fdbab5
        std::cout << "Illegally sized Exif subdirectory (" << NumDirEntries
shun-iwasawa fdbab5
                  << "entries)" << std::endl;
shun-iwasawa fdbab5
        return;
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
    }
shun-iwasawa fdbab5
    if (DumpExifMap) {
shun-iwasawa fdbab5
      printf("Map: %05u-%05u: Directory\n", (int)(DirStart - OffsetBase),
shun-iwasawa fdbab5
             (int)(DirEnd + 4 - OffsetBase));
shun-iwasawa fdbab5
    }
shun-iwasawa fdbab5
  }
shun-iwasawa fdbab5
shun-iwasawa fdbab5
  if (ShowTags) {
shun-iwasawa fdbab5
    printf("(dir has %d entries)\n", NumDirEntries);
shun-iwasawa fdbab5
  }
shun-iwasawa fdbab5
shun-iwasawa fdbab5
  for (de = 0; de < NumDirEntries; de++) {
shun-iwasawa fdbab5
    int Tag, Format, Components;
shun-iwasawa fdbab5
    unsigned char *ValuePtr;
shun-iwasawa fdbab5
    int ByteCount;
shun-iwasawa fdbab5
    unsigned char *DirEntry;
shun-iwasawa 443318
    DirEntry = DIR_ENTRY_ADDR(DirStart, static_cast<int64_t>(de));</int64_t>
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    Tag        = Get16u(DirEntry);
shun-iwasawa fdbab5
    Format     = Get16u(DirEntry + 2);
shun-iwasawa fdbab5
    Components = Get32u(DirEntry + 4);
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    if ((Format - 1) >= NUM_FORMATS) {
shun-iwasawa fdbab5
      // (-1) catches illegal zero case as unsigned underflows to positive
shun-iwasawa fdbab5
      // large.
shun-iwasawa fdbab5
      std::cout << "Illegal number format " << Format << " for tag " << Tag
shun-iwasawa fdbab5
                << " in Exif" << std::endl;
shun-iwasawa fdbab5
      continue;
shun-iwasawa fdbab5
    }
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    if ((unsigned)Components > 0x10000) {
shun-iwasawa fdbab5
      std::cout << "Too many components " << Components << " for tag " << Tag
shun-iwasawa fdbab5
                << " in Exif";
shun-iwasawa fdbab5
      continue;
shun-iwasawa fdbab5
    }
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    ByteCount = Components * BytesPerFormat[Format];
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    if (ByteCount > 4) {
shun-iwasawa fdbab5
      unsigned OffsetVal;
shun-iwasawa fdbab5
      OffsetVal = Get32u(DirEntry + 8);
shun-iwasawa fdbab5
      // If its bigger than 4 bytes, the dir entry contains an offset.
shun-iwasawa fdbab5
      if (OffsetVal + ByteCount > ExifLength) {
shun-iwasawa fdbab5
        // Bogus pointer offset and / or bytecount value
shun-iwasawa fdbab5
        std::cout << "Illegal value pointer for tag " << Tag << " in Exif";
shun-iwasawa fdbab5
        continue;
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
      ValuePtr = OffsetBase + OffsetVal;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
#ifdef ReadAllTags
shun-iwasawa fdbab5
      if (OffsetVal > ImageInfo.LargestExifOffset) {
shun-iwasawa fdbab5
        ImageInfo.LargestExifOffset = OffsetVal;
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
#endif
shun-iwasawa fdbab5
shun-iwasawa fdbab5
      if (DumpExifMap) {
shun-iwasawa fdbab5
        printf("Map: %05u-%05u:   Data for tag %04x\n", OffsetVal,
shun-iwasawa fdbab5
               OffsetVal + ByteCount, Tag);
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
    } else {
shun-iwasawa fdbab5
      // 4 bytes or less and value is in the dir entry itself
shun-iwasawa fdbab5
      ValuePtr = DirEntry + 8;
shun-iwasawa fdbab5
    }
shun-iwasawa fdbab5
shun-iwasawa fdbab5
#ifdef ReadAllTags
shun-iwasawa fdbab5
    if (Tag == TAG_MAKER_NOTE) {
shun-iwasawa fdbab5
      if (ShowTags) {
shun-iwasawa fdbab5
        printf("%s    Maker note: ", IndentString);
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
      // ProcessMakerNote(ValuePtr, ByteCount, OffsetBase, ExifLength);
shun-iwasawa fdbab5
      continue;
shun-iwasawa fdbab5
    }
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    if (ShowTags) {
shun-iwasawa fdbab5
      // Show tag name
shun-iwasawa fdbab5
      for (a = 0;; a++) {
shun-iwasawa fdbab5
        if (a >= TAG_TABLE_SIZE) {
shun-iwasawa fdbab5
          printf("%s    Unknown Tag %04x Value = ", IndentString, Tag);
shun-iwasawa fdbab5
          break;
shun-iwasawa fdbab5
        }
shun-iwasawa fdbab5
        if (TagTable[a].Tag == Tag) {
shun-iwasawa fdbab5
          printf("%s    %s = ", IndentString, TagTable[a].Desc);
shun-iwasawa fdbab5
          break;
shun-iwasawa fdbab5
        }
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
shun-iwasawa fdbab5
      // Show tag value.
shun-iwasawa fdbab5
      switch (Format) {
shun-iwasawa fdbab5
      case FMT_BYTE:
shun-iwasawa fdbab5
        if (ByteCount > 1) {
shun-iwasawa fdbab5
          printf("%.*ls\n", ByteCount / 2, (wchar_t *)ValuePtr);
shun-iwasawa fdbab5
        } else {
shun-iwasawa fdbab5
          PrintFormatNumber(ValuePtr, Format, ByteCount);
shun-iwasawa fdbab5
          printf("\n");
shun-iwasawa fdbab5
        }
shun-iwasawa fdbab5
        break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
      case FMT_UNDEFINED:
Campbell Barton e4aac6
        // Undefined is typically an ascii string.
shun-iwasawa fdbab5
shun-iwasawa fdbab5
      case FMT_STRING:
shun-iwasawa fdbab5
        // String arrays printed without function call (different from int
shun-iwasawa fdbab5
        // arrays)
shun-iwasawa fdbab5
        {
shun-iwasawa fdbab5
          int NoPrint = 0;
shun-iwasawa fdbab5
          printf("\"");
shun-iwasawa fdbab5
          for (a = 0; a < ByteCount; a++) {
shun-iwasawa fdbab5
            if (ValuePtr[a] >= 32) {
shun-iwasawa fdbab5
              putchar(ValuePtr[a]);
shun-iwasawa fdbab5
              NoPrint = 0;
shun-iwasawa fdbab5
            } else {
shun-iwasawa fdbab5
              // Avoiding indicating too many unprintable characters of
shun-iwasawa fdbab5
              // proprietary
shun-iwasawa fdbab5
              // bits of binary information this program may not know how to
shun-iwasawa fdbab5
              // parse.
shun-iwasawa fdbab5
              if (!NoPrint && a != ByteCount - 1) {
shun-iwasawa fdbab5
                putchar('?');
shun-iwasawa fdbab5
                NoPrint = 1;
shun-iwasawa fdbab5
              }
shun-iwasawa fdbab5
            }
shun-iwasawa fdbab5
          }
shun-iwasawa fdbab5
          printf("\"\n");
shun-iwasawa fdbab5
        }
shun-iwasawa fdbab5
        break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
      default:
shun-iwasawa fdbab5
        // Handle arrays of numbers later (will there ever be?)
shun-iwasawa fdbab5
        PrintFormatNumber(ValuePtr, Format, ByteCount);
shun-iwasawa fdbab5
        printf("\n");
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
    }
shun-iwasawa fdbab5
#endif
shun-iwasawa fdbab5
    // Extract useful components of tag
shun-iwasawa fdbab5
    switch (Tag) {
shun-iwasawa fdbab5
#ifdef ReadAllTags
shun-iwasawa fdbab5
    case TAG_MAKE:
shun-iwasawa fdbab5
      strncpy(ImageInfo.CameraMake, (char *)ValuePtr,
shun-iwasawa fdbab5
              ByteCount < 31 ? ByteCount : 31);
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_MODEL:
shun-iwasawa fdbab5
      strncpy(ImageInfo.CameraModel, (char *)ValuePtr,
shun-iwasawa fdbab5
              ByteCount < 39 ? ByteCount : 39);
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_DATETIME_ORIGINAL:
shun-iwasawa fdbab5
      // If we get a DATETIME_ORIGINAL, we use that one.
shun-iwasawa fdbab5
      strncpy(ImageInfo.DateTime, (char *)ValuePtr, 19);
Campbell Barton e4aac6
      // Fallthru...
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_DATETIME_DIGITIZED:
shun-iwasawa fdbab5
    case TAG_DATETIME:
shun-iwasawa fdbab5
      if (!isdigit(static_cast<unsigned char="">(ImageInfo.DateTime[0]))) {</unsigned>
shun-iwasawa fdbab5
        // If we don't already have a DATETIME_ORIGINAL, use whatever
shun-iwasawa fdbab5
        // time fields we may have.
shun-iwasawa fdbab5
        strncpy(ImageInfo.DateTime, (char *)ValuePtr, 19);
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
shun-iwasawa fdbab5
      if (ImageInfo.numDateTimeTags >= MAX_DATE_COPIES) {
shun-iwasawa fdbab5
        std::cout << "More than " << MAX_DATE_COPIES
shun-iwasawa fdbab5
                  << " date fields in Exif.  This is nuts" << std::endl;
shun-iwasawa fdbab5
        break;
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
      ImageInfo.DateTimeOffsets[ImageInfo.numDateTimeTags++] =
shun-iwasawa fdbab5
          (char *)ValuePtr - (char *)OffsetBase;
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_WINXP_COMMENT:
shun-iwasawa fdbab5
      if (ImageInfo.Comments[0]) {  // We already have a jpeg comment.
shun-iwasawa fdbab5
        // Already have a comment (probably windows comment), skip this one.
shun-iwasawa fdbab5
        if (ShowTags)
luz paz 6454c4
          printf("Windows XP comment and other comment in header\n");
shun-iwasawa fdbab5
        break;  // Already have a windows comment, skip this one.
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
shun-iwasawa fdbab5
      if (ByteCount > 1) {
shun-iwasawa fdbab5
        if (ByteCount > MAX_COMMENT_SIZE) ByteCount = MAX_COMMENT_SIZE;
shun-iwasawa fdbab5
        memcpy(ImageInfo.Comments, ValuePtr, ByteCount);
shun-iwasawa fdbab5
        ImageInfo.CommentWidthchars = ByteCount / 2;
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_USERCOMMENT:
shun-iwasawa fdbab5
      if (ImageInfo.Comments[0]) {  // We already have a jpeg comment.
shun-iwasawa fdbab5
        // Already have a comment (probably windows comment), skip this one.
shun-iwasawa fdbab5
        if (ShowTags) printf("Multiple comments in exif header\n");
shun-iwasawa fdbab5
        break;  // Already have a windows comment, skip this one.
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
shun-iwasawa fdbab5
      // Comment is often padded with trailing spaces.  Remove these first.
shun-iwasawa fdbab5
      for (a = ByteCount;;) {
shun-iwasawa fdbab5
        a--;
shun-iwasawa fdbab5
        if ((ValuePtr)[a] == ' ') {
shun-iwasawa fdbab5
          (ValuePtr)[a] = '\0';
shun-iwasawa fdbab5
        } else {
shun-iwasawa fdbab5
          break;
shun-iwasawa fdbab5
        }
shun-iwasawa fdbab5
        if (a == 0) break;
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
shun-iwasawa fdbab5
      // Copy the comment
shun-iwasawa fdbab5
      {
Campbell Barton e4aac6
        int msiz = ExifLength - (ValuePtr - OffsetBase);
shun-iwasawa fdbab5
        if (msiz > ByteCount) msiz = ByteCount;
shun-iwasawa fdbab5
        if (msiz > MAX_COMMENT_SIZE - 1) msiz = MAX_COMMENT_SIZE - 1;
shun-iwasawa fdbab5
        if (msiz > 5 && memcmp(ValuePtr, "ASCII", 5) == 0) {
shun-iwasawa fdbab5
          for (a = 5; a < 10 && a < msiz; a++) {
shun-iwasawa fdbab5
            int c = (ValuePtr)[a];
shun-iwasawa fdbab5
            if (c != '\0' && c != ' ') {
shun-iwasawa fdbab5
              strncpy(ImageInfo.Comments, (char *)ValuePtr + a, msiz - a);
shun-iwasawa fdbab5
              break;
shun-iwasawa fdbab5
            }
shun-iwasawa fdbab5
          }
shun-iwasawa fdbab5
        } else {
shun-iwasawa fdbab5
          strncpy(ImageInfo.Comments, (char *)ValuePtr, msiz);
shun-iwasawa fdbab5
        }
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_FNUMBER:
shun-iwasawa fdbab5
      // Simplest way of expressing aperture, so I trust it the most.
shun-iwasawa fdbab5
      // (overwrite previously computd value if there is one)
shun-iwasawa fdbab5
      ImageInfo.ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format);
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_APERTURE:
shun-iwasawa fdbab5
    case TAG_MAXAPERTURE:
shun-iwasawa fdbab5
      // More relevant info always comes earlier, so only use this field if we
shun-iwasawa fdbab5
      // don't
shun-iwasawa fdbab5
      // have appropriate aperture information yet.
shun-iwasawa fdbab5
      if (ImageInfo.ApertureFNumber == 0) {
shun-iwasawa fdbab5
        ImageInfo.ApertureFNumber =
shun-iwasawa fdbab5
            (float)exp(ConvertAnyFormat(ValuePtr, Format) * log(2) * 0.5);
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_FOCALLENGTH:
shun-iwasawa fdbab5
      // Nice digital cameras actually save the focal length as a function
shun-iwasawa fdbab5
      // of how farthey are zoomed in.
shun-iwasawa fdbab5
      ImageInfo.FocalLength = (float)ConvertAnyFormat(ValuePtr, Format);
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_SUBJECT_DISTANCE:
shun-iwasawa fdbab5
      // Inidcates the distacne the autofocus camera is focused to.
shun-iwasawa fdbab5
      // Tends to be less accurate as distance increases.
shun-iwasawa fdbab5
      ImageInfo.Distance = (float)ConvertAnyFormat(ValuePtr, Format);
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_EXPOSURETIME:
shun-iwasawa fdbab5
      // Simplest way of expressing exposure time, so I trust it most.
shun-iwasawa fdbab5
      // (overwrite previously computd value if there is one)
shun-iwasawa fdbab5
      ImageInfo.ExposureTime = (float)ConvertAnyFormat(ValuePtr, Format);
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_SHUTTERSPEED:
shun-iwasawa fdbab5
      // More complicated way of expressing exposure time, so only use
shun-iwasawa fdbab5
      // this value if we don't already have it from somewhere else.
shun-iwasawa fdbab5
      if (ImageInfo.ExposureTime == 0) {
shun-iwasawa fdbab5
        ImageInfo.ExposureTime =
shun-iwasawa fdbab5
            (float)(1 / exp(ConvertAnyFormat(ValuePtr, Format) * log(2)));
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_FLASH:
shun-iwasawa fdbab5
      ImageInfo.FlashUsed = (int)ConvertAnyFormat(ValuePtr, Format);
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_ORIENTATION:
shun-iwasawa fdbab5
      if (NumOrientations >= 2) {
shun-iwasawa fdbab5
        // Can have another orientation tag for the thumbnail, but if there's
shun-iwasawa fdbab5
        // a third one, things are strange.
shun-iwasawa fdbab5
        std::cout << "More than two orientation in Exif" << std::endl;
shun-iwasawa fdbab5
        break;
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
      OrientationPtr[NumOrientations]       = ValuePtr;
shun-iwasawa fdbab5
      OrientationNumFormat[NumOrientations] = Format;
shun-iwasawa fdbab5
      if (NumOrientations == 0) {
shun-iwasawa fdbab5
        ImageInfo.Orientation = (int)ConvertAnyFormat(ValuePtr, Format);
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
      if (ImageInfo.Orientation < 0 || ImageInfo.Orientation > 8) {
shun-iwasawa fdbab5
        std::cout << "Undefined rotation value " << ImageInfo.Orientation
shun-iwasawa fdbab5
                  << " in Exif" << std::endl;
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
      NumOrientations += 1;
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_PIXEL_Y_DIMENSION:
shun-iwasawa fdbab5
    case TAG_PIXEL_X_DIMENSION:
shun-iwasawa fdbab5
      // Use largest of height and width to deal with images that have been
shun-iwasawa fdbab5
      // rotated to portrait format.
shun-iwasawa fdbab5
      a = (int)ConvertAnyFormat(ValuePtr, Format);
shun-iwasawa fdbab5
      if (ExifImageWidth < a) ExifImageWidth = a;
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_FOCAL_PLANE_XRES:
shun-iwasawa fdbab5
      FocalplaneXRes = ConvertAnyFormat(ValuePtr, Format);
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_FOCAL_PLANE_UNITS:
shun-iwasawa fdbab5
      switch ((int)ConvertAnyFormat(ValuePtr, Format)) {
shun-iwasawa fdbab5
      case 1:
shun-iwasawa fdbab5
        FocalplaneUnits = 25.4;
shun-iwasawa fdbab5
        break;  // inch
shun-iwasawa fdbab5
      case 2:
shun-iwasawa fdbab5
        // According to the information I was using, 2 means meters.
shun-iwasawa fdbab5
        // But looking at the Cannon powershot's files, inches is the only
shun-iwasawa fdbab5
        // sensible value.
shun-iwasawa fdbab5
        FocalplaneUnits = 25.4;
shun-iwasawa fdbab5
        break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
      case 3:
shun-iwasawa fdbab5
        FocalplaneUnits = 10;
shun-iwasawa fdbab5
        break;  // centimeter
shun-iwasawa fdbab5
      case 4:
shun-iwasawa fdbab5
        FocalplaneUnits = 1;
shun-iwasawa fdbab5
        break;  // millimeter
shun-iwasawa fdbab5
      case 5:
shun-iwasawa fdbab5
        FocalplaneUnits = .001;
shun-iwasawa fdbab5
        break;  // micrometer
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_EXPOSURE_BIAS:
shun-iwasawa fdbab5
      ImageInfo.ExposureBias = (float)ConvertAnyFormat(ValuePtr, Format);
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_WHITEBALANCE:
shun-iwasawa fdbab5
      ImageInfo.Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format);
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_LIGHT_SOURCE:
shun-iwasawa fdbab5
      ImageInfo.LightSource = (int)ConvertAnyFormat(ValuePtr, Format);
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_METERING_MODE:
shun-iwasawa fdbab5
      ImageInfo.MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format);
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_EXPOSURE_PROGRAM:
shun-iwasawa fdbab5
      ImageInfo.ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format);
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_EXPOSURE_INDEX:
shun-iwasawa fdbab5
      if (ImageInfo.ISOequivalent == 0) {
shun-iwasawa fdbab5
        // Exposure index and ISO equivalent are often used interchangeably,
shun-iwasawa fdbab5
        // so we will do the same in jhead.
shun-iwasawa fdbab5
        // http://photography.about.com/library/glossary/bldef_ei.htm
shun-iwasawa fdbab5
        ImageInfo.ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_EXPOSURE_MODE:
shun-iwasawa fdbab5
      ImageInfo.ExposureMode = (int)ConvertAnyFormat(ValuePtr, Format);
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_ISO_EQUIVALENT:
shun-iwasawa fdbab5
      ImageInfo.ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_DIGITALZOOMRATIO:
shun-iwasawa fdbab5
      ImageInfo.DigitalZoomRatio = (float)ConvertAnyFormat(ValuePtr, Format);
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_THUMBNAIL_OFFSET:
shun-iwasawa fdbab5
      ThumbnailOffset      = (unsigned)ConvertAnyFormat(ValuePtr, Format);
shun-iwasawa fdbab5
      DirWithThumbnailPtrs = DirStart;
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_THUMBNAIL_LENGTH:
shun-iwasawa fdbab5
      ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format);
shun-iwasawa fdbab5
      ImageInfo.ThumbnailSizeOffset = ValuePtr - OffsetBase;
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_EXIF_OFFSET:
shun-iwasawa fdbab5
      if (ShowTags) printf("%s    Exif Dir:", IndentString);
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_INTEROP_OFFSET:
shun-iwasawa fdbab5
      if (Tag == TAG_INTEROP_OFFSET && ShowTags)
shun-iwasawa fdbab5
        printf("%s    Interop Dir:", IndentString);
shun-iwasawa fdbab5
      {
shun-iwasawa fdbab5
        unsigned char *SubdirStart;
shun-iwasawa fdbab5
        SubdirStart = OffsetBase + Get32u(ValuePtr);
shun-iwasawa fdbab5
        if (SubdirStart < OffsetBase || SubdirStart > OffsetBase + ExifLength) {
shun-iwasawa fdbab5
          std::cout << "Illegal Exif or interop ofset directory link"
shun-iwasawa fdbab5
                    << std::endl;
shun-iwasawa fdbab5
        } else {
shun-iwasawa fdbab5
          ProcessExifDir(SubdirStart, OffsetBase, ExifLength, NestingLevel + 1);
shun-iwasawa fdbab5
        }
shun-iwasawa fdbab5
        continue;
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_GPSINFO:
shun-iwasawa fdbab5
      if (ShowTags) printf("%s    GPS info dir:", IndentString);
shun-iwasawa fdbab5
      {
shun-iwasawa fdbab5
        unsigned char *SubdirStart;
shun-iwasawa fdbab5
        SubdirStart = OffsetBase + Get32u(ValuePtr);
shun-iwasawa fdbab5
        if (SubdirStart < OffsetBase || SubdirStart > OffsetBase + ExifLength) {
shun-iwasawa fdbab5
          std::cout << "Illegal GPS directory link in Exif" << std::endl;
shun-iwasawa fdbab5
        } else {
shun-iwasawa fdbab5
          // ProcessGpsInfo(SubdirStart, OffsetBase, ExifLength);
shun-iwasawa fdbab5
        }
shun-iwasawa fdbab5
        continue;
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_FOCALLENGTH_35MM:
shun-iwasawa fdbab5
      // The focal length equivalent 35 mm is a 2.2 tag (defined as of April
shun-iwasawa fdbab5
      // 2002)
shun-iwasawa fdbab5
      // if its present, use it to compute equivalent focal length instead of
shun-iwasawa fdbab5
      // computing it from sensor geometry and actual focal length.
shun-iwasawa fdbab5
      ImageInfo.FocalLength35mmEquiv =
shun-iwasawa fdbab5
          (unsigned)ConvertAnyFormat(ValuePtr, Format);
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_DISTANCE_RANGE:
shun-iwasawa fdbab5
      // Three possible standard values:
shun-iwasawa fdbab5
      //   1 = macro, 2 = close, 3 = distant
shun-iwasawa fdbab5
      ImageInfo.DistanceRange = (int)ConvertAnyFormat(ValuePtr, Format);
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
#endif
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_X_RESOLUTION:
shun-iwasawa fdbab5
      if (NestingLevel ==
shun-iwasawa fdbab5
          0) {  // Only use the values from the top level directory
shun-iwasawa fdbab5
        ImageInfo.xResolution = (float)ConvertAnyFormat(ValuePtr, Format);
shun-iwasawa fdbab5
        // if yResolution has not been set, use the value of xResolution
shun-iwasawa fdbab5
        if (ImageInfo.yResolution == 0.0)
shun-iwasawa fdbab5
          ImageInfo.yResolution = ImageInfo.xResolution;
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_Y_RESOLUTION:
shun-iwasawa fdbab5
      if (NestingLevel ==
shun-iwasawa fdbab5
          0) {  // Only use the values from the top level directory
shun-iwasawa fdbab5
        ImageInfo.yResolution = (float)ConvertAnyFormat(ValuePtr, Format);
shun-iwasawa fdbab5
        // if xResolution has not been set, use the value of yResolution
shun-iwasawa fdbab5
        if (ImageInfo.xResolution == 0.0)
shun-iwasawa fdbab5
          ImageInfo.xResolution = ImageInfo.yResolution;
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    case TAG_RESOLUTION_UNIT:
shun-iwasawa fdbab5
      if (NestingLevel ==
shun-iwasawa fdbab5
          0) {  // Only use the values from the top level directory
shun-iwasawa fdbab5
        ImageInfo.ResolutionUnit = (int)ConvertAnyFormat(ValuePtr, Format);
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
      break;
shun-iwasawa fdbab5
    }
shun-iwasawa fdbab5
  }
shun-iwasawa fdbab5
shun-iwasawa fdbab5
#ifdef ReadAllTags
shun-iwasawa fdbab5
  {
shun-iwasawa fdbab5
    // In addition to linking to subdirectories via exif tags,
shun-iwasawa fdbab5
    // there's also a potential link to another directory at the end of each
shun-iwasawa fdbab5
    // directory.  this has got to be the result of a committee!
shun-iwasawa fdbab5
    unsigned char *SubdirStart;
shun-iwasawa fdbab5
    unsigned Offset;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    if (DIR_ENTRY_ADDR(DirStart, NumDirEntries) + 4 <=
shun-iwasawa fdbab5
        OffsetBase + ExifLength) {
shun-iwasawa fdbab5
      Offset = Get32u(DirStart + 2 + 12 * NumDirEntries);
shun-iwasawa fdbab5
      if (Offset) {
shun-iwasawa fdbab5
        SubdirStart = OffsetBase + Offset;
shun-iwasawa fdbab5
        if (SubdirStart > OffsetBase + ExifLength || SubdirStart < OffsetBase) {
shun-iwasawa fdbab5
          if (SubdirStart > OffsetBase &&
shun-iwasawa fdbab5
              SubdirStart < OffsetBase + ExifLength + 20) {
shun-iwasawa fdbab5
            // Jhead 1.3 or earlier would crop the whole directory!
shun-iwasawa fdbab5
            // As Jhead produces this form of format incorrectness,
shun-iwasawa fdbab5
            // I'll just let it pass silently
shun-iwasawa fdbab5
            if (ShowTags)
shun-iwasawa fdbab5
              printf("Thumbnail removed with Jhead 1.3 or earlier\n");
shun-iwasawa fdbab5
          } else {
shun-iwasawa fdbab5
            std::cout << "Illegal subdirectory link in Exif header"
shun-iwasawa fdbab5
                      << std::endl;
shun-iwasawa fdbab5
          }
shun-iwasawa fdbab5
        } else {
shun-iwasawa fdbab5
          if (SubdirStart <= OffsetBase + ExifLength) {
shun-iwasawa fdbab5
            if (ShowTags) printf("%s    Continued directory ", IndentString);
shun-iwasawa fdbab5
            ProcessExifDir(SubdirStart, OffsetBase, ExifLength,
shun-iwasawa fdbab5
                           NestingLevel + 1);
shun-iwasawa fdbab5
          }
shun-iwasawa fdbab5
        }
shun-iwasawa fdbab5
        if (Offset > ImageInfo.LargestExifOffset) {
shun-iwasawa fdbab5
          ImageInfo.LargestExifOffset = Offset;
shun-iwasawa fdbab5
        }
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
    } else {
shun-iwasawa fdbab5
      // The exif header ends before the last next directory pointer.
shun-iwasawa fdbab5
    }
shun-iwasawa fdbab5
  }
shun-iwasawa fdbab5
shun-iwasawa fdbab5
  if (ThumbnailOffset) {
shun-iwasawa fdbab5
    ImageInfo.ThumbnailAtEnd = FALSE;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    if (DumpExifMap) {
shun-iwasawa fdbab5
      printf("Map: %05d-%05d: Thumbnail\n", ThumbnailOffset,
shun-iwasawa fdbab5
             ThumbnailOffset + ThumbnailSize);
shun-iwasawa fdbab5
    }
shun-iwasawa fdbab5
shun-iwasawa fdbab5
    if (ThumbnailOffset <= ExifLength) {
shun-iwasawa fdbab5
      if (ThumbnailSize > ExifLength - ThumbnailOffset) {
shun-iwasawa fdbab5
        // If thumbnail extends past exif header, only save the part that
shun-iwasawa fdbab5
        // actually exists.  Canon's EOS viewer utility will do this - the
shun-iwasawa fdbab5
        // thumbnail extracts ok with this hack.
shun-iwasawa fdbab5
        ThumbnailSize = ExifLength - ThumbnailOffset;
shun-iwasawa fdbab5
        if (ShowTags) printf("Thumbnail incorrectly placed in header\n");
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
      // The thumbnail pointer appears to be valid.  Store it.
shun-iwasawa fdbab5
      ImageInfo.ThumbnailOffset = ThumbnailOffset;
shun-iwasawa fdbab5
      ImageInfo.ThumbnailSize   = ThumbnailSize;
shun-iwasawa fdbab5
shun-iwasawa fdbab5
      if (ShowTags) {
shun-iwasawa fdbab5
        printf("Thumbnail size: %u bytes\n", ThumbnailSize);
shun-iwasawa fdbab5
      }
shun-iwasawa fdbab5
    }
shun-iwasawa fdbab5
  }
shun-iwasawa fdbab5
#endif
shun-iwasawa fdbab5
}