Emit EXIF information for Surface images for old and new formats. master
authorPat Thoyts <patthoyts@users.sourceforge.net>
Mon, 22 Sep 2025 13:32:23 +0000 (14:32 +0100)
committerPat Thoyts <patthoyts@users.sourceforge.net>
Mon, 22 Sep 2025 13:32:23 +0000 (14:32 +0100)
CMakeLists.txt
exif.c
exif.h
jpegrdr.c
jpegrdr.h [new file with mode: 0644]
srfdump.c
srfinfo.c
surfacefile.h

index a136d62848984b7734b42c41fe7546553770a392..099e3bdc6a17773df15cf931e539889850cd82f1 100644 (file)
@@ -1,7 +1,7 @@
 cmake_minimum_required(VERSION 3.20)
 project(srfdump VERSION 1.0)
 
-add_executable(srfdump srfdump.c surfacefile.h)
+add_executable(srfdump srfdump.c jpegrdr.c exif.c jpegrdr.h exif.h surfacefile.h)
 target_compile_features(srfdump PRIVATE c_std_11)
 if (WIN32)
   target_compile_definitions(srfdump PRIVATE -DWIN32 -D_CRT_SECURE_NO_WARNINGS)
@@ -20,7 +20,7 @@ endif()
 add_executable(jpegrdr jpegrdr.c exif.c exif.h)
 target_compile_features(jpegrdr PRIVATE c_std_11)
 if (WIN32)
-  target_compile_definitions(jpegrdr PRIVATE -DWIN32 -D_CRT_SECURE_NO_WARNINGS)
+  target_compile_definitions(jpegrdr PRIVATE -DWIN32 _CRT_SECURE_NO_WARNINGS JPEGRDR_MAIN)
 else()
   #target_compile_options(jpegrdr PRIVATE -pedantic)
 endif()
diff --git a/exif.c b/exif.c
index 97decbbb5555f743dccd39fa0b2000b1ecb03ec9..3dfe2ff98d988c605e63189109529458dcd353ea 100644 (file)
--- a/exif.c
+++ b/exif.c
@@ -13,9 +13,9 @@
 
 static const char *EXIF_TYPE_NAMES[11] = {
     // 0  1      2        3        4       5
-    "?", "byte", "ascii", "short", "long", "rational",
+    "?", "BYTE", "ASCII", "USHORT", "ULONG", "URATIONAL",
     // 6  7        8    9        10
-    "char", "binary", "sshort", "slong", "srational"
+    "CHAR", "BINARY", "SHORT", "LONG", "RATIONAL"
 };
 
 static bool big_endian_data = false;
@@ -185,16 +185,72 @@ static size_t exif_parse_item(const uint8_t *buffer, const uint8_t *entry, exif_
     return item_count;
 }
 
-void exif_print_item(const exif_item_t* item)
+typedef struct tag_name_t {
+    uint16_t tag;
+    const char *name;
+} tag_name_t;
+
+tag_name_t tag_names[] = {
+    { 0x0100, "ImageWidth" },
+    { 0x0101, "ImageHeight" },
+    { 0x0102, "BitsPerSample" },
+    { 0x0103, "Compression" },
+    { 0x0106, "PhotometricInterpretation" },
+    { 0x0107, "Thresholding" },
+    { 0x010e, "ImageDescription" },
+    { 0x010f, "Make" },
+    { 0x0112, "Orientation" },
+    { 0x011a, "XResolution" },
+    { 0x011b, "YResolution" },
+    { 0x011c, "PlanarConfiguration" },
+    { 0x011e, "XPosition" },
+    { 0x011f, "YPosition" },
+    { 0x0128, "ResolutionUnit" },
+    { 0x0131, "Software" },
+    { 0x0132, "ModifyDate" },
+    { 0xa001, "ColorSpace" },
+    { 0xa002, "ExifImageWidth" },
+    { 0xa003, "ExifImageHeight" },
+    { 0xa20e, "FocalPlaneXResolution" },
+    { 0xa20f, "FocalPlaneYResolution" },
+    { 0xa210, "FocalPlaneResolutionUnit" },
+    /* Renishaw WiRE Custom Tags */
+    { 0xfea0, "WiREPosition" },
+    { 0xfea1, "WiREFoV" },
+    { 0xfea2, "WiREObjective" },
+    { 0xfea3, "WiRELUTLimits" },
+    { 0xfea4, "WiRERotationAngle" },
+    { 0xfea5, "WiRERotationCenter" },
+    { 0xfea6, "WiREZPosition" }
+};
+
+void exif_print_item(FILE *fp, const exif_item_t* item, const char *prefix)
 {
-    fprintf(stderr, "  %04x %-10s %u: ",
-        item->tag, EXIF_TYPE_NAMES[item->type], item->count);
+    char name_buf[5] = {0};
+    const char *name = NULL;
+    for (int ndx = 0; ndx < sizeof(tag_names)/sizeof(tag_names[0]); ++ndx)
+    {
+        if (item->tag == tag_names[ndx].tag)
+        {
+            name = tag_names[ndx].name;
+            break;
+        }
+    }
+    if (name == NULL)
+    {
+        sprintf(name_buf, "%04x", item->tag);
+        name = name_buf;
+    }
+
+    fprintf(fp, "%s%s %s[%u] ",
+        prefix == NULL ? "" : prefix,
+        name, EXIF_TYPE_NAMES[item->type], item->count);
     if (item->type == EXIF_TYPE_ASCII)
-        fprintf(stderr, "'%s'", item->u.strVal);
+        fprintf(fp, "'%s'", item->u.strVal);
     else if ((item->type == EXIF_TYPE_UNDEFINED
               || item->type == EXIF_TYPE_BYTE) && item->count > 8)
     {
-        fprintf(stderr, "[%hu bytes]", item->count);
+        fprintf(fp, "[%hu bytes]", item->count);
     }
     else
     {
@@ -204,45 +260,45 @@ void exif_print_item(const exif_item_t* item)
             {
             case EXIF_TYPE_BYTE:
             case EXIF_TYPE_UNDEFINED:
-                fprintf(stderr, "%02x", item->u.values[n].byteVal);
+                fprintf(fp, "%02x", item->u.values[n].byteVal);
                 break;
             case EXIF_TYPE_SBYTE:
-                fprintf(stderr, "%d", item->u.values[n].cVal);
+                fprintf(fp, "%d", item->u.values[n].cVal);
                 break;
             case EXIF_TYPE_SSHORT:
-                fprintf(stderr, "%hd", item->u.values[0].hVal);
+                fprintf(fp, "%hd", item->u.values[0].hVal);
                 break;
             case EXIF_TYPE_SHORT:
-                fprintf(stderr, "%hu", item->u.values[0].uhVal);
+                fprintf(fp, "%hu", item->u.values[0].uhVal);
                 break;
             case EXIF_TYPE_LONG:
-                fprintf(stderr, "%u", item->u.values[0].ulVal);
+                fprintf(fp, "%u", item->u.values[0].ulVal);
                 break;
             case EXIF_TYPE_SLONG:
-                fprintf(stderr, "%d", item->u.values[0].lVal);
+                fprintf(fp, "%d", item->u.values[0].lVal);
                 break;
             case EXIF_TYPE_RATIONAL:
-                fprintf(stderr, "%u/%u",
+                fprintf(fp, "%u/%u",
                     item->u.values[n].rationalVal.numerator, item->u.values[n].rationalVal.denominator);
                 break;
             case EXIF_TYPE_SRATIONAL:
-                fprintf(stderr, "%d/%d",
+                fprintf(fp, "%d/%d",
                     item->u.values[n].srationalVal.numerator, item->u.values[n].srationalVal.denominator);
                 break;
             case EXIF_TYPE_FLOAT32:
-                fprintf(stderr, "%f", item->u.values[n].fltVal);
+                fprintf(fp, "%f", item->u.values[n].fltVal);
                 break;
             case EXIF_TYPE_FLOAT64:
-                fprintf(stderr, "%lf", item->u.values[n].dblVal);
+                fprintf(fp, "%lf", item->u.values[n].dblVal);
                 break;
             case EXIF_TYPE_ASCII:
                 break;
             }
             if (n < item->count - 1)
-                fprintf(stderr, ", ");
+                fprintf(fp, ", ");
         }
     }
-    fprintf(stderr, "\n");
+    fprintf(fp, "\n");
 }
 
 void exif_free(exif_item_t *items)
diff --git a/exif.h b/exif.h
index 79cb54fecad9dd9f231bb668f7cefc6627d2735a..cc15e4563764b7e81555514653d34e2a21b620ff 100644 (file)
--- a/exif.h
+++ b/exif.h
@@ -2,6 +2,7 @@
 #define _exif_h_INCLUDE
 
 #include <stdint.h>
+#include <stdio.h>
 
 typedef enum exif_type_t {
     EXIF_TYPE_BYTE = 1, // uint8_t
@@ -52,7 +53,7 @@ typedef struct exif_item_t {
     } u;
 } exif_item_t;
 
-void exif_print_item(const exif_item_t* item);
+void exif_print_item(FILE *fp, const exif_item_t* item, const char *prefix);
 exif_item_t *exif_parse(const uint8_t *data, size_t datalen);
 void exif_free(exif_item_t *items);
 
index 8642b579a3228f8626f8b97ea7862c108736bcd7..99f315943e4e31fd1a0948c7e8e2b85c653fed0a 100644 (file)
--- a/jpegrdr.c
+++ b/jpegrdr.c
@@ -3,8 +3,10 @@
 #include <stdio.h>
 #include <stdint.h>
 #include <stdbool.h>
+#include <stdarg.h>
 #include <assert.h>
 #include "exif.h"
+#include "jpegrdr.h"
 
 typedef uint16_t marker_t;
 
@@ -91,14 +93,15 @@ jfif_frame_t* jfif_read_frame(FILE* fp, size_t len)
     return frame;
 }
 
-void jfif_read_app0(FILE *fp, size_t len)
+exif_item_t * jfif_read_app0(FILE *fp, size_t len, logfunc_t log)
 {
+    exif_item_t *exif_items = NULL;
     char label[6] = {0};
     size_t count = fread(label, 1, 5, fp);
     if (strncmp("JFIF", label, 4) == 0)
     {
         jfif_header_t *hdr = jfif_read_header(fp, len - 5);
-        fprintf(stderr, "  %s version %u.%u density %hu,%hu %s\n",
+        log("  %s version %u.%u density %hu,%hu %s\n",
             label, hdr->major_version, hdr->minor_version,
             hdr->x_density, hdr->y_density,
             hdr->units == 1 ? "dpi" : "???");
@@ -107,33 +110,29 @@ void jfif_read_app0(FILE *fp, size_t len)
     else if (strncmp("Exif", label, 4) == 0)
     {
         uint8_t pad = 0;
-        fprintf(stderr, "  %s\n", label);
         fread(&pad, 1, 1, fp); // read the pad char
 
         // read the EXIF data into a buffer
         uint8_t *data = (uint8_t*)calloc(len - 6, 1);
         assert(data != NULL);
         fread(data, len - 6, 1, fp);
-        exif_item_t *items = exif_parse(data, len - 6);
+        exif_items = exif_parse(data, len - 6);
         free(data);
-
-        // print the items
-        for (exif_item_t* item = items; item; item = item->nextPtr)
-            exif_print_item(item);
-        exif_free(items);
     }
     else
     {
-        fprintf(stderr, "  %s\n", label);
+        log("  %s\n", label);
         fseek(fp, (long)(len - 5), SEEK_CUR);
     }
+    return exif_items;
 }
 
+
 //
 // JFIF images have SOI APP0[JFIF] ...
 // EXIF sections use APP0 as well and in JFIF must follow the JFIF segment.
 //
-void get_jpeg_exif(FILE *fp)
+exif_item_t *jpegrdr_get_exif(FILE *fp, logfunc_t log)
 {
     size_t start = ftell(fp);
     size_t len = 0;
@@ -145,59 +144,77 @@ void get_jpeg_exif(FILE *fp)
         {
             case JFIF_SOI:
                 len = 0;
-                fprintf(stderr, "%08lx SOI\n", ftell(fp));
+                log("%08lx SOI\n", ftell(fp));
                 break;
             case JFIF_SOF0:
             {
                 len = jfif_get_size(fp);
-                fprintf(stderr, "%08lx SOF0 %04zx\n", ftell(fp), len);
+                log("%08lx SOF0 %04zx\n", ftell(fp), len);
                 jfif_frame_t* frame = jfif_read_frame(fp, len);
-                fprintf(stderr, "  image size %u,%u\n", frame->width, frame->height);
+                log("  image size %u,%u\n", frame->width, frame->height);
                 free(frame);
                 len = 0;
                 break;
             }
             case JFIF_DQT:
                 len = jfif_get_size(fp);
-                fprintf(stderr, "%08lx DQT %04zx\n", ftell(fp), len);
+                log("%08lx DQT %04zx\n", ftell(fp), len);
                 break;
             case JFIF_DHT:
                 len = jfif_get_size(fp);
-                fprintf(stderr, "%08lx DHT %04zx\n", ftell(fp), len);
+                log("%08lx DHT %04zx\n", ftell(fp), len);
                 break;
             case JFIF_SOS:
                 len = jfif_get_size(fp);
-                fprintf(stderr, "%08lx SOS %04zx\n", ftell(fp), len);
-                return;
+                log("%08lx SOS %04zx\n", ftell(fp), len);
+                return NULL;
             default:
             {
                 if (JFIF_IS_RST(mark) && JFIF_GET_RST_N(mark) < 8)
                 {
                     len = jfif_get_size(fp);
-                    fprintf(stderr, "%08lx RST%d %04zx\n", ftell(fp), JFIF_GET_RST_N(mark), len);
+                    log("%08lx RST%d %04zx\n", ftell(fp), JFIF_GET_RST_N(mark), len);
                 }
                 else if (JFIF_IS_APP(mark))
                 {
                     len = jfif_get_size(fp);
-                    fprintf(stderr, "%08lx APP%u %04zx\n", ftell(fp), JFIF_GET_APP_N(mark), len);
-                    jfif_read_app0(fp, len);
+                    log("%08lx APP%u %04zx\n", ftell(fp), JFIF_GET_APP_N(mark), len);
+                    exif_item_t *items = jfif_read_app0(fp, len, log);
+                    if (items != NULL)
+                        return items;
                     len = 0;
                 }
                 else
                 {
-                    fprintf(stderr, "OOPS %04x\n", mark);
+                    log("OOPS %04x\n", mark);
                     exit(1);
                 }
             }
         }
         fseek(fp, (long)len, SEEK_CUR);
     }
+    return NULL;
+}
+
+#ifdef JPEGRDR_MAIN
+
+static void _cdecl logger(const char *format, ...)
+{
+    va_list args;
+    va_start(args, format);
+    vfprintf(stderr, format, args);
+    va_end(args);
 }
 
 int main(int argc, char const * const argv[])
 {
     FILE *fp = fopen(argv[1], "rb");
-    get_jpeg_exif(fp);
+    exif_item_t *items = jpegrdr_get_exif(fp, logger);
+    for (exif_item_t *item = items; item; item = item->nextPtr)
+        exif_print_item(stderr, item, "  ");
+    exif_free(items);
     fclose(fp);
     return 0;
 }
+
+#endif
diff --git a/jpegrdr.h b/jpegrdr.h
new file mode 100644 (file)
index 0000000..546416e
--- /dev/null
+++ b/jpegrdr.h
@@ -0,0 +1,11 @@
+#ifndef _jpegrdr_h_INCLUDE
+#define _jpegrdr_h_INCLUDE
+
+#include <stdio.h>
+#include "exif.h"
+
+typedef void (_cdecl * logfunc_t)(const char *format, ...);
+
+exif_item_t *jpegrdr_get_exif(FILE *fp, logfunc_t log);
+
+#endif /* _jpegrdr_h_INCLUDE */
index ce297faa8d3a98ffc848d31b0cc8ad828b1fbc3b..736ceda04781078ef2c7952bd9cb0840478ecb26 100644 (file)
--- a/srfdump.c
+++ b/srfdump.c
@@ -6,11 +6,14 @@
  */
 
 #include <stdlib.h>
+#include <stdarg.h>
 #include <string.h>
 #include <stdio.h>
 #include <sys/stat.h>
 #include <assert.h>
 #include "surfacefile.h"
+#include "exif.h"
+#include "jpegrdr.h"
 
 #ifdef WIN32
 #include <direct.h>
 #include "compat/tchar.h"
 #endif /* !WIN32 */
 
+static void _cdecl logger(const char *format, ...)
+{
+    va_list args;
+    va_start(args, format);
+    //vfprintf(stderr, format, args);
+    va_end(args);
+}
+
 static size_t fcopy(FILE *src, FILE *dst, size_t count)
 {
     char buf[4096];
@@ -33,7 +44,26 @@ static size_t fcopy(FILE *src, FILE *dst, size_t count)
     return total;
 }
 
-// For a single image the table just contains the end of the image.
+// Read a BSTR record from the current file position/.
+// Reads a uint32_t to get the size in bytes
+// Then allocates and returns a Unicode string with terminating NUL
+static wchar_t* read_name(FILE* fp)
+{
+    uint32_t length = 0;
+    fread(&length, sizeof(uint32_t), 1, fp);
+    wchar_t* name = NULL;
+    if (length)
+    {
+        name = (wchar_t*)calloc(1, length + sizeof(wchar_t));
+        fread(name, 1, length, fp);
+    }
+    return name;
+}
+
+// Read the image table and return an array of absolute file position offsets
+// to the start of each image information structure.
+// The final element in the array should point to the end of the final image
+// ie: the end of the file.
 static uint32_t * ReadImageTable(SurfaceFileHeader *hdrPtr, FILE *fp)
 {
     uint32_t *table = NULL;
@@ -46,11 +76,20 @@ static uint32_t * ReadImageTable(SurfaceFileHeader *hdrPtr, FILE *fp)
             exit(1);
         }
         size_t check = fread(table + 1, sizeof(uint32_t), hdrPtr->num_images, fp);
-        // set first image to the end of the table.
+        uint32_t table_end = ftell(fp);
+        // read the directory name from after the offset table for rev7+
+        if (hdrPtr->revision > 6)
+        {
+            hdrPtr->dirname = read_name(fp);
+        }
+        uint32_t name_end = ftell(fp);
+        // set first image offset to the current position (after all header info)
         table[0] = (long)ftell(fp);
-        // convert offsets from relative to table to file absolute positions
+        // The offsets in the table are relative to the end of the table but do not account for the
+        // directory name being inserted at the end.
+        // Convert offsets from relative to table to file absolute positions
         for (uint32_t n = 1; n < hdrPtr->num_images + 1; ++n)
-            table[n] += table[0];
+            table[n] += table_end;
     }
     return table;
 }
@@ -94,8 +133,14 @@ static SurfacePoint * ReadPoints(SurfaceFileHeader *hdrPtr, FILE *fp)
     return points;
 }
 
-static int SrfDump(FILE *fp)
+static int SrfDump(const char *filename)
 {
+    FILE *fp = fopen(filename, "rb");
+    if (fp == NULL) {
+        perror("fopen");
+        return 1;
+    }
+
     int r = 0;
     SurfaceFileHeader hdr = {0};
     fread(&hdr, sizeof(SurfaceFileHeaderRev0), 1, fp);
@@ -108,6 +153,8 @@ static int SrfDump(FILE *fp)
         hdrsize += sizeof(SurfaceFileHeaderRev4);
     if (hdr.revision > 4)
         hdrsize += sizeof(SurfaceFileHeaderRev5);
+    if (hdr.revision > 8)
+        hdrsize += sizeof(SurfaceFileHeaderRev9);
     fseek(fp, 0, SEEK_SET);
     fread(&hdr, hdrsize, 1, fp);
 
@@ -129,10 +176,22 @@ static int SrfDump(FILE *fp)
         printf("use background removal: %s\n", hdr.use_background_removal ? "true" : "false");
     if (hdr.revision > 4)
         printf("extrapolation mode: %d\n", (int)hdr.extrapolation_mode);
+    if (hdr.revision > 8)
+    {
+        printf("objective: %.3lf\n", hdr.objective);
+        printf("motor info:\n");
+        printf("  stage min: %g, %g, %g\n",
+            hdr.motor_info.stage_min.x, hdr.motor_info.stage_min.y, hdr.motor_info.stage_min.z);
+        printf("  stage max: %g, %g, %g\n",
+            hdr.motor_info.stage_max.x, hdr.motor_info.stage_max.y, hdr.motor_info.stage_max.z);
+        printf("  reference: %g, %g\n", hdr.motor_info.reference.x, hdr.motor_info.reference.y);
+        printf("  resolution: %g, %g\n", hdr.motor_info.resolution.x, hdr.motor_info.resolution.y);
+    }
 
     SurfacePoint *points = ReadPoints(&hdr, fp);
     if (points)
     {
+        printf("points:\n");
         for (uint32_t n = 0; n < hdr.num_points; ++n)
         {
             printf("  point %u: %.6g %.6g %.6g\n",
@@ -149,9 +208,9 @@ static int SrfDump(FILE *fp)
     }
 
     uint32_t image_table = ftell(fp);
-    printf("  image table: %#08x\n", image_table);
+    printf("image table: %#08x\n", image_table);
     uint32_t *offsets = ReadImageTable(&hdr, fp);
-    uint32_t table_base = ftell(fp);
+
     if (offsets)
     {
         if (hdr.num_images < 10)
@@ -159,27 +218,67 @@ static int SrfDump(FILE *fp)
             for (uint32_t n = 0; n < hdr.num_images + 1; ++n)
                 printf("    %u: %#08x\n", n, offsets[n]);
         }
-        printf("  image base: %#08x\n", table_base);
+#if 0
 #ifdef WIN32
         _tmkdir(_T("images"));
 #else
         mkdir("images", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
 #endif // !WIN32
+#endif
         for (uint32_t n = 0; n < hdr.num_images; ++n)
         {
+            SurfaceImageInfo info = { 0 };
+            fseek(fp, offsets[n], SEEK_SET);
+            fread(&info, sizeof(SurfaceImageInfo5), 1, fp);
+            if (hdr.revision >= 8)
+            {
+                SurfaceImageInfo8 *ptr = (SurfaceImageInfo8 *)&info.rotation_angle;
+                fread(ptr, sizeof(SurfaceImageInfo8), 1, fp);
+            }
+            if (hdr.revision >= 9)
+            {
+                SurfaceImageInfo9 *ptr = (SurfaceImageInfo9 *)&info.objective;
+                fread(ptr, sizeof(SurfaceImageInfo9), 1, fp);
+            }
+
+            uint32_t image_size = 0;
+            exif_item_t *items = NULL;
+            if (hdr.revision < 7)
+            {
+                fread(&image_size, sizeof(uint32_t), 1, fp);
+                items = jpegrdr_get_exif(fp, logger);
+            }
+            else
+            {
+                fread(&info.width, sizeof(uint32_t), 2, fp);
+                info.imagepath = read_name(fp);
+                info.backuppath = read_name(fp);
+
+                wchar_t wfilename[260] = {0};
+
+                mbstowcs(wfilename, filename, strlen(filename));
+                wchar_t *ext = wcsrchr(wfilename, L'\\');
+                assert(ext != NULL);
+                ext[1] = L'\0';
+                wcscat(wfilename, hdr.dirname);
+                wcscat(wfilename, L"\\");
+                wcscat(wfilename, info.imagepath);
+                FILE *imagefp = _wfopen(wfilename, L"rb");
+                assert(imagefp != NULL);
+                items = jpegrdr_get_exif(imagefp, logger);
+                fclose(imagefp);
+            }
+
+#if 0
             TCHAR name[32 + 16] = {0};
             _stprintf(name, _T("images/img%u.jpg"), n);
+
             FILE *fp2 = _tfopen(name, "wb");
             if (fp2 == NULL) {
                 _tperror("fopen image file");
                 exit(1);
             }
-
-            uint32_t image_section_size = offsets[n + 1] - offsets[n] - sizeof(SurfaceImageInfo);
-            SurfaceImageInfo info = { 0 };
-            fseek(fp, offsets[n], SEEK_SET);
-            fread(&info, sizeof(SurfaceImageInfo), 1, fp);
-            size_t len = fcopy(fp, fp2, info.image_size);
+            size_t len = fcopy(fp, fp2, image_size);
             fclose(fp2);
             // For revision > 2 we have the size of the 'original' image if present and the image data.
             if (hdr.revision > 2)
@@ -190,39 +289,55 @@ static int SrfDump(FILE *fp)
                 len += sizeof(backup_size) + backup_size; // account for 'backup image' and its size
                 printf("  orig:%u: %u\n", n, backup_size);
             }
-
-            printf("  img:%u: %#08x @(%.3f,%.3f,%.3f) fov:%.3f,%.3f res:%u,%u off:%d,%d siz:%#08x\n",
-                n, offsets[n],
+#endif
+            printf("  img %u: %u@%#08x @(%.3f,%.3f,%.3f) fov:%.3f,%.3f res:%u,%u off:%d,%d\n",
+                n, image_size, offsets[n],
                 info.point.x, info.point.y, info.point.z,
                 info.image_width_microns, info.image_height_microns,
                 info.resolution_x, info.resolution_y,
-                info.xoffset, info.yoffset, info.image_size);
-            if ((len - image_section_size) != 0)
+                info.xoffset, info.yoffset);
+            if (hdr.revision >= 7)
+            {
+                char path[256] = { 0 };
+                wcstombs(path, info.imagepath, sizeof(path));
+                printf("    image: %s\n", path);
+                if (info.backuppath != NULL && info.backuppath[0] != L'\0')
+                {
+                    wcstombs(path, info.backuppath, sizeof(path));
+                    printf("    original: %s\n", path);
+                }
+            }
+            if (items)
             {
-                printf("  image size error of %zu bytes\n", (len - image_section_size));
+                for (exif_item_t* item = items; item; item = item->nextPtr)
+                {
+                    if (!(item->tag == 0x010f || item->tag == 0x010e))
+                        exif_print_item(stdout, item, "    ");
+                }
+                exif_free(items);
             }
+
+            free(info.imagepath);
+            free(info.backuppath);
         }
         free(offsets);
+        free(hdr.dirname);
     }
 
+    fclose(fp);
     return r;
 }
 
-int _tmain(int argc, TCHAR *argv[])
+int main(int argc, char *const argv[])
 {
+    // add `-debug` to enable more log output
+    // add `-info` to just read the main header and output csv
+    // add `-export dirname` to output the images for rev < 7 surfaces
     if (argc != 2) {
-        _fputts(_T("usage: srfdump filename"), stderr);
+        fputs("usage: srfdump filename", stderr);
         return 1;
     }
 
-    FILE *fp = _tfopen(argv[1], _T("rb"));
-    if (fp == NULL) {
-        _tperror(_T("fopen"));
-        return 1;
-    }
-
-    int r = SrfDump(fp);
-
-    fclose(fp);
+    int r = SrfDump(argv[1]);
     return r;
 }
index e1bf9f7577c2d5c31faaeea20c1d09a18138578b..579d10f95e5fbe065f62c01eec62261853639b9d 100644 (file)
--- a/srfinfo.c
+++ b/srfinfo.c
@@ -2,7 +2,7 @@
  *
  * Print out some header information from a surface file.
  */
+
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
@@ -31,9 +31,11 @@ static int SrfInfo(const char *filename, FILE *fp)
         hdrsize += sizeof(SurfaceFileHeaderRev4);
     if (hdr.revision > 4)
         hdrsize += sizeof(SurfaceFileHeaderRev5);
+    if (hdr.revision > 8)
+        hdrsize += sizeof(SurfaceFileHeaderRev9);
     fseek(fp, 0, SEEK_SET);
     fread(&hdr, hdrsize, 1, fp);
-    
+
     char ver[5] = { 0 };
     memcpy(ver, &hdr.fileversion, 4);
     char path[1024] = { 0 };
index 26e9c539b41d9192c8258515f2a4f333c4aeaecf..5232c84ffa4bc891a183378db5f742b465db49c1 100644 (file)
@@ -19,6 +19,11 @@ typedef struct SurfaceExtents {
     double max_y;
 } SurfaceExtents;
 
+typedef struct SurfacePoint2 {
+    double x;
+    double y;
+} SurfacePoint2;
+
 typedef struct SurfacePoint {
     double x;
     double y;
@@ -40,6 +45,13 @@ typedef struct SurfaceTranslationCoords {
     float ztrans;
 } SurfaceTranslationCoords;
 
+typedef struct SurfaceMotorPositionInfo {
+    SurfacePoint stage_min; // minimum position in firmware steps
+    SurfacePoint stage_max; // maximum position in firmware steps
+    SurfacePoint2 reference; // reference position in firmware steps
+    SurfacePoint2 resolution; // motor steps per unit (x and y)
+} SurfaceMotorPositionInfo;
+
 typedef struct SurfaceFileHeaderRev0 {
     uint32_t fileversion;
     uint16_t revision;
@@ -65,6 +77,15 @@ typedef struct SurfaceFileHeaderRev5 {
     uint8_t extrapolation_mode;
 } SurfaceFileHeaderRev5;
 
+typedef struct SurfaceFileHeaderRev7 {
+    wchar_t* dirname;
+} SurfaceFileHeaderRev7;
+
+typedef struct SurfaceFileHeaderRev9 {
+    double objective;
+    struct SurfaceMotorPositionInfo motor_info;
+} SurfaceFileHeaderRev9;
+
 typedef struct SurfaceFileHeader {
     struct {
         uint32_t fileversion;
@@ -86,9 +107,16 @@ typedef struct SurfaceFileHeader {
     struct {
         uint8_t extrapolation_mode;
     };
+    struct {
+        double objective;
+        struct SurfaceMotorPositionInfo motor_info;
+    };
+    struct {
+        wchar_t* dirname;
+    };
 } SurfaceFileHeader;
 
-typedef struct SurfaceImageInfo
+typedef struct SurfaceImageInfo5
 {
     /// Position of the top left point of the image in microns
     /// EXIF custom field 0xFEA0
@@ -107,8 +135,50 @@ typedef struct SurfaceImageInfo
     int xoffset;
     /// Vertical offset in pixels (from where?)
     int yoffset;
-    /// Size of the stored image data in bytes.
-    uint32_t image_size;
+} SurfaceImageInfo5;
+
+typedef struct SurfaceImageInfo7 {
+    uint32_t width;
+    uint32_t height;
+    wchar_t *imagepath;
+    wchar_t *backuppath;
+} SurfaceImageInfo7;
+
+typedef struct SurfaceImageInfo8 {
+    /// Rotation angle in degrees
+    double rotationAngle;
+    /// Rotation center in microns
+    SurfacePoint2 rotation_center;
+} SurfaceImageInfo8;
+
+typedef struct SurfaceImageInfo9 {
+    /// Objective magnification
+    double objective;
+} SurfaceImageInfo9;
+
+typedef struct SurfaceImageInfo {
+    struct {
+        struct SurfacePoint point;
+        double image_width_microns;
+        double image_height_microns;
+        uint32_t resolution_x;
+        uint32_t resolution_y;
+        int xoffset;
+        int yoffset;
+    };
+    struct {  // rev 8
+        double rotation_angle;
+        SurfacePoint2 rotation_center;
+    };
+    struct { // rev 9
+        double objective;
+    };
+    struct { // rev 7
+        uint32_t width;
+        uint32_t height;
+        wchar_t *imagepath;
+        wchar_t *backuppath;
+    };
 } SurfaceImageInfo;
 
 #pragma pack(pop)