Index: converter/other/Makefile =================================================================== --- converter/other/Makefile (revision 1475) +++ converter/other/Makefile (working copy) @@ -93,10 +93,10 @@ ifneq ($(DONT_HAVE_PROCESS_MGMT),Y) PORTBINARIES += pstopnm endif -BINARIES = $(PORTBINARIES) pnmtorast rasttopnm pamtopdbimg pdbimgtopam +BINARIES = $(PORTBINARIES) pnmtorast rasttopnm pamtopdbimg pdbimgtopam srftopam pamtosrf ifeq ($(HAVE_PNGLIB),Y) - BINARIES += pnmtopng pngtopam pamrgbatopng + BINARIES += pnmtopng pngtopam pamrgbatopng srftopng endif ifneq ($(JPEGLIB),NONE) BINARIES += jpegtopnm pnmtojpeg @@ -117,7 +117,7 @@ endif MERGEBINARIES = $(BINARIES) -EXTRA_OBJECTS = exif.o rast.o bmepsoe.o ipdb.o +EXTRA_OBJECTS = exif.o rast.o bmepsoe.o ipdb.o srf.o ifeq ($(HAVE_PNGLIB),Y) EXTRA_OBJECTS += pngtxt.o EXTRA_OBJECTS += pngx.o @@ -184,6 +184,21 @@ pnmtojpeg: %: %.o $(NETPBMLIB) $(LIBOPT) $(shell $(LIBOPT) $(NETPBMLIB) $(LIBOPTR) $(JPEGLIB)) \ $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD) +srftopam: %: %.o srf.o $(NETPBMLIB) $(LIBOPT) + $(LD) -o $@ $@.o srf.o \ + $(shell $(LIBOPT) $(NETPBMLIB)) \ + $(PNGLIB_LIBOPTS) $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD) + +pamtosrf: %: %.o srf.o $(NETPBMLIB) $(LIBOPT) + $(LD) -o $@ $@.o srf.o \ + $(shell $(LIBOPT) $(NETPBMLIB)) \ + $(PNGLIB_LIBOPTS) $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD) + +srftopng: %: %.o srf.o pngx.o pngtxt.o $(NETPBMLIB) $(LIBOPT) + $(LD) -o $@ $@.o srf.o pngx.o pngtxt.o \ + $(shell $(LIBOPT) $(NETPBMLIB)) \ + $(PNGLIB_LIBOPTS) $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD) + svgtopam: %: %.o $(NETPBMLIB) $(LIBOPT) $(LD) -o $@ $@.o \ $(shell $(LIBOPT) $(NETPBMLIB) $(LIBOPTR)) \ Index: converter/other/pamtosrf.c =================================================================== --- converter/other/pamtosrf.c (revision 0) +++ converter/other/pamtosrf.c (revision 0) @@ -0,0 +1,207 @@ +/* + * Convert a PAM image to SRF (Garmin vehicle) + * + * Copyright (C) 2011 by Mike Frysinger + * netpbm "structure" based on pnmtopng.c + * + * Copyright (C) 1995-1998 by Alexander Lehmann + * and Willem van Schaik + * Copyright (C) 1999,2001 by Greg Roelofs + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. This software is provided "as is" without express or + * implied warranty. + */ + +#include + +#include "pam.h" +#include "pm_c_util.h" +#include "srf.h" +#include "shhopt.h" +#include "mallocvar.h" +#include "nstring.h" +#include "version.h" + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char * inputFileName; /* '-' if stdin */ + unsigned int verbose; + unsigned int libversion; +}; + +static bool verbose; +static int errorlevel; + +static void +parseCommandLine(int argc, const char ** argv, + struct cmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + parse program command line described in Unix standard form by argc + and argv. Return the information in the options as *cmdlineP. + + If command line is internally inconsistent (invalid options, etc.), + issue error message to stderr and abort program. + + Note that the strings we return are stored in the storage that + was passed to us as the argv array. We also trash *argv. +-----------------------------------------------------------------------------*/ + optEntry *option_def; + /* Instructions to pm_optParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "libversion", OPT_FLAG, NULL, + &cmdlineP->libversion, 0); + OPTENT3(0, "verbose", OPT_FLAG, NULL, + &cmdlineP->verbose, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (argc-1 < 1) + cmdlineP->inputFileName = "-"; + else if (argc-1 == 1) + cmdlineP->inputFileName = argv[1]; + else + pm_error("Program takes at most one argument: input file name"); +} + +static uint16_t +sample2char(sample const s, + sample const maxval) { +/* Scale down a sample to a single byte. */ + + return maxval == 255 ? s : s * 255 / maxval; +} + +#define THIS_SAMPLE(PLANE) \ + sample2char(tuplerow[c][PLANE], pamP->maxval) + +static void +producepam(struct cmdlineInfo const cmdline, + struct pam * const pamP, + FILE * const ofP, + int * const errorLevelP + ) { +/*---------------------------------------------------------------------------- + Design note: It's is really a modularity violation that we have + all the command line parameters as an argument. We do it because we're + lazy -- it takes a great deal of work to carry all that information as + separate arguments -- and it's only a very small violation. +-----------------------------------------------------------------------------*/ + uint16_t width_3d, height_3d, width_ov, height_ov, r, c; + uint32_t i; + struct srf srf; + tuple * tuplerow; + + errorlevel = 0; + + /* XXX: Should we accept RGB too? */ + if (pamP->depth != 4) + pm_error("input pam must be RGBA"); + + if (verbose) + pm_message("reading pam RGBA %ux%u image", pamP->width, pamP->height); + + /* Figure out the dimensions. The frame series should come in pairs, + each series should contain 36 frames, the first set should never + be smaller than the 2nd set, the sets should have the same dimension + combos as other sets, and each frame is square. + + So if we have two frame series with the first being 80px tall and + the second is 60px tall, we can figure out things from their. */ + height_3d = pamP->width / SRF_NUM_FRAMES; + for (r = 1; r <= pamP->height / height_3d; ++r) { + height_ov = (pamP->height - (height_3d * r)) / r; + if (height_ov > height_3d) + continue; + if ((height_ov + height_3d) * r == pamP->height) + break; + } + r *= 2; + width_3d = height_3d * SRF_NUM_FRAMES; + width_ov = height_ov * SRF_NUM_FRAMES; + + if (verbose) + pm_message("detected %u sets of 16-bit RGB images (%ux%u and %ux%u)", + r, width_3d, height_3d, width_ov, height_ov); + + srf_init(&srf, r, width_3d, height_3d, width_ov, height_ov); + + /* Scan out each frame series */ + tuplerow = pnm_allocpamrow(pamP); + for (i = 0; i < srf.header.img_cnt; ++i) { + struct srf_img * img = &srf.imgs[i]; + + for (r = 0; r < img->header.height; ++r) { + uint32_t off = r * img->header.width; + uint16_t * d = &img->data.data[off]; + unsigned char * a = &img->alpha.data[off]; + + pnm_readpamrow(pamP, tuplerow); + for (c = 0; c < img->header.width; ++c) { + a[c] = IMG_SRF_A(THIS_SAMPLE(3)); + d[c] = IMG_SRF_R(THIS_SAMPLE(0)) | + IMG_SRF_G(THIS_SAMPLE(1)) | + IMG_SRF_B(THIS_SAMPLE(2)); + } + } + } + pnm_freepamrow(tuplerow); + + srf_write(ofP, &srf); + + srf_free(&srf); + + *errorLevelP = errorlevel; +} + +static void +displayVersion() { + + fprintf(stderr, "pamtosrf version %s.\n", NETPBM_VERSION); + +} + +int +main(int argc, const char * argv[]) { + + struct cmdlineInfo cmdline; + FILE * ifP; + struct pam inPam; + int errorlevel; + + pm_proginit(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + if (cmdline.libversion) { + displayVersion(); + return 0; + } + verbose = cmdline.verbose; + + ifP = pm_openr(cmdline.inputFileName); + pnm_readpaminit(ifP, &inPam, PAM_STRUCT_SIZE(tuple_type)); + + producepam(cmdline, &inPam, stdout, &errorlevel); + + pm_closer(ifP); + + return errorlevel; +} Index: converter/other/srftopng.c =================================================================== --- converter/other/srftopng.c (revision 0) +++ converter/other/srftopng.c (revision 0) @@ -0,0 +1,287 @@ +/* + * Convert a SRF (Garmin vehicle) to a Portable Network Graphics file + * + * Copyright (C) 2011 by Mike Frysinger + * netpbm "structure" based on pnmtopng.c + * + * Copyright (C) 1995-1998 by Alexander Lehmann + * and Willem van Schaik + * Copyright (C) 1999,2001 by Greg Roelofs + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. This software is provided "as is" without express or + * implied warranty. + */ + +#include +#include + +#include "pm_c_util.h" +#include "srf.h" +#include "pngx.h" +#include "pngtxt.h" +#include "shhopt.h" +#include "mallocvar.h" +#include "nstring.h" +#include "version.h" + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char * inputFileName; /* '-' if stdin */ + unsigned int verbose; + unsigned int apng; + unsigned int libversion; +}; + +static bool verbose; +static bool apng; +static int errorlevel; + +static void +parseCommandLine(int argc, const char ** argv, + struct cmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + parse program command line described in Unix standard form by argc + and argv. Return the information in the options as *cmdlineP. + + If command line is internally inconsistent (invalid options, etc.), + issue error message to stderr and abort program. + + Note that the strings we return are stored in the storage that + was passed to us as the argv array. We also trash *argv. +-----------------------------------------------------------------------------*/ + optEntry *option_def; + /* Instructions to pm_optParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "libversion", OPT_FLAG, NULL, + &cmdlineP->libversion, 0); + OPTENT3(0, "verbose", OPT_FLAG, NULL, + &cmdlineP->verbose, 0); + OPTENT3(0, "apng", OPT_FLAG, NULL, + &cmdlineP->apng, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (argc-1 < 1) + cmdlineP->inputFileName = "-"; + else if (argc-1 == 1) + cmdlineP->inputFileName = argv[1]; + else + pm_error("Program takes at most one argument: input file name"); +} + +static void +srf_img_png(struct pngx * pngxP, + png_byte * line, + uint16_t line_len, + struct srf_img * img, + uint16_t x, + uint16_t y, + uint16_t w, + uint16_t h + ) { + png_byte * pp; + uint16_t d, c, r; + uint32_t off; + + for (r = y; r < y + h; ++r) { + pp = line; + off = r * img->header.width; + + for (c = x; c < x + w; ++c) { + d = img->data.data[off + c]; + *pp++ = SRF_IMG_R(d); + *pp++ = SRF_IMG_G(d); + *pp++ = SRF_IMG_B(d); + + /* This seems wonky, but it works ... */ + d = img->alpha.data[off + c]; + *pp++ = SRF_IMG_A(d); + } + + if (line_len) + memset(pp, 0, (line_len - c) * 4 * sizeof(*pp)); + + pngx_writeRow(pngxP, line); + } +} + +static void +convertsrf(struct cmdlineInfo const cmdline, + FILE * const ifP, + FILE * const ofP, + int * const errorLevelP + ) { +/*---------------------------------------------------------------------------- + Design note: It's is really a modularity violation that we have + all the command line parameters as an argument. We do it because we're + lazy -- it takes a great deal of work to carry all that information as + separate arguments -- and it's only a very small violation. +-----------------------------------------------------------------------------*/ + long width, height, fwidth; + uint32_t i; + struct srf srf; + jmp_buf jmpbuf; + struct pngx * pngxP; + png_byte * line; + unsigned int pass; + + errorlevel = 0; + + if (setjmp(jmpbuf)) + pm_error("setjmp returns error condition"); + + pngx_create(&pngxP, PNGX_WRITE, &jmpbuf); + + srf_read(ifP, verbose, &srf); + + width = height = 0; + for (i = 0; i < srf.header.img_cnt; ++i) { + if (width < srf.imgs[i].header.width) { + width = srf.imgs[i].header.width; + fwidth = srf.imgs[i].header.height; + } + height += srf.imgs[i].header.height; + } + if (apng) + width = fwidth; + + pngx_setIhdr(pngxP, width, height, 8, PNG_COLOR_TYPE_RGBA, 0, 0, 0); + +#ifdef PNG_APNG_SUPPORTED + if (apng) + png_set_acTL(pngxP->png_ptr, pngxP->info_ptr, SRF_NUM_FRAMES, 0); +#endif + + png_init_io(pngxP->png_ptr, ofP); + + pngx_writeInfo(pngxP); + + pngx_setPacking(pngxP); + + MALLOCARRAY_NOFAIL(line, width * 4); + for (pass = 0; pass < pngxP->numPassesRequired; ++pass) + if (apng) { +#ifdef PNG_APNG_SUPPORTED + uint32_t f; + + for (f = 0; f < SRF_NUM_FRAMES; ++f) { + png_write_frame_head(pngxP->png_ptr, pngxP->info_ptr, &line, + width, height, 0, 0, 2, SRF_NUM_FRAMES, + PNG_DISPOSE_OP_NONE, PNG_BLEND_OP_SOURCE); + + for (i = 0; i < srf.header.img_cnt; ++i) { + struct srf_img * img = &srf.imgs[i]; + uint16_t w = img->header.height; + srf_img_png(pngxP, line, 0, img, f * w, 0, w, w); + } + + png_write_frame_tail(pngxP->png_ptr, pngxP->info_ptr); + } +#endif + } else { + for (i = 0; i < srf.header.img_cnt; ++i) { + struct srf_img * img = &srf.imgs[i]; + srf_img_png(pngxP, line, width, img, 0, 0, + img->header.width, img->header.height); + } + } + free(line); + + pngx_writeEnd(pngxP); + + pngx_destroy(pngxP); + + srf_free(&srf); + + *errorLevelP = errorlevel; +} + +static void +displayVersion() { + + fprintf(stderr, "srftopng version %s.\n", NETPBM_VERSION); + + /* We'd like to display the version of libpng with which we're _linked_ as + well as the one with which we're compiled, but it isn't practical. + While libpng is capable of telling you what it's level is, different + versions of it do it two different ways: with png_libpng_ver or with + png_get_header_ver. So we have to be compiled for a particular version + just to find out what version it is! It's not worth having a link + failure, much less a compile failure, if we choose wrong. + png_get_header_ver is not in anything older than libpng 1.0.2a (Dec + 1998). png_libpng_ver is not there in libraries built without + USE_GLOBAL_ARRAYS. Cygwin versions are normally built without + USE_GLOBAL_ARRAYS. -bjh 2002.06.17. + + We'd also like to display the version of libz with which we're linked, + with zlib_version (which nowadays is a macro for zlibVersion), but we + can't for reasons of modularity: We don't really link libz. libpng + does. It's none of our business whether libz is even present. And at + least on Mac OS X, we can't access libz's symbols from here -- we get + undefined reference to zlibVersion. We would have to explicitly link + libz just to find out its version. The right way to do this is for a + subroutine in libpng to give us the information. Until 10.07.08, we + did display zlib_version, but for years Mac OS X build was failing (and + we erroneously thought it was a libpng-config --ldflags bug). + + We _do_ use the compile-time part of libpng (), because it's + part of the interface to libpng. + */ + fprintf(stderr, " srftopng Compiled with libpng %s.\n", + PNG_LIBPNG_VER_STRING); + fprintf(stderr, " srftopng (not libpng) compiled with zlib %s.\n", + ZLIB_VERSION); + fprintf(stderr, "\n"); +} + +int +main(int argc, const char * argv[]) { + + struct cmdlineInfo cmdline; + FILE * ifP; + + int errorlevel; + + pm_proginit(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + if (cmdline.libversion) { + displayVersion(); + return 0; + } + verbose = cmdline.verbose; + apng = cmdline.apng; + +#ifndef PNG_APNG_SUPPORTED + if (apng) + pm_error("sorry, your libpng lacks apng support"); +#endif + + ifP = pm_openr(cmdline.inputFileName); + + convertsrf(cmdline, ifP, stdout, &errorlevel); + + pm_closer(ifP); + pm_close(stdout); + + return errorlevel; +} Index: converter/other/srf.c =================================================================== --- converter/other/srf.c (revision 0) +++ converter/other/srf.c (revision 0) @@ -0,0 +1,504 @@ +/* + * Funcs for working with SRF (Garmin vehicle) files + * + * Written by Mike Frysinger + * Released into the public domain + */ + +#include + +#include "pm_c_util.h" +#include "srf.h" +#include "mallocvar.h" +#include "nstring.h" + +static unsigned char +srf_csum_raw(void * p, + size_t len + ) { + unsigned char * c = p, ret; + + ret = 0; + while (len--) + ret += *c++; + + return ret; +} + +static unsigned char +srf_csum_pstring(struct srf_pstring * p) { + return + srf_csum_raw(&p->len, 4) + + srf_csum_raw(p->val, p->len); +} + +static bool +srf_read_pstring(FILE * ifP, + struct srf_pstring * p + ) { + if (pm_readlittlelong2u(ifP, &p->len) != 0) + return false; + MALLOCARRAY_NOFAIL(p->val, p->len + 1); + if (fread(p->val, 1, p->len, ifP) != p->len) + return false; + p->val[p->len] = '\0'; + return true; +} + +static bool +srf_write_pstring(FILE * ofP, + struct srf_pstring * p + ) { + return + pm_writelittlelongu(ofP, p->len) == 0 && + fwrite(p->val, 1, p->len, ofP) == p->len; +} + +static size_t +srf_len_header(struct srf_header * h) { + return 16 + (4 * 4) + 4 + h->s578.len + 4 + 4 + h->ver.len + 4 + 4 + h->prod.len; +} + +static unsigned char +srf_csum_header(struct srf_header * h) { + return + srf_csum_raw(h->magic, 16) + + srf_csum_raw(&h->_int4, 2 * 4) + + srf_csum_raw(&h->img_cnt, 4) + + srf_csum_raw(&h->_int5, 4) + + srf_csum_pstring(&h->s578) + + srf_csum_raw(&h->_int6, 4) + + srf_csum_pstring(&h->ver) + + srf_csum_raw(&h->_int7, 4) + + srf_csum_pstring(&h->prod); +} + +static bool +srf_read_header(FILE * ifP, + struct srf_header * h + ) { + bool ret = + fread(h->magic, 1, 16, ifP) == 16 && + pm_readlittlelong2u(ifP, &h->_int4[0]) == 0 && + pm_readlittlelong2u(ifP, &h->_int4[1]) == 0 && + pm_readlittlelong2u(ifP, &h->img_cnt) == 0 && + pm_readlittlelong2u(ifP, &h->_int5) == 0 && + srf_read_pstring(ifP, &h->s578) && + pm_readlittlelong2u(ifP, &h->_int6) == 0 && + srf_read_pstring(ifP, &h->ver) && + pm_readlittlelong2u(ifP, &h->_int7) == 0 && + srf_read_pstring(ifP, &h->prod); + h->magic[16] = '\0'; + return ret; +} + +static bool +srf_write_header(FILE * ofP, + struct srf_header * h + ) { + return + fwrite(h->magic, 1, 16, ofP) == 16 && + pm_writelittlelongu(ofP, h->_int4[0]) == 0 && + pm_writelittlelongu(ofP, h->_int4[1]) == 0 && + pm_writelittlelongu(ofP, h->img_cnt) == 0 && + pm_writelittlelongu(ofP, h->_int5) == 0 && + srf_write_pstring(ofP, &h->s578) && + pm_writelittlelongu(ofP, h->_int6) == 0 && + srf_write_pstring(ofP, &h->ver) && + pm_writelittlelongu(ofP, h->_int7) == 0 && + srf_write_pstring(ofP, &h->prod); +} + +static bool +srf_check_header(struct srf_header * h) { + return + strcmp(h->magic, SRF_MAGIC) == 0 && + h->_int4[0] == 4 && + h->_int4[1] == 4 && + /* Should we require img_cnt to be multiple of 2 ? */ + h->img_cnt > 0 && + h->_int5 == 5 && + h->s578.len == 3 && + strcmp(h->s578.val, "578") == 0 && + h->_int6 == 6 && + h->ver.len == 4 && + /* Allow any h->ver value */ + h->_int7 == 7 && + h->prod.len == 12; + /* Allow any h->prod value */ +} + +static size_t +srf_len_img(struct srf_img * img) { + return + (4 * 3) + (2 * 2) + (1 * 2) + 2 + 4 + + 4 + 4 + img->alpha.data_len + + 4 + 4 + img->data.data_len; +} + +static unsigned char +srf_csum_img(struct srf_img * img) { + struct srf_img_header * h = &img->header; + struct srf_img_alpha * a = &img->alpha; + struct srf_img_data * d = &img->data; + return + srf_csum_raw(&h->_ints, 4 * 3) + + srf_csum_raw(&h->height, 2) + + srf_csum_raw(&h->width, 2) + + srf_csum_raw(h->_bytes, 2) + + srf_csum_raw(&h->line_len, 2) + + srf_csum_raw(&h->zeros, 4) + + srf_csum_raw(&a->type, 4) + + srf_csum_raw(&a->data_len, 4) + + srf_csum_raw(a->data, a->data_len) + + srf_csum_raw(&d->type, 4) + + srf_csum_raw(&d->data_len, 4) + + srf_csum_raw(d->data, d->data_len); +} + +static bool +srf_read_img_header(FILE * ifP, + struct srf_img_header * h + ) { + return + pm_readlittlelong2u(ifP, &h->_ints[0]) == 0 && + pm_readlittlelong2u(ifP, &h->_ints[1]) == 0 && + pm_readlittlelong2u(ifP, &h->_ints[2]) == 0 && + pm_readlittleshortu(ifP, &h->height) == 0 && + pm_readlittleshortu(ifP, &h->width) == 0 && + fread(&h->_bytes[0], 1, 1, ifP) == 1 && + fread(&h->_bytes[1], 1, 1, ifP) == 1 && + pm_readlittleshortu(ifP, &h->line_len) == 0 && + pm_readlittlelong2u(ifP, &h->zeros) == 0; +} + +static bool +srf_write_img_header(FILE * ofP, + struct srf_img_header * h + ) { + return + pm_writelittlelongu(ofP, h->_ints[0]) == 0 && + pm_writelittlelongu(ofP, h->_ints[1]) == 0 && + pm_writelittlelongu(ofP, h->_ints[2]) == 0 && + pm_writelittleshortu(ofP, h->height) == 0 && + pm_writelittleshortu(ofP, h->width) == 0 && + fwrite(&h->_bytes[0], 1, 1, ofP) == 1 && + fwrite(&h->_bytes[1], 1, 1, ofP) == 1 && + pm_writelittleshortu(ofP, h->line_len) == 0 && + pm_writelittlelongu(ofP, h->zeros) == 0; +} + +static bool +srf_check_img_header(struct srf_img_header * h) { + return + h->_ints[0] == 0 && + h->_ints[1] == 16 && + h->_ints[2] == 0 && + h->_bytes[0] == 16 && + h->_bytes[1] == 8 && + h->line_len == h->width * 2 && + h->zeros == 0; +} + +static bool +srf_read_img_alpha(FILE * ifP, + struct srf_img_alpha * a + ) { + bool ret = + pm_readlittlelong2u(ifP, &a->type) == 0 && + pm_readlittlelong2u(ifP, &a->data_len) == 0; + if (!ret) + return ret; + MALLOCARRAY_NOFAIL(a->data, a->data_len); + return fread(a->data, 1, a->data_len, ifP) == a->data_len; +} + +static bool +srf_write_img_alpha(FILE * ofP, + struct srf_img_alpha * a + ) { + return + pm_writelittlelongu(ofP, a->type) == 0 && + pm_writelittlelongu(ofP, a->data_len) == 0 && + fwrite(a->data, 1, a->data_len, ofP) == a->data_len; +} + +static bool +srf_check_img_alpha(struct srf_img_alpha * a) { +#if 0 + uint32_t i; +#endif + + if (a->type != 11) + return false; + +#if 0 + /* XXX: Hmm, this is real mask data? */ + for (i = 0; i < a->data_len; ++i) + if (a->data[i] != SRF_ALPHA_OPAQUE && + a->data[i] != SRF_ALPHA_TRANS) + return false; +#endif + + return true; +} + +static bool +srf_read_img_data(FILE * ifP, + struct srf_img_data * d + ) { + bool ret = + pm_readlittlelong2u(ifP, &d->type) == 0 && + pm_readlittlelong2u(ifP, &d->data_len) == 0; + if (!ret) + return ret; + MALLOCARRAY_NOFAIL(d->data, d->data_len / 2); + return fread(d->data, 2, d->data_len / 2, ifP) == d->data_len / 2; +} + +static bool +srf_write_img_data(FILE * ofP, + struct srf_img_data * d + ) { + return + pm_writelittlelongu(ofP, d->type) == 0 && + pm_writelittlelongu(ofP, d->data_len) == 0 && + fwrite(d->data, 2, d->data_len / 2, ofP) == d->data_len / 2; +} + +static bool +srf_check_img_data(struct srf_img_data * d) { + return d->type == 1; +} + +static bool +srf_read_img(FILE * ifP, + bool verbose, + uint32_t i, + struct srf_img * img + ) { + if (!srf_read_img_header(ifP, &img->header)) + pm_error("short srf image %u header", i); + if (!srf_check_img_header(&img->header)) + pm_error("invalid srf image %u header", i); + + if (verbose) + pm_message("reading srf 16-bit RGB %ux%u image %u", + img->header.width, img->header.height, i); + + if (!srf_read_img_alpha(ifP, &img->alpha)) + pm_error("short srf image %u alpha mask", i); + if (!srf_check_img_alpha(&img->alpha)) + pm_error("invalid srf image %u alpha mask", i); + + if (!srf_read_img_data(ifP, &img->data)) + pm_error("short srf image %u data", i); + if (!srf_check_img_data(&img->data)) + pm_error("invalid srf image %u data", i); + + return true; +} + +static bool +srf_write_img(FILE * ofP, + uint32_t i, + struct srf_img * img + ) { + if (!srf_check_img_header(&img->header)) + pm_error("invalid srf image %u header", i); + if (!srf_write_img_header(ofP, &img->header)) + pm_error("short srf image %u header", i); + + if (!srf_check_img_alpha(&img->alpha)) + pm_error("invalid srf image %u alpha mask", i); + if (!srf_write_img_alpha(ofP, &img->alpha)) + pm_error("short srf image %u alpha mask", i); + + if (!srf_check_img_data(&img->data)) + pm_error("invalid srf image %u data", i); + if (!srf_write_img_data(ofP, &img->data)) + pm_error("short srf image %u data", i); + + return true; +} + +static unsigned char +srf_csum(struct srf * srf, + size_t pad_len + ) { + unsigned char ret; + uint32_t i; + + ret = srf_csum_header(&srf->header); + for (i = 0; i < srf->header.img_cnt; ++i) + ret += srf_csum_img(&srf->imgs[i]); + + while (pad_len--) + ret += 0xff; + + return ret; +} + +void +srf_read(FILE * ifP, + bool verbose, + struct srf * srf + ) { + unsigned char csum; + size_t pad_len; + unsigned char pad[256]; + uint32_t i; + + if (!srf_read_header(ifP, &srf->header)) + pm_error("short srf header"); + if (!srf_check_header(&srf->header)) + pm_error("invalid srf header"); + + if (verbose) + pm_message("reading srf ver %s with prod code %s and %u images", + srf->header.ver.val, srf->header.prod.val, srf->header.img_cnt); + + MALLOCARRAY_NOFAIL(srf->imgs, srf->header.img_cnt); + + for (i = 0; i < srf->header.img_cnt; ++i) + if (!srf_read_img(ifP, verbose, i, &srf->imgs[i])) + pm_error("invalid srf image %u", i); + + pad_len = fread(pad, 1, sizeof(pad), ifP); + if (!feof(ifP)) { + pm_errormsg("excess data at end of file"); + return; + } + + csum = srf_csum(srf, 0); + while (pad_len--) + csum += pad[pad_len]; + if (csum) + pm_errormsg("checksum does not match"); +} + +void +srf_write(FILE * ofP, + struct srf * srf + ) { + unsigned char csum; + size_t pad_len; + uint32_t i; + + /* checksum byte */ + pad_len = 1; + + if (!srf_check_header(&srf->header)) + pm_error("invalid srf header"); + if (!srf_write_header(ofP, &srf->header)) + pm_error("write srf header"); + pad_len += srf_len_header(&srf->header); + + for (i = 0; i < srf->header.img_cnt; ++i) { + if (!srf_write_img(ofP, i, &srf->imgs[i])) + pm_error("invalid srf image %u", i); + pad_len += srf_len_img(&srf->imgs[i]); + } + + /* Pad to 256 bytes */ + pad_len = 256 - (pad_len % 256); + if (pad_len) { + char * d; + MALLOCARRAY_NOFAIL(d, pad_len); + memset(d, 0xff, pad_len); + if (fwrite(d, 1, pad_len, ofP) != pad_len) + pm_error("unable to 0xff pad file"); + free(d); + } + + /* Write out checksum byte */ + csum = 0xff - srf_csum(srf, pad_len) + 1; + if (fwrite(&csum, 1, 1, ofP) != 1) + pm_error("unable to write checksum"); +} + +static void +srf_free_img(struct srf_img * img) { + free(img->alpha.data); + free(img->data.data); +} + +void +srf_free(struct srf * srf) { + uint32_t i; + free(srf->header.s578.val); + free(srf->header.ver.val); + free(srf->header.prod.val); + for (i = 0; i < srf->header.img_cnt; ++i) + srf_free_img(&srf->imgs[i]); + free(srf->imgs); +} + +static void +srf_img_init(struct srf_img * img, + uint16_t width, + uint16_t height + ) { + struct srf_img_header * h = &img->header; + struct srf_img_alpha * a = &img->alpha; + struct srf_img_data * d = &img->data; + + h->_ints[0] = 0; + h->_ints[1] = 16; + h->_ints[2] = 0; + h->height = height; + h->width = width; + h->_bytes[0] = 16; + h->_bytes[1] = 8; + h->line_len = width * 2; + h->zeros = 0; + + a->type = 11; + a->data_len = height * width; + MALLOCARRAY_NOFAIL(a->data, a->data_len); + + d->type = 1; + d->data_len = height * width * 2; + MALLOCARRAY_NOFAIL(d->data, d->data_len / 2); +} + +static void +srf_init_pstring(struct srf_pstring * p, + const char * s + ) { + p->len = strlen(s); + MALLOCARRAY_NOFAIL(p->val, p->len); + memcpy(p->val, s, p->len + 1); +} + +void +srf_init(struct srf * srf, + uint32_t img_cnt, + uint16_t width_3d, + uint16_t height_3d, + uint16_t width_ov, + uint16_t height_ov + ) { + struct srf_header * h = &srf->header; + uint32_t i; + + if (!img_cnt || img_cnt % 2) + pm_error("invalid image count"); + + strcpy(h->magic, SRF_MAGIC); + h->_int4[0] = 4; + h->_int4[1] = 4; + h->img_cnt = img_cnt; + h->_int5 = 5; + srf_init_pstring(&h->s578, "578"); + h->_int6 = 6; + srf_init_pstring(&h->ver, "1.00"); + h->_int7 = 7; + srf_init_pstring(&h->prod, "006-D0578-XX"); + + MALLOCARRAY_NOFAIL(srf->imgs, h->img_cnt); + for (i = 0; i < h->img_cnt; i += 2) { + srf_img_init(&srf->imgs[i], width_3d, height_3d); + srf_img_init(&srf->imgs[i + 1], width_ov, height_ov); + } +} Index: converter/other/srftopam.c =================================================================== --- converter/other/srftopam.c (revision 0) +++ converter/other/srftopam.c (revision 0) @@ -0,0 +1,204 @@ +/* + * Convert a SRF (Garmin vehicle) to a PAM image + * + * Copyright (C) 2011 by Mike Frysinger + * netpbm "structure" based on pnmtopng.c + * + * Copyright (C) 1995-1998 by Alexander Lehmann + * and Willem van Schaik + * Copyright (C) 1999,2001 by Greg Roelofs + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. This software is provided "as is" without express or + * implied warranty. + */ + +#include + +#include "pam.h" +#include "pm_c_util.h" +#include "srf.h" +#include "shhopt.h" +#include "mallocvar.h" +#include "nstring.h" +#include "version.h" + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char * inputFileName; /* '-' if stdin */ + unsigned int verbose; + unsigned int libversion; +}; + +static bool verbose; +static int errorlevel; + +static void +parseCommandLine(int argc, const char ** argv, + struct cmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + parse program command line described in Unix standard form by argc + and argv. Return the information in the options as *cmdlineP. + + If command line is internally inconsistent (invalid options, etc.), + issue error message to stderr and abort program. + + Note that the strings we return are stored in the storage that + was passed to us as the argv array. We also trash *argv. +-----------------------------------------------------------------------------*/ + optEntry *option_def; + /* Instructions to pm_optParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "libversion", OPT_FLAG, NULL, + &cmdlineP->libversion, 0); + OPTENT3(0, "verbose", OPT_FLAG, NULL, + &cmdlineP->verbose, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (argc-1 < 1) + cmdlineP->inputFileName = "-"; + else if (argc-1 == 1) + cmdlineP->inputFileName = argv[1]; + else + pm_error("Program takes at most one argument: input file name"); +} + +static void +producePam(struct pam * pamP, + uint16_t line_len, + struct srf_img * img + ) { + tuple * tuplerow; + uint16_t d, c, r; + uint32_t off; + + tuplerow = pnm_allocpamrow(pamP); + + for (r = 0; r < img->header.height; ++r) { + off = r * img->header.width; + + for (c = 0; c < img->header.width; ++c) { + tuple const thisTuple = tuplerow[c]; + + d = img->data.data[off + c]; + thisTuple[0] = SRF_IMG_R(d); + thisTuple[1] = SRF_IMG_G(d); + thisTuple[2] = SRF_IMG_B(d); + + d = img->alpha.data[off + c]; + thisTuple[3] = SRF_IMG_A(d); + } + + memset(tuplerow[c], 0, (line_len - c) * 4 * sizeof(*tuplerow)); + + pnm_writepamrow(pamP, tuplerow); + } + + pnm_freepamrow(tuplerow); +} + +static void +convertsrf(struct cmdlineInfo const cmdline, + FILE * const ifP, + FILE * const ofP, + int * const errorLevelP + ) { +/*---------------------------------------------------------------------------- + Design note: It's is really a modularity violation that we have + all the command line parameters as an argument. We do it because we're + lazy -- it takes a great deal of work to carry all that information as + separate arguments -- and it's only a very small violation. +-----------------------------------------------------------------------------*/ + const char * comment = "Produced by srftopam"; /* constant */ + long width, height, fwidth; + uint32_t i; + struct srf srf; + struct pam outPam; + + errorlevel = 0; + + srf_read(ifP, verbose, &srf); + + width = height = 0; + for (i = 0; i < srf.header.img_cnt; ++i) { + if (width < srf.imgs[i].header.width) { + width = srf.imgs[i].header.width; + fwidth = srf.imgs[i].header.height; + } + height += srf.imgs[i].header.height; + } + + outPam.size = sizeof(struct pam); + outPam.len = PAM_STRUCT_SIZE(comment_p); + outPam.file = ofP; + outPam.format = PAM_FORMAT; + outPam.plainformat = 0; + outPam.width = width; + outPam.height = height; + outPam.depth = 4; + outPam.maxval = 255; + outPam.bytes_per_sample = 1; + sprintf(outPam.tuple_type, "RGB_ALPHA"); + outPam.allocation_depth = 4; + outPam.comment_p = &comment; + + pnm_writepaminit(&outPam); + + for (i = 0; i < srf.header.img_cnt; ++i) + producePam(&outPam, width, &srf.imgs[i]); + + srf_free(&srf); + + *errorLevelP = errorlevel; +} + +static void +displayVersion() { + + fprintf(stderr, "srftopam version %s.\n", NETPBM_VERSION); + +} + +int +main(int argc, const char * argv[]) { + + struct cmdlineInfo cmdline; + FILE * ifP; + int errorlevel; + + pm_proginit(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + if (cmdline.libversion) { + displayVersion(); + return 0; + } + verbose = cmdline.verbose; + + ifP = pm_openr(cmdline.inputFileName); + + convertsrf(cmdline, ifP, stdout, &errorlevel); + + pm_closer(ifP); + + return errorlevel; +} Index: converter/other/srf.h =================================================================== --- converter/other/srf.h (revision 0) +++ converter/other/srf.h (revision 0) @@ -0,0 +1,163 @@ +/* + * Structures for working with SRF (Garmin vehicle) files + * http://www.techmods.net/nuvi/ + * + * Written by Mike Frysinger + * Released into the public domain + */ + +#ifndef _SRF_H_ +#define _SRF_H_ + +#include + +struct srf_header; +struct srf_img_header; +struct srf_img_alpha; +struct srf_img_data; +struct srf_img; + +struct srf_pstring { + uint32_t len; + char *val; +}; + +#define SRF_NUM_FRAMES 36 + +/* + File Header + 16 bytes - string - "GARMIN BITMAP 01" + 32 bytes - two 32-bit ints, [4, 4] -- purpose unknown + 4 bytes - 32-bit int -- number of images (usually just 2) + 4 bytes - 32-bit int, [5] -- purpose unknown + 7 bytes - PString - "578" + 4 bytes - 32-bit int, [6] -- purpose unknown + 8 bytes - PString - version number ("1.00", "2.00", "2.10", or "2.20") + 4 bytes - 32-bit int, [7] -- purpose unknown + 16 bytes - PString - "006-D0578-XX" (where "XX" changes) -- + I assume this is Garmin's product code? + */ +#define SRF_MAGIC "GARMIN BITMAP 01" +struct srf_header { + char magic[16 + 1]; + + uint32_t _int4[2]; + + uint32_t img_cnt; + + uint32_t _int5; + + struct srf_pstring s578; + + uint32_t _int6; + + struct srf_pstring ver; + + uint32_t _int7; + + struct srf_pstring prod; +}; + +/* + Image Header + 12 bytes - three 32-bit ints, [0,16,0] -- purpose unknown + 2 bytes - 16-bit int -- height of image (just the 3D section, so it's 80) + 2 bytes - 16-bit int -- width of image (just the 3D section, 2880 or 2881) + 2 bytes - [16, 8] -- purpose unknown + 2 bytes - 16-bit int -- byte length of each line of image RGB data + (16-bit RGB), so "width * 2" + 4 bytes - all zeroes -- purpose unknown + */ +struct srf_img_header { + uint32_t _ints[3]; + + uint16_t height, width; + + uint8_t _bytes[2]; + + uint16_t line_len; + + uint32_t zeros; +}; + +/* + Image Alpha Mask + 4 bytes - 32-bit int, [11] -- Might specify the type of data that follows? + 4 bytes - 32-bit int, length of following data (width*height of 3D section) + width*height bytes - alpha mask data, 0 = opaque, 128 = transparent + (across, then down) + + Notes: The Garmin format has 129 values: [0..128] [opaque..transparent] + The PNG format has 256 values: [0..255] [transparent..opaque] + So we have to do a little edge case tweaking to keep things lossless. + */ +#define SRF_ALPHA_OPAQUE 0 +#define SRF_ALPHA_TRANS 128 +static inline uint8_t srf_img_a(uint8_t d) +{ + if (d == SRF_ALPHA_OPAQUE) + return 0xff; + return (128 - d) << 1; +} +#define SRF_IMG_A(d) srf_img_a((uint8_t)(d)) +static inline uint8_t img_srf_a(uint8_t d) +{ + if (d == 0xff) + return SRF_ALPHA_OPAQUE; + return 128 - (d >> 1); +} +#define IMG_SRF_A(d) img_srf_a((uint8_t)(d)) +struct srf_img_alpha { + uint32_t type; + + uint32_t data_len; + unsigned char *data; +}; + +/* + Image RGB Data + 4 bytes - 32-bit int, [1] -- Might specify the type of data that follows? + 4 bytes - 32-bit int, length of following data (width*height*2 of 3D + section, as the RGB data is 16-bit) + width*height*2 bytes - RBG values as "rrrrrggggg0bbbbb" bits + (across, then down) + */ +#define SRF_IMG_R(d) ((((d) >> 11) & 0x1f) << 3) +#define SRF_IMG_G(d) ((((d) >> 6) & 0x1f) << 3) +#define SRF_IMG_B(d) ((((d) >> 0) & 0x1f) << 3) +#define IMG_SRF_R(d) ((((d) >> 3) & 0x1f) << 11) +#define IMG_SRF_G(d) ((((d) >> 3) & 0x1f) << 6) +#define IMG_SRF_B(d) ((((d) >> 3) & 0x1f) << 0) +struct srf_img_data { + uint32_t type; + + uint32_t data_len; + uint16_t *data; +}; + +struct srf_img { + struct srf_img_header header; + struct srf_img_alpha alpha; + struct srf_img_data data; +}; + +/* + Footer + arbitrary number of bytes - all 0xFF -- these are used (as well as the + checksum byte) to pad the file + size to a multiple of 256. + 1 byte - checksum byte -- use this byte to adjust so that the ascii sum of + all bytes in the file is a multiple of 256. + */ + +struct srf { + struct srf_header header; + struct srf_img *imgs; +}; + +void srf_read(FILE *, bool, struct srf *); +void srf_init(struct srf *, uint32_t, uint16_t, uint16_t, uint16_t, uint16_t); +void srf_write(FILE *, struct srf *); +void srf_free(struct srf *); + +#endif /*_SRF_H_*/