--- /dev/null
+/* Copyright (c) 2018 Pat Thoyts <patthoyts@users.sourceforge.net>
+ *
+ * Examine a Renishaw .srf surface file and dump the contents.
+ * All images are exported to an images/ subdirectory with an accompanying
+ * data file containing the additional data stored for each image.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include "surfacefile.h"
+
+#ifdef WIN32
+#include <tchar.h>
+#else
+#include "compat/tchar.h"
+#endif /* !WIN32 */
+
+static size_t fcopy(FILE *src, FILE *dst, size_t count)
+{
+ size_t total = 0, n = 0;
+ char buf[4096];
+ do {
+ n = fread(buf, 1, sizeof(buf), src);
+ total += fwrite(buf, 1, n, dst);
+ } while (n == sizeof(buf));
+ return total;
+}
+
+static uint32_t * ReadImageTable(SurfaceFileHeader *hdrPtr, FILE *fp)
+{
+ uint32_t *table = NULL;
+ if (hdrPtr->num_images > 0)
+ {
+ table = (uint32_t *)malloc(sizeof(uint32_t) * (hdrPtr->num_images + 1));
+ fread(table + 1, sizeof(uint32_t), hdrPtr->num_images, fp);
+ // set first image to the end of the table.
+ table[0] = ftell(fp);
+ // convert offsets from relative to table to file absolute positions
+ for (int n = 1; n < hdrPtr->num_images+1; ++n)
+ table[n] += table[0];
+ }
+ return table;
+}
+
+static SurfaceTriangle * ReadTriangles(SurfaceFileHeader *hdrPtr, FILE *fp)
+{
+ SurfaceTriangle *triangles = NULL;
+ if (hdrPtr->num_triangles > 0)
+ {
+ triangles = (SurfaceTriangle *)malloc(sizeof(SurfaceTriangle) * hdrPtr->num_triangles);
+ fread(triangles, sizeof(SurfaceTriangle), hdrPtr->num_triangles, fp);
+ }
+ return triangles;
+}
+
+static SurfacePoint * ReadPoints(SurfaceFileHeader *hdrPtr, FILE *fp)
+{
+ SurfacePoint *points = NULL;
+ if (hdrPtr->num_points > 0)
+ {
+ points = (SurfacePoint *)malloc(sizeof(SurfacePoint) * hdrPtr->num_points);
+ double *buffer = (double *)malloc(sizeof(double) * hdrPtr->num_points);
+ fread(buffer, sizeof(double), hdrPtr->num_points, fp);
+ for (int n = 0; n < hdrPtr->num_points; ++n)
+ points[n].x = buffer[n];
+ fread(buffer, sizeof(double), hdrPtr->num_points, fp);
+ for (int n = 0; n < hdrPtr->num_points; ++n)
+ points[n].y = buffer[n];
+ fread(buffer, sizeof(double), hdrPtr->num_points, fp);
+ for (int n = 0; n < hdrPtr->num_points; ++n)
+ points[n].z = buffer[n];
+ free(buffer);
+ }
+ return points;
+}
+
+static int SrfDump(FILE *fp)
+{
+ int r = 0;
+ SurfaceFileHeader hdr = {0};
+ fread(&hdr, sizeof(SurfaceFileHeaderRev0), 1, fp);
+ size_t hdrsize = sizeof(SurfaceFileHeaderRev0);
+ if (hdr.revision > 1)
+ hdrsize += sizeof(SurfaceFileHeaderRev2);
+ if (hdr.revision > 2)
+ hdrsize += sizeof(SurfaceFileHeaderRev3);
+ if (hdr.revision > 3)
+ hdrsize += sizeof(SurfaceFileHeaderRev4);
+ if (hdr.revision > 4)
+ hdrsize += sizeof(SurfaceFileHeaderRev5);
+ fseek(fp, 0, SEEK_SET);
+ fread(&hdr, hdrsize, 1, fp);
+
+ char ver[5] = {0};
+ memcpy(ver, &hdr.fileversion, 4);
+ printf("ver: %s rev: %d points: %u tri: %u images: %u\n", ver, hdr.revision, hdr.num_points, hdr.num_triangles, hdr.num_images);
+ if (hdr.revision > 1)
+ printf(_T("translation: %.6f, %.6f, %.6f\n"), hdr.translation.xtrans, hdr.translation.ytrans, hdr.translation.ztrans);
+ if (hdr.revision > 2)
+ {
+ printf("image extents: (%.6g,%.6g) -> (%.6g,%.6g)\n",
+ hdr.image_extents.min_x, hdr.image_extents.min_y,
+ hdr.image_extents.max_x, hdr.image_extents.max_y);
+ printf("surface extents: (%.6g,%.6g) -> (%.6g,%.6g)\n",
+ hdr.surface_extents.min_x, hdr.surface_extents.min_y,
+ hdr.surface_extents.max_x, hdr.surface_extents.max_y);
+ }
+ if (hdr.revision > 3)
+ 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);
+
+ SurfacePoint *points = ReadPoints(&hdr, fp);
+ if (points)
+ {
+ for (int n = 0; n < hdr.num_points; ++n)
+ {
+ printf(" point %d: %.6g %.6g %.6g\n",
+ n, points[n].x, points[n].y, points[n].z);
+ }
+ free(points);
+ }
+
+ SurfaceTriangle *triangles = ReadTriangles(&hdr, fp);
+ if (triangles)
+ {
+ printf(" TRIANGLES: FIX ME\n");
+ free(triangles);
+ }
+
+ uint32_t *offsets = ReadImageTable(&hdr, fp);
+ uint32_t table_base = ftell(fp);
+ if (offsets)
+ {
+ printf(" image base: %#08x\n", table_base);
+ mkdir("images", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+ for (int n = 0; n < hdr.num_images; ++n)
+ {
+ uint32_t image_size = offsets[n+1] - offsets[n] - 60;
+ printf(" image %d: %#08x size %#04x\n",
+ n, offsets[n], image_size);
+
+ TCHAR name[32 + 16] = {0};
+ _stprintf(name, _T("images/img%d.jpg"), n);
+ FILE *fp2 = _tfopen(name, "w");
+ if (fp2 == NULL) {
+ _tperror("fopen");
+ exit(1);
+ }
+ // TODO: deal with the 60 bytes of additional data per image....
+ // first 24 bytes are the image point position
+ // {
+ // SurfacePoint point; // in um
+ // double image_width_microns; // field of view x
+ // double image_height_microns; // field of view y
+ // uint32_t image_width; // in pixels ?
+ // uint32_t image_height; // in pixels ?
+ // uint32_t unknown[3];
+ // }
+ fseek(fp, offsets[n], SEEK_SET);
+
+ char data[60] = {0};
+ fread(data, 1, 60, fp);
+ fcopy(fp, fp2, image_size);
+ fclose(fp2);
+
+ _stprintf(name, _T("images/img%d.dat"), n);
+ fp2 = _tfopen(name, "w");
+ if (fp2 == NULL) {
+ _tperror("fopen data");
+ exit(1);
+ }
+ fwrite(data, 1, 60, fp2);
+ fclose(fp2);
+ }
+ free(offsets);
+ }
+
+ return r;
+}
+
+int _tmain(int argc, TCHAR *argv[])
+{
+ if (argc != 2) {
+ _fputts(_T("usage: srfdump filename"), stderr);
+ return 1;
+ }
+
+ FILE *fp = _tfopen(argv[1], _T("r"));
+ if (fp == NULL) {
+ _tperror(_T("fopen"));
+ return 1;
+ }
+
+ int r = SrfDump(fp);
+
+ fclose(fp);
+ return r;
+}
--- /dev/null
+#ifndef _SurfaceFile_h_INCLUDE
+#define _SurfaceFile_h_INCLUDE
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+#define SURFACE_FILE_VERSION 0x46525553UL /* 'S' 'U' 'R' 'F' */
+#define SURFACE_FILE_REVISION 5
+
+#pragma pack(push,1)
+
+typedef struct SurfaceExtents {
+ double min_x;
+ double max_x;
+ double min_y;
+ double max_y;
+} SurfaceExtents;
+
+typedef struct SurfacePoint {
+ double x;
+ double y;
+ double z;
+} SurfacePoint;
+
+typedef struct SurfaceTriangle {
+ int a;
+ int b;
+ int c;
+ int ab;
+ int bc;
+ int ca;
+} SurfaceTriangle;
+
+typedef struct SurfaceTranslationCoords {
+ float xtrans;
+ float ytrans;
+ float ztrans;
+} SurfaceTranslationCoords;
+
+typedef struct SurfaceFileHeaderRev0 {
+ uint32_t fileversion;
+ uint16_t revision;
+ uint32_t num_points;
+ uint32_t num_triangles;
+ uint32_t num_images;
+} SurfaceFileHeaderRev0;
+
+typedef struct SurfaceFileHeaderRev2 {
+ struct SurfaceTranslationCoords translation;
+} SurfaceFileHeaderRev2;
+
+typedef struct SurfaceFileHeaderRev3 {
+ struct SurfaceExtents image_extents;
+ struct SurfaceExtents surface_extents;
+} SurfaceFileHeaderRev3;
+
+typedef struct SurfaceFileHeaderRev4 {
+ uint8_t use_background_removal; // boolean
+} SurfaceFileHeaderRev4;
+
+typedef struct SurfaceFileHeaderRev5 {
+ uint8_t extrapolation_mode;
+} SurfaceFileHeaderRev5;
+
+typedef struct SurfaceFileHeader {
+ struct {
+ uint32_t fileversion;
+ uint16_t revision;
+ uint32_t num_points;
+ uint32_t num_triangles;
+ uint32_t num_images;
+ };
+ struct {
+ struct SurfaceTranslationCoords translation;
+ };
+ struct {
+ struct SurfaceExtents image_extents;
+ struct SurfaceExtents surface_extents;
+ };
+ struct {
+ uint8_t use_background_removal;
+ };
+ struct {
+ uint8_t extrapolation_mode;
+ };
+} SurfaceFileHeader;
+
+#pragma pack(pop)
+
+#if defined(__cplusplus)
+}
+#endif
+#endif // _SurfaceFile_h_INCLUDE