$NetBSD: patch-af,v 1.2 2004/08/15 12:13:53 dillo Exp $ --- src/gens/util/chd.c.orig 2004-08-15 11:35:14.000000000 +0200 +++ src/gens/util/chd.c @@ -0,0 +1,400 @@ +/* + NiH: chd.c,v 1.6 2004/06/25 23:31:08 dillo Exp + + chd.c -- accessing chd files + Copyright (C) 2004 Dieter Baron and Thomas Klausner + + This file is part of ckmame, a program to check rom sets for MAME. + The authors can be contacted at + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + + +#include +#include +#include +#include + +#include "chd.h" + + + +#define MAX_HEADERLEN 120 /* maximum header length */ +#define TAG "MComprHD" +#define TAG_LEN 8 /* length of tag */ +#define TAG_AND_LEN 12 /* length of tag + header length */ + +#define MAP_ENTRY_SIZE_V12 8 /* size of map entry, versions 1 & 2 */ +#define MAP_ENTRY_SIZE_V3 16 /* size of map entry, version 3 */ + +#define GET_LONG(b) (b+=4,((b)[-4]<<24)|((b)[-3]<<16)|((b)[-2]<<8)|(b)[-1]) +#define GET_QUAD(b) (b+=8,((uint64_t)(b)[-8]<<56)|((uint64_t)(b)[-7]<<48) \ + |((uint64_t)(b)[-6]<<40)|((uint64_t)(b)[-5]<<32) \ + |((uint64_t)(b)[-4]<<24)|((uint64_t)(b)[-3]<<16) \ + |((uint64_t)(b)[-2]<<8)|((uint64_t)(b)[-1])) +#define GET_SHORT(b) (b+=2,((b)[-2]<<8)|(b)[-1]) + +static int read_header(struct chd *); +static int read_map(struct chd *); + + + +void +chd_close(struct chd *chd) +{ + fclose(chd->f); + free(chd->name); + free(chd->map); + free(chd->buf); + free(chd->hbuf); + free(chd); +} + + + +struct chd * +chd_open(const char *name, int *errp) +{ + struct chd *chd; + FILE *f; + + if ((f=fopen(name, "rb")) == NULL) { + if (errp) + *errp = CHD_ERR_OPEN; + return NULL; + } + + if ((chd=malloc(sizeof(*chd))) == NULL) { + if (errp) + *errp = CHD_ERR_NOMEM; + return NULL; + } + chd->f = f; + if ((chd->name=strdup(name)) == NULL) { + if (errp) + *errp = CHD_ERR_NOMEM; + chd_close(chd); + return NULL; + } + chd->error = 0; + chd->map = NULL; + chd->buf = NULL; + chd->hno = -1; + chd->hbuf = NULL; + + if (read_header(chd) < 0) { + if (errp) + *errp = chd->error; + chd_close(chd); + return NULL; + } + + return chd; +} + + + +int +chd_read_hunk(struct chd *chd, int idx, char *b) +{ + int i, n, err; + + if (idx < 0 || idx > chd->total_hunks) { + chd->error = CHD_ERR_INVAL; + return -1; + } + + if (chd->map == NULL) { + if (read_map(chd) < 0) + return -1; + } + + if (chd->map[idx].length > chd->hunk_len) { + chd->error = CHD_ERR_NOTSUP; + return -1; + } + + switch (chd->map[idx].flags & CHD_MAP_TYPE_MASK) { + case CHD_MAP_TYPE_COMPRESSED: + /* XXX: CHD_COMP_NONE? */ + if (chd->compression != CHD_COMP_ZLIB + && chd->compression != CHD_COMP_ZLIB_PLUS) { + chd->error = CHD_ERR_NOTSUP; + return -1; + } + + if (chd->buf == NULL) { + if ((chd->buf=malloc(chd->hunk_len)) == NULL) { + chd->error = CHD_ERR_NOMEM; + return -1; + } + chd->z.avail_in = 0; + chd->z.zalloc = Z_NULL; + chd->z.zfree = Z_NULL; + chd->z.opaque = NULL; + err = inflateInit2(&chd->z, -MAX_WBITS); + } + else + err = inflateReset(&chd->z); + if (err != Z_OK) { + chd->error = CHD_ERR_ZLIB; + return -1; + } + + if (fseek(chd->f, chd->map[idx].offset, SEEK_SET) == -1) { + chd->error = CHD_ERR_SEEK; + return -1; + } + if ((n=fread(chd->buf, 1, chd->map[idx].length, chd->f)) < 0) { + chd->error = CHD_ERR_READ; + return -1; + } + + chd->z.next_in = chd->buf; + chd->z.avail_in = n; + chd->z.next_out = b; + chd->z.avail_out = chd->hunk_len; + /* XXX: should use Z_FINISH, but that returns Z_BUF_ERROR */ + if ((err=inflate(&chd->z, 0)) != Z_OK && err != Z_STREAM_END) { + chd->error = CHD_ERR_ZLIB; + return -1; + } + /* XXX: chd->z.avail_out should be 0 */ + n = chd->hunk_len - chd->z.avail_out; + break; + + case CHD_MAP_TYPE_UNCOMPRESSED: + if (fseek(chd->f, chd->map[idx].offset, SEEK_SET) == -1) { + chd->error = CHD_ERR_SEEK; + return -1; + } + /* XXX: use chd->hunk_len instead? */ + if ((n=fread(b, 1, chd->map[idx].length, chd->f)) < 0) { + chd->error = CHD_ERR_READ; + return -1; + } + break; + + case CHD_MAP_TYPE_MINI: + b[0] = (chd->map[idx].offset >> 56) & 0xff; + b[1] = (chd->map[idx].offset >> 48) & 0xff; + b[2] = (chd->map[idx].offset >> 40) & 0xff; + b[3] = (chd->map[idx].offset >> 32) & 0xff; + b[4] = (chd->map[idx].offset >> 24) & 0xff; + b[5] = (chd->map[idx].offset >> 16) & 0xff; + b[6] = (chd->map[idx].offset >> 8) & 0xff; + b[7] = chd->map[idx].offset & 0xff; + n = chd->hunk_len; + for (i=8; imap[idx].offset, b); + + case CHD_MAP_TYPE_PARENT_HUNK: + chd->error = CHD_ERR_NOTSUP; + return -1; + + default: + chd->error = CHD_ERR_NOTSUP; /* XXX: wrong error */ + return -1; + } + + if ((chd->map[idx].flags & CHD_MAP_FL_NOCRC) == 0) { + if (crc32(0, b, n) != chd->map[idx].crc) { + chd->error = CHD_ERR_CRC; + return -1; + } + } + + return n; +} + + + +int +chd_read_range(struct chd *chd, char *b, int off, int len) +{ + int i, s, n; + int copied, o2, l2; + + /* XXX: error handling */ + + s = off/chd->hunk_len; + n = (off+len+chd->hunk_len-1)/chd->hunk_len - s; + + copied = 0; + o2 = off % chd->hunk_len; + l2 = chd->hunk_len - o2; + + for (i=0; ihunk_len; + } + if (i == n-1) { + if (l2 > len-copied) + l2 = len-copied; + } + if (o2 == 0 && l2 == chd->hunk_len && s+i != chd->hno) { + if (chd_read_hunk(chd, s+i, b+copied) < 0) + return -1; + copied += chd->hunk_len; + } + else { + if (chd->hbuf == NULL) + if ((chd->hbuf=malloc(chd->hunk_len)) == NULL) { + chd->error = CHD_ERR_NOMEM; + return -1; + } + if (s+i != chd->hno) { + if (chd_read_hunk(chd, s+i, chd->hbuf) < 0) + return -1; + chd->hno = s+i; + } + memcpy(b+copied, chd->hbuf+o2, l2); + copied += l2; + } + } + + return len; +} + + + +static int +read_header(struct chd *chd) +{ + uint32_t len; + + unsigned char b[MAX_HEADERLEN], *p; + + if (fread(b, TAG_AND_LEN, 1, chd->f) != 1) { + chd->error = CHD_ERR_READ; + return -1; + } + + if (memcmp(b, TAG, TAG_LEN) != 0) { + chd->error = CHD_ERR_NO_CHD; + return -1; + } + + p = b+TAG_LEN; + len = GET_LONG(p); + if (len > MAX_HEADERLEN) { + chd->error = CHD_ERR_NO_CHD; + return -1; + } + if (fread(p, len-TAG_AND_LEN, 1, chd->f) != 1) { + chd->error = CHD_ERR_READ; + return -1; + } + + chd->hdr_length = len; + chd->version = GET_LONG(p); + chd->flags = GET_LONG(p); + chd->compression = GET_LONG(p); + + if (chd->version > 3) { + chd->error = CHD_ERR_VERSION; + return -1; + } + /* XXX: check chd->hdr_length against expected value for version */ + + if (chd->version < 3) { + chd->hunk_len = GET_LONG(p); + chd->total_hunks = GET_LONG(p); + p += 12; /* skip c/h/s */ + memcpy(chd->md5, p, sizeof(chd->md5)); + p += sizeof(chd->md5); + memcpy(chd->parent_md5, p, sizeof(chd->parent_md5)); + p += sizeof(chd->parent_md5); + + if (chd->version == 1) + chd->hunk_len *= 512; + else + chd->hunk_len *= GET_LONG(p); + chd->total_len = chd->hunk_len * chd->total_hunks; + chd->meta_offset = 0; + memset(chd->sha1, 0, sizeof(chd->sha1)); + memset(chd->parent_sha1, 0, sizeof(chd->parent_sha1)); + } + else { + chd->total_hunks = GET_LONG(p); + chd->total_len = GET_QUAD(p); + chd->meta_offset = GET_QUAD(p); + memcpy(chd->md5, p, sizeof(chd->md5)); + p += sizeof(chd->md5); + memcpy(chd->parent_md5, p, sizeof(chd->parent_md5)); + p += sizeof(chd->parent_md5); + chd->hunk_len = GET_LONG(p); + memcpy(chd->sha1, p, sizeof(chd->sha1)); + p += sizeof(chd->sha1); + } + + return 0; +} + + + +static int +read_map(struct chd *chd) +{ + unsigned char b[MAP_ENTRY_SIZE_V3], *p; + int i, len; + uint64_t v; + + if ((chd->map=malloc(sizeof(*chd->map)*chd->total_hunks)) == NULL) { + chd->error = CHD_ERR_NOMEM; + return -1; + } + + if (chd->version < 3) + len = MAP_ENTRY_SIZE_V12; + else + len = MAP_ENTRY_SIZE_V3; + + for (i=0; itotal_hunks; i++) { + if (fread(b, len, 1, chd->f) != 1) { + chd->error = CHD_ERR_READ; + return -1; + } + p = b; + + if (i == 1832) + chd->version = 3; + + if (chd->version < 3) { + v = GET_QUAD(p); + chd->map[i].offset = v & 0xFFFFFFFFFFFLL; + chd->map[i].crc = 0; + chd->map[i].length = v >> 44; + chd->map[i].flags = CHD_MAP_FL_NOCRC + | (chd->map[i].length == chd->hunk_len + ? CHD_MAP_TYPE_UNCOMPRESSED : CHD_MAP_TYPE_COMPRESSED); + } + else { + chd->map[i].offset = GET_QUAD(p); + chd->map[i].crc = GET_LONG(p); + chd->map[i].length = GET_SHORT(p); + chd->map[i].flags = GET_SHORT(p); + } + } + + return 0; +}