/* * Funcs for working with SRF (Garmin vehicle) files * * Written by Mike Frysinger * Released into the public domain */ #include #include "pm_c_util.h" #include "mallocvar.h" #include "nstring.h" #include "srf.h" static unsigned char csumRaw(void * const p, size_t const len) { unsigned char retval; unsigned int i; unsigned char * c; for (i = 0, retval = 0, c = p; i < len; ++i) retval += *c++; return retval; } static unsigned char csumPstring(struct srf_pstring * const pstringP) { return csumRaw(&pstringP->len, 4) + csumRaw(pstringP->val, pstringP->len); } static bool readPstring(FILE * const ifP, struct srf_pstring * const pstringP) { size_t bytesRead; pm_readlittlelong2u(ifP, &pstringP->len); MALLOCARRAY(pstringP->val, pstringP->len + 1); if (!pstringP->val) pm_error("Failed to allocate buffer to read %u-byte pstring", pstringP->len); bytesRead = fread(pstringP->val, 1, pstringP->len, ifP); if (bytesRead != pstringP->len) pm_error("Failed to read pstring. Requested %u bytes, got %u", (unsigned)pstringP->len, (unsigned)bytesRead); pstringP->val[pstringP->len] = '\0'; return true; } static bool writePstring(FILE * const ofP, struct srf_pstring * const pstringP) { bool retval; size_t bytesWritten; pm_writelittlelongu(ofP, pstringP->len); bytesWritten = fwrite(pstringP->val, 1, pstringP->len, ofP); if (bytesWritten == pstringP->len) retval = true; else retval = false; return retval; } static size_t lenHeader(struct srf_header * const headerP) { return 16 + (4 * 4) + 4 + headerP->s578.len + 4 + 4 + headerP->ver.len + 4 + 4 + headerP->prod.len; } static unsigned char csumHeader(struct srf_header * const headerP) { return csumRaw(headerP->magic, 16) + csumRaw(&headerP->_int4, 2 * 4) + csumRaw(&headerP->img_cnt, 4) + csumRaw(&headerP->_int5, 4) + csumPstring(&headerP->s578) + csumRaw(&headerP->_int6, 4) + csumPstring(&headerP->ver) + csumRaw(&headerP->_int7, 4) + csumPstring(&headerP->prod); } static bool readHeader(FILE * const ifP, struct srf_header * const headerP) { bool const retval = fread(headerP->magic, 1, 16, ifP) == 16 && pm_readlittlelong2u(ifP, &headerP->_int4[0]) == 0 && pm_readlittlelong2u(ifP, &headerP->_int4[1]) == 0 && pm_readlittlelong2u(ifP, &headerP->img_cnt) == 0 && pm_readlittlelong2u(ifP, &headerP->_int5) == 0 && readPstring(ifP, &headerP->s578) && pm_readlittlelong2u(ifP, &headerP->_int6) == 0 && readPstring(ifP, &headerP->ver) && pm_readlittlelong2u(ifP, &headerP->_int7) == 0 && readPstring(ifP, &headerP->prod); headerP->magic[16] = '\0'; return retval; } static bool writeHeader(FILE * const ofP, struct srf_header * const headerP) { return fwrite(headerP->magic, 1, 16, ofP) == 16 && pm_writelittlelongu(ofP, headerP->_int4[0]) == 0 && pm_writelittlelongu(ofP, headerP->_int4[1]) == 0 && pm_writelittlelongu(ofP, headerP->img_cnt) == 0 && pm_writelittlelongu(ofP, headerP->_int5) == 0 && writePstring(ofP, &headerP->s578) && pm_writelittlelongu(ofP, headerP->_int6) == 0 && writePstring(ofP, &headerP->ver) && pm_writelittlelongu(ofP, headerP->_int7) == 0 && writePstring(ofP, &headerP->prod); } static bool checkHeader(struct srf_header * const headerP) { return streq(headerP->magic, SRF_MAGIC) && headerP->_int4[0] == 4 && headerP->_int4[1] == 4 && /* Should we require img_cnt to be multiple of 2 ? */ headerP->img_cnt > 0 && headerP->_int5 == 5 && headerP->s578.len == 3 && strcmp(headerP->s578.val, "578") == 0 && headerP->_int6 == 6 && headerP->ver.len == 4 && /* Allow any headerP->ver value */ headerP->_int7 == 7 && headerP->prod.len == 12; /* Allow any headerP->prod value */ } static size_t lenImg(struct srf_img * const imgP) { return (4 * 3) + (2 * 2) + (1 * 2) + 2 + 4 + 4 + 4 + imgP->alpha.data_len + 4 + 4 + imgP->data.data_len; } static unsigned char csumImg(struct srf_img * const imgP) { struct srf_img_header * const headerP = &imgP->header; struct srf_img_alpha * const alphaP = &imgP->alpha; struct srf_img_data * const dataP = &imgP->data; return csumRaw(&headerP->_ints, 4 * 3) + csumRaw(&headerP->height, 2) + csumRaw(&headerP->width, 2) + csumRaw(headerP->_bytes, 2) + csumRaw(&headerP->line_len, 2) + csumRaw(&headerP->zeros, 4) + csumRaw(&alphaP->type, 4) + csumRaw(&alphaP->data_len, 4) + csumRaw(alphaP->data, alphaP->data_len) + csumRaw(&dataP->type, 4) + csumRaw(&dataP->data_len, 4) + csumRaw(dataP->data, dataP->data_len); } static bool readImgHeader(FILE * const ifP, struct srf_img_header * const headerP) { return pm_readlittlelong2u(ifP, &headerP->_ints[0]) == 0 && pm_readlittlelong2u(ifP, &headerP->_ints[1]) == 0 && pm_readlittlelong2u(ifP, &headerP->_ints[2]) == 0 && pm_readlittleshortu(ifP, &headerP->height) == 0 && pm_readlittleshortu(ifP, &headerP->width) == 0 && fread(&headerP->_bytes[0], 1, 1, ifP) == 1 && fread(&headerP->_bytes[1], 1, 1, ifP) == 1 && pm_readlittleshortu(ifP, &headerP->line_len) == 0 && pm_readlittlelong2u(ifP, &headerP->zeros) == 0; } static bool writeImgHeader(FILE * const ofP, struct srf_img_header * const headerP) { return pm_writelittlelongu(ofP, headerP->_ints[0]) == 0 && pm_writelittlelongu(ofP, headerP->_ints[1]) == 0 && pm_writelittlelongu(ofP, headerP->_ints[2]) == 0 && pm_writelittleshortu(ofP, headerP->height) == 0 && pm_writelittleshortu(ofP, headerP->width) == 0 && fwrite(&headerP->_bytes[0], 1, 1, ofP) == 1 && fwrite(&headerP->_bytes[1], 1, 1, ofP) == 1 && pm_writelittleshortu(ofP, headerP->line_len) == 0 && pm_writelittlelongu(ofP, headerP->zeros) == 0; } static bool checkImgHeader(struct srf_img_header * const headerP) { return headerP->_ints[0] == 0 && headerP->_ints[1] == 16 && headerP->_ints[2] == 0 && headerP->_bytes[0] == 16 && headerP->_bytes[1] == 8 && headerP->line_len == headerP->width * 2 && headerP->zeros == 0; } static bool readImgAlpha(FILE * const ifP, struct srf_img_alpha * const alphaP) { bool retval; pm_readlittlelong2u(ifP, &alphaP->type); pm_readlittlelong2u(ifP, &alphaP->data_len); MALLOCARRAY(alphaP->data, alphaP->data_len); if (!alphaP->data) retval = false; else { size_t bytesRead; bytesRead = fread(alphaP->data, 1, alphaP->data_len, ifP); retval = (bytesRead ==alphaP->data_len); } return retval; } static bool writeImageAlpha(FILE * const ofP, struct srf_img_alpha * const alphaP) { return pm_writelittlelongu(ofP, alphaP->type) == 0 && pm_writelittlelongu(ofP, alphaP->data_len) == 0 && fwrite(alphaP->data, 1, alphaP->data_len, ofP) == alphaP->data_len; } static bool checkImgAlpha(struct srf_img_alpha * const alphaP) { return (alphaP->type == 11); } static bool readImgData(FILE * const ifP, struct srf_img_data * const dataP) { bool retval; pm_readlittlelong2u(ifP, &dataP->type); pm_readlittlelong2u(ifP, &dataP->data_len); MALLOCARRAY(dataP->data, dataP->data_len / 2); if (!dataP->data) retval = false; else { size_t bytesRead; bytesRead = fread(dataP->data, 2, dataP->data_len / 2, ifP); retval = (bytesRead == dataP->data_len / 2); } return retval; } static bool writeImgData(FILE * const ofP, struct srf_img_data * const dataP) { return pm_writelittlelongu(ofP, dataP->type) == 0 && pm_writelittlelongu(ofP, dataP->data_len) == 0 && fwrite(dataP->data, 2, dataP->data_len / 2, ofP) == dataP->data_len / 2; } static bool checkImgData(struct srf_img_data * const dataP) { return dataP->type == 1; } static bool readImg(FILE * const ifP, bool const verbose, uint32_t const i, struct srf_img * const imgP) { if (!readImgHeader(ifP, &imgP->header)) pm_error("short srf image %u header", i); if (!checkImgHeader(&imgP->header)) pm_error("invalid srf image %u header", i); if (verbose) pm_message("reading srf 16-bit RGB %ux%u image %u", imgP->header.width, imgP->header.height, i); if (!readImgAlpha(ifP, &imgP->alpha)) pm_error("short srf image %u alpha mask", i); if (!checkImgAlpha(&imgP->alpha)) pm_error("invalid srf image %u alpha mask", i); if (!readImgData(ifP, &imgP->data)) pm_error("short srf image %u data", i); if (!checkImgData(&imgP->data)) pm_error("invalid srf image %u data", i); return true; } static bool writeImg(FILE * const ofP, uint32_t const i, struct srf_img * const imgP) { if (!checkImgHeader(&imgP->header)) pm_error("invalid srf image %u header", i); if (!writeImgHeader(ofP, &imgP->header)) pm_error("short srf image %u header", i); if (!checkImgAlpha(&imgP->alpha)) pm_error("invalid srf image %u alpha mask", i); if (!writeImageAlpha(ofP, &imgP->alpha)) pm_error("short srf image %u alpha mask", i); if (!checkImgData(&imgP->data)) pm_error("invalid srf image %u data", i); if (!writeImgData(ofP, &imgP->data)) pm_error("short srf image %u data", i); return true; } static uint8_t csum(struct srf * const srfP, size_t const padLen) { /*---------------------------------------------------------------------------- The sum of everything in the SRF image except the checksum byte. The checksum byte is supposed to be the arithmetic opposite of this so that the sum of everything is zero. -----------------------------------------------------------------------------*/ unsigned char retval; unsigned int i; retval = csumHeader(&srfP->header); for (i = 0; i < srfP->header.img_cnt; ++i) retval += csumImg(&srfP->imgs[i]); for (i = 0; i < padLen; ++i) retval += 0xff; return retval; } void srf_read(FILE * const ifP, bool const verbose, struct srf * const srfP) { uint8_t trialCsum; size_t padLen; unsigned char pad[256]; unsigned int i; if (!readHeader(ifP, &srfP->header)) pm_error("short srf header"); if (!checkHeader(&srfP->header)) pm_error("invalid srf header"); if (verbose) pm_message("reading srf ver %s with prod code %s and %u images", srfP->header.ver.val, srfP->header.prod.val, srfP->header.img_cnt); MALLOCARRAY(srfP->imgs, srfP->header.img_cnt); if (!srfP->imgs) pm_error("Could not allocate memory for %u images", srfP->header.img_cnt); for (i = 0; i < srfP->header.img_cnt; ++i) if (!readImg(ifP, verbose, i, &srfP->imgs[i])) pm_error("invalid srf image %u", i); padLen = fread(pad, 1, sizeof(pad), ifP); if (!feof(ifP)) { pm_errormsg("excess data at end of file"); return; } trialCsum = csum(srfP, 0); /* initial value */ for (i = 0; i < padLen; ++i) trialCsum += pad[i]; if (trialCsum != 0) pm_errormsg("checksum does not match"); } void srf_write(FILE * const ofP, struct srf * const srfP) { uint8_t srfCsum; /* checksum value in SRF image */ size_t padLen; unsigned int i; size_t bytesWritten; padLen = 1; /* initial value */ if (!checkHeader(&srfP->header)) pm_error("invalid srf header"); if (!writeHeader(ofP, &srfP->header)) pm_error("write srf header"); padLen += lenHeader(&srfP->header); for (i = 0; i < srfP->header.img_cnt; ++i) { if (!writeImg(ofP, i, &srfP->imgs[i])) pm_error("invalid srf image %u", i); padLen += lenImg(&srfP->imgs[i]); } /* Pad to 256 bytes */ padLen = 256 - (padLen % 256); if (padLen) { char * d; size_t bytesWritten; MALLOCARRAY(d, padLen); if (!d) pm_error("Could not allocate memory for %u bytes of padding", (unsigned)padLen); memset(d, 0xff, padLen); bytesWritten = fwrite(d, 1, padLen, ofP); if (bytesWritten != padLen) pm_error("unable to 0xff pad file"); free(d); } /* Write out checksum byte */ srfCsum = 0xff - csum(srfP, padLen) + 1; bytesWritten = fwrite(&srfCsum, 1, 1, ofP); if (bytesWritten != 1) pm_error("unable to write checksum"); } static void freeImg(struct srf_img * const imgP) { free(imgP->alpha.data); free(imgP->data.data); } void srf_term(struct srf * const srfP) { unsigned int i; free(srfP->header.s578.val); free(srfP->header.ver.val); free(srfP->header.prod.val); for (i = 0; i < srfP->header.img_cnt; ++i) freeImg(&srfP->imgs[i]); free(srfP->imgs); } static void srf_img_init(struct srf_img * const imgP, uint16_t const width, uint16_t const height) { struct srf_img_header * const headerP = &imgP->header; struct srf_img_alpha * const alphaP = &imgP->alpha; struct srf_img_data * const dataP = &imgP->data; headerP->_ints[0] = 0; headerP->_ints[1] = 16; headerP->_ints[2] = 0; headerP->height = height; headerP->width = width; headerP->_bytes[0] = 16; headerP->_bytes[1] = 8; headerP->line_len = width * 2; headerP->zeros = 0; alphaP->type = 11; alphaP->data_len = height * width; MALLOCARRAY(alphaP->data, alphaP->data_len); if (!alphaP->data) pm_error("Could not allocate buffer for %u bytes of alpha", alphaP->data_len); dataP->type = 1; dataP->data_len = height * width * 2; MALLOCARRAY(dataP->data, dataP->data_len / 2); if (!dataP->data) pm_error("Could not allocation buffer for %u units of data", dataP->data_len); } static void initPstring(struct srf_pstring * const pstringP, const char * const s) { pstringP->len = strlen(s); MALLOCARRAY(pstringP->val, pstringP->len + 1); if (!pstringP->val) pm_error("Could not allocate memory for string of length %u", pstringP->len); memcpy(pstringP->val, s, pstringP->len + 1); } void srf_init(struct srf * const srfP) { struct srf_header * const headerP = &srfP->header; strcpy(headerP->magic, SRF_MAGIC); headerP->_int4[0] = 4; headerP->_int4[1] = 4; headerP->img_cnt = 0; headerP->_int5 = 5; initPstring(&headerP->s578, "578"); headerP->_int6 = 6; initPstring(&headerP->ver, "1.00"); headerP->_int7 = 7; initPstring(&headerP->prod, "006-D0578-XX"); srfP->imgs = NULL; } void srf_create_img(struct srf * const srfP, uint16_t const width, uint16_t const height) { /*---------------------------------------------------------------------------- Add an "image" to the SRF. An image is a horizontal series of 36 square frames, each showing a different angle view of an object, 10 degrees about. At least that's what it's supposed to be. We don't really care -- it's just an arbitrary rectangular raster image to us. -----------------------------------------------------------------------------*/ ++srfP->header.img_cnt; REALLOCARRAY(srfP->imgs, srfP->header.img_cnt); if (!srfP->imgs) pm_error("Could not allocate memory for %u images", srfP->header.img_cnt); srf_img_init(&srfP->imgs[srfP->header.img_cnt-1], width, height); }