/*
 * Fix buggy GPS EXIF of ZenFone3 (UL-Z016-WW-4.12.40.1698)
 * <sengoku@gcd.org>
 * Thanks to Mr. tanabe
 * http://dsas.blog.klab.org/archives/52123322.html
 * gcc -o fix_GPSInfo fix_GPSInfo.c exif.c

diff --git a/exif.c b/exif.c
index dc239c8..da15306 100644
--- a/exif.c
+++ b/exif.c
@@ -1312,19 +1312,19 @@ static unsigned int swab32(unsigned int ui)
     ((ui >> 8)  & 0x0000FF00) | ((ui >> 24) & 0x000000FF);
 }
 
-static unsigned short fix_short(unsigned short us)
+unsigned short fix_short(unsigned short us)
 {
     return (dataIsLittleEndian() !=
         systemIsLittleEndian()) ? swab16(us) : us;
 }
 
-static unsigned int fix_int(unsigned int ui)
+unsigned int fix_int(unsigned int ui)
 {
     return (dataIsLittleEndian() !=
         systemIsLittleEndian()) ? swab32(ui) : ui;
 }
 
-static int seekToRelativeOffset(FILE *fp, unsigned int ofs)
+int seekToRelativeOffset(FILE *fp, unsigned int ofs)
 {
     static int start = offsetof(APP1_HEADER, tiff);
     return fseek(fp, (App1StartOffset + start) + ofs, SEEK_SET);

 */

#include <stdio.h>
#include <errno.h>
#include "exif.h"

typedef struct {
    unsigned short tag;
    unsigned short type;
    unsigned int count;
    unsigned int offset;
} IFD_TAG;

unsigned short fix_short(unsigned short);
unsigned int fix_int(unsigned int);
int seekToRelativeOffset(FILE *, unsigned int);

void fix_GPSInfo(char *fname, FILE *fp, int lonRef, int verbose, int dryrun) {
    void **ifdArray;
    TagNodeInfo *tag;
    unsigned short tagCount;
    IFD_TAG tagf;
    FILE *fpw = NULL;
    int i, result;
    int ofs = -1;

    ifdArray = createIfdTableArray(fname, &result);
    if (!ifdArray) {
	if (result == ERR_INVALID_JPEG) {
	    if (verbose) printf("%s: not JPEG\n", fname);
	} else {
	    fprintf(stderr, "%s: createIfdTableArray %d\n", fname, result);
	}
	return;
    }
    tag = getTagInfo(ifdArray, IFD_0TH, TAG_GPSInfoIFDPointer);
    if (tag) {
	if (tag->error) {
	    fprintf(stderr, "%s: getTagInfo %d\n", fname, tag->error);
	} else {
	    ofs = (unsigned short)tag->numData[0];
        }
        freeTagInfo(tag);
    }
    if (ofs < 0) {
	if (verbose) printf("%s: no GPS Info\n", fname);
	goto end;
    }
    if (seekToRelativeOffset(fp, ofs) != 0 ||
        fread(&tagCount, 1, sizeof(short), fp) < sizeof(short)) {
	fprintf(stderr, "%s: seekToRelativeOffset\n", fname);
	goto end;
    }
    tagCount = fix_short(tagCount);
    for (i=0; i < tagCount; i++) {
	ofs = ftell(fp);
	if (fread(&tagf, 1, sizeof(tagf), fp) < sizeof(tagf)) {
	    fprintf(stderr, "%s: fread tagf #%d\n", fname, i);
	    break;
	}
	if (fix_short(tagf.tag) == TAG_GPSLongitudeRef
	    && fix_int(tagf.offset) == 0x73650000) {	/* [se] */
	    if (!dryrun && !fpw) {
		fpw = fopen(fname, "r+");
		if (!fpw) {
		    fprintf(stderr, "%s: fopen write %d\n", fname, errno);
		    break;
		}
	    }
	    tagf.count = fix_int(2);
	    tagf.offset = fix_int(lonRef);
	    if (dryrun) {
		printf("%s: fix\n", fname);
	    } else {
		fseek(fpw, ofs, SEEK_SET);
		if (fwrite(&tagf, 1, sizeof(tagf), fpw) < sizeof(tagf)) {
		    fprintf(stderr, "%s: fwrite %d\n", fname, errno);
		    break;
		}
	    }
	}
	if (verbose > 1) {
	    printf("%s: tag=%d type=%d count=%d offset=%x\n", fname,
		   fix_short(tagf.tag), fix_short(tagf.type),
		   fix_int(tagf.count), fix_int(tagf.offset));
	}
    }
    if (fpw) {
	if (fclose(fpw) == 0) {
	    printf("%s: fixed\n", fname);
	}
	fpw = NULL;
    }
end:
    if (ifdArray) freeIfdTableArray(ifdArray);
}

void help(char *com) {
    fprintf(stderr, "Usage: <opt> %s <file>...\n"
	    "opt:   -v     ; verbose\n"
	    "       -n     ; dry run\n"
	    "       -W     ; West Longitude\n"
	    "       -E     ; East Longitude\n"
	    , com);
}

int main(int argc, char *argv[]) {
    FILE *fp;
    int i;
    int lonRef = 0x45000000;	/* [E] */
    int verbose = 0;
    int dryrun = 0;
    for (i=1; i < argc; i++) {
	if (argv[i][0] == '-') {
	    char *p = argv[i];
	    while (*++p) {
		switch(*p) {
		case 'E':
		    lonRef = 0x45000000;	/* [W] */
		    break;
		case 'W':
		    lonRef = 0x57000000;	/* [W] */
		    break;
		case 'v':
		    verbose++;
		    break;
		case 'n':
		    dryrun++;
		    break;
		default:
		    help(argv[0]);
		    return 1;
		}
	    }
	} else {
	    fp = fopen(argv[i], "r");
	    if (!fp) {
		fprintf(stderr, "Can't open: %s\n", argv[i]);
		help(argv[0]);
		return 1;
	    }
	    fix_GPSInfo(argv[i], fp, lonRef, verbose, dryrun);
	    fclose(fp);
	}
    }
    return 0;
}

/*
  For Gnu Emacs.
  Local Variables:
  tab-width: 8
  c-basic-offset: 4
  End:
*/
