2013-09-26 17:14:40 +02:00

406 lines
9.4 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

$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 <nih@giga.or.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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <zlib.h>
+
+#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; i<n; i++)
+ b[i] = b[i-8];
+ break;
+
+ case CHD_MAP_TYPE_SELF_HUNK:
+ /* XXX: check CRC here too? */
+ return chd_read_hunk(chd, chd->map[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; i<n; i++) {
+ if (i == 1) {
+ o2 = 0;
+ l2 = chd->hunk_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; i<chd->total_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;
+}