/* * Funcs for working with SRF (Garmin vehicle) files * * Written by Mike Frysinger * Released into the public domain */ #include "headers.h" static bool srf_read_uint16(FILE *fp, uint16_t *ui) { unsigned char buf[sizeof(*ui)]; if (fread(buf, 1, sizeof(buf), fp) != sizeof(buf)) return false; /* The image is all little endian. */ *ui = ((uint16_t)buf[0] << 0) | ((uint16_t)buf[1] << 8); return true; } static bool srf_write_uint16(FILE *fp, uint16_t ui) { unsigned char buf[sizeof(ui)]; buf[0] = ui >> 0; buf[1] = ui >> 8; return fwrite(buf, 1, sizeof(buf), fp) == sizeof(buf); } static bool srf_read_uint32(FILE *fp, uint32_t *ui) { unsigned char buf[sizeof(*ui)]; if (fread(buf, 1, sizeof(buf), fp) != sizeof(buf)) return false; /* The image is all little endian. */ *ui = ((uint32_t)buf[0] << 0) | ((uint32_t)buf[1] << 8) | ((uint32_t)buf[2] << 16) | ((uint32_t)buf[3] << 24); return true; } static bool srf_write_uint32(FILE *fp, uint32_t ui) { unsigned char buf[sizeof(ui)]; buf[0] = ui >> 0; buf[1] = ui >> 8; buf[2] = ui >> 16; buf[3] = ui >> 24; return fwrite(buf, 1, sizeof(buf), fp) == sizeof(buf); } 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 *fp, struct srf_pstring *p) { if (!srf_read_uint32(fp, &p->len)) return false; p->val = malloc(p->len + 1); if (!p->val) return false; if (fread(p->val, 1, p->len, fp) != p->len) return false; p->val[p->len] = '\0'; return true; } static bool srf_write_pstring(FILE *fp, struct srf_pstring *p) { return srf_write_uint32(fp, p->len) && fwrite(p->val, 1, p->len, fp) == 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 && srf_read_uint32(ifp, &h->_int4[0]) && srf_read_uint32(ifp, &h->_int4[1]) && srf_read_uint32(ifp, &h->img_cnt) && srf_read_uint32(ifp, &h->_int5) && srf_read_pstring(ifp, &h->s578) && srf_read_uint32(ifp, &h->_int6) && srf_read_pstring(ifp, &h->ver) && srf_read_uint32(ifp, &h->_int7) && 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 && srf_write_uint32(ofp, h->_int4[0]) && srf_write_uint32(ofp, h->_int4[1]) && srf_write_uint32(ofp, h->img_cnt) && srf_write_uint32(ofp, h->_int5) && srf_write_pstring(ofp, &h->s578) && srf_write_uint32(ofp, h->_int6) && srf_write_pstring(ofp, &h->ver) && srf_write_uint32(ofp, h->_int7) && 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 srf_read_uint32(ifp, &h->_ints[0]) && srf_read_uint32(ifp, &h->_ints[1]) && srf_read_uint32(ifp, &h->_ints[2]) && srf_read_uint16(ifp, &h->height) && srf_read_uint16(ifp, &h->width) && fread(&h->_bytes[0], 1, 1, ifp) == 1 && fread(&h->_bytes[1], 1, 1, ifp) == 1 && srf_read_uint16(ifp, &h->line_len) && srf_read_uint32(ifp, &h->zeros); } static bool srf_write_img_header(FILE *ofp, struct srf_img_header *h) { return srf_write_uint32(ofp, h->_ints[0]) && srf_write_uint32(ofp, h->_ints[1]) && srf_write_uint32(ofp, h->_ints[2]) && srf_write_uint16(ofp, h->height) && srf_write_uint16(ofp, h->width) && fwrite(&h->_bytes[0], 1, 1, ofp) == 1 && fwrite(&h->_bytes[1], 1, 1, ofp) == 1 && srf_write_uint16(ofp, h->line_len) && srf_write_uint32(ofp, h->zeros); } 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 = srf_read_uint32(ifp, &a->type) && srf_read_uint32(ifp, &a->data_len); if (!ret) return ret; a->data = malloc(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 srf_write_uint32(ofp, a->type) && srf_write_uint32(ofp, a->data_len) && 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 = srf_read_uint32(ifp, &d->type) && srf_read_uint32(ifp, &d->data_len); if (!ret) return ret; d->data = malloc(d->data_len); 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 srf_write_uint32(ofp, d->type) && srf_write_uint32(ofp, d->data_len) && 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)) err("short srf image %u header", i); if (!srf_check_img_header(&img->header)) err("invalid srf image %u header", i); if (verbose) msg("reading srf 16-bit RGB %ux%u image %u", img->header.width, img->header.height, i); if (!srf_read_img_alpha(ifp, &img->alpha)) err("short srf image %u alpha mask", i); if (!srf_check_img_alpha(&img->alpha)) err("invalid srf image %u alpha mask", i); if (!srf_read_img_data(ifp, &img->data)) err("short srf image %u data", i); if (!srf_check_img_data(&img->data)) err("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)) err("invalid srf image %u header", i); if (!srf_write_img_header(ofp, &img->header)) err("short srf image %u header", i); if (!srf_check_img_alpha(&img->alpha)) err("invalid srf image %u alpha mask", i); if (!srf_write_img_alpha(ofp, &img->alpha)) err("short srf image %u alpha mask", i); if (!srf_check_img_data(&img->data)) err("invalid srf image %u data", i); if (!srf_write_img_data(ofp, &img->data)) err("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; unsigned char pad[256]; size_t pad_len; uint32_t i; if (!srf_read_header(ifp, &srf->header)) err("short srf header"); if (!srf_check_header(&srf->header)) err("invalid srf header"); if (verbose) msg("reading srf ver %s with prod code %s and %u images", srf->header.ver.val, srf->header.prod.val, srf->header.img_cnt); srf->imgs = malloc(sizeof(*srf->imgs) * srf->header.img_cnt); if (!srf->imgs) return; for (i = 0; i < srf->header.img_cnt; ++i) if (!srf_read_img(ifp, verbose, i, &srf->imgs[i])) err("invalid srf image %u", i); pad_len = fread(pad, 1, sizeof(pad), ifp); if (!feof(ifp)) { warn("excess data at end of file"); return; } csum = srf_csum(srf, 0); while (pad_len--) csum += pad[pad_len]; if (csum) warn("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)) err("invalid srf header"); if (!srf_write_header(ofp, &srf->header)) err("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])) err("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; d = malloc(pad_len); memset(d, 0xff, pad_len); if (fwrite(d, 1, pad_len, ofp) != pad_len) err("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) err("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; a->data = malloc(a->data_len); d->type = 1; d->data_len = height * width * 2; d->data = malloc(d->data_len); } static void srf_init_pstring(struct srf_pstring *p, const char *s) { p->len = strlen(s); p->val = malloc(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) err("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"); srf->imgs = malloc(sizeof(*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); } }