Use UTF-8 char strings and support some command line options. pt/linux-support
authorPat Thoyts <pat.thoyts@gmail.com>
Sat, 4 Oct 2025 12:35:49 +0000 (13:35 +0100)
committerPat Thoyts <pat.thoyts@gmail.com>
Sat, 4 Oct 2025 12:35:49 +0000 (13:35 +0100)
wchar_t on linux is 4 bytes so had to do a UTF-16 to UTF-8 converter.
Use UTF-8 internally everywhere.

CMakeLists.txt
compat/platform.h [new file with mode: 0644]
jpegrdr.c
jpegrdr.h
srfdump.c
srfinfo.c
surfacefile.h

index 099e3bdc6a17773df15cf931e539889850cd82f1..2c761cb806a1b25a30467a66b3515f719dae3754 100644 (file)
@@ -22,5 +22,6 @@ target_compile_features(jpegrdr PRIVATE c_std_11)
 if (WIN32)
   target_compile_definitions(jpegrdr PRIVATE -DWIN32 _CRT_SECURE_NO_WARNINGS JPEGRDR_MAIN)
 else()
+  target_compile_definitions(jpegrdr PRIVATE JPEGRDR_MAIN)
   #target_compile_options(jpegrdr PRIVATE -pedantic)
 endif()
diff --git a/compat/platform.h b/compat/platform.h
new file mode 100644 (file)
index 0000000..b554ee6
--- /dev/null
@@ -0,0 +1,7 @@
+#ifdef WIN32
+#define CDECL _cdecl
+#define PATHSEP '\\'
+#else
+#define CDECL
+#define PATHSEP '/'
+#endif
index 99f315943e4e31fd1a0948c7e8e2b85c653fed0a..dde1396b748f505a5d0752ba430ff29da075f7ca 100644 (file)
--- a/jpegrdr.c
+++ b/jpegrdr.c
@@ -198,7 +198,7 @@ exif_item_t *jpegrdr_get_exif(FILE *fp, logfunc_t log)
 
 #ifdef JPEGRDR_MAIN
 
-static void _cdecl logger(const char *format, ...)
+static void CDECL logger(const char *format, ...)
 {
     va_list args;
     va_start(args, format);
index 546416e99b3953a658e331826aae6359b1a5ba5d..29d5bdefed902946dd013fc4e3358fb8ced7787b 100644 (file)
--- a/jpegrdr.h
+++ b/jpegrdr.h
@@ -2,9 +2,10 @@
 #define _jpegrdr_h_INCLUDE
 
 #include <stdio.h>
+#include "compat/platform.h"
 #include "exif.h"
 
-typedef void (_cdecl * logfunc_t)(const char *format, ...);
+typedef void (CDECL * logfunc_t)(const char *format, ...);
 
 exif_item_t *jpegrdr_get_exif(FILE *fp, logfunc_t log);
 
index 736ceda04781078ef2c7952bd9cb0840478ecb26..faf3da590bbc2947fa453c6330631b33871ca8d0 100644 (file)
--- a/srfdump.c
+++ b/srfdump.c
@@ -7,22 +7,42 @@
 
 #include <stdlib.h>
 #include <stdarg.h>
+#include <stdbool.h>
 #include <string.h>
 #include <stdio.h>
+#include <wchar.h>
 #include <sys/stat.h>
 #include <assert.h>
 #include "surfacefile.h"
 #include "exif.h"
 #include "jpegrdr.h"
 
+#include <ctype.h>
+
 #ifdef WIN32
 #include <direct.h>
 #include <tchar.h>
 #else
 #include "compat/tchar.h"
 #endif /* !WIN32 */
+#include "compat/platform.h"
+
+typedef struct options_t {
+    bool info;
+    const char *export;
+    const char *filename;
+    logfunc_t log;
+} options_t;
 
-static void _cdecl logger(const char *format, ...)
+static void CDECL log_debug(const char *format, ...)
+{
+    va_list args;
+    va_start(args, format);
+    vfprintf(stderr, format, args);
+    va_end(args);
+}
+
+static void CDECL log_null(const char *format, ...)
 {
     va_list args;
     va_start(args, format);
@@ -44,18 +64,64 @@ static size_t fcopy(FILE *src, FILE *dst, size_t count)
     return total;
 }
 
+// FIXME: do surrogate pairs?
+static size_t utf16_to_utf8(char* buffer, int* offset, uint16_t wc)
+{
+    int r = 0;
+    if (wc <= 0x7F)
+    {
+        r = 1;
+        if (buffer)
+            buffer[(*offset)++] = (char)wc;
+    }
+    else if (wc <= 0x7FF)
+    {
+        r = 2;
+        if (buffer)
+        {
+            buffer[(*offset)++] = 0xC0 | (wc >> 6);
+            buffer[(*offset)++] = 0x80 | ((wc >> 0) & 0x3F);
+        }
+    }
+    else if (wc <= 0xFFFF)
+    {
+        r = 3;
+        if (buffer)
+        {
+            buffer[(*offset)++] = 0xE0 | (wc >> 12);
+            buffer[(*offset)++] = 0x80 | ((wc >> 6) & 0x3F);
+            buffer[(*offset)++] = 0x80 | ((wc >> 0) & 0x3F);
+        }
+    }
+    return r;
+}
+
 // 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)
+// Then converts the UTF-16 string to UTF-8 with terminating NUL
+static char* read_name(FILE* fp)
 {
     uint32_t length = 0;
     fread(&length, sizeof(uint32_t), 1, fp);
-    wchar_t* name = NULL;
+    char* name = NULL;
     if (length)
     {
-        name = (wchar_t*)calloc(1, length + sizeof(wchar_t));
-        fread(name, 1, length, fp);
+        uint16_t *bstr = (uint16_t*)calloc(length, 1);
+        fread(bstr, 1, length, fp);
+
+        size_t len = 0;
+        for (uint32_t n = 0; n < length / sizeof(uint16_t); ++n)
+        {
+            len += utf16_to_utf8(NULL, NULL, bstr[n]);
+        }
+
+        int offset = 0;
+        name = (char *)calloc(len + 1, 1);
+        for (uint32_t n = 0; n < length / sizeof(uint16_t); ++n)
+        {
+            utf16_to_utf8(name, &offset, bstr[n]);
+        }
+        free(bstr);
     }
     return name;
 }
@@ -69,7 +135,7 @@ static uint32_t * ReadImageTable(SurfaceFileHeader *hdrPtr, FILE *fp)
     uint32_t *table = NULL;
     if (hdrPtr->num_images > 0)
     {
-        table = (uint32_t *)calloc(sizeof(uint32_t), (hdrPtr->num_images + 1));
+        table = (uint32_t *)calloc((hdrPtr->num_images + 1), sizeof(uint32_t));
         if (table == NULL)
         {
             _tperror("calloc table");
@@ -133,9 +199,9 @@ static SurfacePoint * ReadPoints(SurfaceFileHeader *hdrPtr, FILE *fp)
     return points;
 }
 
-static int SrfDump(const char *filename)
+static int SrfDump(const options_t *options)
 {
-    FILE *fp = fopen(filename, "rb");
+    FILE *fp = fopen(options->filename, "rb");
     if (fp == NULL) {
         perror("fopen");
         return 1;
@@ -218,13 +284,14 @@ static int SrfDump(const char *filename)
             for (uint32_t n = 0; n < hdr.num_images + 1; ++n)
                 printf("    %u: %#08x\n", n, offsets[n]);
         }
-#if 0
+        if (hdr.revision < 7 && options->export != NULL)
+        {
 #ifdef WIN32
-        _tmkdir(_T("images"));
+            mkdir(options->export);
 #else
-        mkdir("images", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+            mkdir(options->export, 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 };
@@ -243,10 +310,11 @@ static int SrfDump(const char *filename)
 
             uint32_t image_size = 0;
             exif_item_t *items = NULL;
+            int pos = fseek(fp, 0, SEEK_CUR);
             if (hdr.revision < 7)
             {
                 fread(&image_size, sizeof(uint32_t), 1, fp);
-                items = jpegrdr_get_exif(fp, logger);
+                items = jpegrdr_get_exif(fp, options->log);
             }
             else
             {
@@ -254,42 +322,49 @@ static int SrfDump(const char *filename)
                 info.imagepath = read_name(fp);
                 info.backuppath = read_name(fp);
 
-                wchar_t wfilename[260] = {0};
+                char path[256] = {0};
+                strcpy(path, options->filename);
+                char *sep = strrchr(path, PATHSEP);
+                assert(sep != NULL);
+                sep[1] = '\0';
+                strcat(path, hdr.dirname);
+                const char separator[2] = {PATHSEP, 0};
+                strcat(path, separator);
+                strcat(path, info.imagepath);
 
-                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");
+                FILE *imagefp = fopen(path, "r");
                 assert(imagefp != NULL);
-                items = jpegrdr_get_exif(imagefp, logger);
-                fclose(imagefp);
+                if (imagefp != NULL)
+                {
+                    items = jpegrdr_get_exif(imagefp, options->log);
+                    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);
-            }
-            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)
+            if (hdr.revision < 7 && options->export != NULL)
             {
-                uint32_t backup_size = 0;
-                fread(&backup_size, sizeof(uint32_t), 1, fp);
-                fseek(fp, backup_size, SEEK_CUR);
-                len += sizeof(backup_size) + backup_size; // account for 'backup image' and its size
-                printf("  orig:%u: %u\n", n, backup_size);
+                char name[256] = {0};
+                sprintf(name, "%s/img%u.jpg", options->export, n);
+
+                FILE *fp2 = fopen(name, "wb");
+                if (fp2 == NULL) {
+                    perror("fopen image file");
+                    exit(1);
+                }
+                fseek(fp, pos, SEEK_SET);
+                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)
+                {
+                    uint32_t backup_size = 0;
+                    fread(&backup_size, sizeof(uint32_t), 1, fp);
+                    fseek(fp, backup_size, SEEK_CUR);
+                    len += sizeof(backup_size) + backup_size; // account for 'backup image' and its size
+                    printf("  orig:%u: %u\n", n, backup_size);
+                }
             }
-#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,
@@ -298,13 +373,10 @@ static int SrfDump(const char *filename)
                 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')
+                printf("    image: %s\n", info.imagepath);
+                if (info.backuppath != NULL && info.backuppath[0] != '\0')
                 {
-                    wcstombs(path, info.backuppath, sizeof(path));
-                    printf("    original: %s\n", path);
+                    printf("    original: %s\n", info.backuppath);
                 }
             }
             if (items)
@@ -330,14 +402,36 @@ static int SrfDump(const char *filename)
 
 int main(int argc, char *const argv[])
 {
+    options_t options = {false, NULL, NULL, log_null};
+
     // 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) {
-        fputs("usage: srfdump filename", stderr);
+    int opt = 1;
+    for (; opt < argc - 1; ++opt)
+    {
+        if (strncmp("-d", argv[opt], 2) == 0
+            || strncmp("--debug", argv[opt], 7) == 0)
+        {
+            options.log = log_debug;
+        }
+        else if (strncmp("-i", argv[opt], 2) == 0
+            || strncmp("--info", argv[opt], 6) == 0)
+        {
+            options.info = true;
+        }
+        else if (strncmp("-e", argv[opt], 2 == 0)
+            || strncmp("--export", argv[opt], 8) == 0)
+        {
+            ++opt;
+            options.export = argv[opt];
+        }
+    }
+    if (opt != argc - 1) {
+        fputs("usage: srfdump [-di] [--debug] [--info] [--export dir] filename", stderr);
         return 1;
     }
+    options.filename = argv[opt];
 
-    int r = SrfDump(argv[1]);
-    return r;
+    return SrfDump(&options);
 }
index 579d10f95e5fbe065f62c01eec62261853639b9d..ce54969347a4ff603ddf7713aecead2a5fb0f6e8 100644 (file)
--- a/srfinfo.c
+++ b/srfinfo.c
@@ -16,6 +16,7 @@
 #else
 #include "compat/tchar.h"
 #endif /* !WIN32 */
+#include "compat/platform.h"
 
 static int SrfInfo(const char *filename, FILE *fp)
 {
index 5232c84ffa4bc891a183378db5f742b465db49c1..041f056996eb66493fbb45feae2ea8bcae103245 100644 (file)
@@ -112,7 +112,7 @@ typedef struct SurfaceFileHeader {
         struct SurfaceMotorPositionInfo motor_info;
     };
     struct {
-        wchar_t* dirname;
+        char *dirname;
     };
 } SurfaceFileHeader;
 
@@ -140,8 +140,8 @@ typedef struct SurfaceImageInfo5
 typedef struct SurfaceImageInfo7 {
     uint32_t width;
     uint32_t height;
-    wchar_t *imagepath;
-    wchar_t *backuppath;
+    char *imagepath;
+    char *backuppath;
 } SurfaceImageInfo7;
 
 typedef struct SurfaceImageInfo8 {
@@ -176,8 +176,8 @@ typedef struct SurfaceImageInfo {
     struct { // rev 7
         uint32_t width;
         uint32_t height;
-        wchar_t *imagepath;
-        wchar_t *backuppath;
+        char *imagepath;
+        char *backuppath;
     };
 } SurfaceImageInfo;