mirror of
https://github.com/Stichting-MINIX-Research-Foundation/pkgsrc-ng.git
synced 2025-09-22 19:24:22 -04:00
406 lines
9.4 KiB
Plaintext
406 lines
9.4 KiB
Plaintext
$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;
|
||
+}
|