From 8db90c20b354761bdc4be4a3bbb4bd175ae1c7ea Mon Sep 17 00:00:00 2001 From: Pat Thoyts Date: Sat, 4 Oct 2025 13:35:49 +0100 Subject: [PATCH] Use UTF-8 char strings and support some command line options. 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 | 1 + compat/platform.h | 7 ++ jpegrdr.c | 2 +- jpegrdr.h | 3 +- srfdump.c | 202 +++++++++++++++++++++++++++++++++------------- srfinfo.c | 1 + surfacefile.h | 10 +-- 7 files changed, 165 insertions(+), 61 deletions(-) create mode 100644 compat/platform.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 099e3bd..2c761cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 index 0000000..b554ee6 --- /dev/null +++ b/compat/platform.h @@ -0,0 +1,7 @@ +#ifdef WIN32 +#define CDECL _cdecl +#define PATHSEP '\\' +#else +#define CDECL +#define PATHSEP '/' +#endif diff --git a/jpegrdr.c b/jpegrdr.c index 99f3159..dde1396 100644 --- 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); diff --git a/jpegrdr.h b/jpegrdr.h index 546416e..29d5bde 100644 --- a/jpegrdr.h +++ b/jpegrdr.h @@ -2,9 +2,10 @@ #define _jpegrdr_h_INCLUDE #include +#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); diff --git a/srfdump.c b/srfdump.c index 736ceda..faf3da5 100644 --- a/srfdump.c +++ b/srfdump.c @@ -7,22 +7,42 @@ #include #include +#include #include #include +#include #include #include #include "surfacefile.h" #include "exif.h" #include "jpegrdr.h" +#include + #ifdef WIN32 #include #include #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); } diff --git a/srfinfo.c b/srfinfo.c index 579d10f..ce54969 100644 --- 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) { diff --git a/surfacefile.h b/surfacefile.h index 5232c84..041f056 100644 --- a/surfacefile.h +++ b/surfacefile.h @@ -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; -- 2.23.0