diff options
Diffstat (limited to 'engines/sci/scicore')
38 files changed, 10927 insertions, 0 deletions
diff --git a/engines/sci/scicore/Makefile.am b/engines/sci/scicore/Makefile.am new file mode 100644 index 0000000000..dfc61adbaa --- /dev/null +++ b/engines/sci/scicore/Makefile.am @@ -0,0 +1,11 @@ +INCLUDES = -I$(top_srcdir)/src/include @EXTRA_INCLUDES@ +EXTRA_DIST = sci_dos.c treedef.1 treedef.2 treedef.3 hufftree.1 \ +			 hufftree.2 hufftree.3 huffmake.pl      \ +			 hashmap.c exe.h exe_dec.h games.h +noinst_LIBRARIES = libscicore.a +libscicore_a_SOURCES = aatree.c tools.c resource.c decompress0.c        \ +		 versions.c decompress01.c decompress1.c decompress11.c \ +		 script.c vocab.c vocab_debug.c old_objects.c modules.c	\ +		 sci_memory.c resource_map.c resource_patch.c		\ +		 fnmatch.c int_hashmap.c console.c exe.c exe_lzexe.c	\ +		 exe_raw.c reg_t_hashmap.c diff --git a/engines/sci/scicore/aatree.c b/engines/sci/scicore/aatree.c new file mode 100644 index 0000000000..74c8f6519e --- /dev/null +++ b/engines/sci/scicore/aatree.c @@ -0,0 +1,181 @@ +/*************************************************************************** + aatree.c Copyright (C) 2006 Walter van Niftrik + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + +    Walter van Niftrik [w.f.b.w.v.niftrik@stud.tue.nl] + +***************************************************************************/ + +#include <stdlib.h> +#include <aatree.h> + +#include <sci_memory.h> + +struct aatree +{ +	struct aatree *left, *right; +	int level; +	void *key; +}; + +/* Sentinel node */ +static aatree_t bottom = {&bottom, &bottom, 0, NULL}; + +static void +skew(aatree_t **t) +{ +	if ((*t)->left->level == (*t)->level) { +		/* Rotate right */ +		aatree_t *temp = *t; +		*t = (*t)->left; +		temp->left = (*t)->right; +		(*t)->right = temp; +	} +} + +static void +split(aatree_t **t) +{ +	if ((*t)->right->right->level == (*t)->level) { +		/* Rotate left */ +		aatree_t *temp = *t; +		*t = (*t)->right; +		temp->right = (*t)->left; +		(*t)->left = temp; +		(*t)->level++; +	} +} + +static int +delete_node(void *x, aatree_t **t, aatree_t *deleted, int (*compar)(const void *, const void *)) +{ +	int retval = -1; + +	if (*t != &bottom) { +		/* Search down the tree */ +		aatree_t **n; + +		if (compar(x, (*t)->key) < 0) +			n = &(*t)->left; +		else { +			n = &(*t)->right; +			deleted = *t; +		} + +		retval = delete_node(x, n, deleted, compar); + +		/* At the bottom of the tree we remove the element (if it is present) */ +		if ((*n == &bottom) && (deleted != &bottom) && (compar(x, deleted->key) == 0)) { +			aatree_t *temp; +			deleted->key = (*t)->key; +			temp = *t; +			*t = (*t)->right; +			sci_free(temp); +			retval = 0; +		} +		else if (((*t)->left->level < (*t)->level - 1) || ((*t)->right->level < (*t)->level - 1)) { +			(*t)->level--; +			if ((*t)->right->level > (*t)->level) +				(*t)->right->level = (*t)->level; +			skew(t); +			skew(&(*t)->right); +			skew(&(*t)->right->right); +			split(t); +			split(&(*t)->right); +		} +	} + +	return retval; +} + +aatree_t * +aatree_new() +{ +	return ⊥ +} + +int +aatree_insert(void *x, aatree_t **t, int (*compar)(const void *, const void *)) +{ +	int retval = -1; +	int c; + +	if (*t == &bottom) { +		*t = (aatree_t*)sci_malloc(sizeof(aatree_t)); + +		if (*t == NULL) +			return 1; + +		(*t)->key = x; +		(*t)->left = ⊥ +		(*t)->right = ⊥ +		(*t)->level = 1; +		return 0; +	} + +	c = compar(x, (*t)->key); + +	if (c < 0) +		retval = aatree_insert(x, &(*t)->left, compar); +	else if (c > 0) +		retval = aatree_insert(x, &(*t)->right, compar); + +	skew(t); +	split(t); +	return retval; +} + +int +aatree_delete(void *x, aatree_t **t, int (*compar)(const void *, const void *)) +{ +	return delete_node(x, t, &bottom, compar); +} + +aatree_t * +aatree_walk(aatree_t *t, int direction) +{ +	if ((direction == AATREE_WALK_LEFT) && (t->left != &bottom)) +		return t->left; + +	if ((direction == AATREE_WALK_RIGHT) && (t->right != &bottom)) +		return t->right; + +	return NULL; +} + +void * +aatree_get_data(aatree_t *t) +{ +	return t->key; +} + +void +aatree_free(aatree_t *t) +{ +	if (t == &bottom) +		return; + +	aatree_free(t->left); +	aatree_free(t->right); + +	sci_free(t); +} diff --git a/engines/sci/scicore/console.c b/engines/sci/scicore/console.c new file mode 100644 index 0000000000..655cbbb66e --- /dev/null +++ b/engines/sci/scicore/console.c @@ -0,0 +1,151 @@ +/*************************************************************************** + console.c Copyright (C) 1999..2002 Christoph Reichenbach, TU Darmstadt + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + +    Christoph Reichenbach (CJR) [creichen@rbg.informatik.tu-darmstadt.de] + +***************************************************************************/ +/* First part of the console implmentation: VM independent stuff */ +/* Remember, it doesn't have to be fast. */ + +#include <sci_memory.h> +#include <engine.h> +#ifdef SCI_CONSOLE + +int con_passthrough = 0; +FILE *con_file = NULL; + +static void(*_con_string_callback)(char*) = NULL; +static void (*_con_pixmap_callback)(gfx_pixmap_t *) = NULL; + + +/****************************************/ +/* sciprintf                            */ +/****************************************/ + + +int +sciprintf (const char *fmt, ...) +{ +	va_list argp; +	size_t bufsize = 256; +	int i; +	char *buf 	= (char *) sci_malloc (bufsize); + +	if (NULL == fmt) { +		fprintf(stderr, "console.c: sciprintf(): NULL passed for parameter fmt\n"); +		return -1; +	} + +	if (NULL == buf) { +		fprintf(stderr, "console.c: sciprintf(): malloc failed for buf\n"); +		return -1; +	} + +	va_start (argp, fmt); +	while ((i = vsnprintf (buf, bufsize - 1, fmt, argp)) == -1 +	       || (i >= bufsize - 2)) { +		/* while we're out of space... */ +		va_end (argp); +		va_start (argp, fmt);	/* reset argp */ + +		free (buf); +		buf = (char *) sci_malloc (bufsize <<= 1); +	} +	va_end (argp); + +	if (con_passthrough) +		printf ("%s", buf); +	if (con_file) +		fprintf (con_file, "%s", buf); + +	 +	if (_con_string_callback) +		_con_string_callback(buf); +	else +		free(buf); + +	return 1; +} + +void +con_set_string_callback(void(*callback)(char *)) +{ +	_con_string_callback = callback; +} + +void +con_set_pixmap_callback(void(*callback)(gfx_pixmap_t *)) +{ +	_con_pixmap_callback = callback; +} + +int +con_can_handle_pixmaps(void) +{ +	return _con_pixmap_callback != NULL; +} + +int +con_insert_pixmap(gfx_pixmap_t *pixmap) +{ +	if (_con_pixmap_callback) +		_con_pixmap_callback(pixmap); +	else +		return 1; +	return 0; +} + + +void +open_console_file (char *filename) +{ +  if (con_file != NULL) +    fclose (con_file); + +  if (NULL == filename) +  { +    fprintf(stderr, "console.c: open_console_file(): NULL passed for parameter filename\r\n"); +  } +#ifdef WIN32 +  con_file = fopen (filename, "wt"); +#else +  con_file = fopen (filename, "w"); +#endif + +  if (NULL == con_file) +    fprintf(stderr, "console.c: open_console_file(): Could not open output file %s\n", filename); + +} + +void +close_console_file (void) +{ +  if (con_file != NULL) +  { +    fclose (con_file); +    con_file = NULL; +  } +} + + +#endif /* SCI_CONSOLE */ diff --git a/engines/sci/scicore/decompress0.c b/engines/sci/scicore/decompress0.c new file mode 100644 index 0000000000..00c98e03df --- /dev/null +++ b/engines/sci/scicore/decompress0.c @@ -0,0 +1,372 @@ +/*************************************************************************** + decompress0.c Copyright (C) 1999 Christoph Reichenbach, TU Darmstadt + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + +    Christoph Reichenbach (CJR) [creichen@rbg.informatik.tu-darmstadt.de] + +***************************************************************************/ +/* Reads data from a resource file and stores the result in memory. +** This is for SCI version 0 style compression. +*/ + +#include <sci_memory.h> +#include <sciresource.h> + +/* #define _SCI_DECOMPRESS_DEBUG */ + +/* 9-12 bit LZW encoding */ +int +decrypt1(guint8 *dest, guint8 *src, int length, int complength) +     /* Doesn't do length checking yet */ +{ +	/* Theory: Considering the input as a bit stream, we get a series of +	** 9 bit elements in the beginning. Every one of them is a 'token' +	** and either represents a literal (if < 0x100), or a link to a previous +	** token (tokens start at 0x102, because 0x101 is the end-of-stream +	** indicator and 0x100 is used to reset the bit stream decoder). +	** If it's a link, the indicated token and the character following it are +	** placed into the output stream. Note that the 'indicated token' may +	** very well consist of a link-token-plus-literal construct again, so +	** it's possible to represent strings longer than 2 recursively. +	** If the maximum number of tokens has been reached, the bit length is +	** increased by one, up to a maximum of 12 bits. +	** This implementation remembers the position each token was print to in +	** the output array, and the length of this token. This method should +	** be faster than the recursive approach. +	*/ + +	guint16 bitlen = 9; /* no. of bits to read (max. 12) */ +	guint16 bitmask = 0x01ff; +	guint16 bitctr = 0; /* current bit position */ +	guint16 bytectr = 0; /* current byte position */ +	guint16 token; /* The last received value */ +	guint16 maxtoken = 0x200; /* The biggest token */ + +	guint16 tokenlist[4096]; /* pointers to dest[] */ +	guint16 tokenlengthlist[4096]; /* char length of each token */ +	guint16 tokenctr = 0x102; /* no. of registered tokens (starts here)*/ + +	guint16 tokenlastlength = 0; + +	guint16 destctr = 0; + +	while (bytectr < complength) { + +		guint32 tokenmaker = src[bytectr++] >> bitctr; +		if (bytectr < complength) +			tokenmaker |= (src[bytectr] << (8-bitctr)); +		if (bytectr+1 < complength) +			tokenmaker |= (src[bytectr+1] << (16-bitctr)); + +		token = tokenmaker & bitmask; + +		bitctr += bitlen - 8; + +		while (bitctr >= 8) { +			bitctr -= 8; +			bytectr++; +		} + +		if (token == 0x101) return 0; /* terminator */ +		if (token == 0x100) { /* reset command */ +			maxtoken = 0x200; +			bitlen = 9; +			bitmask = 0x01ff; +			tokenctr = 0x0102; +		} else { + +			{ +				int i; + +				if (token > 0xff) { +				  if (token >= tokenctr) +				    { +#ifdef _SCI_DECOMPRESS_DEBUG +				      fprintf(stderr, "decrypt1: Bad token %x!\n", token); +#endif +				      /* Well this is really bad  */ +				      /* May be it should throw something like SCI_ERROR_DECOMPRESSION_INSANE */ +				    } else +				      { +					tokenlastlength = tokenlengthlist[token]+1; +					if (destctr+tokenlastlength>length) +					  { +#ifdef _SCI_DECOMPRESS_DEBUG + +					    /* For me this seems a normal situation, It's necessary to handle it*/ +					    printf ("decrypt1: Trying to write beyond the end of array(len=%d, destctr=%d, tok_len=%d)!\n", +						    length, destctr, tokenlastlength); +#endif + +					    i = 0; +					    for (; destctr<length; destctr++) { +					      dest[destctr++] = dest [tokenlist[token]+i]; +					      i++; +					    } +					  } else +					for (i=0; i< tokenlastlength; i++) { +						dest[destctr++] = dest[tokenlist[token]+i]; +					} +				      } +				} else { +					tokenlastlength = 1; +				  if (destctr >= length) +				    { +#ifdef _SCI_DECOMPRESS_DEBUG +				      printf ("decrypt1: Try to write single byte beyond end of array!\n"); +#endif +				    } else +					dest[destctr++] = (byte)token; +				} + +			} + +			if (tokenctr == maxtoken) { +				if (bitlen < 12) { +					bitlen++; +					bitmask <<= 1; +					bitmask |= 1; +					maxtoken <<= 1; +				} else continue; /* no further tokens allowed */ +			} + +			tokenlist[tokenctr] = destctr-tokenlastlength; +			tokenlengthlist[tokenctr++] = tokenlastlength; + +		} + +	} + +	return 0; + +} + + +/* Huffman-style token encoding */ +/***************************************************************************/ +/* This code was taken from Carl Muckenhoupt's sde.c, with some minor      */ +/* modifications.                                                          */ +/***************************************************************************/ + +/* decrypt2 helper function */ +gint16 getc2(guint8 *node, guint8 *src, +	     guint16 *bytectr, guint16 *bitctr, int complength) +{ +	guint16 next; + +	while (node[1] != 0) { +		gint16 value = (src[*bytectr] << (*bitctr)); +		(*bitctr)++; +		if (*bitctr == 8) { +			(*bitctr) = 0; +			(*bytectr)++; +		} + +		if (value & 0x80) { +			next = node[1] & 0x0f; /* low 4 bits */ +			if (next == 0) { +				guint16 result = (src[*bytectr] << (*bitctr)); + +				if (++(*bytectr) > complength) +					return -1; +				else if (*bytectr < complength) +					result |= src[*bytectr] >> (8-(*bitctr)); + +				result &= 0x0ff; +				return (result | 0x100); +			} +		} +		else { +			next = node[1] >> 4;  /* high 4 bits */ +		} +		node += next<<1; +	} +	return getInt16(node); +} + +/* Huffman token decryptor */ +int decrypt2(guint8* dest, guint8* src, int length, int complength) +     /* no complength checking atm */ +{ +	guint8 numnodes, terminator; +	guint8 *nodes; +	gint16 c; +	guint16 bitctr = 0, bytectr; + +	numnodes = src[0]; +	terminator = src[1]; +	bytectr = 2+ (numnodes << 1); +	nodes = src+2; + +	while (((c = getc2(nodes, src, &bytectr, &bitctr, complength)) +		!= (0x0100 | terminator)) && (c >= 0)) { +		if (length-- == 0) return SCI_ERROR_DECOMPRESSION_OVERFLOW; + +		*dest = (guint8)c; +		dest++; +	} + +	return (c == -1) ? SCI_ERROR_DECOMPRESSION_OVERFLOW : 0; + +} +/***************************************************************************/ +/* Carl Muckenhoupt's decompression code ends here                         */ +/***************************************************************************/ + +int sci0_get_compression_method(int resh) +{ +	guint16 compressedLength; +	guint16 compressionMethod; +	guint16 result_size; + +	/* Dummy variable */ +	if (read(resh, &result_size, 2) != 2) +		return SCI_ERROR_IO_ERROR; + +	if ((read(resh, &compressedLength, 2) != 2) || +	    (read(resh, &result_size, 2) != 2) || +	    (read(resh, &compressionMethod, 2) != 2)) +		return SCI_ERROR_IO_ERROR; + +#ifdef WORDS_BIGENDIAN +	compressionMethod = GUINT16_SWAP_LE_BE_CONSTANT(compressionMethod); +#endif + +	return compressionMethod; +} + + +int decompress0(resource_t *result, int resh, int sci_version) +{ +	guint16 compressedLength; +	guint16 compressionMethod; +	guint16 result_size; +	guint8 *buffer; + +	if (read(resh, &(result->id),2) != 2) +		return SCI_ERROR_IO_ERROR; + +#ifdef WORDS_BIGENDIAN +	result->id = GUINT16_SWAP_LE_BE_CONSTANT(result->id); +#endif +	result->number = result->id & 0x07ff; +	result->type = result->id >> 11; + +	if ((result->number > sci_max_resource_nr[sci_version]) || (result->type > sci_invalid_resource)) +		return SCI_ERROR_DECOMPRESSION_INSANE; + +	if ((read(resh, &compressedLength, 2) != 2) || +	    (read(resh, &result_size, 2) != 2) || +	    (read(resh, &compressionMethod, 2) != 2)) +		return SCI_ERROR_IO_ERROR; + +#ifdef WORDS_BIGENDIAN +	compressedLength = GUINT16_SWAP_LE_BE_CONSTANT(compressedLength); +	result_size = GUINT16_SWAP_LE_BE_CONSTANT(result_size); +	compressionMethod = GUINT16_SWAP_LE_BE_CONSTANT(compressionMethod); +#endif +	result->size = result_size; + +	if ((result->size > SCI_MAX_RESOURCE_SIZE) || +	    (compressedLength > SCI_MAX_RESOURCE_SIZE)) +		return SCI_ERROR_RESOURCE_TOO_BIG; +	/* With SCI0, this simply cannot happen. */ + +	if (compressedLength > 4) +		compressedLength -= 4; +	else { /* Object has size zero (e.g. view.000 in sq3) (does this really exist?) */ +		result->data = 0; +		result->status = SCI_STATUS_NOMALLOC; +		return SCI_ERROR_EMPTY_OBJECT; +	} + +	buffer = (guint8*)sci_malloc(compressedLength); +	result->data = (unsigned char*)sci_malloc(result->size); + +	if (read(resh, buffer, compressedLength) != compressedLength) { +		free(result->data); +		free(buffer); +		return SCI_ERROR_IO_ERROR; +	}; + + +#ifdef _SCI_DECOMPRESS_DEBUG +	fprintf(stderr, "Resource %s.%03hi encrypted with method %hi at %.2f%%" +		" ratio\n", +		sci_resource_types[result->type], result->number, compressionMethod, +		(result->size == 0)? -1.0 : +		(100.0 * compressedLength / result->size)); +	fprintf(stderr, "  compressedLength = 0x%hx, actualLength=0x%hx\n", +		compressedLength, result->size); +#endif + +	switch(compressionMethod) { + +	case 0: /* no compression */ +		if (result->size != compressedLength) { +			free(result->data); +			result->data = NULL; +			result->status = SCI_STATUS_NOMALLOC; +			free(buffer); +			return SCI_ERROR_DECOMPRESSION_OVERFLOW; +		} +		memcpy(result->data, buffer, compressedLength); +		result->status = SCI_STATUS_ALLOCATED; +		break; + +	case 1: /* LZW compression */ +		if (decrypt1(result->data, buffer, result->size, compressedLength)) { +			free(result->data); +			result->data = 0; /* So that we know that it didn't work */ +			result->status = SCI_STATUS_NOMALLOC; +			free(buffer); +			return SCI_ERROR_DECOMPRESSION_OVERFLOW; +		} +		result->status = SCI_STATUS_ALLOCATED; +		break; + +	case 2: /* Some sort of Huffman encoding */ +		if (decrypt2(result->data, buffer, result->size, compressedLength)) { +			free(result->data); +			result->data = 0; /* So that we know that it didn't work */ +			result->status = SCI_STATUS_NOMALLOC; +			free(buffer); +			return SCI_ERROR_DECOMPRESSION_OVERFLOW; +		} +		result->status = SCI_STATUS_ALLOCATED; +		break; + +	default: +		fprintf(stderr,"Resource %s.%03hi: Compression method %hi not " +			"supported!\n", sci_resource_types[result->type], result->number, +			compressionMethod); +		free(result->data); +		result->data = 0; /* So that we know that it didn't work */ +		result->status = SCI_STATUS_NOMALLOC; +		free(buffer); +		return SCI_ERROR_UNKNOWN_COMPRESSION; +	} + +	free(buffer); +	return 0; +} + diff --git a/engines/sci/scicore/decompress01.c b/engines/sci/scicore/decompress01.c new file mode 100644 index 0000000000..69795b1c6d --- /dev/null +++ b/engines/sci/scicore/decompress01.c @@ -0,0 +1,655 @@ +/*************************************************************************** + decompress01.c Copyright (C) 1999 The FreeSCI project + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + +    Christoph Reichenbach (CJR) [creichen@rbg.informatik.tu-darmstadt.de] + +***************************************************************************/ +/* Reads data from a resource file and stores the result in memory */ + +#include <sci_memory.h> +#include <sciresource.h> + +/*************************************************************************** +* The following code was originally created by Carl Muckenhoupt for his +* SCI decoder. It has been ported to the FreeSCI environment by Sergey Lapin. +***************************************************************************/ + +/* TODO: Clean up, re-organize, improve speed-wise */ + +struct tokenlist { +	guint8 data; +	gint16 next; +} tokens[0x1004]; + +static gint8 stak[0x1014] = {0}; +static gint8 lastchar = 0; +static gint16 stakptr = 0; +static guint16 numbits, bitstring, lastbits, decryptstart; +static gint16 curtoken, endtoken; + + +guint32 gbits(int numbits,  guint8 * data, int dlen); + +void decryptinit3(void) +{ +	int i; +	lastchar = lastbits = bitstring = stakptr = 0; +	numbits = 9; +	curtoken = 0x102; +	endtoken = 0x1ff; +	decryptstart = 0; +	gbits(0,0,0); +	for(i=0;i<0x1004;i++) { +		tokens[i].next=0; +		tokens[i].data=0; +	} +} + +int decrypt3(guint8 *dest, guint8 *src, int length, int complength) +{ +	static gint16 token; +	while(length != 0) { + +		switch (decryptstart) { +		case 0: +		case 1: +			bitstring = gbits(numbits, src, complength); +			if (bitstring == 0x101) { /* found end-of-data signal */ +				decryptstart = 4; +				return 0; +			} +			if (decryptstart == 0) { /* first char */ +				decryptstart = 1; +				lastbits = bitstring; +				*(dest++) = lastchar = (bitstring & 0xff); +				if (--length != 0) continue; +				return 0; +			} +			if (bitstring == 0x100) { /* start-over signal */ +				numbits = 9; +				endtoken = 0x1ff; +				curtoken = 0x102; +				decryptstart = 0; +				continue; +			} +			token = bitstring; +			if (token >= curtoken) { /* index past current point */ +				token = lastbits; +				stak[stakptr++] = lastchar; +			} +			while ((token > 0xff)&&(token < 0x1004)) { /* follow links back in data */ +				stak[stakptr++] = tokens[token].data; +				token = tokens[token].next; +			} +			lastchar = stak[stakptr++] = token & 0xff; +		case 2: +			while (stakptr > 0) { /* put stack in buffer */ +				*(dest++) = stak[--stakptr]; +				length--; +				if (length == 0) { +					decryptstart = 2; +					return 0; +				} +			} +			decryptstart = 1; +			if (curtoken <= endtoken) { /* put token into record */ +				tokens[curtoken].data = lastchar; +				tokens[curtoken].next = lastbits; +				curtoken++; +				if (curtoken == endtoken && numbits != 12) { +					numbits++; +					endtoken <<= 1; +					endtoken++; +				} +			} +			lastbits = bitstring; +			continue; /* When are "break" and "continue" synonymous? */ +		case 4: +			return 0; +		} +	} +	return 0;     /* [DJ] shut up compiler warning */ +} + +guint32 gbits(int numbits,  guint8 * data, int dlen) +{ +	int place; /* indicates location within byte */ +	guint32 bitstring; +	static guint32 whichbit=0; +	int i; + +	if(numbits==0) {whichbit=0; return 0;} + +	place = whichbit >> 3; +	bitstring=0; +	for(i=(numbits>>3)+1;i>=0;i--) +		{ +			if (i+place < dlen) +				bitstring |=data[place+i] << (8*(2-i)); +		} +	/*  bitstring = data[place+2] | (long)(data[place+1])<<8 +	    | (long)(data[place])<<16;*/ +	bitstring >>= 24-(whichbit & 7)-numbits; +	bitstring &= (0xffffffff >> (32-numbits)); +	/* Okay, so this could be made faster with a table lookup. +	   It doesn't matter. It's fast enough as it is. */ +	whichbit += numbits; +	return bitstring; +} + +/*************************************************************************** +* Carl Muckenhoupt's code ends here +***************************************************************************/ + +enum { +	PIC_OP_SET_COLOR = 0xf0, +	PIC_OP_DISABLE_VISUAL = 0xf1, +	PIC_OP_SET_PRIORITY = 0xf2, +	PIC_OP_DISABLE_PRIORITY = 0xf3, +	PIC_OP_SHORT_PATTERNS = 0xf4, +	PIC_OP_MEDIUM_LINES = 0xf5, +	PIC_OP_LONG_LINES = 0xf6, +	PIC_OP_SHORT_LINES = 0xf7, +	PIC_OP_FILL = 0xf8, +	PIC_OP_SET_PATTERN = 0xf9, +	PIC_OP_ABSOLUTE_PATTERN = 0xfa, +	PIC_OP_SET_CONTROL = 0xfb, +	PIC_OP_DISABLE_CONTROL = 0xfc, +	PIC_OP_MEDIUM_PATTERNS = 0xfd, +	PIC_OP_OPX = 0xfe, +	PIC_OP_TERMINATE = 0xff +}; + +enum { +	PIC_OPX_SET_PALETTE_ENTRIES = 0, +	PIC_OPX_EMBEDDED_VIEW = 1, +	PIC_OPX_SET_PALETTE = 2, +	PIC_OPX_PRIORITY_TABLE_EQDIST = 3, +	PIC_OPX_PRIORITY_TABLE_EXPLICIT = 4 +}; + +#define PAL_SIZE 1284 +#define CEL_HEADER_SIZE 7 +#define EXTRA_MAGIC_SIZE 15 + +static +void decode_rle(byte **rledata, byte **pixeldata, byte *outbuffer, int size) +{ +	int pos = 0; +	char nextbyte; +	byte *rd = *rledata; +	byte *ob = outbuffer; +	byte *pd = *pixeldata; + +	while (pos < size) +	{ +		nextbyte = *(rd++); +		*(ob++) = nextbyte; +		pos ++; +		switch (nextbyte&0xC0) +		{ +		case 0x40 : +		case 0x00 : +			memcpy(ob, pd, nextbyte); +			pd +=nextbyte; +			ob += nextbyte; +			pos += nextbyte; +			break; +		case 0xC0 : +			break; +		case 0x80 : +			nextbyte = *(pd++); +			*(ob++) = nextbyte; +			pos ++; +			break; +		} +	} + +	*rledata = rd; +	*pixeldata = pd; +} + +/* + * Does the same this as above, only to determine the length of the compressed + * source data. + * + * Yes, this is inefficient. + */ +static +int rle_size(byte *rledata, int dsize) +{ +	int pos = 0; +	char nextbyte; +	int size = 0; +	 +	while (pos < dsize) +	{ +		nextbyte = *(rledata++); +		pos ++; +		size ++; +		 +		switch (nextbyte&0xC0) +		{ +		case 0x40 : +		case 0x00 : +			pos += nextbyte; +			break; +		case 0xC0 : +			break; +		case 0x80 : +			pos ++; +			break; +		} +	} + +	return size; +} + +byte *pic_reorder(byte *inbuffer, int dsize) +{ +	byte *reorderBuffer; +	int view_size; +	int view_start; +	int cdata_size; +	int i; +	byte *seeker = inbuffer; +	byte *writer; +	char viewdata[CEL_HEADER_SIZE]; +	byte *cdata, *cdata_start; +	 +	writer = reorderBuffer=(byte *) malloc(dsize); + +	*(writer++) = PIC_OP_OPX; +	*(writer++) = PIC_OPX_SET_PALETTE; + +	for (i=0;i<256;i++) /* Palette translation map */ +		*(writer++) = i; + +	putInt16(writer, 0); /* Palette stamp */ +	writer += 2; +	putInt16(writer, 0); +	writer += 2; + +	view_size = getUInt16(seeker); +	seeker += 2; +	view_start = getUInt16(seeker); +	seeker += 2; +	cdata_size = getUInt16(seeker); +	seeker += 2; + +	memcpy(viewdata, seeker, sizeof(viewdata)); +	seeker += sizeof(viewdata); +	 +	memcpy(writer, seeker, 4*256); /* Palette */ +	seeker += 4*256; +	writer += 4*256; + +	if (view_start != PAL_SIZE + 2) /* +2 for the opcode */ +	{ +		memcpy(writer, seeker, view_start-PAL_SIZE-2); +		seeker += view_start - PAL_SIZE - 2; +		writer += view_start - PAL_SIZE - 2; +	} + +	if (dsize != view_start+EXTRA_MAGIC_SIZE+view_size) +	{ +		memcpy(reorderBuffer+view_size+view_start+EXTRA_MAGIC_SIZE, seeker,  +		       dsize-view_size-view_start-EXTRA_MAGIC_SIZE); +		seeker += dsize-view_size-view_start-EXTRA_MAGIC_SIZE; +	} + +	cdata_start=cdata=(byte *) malloc(cdata_size); +	memcpy(cdata, seeker, cdata_size); +	seeker += cdata_size; +	 +	writer = reorderBuffer + view_start; +	*(writer++) = PIC_OP_OPX; +	*(writer++) = PIC_OPX_EMBEDDED_VIEW; +	*(writer++) = 0; +	*(writer++) = 0; +	*(writer++) = 0; +	putInt16(writer, view_size + 8); +	writer += 2; + +	memcpy(writer, viewdata, sizeof(viewdata)); +	writer += sizeof(viewdata); + +	*(writer++) = 0; +	 +	decode_rle(&seeker, &cdata, writer, view_size); +	 +	free(cdata_start); +	free(inbuffer); +	return reorderBuffer; +} + +#define VIEW_HEADER_COLORS_8BIT 0x80 + +static +void build_cel_headers(byte **seeker, byte **writer, int celindex, int *cc_lengths, int max) +{ +	int c, w; +	 +	for (c=0;c<max;c++) +	{ +		w=getUInt16(*seeker); +		putInt16(*writer, w);  +		*seeker += 2; *writer += 2; +		w=getUInt16(*seeker); +		putInt16(*writer, w);  +		*seeker += 2; *writer += 2; +		w=getUInt16(*seeker); +		putInt16(*writer, w);  +		*seeker += 2; *writer += 2; +		w=*((*seeker)++); +		putInt16(*writer, w); /* Zero extension */ +		*writer += 2; + +		*writer += cc_lengths[celindex]; +		celindex ++; +	} +} + + + +byte *view_reorder(byte *inbuffer, int dsize) +{ +	byte *cellengths; +	int loopheaders; +	int lh_present; +	int lh_mask; +	int pal_offset; +	int cel_total; +	int unknown; +	byte *seeker = inbuffer; +	char celcounts[100]; +	byte *outbuffer = (byte *) malloc(dsize); +	byte *writer = outbuffer; +	byte *lh_ptr; +	byte *rle_ptr,*pix_ptr; +	int l, lb, c, celindex, lh_last = -1; +	int chptr; +	int w; +	int *cc_lengths; +	byte **cc_pos; +	 +	/* Parse the main header */ +	cellengths = inbuffer+getUInt16(seeker)+2; +	seeker += 2; +	loopheaders = *(seeker++); +	lh_present = *(seeker++); +	lh_mask = getUInt16(seeker); +	seeker += 2; +	unknown = getUInt16(seeker); +	seeker += 2; +	pal_offset = getUInt16(seeker); +	seeker += 2; +	cel_total = getUInt16(seeker); +	seeker += 2; + +	cc_pos = (byte **) malloc(sizeof(byte *)*cel_total); +	cc_lengths = (int *) malloc(sizeof(int)*cel_total); +	 +	for (c=0;c<cel_total;c++) +		cc_lengths[c] = getUInt16(cellengths+2*c); +	 +	*(writer++) = loopheaders; +	*(writer++) = VIEW_HEADER_COLORS_8BIT; +	putInt16(writer, lh_mask); +	writer += 2; +	putInt16(writer, unknown); +	writer += 2; +	putInt16(writer, pal_offset); +	writer += 2; + +	lh_ptr = writer; +	writer += 2*loopheaders; /* Make room for the loop offset table */ + +	pix_ptr = writer; +	 +	memcpy(celcounts, seeker, lh_present); +	seeker += lh_present; + +	lb = 1; +	celindex = 0; + +	rle_ptr = pix_ptr = cellengths + (2*cel_total); +	w = 0; +	 +	for (l=0;l<loopheaders;l++) +	{ +		if (lh_mask & lb) /* The loop is _not_ present */ +		{ +			if (lh_last == -1) { +				fprintf(stderr, "Error: While reordering view: Loop not present, but can't re-use last loop!\n"); +				lh_last = 0; +			} +			putInt16(lh_ptr, lh_last); +			lh_ptr += 2; +		} else +		{ +			lh_last = writer-outbuffer; +			putInt16(lh_ptr, lh_last); +			lh_ptr += 2; +			putInt16(writer, celcounts[w]); +			writer += 2; +			putInt16(writer, 0); +			writer += 2; + +			/* Now, build the cel offset table */ +			chptr = (writer - outbuffer)+(2*celcounts[w]); + +			for (c=0;c<celcounts[w];c++) +			{ +				putInt16(writer, chptr); +				writer += 2; +				cc_pos[celindex+c] = outbuffer + chptr; +				chptr += 8 + getUInt16(cellengths+2*(celindex+c)); +			} + +			build_cel_headers(&seeker, &writer, celindex, cc_lengths, celcounts[w]); +			 +			celindex += celcounts[w]; +			w++; +		} + +		lb = lb << 1;	 +	}	 + +	if (celindex < cel_total) +	{ +		fprintf(stderr, "View decompression generated too few (%d / %d) headers!\n", celindex, cel_total); +		return NULL; +	} +	 +	/* Figure out where the pixel data begins. */ +	for (c=0;c<cel_total;c++) +		pix_ptr += rle_size(pix_ptr, cc_lengths[c]); + +	rle_ptr = cellengths + (2*cel_total); +	for (c=0;c<cel_total;c++) +		decode_rle(&rle_ptr, &pix_ptr, cc_pos[c]+8, cc_lengths[c]); + +	*(writer++) = 'P'; +	*(writer++) = 'A'; +	*(writer++) = 'L'; +	 +	for (c=0;c<256;c++) +		*(writer++) = c; + +	seeker -= 4; /* The missing four. Don't ask why. */ +	memcpy(writer, seeker, 4*256+4); +	 +	free(cc_pos); +	free(cc_lengths); +	free(inbuffer); +	return outbuffer;	 +} + + + +int decompress01(resource_t *result, int resh, int sci_version) +{ +	guint16 compressedLength, result_size; +	guint16 compressionMethod; +	guint8 *buffer; + +	if (read(resh, &(result->id),2) != 2) +		return SCI_ERROR_IO_ERROR; + +#ifdef WORDS_BIGENDIAN +	result->id = GUINT16_SWAP_LE_BE_CONSTANT(result->id); +#endif + +	result->number = result->id & 0x07ff; +	result->type = result->id >> 11; + +	if ((result->number > sci_max_resource_nr[sci_version] || (result->type > sci_invalid_resource))) +		return SCI_ERROR_DECOMPRESSION_INSANE; + +	if ((read(resh, &compressedLength, 2) != 2) || +	    (read(resh, &result_size, 2) != 2) || +	    (read(resh, &compressionMethod, 2) != 2)) +		return SCI_ERROR_IO_ERROR; + +#ifdef WORDS_BIGENDIAN +	compressedLength = GUINT16_SWAP_LE_BE_CONSTANT(compressedLength); +	result_size = GUINT16_SWAP_LE_BE_CONSTANT(result_size); +	compressionMethod = GUINT16_SWAP_LE_BE_CONSTANT(compressionMethod); +#endif +	result->size = result_size; + +	/*  if ((result->size < 0) || (compressedLength < 0)) +	    return SCI_ERROR_DECOMPRESSION_INSANE; */ +	/* This return will never happen in SCI0 or SCI1 (does it have any use?) */ + +	if ((result->size > SCI_MAX_RESOURCE_SIZE) || +	    (compressedLength > SCI_MAX_RESOURCE_SIZE)) +		return SCI_ERROR_RESOURCE_TOO_BIG; + +	if (compressedLength > 4) +		compressedLength -= 4; +	else { /* Object has size zero (e.g. view.000 in sq3) (does this really exist?) */ +		result->data = 0; +		result->status = SCI_STATUS_NOMALLOC; +		return SCI_ERROR_EMPTY_OBJECT; +	} + +	buffer = (guint8*)sci_malloc(compressedLength); +	result->data = (unsigned char*)sci_malloc(result->size); + +	if (read(resh, buffer, compressedLength) != compressedLength) { +		free(result->data); +		free(buffer); +		return SCI_ERROR_IO_ERROR; +	}; + + +#ifdef _SCI_DECOMPRESS_DEBUG +	fprintf(stderr, "Resource %s.%03hi encrypted with method SCI01/%hi at %.2f%%" +		" ratio\n", +		sci_resource_types[result->type], result->number, compressionMethod, +		(result->size == 0)? -1.0 : +		(100.0 * compressedLength / result->size)); +	fprintf(stderr, "  compressedLength = 0x%hx, actualLength=0x%hx\n", +		compressedLength, result->size); +#endif + +	switch(compressionMethod) { + +	case 0: /* no compression */ +		if (result->size != compressedLength) { +			free(result->data); +			result->data = NULL; +			result->status = SCI_STATUS_NOMALLOC; +			free(buffer); +			return SCI_ERROR_DECOMPRESSION_OVERFLOW; +		} +		memcpy(result->data, buffer, compressedLength); +		result->status = SCI_STATUS_ALLOCATED; +		break; + +	case 1: /* Some huffman encoding */ +		if (decrypt2(result->data, buffer, result->size, compressedLength)) { +			free(result->data); +			result->data = 0; /* So that we know that it didn't work */ +			result->status = SCI_STATUS_NOMALLOC; +			free(buffer); +			return SCI_ERROR_DECOMPRESSION_OVERFLOW; +		} +		result->status = SCI_STATUS_ALLOCATED; +		break; + +	case 2: /* ??? */ +		decryptinit3(); +		if (decrypt3(result->data, buffer, result->size, compressedLength)) { +			free(result->data); +			result->data = 0; /* So that we know that it didn't work */ +			result->status = SCI_STATUS_NOMALLOC; +			free(buffer); +			return SCI_ERROR_DECOMPRESSION_OVERFLOW; +		} +		result->status = SCI_STATUS_ALLOCATED; +		break; + +	case 3:  +		decryptinit3(); +		if (decrypt3(result->data, buffer, result->size, compressedLength)) { +			free(result->data); +			result->data = 0; /* So that we know that it didn't work */ +			result->status = SCI_STATUS_NOMALLOC; +			free(buffer); +			return SCI_ERROR_DECOMPRESSION_OVERFLOW; +		} +		result->data = view_reorder(result->data, result->size); +		result->status = SCI_STATUS_ALLOCATED; +		break; + +	case 4: +		decryptinit3(); +		if (decrypt3(result->data, buffer, result->size, compressedLength)) { +			free(result->data); +			result->data = 0; /* So that we know that it didn't work */ +			result->status = SCI_STATUS_NOMALLOC; +			free(buffer); +			return SCI_ERROR_DECOMPRESSION_OVERFLOW; +		} +		result->data = pic_reorder(result->data, result->size); +		result->status = SCI_STATUS_ALLOCATED; +		break; +		 +	default: +		fprintf(stderr,"Resource %s.%03hi: Compression method SCI1/%hi not " +			"supported!\n", sci_resource_types[result->type], result->number, +			compressionMethod); +		free(result->data); +		result->data = 0; /* So that we know that it didn't work */ +		result->status = SCI_STATUS_NOMALLOC; +		free(buffer); +		return SCI_ERROR_UNKNOWN_COMPRESSION; +	} + +	free(buffer); +	return 0; +} + diff --git a/engines/sci/scicore/decompress1.c b/engines/sci/scicore/decompress1.c new file mode 100644 index 0000000000..978a00bf86 --- /dev/null +++ b/engines/sci/scicore/decompress1.c @@ -0,0 +1,443 @@ +/*************************************************************************** + decompress1.c Copyright (C) 1999 The FreeSCI project + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + +    Christoph Reichenbach (CJR) [creichen@rbg.informatik.tu-darmstadt.de] + +***************************************************************************/ +/* Reads data from a resource file and stores the result in memory */ + +#include <sci_memory.h> +#include <sciresource.h> + + + +/* DEFLATE-DCL +** Refer to the FreeSCI docs for a full description. +*/ + +#define HUFFMAN_LEAF 0x40000000 + +struct bit_read_struct { +	int length; +	int bitpos; +	int bytepos; +	byte *data; +}; + +#define BRANCH_SHIFT 12 +#define BRANCH_NODE(pos, left, right)  ((left << BRANCH_SHIFT) | (right)), +#define LEAF_NODE(pos, value)  ((value) | HUFFMAN_LEAF), + + +static int length_tree[] = { +#include "treedef.1" +	0 /* We need something witout a comma at the end */ +}; + +static int distance_tree[] = { +#include "treedef.2" +	0 /* We need something witout a comma at the end */ +}; + +static int ascii_tree[] = { +#include "treedef.3" +	0 /* We need something witout a comma at the end */ +}; + +#define CALLC(x) { if ((x) == -SCI_ERROR_DECOMPRESSION_OVERFLOW) return -SCI_ERROR_DECOMPRESSION_OVERFLOW; } + +static inline int +getbits_msb_first(struct bit_read_struct *inp, int bits) +{ +	int morebytes = (bits + inp->bitpos - 1) >> 3; +	int result = 0; +	int i; + +	if (inp->bytepos + morebytes >= inp->length) { +		fprintf(stderr,"read out-of-bounds with bytepos %d + morebytes %d >= length %d\n", +			inp->bytepos, morebytes, inp->length); +		return -SCI_ERROR_DECOMPRESSION_OVERFLOW; +	} + +	for (i = 0; i <= morebytes; i++) +		result |= (inp->data[inp->bytepos + i]) << (i << 3); + +	result >>= inp->bitpos; +	result &= ~(~0 << bits); + +	inp->bitpos += bits - (morebytes << 3); +	inp->bytepos += morebytes; + +	return result; +} + +static int DEBUG_DCL_INFLATE = 0; /* FIXME: Make this a define eventually */ + +static inline int +getbits(struct bit_read_struct *inp, int bits) +{ +	int morebytes = (bits + inp->bitpos - 1) >> 3; +	int result = 0; +	int i; + +	if (inp->bytepos + morebytes >= inp->length) { +		fprintf(stderr,"read out-of-bounds with bytepos %d + morebytes %d >= length %d\n", +			inp->bytepos, morebytes, inp->length); +		return -SCI_ERROR_DECOMPRESSION_OVERFLOW; +	} + +	for (i = 0; i <= morebytes; i++) +		result |= (inp->data[inp->bytepos + i]) << (i << 3); + +	result >>= inp->bitpos; +	result &= ~((~0) << bits); + +	inp->bitpos += bits - (morebytes << 3); +	inp->bytepos += morebytes; + +	if (DEBUG_DCL_INFLATE) +		fprintf(stderr,"(%d:%04x)", bits, result); + +	return result; +} + +static int +huffman_lookup(struct bit_read_struct *inp, int *tree) +{ +	int pos = 0; +	int bit; + +	while (!(tree[pos] & HUFFMAN_LEAF)) { +		CALLC(bit = getbits(inp, 1)); +		if (DEBUG_DCL_INFLATE) +			fprintf(stderr,"[%d]:%d->", pos, bit); +		if (bit) +			pos = tree[pos] & ~(~0 << BRANCH_SHIFT); +		else +			pos = tree[pos] >> BRANCH_SHIFT; +	} +	if (DEBUG_DCL_INFLATE) +		fprintf(stderr,"=%02x\n", tree[pos] & 0xffff); +	return tree[pos] & 0xffff; +} + +#define VALUE_M(i) ((i == 0)? 7 : (VALUE_M(i - 1) + 2**i)); + +#define DCL_ASCII_MODE 1 + +static int +decrypt4_hdyn(byte *dest, int length, struct bit_read_struct *reader) +{ +	int mode, length_param, value, val_length, val_distance; +	int write_pos = 0; +	int M[] = {0x07, 0x08, 0x0A, 0x0E, 0x16, 0x26, 0x46, 0x86, 0x106}; + +	CALLC(mode = getbits(reader, 8)); +	CALLC(length_param = getbits(reader, 8)); + +	if (mode == DCL_ASCII_MODE) { +		fprintf(stderr,"DCL-INFLATE: Warning: Decompressing ASCII mode (untested)\n"); +		/*		DEBUG_DCL_INFLATE = 1; */ +	} else if (mode) { +		fprintf(stderr,"DCL-INFLATE: Error: Encountered mode %02x, expected 00 or 01\n", mode); +		return 1; +	} + +	if (DEBUG_DCL_INFLATE) { +		int i; +		for (i = 0; i < reader->length; i++) { +			fprintf(stderr,"%02x ", reader->data[i]); +			if (!((i+1) & 0x1f)) +				fprintf(stderr,"\n"); +		} + + +		fprintf(stderr,"\n---\n"); +	} + + +	if (length_param < 3 || length_param > 6) +		fprintf(stderr,"Warning: Unexpected length_param value %d (expected in [3,6])\n", length_param); + +	while (write_pos < length) { +		CALLC(value = getbits(reader, 1)); + +		if (value) { /* (length,distance) pair */ +			CALLC(value = huffman_lookup(reader, length_tree)); + +			if (value < 8) +				val_length = value + 2; +			else { +				int length_bonus; + +				val_length = M[value - 7] + 2; +				CALLC(length_bonus = getbits(reader, value - 7)); +				val_length += length_bonus; +			} + +			if (DEBUG_DCL_INFLATE) +				fprintf(stderr," | "); + +			CALLC(value = huffman_lookup(reader, distance_tree)); + +			if (val_length == 2) { +				val_distance = value << 2; + +				CALLC(value = getbits(reader, 2)); +				val_distance |= value; +			} else { +				val_distance = value << length_param; + +				CALLC(value = getbits(reader, length_param)); +				val_distance |= value; +			} +			++val_distance; + +			if (DEBUG_DCL_INFLATE) +				fprintf(stderr,"\nCOPY(%d from %d)\n", val_length, val_distance); + +			if (val_length + write_pos > length) { +				fprintf(stderr, "DCL-INFLATE Error: Write out of bounds while copying %d bytes\n", val_length); +				return -SCI_ERROR_DECOMPRESSION_OVERFLOW; +			} + +			if (write_pos < val_distance) { +				fprintf(stderr, "DCL-INFLATE Error: Attempt to copy from before beginning of input stream\n"); +				return -SCI_ERROR_DECOMPRESSION_INSANE; +			} + +			while (val_length) { +				int copy_length = (val_length > val_distance)? val_distance : val_length; + +				memcpy(dest + write_pos, dest + write_pos - val_distance, copy_length); + +				if (DEBUG_DCL_INFLATE) { +					int i; +					for (i = 0; i < copy_length; i++) +						fprintf(stderr,"\33[32;31m%02x\33[37;37m ", dest[write_pos + i]); +					fprintf(stderr, "\n"); +				} + +				val_length -= copy_length; +				val_distance += copy_length; +				write_pos += copy_length; +			} + +		} else { /* Copy byte verbatim */ +			if (mode == DCL_ASCII_MODE) { +				CALLC(value = huffman_lookup(reader, ascii_tree)); +			} else { +				CALLC(value = getbits(reader, 8)); +			} + +			dest[write_pos++] = value; + +			if (DEBUG_DCL_INFLATE) +				fprintf(stderr, "\33[32;31m%02x \33[37;37m", value); +		} +	} + +	return 0; +} + +int +decrypt4(guint8* dest, guint8* src, int length, int complength) +{ +	struct bit_read_struct reader; + +	reader.length = complength; +	reader.bitpos = 0; +	reader.bytepos = 0; +	reader.data = src; + +	return -decrypt4_hdyn(dest, length, &reader); +} + + + + +void decryptinit3(void); +int decrypt3(guint8* dest, guint8* src, int length, int complength); +int decompress1(resource_t *result, int resh, int early); + +int decompress1(resource_t *result, int resh, int sci_version) +{ +	guint16 compressedLength; +	guint16 compressionMethod, result_size; +	guint8 *buffer; +	guint8 tempid; + +	if (sci_version == SCI_VERSION_1_EARLY) { +		if (read(resh, &(result->id),2) != 2) +			return SCI_ERROR_IO_ERROR; + +#ifdef WORDS_BIGENDIAN +		result->id = GUINT16_SWAP_LE_BE_CONSTANT(result->id); +#endif + +		result->number = result->id & 0x07ff; +		result->type = result->id >> 11; + +		if ((result->number >= sci_max_resource_nr[SCI_VERSION_1_LATE]) || (result->type > sci_invalid_resource)) +			return SCI_ERROR_DECOMPRESSION_INSANE; +	} else { +		if (read(resh, &tempid, 1) != 1) +			return SCI_ERROR_IO_ERROR; + +		result->id = tempid; + +		result->type = result->id &0x7f; +		if (read(resh, &(result->number), 2) != 2) +			return SCI_ERROR_IO_ERROR; + +#ifdef WORDS_BIGENDIAN +		result->number = GUINT16_SWAP_LE_BE_CONSTANT(result->number); +#endif /* WORDS_BIGENDIAN */ +		if ((result->number >= sci_max_resource_nr[SCI_VERSION_1_LATE]) || (result->type > sci_invalid_resource)) +			return SCI_ERROR_DECOMPRESSION_INSANE; +	} + +	if ((read(resh, &compressedLength, 2) != 2) || +	    (read(resh, &result_size, 2) != 2) || +	    (read(resh, &compressionMethod, 2) != 2)) +		return SCI_ERROR_IO_ERROR; + +#ifdef WORDS_BIGENDIAN +	compressedLength = GUINT16_SWAP_LE_BE_CONSTANT(compressedLength); +	result_size = GUINT16_SWAP_LE_BE_CONSTANT(result_size); +	compressionMethod = GUINT16_SWAP_LE_BE_CONSTANT(compressionMethod); +#endif +	result->size = result_size; + +	if ((result->size > SCI_MAX_RESOURCE_SIZE) || +	    (compressedLength > SCI_MAX_RESOURCE_SIZE)) +		return SCI_ERROR_RESOURCE_TOO_BIG; + +	if (compressedLength > 4) +		compressedLength -= 4; +	else { /* Object has size zero (e.g. view.000 in sq3) (does this really exist?) */ +		result->data = 0; +		result->status = SCI_STATUS_NOMALLOC; +		return SCI_ERROR_EMPTY_OBJECT; +	} + +	buffer = (guint8*)sci_malloc(compressedLength); +	result->data = (unsigned char*)sci_malloc(result->size); + +	if (read(resh, buffer, compressedLength) != compressedLength) { +		free(result->data); +		free(buffer); +		return SCI_ERROR_IO_ERROR; +	}; + + +#ifdef _SCI_DECOMPRESS_DEBUG +	fprintf(stderr, "Resource %i.%s encrypted with method SCI1%c/%hi at %.2f%%" +		" ratio\n", +		result->number, sci_resource_type_suffixes[result->type], +		early? 'e' : 'l', +		compressionMethod, +		(result->size == 0)? -1.0 : +		(100.0 * compressedLength / result->size)); +	fprintf(stderr, "  compressedLength = 0x%hx, actualLength=0x%hx\n", +		compressedLength, result->size); +#endif + +	switch(compressionMethod) { + +	case 0: /* no compression */ +		if (result->size != compressedLength) { +			free(result->data); +			result->data = NULL; +			result->status = SCI_STATUS_NOMALLOC; +			free(buffer); +			return SCI_ERROR_DECOMPRESSION_OVERFLOW; +		} +		memcpy(result->data, buffer, compressedLength); +		result->status = SCI_STATUS_ALLOCATED; +		break; + +	case 1: /* LZW */ +		if (decrypt2(result->data, buffer, result->size, compressedLength)) { +			free(result->data); +			result->data = 0; /* So that we know that it didn't work */ +			result->status = SCI_STATUS_NOMALLOC; +			free(buffer); +			return SCI_ERROR_DECOMPRESSION_OVERFLOW; +		} +		result->status = SCI_STATUS_ALLOCATED; +		break; + +	case 2: /* ??? */ +		decryptinit3(); +		if (decrypt3(result->data, buffer, result->size, compressedLength)) { +			free(result->data); +			result->data = 0; /* So that we know that it didn't work */ +			result->status = SCI_STATUS_NOMALLOC; +			free(buffer); +			return SCI_ERROR_DECOMPRESSION_OVERFLOW; +		} +		result->status = SCI_STATUS_ALLOCATED; +		break; + +	case 3:  +		decryptinit3(); +		if (decrypt3(result->data, buffer, result->size, compressedLength)) { +			free(result->data); +			result->data = 0; /* So that we know that it didn't work */ +			result->status = SCI_STATUS_NOMALLOC; +			free(buffer); +			return SCI_ERROR_DECOMPRESSION_OVERFLOW; +		} +		result->data = view_reorder(result->data, result->size); +		result->status = SCI_STATUS_ALLOCATED; +		break; + +	case 4: +		decryptinit3(); +		if (decrypt3(result->data, buffer, result->size, compressedLength)) { +			free(result->data); +			result->data = 0; /* So that we know that it didn't work */ +			result->status = SCI_STATUS_NOMALLOC; +			free(buffer); +			return SCI_ERROR_DECOMPRESSION_OVERFLOW; +		} +		result->data = pic_reorder(result->data, result->size); +		result->status = SCI_STATUS_ALLOCATED; +		break; +		 +	default: +		fprintf(stderr,"Resource %s.%03hi: Compression method SCI1/%hi not " +			"supported!\n", sci_resource_types[result->type], result->number, +			compressionMethod); +		free(result->data); +		result->data = 0; /* So that we know that it didn't work */ +		result->status = SCI_STATUS_NOMALLOC; +		free(buffer); +		return SCI_ERROR_UNKNOWN_COMPRESSION; +	} + +	free(buffer); +	return 0; +} + diff --git a/engines/sci/scicore/decompress11.c b/engines/sci/scicore/decompress11.c new file mode 100644 index 0000000000..eb3cc39a73 --- /dev/null +++ b/engines/sci/scicore/decompress11.c @@ -0,0 +1,167 @@ +/*************************************************************************** + decompress11.c Copyright (C) 1999 The FreeSCI project + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + +    Christoph Reichenbach (CJR) [creichen@rbg.informatik.tu-darmstadt.de] + +***************************************************************************/ +/* Reads data from a resource file and stores the result in memory */ + +#include <sci_memory.h> +#include <sciresource.h> + +#define DDEBUG if (0) printf + +void decryptinit3(void); +int decrypt3(guint8* dest, guint8* src, int length, int complength); +int decrypt4(guint8* dest, guint8* src, int length, int complength); + +int decompress11(resource_t *result, int resh, int sci_version) +{ +	guint16 compressedLength; +	guint16 compressionMethod, result_size; +	guint8 *buffer; +	guint8 tempid; + +	DDEBUG("d1"); + +	if (read(resh, &tempid, 1) != 1) +		return SCI_ERROR_IO_ERROR; + +	result->id = tempid; + +	result->type = result->id &0x7f; +	if (read(resh, &(result->number), 2) != 2) +		return SCI_ERROR_IO_ERROR; + +#ifdef WORDS_BIGENDIAN +	result->number = GUINT16_SWAP_LE_BE_CONSTANT(result->number); +#endif /* WORDS_BIGENDIAN */ +	if ((result->type > sci_invalid_resource)) +		return SCI_ERROR_DECOMPRESSION_INSANE; + +	if ((read(resh, &compressedLength, 2) != 2) || +	    (read(resh, &result_size, 2) != 2) || +	    (read(resh, &compressionMethod, 2) != 2)) +		return SCI_ERROR_IO_ERROR; + +#ifdef WORDS_BIGENDIAN +	compressedLength = GUINT16_SWAP_LE_BE_CONSTANT(compressedLength); +	result_size = GUINT16_SWAP_LE_BE_CONSTANT(result_size); +	compressionMethod = GUINT16_SWAP_LE_BE_CONSTANT(compressionMethod); +#endif +	result->size = result_size; + +  /*  if ((result->size < 0) || (compressedLength < 0)) +      return SCI_ERROR_DECOMPRESSION_INSANE; */ +  /* This return will never happen in SCI0 or SCI1 (does it have any use?) */ + +	if ((result->size > SCI_MAX_RESOURCE_SIZE) || +	    (compressedLength > SCI_MAX_RESOURCE_SIZE)) +		return SCI_ERROR_RESOURCE_TOO_BIG; + +	if (compressedLength > 0) +		compressedLength -= 0; +	else { /* Object has size zero (e.g. view.000 in sq3) (does this really exist?) */ +		result->data = 0; +		result->status = SCI_STATUS_NOMALLOC; +		return SCI_ERROR_EMPTY_OBJECT; +	} + +	buffer = (guint8*)sci_malloc(compressedLength); +	result->data = (unsigned char*)sci_malloc(result->size); + +	if (read(resh, buffer, compressedLength) != compressedLength) { +		free(result->data); +		free(buffer); +		return SCI_ERROR_IO_ERROR; +	}; + +	if (!(compressedLength & 1)) { /* Align */ +		int foo; +		read(resh, &foo, 1); +	} + +#ifdef _SCI_DECOMPRESS_DEBUG +	fprintf(stderr, "Resource %i.%s encrypted with method SCI1.1/%hi at %.2f%%" +		" ratio\n", +		result->number, sci_resource_type_suffixes[result->type], +		compressionMethod, +		(result->size == 0)? -1.0 : +		(100.0 * compressedLength / result->size)); +	fprintf(stderr, "  compressedLength = 0x%hx, actualLength=0x%hx\n", +		compressedLength, result->size); +#endif + +	DDEBUG("/%d[%d]", compressionMethod, result->size); + +	switch(compressionMethod) { + +	case 0: /* no compression */ +		if (result->size != compressedLength) { +			free(result->data); +			result->data = NULL; +			result->status = SCI_STATUS_NOMALLOC; +			free(buffer); +			return SCI_ERROR_DECOMPRESSION_OVERFLOW; +		} +		memcpy(result->data, buffer, compressedLength); +		result->status = SCI_STATUS_ALLOCATED; +		break; + +	case 18: +	case 19: +	case 20: +		if (decrypt4(result->data, buffer, result->size, compressedLength)) { +			free(result->data); +			result->data = 0; /* So that we know that it didn't work */ +			result->status = SCI_STATUS_NOMALLOC; +			free(buffer); +			return SCI_ERROR_DECOMPRESSION_OVERFLOW; +		} +		result->status = SCI_STATUS_ALLOCATED; +		break; + +	case 3: +	case 4: /* NYI */ +		fprintf(stderr,"Resource %d.%s: Warning: compression type #%d not yet implemented\n", +			result->number, sci_resource_type_suffixes[result->type], compressionMethod); +		free(result->data); +		result->data = NULL; +		result->status = SCI_STATUS_NOMALLOC; +		break; + +	default: +		fprintf(stderr,"Resource %d.%s: Compression method SCI1/%hi not " +			"supported!\n", result->number, sci_resource_type_suffixes[result->type], +			compressionMethod); +		free(result->data); +		result->data = NULL; /* So that we know that it didn't work */ +		result->status = SCI_STATUS_NOMALLOC; +		free(buffer); +		return SCI_ERROR_UNKNOWN_COMPRESSION; +	} + +  free(buffer); +  return 0; +} + diff --git a/engines/sci/scicore/exe.c b/engines/sci/scicore/exe.c new file mode 100644 index 0000000000..d2ff72995a --- /dev/null +++ b/engines/sci/scicore/exe.c @@ -0,0 +1,86 @@ +/*************************************************************************** + exe.c Copyright (C) 2005 Walter van Niftrik + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + +    Walter van Niftrik <w.f.b.w.v.niftrik@stud.tue.nl> + +***************************************************************************/ + +#include <sci_memory.h> + +#include "exe.h" +#include "exe_dec.h" + +extern exe_decompressor_t exe_decompressor_lzexe; +extern exe_decompressor_t exe_decompressor_raw; + +exe_decompressor_t *exe_decompressors[] = { +	&exe_decompressor_lzexe, +	&exe_decompressor_raw, +	NULL +}; + +struct _exe_file +{ +	struct _exe_decompressor *decompressor; +	struct _exe_handle *handle; +}; + +exe_file_t * +exe_open(const char *filename) +{ +	int i = 0; +	exe_decompressor_t *dec; + +	while ((dec = exe_decompressors[i])) { +		exe_handle_t *handle = dec->open(filename); + +		if (handle) { +			exe_file_t *file = (exe_file_t*)sci_malloc(sizeof(exe_file_t)); + +			sciprintf("Scanning '%s' with decompressor '%s'\n", +				  filename, dec->name); + +			file->handle = handle; +			file->decompressor = dec; +			return file; +		} + +		i++; +	} + +	return NULL; +} + +int +exe_read(exe_file_t *file, void *buf, int count) +{ +	return file->decompressor->read(file->handle, buf, count); +} + +void +exe_close(exe_file_t *file) +{ +	file->decompressor->close(file->handle); + +	sci_free(file); +} diff --git a/engines/sci/scicore/exe.h b/engines/sci/scicore/exe.h new file mode 100644 index 0000000000..ebc74ae3bc --- /dev/null +++ b/engines/sci/scicore/exe.h @@ -0,0 +1,60 @@ +/*************************************************************************** + exe.h Copyright (C) 2005 Walter van Niftrik + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + +    Walter van Niftrik <w.f.b.w.v.niftrik@stud.tue.nl> + +***************************************************************************/ + +#ifndef _SCI_EXE_H_ +#define _SCI_EXE_H_ + +typedef struct _exe_file exe_file_t; + +exe_file_t * +exe_open(const char *filename); +/* Opens an executable file +** Parameters: (const char *) filename: Filename of executable to open +** Returns   : (exe_file_t *) File handle, or NULL on error +** This function will try to find a decompressor that can handle this type +** of executable +*/ + +int +exe_read(exe_file_t *file, void *buf, int count); +/* Reads from an executable file +** Parameters: (exe_file_t *) file: File handle +**             (void *) buf: Buffer to store decompressed data +**             (int) count: Size of decompressed data requested, in bytes +** Returns   : (int) Number of bytes of decompressed data that was stored in +**                   buf. If this value is less than count an error has +**                   occured, or end-of-file was reached. +*/ + +void +exe_close(exe_file_t *handle); +/* Closes an executable file +** Parameters: (exe_file_t *) file: File handle +** Returns   : (void) +*/ + +#endif /* !_SCI_EXE_H_ */ diff --git a/engines/sci/scicore/exe_dec.h b/engines/sci/scicore/exe_dec.h new file mode 100644 index 0000000000..9b30da0772 --- /dev/null +++ b/engines/sci/scicore/exe_dec.h @@ -0,0 +1,66 @@ +/*************************************************************************** + exe_dec.h Copyright (C) 2005 Walter van Niftrik + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + +    Walter van Niftrik <w.f.b.w.v.niftrik@stud.tue.nl> + +***************************************************************************/ + +#ifndef _SCI_EXE_DEC_H_ +#define _SCI_EXE_DEC_H_ + +typedef struct _exe_handle exe_handle_t; + +typedef struct _exe_decompressor { +	const char *name; /* Decompressor name. Unique identifier, should consist +	            ** of lower-case (where applicable) alphanumerics +	            */ + +	exe_handle_t * (*open) (const char *filename); +	/* Opens an executable file +	** Parameters: (const char *) filename: Filename of executable to open. +	** Returns   : (exe_handle_t *) Decompressor file handle, or NULL on +	**                              error. +	** This function will verify that the file can be handled by the +	** decompressor. If this is not the case the function will fail. +	*/ + +	int (*read) (exe_handle_t *handle, void *buf, int count); +	/* Reads from executable file +	** Parameters: (exe_handle_t *) handle: Decompressor file handle. +	**             (void *) buf: Buffer to store decompressed data. +	**             (int) count: Size of decompressed data requested, in +	**                          bytes. +	** Returns   : (int) Number of bytes of decompressed data that was +	**                   stored in buf. If this value is less than count +	**                   an error has occured, or end-of-file was +	**                   reached. +	*/ + +	void (*close) (exe_handle_t *handle); +	/* Closes a decompressor file handle. +	** Parameters: (exe_handle_t *) handle: Decompressor file handle. +	** Returns   : (void) +	*/ +} exe_decompressor_t; + +#endif /* !_SCI_EXE_DEC_H_ */ diff --git a/engines/sci/scicore/exe_lzexe.c b/engines/sci/scicore/exe_lzexe.c new file mode 100644 index 0000000000..2331c01fb4 --- /dev/null +++ b/engines/sci/scicore/exe_lzexe.c @@ -0,0 +1,342 @@ +/*************************************************************************** + exe_lzexe.c Copyright (C) 2005 Walter van Niftrik + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + +    Walter van Niftrik <w.f.b.w.v.niftrik@stud.tue.nl> + +***************************************************************************/ + +/* Based on public domain code by Mitugu Kurizono. */ + +#include <stdio.h> +#include <sci_memory.h> +#include "exe_dec.h" + +/* Macro to interpret two sequential bytes as an unsigned integer. */ +#define UINT16(A) ((*((A) + 1) << 8) + *(A)) + +/* The amount of most recent data (in bytes) that we need to keep in the +** buffer. lzexe compression is based on copying chunks of previous data to +** form new data. +*/ +#define LZEXE_WINDOW 8192 + +/* Buffer size. */ +#define LZEXE_BUFFER_SIZE (LZEXE_WINDOW + 4096) + +/* Maximum amount of data (in bytes) that can be in the buffer at the start +** of the decompression loop. The maximum amount of data that can be added +** to the buffer during a single step of the loop is 256 bytes. +*/ +#define LZEXE_BUFFER_MAX (LZEXE_BUFFER_SIZE - 256) + +struct _exe_handle +{ +	FILE *f; + +	/* Output buffer. */ +	guint8 buffer[LZEXE_BUFFER_SIZE]; +	guint8 *bufptr; + +	/* Bit buffer. Bits [0..count) still contain unprocessed data. */ +	int buf; +	int count; + +	/* End of data flag. */ +	int eod; +}; + +static int +lzexe_read_uint16(FILE *f, int *value) +{ +	int data; + +	if ((*value = fgetc(f)) == EOF) +		return 0; + +	if ((data = fgetc(f)) == EOF) +		return 0; + +	*value |= data << 8; +	return 1; +} + +static int +lzexe_read_uint8(FILE *f, int *value) +{ +	if ((*value = fgetc(f)) == EOF) +		return 0; + +	return 1; +} + +static int +lzexe_init(exe_handle_t *handle, FILE *f) +{ +	handle->f = f; +	handle->bufptr = handle->buffer; +	handle->eod = 0; + +	if (!lzexe_read_uint16(handle->f, &handle->buf)) +		return 0; + +	handle->count = 16; +	return 1; +} + +static int +lzexe_get_bit(exe_handle_t *handle, int *bit) +{ +	*bit = handle->buf & 1; + +	if (--handle->count == 0) +	{ +		if (!lzexe_read_uint16(handle->f, &handle->buf)) +			return 0; +		handle->count = 16; +	} +	else +		handle->buf >>= 1; + +	return 1; +} + +static int +lzexe_decompress(exe_handle_t *handle) +{ +	while (!handle->eod +	       && handle->bufptr - handle->buffer <= LZEXE_BUFFER_MAX) { +		int bit; +		int len, span; + +		if (!lzexe_get_bit(handle, &bit)) +			return 0; + +		if (bit) { +			/* 1: copy byte verbatim. */ + +			int data; + +			if (!lzexe_read_uint8(handle->f, &data)) +				return 0; + +			*handle->bufptr++ = data; + +			continue; +		} + +		if (!lzexe_get_bit(handle, &bit)) +			return 0; + +		if (!bit) { +			/* 00: copy small block. */ + +			/* Next two bits indicate block length - 2. */ +			if (!lzexe_get_bit(handle, &bit)) +				return 0; + +			len = bit << 1; + +			if (!lzexe_get_bit(handle, &bit)) +				return 0; + +			len |= bit; +			len += 2; + +			/* Read span byte. This forms the low byte of a +			** negative two's compliment value. +			*/ +			if (!lzexe_read_uint8(handle->f, &span)) +				return 0; + +			/* Convert to negative integer. */ +			span -= 256; +		} else { +			/* 01: copy large block. */ +			int data; + +			/* Read low byte of span value. */ +			if (!lzexe_read_uint8(handle->f, &span)) +				return 0; + +			/* Read next byte. Bits [7..3] contain bits [12..8] +			** of span value. Bits [2..0] contain block length - +			** 2. +			*/ +			if (!lzexe_read_uint8(handle->f, &data)) +				return 0; +			span |= (data & 0xf8) << 5; +			/* Convert to negative integer. */ +			span -= 8192; + +			len = (data & 7) + 2; + +			if (len == 2) { +				/* Next byte is block length value - 1. */ +				if (!lzexe_read_uint8(handle->f, &len)) +					return 0; + +				if (len == 0) { +					/* End of data reached. */ +					handle->eod = 1; +					break; +				} + +				if (len == 1) +					/* Segment change marker. */ +					continue; + +				len++; +			} +		} + +		assert(handle->bufptr + span >= handle->buffer); + +		/* Copy block. */ +		while (len-- > 0) { +			*handle->bufptr = *(handle->bufptr + span); +			handle->bufptr++; +		} +	} + +	return 1; +} + +static exe_handle_t * +lzexe_open(const char *filename) +{ +	exe_handle_t *handle; +	guint8 head[0x20]; +	guint8 size[2]; +	off_t fpos; + +	FILE *f = sci_fopen(filename, "rb"); + +	if (!f) +		return NULL; + +	/* Read exe header plus possible lzexe signature. */ +	if (fread(head, 1, 0x20, f) != 0x20) +		return NULL; + +	/* Verify "MZ" signature, header size == 2 paragraphs and number of +	** overlays == 0. +	*/ +	if (UINT16(head) != 0x5a4d || UINT16(head + 8) != 2 +	    || UINT16(head + 0x1a) != 0) +		return NULL; + +	/* Verify that first relocation item offset is 0x1c. */ +	if (UINT16(head + 0x18) != 0x1c) +		return NULL; + +	/* Look for lzexe signature. */ +	if (memcmp(head + 0x1c, "LZ09", 4) +            && memcmp(head + 0x1c, "LZ91", 4)) { +		return NULL; +	} + +	/* Calculate code segment offset in exe file. */ +	fpos = (UINT16(head + 0x16) + UINT16(head + 8)) << 4; +	/* Seek to offset 8 of info table at start of code segment. */ +	if (fseek(f, fpos + 8, SEEK_SET) == -1) +		return NULL; + +	/* Read size of compressed data in paragraphs. */ +	if (fread(size, 1, 2, f) != 2) +		return NULL; + +	/* Move file pointer to start of compressed data. */ +	fpos -= UINT16(size) << 4; +	if (fseek(f, fpos, SEEK_SET) == -1) +		return NULL; + +	handle = (exe_handle_t*)sci_malloc(sizeof(exe_handle_t)); + +	if (!lzexe_init(handle, f)) { +		sci_free(handle); +		return NULL; +	} + +	return handle; +} + +static int +lzexe_read(exe_handle_t *handle, void *buf, int count) +{ +	int done = 0; + +	while (done != count) { +		int size, copy, i; +		int left = count - done; + +		if (!lzexe_decompress(handle)) +			return done; + +		/* Total amount of bytes in buffer. */ +		size = handle->bufptr - handle->buffer; + +		/* If we're not at end of data we need to maintain the +		** window. +		*/ +		if (!handle->eod) +			copy = size - LZEXE_WINDOW; +		else { +			if (size == 0) +				/* No data left. */ +				return done; + +			copy = size; +		} + +		/* Do not copy more than requested. */ +		if (copy > left) +			copy = left; + +		memcpy((char *) buf + done, handle->buffer, copy); + +		/* Move remaining data to start of buffer. */ +		for (i = copy; i < size; i++) +			handle->buffer[i - copy] = handle->buffer[i]; + +		handle->bufptr -= copy; +		done += copy; +	} + +	return done; +} + +static void +lzexe_close(exe_handle_t *handle) +{ +	fclose(handle->f); + +	sci_free(handle); +} + +exe_decompressor_t +exe_decompressor_lzexe = { +	"lzexe", +	lzexe_open, +	lzexe_read, +	lzexe_close +}; diff --git a/engines/sci/scicore/exe_raw.c b/engines/sci/scicore/exe_raw.c new file mode 100644 index 0000000000..ecc7089b2a --- /dev/null +++ b/engines/sci/scicore/exe_raw.c @@ -0,0 +1,73 @@ +/*************************************************************************** + exe_raw.c Copyright (C) 2005 Walter van Niftrik + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + +    Walter van Niftrik <w.f.b.w.v.niftrik@stud.tue.nl> + +***************************************************************************/ + +#include <stdio.h> +#include <sci_memory.h> + +struct _exe_handle +{ +	FILE *f; +}; + +#include "exe_dec.h" + +static exe_handle_t * +raw_open(const char *filename) +{ +	FILE *f = sci_fopen(filename, "rb"); +	exe_handle_t *handle; + +	if (!f) +		return NULL; + +	handle = (exe_handle_t*)sci_malloc(sizeof(exe_handle_t)); +	handle->f = f; + +	return handle; +} + +static int +raw_read(exe_handle_t *handle, void *buf, int count) +{ +	return fread(buf, 1, count, handle->f); +} + +static void +raw_close(exe_handle_t *handle) +{ +	fclose(handle->f); + +	sci_free(handle); +} + +exe_decompressor_t +exe_decompressor_raw = { +	"raw", +	raw_open, +	raw_read, +	raw_close +}; diff --git a/engines/sci/scicore/fnmatch.c b/engines/sci/scicore/fnmatch.c new file mode 100644 index 0000000000..ffc9a1b451 --- /dev/null +++ b/engines/sci/scicore/fnmatch.c @@ -0,0 +1,849 @@ +/* fnmatch.c -- ksh-like extended pattern matching for the shell and filename +		globbing. */ + +/* Copyright (C) 1991, 1997 Free Software Foundation, Inc. + +   This file is part of GNU Bash, the Bourne Again SHell. +    +   Bash is free software; you can redistribute it and/or modify it under +   the terms of the GNU General Public License as published by the Free +   Software Foundation; either version 2, or (at your option) any later +   version. +	       +   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software +   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif + +#ifndef HAVE_FNMATCH + +#include <stdio.h>	/* for debugging */ +				 +#include <beos/fnmatch.h> +#include <beos/collsyms.h> +#include <ctype.h> + +#if defined (HAVE_STRING_H) +#  include <string.h> +#else +#  include <strings.h> +#endif /* HAVE_STRING_H */ + +static int gmatch (); +static char *brackmatch (); +#ifdef EXTENDED_GLOB +static int extmatch (); +static char *patscan (); +#endif +   +#if !defined (isascii) +#  define isascii(c)	((unsigned int)(c) <= 0177) +#endif + +/* Note that these evaluate C many times.  */ + +#ifndef isblank +#  define isblank(c)	((c) == ' ' || (c) == '\t') +#endif + +#ifndef isgraph +#  define isgraph(c)	((c) != ' ' && isprint((c))) +#endif + +#ifndef isxdigit +#  define isxdigit(c)	(((c) >= '0' && (c) <= '9') || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F')) +#endif + +/* The result of FOLD is an `unsigned char' */ +# define FOLD(c) ((flags & FNM_CASEFOLD) && isupper ((unsigned char)c) \ +	? tolower ((unsigned char)c) \ +	: ((unsigned char)c)) + +#ifndef STREQ +#define STREQ(a, b) ((a)[0] == (b)[0] && strcmp(a, b) == 0) +#define STREQN(a, b, n) ((a)[0] == (b)[0] && strncmp(a, b, n) == 0) +#endif + +/* We use strcoll(3) for range comparisons in bracket expressions, +   even though it can have unwanted side effects in locales +   other than POSIX or US.  For instance, in the de locale, [A-Z] matches +   all characters. */ + +#if defined (HAVE_STRCOLL) +/* Helper function for collating symbol equivalence. */ +static int rangecmp (c1, c2) +     int c1, c2; +{ +  static char s1[2] = { ' ', '\0' }; +  static char s2[2] = { ' ', '\0' }; +  int ret; + +  /* Eight bits only.  Period. */ +  c1 &= 0xFF; +  c2 &= 0xFF; + +  if (c1 == c2) +    return (0); + +  s1[0] = c1; +  s2[0] = c2; + +  if ((ret = strcoll (s1, s2)) != 0) +    return ret; +  return (c1 - c2); +} +#else /* !HAVE_STRCOLL */ +#  define rangecmp(c1, c2)	((int)(c1) - (int)(c2)) +#endif /* !HAVE_STRCOLL */ + +#if defined (HAVE_STRCOLL) +static int collequiv (c1, c2) +     int c1, c2; +{ +  return (rangecmp (c1, c2) == 0); +} +#else +#  define collequiv(c1, c2)	((c1) == (c2)) +#endif + +static int +collsym (s, len) +     char *s; +     int len; +{ +  register struct _collsym *csp; + +  for (csp = posix_collsyms; csp->name; csp++) +    { +      if (STREQN(csp->name, s, len) && csp->name[len] == '\0') +	return (csp->code); +    } +  if (len == 1) +    return s[0]; +  return -1; +} + +int +fnmatch (pattern, string, flags) +     char *pattern; +     char *string; +     int flags; +{ +  char *se, *pe; + +  if (string == 0 || pattern == 0) +    return FNM_NOMATCH; + +  se = string + strlen (string); +  pe = pattern + strlen (pattern); + +  return (gmatch (string, se, pattern, pe, flags)); +} + +/* Match STRING against the filename pattern PATTERN, returning zero if +   it matches, FNM_NOMATCH if not.  */ +static int +gmatch (string, se, pattern, pe, flags) +     char *string, *se; +     char *pattern, *pe; +     int flags; +{ +  register char *p, *n;		/* pattern, string */ +  register char c;		/* current pattern character */ +  register char sc;		/* current string character */ + +  p = pattern; +  n = string; + +  if (string == 0 || pattern == 0) +    return FNM_NOMATCH; + +#if DEBUG_MATCHING +fprintf(stderr, "gmatch: string = %s; se = %s\n", string, se); +fprintf(stderr, "gmatch: pattern = %s; pe = %s\n", pattern, pe); +#endif + +  while (p < pe) +    { +      c = *p++; +      c = FOLD (c); + +      sc = n < se ? *n : '\0'; + +#ifdef EXTENDED_GLOB +      /* extmatch () will handle recursively calling gmatch, so we can +	 just return what extmatch() returns. */ +      if ((flags & FNM_EXTMATCH) && *p == '(' && +	  (c == '+' || c == '*' || c == '?' || c == '@' || c == '!')) /* ) */ +	{ +	  int lflags; +	  /* If we're not matching the start of the string, we're not +	     concerned about the special cases for matching `.' */ +	  lflags = (n == string) ? flags : (flags & ~FNM_PERIOD); +	  return (extmatch (c, n, se, p, pe, lflags)); +	} +#endif + +      switch (c) +	{ +	case '?':		/* Match single character */ +	  if (sc == '\0') +	    return FNM_NOMATCH; +	  else if ((flags & FNM_PATHNAME) && sc == '/') +	    /* If we are matching a pathname, `?' can never match a `/'. */ +	    return FNM_NOMATCH; +	  else if ((flags & FNM_PERIOD) && sc == '.' && +		   (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/'))) +	    /* `?' cannot match a `.' if it is the first character of the +	       string or if it is the first character following a slash and +	       we are matching a pathname. */ +	    return FNM_NOMATCH; +	  break; + +	case '\\':		/* backslash escape removes special meaning */ +	  if (p == pe) +	    return FNM_NOMATCH; + +	  if ((flags & FNM_NOESCAPE) == 0) +	    { +	      c = *p++; +	      /* A trailing `\' cannot match. */ +	      if (p > pe) +		return FNM_NOMATCH; +	      c = FOLD (c); +	    } +	  if (FOLD (sc) != (unsigned char)c) +	    return FNM_NOMATCH; +	  break; + +	case '*':		/* Match zero or more characters */ +	  if (p == pe) +	    return 0; +	   +	  if ((flags & FNM_PERIOD) && sc == '.' && +	      (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/'))) +	    /* `*' cannot match a `.' if it is the first character of the +	       string or if it is the first character following a slash and +	       we are matching a pathname. */ +	    return FNM_NOMATCH; + +	  /* Collapse multiple consecutive, `*' and `?', but make sure that +	     one character of the string is consumed for each `?'. */ +	  for (c = *p++; (c == '?' || c == '*'); c = *p++) +	    { +	      if ((flags & FNM_PATHNAME) && sc == '/') +		/* A slash does not match a wildcard under FNM_PATHNAME. */ +		return FNM_NOMATCH; +	      else if (c == '?') +		{ +		  if (sc == '\0') +		    return FNM_NOMATCH; +		  /* One character of the string is consumed in matching +		     this ? wildcard, so *??? won't match if there are +		     fewer than three characters. */ +		  n++; +		  sc = n < se ? *n : '\0'; +		} + +#ifdef EXTENDED_GLOB +	      /* Handle ******(patlist) */ +	      if ((flags & FNM_EXTMATCH) && c == '*' && *p == '(')  /*)*/ +		{ +		  char *newn; +		  /* We need to check whether or not the extended glob +		     pattern matches the remainder of the string. +		     If it does, we match the entire pattern. */ +		  for (newn = n; newn < se; ++newn) +		    { +		      if (extmatch (c, newn, se, p, pe, flags) == 0) +			return (0); +		    } +		  /* We didn't match the extended glob pattern, but +		     that's OK, since we can match 0 or more occurrences. +		     We need to skip the glob pattern and see if we +		     match the rest of the string. */ +		  newn = patscan (p + 1, pe, 0); +		  p = newn; +		} +#endif +	      if (p == pe) +		break; +	    } + +	  /* If we've hit the end of the pattern and the last character of +	     the pattern was handled by the loop above, we've succeeded. +	     Otherwise, we need to match that last character. */ +	  if (p == pe && (c == '?' || c == '*')) +	    return (0); + +	  /* General case, use recursion. */ +	  { +	    unsigned char c1; + +	    c1 = (unsigned char)((flags & FNM_NOESCAPE) == 0 && c == '\\') ? *p : c; +	    c1 = FOLD (c1); +	    for (--p; n < se; ++n) +	      { +		/* Only call fnmatch if the first character indicates a +		   possible match.  We can check the first character if +		   we're not doing an extended glob match. */ +		if ((flags & FNM_EXTMATCH) == 0 && c != '[' && FOLD (*n) != c1) /*]*/ +		  continue; + +		/* If we're doing an extended glob match and the pattern is not +		   one of the extended glob patterns, we can check the first +		   character. */ +		if ((flags & FNM_EXTMATCH) && p[1] != '(' && /*)*/ +		    strchr ("?*+@!", *p) == 0 && c != '[' && FOLD (*n) != c1) /*]*/ +		  continue; + +		/* Otherwise, we just recurse. */ +		if (gmatch (n, se, p, pe, flags & ~FNM_PERIOD) == 0) +		  return (0); +	      } +	    return FNM_NOMATCH; +	  } + +	case '[': +	  { +	    if (sc == '\0' || n == se) +	      return FNM_NOMATCH; + +	    /* A character class cannot match a `.' if it is the first +	       character of the string or if it is the first character +	       following a slash and we are matching a pathname. */ +	    if ((flags & FNM_PERIOD) && sc == '.' && +		(n == string || ((flags & FNM_PATHNAME) && n[-1] == '/'))) +	      return (FNM_NOMATCH); + +	    p = brackmatch (p, sc, flags); +	    if (p == 0) +	      return FNM_NOMATCH; +	  } +	  break; + +	default: +	  if ((unsigned char)c != FOLD (sc)) +	    return (FNM_NOMATCH); +	} + +      ++n; +    } + +  if (n == se) +    return (0); + +  if ((flags & FNM_LEADING_DIR) && *n == '/') +    /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz".  */ +    return 0; +	   +  return (FNM_NOMATCH); +} + +/* Parse a bracket expression collating symbol ([.sym.]) starting at P, find +   the value of the symbol, and move P past the collating symbol expression. +   The value is returned in *VP, if VP is not null. */ +static char * +parse_collsym (p, vp) +     char *p; +     int *vp; +{ +  register int pc; +  int val; + +  p++;				/* move past the `.' */ +	   +  for (pc = 0; p[pc]; pc++) +    if (p[pc] == '.' && p[pc+1] == ']') +      break; +   val = collsym (p, pc); +   if (vp) +     *vp = val; +   return (p + pc + 2); +} + +static char * +brackmatch (p, test, flags) +     char *p; +     unsigned char test; +     int flags; +{ +  register char cstart, cend, c; +  register int not;    /* Nonzero if the sense of the character class is inverted.  */ +  int pc, brcnt; +  char *savep; + +  test = FOLD (test); + +  savep = p; + +  /* POSIX.2 3.13.1 says that an exclamation mark (`!') shall replace the +     circumflex (`^') in its role in a `nonmatching list'.  A bracket +     expression starting with an unquoted circumflex character produces +     unspecified results.  This implementation treats the two identically. */ +  if (not = (*p == '!' || *p == '^')) +    ++p; + +  c = *p++; +  for (;;) +    { +      /* Initialize cstart and cend in case `-' is the last +	 character of the pattern. */ +      cstart = cend = c; + +      /* POSIX.2 equivalence class:  [=c=].  See POSIX.2 2.8.3.2.  Find +	 the end of the equivalence class, move the pattern pointer past +	 it, and check for equivalence.  XXX - this handles only +	 single-character equivalence classes, which is wrong, or at +	 least incomplete. */ +      if (c == '[' && *p == '=' && p[2] == '=' && p[3] == ']') +	{ +	  pc = FOLD (p[1]); +	  p += 4; +	  if (collequiv (test, pc)) +	    { +/*[*/	      /* Move past the closing `]', since the first thing we do at +		 the `matched:' label is back p up one. */ +	      p++; +	      goto matched; +	    } +	  else +	    { +	      c = *p++; +	      if (c == '\0') +		return ((test == '[') ? savep : (char *)0); /*]*/ +	      c = FOLD (c); +	      continue; +	    } +	} + +      /* POSIX.2 character class expression.  See POSIX.2 2.8.3.2. */ +      if (c == '[' && *p == ':')	/*]*/ +	{ +	  pc = 0;	/* make sure invalid char classes don't match. */ +	  if (STREQN (p+1, "alnum:]", 7)) +	    { pc = isalnum (test); p += 8; } +	  else if (STREQN (p+1, "alpha:]", 7)) +	    { pc = isalpha (test); p += 8; } +	  else if (STREQN (p+1, "blank:]", 7)) +	    { pc = isblank (test); p += 8; } +	  else if (STREQN (p+1, "cntrl:]", 7)) +	    { pc = iscntrl (test); p += 8; } +	  else if (STREQN (p+1, "digit:]", 7)) +	    { pc = isdigit (test); p += 8; } +	  else if (STREQN (p+1, "graph:]", 7)) +	    { pc = isgraph (test); p += 8; } +	  else if (STREQN (p+1, "lower:]", 7)) +	    { pc = islower (test); p += 8; } +	  else if (STREQN (p+1, "print:]", 7)) +	    { pc = isprint (test); p += 8; } +	  else if (STREQN (p+1, "punct:]", 7)) +	    { pc = ispunct (test); p += 8; } +	  else if (STREQN (p+1, "space:]", 7)) +	    { pc = isspace (test); p += 8; } +	  else if (STREQN (p+1, "upper:]", 7)) +	    { pc = isupper (test); p += 8; } +	  else if (STREQN (p+1, "xdigit:]", 8)) +	    { pc = isxdigit (test); p += 9; } +	  else if (STREQN (p+1, "ascii:]", 7)) +	    { pc = isascii (test); p += 8; } +	  if (pc) +	    { +/*[*/	      /* Move past the closing `]', since the first thing we do at +		 the `matched:' label is back p up one. */ +	      p++; +	      goto matched; +	    } +	  else +	    { +	      /* continue the loop here, since this expression can't be +		 the first part of a range expression. */ +	      c = *p++; +	      if (c == '\0') +		return ((test == '[') ? savep : (char *)0); +	      else if (c == ']') +		break; +	      c = FOLD (c); +	      continue; +	    } +	} +  +      /* POSIX.2 collating symbols.  See POSIX.2 2.8.3.2.  Find the end of +	 the symbol name, make sure it is terminated by `.]', translate +	 the name to a character using the external table, and do the +	 comparison. */ +      if (c == '[' && *p == '.') +	{ +	  p = parse_collsym (p, &pc); +	  /* An invalid collating symbol cannot be the first point of a +	     range.  If it is, we set cstart to one greater than `test', +	     so any comparisons later will fail. */ +	  cstart = (pc == -1) ? test + 1 : pc; +	} + +      if (!(flags & FNM_NOESCAPE) && c == '\\') +	{ +	  if (*p == '\0') +	    return (char *)0; +	  cstart = cend = *p++; +	} + +      cstart = cend = FOLD (cstart); + +      /* POSIX.2 2.8.3.1.2 says: `An expression containing a `[' that +	 is not preceded by a backslash and is not part of a bracket +	 expression produces undefined results.'  This implementation +	 treats the `[' as just a character to be matched if there is +	 not a closing `]'. */ +      if (c == '\0') +	return ((test == '[') ? savep : (char *)0); + +      c = *p++; +      c = FOLD (c); + +      if ((flags & FNM_PATHNAME) && c == '/') +	/* [/] can never match when matching a pathname.  */ +	return (char *)0; + +      /* This introduces a range, unless the `-' is the last +	 character of the class.  Find the end of the range +	 and move past it. */ +      if (c == '-' && *p != ']') +	{ +	  cend = *p++; +	  if (!(flags & FNM_NOESCAPE) && cend == '\\') +	    cend = *p++; +	  if (cend == '\0') +	    return (char *)0; +	  if (cend == '[' && *p == '.') +	    { +	      p = parse_collsym (p, &pc); +	      /* An invalid collating symbol cannot be the second part of a +		 range expression.  If we get one, we set cend to one fewer +		 than the test character to make sure the range test fails. */ +	      cend = (pc == -1) ? test - 1 : pc; +	    } +	  cend = FOLD (cend); + +	  c = *p++; + +	  /* POSIX.2 2.8.3.2:  ``The ending range point shall collate +	     equal to or higher than the starting range point; otherwise +	     the expression shall be treated as invalid.''  Note that this +	     applies to only the range expression; the rest of the bracket +	     expression is still checked for matches. */ +	  if (rangecmp (cstart, cend) > 0) +	    { +	      if (c == ']') +		break; +	      c = FOLD (c); +	      continue; +	    } +	} + +      if (rangecmp (test, cstart) >= 0 && rangecmp (test, cend) <= 0) +	goto matched; + +      if (c == ']') +	break; +    } +  /* No match. */ +  return (!not ? (char *)0 : p); + +matched: +  /* Skip the rest of the [...] that already matched.  */ +#if 0 +  brcnt = (c != ']') + (c == '[' && (*p == '=' || *p == ':' || *p == '.')); +#else +  c = *--p; +  brcnt = 1; +#endif +  while (brcnt > 0) +    { +      /* A `[' without a matching `]' is just another character to match. */ +      if (c == '\0') +	return ((test == '[') ? savep : (char *)0); + +      c = *p++; +      if (c == '[' && (*p == '=' || *p == ':' || *p == '.')) +	brcnt++; +      else if (c == ']') +	brcnt--; +      else if (!(flags & FNM_NOESCAPE) && c == '\\') +	{ +	  if (*p == '\0') +	    return (char *)0; +	  /* XXX 1003.2d11 is unclear if this is right. */ +	  ++p; +	} +    } +  return (not ? (char *)0 : p); +} + +#if defined (EXTENDED_GLOB) +/* ksh-like extended pattern matching: + +	[?*+@!](pat-list) + +   where pat-list is a list of one or patterns separated by `|'.  Operation +   is as follows: + +	?(patlist)	match zero or one of the given patterns +	*(patlist)	match zero or more of the given patterns +	+(patlist)	match one or more of the given patterns +	@(patlist)	match exactly one of the given patterns +	!(patlist)	match anything except one of the given patterns +*/ + +/* Scan a pattern starting at STRING and ending at END, keeping track of +   embedded () and [].  If DELIM is 0, we scan until a matching `)' +   because we're scanning a `patlist'.  Otherwise, we scan until we see +   DELIM.  In all cases, we never scan past END.  The return value is the +   first character after the matching DELIM. */ +static char * +patscan (string, end, delim) +     char *string, *end; +     int delim; +{ +  int pnest, bnest, cchar; +  char *s, c, *bfirst; + +  pnest = bnest = cchar = 0; +  bfirst = 0; +  for (s = string; c = *s; s++) +    { +      if (s >= end) +	return (s); +      switch (c) +	{ +	case '\0': +	  return ((char *)0); + +	/* `[' is not special inside a bracket expression, but it may +	   introduce one of the special POSIX bracket expressions +	   ([.SYM.], [=c=], [: ... :]) that needs special handling. */ +	case '[': +	  if (bnest == 0) +	    { +	      bfirst = s + 1; +	      if (*bfirst == '!' || *bfirst == '^') +		bfirst++; +	      bnest++; +	    } +	  else if (s[1] == ':' || s[1] == '.' || s[1] == '=') +	    cchar = s[1]; +	  break; + +	/* `]' is not special if it's the first char (after a leading `!' +	   or `^') in a bracket expression or if it's part of one of the +	   special POSIX bracket expressions ([.SYM.], [=c=], [: ... :]) */ +	case ']': +	  if (bnest) +	    { +	      if (cchar && s[-1] == cchar) +		cchar = 0; +	      else if (s != bfirst) +		{ +		  bnest--; +		  bfirst = 0; +		} +	    } +	  break; + +	case '(': +	  if (bnest == 0) +	    pnest++; +	  break; + +	case ')': +#if 0 +	  if (bnest == 0) +	    pnest--; +	  if (pnest <= 0) +	    return ++s; +#else +	  if (bnest == 0 && pnest-- <= 0) +	    return ++s; +#endif +	  break; + +	case '|': +	  if (bnest == 0 && pnest == 0 && delim == '|') +	    return ++s; +	  break; +	} +    } + +  return (char *)0; +} + +/* Return 0 if dequoted pattern matches S in the current locale. */ +static int +strcompare (p, pe, s, se) +     char *p, *pe, *s, *se; +{ +  int ret; +  char c1, c2; + +  c1 = *pe; +  c2 = *se; + +  *pe = *se = '\0'; +#if defined (HAVE_STRCOLL) +  ret = strcoll (p, s); +#else +  ret = strcmp (p, s); +#endif + +  *pe = c1; +  *se = c2; + +  return (ret == 0 ? ret : FNM_NOMATCH); +} + +/* Match a ksh extended pattern specifier.  Return FNM_NOMATCH on failure or +   0 on success.  This is handed the entire rest of the pattern and string +   the first time an extended pattern specifier is encountered, so it calls +   gmatch recursively. */ +static int +extmatch (xc, s, se, p, pe, flags) +     int xc;		/* select which operation */ +     char *s, *se; +     char *p, *pe; +     int flags; +{ +  char *prest;			/* pointer to rest of pattern */ +  char *psub;			/* pointer to sub-pattern */ +  char *pnext;			/* pointer to next sub-pattern */ +  char *srest;			/* pointer to rest of string */ +  int m1, m2; + +#if DEBUG_MATCHING +fprintf(stderr, "extmatch: xc = %c\n", xc); +fprintf(stderr, "extmatch: s = %s; se = %s\n", s, se); +fprintf(stderr, "extmatch: p = %s; pe = %s\n", p, pe); +#endif + +  prest = patscan (p + (*p == '('), pe, 0); /* ) */ +  if (prest == 0) +    /* If PREST is 0, we failed to scan a valid pattern.  In this +       case, we just want to compare the two as strings. */ +    return (strcompare (p - 1, pe, s, se)); + +  switch (xc) +    { +    case '+':			/* match one or more occurrences */ +    case '*':			/* match zero or more occurrences */ +      /* If we can get away with no matches, don't even bother.  Just +	 call gmatch on the rest of the pattern and return success if +	 it succeeds. */ +      if (xc == '*' && (gmatch (s, se, prest, pe, flags) == 0)) +	return 0; + +      /* OK, we have to do this the hard way.  First, we make sure one of +	 the subpatterns matches, then we try to match the rest of the +	 string. */ +      for (psub = p + 1; ; psub = pnext) +	{ +	  pnext = patscan (psub, pe, '|'); +	  for (srest = s; srest <= se; srest++) +	    { +	      /* Match this substring (S -> SREST) against this +		 subpattern (psub -> pnext - 1) */ +	      m1 = gmatch (s, srest, psub, pnext - 1, flags) == 0; +	      /* OK, we matched a subpattern, so make sure the rest of the +		 string matches the rest of the pattern.  Also handle +		 multiple matches of the pattern. */ +	      if (m1) +		m2 = (gmatch (srest, se, prest, pe, flags) == 0) || +		      (s != srest && gmatch (srest, se, p - 1, pe, flags) == 0); +	      if (m1 && m2) +		return (0); +	    } +	  if (pnext == prest) +	    break; +	} +      return (FNM_NOMATCH); + +    case '?':		/* match zero or one of the patterns */ +    case '@':		/* match exactly one of the patterns */ +      /* If we can get away with no matches, don't even bother.  Just +	 call gmatch on the rest of the pattern and return success if +	 it succeeds. */ +      if (xc == '?' && (gmatch (s, se, prest, pe, flags) == 0)) +	return 0; + +      /* OK, we have to do this the hard way.  First, we see if one of +	 the subpatterns matches, then, if it does, we try to match the +	 rest of the string. */ +      for (psub = p + 1; ; psub = pnext) +	{ +	  pnext = patscan (psub, pe, '|'); +	  srest = (prest == pe) ? se : s; +	  for ( ; srest <= se; srest++) +	    { +	      if (gmatch (s, srest, psub, pnext - 1, flags) == 0 && +		  gmatch (srest, se, prest, pe, flags) == 0) +		return (0); +	    } +	  if (pnext == prest) +	    break; +	} +      return (FNM_NOMATCH); + +    case '!':		/* match anything *except* one of the patterns */ +      for (srest = s; srest <= se; srest++) +	{ +	  m1 = 0; +	  for (psub = p + 1; ; psub = pnext) +	    { +	      pnext = patscan (psub, pe, '|'); +	      /* If one of the patterns matches, just bail immediately. */ +	      if (m1 = (gmatch (s, srest, psub, pnext - 1, flags) == 0)) +		break; +	      if (pnext == prest) +		break; +	    } +	  if (m1 == 0 && gmatch (srest, se, prest, pe, flags) == 0) +	    return (0); +	} +      return (FNM_NOMATCH); +    } + +  return (FNM_NOMATCH); +} +#endif /* EXTENDED_GLOB */ + +#ifdef TEST +main (c, v) +     int c; +     char **v; +{ +  char *string, *pat; + +  string = v[1]; +  pat = v[2]; + +  if (fnmatch (pat, string, 0) == 0) +    { +      printf ("%s matches %s\n", string, pat); +      exit (0); +    } +  else +    { +      printf ("%s does not match %s\n", string, pat); +      exit (1); +    } +} +#endif + +#endif + diff --git a/engines/sci/scicore/games.h b/engines/sci/scicore/games.h new file mode 100644 index 0000000000..d8b9642fba --- /dev/null +++ b/engines/sci/scicore/games.h @@ -0,0 +1,125 @@ +/*************************************************************************** + games.h Copyright (C) 2002 Solomon Peachy + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + +    Solomon Peachy [pizza@shaftnet.org] + +***************************************************************************/ + +/* Game identification */ + +#ifndef _SCI_GAMES_H_ +#define _SCI_GAMES_H_ + +#ifndef NEED_SCI_VERSIONS +#  error "You shouldn't be including this header file." +#endif + +#include <versions.h> + +typedef struct _sci_game { +  int id; /* currently CRC of resource.001 */ +  int res_version; +  sci_version_t version; +  const char *name; +} sci_game_t; + +/* Interpreter versions for Amiga and Atari ST ports are tentative */ +sci_game_t sci_games[] = { +	{ 0x5D451535, SCI_VERSION_AUTODETECT, SCI_VERSION(1,000,510), "Leisure Suit Larry 1 v1.0-mac"}, /* x.yyy.zzz */ /* Launcher says v2.0, game crashes on DoAvoider */ +	{ 0x6C176EE0, SCI_VERSION_AUTODETECT, SCI_VERSION(1,000,577), "Leisure Suit Larry 1 v2.1"}, +	{ 0x1C36E076, SCI_VERSION_AUTODETECT, SCI_VERSION(1,000,510), "Leisure Suit Larry 1 v1.000-es"}, /* 1.SQ4.057 */ /* Crashes on function 0x7b */ + +	{ 0xFEAB629D, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,343), "Leisure Suit Larry 2 v1.000.011-3.5"}, +	{ 0x13DD3CD2, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,343), "Leisure Suit Larry 2 v1.000.011-5.25" }, +	{ 0x1D0F3B31, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,572), "Leisure Suit Larry 2 v1.001.006-st"}, /* 1.000.159 */ +	{ 0x40BEC726, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,409), "Leisure Suit Larry 2 v1.002.000-3.5"}, +	{ 0x0C848403, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,409), "Leisure Suit Larry 2 v1.002.000-5.25" }, +	{ 0x7E9CF339, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,572), "Leisure Suit Larry 2 v1.003-ami"}, /* x.yyy.zzz */ + +	{ 0x06D737B5, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,572), "Leisure Suit Larry 3 v1.003-3.5" }, +	{ 0xE0A1C352, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,572), "Leisure Suit Larry 3 v1.003-5.25" }, +	{ 0xC48FE83A, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,572), "Leisure Suit Larry 3 v1.021-3.5" }, +	{ 0x484587DD, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,572), "Leisure Suit Larry 3 v1.021-5.25"}, +/*	{ 0x????????, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,572), "Leisure Suit Larry 3 v1.021-st"},*/ /* 1.002.026 */ +	{ 0x6348030A, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,572), "Leisure Suit Larry 3 v1.039-ami"}, /* 1.002.032 */ + +	{ 0x94EA377B, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,685), "CB1" }, +	{ 0xFD9EE7BD, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,685), "Camelot" }, +	{ 0x2829987F, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,685), "Camelot" }, +	{ 0x980CEAD3, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,629), "Demo Quest" }, +	{ 0x3DB972CA, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,572), "Hoyle 2" }, +	{ 0xC0B37651, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,685), "Iceman" }, +	{ 0xDABA6B8A, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,999), "KQ1 v1.000.051-3.5" }, /* S.old.010 */ +	{ 0x270E37F3, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,274), "KQ4" }, +	{ 0x685F1205, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,502), "KQ4" }, +	{ 0xC14E3A2A, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,395), "PQ2" }, +	{ 0x4BD66036, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,490), "PQ2" }, +	{ 0x7132D6D8, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,629), "QfG1" }, +	{ 0xF8F4913F, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,685), "SQ3" }, +	{ 0x34FBC324, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,999), "SQ3/DE" }, /* S.old.114 */ +	{ 0xE4A3234D, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,506), "Fun Seekers Guide v1.02"}, +	{ 0x85AFE241, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,519), "Hoyle 1 v1.000.104"}, +	{ 0xE0E070C3, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,572), "Hoyle 2 v1.000.011"}, +	{ 0xD0B8794E, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,668), "Iceman v1.023"}, +	{ 0x94EA377B, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,631), "The Colonel's Bequest v1.000.046"}, +	{ 0x28543FDF, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,453), "Astro Chicken"}, +	{ 0x31F46F7D, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,453), "Space Quest III v1.0V int"}, +	{ 0xAA2C94B9, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,685), "Mixed-Up Mother Goose v1.011 Int.#8.2.90"}, +	{ 0x3B15678B, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,631), "The Colonel's Bequest v1.000.046-3.5"}, +	{ 0x0E042F46, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,530), "Hoyle 1 v1.000.113-3.5"}, +	{ 0x1EACB959, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,566), "HQ v1.000-5.25"}, +	{ 0x2BEAF5E7, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,566), "HQ v1.001-5.25"}, +	{ 0x63626D3E, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,668), "Iceman v1.023-5.25"}, +	{ 0xDA5E7B7D, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,409), "KQ4 v1.003.006-3.5"}, +	{ 0x376A5472, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,502), "KQ4 v1.006.003-5.25"}, +	{ 0x364B40B2, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,395), "PQ2 v1.001.000-5.25"}, +	{ 0x664B4123, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,409), "PQ2 v1.001.006-3.5"}, +	{ 0x379F4582, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,453), "SQ3 v1.0V-5.25"}, +	{ 0x04B0B081, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,294), "xmascard v1.04"}, + +	{ 0x4447B28D, SCI_VERSION_AUTODETECT, SCI_VERSION(1,000,72),  "Trial v1.105"}, + +	{ 0xB1C2CCAE, SCI_VERSION_AUTODETECT, SCI_VERSION(1,000,200), "SQ4 v1.052"}, /* 1.000.753 */ +	{ 0xAA6AF6A9, SCI_VERSION_AUTODETECT, SCI_VERSION(1,000,60),  "KQ5 v0.000.062"}, +	{ 0x092C2C0D, 3,		      SCI_VERSION(1,000,172), "jones v1.000.060"}, /* x.yyy.zzz */ + +	{ 0xC415A485, SCI_VERSION_AUTODETECT, SCI_VERSION(1,000,172), "jones v1.000.060-cd"}, /* x.yyy.zzz */ + +	{ 0x89C595E3, SCI_VERSION_AUTODETECT, SCI_VERSION(1,000,510), "SQ1 v2.000"}, /* T.A00.081 */ +	{ 0x09D4FC54, SCI_VERSION_AUTODETECT, SCI_VERSION(1,000,510), "LSL5 v1.000"}, /* T.A00.169 */ +	{ 0xF3ED1D81, SCI_VERSION_AUTODETECT, SCI_VERSION(1,000,510), "PQ3 v1.00"}, /* T.A00.178 */ +	{ 0x501B5E6B, SCI_VERSION_AUTODETECT, SCI_VERSION(1,000,510), "Brain v1.000"}, /* 1.000.044 */ +	{ 0xB1B7279B, SCI_VERSION_AUTODETECT, SCI_VERSION(1,000,510), "Longbow v1.000"}, /* 1.000.168 */ + +	{ 0x82595EBE, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,453), "SQ3 v1.0V-ami"}, /* x.yyy.zzz */ +	{ 0xF6080B61, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,530), "Hoyle 1 v1.000.139-ami"}, /* x.yyy.zzz */ + +	{ 0x8AFEA2D0, 2,		      SCI_VERSION(1,000,000), "KQ1 v1.000.054-ami"}, /* 1.003.007 */ + +	/* Undetermined Amiga versions: */ +/*	{ 0x8AE5F854, ?, SCI_VERSION(), "ARTHUR" }, */ +/*	{ 0x9FB7015B, ?, SCI_VERSION(), "CB1" }, */ +/*	{ 0x560CEDD5, ?, SCI_VERSION(), "iceMan" }, */ + +	{ 0, 0, 0, NULL } /* terminator */ +}; + +#endif /* _SCI_GAMES_H_ */ diff --git a/engines/sci/scicore/hashmap.c b/engines/sci/scicore/hashmap.c new file mode 100644 index 0000000000..1b2b6af5c1 --- /dev/null +++ b/engines/sci/scicore/hashmap.c @@ -0,0 +1,186 @@ +/*************************************************************************** + hashmap.c Copyright (C) 2001 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + +    Christoph Reichenbach (CR) <jameson@linuxgames.com> + +***************************************************************************/ +/* Defines a universal hash map. +** Preprocessor parameters: +** TYPE: The type to hash +** HASH_MAX: Maximum hash value +** HASH(x): Hashes a value of type TYPE to an int from 0 to HASH_MAX +*/ +#include <stdio.h> +#include <stdlib.h> + +#ifdef MUST_FREE +#  define CLEAR_NODE(n) free(n->name); n->name = NULL +#  define FREE_PARAM(n) free(n) +#else +#  define CLEAR_NODE(n) +#  define FREE_PARAM(n) +#endif + + +#ifdef DUPLICATOR +#  define DUP_VALUE(x) DUPLICATOR((x)) +#else +#  define DUP_VALUE(x) (x) +#endif + + +#define DEFINE_FUNCTIONS(TYPE) \ +										\ +										\ +TYPE##_hash_map_t *								\ +new_##TYPE##_hash_map(void)							\ +{										\ +	TYPE##_hash_map_t *map = (TYPE##_hash_map_t*)calloc(1, sizeof(TYPE##_hash_map_t));\ +										\ +	return map;								\ +}										\ +                                                                \ +                                                                \ +static void							\ +print_##TYPE##_nodes(TYPE##_hash_map_node_t *node)		\ +{								\ +	while (node) {						\ +		fprintf(stderr,"%p  ", (void *)node);		\ +		node = node->next;				\ +	}							\ +}								\ +								\ +void								\ +print_##TYPE##_hash_map(TYPE##_hash_map_t *map)			\ +{								\ +	int bucket;						\ +	fprintf(stderr, #TYPE " map %p: base value=%d\n", (void *)map,  \ +		map->base_value);		                \ +	for (bucket = 0; bucket <= HASH_MAX; bucket++) {        \ +		fprintf(stderr,"bucket %d: ", bucket);		\ +		print_##TYPE##_nodes(map->nodes[bucket]);	\ +		fprintf(stderr,"\n");				\ +	}							\ +	fprintf(stderr,"holes: ");				\ +	print_##TYPE##_nodes(map->holes);			\ +	fprintf(stderr,"\n");					\ +}                                                               \ +                                                                \ +void														 \ +apply_to_##TYPE##_hash_map(TYPE##_hash_map_t *map, void *param, void (*note)(void *param, TYPE name, int value)) \ +{														 \ +	int i;													 \ +	for (i = 0; i < HASH_MAX; i++) {									 \ +		TYPE##_hash_map_node_t *node = map->nodes[i];							 \ +		while (node) {											 \ +			note(param, node->name, node->value);							 \ +			node = node->next;									 \ +		}												 \ +	}													 \ +}														 \ +														 \ +										\ +static void									\ +free_##TYPE##_hash_map_node_t##_recursive(TYPE##_hash_map_node_t *node)		\ +{										\ +	if (node) {								\ +		CLEAR_NODE(node);				                \ +		free_##TYPE##_hash_map_node_t##_recursive(node->next);		\ +		free(node);							\ +	}									\ +}										\ +										\ +										\ +void										\ +free_##TYPE##_hash_map(TYPE##_hash_map_t *map)					\ +{										\ +	int i;									\ +										\ +	for (i = 0; i <= HASH_MAX; i++)						\ +		free_##TYPE##_hash_map_node_t##_recursive(map->nodes[i]);	\ +										\ +	free_##TYPE##_hash_map_node_t##_recursive(map->holes);			\ +										\ +	map->base_value = -42000; /* Trigger problems for people who		\ +			     ** forget to loose the reference  */		\ +	free(map);								\ +}										\ +										\ +int										\ +TYPE##_hash_map_check_value(TYPE##_hash_map_t *map, TYPE value,                 \ +			    char add, char *was_added)                  	\ +{										\ +	TYPE##_hash_map_node_t **node = &(map->nodes[HASH(value)]);		\ +										\ +	while (*node && COMP(value, (*node)->name))				\ +		node = &((*node)->next);					\ +										\ +        if (was_added)								\ +                *was_added = 0;							\ +										\ +	if (*node) {								\ +		FREE_PARAM(value);						\ +		return (*node)->value;						\ +	}									\ +	/* Not found */								\ +										\ +	if (!add)								\ +		return -1;							\ +										\ +        if (was_added)								\ +                *was_added = 1;							\ +										\ +	if (map->holes) { /* Re-use old node */					\ +		(*node) = map->holes;						\ +		map->holes = (*node)->next;					\ +                (*node)->next = NULL;                                           \ +		(*node)->name = DUP_VALUE(value);				\ +	} else {								\ +		*node = (TYPE##_hash_map_node_t*)malloc(sizeof(TYPE##_hash_map_node_t));\ +		(*node)->name = DUP_VALUE(value);				\ +		(*node)->value = map->base_value++;				\ +                (*node)->next = NULL;                                           \ +	}									\ +										\ +	return (*node)->value;							\ +}										\ +										\ +										\ +int										\ +TYPE##_hash_map_remove_value(TYPE##_hash_map_t *map, TYPE value)		\ +{										\ +	TYPE##_hash_map_node_t **node = &(map->nodes[HASH(value)]);		\ +										\ +	while (*node && COMP(value, (*node)->name))				\ +		node = &((*node)->next);					\ +										\ +	if (*node) {								\ +		TYPE##_hash_map_node_t *oldnode = *node;			\ +		*node = (*node)->next;						\ +										\ +		oldnode->next = map->holes; /* Old node is now a 'hole' */	\ +		map->holes = oldnode;						\ +		return oldnode->value;						\ +	} else return -1; /* Not found */					\ +}										\ + diff --git a/engines/sci/scicore/huffmake.pl b/engines/sci/scicore/huffmake.pl new file mode 100644 index 0000000000..8c34537d81 --- /dev/null +++ b/engines/sci/scicore/huffmake.pl @@ -0,0 +1,157 @@ +#! /usr/bin/perl + +# Uncomment the following line to debug +# $DEBUG=1; +@codes; + +$leaf_value = 0; +while (<>) { +    chop; +    @tokens = split //; + +    if ($_ eq "-") { +	calc_values(); +	print stderr "$leaf_value tokens processed; result is a huffman tree.\n"; +	exit(0); +    } + +    $codes_len[$leaf_value] = scalar @tokens; + +    for ($i = 0; $i < scalar @tokens; $i++) { +	$codes[$leaf_value][$i] = $tokens[$i]; +    } + +    $leaf_value++; +} + +$nodes_counter = 0; +@unlinked; + +sub branch_node { +    $left = shift; +    $right = shift; +    print ("\tBRANCH_NODE(", $nodes_counter || "0" , ", $left, $right)\n"); +    $nodes_counter++; +} + +sub leaf_node { +    $value = shift; +    print ("\tLEAF_NODE  (", $nodes_counter || "0" ,", ", $value || "0" , ")\n"); +    $nodes_counter++; +} + +sub intval { +    my $nr = shift; +    my $retval = sub_intval(0, $codes_len[$nr], $nr); +    return $retval >> 1; +} + +sub sub_intval { +    my $lv = shift; +    my $maxlv = shift; +    my $nr = shift; + +    if ($maxlv >= 0) { +	my $v = $codes[$nr][$maxlv]; +	my $retval = sub_intval($lv + 1, $maxlv-1, $nr) << 1; + +	if ($v == "1") { +	    $retval |= 1; +	} +	return $retval || 0; +    } else { +	return 0; +    } +} + +sub calc_values() { + +    $depth = 1; +    my $startdepth = 100000; + +    for ($i; $i < scalar @codes; $i++) { +	if ($codes_len[$i] > $depth) { +	    $depth = $codes_len[$i]; +	} + +	if ($codes_len[$i] < $startdepth) { +	    $startdepth = $codes_len[$i]; +	} +    } + +    branch_node(1, 2); + +    $level = 1; +    $unlinked[0] = 1; +    $unlinked[1] = 2; +    $linkctr = 3; + +    for (; $level <= $depth; $level++) { +	my $entries = 1 << $level; + +	for ($j = 0; $j < ($entries << 1); $j++) { +	    $new_unlinked[$j] = -1; +	} + +	for ($i = 0; $i < $entries; $i++) { +	    if ($unlinked[$i] > -1) { +		$match = -1; + +		if ($DEBUG) { +		    print " Finding len=$level val=$i: "; +		} +		for ($j = 0; ($match == -1) && $j < $leaf_value; $j++) { +		    if (($codes_len[$j] == $level) +			&& (intval($j) == $i)) { +			$match = $j; +		    } else { +			if ($DEBUG) { +			    print "($j:$codes_len[$j],",intval($j),") "; +			} +		    } +		} +		if ($DEBUG) { +		    print "\n"; +		} + +		if ($match == -1) { +		    die "Expected $unlinked[$i], but counted $nodes_counter in $i at level $level" unless ($unlinked[$i] == $nodes_counter); +		    my $lnr = $linkctr++; +		    my $rnr = $linkctr++; +		    $new_unlinked[$i << 1] = $lnr; +		    $new_unlinked[1+($i << 1)] = $rnr; +		    branch_node($lnr, $rnr); +		} else { +		    leaf_node($match); +		    $new_unlinked[$i << 1] = -1; +		    $new_unlinked[1+($i << 1)] = -1; +		} +		 +	    } +	} + +	if ($DEBUG) { +	    print "level $level: Copying ", ($entries << 1), "\n"; +	} +	for ($j = 0; $j < ($entries << 1); $j++) { +	    $unlinked[$j] = $new_unlinked[$j]; +	    if ($DEBUG) { +		print $unlinked[$j], " "; +	    } +	} +	if ($DEBUG) { +	    print "\n"; +	} +    } + +    my $ok = 1; +    for ($j = 0; $j < ($entries << 1); $j++) { +	if ($unlinked[$j] != -1) { +	    $ok = 0; +	} +    } + +    print "#warning \"Tree is not a huffman tree!\"\n" unless $ok; +} + + diff --git a/engines/sci/scicore/hufftree.1 b/engines/sci/scicore/hufftree.1 new file mode 100644 index 0000000000..d51462d304 --- /dev/null +++ b/engines/sci/scicore/hufftree.1 @@ -0,0 +1,17 @@ +101 +11 +100 +011 +0101 +0100 +0011 +00101 +00100 +00011 +00010 +000011 +000010 +000001 +0000001 +0000000 +- diff --git a/engines/sci/scicore/hufftree.2 b/engines/sci/scicore/hufftree.2 new file mode 100644 index 0000000000..d13cd2fa66 --- /dev/null +++ b/engines/sci/scicore/hufftree.2 @@ -0,0 +1,69 @@ +11 +1011 +1010 +10011 +10010 +10001 +10000 +011111 +011110 +011101 +011100 +011011 +011010 +011001 +011000 +010111 +010110 +010101 +010100 +010011 +010010 +010001 +0100001 +0100000 +0011111 +0011110 +0011101 +0011100 +0011011 +0011010 +0011001 +0011000 +0010111 +0010110 +0010101 +0010100 +0010011 +0010010 +0010001 +0010000 +0001111 +0001110 +0001101 +0001100 +0001011 +0001010 +0001001 +0001000 +00001111 +00001110 +00001101 +00001100 +00001011 +00001010 +00001001 +00001000 +00000111 +00000110 +00000101 +00000100 +00000011 +00000010 +00000001 +00000000 +- + + + + diff --git a/engines/sci/scicore/hufftree.3 b/engines/sci/scicore/hufftree.3 new file mode 100644 index 0000000000..116b195383 --- /dev/null +++ b/engines/sci/scicore/hufftree.3 @@ -0,0 +1,257 @@ +00001001001 +000001111111 +000001111110 +000001111101 +000001111100 +000001111011 +000001111010 +000001111001 +000001111000 +00011101 +0100011 +000001110111 +000001110110 +0100010 +000001110101 +000001110100 +000001110011 +000001110010 +000001110001 +000001110000 +000001101111 +000001101110 +000001101101 +000001101100 +000001101011 +000001101010 +0000001001001 +000001101001 +000001101000 +000001100111 +000001100110 +000001100101 +1111 +0000101001 +00011100 +000001100100 +0000101000 +000001100011 +0000100111 +00011011 +0100001 +0100000 +00011010 +000011011 +0011111 +100101 +0011110 +00011001 +0011101 +100100 +0011100 +0011011 +0011010 +0011001 +00011000 +0011000 +0010111 +00010111 +00010110 +000001100010 +00001001000 +0010110 +000011010 +00001000111 +000001100001 +100011 +0010101 +100010 +100001 +11101 +0010100 +00010101 +00010100 +100000 +00001000110 +000011001 +011111 +0010011 +011110 +011101 +0010010 +00001000101 +011100 +011011 +011010 +0010001 +000011000 +00010011 +000010111 +000010110 +00001000100 +00010010 +00001000011 +000010101 +000001100000 +00010001 +000001011111 +11100 +011001 +011000 +010111 +11011 +010110 +010101 +010100 +11010 +00001000010 +0010000 +11001 +010011 +11000 +10111 +010010 +0000100110 +10110 +10101 +10100 +10011 +00010000 +0001111 +00001111 +00001110 +0000100101 +00001000001 +00001000000 +000001011110 +000001011101 +000001011100 +0000001001000 +0000001000111 +0000001000110 +0000001000101 +0000001000100 +0000001000011 +0000001000010 +0000001000001 +0000001000000 +0000000111111 +0000000111110 +0000000111101 +0000000111100 +0000000111011 +0000000111010 +0000000111001 +0000000111000 +0000000110111 +0000000110110 +0000000110101 +0000000110100 +0000000110011 +0000000110010 +0000000110001 +0000000110000 +0000000101111 +0000000101110 +0000000101101 +0000000101100 +0000000101011 +0000000101010 +0000000101001 +0000000101000 +0000000100111 +0000000100110 +0000000100101 +0000000100100 +0000000100011 +0000000100010 +0000000100001 +0000000100000 +0000000011111 +0000000011110 +0000000011101 +0000000011100 +0000000011011 +0000000011010 +0000000011001 +000001011011 +000001011010 +000001011001 +000001011000 +000001010111 +000001010110 +000001010101 +000001010100 +000001010011 +000001010010 +000001010001 +000001010000 +000001001111 +000001001110 +000001001101 +000001001100 +000001001011 +000001001010 +000001001001 +000001001000 +000001000111 +000001000110 +000001000101 +000001000100 +000001000011 +000001000010 +000001000001 +000001000000 +000000111111 +000000111110 +000000111101 +000000111100 +000000111011 +000000111010 +000000111001 +000000111000 +000000110111 +000000110110 +000000110101 +000000110100 +000000110011 +000000110010 +000000110001 +000000110000 +000000101111 +000000101110 +000000101101 +000000101100 +0000000011000 +000000101011 +0000000010111 +0000000010110 +0000000010101 +000000101010 +0000000010100 +0000000010011 +0000000010010 +000000101001 +0000000010001 +0000000010000 +0000000001111 +0000000001110 +000000101000 +0000000001101 +0000000001100 +0000000001011 +000000100111 +000000100110 +000000100101 +0000000001010 +0000000001001 +0000000001000 +0000000000111 +0000000000110 +0000000000101 +0000000000100 +0000000000011 +0000000000010 +0000000000001 +0000000000000 +- diff --git a/engines/sci/scicore/int_hashmap.c b/engines/sci/scicore/int_hashmap.c new file mode 100644 index 0000000000..0b70838e17 --- /dev/null +++ b/engines/sci/scicore/int_hashmap.c @@ -0,0 +1,33 @@ +/*************************************************************************** + int_hashmap. Copyright (C) 2001 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + +    Christoph Reichenbach (CR) <jameson@linuxgames.com> + +***************************************************************************/ + +#define BUILD_MAP_FUNCTIONS +#include "int_hashmap.h" + +#include "hashmap.c" + +DEFINE_FUNCTIONS(int) diff --git a/engines/sci/scicore/makefile.dos b/engines/sci/scicore/makefile.dos new file mode 100644 index 0000000000..d456fb16de --- /dev/null +++ b/engines/sci/scicore/makefile.dos @@ -0,0 +1,19 @@ +# +# FreeSCI/DOS Makefile +# +# 19991220              rink            created this file +# +# +TARGET  :       scicore.a + +FILES   =       console.o tools.o resource.o decompress0.o decompress1.o \ +                script.o vocab.o vocab_debug.o  old_objects.o sci_dos.o +		 +CC	= gcc +CFLAGS	= -g -c -I../include -I../.. -D_DOS -DHAVE_LIBPNG -DHAVE_UNISTD_H + +clean: +		del *.o *.a + +scicore.a:      ${FILES} +		ar r scicore.a ${FILES} diff --git a/engines/sci/scicore/modules.c b/engines/sci/scicore/modules.c new file mode 100644 index 0000000000..f63d4e4aa0 --- /dev/null +++ b/engines/sci/scicore/modules.c @@ -0,0 +1,153 @@ +/*************************************************************************** + modules.c Copyright (C) 2001 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + +    Christoph Reichenbach (CR) <jameson@linuxgames.com> + +***************************************************************************/ + +#if 0 +#ifndef __BEOS__ + +#include <sci_memory.h> +#include <modules.h> +#include <dlfcn.h> + + +static sci_module_t * +_sci_try_open_module(char *filename, char *path, char *struct_name, void **handle) +{ +	char *fullname = sci_malloc(strlen(path) + strlen(DIR_SEPARATOR_STR) +				+ strlen(filename)); +	sci_module_t *module; +fprintf(stderr,"Trying module %s at %s\n", filename, path); +	strcpy(fullname, path); +	strcat(fullname, DIR_SEPARATOR_STR); +	strcat(fullname, filename); + +fprintf(stderr,"Total name is %s\n", fullname); +	*handle = dlopen(fullname, RTLD_NOW); +fprintf(stderr,"Could not open because: %s\n", dlerror()); +	free(fullname); + +	if (!*handle) +		return NULL; + +	module = (sci_module_t *) dlsym(*handle, struct_name); +	if (!module) +		fprintf(stderr,"%s: Failed to find symbol '%s'.\n", +			fullname, struct_name); + +	return module; +} + +void * +sci_find_module(char *path, char *name, char *type, char *struct_prefix, +		char *file_suffix, int magic, int version, void **handle) +{ +	char *module_name = sci_malloc(strlen(type) + strlen(DIR_SEPARATOR_STR) +				 + strlen(name) + strlen(file_suffix) +				 + strlen(MODULE_NAME_SUFFIX) + 1); +	char *struct_name = sci_malloc(strlen(struct_prefix) + strlen(name) + 1); +	char *dir_end; +	char *path_pos = path; +	char path_separator = PATH_SEPARATOR_STR[0]; +	sci_module_t *module = NULL; + +	strcpy(module_name, type); +	strcat(module_name, DIR_SEPARATOR_STR); +	strcat(module_name, name); +	strcat(module_name, file_suffix); +	strcat(module_name, MODULE_NAME_SUFFIX); + +	strcpy(struct_name, struct_prefix); +	strcat(struct_name, name); + +	do { +		dir_end = strchr(path_pos, path_separator); +		if (dir_end) +			*dir_end = 0; + +		module = _sci_try_open_module(module_name, path_pos, +					      struct_name, handle); + +		if (module) { +			if (module->class_magic != magic) { +				fprintf(stderr, "%s at %s is not a %s module, skipping...\n", +					module_name, path_pos, type); +				dlclose(*handle); +				module = NULL; +			} else if (module->class_version != version) { +				fprintf(stderr, "%s at %s has %s module version %d," +					" expected %d- skipping...\n", +					module_name, path_pos, type, module->class_version, +					version); +				dlclose(*handle); +				module = NULL; +			} +		} + +		if (dir_end) { +			*dir_end = path_separator; +			path_pos = dir_end + 1; +		} + +	} while (!module && dir_end); + +	if (!module) { +		*handle = NULL; +		fprintf(stderr,"%s module '%s' not found in path '%s'.\n", +			type, name, path); +	} else { +		if (dir_end) +			*dir_end = 0; + +		printf("Using %s driver '%s', version %s, from '%s'.\n", +		       type, module->module_name, module->module_version, +		       path_pos); + +		if (dir_end) +			*dir_end = path_separator; +	} + +	free(module_name); +	free(struct_name); + +	return (void *) module; +} + + +void +sci_close_module(void *module, char *type, char *name) +{ +	if (!module) +		return; + +	if (dlclose(module)) { +		fprintf(stderr,"Error while closing %s module '%s': %s\n", +			type, name, dlerror()); +	} +} + +#endif /* !__BEOS__ */ + +#endif /* 0 */ diff --git a/engines/sci/scicore/old_objects.c b/engines/sci/scicore/old_objects.c new file mode 100644 index 0000000000..8408373537 --- /dev/null +++ b/engines/sci/scicore/old_objects.c @@ -0,0 +1,716 @@ +#include <sciresource.h> +#include <console.h> +#include <script.h> +#include <vocabulary.h> +#include <old_objects.h> +#include <stdio.h> +#include <stdlib.h> +#include <util.h> +#include <vm.h> +#include <assert.h> + +#ifdef SCI_CONSOLE +#define printf sciprintf +/* Yeah, I shouldn't be doing this ;-) [CJR] */ +#endif + +FLEXARRAY_NOEXTRA(object*) fobjects; + +static int knames_count; +static char** knames; +static char** snames; +static opcode* opcodes; + +object **object_map, *object_root; +int max_object; + +const char* globals[] = { +  /*00*/ +  "ego", +  "GAMEID", +  "roomXX", +  "speed", +  /*04*/ +  "quitFlag", +  "cast", +  "regions", +  "timer", +  /*08*/ +  "sounds", +  "inv", +  "eventHandler", +  "roomNumberExit", +  /*0C*/ +  "previousRoomNumber", +  "roomNumber", +  "enterDebugModeOnRoomExit", +  "score", +  /*10*/ +  "maximumScore", +  "11", +  "speed", +  "13", +  /*14*/ +  "14", +  "loadCursor", +  "normalFont", +  "restoreSaveFont", /*dialogFont*/ +  /*18*/ +  "18", +  "19", +  "defaultFont", +  "1B", +  /*1C*/ +  "pointerToVersionNumber", +  "locales", +  "pointerToSaveGameDirectory", +  "1F" +}; + +static int add_object(object* obj) +{ +	FLEXARRAY_APPEND(object*, fobjects, obj, return 1); +	return 0; +} + +static void dump(byte* data, int len) +{ +	int i=0; +	while(i<len) +	{ +		printf("%02X ", data[i++]); +		if(i%8==0) printf("   "); +		if(i%16==0) printf("\n"); +	} +	if(i%16) printf("\n"); +} + +static void printMethod(object* obj, int meth, int indent) +{ +	script_method* m=obj->methods[meth]; +	int i, j; + +	for(j=0; j<indent*2-1; j++) printf(" "); +	printf("Method %s\n", snames[m->number]); + +	for(i=0; i<m->used; i++) +	{ +		script_opcode op=m->data[i]; + +		for(j=0; j<indent; j++) printf("  "); +		printf("%s ", opcodes[op.opcode].name); + +		switch(op.opcode) +		{ +			case 0x21: /*callk*/ +			{ +				if(op.arg1>knames_count) printf("<no such kernel function %02X> ", op.arg1); +				else printf("%s ", knames[op.arg1]); +				printf("%02X", op.arg2); +			} break; +			case 0x28: /*class*/ +			{ +				if(op.arg1>max_object) printf("<no such class %02X>", op.arg1); +				else +				{ +					/* [DJ] op.arg1+1 adjusts for the <root> object */ +                                        if(fobjects.data[op.arg1+1]==0) printf("<null object>"); +					else printf("%s", fobjects.data[op.arg1+1]->name); +				} +			} break; +			case 0x44: +			{ +				if(op.arg1>0x20) printf("<no such global %02X> ", op.arg1); +				else printf("%s ", globals[op.arg1]); +			} break; +			default: +			{ +				int args[3]; +				args[0]=op.arg1; +				args[1]=op.arg2; +				args[2]=op.arg3; +				for(j=0; j<3; j++) +				{ +					switch(formats[op.opcode][j]) +					{ +						case Script_Invalid: +						{ +								printf("<invalid> "); +						} break; +						case Script_None: +						{ +							j=3; +						} break; +					        case Script_SByte: +						case Script_Byte: +						{ +							printf("%02X ", args[j]); +						} break; +						case Script_Word: +					        case Script_SVariable: +						case Script_Variable: +                                                case Script_SRelative: +                                                case Script_Property: +                                                case Script_Global: +                                                case Script_Local: +                                                case Script_Temp: +                                                case Script_Param: +						{ +							printf("%04X ", args[j]); +						} break; +						case Script_SWord: +						{ +							if(args[j]<0) printf("-%04X", -args[j]); +							else printf("%04X", args[j]); +						} break; +						case Script_End: +						{ +							printf("\n"); +							return; +						} break; +						default: +						{ +							printf("<unknown arg type %d> ", formats[op.opcode][j]); +						} +					} +				} +			} break; +		} +		printf("\n"); +	} +} + +static void printObject_r(object* obj, int flags, int level) +{ +	int i; +	for(i=0; i<level; i++) printf("  "); +	if(obj==0) printf("(null)\n"); +	else +	{ +		printf("%s\n", obj->name); +		if(flags&SCRIPT_PRINT_METHODS) +		{ +			for(i=0; i<obj->method_count; i++) +			{ +				printMethod(obj, i, level+1); +			} +		} +		if(flags&SCRIPT_PRINT_CHILDREN) +		{ +			for(i=0; i<obj->children.used; i++) +			{ +				printObject_r(obj->children.data[i], flags, level+1); +			} +		} +	} +} + +void printObject(object* obj, int flags) +{ +	printf("pO(%p, %d)\n", obj, flags); +	printObject_r(obj, flags, 0); +} + +static object* object_new() +{ +	object* obj= (object*)sci_malloc(sizeof(object)); +	if(obj==0) return 0; + +	obj->parent=0; +	FLEXARRAY_INIT(object*, obj->children); +	obj->name=0; +	obj->selector_count=0; +	obj->selector_numbers=0; +	obj->methods=0; +	obj->method_count=0; + +	return obj; +} + +static int add_child(object* parent, object* child) +{ +	FLEXARRAY_APPEND(object*, parent->children, child, return 1); +	return 0; +} + +static object* fake_object(const char* reason) +{ +	object* obj=object_new(); +	if(obj==0) +	{ +		#ifdef SCRIPT_DEBUG +		printf("object_new failed during fake for %s\n", reason); +		#endif +		free(obj); +		return 0; +	} +	if(add_child(object_root, obj)) +	{ +		#ifdef SCRIPT_DEBUG +		printf("add_child failed during fake for %s\n", reason); +		#endif +		free(obj); +		return 0; +	} +	obj->name=reason; +	if(add_object(obj)) +	{ +		#ifdef SCRIPT_DEBUG +		printf("add_object failed during fake for %s\n", reason); +		#endif +		/*FIXME: clean up parent*/ +		return 0; +	} +	return obj; +} + +static script_method* decode_method(byte* data) +{ +  script_method* m; +  int done=0; +  int pos=0; +  static int count=0; + +  count++; + +  if((m= (script_method*)sci_malloc(sizeof(script_method)))==0) return 0; +  FLEXARRAY_INIT(script_opcode, *m); + +  while(!done) +    { +      int op=data[pos]>>1; +      int size=2-(data[pos]&1); +      int* args[3]; +      int arg; +      int old_pos; + +      FLEXARRAY_ADD_SPACE(script_opcode, *m, 1, return 0); +      old_pos=pos; +      m->data[m->used-1].pos=pos; +      m->data[m->used-1].opcode=op; + +      /*Copy the adresses of the args to an array for convenience*/ +      args[0]=&m->data[m->used-1].arg1; +      args[1]=&m->data[m->used-1].arg2; +      args[2]=&m->data[m->used-1].arg3; + +      /*Skip past the opcode*/ +      pos++; + +      for(arg=0; arg<4; arg++) +	{ +	  switch(formats[op][arg]) +	    { +	    case Script_Invalid: /*Can't happen(tm)*/ +	      { +	      	int i; +		printf("Invalid opcode %02X at %04X in method %d\n", op, pos, count); +		for(i=m->used-9; i<m->used-1; i++) +		{ +			printf("%s[%02X] ", opcodes[m->data[i].opcode].name, m->data[i].opcode); +			dump(data+m->data[i].pos, m->data[i].size); +		} +		printf("Dump from %04X-%04X\n", pos-16, pos+16); +		dump(data + pos - 16, 32); +	      } break; +	    case Script_None: /*No more args*/ +	      { +		arg=4; +	      } break; +	    case Script_Byte: /*Just a one byte arg*/ +	    case Script_SByte: +	      { +		*args[arg]=data[pos++]; +	      } break; +	    case Script_Word: /*A two byte arg*/ +	      { +		*args[arg]=getInt16(data+pos); +		pos+=2; +	      } break; +	    case Script_SWord: /*A signed two-byte arg*/ +	      { +		int t=getInt16(data+pos); +		if(t&0x8000) *args[arg]=-(t&0x7FFF); +		else *args[arg]=t; +		pos+=2; +	      } break; +	    case Script_Variable: /*Size of arg depends on LSB in opcode*/ +            case Script_SVariable: +            case Script_SRelative: +            case Script_Property: +            case Script_Global: +            case Script_Local: +            case Script_Temp: +            case Script_Param: +	      { +		if(size==1) *args[arg]=data[pos++]; +		else +		{ +			*args[arg]=getInt16(data+pos); +			pos+=2; +		} +	      } break; +	    case Script_End: /*Special tag for ret*/ +	      { +		done=1; +		arg=4; +	      } break; +	    default: /*Can't happen(tm)*/ +	      { +		printf("Unknown argument format %d for op %02X\n", formats[op][arg], op); +	      } break; +	    } +	} +      fflush(stdout); +      if (m->used) m->data[m->used-1].size=pos-old_pos; +    } + +  return m; +} + +#ifdef SCRIPT_DEBUG +void list_code_blocks(resource_t* r) +{ +	int pos=getInt16(r->data+2); +	while(pos<r->size-2) +	{ +		int type=getInt16(r->data+pos); +		int len=getInt16(r->data+pos+2); +		if(type==2) printf("%X-%X\n", pos, pos+len); +		pos+=len; +	} +} +#endif + + +/*These expect the frame, the whole frame, and, well, other stuff too, + *I guess, as long as it looks like a frame*/ +static int get_type(unsigned char* obj) +{ +	return getInt16(obj); +} + +static int get_length(unsigned char* obj) +{ +	return getInt16(obj+2); +} + +static int get_selector_count(unsigned char* obj) +{ +	return getInt16(obj+10); +} + +static int get_selector_value(unsigned char* obj, int sel) +{ +	assert(sel<get_selector_count(obj)); +	return getInt16(obj + 12 + sel*2); +} + +/*Bas things happen if the method offset value is wrong*/ +static unsigned char* get_method_area(unsigned char* obj) +{ +	return obj+getInt16(obj+8)+10; +} + +static int get_method_count(unsigned char* obj) +{ +	return getInt16(get_method_area(obj)); +} + +static int get_method_number(unsigned char* obj, int i) +{ +	assert(i<get_method_count(obj)); +	return getInt16(get_method_area(obj)+2+2*i); +} + +static int get_method_location(unsigned char* obj, int i) +{ +	assert(i<get_method_count(obj)); +	return getInt16(get_method_area(obj)+4+2*get_method_count(obj)+2*i); +} + + +/*Returns the position of the first frame of type 'type' in resource 'r', + *starting from the frame starting at 'start', or -1 on failure. + */ +static int find_frame(resource_t* r, int type, unsigned int start) +{ +	int t=-1; +	unsigned int pos = start; +	unsigned char* frame; + +	assert(start<=r->size-4); + +	#ifdef SCRIPT_DEBUG +	printf("Searching for frame of type %d in script %03d, starting at %#x\n", type, r->number, start); +	dump(r->data+start, 32); +	#endif + +	/*Some times there's an extra byte at the beginning. Christoph?*/ +#if 1 +	if(pos==0 && r->size>=6 && \ +	   !((0<getInt16(r->data)) && (10>getInt16(r->data)))) pos=2; +#else +	if(pos == 0)  +		pos = 2; +#endif +	frame = r->data + pos; +	while(1) +	{ +#ifdef SCRIPT_DEBUG +		printf("offset = %#x\n", pos); +		dump(frame, 32); +#endif +		t = get_type(frame); +		if(t == type)  +			break; + +		if(t == 0)  +			return -1; + +		pos+=get_length(frame); +		if(pos > (r->size - 2))  +			return -1; +		frame+=get_length(frame); +	} + +	return pos; +} + + + +/*FIXME: lots of things are identical to read_object and read_class. Some of + *these would benefit from being put in separate functions.*/ + +static object* read_object(resource_mgr_t *resmgr, int script, int positions[1000]) +{ +	resource_t* r = scir_find_resource(resmgr, sci_script, script, 0); +	unsigned char* raw; +	int pos; +	object* obj; + +	printf("Searching for object in script %03d\n", script); + +	if(r==0) return 0; + +	/*Skip to the next object*/ +	#ifdef SCRIPT_DEBUG +	printf("pre skip: pos=%#x\n", positions[script]); +	#endif +	pos=find_frame(r, 1, positions[script]); +	#ifdef SCRIPT_DEBUG +	printf("post skip: pos=%#x\n", pos); +	#endif +	if(pos==-1) return 0; +	else positions[script]=pos+get_length(r->data+pos); +	#ifdef SCRIPT_DEBUG +	printf("post post: pos=%#x (len=%#x)\n", positions[script], get_length(r->data+pos)); +	#endif + +	/*Construct the object*/ +	obj=object_new(); +	raw=r->data+pos; + +	/*Fill in the name*/ +	if(get_selector_count(raw)<4) obj->name="<anonymous>"; +	else +	{ +		if (get_selector_value(raw, 3)) +			obj->name = (char *) r->data + get_selector_value(raw, 3); +		else obj->name="<null>"; +	} + +	/*Fill in the class*/ +	if(get_selector_count(raw)==0) obj->parent=object_root; +	else +	{ +		int parent_id=get_selector_value(raw, 1); +		if(parent_id>=fobjects.used) +		{ +			free(obj); +			return 0; +		} +		if(parent_id<1) obj->parent=object_root; +		else obj->parent=fobjects.data[parent_id]; +	} + +	/*Add the object to the class*/ +	if(!obj->parent) +	{ +		free(obj); +		return 0; +	} +	if(add_child(obj->parent, obj)){ +		free(obj); +		return 0; +	} +	if(add_object(obj)) +	{ +		free(obj); +		return 0; +	} + +	/*FIXME: decode selectors here*/ + +	obj->method_count=get_method_count(raw); +	obj->methods= (script_method**)sci_malloc(obj->method_count*sizeof(script_method)); +	if(obj->methods==0) +	{ +		free(obj); +		return 0; +	} else { +		int i; +		for(i=0; i<obj->method_count; i++) +		{ +			int number=get_method_number(raw, i); +			int position=get_method_location(raw, i); + +			if((obj->methods[i]=decode_method(r->data+position))==0) +			{ +				obj->method_count=i-1; +				break; +			} +			obj->methods[i]->number=number; +		} +	} + +	return obj; +} + +static object* read_class(resource_mgr_t *resmgr, int script, int positions[1000]) +{ +	resource_t* r = scir_find_resource(resmgr, sci_script, script, 0); +	unsigned char* raw; +	int pos; +	object* obj; + +	printf("Searching for class in script %03d\n", script); + +	if(r==0) return fake_object("<resource not found>"); + +	/*Skip to the next class*/ +	#ifdef SCRIPT_DEBUG +	printf("pre skip: pos=%#x\n", positions[script]); +	#endif +	pos=find_frame(r, 6, positions[script]); +	#ifdef SCRIPT_DEBUG +	printf("post skip: pos=%#x\n", pos); +	#endif +	if(pos==-1) return fake_object("<no more classes in script>"); +	else positions[script]=pos+get_length(r->data+pos); +	#ifdef SCRIPT_DEBUG +	printf("post post: pos=%#x (len=%#x)\n", positions[script], get_length(r->data+pos)); +	#endif + +	/*Construct the object*/ +	obj=object_new(); +	raw=r->data+pos; + +	/*Fill in the name*/ +	if(get_selector_count(raw)<4) obj->name="<anonymous>"; +	else +	{ +		if (get_selector_value(raw, 3)) +			obj->name = (char *) r->data + get_selector_value(raw, 3); +		else obj->name="<null>"; +	} + +	/*Fill in the parent*/ +	if(get_selector_count(raw)==0) obj->parent=object_root; +	else +	{ +		int superclass_id=get_selector_value(raw, 1); +		printf("superclass==%d\n", superclass_id); +		if(superclass_id>=fobjects.used) +		{ +			free(obj); +			return fake_object("<no such superclass>"); +		} +		if(superclass_id<1) obj->parent=object_root; +		else obj->parent=fobjects.data[superclass_id]; +	} + +	/*Add the class to the hierarchy*/ +	if(!obj->parent) +	{ +		free(obj); +		return fake_object("<null parent>"); +	} +	if(add_child(obj->parent, obj)){ +		free(obj); +		return fake_object("<add_child failed>"); +	} +	if(add_object(obj)) +	{ +		free(obj); +		return fake_object("<add_object failed>"); +	} + +	/*FIXME: decode selectors and methods here*/ + +	return obj; +} + +void freeObject(object* obj) +{ +	int i; +	for(i=0; i<obj->children.used; i++) freeObject(obj->children.data[i]); +	free(obj); +} + +static int objects_init(resource_mgr_t *resmgr) +{ +	FLEXARRAY_INIT(object*, fobjects); +	max_object=0; + +	if((object_root=object_new())==0) return 1; +	object_root->name="<root>"; +	add_object(object_root); + +	opcodes=vocabulary_get_opcodes(resmgr); +	knames=vocabulary_get_knames(resmgr, &knames_count); +	snames=vocabulary_get_snames(resmgr, NULL, 0); + +	return 0; +} + +int loadObjects(resource_mgr_t *resmgr) +{ +	int i; +	int *classes, class_count; +	int positions[1000]; + +	if(objects_init(resmgr)) +	{ +		#ifdef SCRIPT_DEBUG +		perror("objects_init"); +		#endif +		return 1; +	} +	classes=vocabulary_get_classes(resmgr, &class_count); + +	for(i=0; i<1000; i++) positions[i]=0; +	for(i=0; i<class_count; i++) +	{ +#ifdef SCRIPT_DEBUG +                printf ("\n\nReading class 0x%02X\n", i); +#endif +                if(read_class(resmgr, classes[i], positions)==0) +		{ +			#ifdef SCRIPT_DEBUG +			fprintf(stderr, "Failed to load class %d, which is a parent.\n", i); +			#endif +			return 1; +		} +	} + +	for(i=0; i<1000; i++) positions[i]=0; +	for(i=0; i<1000; i++) while(read_object(resmgr, i, positions)); + +	object_map=fobjects.data; +	max_object=fobjects.used; + +	return 0; +} + + diff --git a/engines/sci/scicore/reg_t_hashmap.c b/engines/sci/scicore/reg_t_hashmap.c new file mode 100644 index 0000000000..0ac8382422 --- /dev/null +++ b/engines/sci/scicore/reg_t_hashmap.c @@ -0,0 +1,42 @@ +/*************************************************************************** + int_hashmap. Copyright (C) 2001 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + +    Christoph Reichenbach (CR) <jameson@linuxgames.com> + +***************************************************************************/ + +#define BUILD_MAP_FUNCTIONS +#include "reg_t_hashmap.h" + +#include "hashmap.c" + +static inline int +compare_reg_t (reg_t lhs, reg_t rhs) +{ +	if (lhs.segment == rhs.segment) +		return lhs.offset - rhs.offset; +	else +		return lhs.segment - rhs.segment; +} + +DEFINE_FUNCTIONS(reg_t) diff --git a/engines/sci/scicore/resource.c b/engines/sci/scicore/resource.c new file mode 100644 index 0000000000..b137790f89 --- /dev/null +++ b/engines/sci/scicore/resource.c @@ -0,0 +1,951 @@ +/*************************************************************************** + resource.c Copyright (C) 1999 Christoph Reichenbach, TU Darmstadt + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + +    Christoph Reichenbach (CJR) [creichen@rbg.informatik.tu-darmstadt.de] + + History: + +   990327 - created (CJR) + +***************************************************************************/ +/* Resource library */ + +#include <sci_memory.h> +#include <sciresource.h> +#include <vocabulary.h> /* For SCI version auto-detection */ + +#include <ctype.h> + +#ifdef _WIN32 +#include <direct.h> +#endif + +#undef SCI_REQUIRE_RESOURCE_FILES +/* #define SCI_VERBOSE_RESMGR 1 */ + +const char* sci_version_types[] = { +	"SCI version undetermined (Autodetect failed / not run)", +	"SCI version 0.xxx", +	"SCI version 0.xxx w/ 1.000 compression", +	"SCI version 1.000 w/ 0.xxx resource.map", +	"SCI version 1.000 w/ special resource.map", +	"SCI version 1.000 (early)", +	"SCI version 1.000 (late)", +	"SCI version 1.001", +	"SCI WIN/32" +}; + +const int sci_max_resource_nr[] = {65536, 1000, 2048, 2048, 2048, 8192, 8192, 65536}; + +const char* sci_error_types[] = { +	"No error", +	"I/O error", +	"Resource is empty (size 0)", +	"resource.map entry is invalid", +	"resource.map file not found", +	"No resource files found", +	"Unknown compression method", +	"Decompression failed: Decompression buffer overflow", +	"Decompression failed: Sanity check failed", +	"Decompression failed: Resource too big", +	"SCI version is unsupported"}; + +const char* sci_resource_types[] = {"view","pic","script","text","sound", +				    "memory","vocab","font","cursor", +				    "patch","bitmap","palette","cdaudio", +				    "audio","sync","message","map","heap"}; +/* These are the 18 resource types supported by SCI1 */ + +const char *sci_resource_type_suffixes[] = {"v56","p56","scr","tex","snd", +					    "   ","voc","fon","cur","pat", +					    "bit","pal","cda","aud","syn", +					    "msg","map","hep"}; + + +int resourcecmp(const void *first, const void *second); + + +typedef int decomp_funct(resource_t *result, int resh, int sci_version); +typedef void patch_sprintf_funct(char *string, resource_t *res); + +static decomp_funct *decompressors[] = { +	NULL, +	&decompress0, +	&decompress01, +	&decompress01, +	&decompress01, +	&decompress1, +	&decompress1, +	&decompress11, +	NULL +}; + +static patch_sprintf_funct *patch_sprintfers[] = { +	NULL, +	&sci0_sprintf_patch_file_name, +	&sci0_sprintf_patch_file_name, +	&sci1_sprintf_patch_file_name, +	&sci1_sprintf_patch_file_name, +	&sci1_sprintf_patch_file_name, +	&sci1_sprintf_patch_file_name, +	&sci1_sprintf_patch_file_name, +	&sci1_sprintf_patch_file_name +}; + + +int resourcecmp (const void *first, const void *second) +{ +	if (((resource_t *)first)->type == +	    ((resource_t *)second)->type) +		return (((resource_t *)first)->number < +			((resource_t *)second)->number)? -1 : +		!(((resource_t *)first)->number == +		  ((resource_t *)second)->number); +	else +		return (((resource_t *)first)->type < +			((resource_t *)second)->type)? -1 : 1; +} + + + + + +/*-----------------------------*/ +/*-- Resmgr helper functions --*/ +/*-----------------------------*/ + +void +_scir_add_altsource(resource_t *res, resource_source_t *source, unsigned int file_offset) +{ +	resource_altsource_t *rsrc = (resource_altsource_t*)sci_malloc(sizeof(resource_altsource_t)); + +	rsrc->next = res->alt_sources; +	rsrc->source = source; +	rsrc->file_offset = file_offset; +	res->alt_sources = rsrc; +} + +resource_t * +_scir_find_resource_unsorted(resource_t *res, int res_nr, int type, int number) +{ +	int i; +	for (i = 0; i < res_nr; i++) +		if (res[i].number == number && res[i].type == type) +			return res + i; +	return NULL; +} + +/*-----------------------------------*/ +/** Resource source list management **/ +/*-----------------------------------*/ + +resource_source_t * +scir_add_external_map(resource_mgr_t *mgr, char *file_name) +{ +	resource_source_t *newsrc = (resource_source_t *)  +		malloc(sizeof(resource_source_t)); + +	/* Add the new source to the SLL of sources */ +	newsrc->next = mgr->sources; +	mgr->sources = newsrc; + +	newsrc->source_type = RESSOURCE_TYPE_EXTERNAL_MAP; +	newsrc->location.file.name = strdup(file_name); +	newsrc->scanned = 0; +	newsrc->associated_map = NULL; + +	return newsrc; +} + +resource_source_t * +scir_add_volume(resource_mgr_t *mgr, resource_source_t *map, char *filename, +		int number, int extended_addressing) +{ +	resource_source_t *newsrc = (resource_source_t *)  +		malloc(sizeof(resource_source_t)); + +	/* Add the new source to the SLL of sources */ +	newsrc->next = mgr->sources; +	mgr->sources = newsrc; + +	newsrc->source_type = RESSOURCE_TYPE_VOLUME; +	newsrc->scanned = 0; +	newsrc->location.file.name = strdup(filename); +	newsrc->location.file.volume_number = number; +	newsrc->associated_map = map; +} + +resource_source_t * +scir_add_patch_dir(resource_mgr_t *mgr, int type, char *dirname) +{ +	resource_source_t *newsrc = (resource_source_t *)  +		malloc(sizeof(resource_source_t)); + +	/* Add the new source to the SLL of sources */ +	newsrc->next = mgr->sources; +	mgr->sources = newsrc; + +	newsrc->source_type = RESSOURCE_TYPE_DIRECTORY; +	newsrc->scanned = 0; +	newsrc->location.dir.name = strdup(dirname); +} + +resource_source_t * +scir_get_volume(resource_mgr_t *mgr, resource_source_t *map, int volume_nr) +{ +	resource_source_t *seeker = mgr->sources; + +	while (seeker) +	{ +		if (seeker->source_type == RESSOURCE_TYPE_VOLUME && +		    seeker->associated_map == map && +		    seeker->location.file.volume_number == volume_nr) +			return seeker; +		seeker = seeker->next; +	} + +	return NULL; +} + +/*------------------------------------------------*/ +/** Resource manager constructors and operations **/ +/*------------------------------------------------*/ + +static void +_scir_init_trivial(resource_mgr_t *mgr) +{ +	mgr->resources_nr = 0; +	mgr->resources = (resource_t*)sci_malloc(1); +} + + +static void +_scir_load_from_patch_file(int fh, resource_t *res, char *filename) +{ +	int really_read; + +	res->data = (unsigned char*)sci_malloc(res->size); +	really_read = read(fh, res->data, res->size); + +	if (really_read < res->size) { +		sciprintf("Error: Read %d bytes from %s but expected %d!\n", +			  really_read, filename, res->size); +		exit(1); +	} + +	res->status = SCI_STATUS_ALLOCATED; +} + +static void +_scir_load_resource(resource_mgr_t *mgr, resource_t *res, int protect) +{ +	char filename[PATH_MAX]; +	int fh; +	resource_t backup; +	char *save_cwd = sci_getcwd(); + +	memcpy(&backup, res, sizeof(resource_t)); + +	/* First try lower-case name */ +	if (res->source->source_type == RESSOURCE_TYPE_DIRECTORY) { + +		if (!patch_sprintfers[mgr->sci_version]) { +			sciprintf("Resource manager's SCI version (%d) has no patch file name printers -> internal error!\n", +				  mgr->sci_version); +			exit(1); +		} + +		/* Get patch file name */ +		patch_sprintfers[mgr->sci_version](filename, res); +		chdir(res->source->location.dir.name); +	} else +		strcpy(filename, res->source->location.file.name); + +	fh = open(filename, O_RDONLY | O_BINARY); + + +	if (!IS_VALID_FD(fh)) { +		char *raiser = filename; +		while (*raiser) { +			*raiser = toupper(*raiser); /* Uppercasify */ +			++raiser; +		} +		fh = sci_open(filename, O_RDONLY|O_BINARY); +	}    /* Try case-insensitively name */ + +	if (!IS_VALID_FD(fh)) { +		sciprintf("Failed to open %s!\n", filename); +		res->data = NULL; +		res->status = SCI_STATUS_NOMALLOC; +		res->size = 0; +		chdir(save_cwd); +		free(save_cwd); +		return; +	} + + +	lseek(fh, res->file_offset, SEEK_SET); + +	if (res->source->source_type == RESSOURCE_TYPE_DIRECTORY || +	    res->source->source_type == RESSOURCE_TYPE_AUDIO_DIRECTORY) +		_scir_load_from_patch_file(fh, res, filename); +	else if (!decompressors[mgr->sci_version]) { +		/* Check whether we support this at all */ +		sciprintf("Resource manager's SCI version (%d) is invalid!\n", +			  mgr->sci_version); +		exit(1); +	} else { +		int error = /* Decompress from regular resource file */ +			decompressors[mgr->sci_version](res, fh, mgr->sci_version); + +		if (error) { +			sciprintf("Error %d occured while reading %s.%03d" +				  " from resource file: %s\n", +				  error, sci_resource_types[res->type], res->number, +				  sci_error_types[error]); + +			if (protect) +				memcpy(res, &backup, sizeof(resource_t)); + +			res->data = NULL; +			res->status = SCI_STATUS_NOMALLOC; +			res->size = 0; +			chdir(save_cwd); +			free(save_cwd); +			return; +		} +	} + +	close(fh); +} + +resource_t * +scir_test_resource(resource_mgr_t *mgr, int type, int number) +{ +	resource_t binseeker; +	binseeker.type = type; +	binseeker.number = number; +	return (resource_t *) +		bsearch(&binseeker, mgr->resources, mgr->resources_nr, +			sizeof(resource_t), resourcecmp); +} + +int sci0_get_compression_method(int resh); + +int +sci_test_view_type(resource_mgr_t *mgr) +{ +	int fh; +	char filename[PATH_MAX]; +	int compression; +	resource_t *res; +	int i; + +	mgr->sci_version = SCI_VERSION_AUTODETECT; + +	for (i=0;i<1000;i++) +	{ +		res = scir_test_resource(mgr, sci_view, i); + +		if (!res) continue; + +		if (res->source->source_type == RESSOURCE_TYPE_DIRECTORY || +		    res->source->source_type == RESSOURCE_TYPE_AUDIO_DIRECTORY) +			continue; + +		strcpy(filename, res->source->location.file.name); +		fh = open(filename, O_RDONLY | O_BINARY); + +		if (!IS_VALID_FD(fh)) { +			char *raiser = filename; +			while (*raiser) { +				*raiser = toupper(*raiser); /* Uppercasify */ +				++raiser; +			} +			fh = sci_open(filename, O_RDONLY|O_BINARY); +		}    /* Try case-insensitively name */ +		 +		if (!IS_VALID_FD(fh)) continue; +		lseek(fh, res->file_offset, SEEK_SET); + +		compression = sci0_get_compression_method(fh); +		close(fh); + +		if (compression == 3) +			return (mgr->sci_version = SCI_VERSION_01_VGA); +	} + +	/* Try the same thing with pics */ +	for (i=0;i<1000;i++) +	{ +		res = scir_test_resource(mgr, sci_pic, i); + +		if (!res) continue; + +		if (res->source->source_type == RESSOURCE_TYPE_DIRECTORY || +		    res->source->source_type == RESSOURCE_TYPE_AUDIO_DIRECTORY) +			continue; + +		strcpy(filename, res->source->location.file.name); +		fh = open(filename, O_RDONLY | O_BINARY); + + +		if (!IS_VALID_FD(fh)) { +			char *raiser = filename; +			while (*raiser) { +				*raiser = toupper(*raiser); /* Uppercasify */ +				++raiser; +			} +			fh = sci_open(filename, O_RDONLY|O_BINARY); +		}    /* Try case-insensitively name */ +		 +		if (!IS_VALID_FD(fh)) continue; +		lseek(fh, res->file_offset, SEEK_SET); + +		compression = sci0_get_compression_method(fh); +		close(fh); + +		if (compression == 3) +			return (mgr->sci_version = SCI_VERSION_01_VGA); +	} + +	return mgr->sci_version; +} +	 + +		 +int +scir_add_appropriate_sources(resource_mgr_t *mgr, +			     int allow_patches, +			     char *dir) +{ +	char *trailing_slash = ""; +	char path_separator; +	sci_dir_t dirent; +	char *name; +	resource_source_t *map; +	int fd; +	char fullname[PATH_MAX]; + +	if (dir[strlen(dir)-1] != G_DIR_SEPARATOR) +	{ +		trailing_slash = G_DIR_SEPARATOR_S; +	} + +	name = (char *)malloc(strlen(dir) + 1 + +		      strlen("RESOURCE.MAP") + 1); +	 +	sprintf(fullname, "%s%s%s", dir, trailing_slash, "RESOURCE.MAP"); +	fd = sci_open("RESOURCE.MAP", O_RDONLY | O_BINARY); +	if (!IS_VALID_FD(fd)) return 0; +	close(fd); +	map = scir_add_external_map(mgr, fullname); +	free(name); +	sci_init_dir(&dirent); +	name = sci_find_first(&dirent, "RESOURCE.0??"); +	while (name != NULL) +	{ +		char *dot = strrchr(name, '.'); +		int number = atoi(dot + 1); + +		sprintf(fullname, "%s%s%s", dir, G_DIR_SEPARATOR_S, name); +		scir_add_volume(mgr, map, fullname, number, 0); +		name = sci_find_next(&dirent); +	} +	sci_finish_find(&dirent); + +	sci_finish_find(&dirent); +	sprintf(fullname, "%s%s", dir, G_DIR_SEPARATOR_S); +	scir_add_patch_dir(mgr, RESSOURCE_TYPE_DIRECTORY, fullname); + +	return 1; +} + +static int +_scir_scan_new_sources(resource_mgr_t *mgr, int *detected_version, resource_source_t *source) +{ +	int preset_version = mgr->sci_version; +	int resource_error = 0; +	int dummy = mgr->sci_version; +	resource_t **concat_ptr = &(mgr->resources[mgr->resources_nr-1].next); + +	if (detected_version == NULL) +		detected_version = &dummy; + +	*detected_version = mgr->sci_version; +	if (source->next) +		_scir_scan_new_sources(mgr, detected_version, source->next); + +	if (!source->scanned) +	{ +		source->scanned = 1; +		switch (source->source_type) +		{ +		case RESSOURCE_TYPE_DIRECTORY: +			if (mgr->sci_version <= SCI_VERSION_01) +				sci0_read_resource_patches(source, +							   &mgr->resources, +							   &mgr->resources_nr); +			else +				sci1_read_resource_patches(source, +							   &mgr->resources, +							   &mgr->resources_nr); +			break; +		case RESSOURCE_TYPE_EXTERNAL_MAP: +			if (preset_version <= SCI_VERSION_01_VGA_ODD +			    /* || preset_version == SCI_VERSION_AUTODETECT -- subsumed by the above line */) { +				resource_error = +					sci0_read_resource_map(mgr,  +							       source, +							       &mgr->resources, +							       &mgr->resources_nr, +							       detected_version); +				 +#if 0 +				if (resource_error >= SCI_ERROR_CRITICAL) { +					sciprintf("Resmgr: Error while loading resource map: %s\n", +						  sci_error_types[resource_error]); +					if (resource_error == SCI_ERROR_RESMAP_NOT_FOUND) +						sciprintf("Running SCI games without a resource map is not supported ATM\n"); +					sci_free(mgr); +					chdir(caller_cwd); +					free(caller_cwd); +					return NULL; +				} +				if (resource_error == SCI_ERROR_RESMAP_NOT_FOUND) { +					/* fixme: Try reading w/o resource.map */ +					resource_error = SCI_ERROR_NO_RESOURCE_FILES_FOUND; +				} + +				if (resource_error == SCI_ERROR_NO_RESOURCE_FILES_FOUND) { +					/* Initialize empty resource manager */ +					_scir_init_trivial(mgr); +					resource_error = 0; +				} +#endif +			} +			 +			if ((preset_version == SCI_VERSION_1_EARLY)|| +			    (preset_version == SCI_VERSION_1_LATE)|| +			    (preset_version == SCI_VERSION_1_1)|| +			    ((*detected_version == SCI_VERSION_AUTODETECT)&&(preset_version == SCI_VERSION_AUTODETECT))) +			{ +				resource_error = +					sci1_read_resource_map(mgr, +							       source, +							       scir_get_volume(mgr, source, 0), +							       &mgr->resources, +							       &mgr->resources_nr, +							       detected_version); +					 +				if (resource_error == SCI_ERROR_RESMAP_NOT_FOUND) { +					/* fixme: Try reading w/o resource.map */ +					resource_error = SCI_ERROR_NO_RESOURCE_FILES_FOUND; +				} +				 +				if (resource_error == SCI_ERROR_NO_RESOURCE_FILES_FOUND) { +					/* Initialize empty resource manager */ +					_scir_init_trivial(mgr); +					resource_error = 0; +				} +				 +				*detected_version = SCI_VERSION_1; +			} +			 +			mgr->sci_version = *detected_version; +			break; +		} +		qsort(mgr->resources, mgr->resources_nr, sizeof(resource_t), +		      resourcecmp); /* Sort resources */ +	} +	return resource_error; +} + +int +scir_scan_new_sources(resource_mgr_t *mgr, int *detected_version) +{ +	_scir_scan_new_sources(mgr, detected_version, mgr->sources); +} + +static void +_scir_free_resource_sources(resource_source_t *rss) +{ +	if (rss) { +		_scir_free_resource_sources(rss->next); +		free(rss); +	} +} + +resource_mgr_t * +scir_new_resource_manager(char *dir, int version, +			  char allow_patches, int max_memory) +{ +	int resource_error = 0; +	resource_mgr_t *mgr = (resource_mgr_t*)sci_malloc(sizeof(resource_mgr_t)); +	char *caller_cwd = sci_getcwd(); +	int resmap_version = version; + +	if (chdir(dir)) { +		sciprintf("Resmgr: Directory '%s' is invalid!\n", dir); +		free(caller_cwd); +		return NULL; +	} + +	mgr->max_memory = max_memory; + +	mgr->memory_locked = 0; +	mgr->memory_lru = 0; + +	mgr->resource_path = dir; + +	mgr->resources = NULL; +	mgr->resources_nr = 0; +	mgr->sources = NULL; +	mgr->sci_version = version; + +	scir_add_appropriate_sources(mgr, allow_patches, dir); +	scir_scan_new_sources(mgr, &resmap_version); + +	if (!mgr->resources || !mgr->resources_nr) { +		if (mgr->resources) { +			free(mgr->resources); +			mgr->resources = NULL; +		} +		sciprintf("Resmgr: Could not retreive a resource list!\n"); +		_scir_free_resource_sources(mgr->sources); +		sci_free(mgr); +		chdir(caller_cwd); +		free(caller_cwd); +		return NULL; +	} + +	mgr->lru_first = NULL; +	mgr->lru_last = NULL; + +	mgr->allow_patches = allow_patches; + +	qsort(mgr->resources, mgr->resources_nr, sizeof(resource_t), +	      resourcecmp); /* Sort resources */ + +	if (version == SCI_VERSION_AUTODETECT) +		switch (resmap_version) { +		case SCI_VERSION_0: +			if (scir_test_resource(mgr, sci_vocab, +					       VOCAB_RESOURCE_SCI0_MAIN_VOCAB)) { +				version = sci_test_view_type(mgr); +				if (version == SCI_VERSION_01_VGA) +				{ +					sciprintf("Resmgr: Detected KQ5 or similar\n"); +				} else { +					sciprintf("Resmgr: Detected SCI0\n"); +					version = SCI_VERSION_0; +				} +			} else if (scir_test_resource(mgr, sci_vocab, +						      VOCAB_RESOURCE_SCI1_MAIN_VOCAB)) { +				version = sci_test_view_type(mgr); +				if (version == SCI_VERSION_01_VGA) +				{ +					sciprintf("Resmgr: Detected KQ5 or similar\n"); +				} else { +					if (scir_test_resource(mgr, sci_vocab, 912)) { +						sciprintf("Resmgr: Running KQ1 or similar, using SCI0 resource encoding\n"); +						version = SCI_VERSION_0; +					} else { +						version = SCI_VERSION_01; +						sciprintf("Resmgr: Detected SCI01\n"); +					} +				} +			} else { +				version = sci_test_view_type(mgr); +				if (version == SCI_VERSION_01_VGA) +				{ +					sciprintf("Resmgr: Detected KQ5 or similar\n"); +				} else { +					sciprintf("Resmgr: Warning: Could not find vocabulary; assuming SCI0 w/o parser\n"); +					version = SCI_VERSION_0; +				} +			} break; +		case SCI_VERSION_01_VGA_ODD: +			version = resmap_version; +			sciprintf("Resmgr: Detected Jones/CD or similar\n"); +			break; +		case SCI_VERSION_1: +		{ +			resource_t *res = scir_test_resource(mgr, sci_script, 0); +			 +			mgr->sci_version = version = SCI_VERSION_1_EARLY; +			_scir_load_resource(mgr, res, 1); +			 +			if (res->status == SCI_STATUS_NOMALLOC) +			    mgr->sci_version = version = SCI_VERSION_1_LATE; + +			/* No need to handle SCI 1.1 here - it was done in resource_map.c */ +			break; +		} +		default: +			sciprintf("Resmgr: Warning: While autodetecting: Couldn't" +				  " determine SCI version!\n"); +		} + +	if (!resource_error) +	{ +#if 0 +		if (version <= SCI_VERSION_01) +			sci0_read_resource_patches(dir, +						   &mgr->resources, +						   &mgr->resources_nr); +		else +			sci1_read_resource_patches(dir, +						   &mgr->resources, +						   &mgr->resources_nr); +#endif + +		qsort(mgr->resources, mgr->resources_nr, sizeof(resource_t), +		      resourcecmp); /* Sort resources */ +	} + +	mgr->sci_version = version; + +	chdir(caller_cwd); +	free(caller_cwd); + +	return mgr; +} + +static void +_scir_free_altsources(resource_altsource_t *dynressrc) +{ +	if (dynressrc) { +		_scir_free_altsources(dynressrc->next); +		free(dynressrc); +	} +} + +void +_scir_free_resources(resource_t *resources, int resources_nr) +{ +	int i; + +	for (i = 0; i < resources_nr; i++) { +		resource_t *res = resources + i; + +		_scir_free_altsources(res->alt_sources); + +		if (res->status != SCI_STATUS_NOMALLOC) +			sci_free(res->data); +	} + +	sci_free(resources); +} + +void +scir_free_resource_manager(resource_mgr_t *mgr) +{ +	_scir_free_resources(mgr->resources, mgr->resources_nr); +	_scir_free_resource_sources(mgr->sources); +	mgr->resources = NULL; + +	sci_free(mgr); +} + + +static void +_scir_unalloc(resource_t *res) +{ +	sci_free(res->data); +	res->data = NULL; +	res->status = SCI_STATUS_NOMALLOC; +} + + +static void +_scir_remove_from_lru(resource_mgr_t *mgr, resource_t *res) +{ +	if (res->status != SCI_STATUS_ENQUEUED) { +		sciprintf("Resmgr: Oops: trying to remove resource that isn't" +			  " enqueued\n"); +		return; +	} + +	if (res->next) +		res->next->prev = res->prev; +	if (res->prev) +		res->prev->next = res->next; +	if (mgr->lru_first == res) +		mgr->lru_first = res->next; +	if (mgr->lru_last == res) +		mgr->lru_last = res->prev; + +	mgr->memory_lru -= res->size; + +	res->status = SCI_STATUS_ALLOCATED; +} + +static void +_scir_add_to_lru(resource_mgr_t *mgr, resource_t *res) +{ +	if (res->status != SCI_STATUS_ALLOCATED) { +		sciprintf("Resmgr: Oops: trying to enqueue resource with state" +			  " %d\n", res->status); +		return; +	} + +	res->prev = NULL; +	res->next = mgr->lru_first; +	mgr->lru_first = res; +	if (!mgr->lru_last) +		mgr->lru_last = res; +	if (res->next) +		res->next->prev = res; + +	mgr->memory_lru += res->size; +#if (SCI_VERBOSE_RESMGR > 1) +	fprintf(stderr, "Adding %s.%03d (%d bytes) to lru control: %d bytes total\n", +		sci_resource_types[res->type], res->number, res->size, +		mgr->memory_lru); + +#endif + +	res->status = SCI_STATUS_ENQUEUED; +} + +static void +_scir_print_lru_list(resource_mgr_t *mgr) +{ +	int mem = 0; +	int entries = 0; +	resource_t *res = mgr->lru_first; + +	while (res) { +		fprintf(stderr,"\t%s.%03d: %d bytes\n", +			sci_resource_types[res->type], res->number, +			res->size); +		mem += res->size; +		++entries; +		res = res->next; +	} + +	fprintf(stderr,"Total: %d entries, %d bytes (mgr says %d)\n", +		entries, mem, mgr->memory_lru); +} + +static void +_scir_free_old_resources(resource_mgr_t *mgr, int last_invulnerable) +{ +	while (mgr->max_memory < mgr->memory_lru +	       && (!last_invulnerable || mgr->lru_first != mgr->lru_last)) { +		resource_t *goner = mgr->lru_last; +		if (!goner) { +			fprintf(stderr,"Internal error: mgr->lru_last is NULL!\n"); +			fprintf(stderr,"LRU-mem= %d\n", mgr->memory_lru); +			fprintf(stderr,"lru_first = %p\n", (void *)mgr->lru_first); +			_scir_print_lru_list(mgr); +		} + +		_scir_remove_from_lru(mgr, goner); +		_scir_unalloc(goner); +#ifdef SCI_VERBOSE_RESMGR +		sciprintf("Resmgr-debug: LRU: Freeing %s.%03d (%d bytes)\n", +			  sci_resource_types[goner->type], goner->number, +			  goner->size); +#endif +	} +} + +resource_t * +scir_find_resource(resource_mgr_t *mgr, int type, int number, int lock) +{ +	resource_t *retval; + +	if (number >= sci_max_resource_nr[mgr->sci_version]) { +		int modded_number = number % sci_max_resource_nr[mgr->sci_version]; +		sciprintf("[resmgr] Requested invalid resource %s.%d, mapped to %s.%d\n", +			  sci_resource_types[type], number, +			  sci_resource_types[type], modded_number); +		number = modded_number; +	} + +	retval = scir_test_resource(mgr, type, number); + +	if (!retval) +		return NULL; + +	if (!retval->status) +		_scir_load_resource(mgr, retval, 0); + +	else if (retval->status == SCI_STATUS_ENQUEUED) +		_scir_remove_from_lru(mgr, retval); +	/* Unless an error occured, the resource is now either +	** locked or allocated, but never queued or freed.  */ + +	if (lock) { +		if (retval->status == SCI_STATUS_ALLOCATED) { +			retval->status = SCI_STATUS_LOCKED; +			retval->lockers = 0; +			mgr->memory_locked += retval->size; +		} + +		++retval->lockers; + +	} else if (retval->status != SCI_STATUS_LOCKED) { /* Don't lock it */ +		if (retval->status == SCI_STATUS_ALLOCATED) +			_scir_add_to_lru(mgr, retval); +	} + +	_scir_free_old_resources(mgr, retval->status == SCI_STATUS_ALLOCATED); + +	if (retval->data) +		return retval; +	else { +		sciprintf("Resmgr: Failed to read %s.%03d\n", +			  sci_resource_types[retval->type], retval->number); +		return NULL; +	} +} + +void +scir_unlock_resource(resource_mgr_t *mgr, resource_t *res, int resnum, int restype) +{ +	if (!res) { +		sciprintf("Resmgr: Warning: Attempt to unlock non-existant" +			  " resource %s.%03d!\n", +			  sci_resource_types[restype], resnum); +		return; +	} + +	if (res->status != SCI_STATUS_LOCKED) { +		sciprintf("Resmgr: Warning: Attempt to unlock unlocked" +			  " resource %s.%03d\n", +			  sci_resource_types[res->type], res->number); +		return; +	} + +	if (!--res->lockers) { /* No more lockers? */ +		res->status = SCI_STATUS_ALLOCATED; +		mgr->memory_locked -= res->size; +		_scir_add_to_lru(mgr, res); +	} + +	_scir_free_old_resources(mgr, 0); +} + diff --git a/engines/sci/scicore/resource_map.c b/engines/sci/scicore/resource_map.c new file mode 100644 index 0000000000..5a85383911 --- /dev/null +++ b/engines/sci/scicore/resource_map.c @@ -0,0 +1,515 @@ +/*************************************************************************** + resource_map.c Copyright (C) 2001 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + +    Christoph Reichenbach (CR) <jameson@linuxgames.com> + +***************************************************************************/ + +#include <sci_memory.h> +#include <sciresource.h> +#include <resource.h> +#ifdef HAVE_UNISTD_H +#  include <unistd.h> +#endif + +#define RESOURCE_MAP_FILENAME "resource.map" + +#define SCI0_RESMAP_ENTRIES_SIZE 6 +#define SCI1_RESMAP_ENTRIES_SIZE 6 +#define SCI11_RESMAP_ENTRIES_SIZE 5 + +static int +detect_odd_sci01(int fh) +{ +    byte buf[6]; +    int files_ok = 1; +    int fsize, resources_nr, tempfh, read_ok; +    char filename[14]; +         +    fsize = sci_fd_size(fh); +    if (fsize < 0) { +	perror("Error occured while trying to get filesize of resource.map"); +	return SCI_ERROR_RESMAP_NOT_FOUND; +    } + +    resources_nr = fsize / SCI0_RESMAP_ENTRIES_SIZE; +     +    while (resources_nr-->1) +    { +        read_ok = read(fh, &buf, SCI0_RESMAP_ENTRIES_SIZE); + +	if (read_ok) +	{	 +		sprintf(filename, "resource.%03i", SCI0_RESFILE_GET_FILE(buf+2)); +		tempfh = sci_open(filename, O_RDONLY | O_BINARY); +     +		if (tempfh == SCI_INVALID_FD) { +			files_ok = 0; +			break; +		} + +		close(tempfh); +	} +    } + +    lseek(fh, 0, SEEK_SET); + +    return files_ok; +} +		 +static int +sci_res_read_entry(resource_mgr_t *mgr, resource_source_t *map,  +		   byte *buf, resource_t *res, int sci_version) +{ +	res->id = buf[0] | (buf[1] << 8); +	res->type = SCI0_RESID_GET_TYPE(buf); +	res->number = SCI0_RESID_GET_NUMBER(buf); +	res->status = SCI_STATUS_NOMALLOC; + +	if (sci_version == SCI_VERSION_01_VGA_ODD) { +		res->source = scir_get_volume(mgr, map, SCI01V_RESFILE_GET_FILE(buf + 2)); +		res->file_offset = SCI01V_RESFILE_GET_OFFSET(buf + 2); + +#if 0 +		if (res->type < 0 || res->type > sci1_last_resource) +			return 1; +#endif +	} else { +		res->source = scir_get_volume(mgr, map, SCI0_RESFILE_GET_FILE(buf + 2)); +		res->file_offset = SCI0_RESFILE_GET_OFFSET(buf + 2); + +#if 0 +		if (res->type < 0 || res->type > sci0_last_resource) +			return 1; +#endif +	} + +#if 0 +	fprintf(stderr, "Read [%04x] %6d.%s\tresource.%03d, %08x\n", +		res->id, res->number, +		sci_resource_type_suffixes[res->type], +		res->file, res->file_offset); +#endif + +	if (res->source == NULL) return 1; +	return 0; +} + +inline int sci1_res_type(int ofs, int *types, int lastrt) +{ +	int i, last = -1; + +	for (i=0;i<=sci1_last_resource;i++) +		if (types[i]) +		{ +			if (types[i]>ofs) +				return last; +			last=i; +		} + +	return lastrt; +} + +int sci1_parse_header(int fd, int *types, int *lastrt) +{ +	unsigned char rtype; +	unsigned char offset[2]; +	int read_ok; +	int size = 0; + +	do +	{ +		read_ok = read(fd, &rtype, 1); +		if (!read_ok) break; +		read_ok = read(fd, &offset, 2); +		if (read_ok<2)  +			read_ok=0; +		if (rtype!=0xff) +		{ +			types[rtype&0x7f]=(offset[1]<<8)|(offset[0]); +			*lastrt = rtype&0x7f; +		} +		size+=3; +	} while (read_ok && (rtype != 0xFF)); + +	if (!read_ok) return 0; +	 +	return size; +} + + + +int +sci0_read_resource_map(resource_mgr_t *mgr, resource_source_t *map, resource_t **resource_p, int *resource_nr_p, int *sci_version) +{ +	int fsize; +	int fd; +	resource_t *resources; +	int resources_nr; +	int resource_index = 0; +	int resources_total_read = 0; +	int next_entry; +	int max_resfile_nr = 0; +	 +	byte buf[SCI0_RESMAP_ENTRIES_SIZE]; +	fd = sci_open(map->location.file.name, O_RDONLY | O_BINARY); + +	if (!IS_VALID_FD(fd)) +		return SCI_ERROR_RESMAP_NOT_FOUND; + +	read(fd, &buf, 4); + +	/* Theory: An SCI1 map file begins with an index that allows us to seek quickly +	   to a particular resource type. The entries are three bytes long; one byte +	   resource type, two bytes start position and so on.  +	   The below code therefore tests for three things: +	    +	   Is the first resource type 'view'? +	   Do those entries start at an offset that is an exact multiple of the +	   index entry size? +	   Is the second resource type 'pic'? +	    +	   This requires that a given game has both views and pics,  +	   a safe assumption usually, except in message.map and room-specific +	   (audio) map files, neither of which SCI0 has. +	    +	   */ +	    +	if ((buf[0] == 0x80) && +	    (buf[1] % 3 == 0) && +	    (buf[3] == 0x81)) +	{ +		close(fd); +		return SCI_ERROR_INVALID_RESMAP_ENTRY; +	} + +	lseek(fd, 0, SEEK_SET); + +	switch (detect_odd_sci01(fd)) +	{ +	    case 0 : /* Odd SCI01 */ +		if (*sci_version == SCI_VERSION_AUTODETECT) +		    *sci_version = SCI_VERSION_01_VGA_ODD; +		break; +	    case 1 : /* SCI0 or normal SCI01 */ +		if (*sci_version == SCI_VERSION_AUTODETECT) +		    *sci_version = SCI_VERSION_0; +		break; +	    default : /* Neither, or error occurred */ +		return SCI_ERROR_RESMAP_NOT_FOUND; +	} +	 +	if ((fsize = sci_fd_size(fd)) < 0) { +		perror("Error occured while trying to get filesize of resource.map"); +		return SCI_ERROR_RESMAP_NOT_FOUND; +	} + +	resources_nr = fsize / SCI0_RESMAP_ENTRIES_SIZE; + +	resources = (resource_t*)sci_calloc(resources_nr, sizeof(resource_t)); +	/* Sets valid default values for most entries */ + +	do { +		int read_ok = read(fd, &buf, SCI0_RESMAP_ENTRIES_SIZE); +		next_entry = 1; + +		if (read_ok < 0 ) { +			sciprintf("Error while reading %s: ", map->location.file.name); +			perror(""); +			next_entry = 0; +		} else if (read_ok != SCI0_RESMAP_ENTRIES_SIZE) { +			next_entry = 0; +		} else if (buf[5] == 0xff) /* Most significant offset byte */ +			next_entry = 0; + +		if (next_entry) { +			int fresh = 1; +			int addto = resource_index; +			int i; + +			if (sci_res_read_entry(mgr, map, buf, resources + resource_index, *sci_version)) { +				sci_free(resources); +				close(fd); +				return SCI_ERROR_RESMAP_NOT_FOUND; +			} + +			for (i = 0; i < resource_index; i++) +				if (resources[resource_index].id == +				    resources[i].id) { +					addto = i; +					fresh = 0; +				} + +			_scir_add_altsource(resources + addto, +					    resources[resource_index].source, +					    resources[resource_index].file_offset); + +			if (fresh) +				++resource_index; + +			if (++resources_total_read >= resources_nr) { +				sciprintf("Warning: After %d entries, resource.map" +					  " is not terminated!\n", resource_index); +				next_entry = 0; +			} + +		} + +	} while (next_entry); + +	close(fd); + +	if (!resource_index) { +		sciprintf("resource.map was empty!\n"); +		_scir_free_resources(resources, resources_nr); +		return SCI_ERROR_RESMAP_NOT_FOUND; +	} + +	if (max_resfile_nr > 999) { +		_scir_free_resources(resources, resources_nr); +		return SCI_ERROR_INVALID_RESMAP_ENTRY; +	} else { +#if 0 +/* Check disabled, Mac SQ3 thinks it has resource.004 but doesn't need it -- CR */ +		/* Check whether the highest resfile used exists */ +		char filename_buf[14]; +		sprintf(filename_buf, "resource.%03d", max_resfile_nr); +		fd = sci_open(filename_buf, O_RDONLY); + +		if (!IS_VALID_FD(fd)) { +			_scir_free_resources(resources, resources_nr); +			sciprintf("'%s' requested by resource.map, but not found\n", filename_buf); +			return SCI_ERROR_INVALID_RESMAP_ENTRY; +		} else +			close(fd); +#endif +	} + +	if (resource_index < resources_nr) +		resources = (resource_t*)sci_realloc(resources, sizeof(resource_t) * resource_index); + +	*resource_p = resources; +	*resource_nr_p = resource_index; + +	return 0; +} + +#define TEST fprintf(stderr, "OK in line %d\n", __LINE__); + +static int sci10_or_11(int *types) +{ +	int this_restype = 0; +	int next_restype = 1; + +	while (next_restype <= sci_heap) +	{ +		int could_be_10 = 0; +		int could_be_11 = 0; + +		while (types[this_restype] == 0)  +		{ +			this_restype++; +			next_restype++; +		} + +		while (types[next_restype] == 0)  +			next_restype++; + +		could_be_10 = ((types[next_restype] - types[this_restype]) +				 % SCI1_RESMAP_ENTRIES_SIZE) == 0; +		could_be_11 = ((types[next_restype] - types[this_restype]) +				 % SCI11_RESMAP_ENTRIES_SIZE) == 0; + +		if (could_be_10 && !could_be_11) return SCI_VERSION_1; +		if (could_be_11 && !could_be_10) return SCI_VERSION_1_1; + +		this_restype++; +		next_restype++; +	} + +	return SCI_VERSION_AUTODETECT; +} + +int +sci1_read_resource_map(resource_mgr_t *mgr, resource_source_t *map, resource_source_t *vol, +		       resource_t **resource_p, int *resource_nr_p, int *sci_version) +{ +	int fsize; +	int fd; +	resource_t *resources, *resource_start; +	int resources_nr; +	int resource_index = 0; +	int ofs, header_size; +	int *types = (int*)sci_malloc(sizeof(int) * (sci1_last_resource+1)); +	int i; +	byte buf[SCI1_RESMAP_ENTRIES_SIZE]; +	int lastrt; +	int entrysize; +	int entry_size_selector; + +	fd = sci_open(map->location.file.name, O_RDONLY | O_BINARY); + +	if (!IS_VALID_FD(fd)) +		return SCI_ERROR_RESMAP_NOT_FOUND; + +	memset(types, 0, sizeof(int) * (sci1_last_resource + 1)); + +	if (!(sci1_parse_header(fd, types, &lastrt))) +	{ +		close(fd); +		return SCI_ERROR_INVALID_RESMAP_ENTRY; +	} + +	entry_size_selector = sci10_or_11(types); +	if (*sci_version == SCI_VERSION_AUTODETECT) +		*sci_version = entry_size_selector; + +	if (*sci_version == SCI_VERSION_AUTODETECT) /* That didn't help */ +	{ +		sciprintf("Unable to detect resource map version\n"); +		close(fd); +		return SCI_ERROR_NO_RESOURCE_FILES_FOUND; +	} + +	entrysize = entry_size_selector == SCI_VERSION_1_1  +	  ? SCI11_RESMAP_ENTRIES_SIZE +	  : SCI1_RESMAP_ENTRIES_SIZE; +						        +	if ((fsize = sci_fd_size(fd)) < 0) { +		perror("Error occured while trying to get filesize of resource.map"); +		close(fd); +		return SCI_ERROR_RESMAP_NOT_FOUND; +	} + +	resources_nr = (fsize - types[0]) / entrysize; +	resource_start = resources = (resource_t*)sci_realloc(mgr->resources, (mgr->resources_nr + resources_nr)*sizeof(resource_t)); +	resources += mgr->resources_nr; + +	i = 0; +	while (types[i] == 0) i++; +	header_size = ofs = types[i]; + +	lseek(fd, ofs, SEEK_SET); + +	for (i=0; i<resources_nr; i++) +	{	 +		int read_ok = read(fd, &buf, entrysize);	 +		int j; +		resource_t *res; +		int addto = resource_index; +		int fresh = 1; + +		if (read_ok < entrysize) +		{ +#if 0 +			if (!eof(fd)) +			{ +				sciprintf("Error while reading %s: ", map->location.file.name); +				perror(""); +			} else read_ok = 1; +			break; +#endif +		} + +		res = &(resources[resource_index]); +		res->type = sci1_res_type(ofs, types, lastrt); +		res->number= SCI1_RESFILE_GET_NUMBER(buf); +		res->status = SCI_STATUS_NOMALLOC; + +		if (entry_size_selector < SCI_VERSION_1_1) +		{ +			res->source = scir_get_volume(mgr, map, SCI1_RESFILE_GET_FILE(buf)); +			res->file_offset = SCI1_RESFILE_GET_OFFSET(buf); +		} else +		{ +			res->source = vol;  +			res->file_offset = SCI11_RESFILE_GET_OFFSET(buf); +		}; +		 +		res->id = res->number | (res->type << 16); + +		for (j = 0; i < resource_index; i++) +			if (resources[resource_index].id == +			    resources[i].id) { +				addto = i; +				fresh = 0; +			} + +#if 0 +		fprintf(stderr, "Read [%04x] %6d.%s\tresource.%03d, %08x ==> %d\n", +			res->id, res->number, +			sci_resource_type_suffixes[res->type], +			res->file, res->file_offset, addto); +#endif + +		_scir_add_altsource(resources + addto, +				    resources[resource_index].source, +				    resources[resource_index].file_offset); +		 +		if (fresh) +			++resource_index; + +		ofs += entrysize; +	} + +	close(fd); +	free(types); + +	*resource_p = resource_start; +	*resource_nr_p += resource_index; +	return 0; +		 +} + +#ifdef TEST_RESOURCE_MAP +int +main(int argc, char **argv) +{ +	int resources_nr; +	resource_t *resources; +	int notok = sci0_read_resource_map(".", &resources, &resources_nr); + +	if (notok) { +		fprintf(stderr,"Failed: Error code %d\n",notok); +		return 1; +	} + +	if (resources) { +		int i; + +		printf("Found %d resources:\n", resources_nr); + +		for (i = 0; i < resources_nr; i++) { +			resource_t *res = resources + i; + +			printf("#%04d:\tRESOURCE.%03d:%8d\t%s.%03d\n", +			       i, res->file, res->file_offset, +			       sci_resource_types[res->type], +			       res->number); +		} +	} else +		fprintf(stderr, "Found no resources.\n"); + +	return 0; +} +#endif diff --git a/engines/sci/scicore/resource_patch.c b/engines/sci/scicore/resource_patch.c new file mode 100644 index 0000000000..3de384e25b --- /dev/null +++ b/engines/sci/scicore/resource_patch.c @@ -0,0 +1,224 @@ +/*************************************************************************** + resource_patch.c  Copyright (C) 2001 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + +    Christoph Reichenbach (CR) <jameson@linuxgames.com> + +***************************************************************************/ + + +#include <sciresource.h> +#include <sci_memory.h> + + +void +sci0_sprintf_patch_file_name(char *string, resource_t *res) +{ +	sprintf(string, "%s.%03i", sci_resource_types[res->type], res->number); +} + +void +sci1_sprintf_patch_file_name(char *string, resource_t *res) +{ +	sprintf(string, "%d.%s", res->number, sci_resource_type_suffixes[res->type]); +} + +/* version-agnostic patch application */ +static void +process_patch(resource_source_t *source, +	      char *entry, int restype, int resnumber, resource_t **resource_p, int *resource_nr_p) +{ +	int fsize; +	char filename[PATH_MAX]; + +	if (restype == sci_invalid_resource) +		return; + +	printf("Patching \"%s\": ", entry); + +	sprintf(filename, "%s%s", source->location.dir.name, entry); +	fsize = sci_file_size(filename); +	if (fsize < 0) +		perror("""__FILE__"": (""__LINE__""): sci_file_size()"); +	else { +		int file; +		guint8 filehdr[2]; +		resource_t *newrsc = _scir_find_resource_unsorted(*resource_p, +								  *resource_nr_p, +								  restype, +								  resnumber); + +		if (fsize < 3) { +			printf("File too small\n"); +			return; +		} + +		file = open(entry, O_RDONLY); +		if (!IS_VALID_FD(file)) +			perror("""__FILE__"": (""__LINE__""): open()"); +		else { +			int patch_data_offset; + +			read(file, filehdr, 2); + +			patch_data_offset = filehdr[1]; + +			if ((filehdr[0] & 0x7f) != restype) { +				printf("Failed; resource type mismatch\n"); +				close(file); +			} else if (patch_data_offset + 2 >= fsize) { +				printf("Failed; patch starting at offset %d can't be in file of size %d\n", filehdr[1] + 2, fsize); +				close(file); +			} else { +				/* Adjust for file offset */ +				fsize -= patch_data_offset; + +				/* Prepare destination, if neccessary */ +				if (!newrsc) { +					/* Completely new resource! */ +					++(*resource_nr_p); +					*resource_p = (resource_t*)sci_realloc(*resource_p, +									       *resource_nr_p +									       * sizeof(resource_t)); +					newrsc = (*resource_p-1) + *resource_nr_p; +					newrsc->alt_sources = NULL; +				} + +				/* Overwrite everything, because we're patching */ +				newrsc->size = fsize - 2; +				newrsc->id = restype << 11 | resnumber; +				newrsc->number = resnumber; +				newrsc->status = SCI_STATUS_NOMALLOC; +				newrsc->type = restype; +				newrsc->source = source; +				newrsc->file_offset = 2 + patch_data_offset; + +				_scir_add_altsource(newrsc, source, 2); + +				close(file); + +				printf("OK\n"); + +			} +		} +	} +} + + +int +sci0_read_resource_patches(resource_source_t *source, resource_t **resource_p, int *resource_nr_p) +{ +	sci_dir_t dir; +	char *entry; +	char *caller_cwd = sci_getcwd(); + +	chdir(source->location.dir.name); +	sci_init_dir(&dir); +	entry = sci_find_first(&dir, "*.???"); +	while (entry) { +		int restype = sci_invalid_resource; +		int resnumber = -1; +		int i; +		unsigned int resname_len; +		char *endptr; + +		for (i = sci_view; i < sci_invalid_resource; i++) +			if (strncasecmp(sci_resource_types[i], entry, +					strlen(sci_resource_types[i])) == 0) +				restype = i; + +		if (restype != sci_invalid_resource) { + +			resname_len = strlen(sci_resource_types[restype]); +			if (entry[resname_len] != '.') +				restype = sci_invalid_resource; +			else { +				resnumber = strtol(entry + 1 + resname_len, +						   &endptr, 10); /* Get resource number */ +				if ((*endptr != '\0') || (resname_len+1 == strlen(entry))) +					restype = sci_invalid_resource; + +				if ((resnumber < 0) || (resnumber > 1000)) +					restype = sci_invalid_resource; +			} +		} + +		process_patch (source, entry, restype, resnumber, resource_p, resource_nr_p); + +		entry = sci_find_next(&dir); +	} + +	chdir(caller_cwd); +	free(caller_cwd); +	return 0; +} + +int +sci1_read_resource_patches(resource_source_t *source, resource_t **resource_p, int *resource_nr_p) +{ +	sci_dir_t dir; +	char *entry; +	char *caller_cwd = sci_getcwd(); + +	chdir(source->location.dir.name); +	sci_init_dir(&dir); +	entry = sci_find_first(&dir, "*.*"); +	while (entry) { +		int restype = sci_invalid_resource; +		int resnumber = -1; +		int i; +		char *endptr; +		char *dot = strchr(entry, '.'); + +		for (i = sci_view; i < sci_invalid_resource; i++) { +			if (dot != NULL) { +				if (strncasecmp(sci_resource_type_suffixes[i], dot+1, 3) == 0) { +					restype = i; +				} +			} +		} + +		if (restype != sci_invalid_resource) { + +		  resnumber = strtol(entry, +				     &endptr, 10); /* Get resource number */ + +		  if (endptr != dot) +			  restype = sci_invalid_resource; + +		  if (*(dot + 4) != '\0') +			  restype = sci_invalid_resource; +		   +		  if ((resnumber < 0) || (resnumber > 8192)) +			  restype = sci_invalid_resource; +		} + +		process_patch (source, entry, restype, resnumber, resource_p, resource_nr_p); + +		entry = sci_find_next(&dir); +	} + +	chdir(caller_cwd); +	free(caller_cwd); +	return 0; +} + diff --git a/engines/sci/scicore/resourcecheck.c b/engines/sci/scicore/resourcecheck.c new file mode 100644 index 0000000000..f4c731b01f --- /dev/null +++ b/engines/sci/scicore/resourcecheck.c @@ -0,0 +1,38 @@ +/*************************************************************************** + resourcecheck.c (C) 1999 Christoph Reichenbach, TU Darmstadt + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + +    Christoph Reichenbach (CJR) [creichen@rbg.informatik.tu-darmstadt.de] + + History: + +   990403 - created (CJR) + +***************************************************************************/ + +/* #include "resource.h" */ + +int main() +{ +  /* No checks yet... */ +  return 0; +} diff --git a/engines/sci/scicore/sci_dos.c b/engines/sci/scicore/sci_dos.c new file mode 100644 index 0000000000..0435afa32b --- /dev/null +++ b/engines/sci/scicore/sci_dos.c @@ -0,0 +1,219 @@ +/*************************************************************************** + sci_dos.c Copyright (C) 1999 Rink Springer + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + +    Rink Springer [rink@springer.cx] + +***************************************************************************/ + +#include <stdlib.h> +#include <sci_dos.h> +#include <string.h> + +#define G_VA_COPY(ap1, ap2)     ((ap1) = (ap2)) + +gpointer +malloc0(guint32 size) { +    char* ptr; + +    /* allocate the buffer, return NULL if no buffer */ +    if((ptr= sci_malloc(size))==NULL) return NULL; + +    /* clear it to zeros */ +    memset(ptr,0,size); + +    /* return the pointer */ +    return ptr; +} + +guint +g_printf_string_upper_bound (const gchar* format, +                 va_list      args) +{ +  guint len = 1; + +  while (*format) +    { +      gboolean long_int = FALSE; +      gboolean extra_long = FALSE; +      gchar c; + +      c = *format++; + +      if (c == '%') +    { +      gboolean done = FALSE; + +      while (*format && !done) +        { +          switch (*format++) +        { +          gchar *string_arg; + +        case '*': +          len += va_arg (args, int); +          break; +        case '1': +        case '2': +        case '3': +        case '4': +        case '5': +        case '6': +        case '7': +        case '8': +        case '9': +          /* add specified format length, since it might exceed the +           * size we assume it to have. +           */ +          format -= 1; +          len += strtol (format, (char**) &format, 10); +          break; +        case 'h': +          /* ignore short int flag, since all args have at least the +           * same size as an int +           */ +          break; +        case 'l': +          if (long_int) +            extra_long = TRUE; /* linux specific */ +          else +            long_int = TRUE; +          break; +        case 'q': +        case 'L': +          long_int = TRUE; +          extra_long = TRUE; +          break; +        case 's': +          string_arg = va_arg (args, char *); +          if (string_arg) +            len += strlen (string_arg); +          else +            { +              /* add enough padding to hold "(null)" identifier */ +              len += 16; +            } +          done = TRUE; +          break; +        case 'd': +        case 'i': +        case 'o': +        case 'u': +        case 'x': +        case 'X': +            { +              if (long_int) +            (void) va_arg (args, long); +              else +            (void) va_arg (args, int); +            } +          len += extra_long ? 64 : 32; +          done = TRUE; +          break; +        case 'D': +        case 'O': +        case 'U': +          (void) va_arg (args, long); +          len += 32; +          done = TRUE; +          break; +        case 'e': +        case 'E': +        case 'f': +        case 'g': +            (void) va_arg (args, double); +          len += extra_long ? 64 : 32; +          done = TRUE; +          break; +        case 'c': +          (void) va_arg (args, int); +          len += 1; +          done = TRUE; +          break; +        case 'p': +        case 'n': +          (void) va_arg (args, void*); +          len += 32; +          done = TRUE; +          break; +        case '%': +          len += 1; +          done = TRUE; +          break; +        default: +          /* ignore unknow/invalid flags */ +          break; +        } +        } +    } +      else +    len += 1; +    } + +return len; +} + +gchar* +g_strdup_vprintf (const gchar *format, +                  va_list      args1) { +  gchar *buffer; +  va_list args2; + +  G_VA_COPY (args2, args1); + +  buffer = g_new (gchar, g_printf_string_upper_bound (format, args1)); + +  vsprintf (buffer, format, args2); +  va_end (args2); + +  return buffer; +} + +gint +g_vsnprintf (gchar       *str, +             gulong       n, +             gchar const *fmt, +             va_list      args) { +  gchar* printed; + + +  printed = g_strdup_vprintf (fmt, args); +  strncpy (str, printed, n); +  str[n-1] = '\0'; + +  free (printed); + +  return strlen (str); +} + +gpointer +g_memdup (gpointer mem, guint byte_size) { +  gpointer new_mem; + +  if (mem) { +    new_mem = sci_malloc (byte_size); +    memcpy (new_mem, mem, byte_size); +  } else { +    new_mem = NULL; +  } + +  return new_mem; +} diff --git a/engines/sci/scicore/sci_memory.c b/engines/sci/scicore/sci_memory.c new file mode 100644 index 0000000000..ba666e1ceb --- /dev/null +++ b/engines/sci/scicore/sci_memory.c @@ -0,0 +1,311 @@ +/*************************************************************************** + sci_memory.c Copyright (C) 2001 Alexander R Angas +			Refcounted memory by Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + +    Alexander R Angas (Alex Angas) <wgd@internode.on.net> + + History: + +   20010815 - assembled from the various memory allocation functions lying +              about, namely console.c (extra dmalloc stuff), menubar.c +              (for malloc_cpy -> strdup, malloc_ncpy -> strndup). +                -- Alex Angas + +***************************************************************************/ + +#include <sci_memory.h> + +/*#define POISON_MEMORY*/ + +/* set optimisations for Win32: */ +/* g on: enable global optimizations */ +/* t on: use fast code */ +/* y on: suppress creation of frame pointers on stack */ +/* s off: disable minimize size code */ + +#ifdef _MSC_VER +#	include <crtdbg.h> +#	ifndef SATISFY_PURIFY +#		pragma optimize( "s", off ) +#		pragma optimize( "gty", on ) +#		pragma intrinsic( memcpy, strlen ) +#	endif +#endif + + +void * +_SCI_MALLOC(size_t size, const char *file, int line, const char *funct) +{ +	void *res; +#ifdef MALLOC_DEBUG +	INFO_MEMORY("_SCI_MALLOC()", size, file, line, funct); +#endif +	ALLOC_MEM((res = malloc(size)), size, file, line, funct) +#ifdef POISON_MEMORY +	{ +		memset(res, 0xa5, size); +	} +#endif +	return res; +} + + +void * +_SCI_CALLOC(size_t num, size_t size, const char *file, int line, const char *funct) +{ +	void *res; +#ifdef MALLOC_DEBUG +	INFO_MEMORY("_SCI_CALLOC()", size, file, line, funct); +#endif +	ALLOC_MEM((res = calloc(num, size)), num * size, file, line, funct) +	return res; +} + + +void * +_SCI_REALLOC(void *ptr, size_t size, const char *file, int line, const char *funct) +{ +	void *res; +#ifdef MALLOC_DEBUG +	INFO_MEMORY("_SCI_REALLOC()", size, file, line, funct); +#endif +	ALLOC_MEM((res = realloc(ptr, size)), size, file, line, funct) +	return res; +} + + +void +_SCI_FREE(void *ptr, const char *file, int line, const char *funct) +{ +#ifdef MALLOC_DEBUG +	INFO_MEMORY("_SCI_FREE()", 0, file, line, funct); +#endif +	if (!ptr) +	{ +		fprintf(stderr, "_SCI_FREE() [%s (%s) : %u]\n", +			file, funct, line); +		fprintf(stderr, " attempt to free NULL pointer\n"); +		BREAKPOINT(); +	} +	free(ptr); +} + + +void * +_SCI_MEMDUP(const void *ptr, size_t size, const char *file, int line, const char *funct) +{ +	void *res; +#ifdef MALLOC_DEBUG +	INFO_MEMORY("_SCI_MEMDUP()", size, file, line, funct); +#endif +	if (!ptr) +	{ +		fprintf(stderr, "_SCI_MEMDUP() [%s (%s) : %u]\n", +			file, funct, line); +		fprintf(stderr, " attempt to memdup NULL pointer\n"); +		BREAKPOINT(); +	} +	ALLOC_MEM((res = malloc(size)), size, file, line, funct) +	memcpy(res, ptr, size); +	return res; +} + + +char * +_SCI_STRDUP(const char *src, const char *file, int line, const char *funct) +{ +	void *res; +#ifdef MALLOC_DEBUG +	INFO_MEMORY("_SCI_STRDUP()", 0, file, line, funct); +#endif +	if (!src) +	{ +		fprintf(stderr, "_SCI_STRDUP() [%s (%s) : %u]\n", +			file, funct, line); +		fprintf(stderr, " attempt to strdup NULL pointer\n"); +		BREAKPOINT(); +	} +	ALLOC_MEM((res = strdup(src)), strlen(src), file, line, funct) +	return (char*)res; +} + + +char * +_SCI_STRNDUP(const char *src, size_t length, const char *file, int line, const char *funct) +{ +	void *res; +	char *strres; +	size_t rlen = (int)MIN(strlen(src), length) + 1; +#ifdef MALLOC_DEBUG +	INFO_MEMORY("_SCI_STRNDUP()", 0, file, line, funct); +#endif +	if (!src) +	{ +		fprintf(stderr, "_SCI_STRNDUP() [%s (%s) : %u]\n", +			file, funct, line); +		fprintf(stderr, " attempt to strndup NULL pointer\n"); +		BREAKPOINT(); +	} +	ALLOC_MEM((res = malloc(rlen)), rlen, file, line, funct) + +	strres = (char*)res; +	strncpy(strres, src, rlen); +	strres[rlen - 1] = 0; + +	return strres; +} + + +/********** Win32 functions **********/ + +#ifdef _MSC_VER +void +debug_win32_memory(int dbg_setting) +{ +#if defined(NDEBUG) +	fprintf(stderr, +		"WARNING: Cannot debug Win32 memory in release mode.\n"); +#elif defined(SATISFY_PURIFY) +	fprintf(stderr, +		"WARNING: Cannot debug Win32 memory in this mode.\n"); +#else + +	int tmpFlag; + +	tmpFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + +	if (dbg_setting > 0) +		tmpFlag |= _CRTDBG_CHECK_ALWAYS_DF; +		/* call _CrtCheckMemory at every request */ + +	if (dbg_setting > 1) +        tmpFlag |= _CRTDBG_LEAK_CHECK_DF; +		/* perform automatic leak checking at program exit */ + +	if (dbg_setting > 2) +        tmpFlag |= _CRTDBG_DELAY_FREE_MEM_DF; +		/* enable debug heap allocations */ + +	if (dbg_setting > 3) +	{ +		PANIC((stderr, "Invalid value for debug_win32_memory!\n")); +		BREAKPOINT(); +	} + +	if (dbg_setting <= 0) +	{ +		/* turn off above */ +		tmpFlag &= ~_CRTDBG_CHECK_ALWAYS_DF; +        tmpFlag &= ~_CRTDBG_DELAY_FREE_MEM_DF; +        tmpFlag &= ~_CRTDBG_LEAK_CHECK_DF; +	} + +	/* set new state for flag */ +	_CrtSetDbgFlag( tmpFlag ); +#endif +} +#endif + + + +/*-------- Refcounting ----------*/ + +#define REFCOUNT_OVERHEAD (sizeof(guint32)*3) +#define REFCOUNT_MAGIC_LIVE_1 0xebdc1741 +#define REFCOUNT_MAGIC_LIVE_2 0x17015ac9 +#define REFCOUNT_MAGIC_DEAD_1 0x11dead11 +#define REFCOUNT_MAGIC_DEAD_2 0x22dead22 + +#define REFCOUNT_CHECK(p) ((((guint32 *)(p))[-3] == REFCOUNT_MAGIC_LIVE_2)    \ +			   && (((guint32 *)(p))[-1] == REFCOUNT_MAGIC_LIVE_1)) + +#define REFCOUNT(p) (((guint32 *)p)[-2]) + +#undef TRACE_REFCOUNT + + +extern void * +sci_refcount_alloc(size_t length) +{ +	guint32 *data = (guint32*)sci_malloc(REFCOUNT_OVERHEAD + length); +#ifdef TRACE_REFCOUNT +fprintf(stderr, "[] REF: Real-alloc at %p\n", data); +#endif +	data += 3; + +	data[-1] = REFCOUNT_MAGIC_LIVE_1; +	data[-3] = REFCOUNT_MAGIC_LIVE_2; +	REFCOUNT(data) = 1; +#ifdef TRACE_REFCOUNT +fprintf(stderr, "[] REF: Alloc'd %p (ref=%d) OK=%d\n", data, REFCOUNT(data), +	REFCOUNT_CHECK(data)); +#endif +	return data; +} + +extern void * +sci_refcount_incref(void *data) +{ +	if (!REFCOUNT_CHECK(data)) { +		BREAKPOINT(); +	} else +		REFCOUNT(data)++; + +#ifdef TRACE_REFCOUNT +fprintf(stderr, "[] REF: Inc'ing %p (now ref=%d)\n", data, REFCOUNT(data)); +#endif +	return data; +} + +extern void +sci_refcount_decref(void *data) +{ +#ifdef TRACE_REFCOUNT +fprintf(stderr, "[] REF: Dec'ing %p (prev ref=%d) OK=%d\n", data, REFCOUNT(data), +	REFCOUNT_CHECK(data)); +#endif +	if (!REFCOUNT_CHECK(data)) { +		BREAKPOINT(); +	} else if (--REFCOUNT(data) == 0) { +		guint32 *fdata = (guint32*)data; + +		fdata[-1] = REFCOUNT_MAGIC_DEAD_1; +		fdata[-3] = REFCOUNT_MAGIC_DEAD_2; + +#ifdef TRACE_REFCOUNT +fprintf(stderr, "[] REF: Freeing (%p)...\n", fdata - 3); +#endif +		sci_free(fdata - 3); +#ifdef TRACE_REFCOUNT +fprintf(stderr, "[] REF: Done.\n"); +#endif +	} +} + +extern void * +sci_refcount_memdup(void *data, size_t len) +{ +	void *dest = sci_refcount_alloc(len); +	memcpy(dest, data, len); +	return dest; +} diff --git a/engines/sci/scicore/script.c b/engines/sci/scicore/script.c new file mode 100644 index 0000000000..921e0fa50b --- /dev/null +++ b/engines/sci/scicore/script.c @@ -0,0 +1,488 @@ +/*************************************************************************** + script.c  Copyright (C) 2003 Magnus Reftel, Christoph Reichenbach + + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public Licence as + published by the Free Software Foundaton; either version 2 of the + Licence, or (at your option) any later version. + + It is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + merchantibility or fitness for a particular purpose. See the + GNU General Public Licence for more details. + + You should have received a copy of the GNU General Public Licence + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + + Please contact the maintainer for any program-related bug reports or + inquiries. + + Current Maintainer: + +    Christoph Reichenbach (CR) <jameson@linuxgames.com> + +***************************************************************************/ + +#include <sciresource.h> +#include <engine.h> + +/* #define SCRIPT_DEBUG */ + +#define END Script_None + +opcode_format formats[128][4]={ +  /*00*/ +  {Script_None}, {Script_None}, {Script_None}, {Script_None}, +  /*04*/ +  {Script_None}, {Script_None}, {Script_None}, {Script_None}, +  /*08*/ +  {Script_None}, {Script_None}, {Script_None}, {Script_None}, +  /*0C*/ +  {Script_None}, {Script_None}, {Script_None}, {Script_None}, +  /*10*/ +  {Script_None}, {Script_None}, {Script_None}, {Script_None}, +  /*14*/ +  {Script_None}, {Script_None}, {Script_None}, {Script_SRelative, END}, +  /*18*/ +  {Script_SRelative, END}, {Script_SRelative, END}, {Script_SVariable, END}, {Script_None}, +  /*1C*/ +  {Script_SVariable, END}, {Script_None}, {Script_None}, {Script_Variable, END}, +  /*20*/ +  {Script_SRelative, Script_Byte, END}, {Script_Variable, Script_Byte, END}, {Script_Variable, Script_Byte, END}, {Script_Variable, Script_SVariable, Script_Byte, END}, +  /*24 (24=ret)*/ +  {Script_End}, {Script_Byte, END}, {Script_Invalid}, {Script_Invalid}, +  /*28*/ +  {Script_Variable, END}, {Script_Invalid}, {Script_Byte, END}, {Script_Variable, Script_Byte, END}, +  /*2C*/ +  {Script_SVariable, END}, {Script_SVariable, Script_Variable, END}, {Script_None}, {Script_Invalid}, +  /*30*/ +  {Script_None}, {Script_Property, END}, {Script_Property, END}, {Script_Property, END}, +  /*34*/ +  {Script_Property, END}, {Script_Property, END}, {Script_Property, END}, {Script_Property, END}, +  /*38*/ +  {Script_Property, END}, {Script_SRelative, END}, {Script_SRelative, END}, {Script_None}, +  /*3C*/ +  {Script_None}, {Script_None}, {Script_None}, {Script_Invalid}, +  /*40-4F*/ +  {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, +  {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, +  {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, +  {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, +  /*50-5F*/ +  {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, +  {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, +  {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, +  {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, +  /*60-6F*/ +  {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, +  {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, +  {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, +  {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, +  /*70-7F*/ +  {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, +  {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, +  {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, +  {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END} +}; +#undef END + +void script_adjust_opcode_formats(int res_version) +{ +	switch (res_version) +	{ +		case SCI_VERSION_0: +		case SCI_VERSION_01: +			break; +		case SCI_VERSION_01_VGA: +		case SCI_VERSION_01_VGA_ODD: +		case SCI_VERSION_1_EARLY: +		case SCI_VERSION_1_LATE: +			formats[op_lofsa][0]=Script_Offset; +			formats[op_lofss][0]=Script_Offset; +			break; +		default: +			sciprintf("script_adjust_opcode_formats(): Unknown script version %d\n", res_version); +	} +} +	 +int +script_find_selector(state_t *s, const char *selectorname) +{ +  int i; +  for (i = 0; i < s->selector_names_nr; i++) +    if (strcmp(selectorname, s->selector_names[i]) == 0) +        return i; + +  sciprintf("Warning: Could not map '%s' to any selector!\n", selectorname); +  return -1; +} + +#define FIND_SELECTOR(_slc_, _slcstr_) map->_slc_ = script_find_selector(s, _slcstr_); + +void +script_map_selectors(state_t *s, selector_map_t *map) +{ +  map->init = script_find_selector(s, "init"); +  map->play = script_find_selector(s, "play"); +  FIND_SELECTOR(replay, "replay"); +  map->x = script_find_selector(s, "x"); +  map->y = script_find_selector(s, "y"); +  map->z = script_find_selector(s, "z"); +  map->priority = script_find_selector(s, "priority"); +  map->view = script_find_selector(s, "view"); +  map->loop = script_find_selector(s, "loop"); +  map->cel = script_find_selector(s, "cel"); +  FIND_SELECTOR(brLeft, "brLeft"); +  FIND_SELECTOR(brRight, "brRight"); +  FIND_SELECTOR(brTop, "brTop"); +  FIND_SELECTOR(brBottom, "brBottom"); +  FIND_SELECTOR(xStep, "xStep"); +  FIND_SELECTOR(yStep, "yStep"); +  FIND_SELECTOR(nsBottom, "nsBottom"); +  FIND_SELECTOR(nsTop, "nsTop"); +  FIND_SELECTOR(nsLeft, "nsLeft"); +  FIND_SELECTOR(nsRight, "nsRight"); +  FIND_SELECTOR(font, "font"); +  FIND_SELECTOR(text, "text"); +  FIND_SELECTOR(type, "type"); +  FIND_SELECTOR(state, "state"); +  FIND_SELECTOR(doit, "doit"); +  FIND_SELECTOR(delete, "delete"); +  FIND_SELECTOR(signal, "signal"); +  FIND_SELECTOR(underBits, "underBits"); +  FIND_SELECTOR(canBeHere, "canBeHere"); +  FIND_SELECTOR(client, "client"); +  FIND_SELECTOR(dx, "dx"); +  FIND_SELECTOR(dy, "dy"); +  FIND_SELECTOR(xStep, "xStep"); +  FIND_SELECTOR(yStep, "yStep"); +  FIND_SELECTOR(b_movCnt, "b-moveCnt"); +  FIND_SELECTOR(b_i1, "b-i1"); +  FIND_SELECTOR(b_i2, "b-i2"); +  FIND_SELECTOR(b_di, "b-di"); +  FIND_SELECTOR(b_xAxis, "b-xAxis"); +  FIND_SELECTOR(b_incr, "b-incr"); +  FIND_SELECTOR(completed, "completed"); +  FIND_SELECTOR(illegalBits, "illegalBits"); +  FIND_SELECTOR(dispose, "dispose"); +  FIND_SELECTOR(prevSignal, "prevSignal"); +  FIND_SELECTOR(message, "message"); +  FIND_SELECTOR(modifiers, "modifiers"); +  FIND_SELECTOR(cue, "cue"); +  FIND_SELECTOR(owner, "owner"); +  FIND_SELECTOR(handle, "handle"); +  FIND_SELECTOR(number, "number"); +  FIND_SELECTOR(max, "max"); +  FIND_SELECTOR(cursor, "cursor"); +  FIND_SELECTOR(claimed, "claimed"); +  FIND_SELECTOR(edgeHit, "edgeHit"); +  FIND_SELECTOR(wordFail, "wordFail"); +  FIND_SELECTOR(syntaxFail, "syntaxFail"); +  FIND_SELECTOR(semanticFail, "semanticFail"); +  FIND_SELECTOR(cycler, "cycler"); +  FIND_SELECTOR(elements, "elements"); +  FIND_SELECTOR(lsTop, "lsTop"); +  FIND_SELECTOR(lsBottom, "lsBottom"); +  FIND_SELECTOR(lsLeft, "lsLeft"); +  FIND_SELECTOR(lsRight, "lsRight"); +  FIND_SELECTOR(baseSetter, "baseSetter"); +  FIND_SELECTOR(who, "who"); +  FIND_SELECTOR(distance, "distance"); +  FIND_SELECTOR(mover, "mover"); +  FIND_SELECTOR(looper, "looper"); +  FIND_SELECTOR(isBlocked, "isBlocked"); +  FIND_SELECTOR(heading, "heading"); +  FIND_SELECTOR(mode, "mode"); +  FIND_SELECTOR(caller, "caller"); +  FIND_SELECTOR(moveDone, "moveDone"); +  FIND_SELECTOR(vol, "vol"); +  FIND_SELECTOR(pri, "pri"); +  FIND_SELECTOR(min, "min"); +  FIND_SELECTOR(sec, "sec"); +  FIND_SELECTOR(frame, "frame"); +  FIND_SELECTOR(dataInc, "dataInc"); +  FIND_SELECTOR(size, "size"); +  FIND_SELECTOR(palette, "palette"); +  FIND_SELECTOR(moveSpeed, "moveSpeed"); +  FIND_SELECTOR(cantBeHere, "cantBeHere"); +  FIND_SELECTOR(nodePtr, "nodePtr"); +  FIND_SELECTOR(flags, "flags"); +  FIND_SELECTOR(points, "points"); +} + +int +sci_hexdump(byte *data, int length, int offsetplus) +{ +  char tempstr[40]; +  int i; +  for (i = 0; i < length; i+= 8) { +    int j; + +    sprintf(tempstr, "%04x:                                 ", i + offsetplus); +    for (j = 0; j < MIN(8, length - i); j++) +      sprintf(tempstr + 6 + (j*3) + (j > 3), "%02x  ", data[i+j]); +    for (j = 0; j < MIN(8, length - i); j++) { +      int thechar; +      thechar = data[i+j]; +      sprintf(tempstr + 31 + j, "%c", +	      ((thechar < ' ') || (thechar > 127))? '.' : thechar ); +    } + +    for (j = 0; j < 38; j++) +      if (!tempstr[j]) +	tempstr[j] = ' '; /* get rid of sprintf's \0s */ + +    sciprintf("%s\n", tempstr); +  } +  return 0; +} + +static void +script_dump_object(char *data, int seeker, int objsize, char **snames, int snames_nr) +{ +  int selectors, overloads, selectorsize; +  int species = getInt16((unsigned char *) data + 8 + seeker); +  int superclass = getInt16((unsigned char *) data + 10 + seeker); +  int namepos = getInt16((unsigned char *) data + 14 + seeker); +  int i = 0; + +  sciprintf("Object\n"); + +  sci_hexdump((unsigned char *) data + seeker, objsize -4, seeker); +  /*-4 because the size includes the two-word header */ + +  sciprintf("Name: %s\n", namepos? ((char *)(data + namepos)) : "<unknown>"); +  sciprintf("Superclass: %x\n", superclass); +  sciprintf("Species: %x\n", species); +  sciprintf("-info-:%x\n", getInt16((unsigned char *) data + 12 + seeker) & 0xffff); + +  sciprintf("Function area offset: %x\n", getInt16((unsigned char *) data + seeker + 4)); +  sciprintf("Selectors [%x]:\n", +	    selectors = (selectorsize = getInt16((unsigned char *) data + seeker + 6))); + +  seeker += 8; + +  while (selectors--) { +    sciprintf("  [#%03x] = 0x%x\n", i++, getInt16((unsigned char *) data + seeker) & 0xffff); + +    seeker += 2; +  } + + +  sciprintf("Overridden functions: %x\n", selectors = +	    overloads = getInt16((unsigned char *) data + seeker)); + +  seeker += 2; + +  if (overloads < 100) +  while (overloads--) { +    int selector = getInt16((unsigned char *) data + (seeker)); + +    sciprintf("  [%03x] %s: @", selector & 0xffff, +	      (snames && selector >= 0 && selector < snames_nr)? snames[selector] : "<?>"); +    sciprintf("%04x\n", getInt16((unsigned char *) data + seeker + selectors*2 + 2) & 0xffff); + +    seeker += 2; +  } +} + +static void +script_dump_class(char *data, int seeker, int objsize, char **snames, int snames_nr) +{ +  int selectors, overloads, selectorsize; +  int species = getInt16((unsigned char *) data + 8 + seeker); +  int superclass = getInt16((unsigned char *) data + 10 + seeker); +  int namepos = getInt16((unsigned char *) data + 14 + seeker); + +  sciprintf("Class\n"); + +  sci_hexdump((unsigned char *) data + seeker, objsize -4, seeker); + +  sciprintf("Name: %s\n", namepos? ((char *)data + namepos) : "<unknown>"); +  sciprintf("Superclass: %x\n", superclass); +  sciprintf("Species: %x\n", species); +  sciprintf("-info-:%x\n", getInt16((unsigned char *) data + 12 + seeker) & 0xffff); + +  sciprintf("Function area offset: %x\n", getInt16((unsigned char *) data + seeker + 4)); +  sciprintf("Selectors [%x]:\n", +	    selectors = (selectorsize = getInt16((unsigned char *) data + seeker + 6))); + +  seeker += 8; +  selectorsize <<= 1; + +  while (selectors--) { +    int selector = getInt16((unsigned char *) data + (seeker) + selectorsize); + +    sciprintf("  [%03x] %s = 0x%x\n", 0xffff & selector, +	      (snames && selector >= 0 && selector < snames_nr)? snames[selector] : "<?>", +	      getInt16((unsigned char *) data + seeker) & 0xffff); + +    seeker += 2; +  } + +  seeker += selectorsize; + +  sciprintf("Overloaded functions: %x\n", selectors = +	    overloads = getInt16((unsigned char *) data + seeker)); + +  seeker += 2; + +  while (overloads--) { +    int selector = getInt16((unsigned char *) data + (seeker)); +    fprintf(stderr,"selector=%d; snames_nr =%d\n", selector, snames_nr); +    sciprintf("  [%03x] %s: @", selector & 0xffff, +	      (snames && selector >= 0 && selector < snames_nr)? +	      snames[selector] : "<?>"); +    sciprintf("%04x\n", getInt16((unsigned char *) data + seeker + selectors*2 + 2) & 0xffff); + +    seeker += 2; +  } +} + + +void +script_dissect(resource_mgr_t *resmgr, int res_no, char **snames, int snames_nr) +{ +  int objectctr[11] = {0,0,0,0,0,0,0,0,0,0,0}; +  unsigned int _seeker = 0; +  resource_t *script = scir_find_resource(resmgr, sci_script, res_no, 0); +  word_t **words; +  int word_count; + +  if (!script) { +    sciprintf("Script not found!\n"); +    return; +  } + +  words=vocab_get_words (resmgr, &word_count); + +  while (_seeker < script->size) { +    int objtype = getInt16(script->data + _seeker); +    int objsize; +    int seeker = _seeker + 4; + +    if (!objtype) { +      sciprintf("End of script object (#0) encountered.\n"); +      sciprintf("Classes: %i, Objects: %i, Export: %i,\n Var: %i (all base 10)", +		objectctr[6], objectctr[1], objectctr[7], objectctr[10]); +      /*vocabulary_free_snames(snames);*/ +      vocab_free_words (words, word_count); +      return; +    } + +    sciprintf("\n"); + +    objsize = getInt16(script->data + _seeker + 2); + +    sciprintf("Obj type #%x, size 0x%x: ", objtype, objsize); + +    _seeker += objsize; + +    objectctr[objtype]++; + +    switch (objtype) { +    case sci_obj_object: +      script_dump_object ((char *) script->data, seeker, objsize, snames, snames_nr); +      break; + +    case sci_obj_code: { +      sciprintf("Code\n"); +      sci_hexdump(script->data + seeker, objsize -4, seeker); +    }; +    break; + +    case 3: { +      sciprintf("<unknown>\n"); +      sci_hexdump(script->data + seeker, objsize -4, seeker); +    }; +    break; + +    case sci_obj_said: { +      sciprintf("Said\n"); +      sci_hexdump(script->data + seeker, objsize -4, seeker); + +      sciprintf ("%04x: ", seeker); +      while (seeker < _seeker) +      { +        unsigned char nextitem=script->data [seeker++]; +        if (nextitem == 0xFF) +          sciprintf ("\n%04x: ", seeker); +        else if (nextitem >= 0xF0) +        { +          switch (nextitem) +          { +          case 0xf0: sciprintf(", "); break; +          case 0xf1: sciprintf("& "); break; +          case 0xf2: sciprintf("/ "); break; +          case 0xf3: sciprintf("( "); break; +          case 0xf4: sciprintf(") "); break; +          case 0xf5: sciprintf("[ "); break; +          case 0xf6: sciprintf("] "); break; +          case 0xf7: sciprintf("# "); break; +          case 0xf8: sciprintf("< "); break; +          case 0xf9: sciprintf("> "); break; +          } +        } +        else { +          nextitem = nextitem << 8 | script->data [seeker++]; +          sciprintf ("%s[%03x] ", vocab_get_any_group_word (nextitem, words, word_count), nextitem); +        } +      } +      sciprintf ("\n"); +    } +    break; + +    case sci_obj_strings: { +      sciprintf("Strings\n"); +      while (script->data [seeker]) +      { +        sciprintf ("%04x: %s\n", seeker, script->data+seeker); +        seeker += strlen ((char *) script->data+seeker)+1; +      } +      seeker++; /* the ending zero byte */ +    }; +    break; + +    case sci_obj_class: +      script_dump_class ((char *) script->data, seeker, objsize, snames, snames_nr); +      break; + +    case sci_obj_exports: { +      sciprintf("Exports\n"); +      sci_hexdump((unsigned char *) script->data + seeker, objsize -4, seeker); +    }; +    break; + +    case sci_obj_pointers: { +      sciprintf("Pointers\n"); +      sci_hexdump(script->data + seeker, objsize -4, seeker); +    }; +    break; + +    case 9: { +      sciprintf("<unknown>\n"); +      sci_hexdump(script->data + seeker, objsize -4, seeker); +    }; +    break; + +    case sci_obj_localvars: { +      sciprintf("Local vars\n"); +      sci_hexdump(script->data + seeker, objsize -4, seeker); +    }; +    break; + +    default: +      sciprintf("Unsupported!\n"); +      return; +    } + +  } + +  sciprintf("Script ends without terminator\n"); + +  /*vocabulary_free_snames(snames);*/ +} diff --git a/engines/sci/scicore/tools.c b/engines/sci/scicore/tools.c new file mode 100644 index 0000000000..4194a44047 --- /dev/null +++ b/engines/sci/scicore/tools.c @@ -0,0 +1,780 @@ +/*************************************************************************** + tools.c Copyright (C) 1999,2000,2001,2002 Christoph Reichenbach + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + +		Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ + +#define _GNU_SOURCE /* For FNM_CASEFOLD in fnmatch.h */ + +#include <stdlib.h> +#include <engine.h> + +#ifdef HAVE_SYS_TIME_H +#  include <sys/time.h> +#endif +#ifdef _MSC_VER +#  include <sys/timeb.h> +#  include <windows.h> +#  include <sci_win32.h> +#  include <sys/types.h> +#  include <sys/stat.h> +#else +#ifdef _WIN32 +#  include <windows.h> +#  include <win32/usleep.h> +#  include <win32/sci_win32.h> +#endif +#endif +#if !defined(HAVE_FNMATCH) && !defined(_MSC_VER) +#  include <beos/fnmatch.h> +#endif + +#ifdef _DREAMCAST +#  include <kos/thread.h> +#endif + +#ifdef __BEOS__ +#  include <be/kernel/OS.h> +#endif + +#ifdef HAVE_MEMFROB +void *memfrob(void *s, size_t n); +#endif + +int script_debug_flag = 0; /* Defaulting to running mode */ +int sci_debug_flags = 0; /* Special flags */ + +#ifndef con_file +#	define con_file 0 +#endif + +#define MEMTEST_HARDNESS 31 + +int +memtest(const char *file, int line) +{ +	/* va_list argp; -- unused */ +	int i; +	void *blocks[MEMTEST_HARDNESS + 1]; +	fprintf(stderr,"Memtesting in %s, L%d\n", file, line); + +	for (i = 0; i < MEMTEST_HARDNESS; i++) { +		blocks[i] = sci_malloc(1 + i); +#ifdef HAVE_MEMFROB +		memfrob(blocks[i], 1 + i); +#else +		memset(blocks[i], 42, 1 + i); +#endif +	} +	for (i = 0; i < MEMTEST_HARDNESS; i++) +		free(blocks[i]); + +	for (i = 0; i < MEMTEST_HARDNESS; i++) { +		blocks[i] = sci_malloc(5 + i*5); +#ifdef HAVE_MEMFROB +		memfrob(blocks[i], 5 + i*5); +#else +		memset(blocks[i], 42, 5 + i*5); +#endif +	} +	for (i = 0; i < MEMTEST_HARDNESS; i++) +		free(blocks[i]); + +	for (i = 0; i < MEMTEST_HARDNESS; i++) { +		blocks[i] = sci_malloc(5 + i*100); +#ifdef HAVE_MEMFROB +		memfrob(blocks[i], 5 + i*100); +#else +		memset(blocks[i], 42, 5 + i*100); +#endif +	} +	for (i = 0; i < MEMTEST_HARDNESS; i++) +		free(blocks[i]); + +	for (i = 0; i < MEMTEST_HARDNESS; i++) { +		blocks[i] = sci_malloc(5 + i*1000); +#ifdef HAVE_MEMFROB +		memfrob(blocks[i], 5 + i * 1000); +#else +		memset(blocks[i], 42, 5 + i * 1000); +#endif +	} +	for (i = 0; i < MEMTEST_HARDNESS; i++) +		free(blocks[i]); +	fprintf(stderr,"Memtest succeeded!\n"); +	return 0; +} + +void * +memdup(void *src, int size) +{ +	void *b = malloc(size); +	memcpy(b, src, size); +	return b; +} + +int sci_ffs(int _mask) +{ +	int retval = 0; + +	if (!_mask) return 0; +	retval++; +	while (! (_mask & 1)) +	{ +		retval++; +		_mask >>= 1; +	} + +	return retval; +} + + +/******************** Debug functions ********************/ + +void +_SCIkvprintf(FILE *file, const char *format, va_list args) +{ +	vfprintf(file, format, args); +	if (con_file) vfprintf(con_file, format, args); +} + +void +_SCIkprintf(FILE *file, const char *format, ...) +{ +	va_list args; + +	va_start(args, format); +	_SCIkvprintf(file, format, args); +	va_end (args); +} + + +void +_SCIkwarn(state_t *s, const char *file, int line, int area, const char *format, ...) +{ +	va_list args; + +	if (area == SCIkERROR_NR) +		_SCIkprintf(stderr, "ERROR: "); +	else +		_SCIkprintf(stderr, "Warning: "); + +	va_start(args, format); +	_SCIkvprintf(stderr, format, args); +	va_end(args); +	fflush(NULL); + +	if (sci_debug_flags & _DEBUG_FLAG_BREAK_ON_WARNINGS) script_debug_flag=1; +} + +void +_SCIkdebug(state_t *s, const char *file, int line, int area, const char *format, ...) +{ +	va_list args; + +	if (s->debug_mode & (1 << area)) { +		_SCIkprintf(stdout, " kernel: (%s L%d): ", file, line); +		va_start(args, format); +		_SCIkvprintf(stdout, format, args); +		va_end(args); +		fflush(NULL); +	} +} + +void +_SCIGNUkdebug(const char *funcname, state_t *s, const char *file, int line, int area, const char *format, ...) +{ +	va_list xargs; +	int error = ((area == SCIkWARNING_NR) || (area == SCIkERROR_NR)); + +	if (error || (s->debug_mode & (1 << area))) { /* Is debugging enabled for this area? */ + +		_SCIkprintf(stderr, "FSCI: "); + +		if (area == SCIkERROR_NR) +			_SCIkprintf(stderr, "ERROR in %s ", funcname); +		else if (area == SCIkWARNING_NR) +			_SCIkprintf(stderr, "%s: Warning ", funcname); +		else _SCIkprintf(stderr, funcname); + +		_SCIkprintf(stderr, "(%s L%d): ", file, line); + +		va_start(xargs, format); +		_SCIkvprintf(stderr, format, xargs); +		va_end(xargs); + +	} +} + + +#if defined(HAVE_GETTIMEOFDAY) +void +sci_gettime(long *seconds, long *useconds) +{ +	struct timeval tv; + +	assert(!gettimeofday(&tv, NULL)); +	*seconds = tv.tv_sec; +	*useconds = tv.tv_usec; +} +#elif defined (_WIN32) + +/*WARNING(Incorrect)*/ +/* Warning: This function only retrieves the amount of mseconds since the start of +** the Win32 kernel; it does /not/ provide the number of seconds since the epoch! +** There are no known cases where this causes problems, though.  */ +void sci_gettime(long *seconds, long *useconds) +{ +	DWORD tm; + +	if (TIMERR_NOERROR != timeBeginPeriod(1)) +	{ +		fprintf(stderr, "timeBeginPeriod(1) failed in sci_gettime\n"); +	} + +	tm = timeGetTime(); + +	if (TIMERR_NOERROR != timeEndPeriod(1)) +	{ +		fprintf(stderr, "timeEndPeriod(1) failed in sci_gettime\n"); +	} + +	*seconds = tm/1000; +	*useconds = (tm%1000)*1000; +} +#else +#  error "You need to provide a microsecond resolution sci_gettime implementation for your platform!" +#endif + + +void +sci_get_current_time(GTimeVal *val) +{ +	long foo, bar; +	sci_gettime(&foo, &bar); +	val->tv_sec = foo; +	val->tv_usec = bar; +} + + +/************* Directory entities *************/ +#if defined(_WIN32) && defined(_MSC_VER) +/******** Dir: Win32 CODE ********/ + +void +sci_init_dir(sci_dir_t *dir) +{ +	dir->search = -1; +} + +char * +sci_find_first(sci_dir_t *dir, const char *mask) +{ +	dir->search = _findfirst(mask, &(dir->fileinfo)); + +	if (dir->search != -1) +	{ +		if (dir->fileinfo.name == NULL) +		{ +			return NULL; +		} + +		if (strcmp(dir->fileinfo.name, ".") == 0 || +			strcmp(dir->fileinfo.name, "..") == 0) +		{ +			if (sci_find_next(dir) == NULL) +			{ +				return NULL; +			} +		} + +		return dir->fileinfo.name; +	} +	else +	{ +		switch (errno) +		{ +			case ENOENT: +			{ +#ifdef _DEBUG +				printf("_findfirst errno = ENOENT: no match\n"); + +				if (mask) +					printf(" in: %s\n", mask); +				else +					printf(" - searching in undefined directory\n"); +#endif +				break; +			} +			case EINVAL: +			{ +				printf("_findfirst errno = EINVAL: invalid filename\n"); +				break; +			} +			default: +				printf("_findfirst errno = unknown (%d)", errno); +		} +	} + +	return NULL; +} + +char * +sci_find_next(sci_dir_t *dir) +{ +			  if (dir->search == -1) +			          return NULL; + +			  if (_findnext(dir->search, &(dir->fileinfo)) < 0) { +			          _findclose(dir->search); +			          dir->search = -1; +			          return NULL; +			  } + +		if (strcmp(dir->fileinfo.name, ".") == 0 || +			strcmp(dir->fileinfo.name, "..") == 0) +		{ +			if (sci_find_next(dir) == NULL) +			{ +				return NULL; +			} +		} + +		return dir->fileinfo.name; +} + +void +sci_finish_find(sci_dir_t *dir) +{ +			  if(dir->search != -1) { +			          _findclose(dir->search); +		dir->search = -1; +	} +} + +#else /* !_WIN32 */ +/******** Dir: UNIX CODE ********/ + +void +sci_init_dir(sci_dir_t *dir) +{ +	dir->dir = NULL; +	dir->mask_copy = NULL; +} + +char * +sci_find_first(sci_dir_t *dir, const char *mask) +{ +	if (dir->dir) +		closedir(dir->dir); + +	if (!(dir->dir = opendir("."))) { +		sciprintf("%s, L%d: opendir(\".\") failed!\n", __FILE__, __LINE__); +		return NULL; +	} + +	dir->mask_copy = sci_strdup(mask); + +	return sci_find_next(dir); +} + +#ifndef FNM_CASEFOLD +#define FNM_CASEFOLD 0 +#warning "File searches will not be case-insensitive!" +#endif + +char * +sci_find_next(sci_dir_t *dir) +{ +	struct dirent *match; + +	while ((match = readdir(dir->dir))) { +		if (match->d_name[0] == '.') +			continue; + +		if (!fnmatch(dir->mask_copy, match->d_name, FNM_CASEFOLD)) +			return match->d_name; +	} + +	sci_finish_find(dir); +	return NULL; +} + +void +sci_finish_find(sci_dir_t *dir) +{ +	if (dir->dir) { +		closedir(dir->dir); +		dir->dir = NULL; +		free(dir->mask_copy); +		dir->mask_copy = NULL; +	} +} + +#endif /* !_WIN32 */ + +/************* /Directory entities *************/ + + +int +sci_mkpath(const char *path) +{ +	const char *path_position = path; +        char *next_separator = NULL; + +        if (chdir(G_DIR_SEPARATOR_S)) { /* Go to root */ +                sciprintf("Error: Could not change to root directory '%s'!\n", +			  G_DIR_SEPARATOR_S); +                return -1; +        } + +        do { +                if (next_separator) +                        *next_separator = G_DIR_SEPARATOR_S[0]; +                next_separator = (char *)strchr(path_position, G_DIR_SEPARATOR_S[0]); + +                if (next_separator) +                        *next_separator = 0; + +		if (*path_position) { /* Unless we're at the first slash... */ +			if (chdir(path_position)) { +				if (scimkdir(path_position, 0700) || chdir(path_position)) { +					sciprintf("Error: Could not create subdirectory '%s' in", +						  path_position); +					if (next_separator) +						*next_separator = G_DIR_SEPARATOR_S[0]; +					sciprintf(" '%s'!\n", path); +					return -2; +				} +			} +		} +		path_position = next_separator + 1; + +        } while (next_separator); + +        return 0; +} + + + +char * +sci_get_homedir(void) +{ +#ifdef _WIN32 +	char *_path_buf = (char*)malloc(MAX_PATH); +	char *dr = getenv("HOMEDRIVE"); +	char *path = getenv("HOMEPATH"); + +	if (!dr || !path) +		return getenv("WINDIR"); + +	strncpy(_path_buf, dr, 4); +	strncat(_path_buf, path, MAX_PATH - 4); + +	return _path_buf; +#elif defined(__unix__) || !defined(X_DISPLAY_MISSING) || defined (__BEOS__) || defined(MACOSX) +	return getenv("HOME"); +#elif defined(_DREAMCAST) +	return NULL; +#elif defined(__amigaos4__) +	return "/PROGDIR/"; +#else +#  error Please add a $HOME policy for your platform! +#endif +} + + +sci_queue_t * +sci_init_queue(sci_queue_t *queue) +{ +	queue->start = queue->end = NULL; +	return queue; +} + +sci_queue_t * +sci_add_to_queue(sci_queue_t *queue, void *data, int type) +{ +	sci_queue_node_t *node = (sci_queue_node_t*)sci_malloc(sizeof(sci_queue_node_t)); + +	node->next = NULL; +	node->data = data; +	node->type = type; + +	if (queue->start) +		queue->start->next = node; + +	queue->start = node; + +	if (!queue->end) +		queue->end = node; + +	return queue; +} + +void * +sci_get_from_queue(sci_queue_t *queue, int *type) +{ +	sci_queue_node_t *node = queue->end; +	if (node) { +		void *retval = node->data; +		if (type) +			*type = node->type; + +		queue->end = node->next; + +		if (queue->end == NULL) /* Queue empty? */ +			queue->start = NULL; + +		free(node); +		return retval; +	} +	return NULL; +} + + +/*-- Yielding to the scheduler --*/ + +#ifdef HAVE_SCHED_YIELD +#  include <sched.h> + +void +sci_sched_yield(void) +{ +	sched_yield(); +} + +#elif defined (_DREAMCAST) + +void +sci_sched_yield() +{ +	thd_pass(); +} + +#elif defined (__BEOS__) + +void +sci_sched_yield() +{ +	snooze(0); +} + +#elif defined (_WIN32) + +void +sci_sched_yield() +{ +	sleep(1); +} + +#else + +void +sci_sched_yield() +{ +} + +#endif /* !HAVE_SCHED_YIELD */ + + +char * +_fcaseseek(const char *fname, sci_dir_t *dir) +/* Expects *dir to be uninitialized and the caller to + ** free it afterwards  */ +{ +	char *buf, *iterator; +	char _buf[14]; +	char *retval = NULL, *name; + +#ifdef _MSC_VER +	return fname; +#endif + +	if (strchr(fname, G_DIR_SEPARATOR)) { +		fprintf(stderr, "_fcaseseek() does not support subdirs\n"); +		BREAKPOINT(); +	} + +	if (strlen(fname) > 12) /* not a DOS file? */ +		buf = (char*)sci_malloc(strlen(fname) + 1); +	else +		buf = _buf; + +	sci_init_dir(dir); + +	/* Replace all letters with '?' chars */ +	strcpy(buf, fname); +	iterator = buf; +	while (*iterator) { +		if (isalpha(*iterator)) +			*iterator = '?'; +		iterator++; +	} + +	name = sci_find_first(dir, buf); + +	while (name && !retval) { +		if (!strcasecmp(fname, name)) +			retval = name; +		else +			name = sci_find_next(dir); +	} + +	if (strlen(fname) > 12) +		free(buf); + +	return retval; +} + + +FILE * +sci_fopen(const char *fname, const char *mode) +{ +	sci_dir_t dir; +	char *name = _fcaseseek(fname, &dir); +	FILE *file = NULL; + +	if (name) +		file = fopen(name, mode); +	else if (strchr(mode, 'w')) +		file = fopen(fname, mode); + +	sci_finish_find(&dir); /* Free memory */ + +	return file; +} + +int +sci_open(const char *fname, int flags) +{ +	sci_dir_t dir; +	char *name; +	int file = SCI_INVALID_FD; +	char *separator_position; +	char *path; +	char *caller_cwd; + +	sci_init_dir(&dir); + +	separator_position = (char *)strrchr(fname, G_DIR_SEPARATOR); +	if (separator_position) +	{ +		path = (char *) malloc(separator_position-fname+1); +		path[separator_position-fname] = 0; +		strncpy(path, fname, separator_position-fname); +		chdir(path); +		free(path); +	} + +	name = _fcaseseek(separator_position ? separator_position + 1 : fname, &dir); +	if (name) +		file = open(name, flags); + +	sci_finish_find(&dir); /* Free memory */ + +	caller_cwd = sci_getcwd(); +	chdir(caller_cwd); +	free(caller_cwd); + +	return file; +} + +char * +sci_getcwd(void) +{ +	int size = 0; +	char *cwd = NULL; + +	while (size < 8192) { +		size += 256; +		cwd = (char*)sci_malloc(size); +		if (getcwd(cwd, size-1)) +			return cwd; + +		sci_free(cwd); +	} + +	fprintf(stderr,"Could not determine current working directory!\n"); +	return NULL; +} + +#ifdef _DREAMCAST + +int +sci_fd_size(int fd) +{ +	return fs_total(fd); +} + +int +sci_file_size(const char *fname) +{ +	int fd = fs_open(fname, O_RDONLY); +	int retval = -1; + +	if (fd != 0) { +		retval = sci_fd_size(fd); +		fs_close(fd); +	} + +	return retval; +} + +#else + +int +sci_fd_size(int fd) +{ +	struct stat fd_stat; +	if (fstat(fd, &fd_stat)) return -1; +	return fd_stat.st_size; +} + +int +sci_file_size(const char *fname) +{ +	struct stat fn_stat; +	if (stat(fname, &fn_stat)) return -1; +	return fn_stat.st_size; +} + +#endif + +/* Simple heuristic to work around array handling peculiarity in SQ4: +It uses StrAt() to read the individual elements, so we must determine +whether a string is really a string or an array. */ +int is_print_str(char *str) +{ +	int printable = 0; +	int len = strlen(str); +	 +	if (len == 0) return 1; + +	while (*str) +	{ +		if (isprint(*str)) printable++; +		str++; +	} + +	return ((float) printable / (float) len >= 0.5); +} diff --git a/engines/sci/scicore/treedef.1 b/engines/sci/scicore/treedef.1 new file mode 100644 index 0000000000..496046bb60 --- /dev/null +++ b/engines/sci/scicore/treedef.1 @@ -0,0 +1,31 @@ +	BRANCH_NODE(0, 1, 2) +	BRANCH_NODE(1, 3, 4) +	BRANCH_NODE(2, 5, 6) +	BRANCH_NODE(3, 7, 8) +	BRANCH_NODE(4, 9, 10) +	BRANCH_NODE(5, 11, 12) +	LEAF_NODE  (6, 1) +	BRANCH_NODE(7, 13, 14) +	BRANCH_NODE(8, 15, 16) +	BRANCH_NODE(9, 17, 18) +	LEAF_NODE  (10, 3) +	LEAF_NODE  (11, 2) +	LEAF_NODE  (12, 0) +	BRANCH_NODE(13, 19, 20) +	BRANCH_NODE(14, 21, 22) +	BRANCH_NODE(15, 23, 24) +	LEAF_NODE  (16, 6) +	LEAF_NODE  (17, 5) +	LEAF_NODE  (18, 4) +	BRANCH_NODE(19, 25, 26) +	BRANCH_NODE(20, 27, 28) +	LEAF_NODE  (21, 10) +	LEAF_NODE  (22, 9) +	LEAF_NODE  (23, 8) +	LEAF_NODE  (24, 7) +	BRANCH_NODE(25, 29, 30) +	LEAF_NODE  (26, 13) +	LEAF_NODE  (27, 12) +	LEAF_NODE  (28, 11) +	LEAF_NODE  (29, 15) +	LEAF_NODE  (30, 14) diff --git a/engines/sci/scicore/treedef.2 b/engines/sci/scicore/treedef.2 new file mode 100644 index 0000000000..b4d71bdfed --- /dev/null +++ b/engines/sci/scicore/treedef.2 @@ -0,0 +1,127 @@ +	BRANCH_NODE(0, 1, 2) +	BRANCH_NODE(1, 3, 4) +	BRANCH_NODE(2, 5, 6) +	BRANCH_NODE(3, 7, 8) +	BRANCH_NODE(4, 9, 10) +	BRANCH_NODE(5, 11, 12) +	LEAF_NODE  (6, 0) +	BRANCH_NODE(7, 13, 14) +	BRANCH_NODE(8, 15, 16) +	BRANCH_NODE(9, 17, 18) +	BRANCH_NODE(10, 19, 20) +	BRANCH_NODE(11, 21, 22) +	BRANCH_NODE(12, 23, 24) +	BRANCH_NODE(13, 25, 26) +	BRANCH_NODE(14, 27, 28) +	BRANCH_NODE(15, 29, 30) +	BRANCH_NODE(16, 31, 32) +	BRANCH_NODE(17, 33, 34) +	BRANCH_NODE(18, 35, 36) +	BRANCH_NODE(19, 37, 38) +	BRANCH_NODE(20, 39, 40) +	BRANCH_NODE(21, 41, 42) +	BRANCH_NODE(22, 43, 44) +	LEAF_NODE  (23, 2) +	LEAF_NODE  (24, 1) +	BRANCH_NODE(25, 45, 46) +	BRANCH_NODE(26, 47, 48) +	BRANCH_NODE(27, 49, 50) +	BRANCH_NODE(28, 51, 52) +	BRANCH_NODE(29, 53, 54) +	BRANCH_NODE(30, 55, 56) +	BRANCH_NODE(31, 57, 58) +	BRANCH_NODE(32, 59, 60) +	BRANCH_NODE(33, 61, 62) +	BRANCH_NODE(34, 63, 64) +	BRANCH_NODE(35, 65, 66) +	BRANCH_NODE(36, 67, 68) +	BRANCH_NODE(37, 69, 70) +	BRANCH_NODE(38, 71, 72) +	BRANCH_NODE(39, 73, 74) +	BRANCH_NODE(40, 75, 76) +	LEAF_NODE  (41, 6) +	LEAF_NODE  (42, 5) +	LEAF_NODE  (43, 4) +	LEAF_NODE  (44, 3) +	BRANCH_NODE(45, 77, 78) +	BRANCH_NODE(46, 79, 80) +	BRANCH_NODE(47, 81, 82) +	BRANCH_NODE(48, 83, 84) +	BRANCH_NODE(49, 85, 86) +	BRANCH_NODE(50, 87, 88) +	BRANCH_NODE(51, 89, 90) +	BRANCH_NODE(52, 91, 92) +	BRANCH_NODE(53, 93, 94) +	BRANCH_NODE(54, 95, 96) +	BRANCH_NODE(55, 97, 98) +	BRANCH_NODE(56, 99, 100) +	BRANCH_NODE(57, 101, 102) +	BRANCH_NODE(58, 103, 104) +	BRANCH_NODE(59, 105, 106) +	BRANCH_NODE(60, 107, 108) +	BRANCH_NODE(61, 109, 110) +	LEAF_NODE  (62, 21) +	LEAF_NODE  (63, 20) +	LEAF_NODE  (64, 19) +	LEAF_NODE  (65, 18) +	LEAF_NODE  (66, 17) +	LEAF_NODE  (67, 16) +	LEAF_NODE  (68, 15) +	LEAF_NODE  (69, 14) +	LEAF_NODE  (70, 13) +	LEAF_NODE  (71, 12) +	LEAF_NODE  (72, 11) +	LEAF_NODE  (73, 10) +	LEAF_NODE  (74, 9) +	LEAF_NODE  (75, 8) +	LEAF_NODE  (76, 7) +	BRANCH_NODE(77, 111, 112) +	BRANCH_NODE(78, 113, 114) +	BRANCH_NODE(79, 115, 116) +	BRANCH_NODE(80, 117, 118) +	BRANCH_NODE(81, 119, 120) +	BRANCH_NODE(82, 121, 122) +	BRANCH_NODE(83, 123, 124) +	BRANCH_NODE(84, 125, 126) +	LEAF_NODE  (85, 47) +	LEAF_NODE  (86, 46) +	LEAF_NODE  (87, 45) +	LEAF_NODE  (88, 44) +	LEAF_NODE  (89, 43) +	LEAF_NODE  (90, 42) +	LEAF_NODE  (91, 41) +	LEAF_NODE  (92, 40) +	LEAF_NODE  (93, 39) +	LEAF_NODE  (94, 38) +	LEAF_NODE  (95, 37) +	LEAF_NODE  (96, 36) +	LEAF_NODE  (97, 35) +	LEAF_NODE  (98, 34) +	LEAF_NODE  (99, 33) +	LEAF_NODE  (100, 32) +	LEAF_NODE  (101, 31) +	LEAF_NODE  (102, 30) +	LEAF_NODE  (103, 29) +	LEAF_NODE  (104, 28) +	LEAF_NODE  (105, 27) +	LEAF_NODE  (106, 26) +	LEAF_NODE  (107, 25) +	LEAF_NODE  (108, 24) +	LEAF_NODE  (109, 23) +	LEAF_NODE  (110, 22) +	LEAF_NODE  (111, 63) +	LEAF_NODE  (112, 62) +	LEAF_NODE  (113, 61) +	LEAF_NODE  (114, 60) +	LEAF_NODE  (115, 59) +	LEAF_NODE  (116, 58) +	LEAF_NODE  (117, 57) +	LEAF_NODE  (118, 56) +	LEAF_NODE  (119, 55) +	LEAF_NODE  (120, 54) +	LEAF_NODE  (121, 53) +	LEAF_NODE  (122, 52) +	LEAF_NODE  (123, 51) +	LEAF_NODE  (124, 50) +	LEAF_NODE  (125, 49) +	LEAF_NODE  (126, 48) diff --git a/engines/sci/scicore/treedef.3 b/engines/sci/scicore/treedef.3 new file mode 100644 index 0000000000..f75a01c1e6 --- /dev/null +++ b/engines/sci/scicore/treedef.3 @@ -0,0 +1,511 @@ +	BRANCH_NODE(0, 1, 2) +	BRANCH_NODE(1, 3, 4) +	BRANCH_NODE(2, 5, 6) +	BRANCH_NODE(3, 7, 8) +	BRANCH_NODE(4, 9, 10) +	BRANCH_NODE(5, 11, 12) +	BRANCH_NODE(6, 13, 14) +	BRANCH_NODE(7, 15, 16) +	BRANCH_NODE(8, 17, 18) +	BRANCH_NODE(9, 19, 20) +	BRANCH_NODE(10, 21, 22) +	BRANCH_NODE(11, 23, 24) +	BRANCH_NODE(12, 25, 26) +	BRANCH_NODE(13, 27, 28) +	BRANCH_NODE(14, 29, 30) +	BRANCH_NODE(15, 31, 32) +	BRANCH_NODE(16, 33, 34) +	BRANCH_NODE(17, 35, 36) +	BRANCH_NODE(18, 37, 38) +	BRANCH_NODE(19, 39, 40) +	BRANCH_NODE(20, 41, 42) +	BRANCH_NODE(21, 43, 44) +	BRANCH_NODE(22, 45, 46) +	BRANCH_NODE(23, 47, 48) +	BRANCH_NODE(24, 49, 50) +	BRANCH_NODE(25, 51, 52) +	BRANCH_NODE(26, 53, 54) +	BRANCH_NODE(27, 55, 56) +	BRANCH_NODE(28, 57, 58) +	BRANCH_NODE(29, 59, 60) +	LEAF_NODE  (30, 32) +	BRANCH_NODE(31, 61, 62) +	BRANCH_NODE(32, 63, 64) +	BRANCH_NODE(33, 65, 66) +	BRANCH_NODE(34, 67, 68) +	BRANCH_NODE(35, 69, 70) +	BRANCH_NODE(36, 71, 72) +	BRANCH_NODE(37, 73, 74) +	BRANCH_NODE(38, 75, 76) +	BRANCH_NODE(39, 77, 78) +	BRANCH_NODE(40, 79, 80) +	BRANCH_NODE(41, 81, 82) +	BRANCH_NODE(42, 83, 84) +	BRANCH_NODE(43, 85, 86) +	BRANCH_NODE(44, 87, 88) +	BRANCH_NODE(45, 89, 90) +	BRANCH_NODE(46, 91, 92) +	BRANCH_NODE(47, 93, 94) +	BRANCH_NODE(48, 95, 96) +	BRANCH_NODE(49, 97, 98) +	LEAF_NODE  (50, 117) +	LEAF_NODE  (51, 116) +	LEAF_NODE  (52, 115) +	LEAF_NODE  (53, 114) +	LEAF_NODE  (54, 111) +	LEAF_NODE  (55, 110) +	LEAF_NODE  (56, 108) +	LEAF_NODE  (57, 105) +	LEAF_NODE  (58, 101) +	LEAF_NODE  (59, 97) +	LEAF_NODE  (60, 69) +	BRANCH_NODE(61, 99, 100) +	BRANCH_NODE(62, 101, 102) +	BRANCH_NODE(63, 103, 104) +	BRANCH_NODE(64, 105, 106) +	BRANCH_NODE(65, 107, 108) +	BRANCH_NODE(66, 109, 110) +	BRANCH_NODE(67, 111, 112) +	BRANCH_NODE(68, 113, 114) +	BRANCH_NODE(69, 115, 116) +	BRANCH_NODE(70, 117, 118) +	BRANCH_NODE(71, 119, 120) +	BRANCH_NODE(72, 121, 122) +	BRANCH_NODE(73, 123, 124) +	BRANCH_NODE(74, 125, 126) +	BRANCH_NODE(75, 127, 128) +	BRANCH_NODE(76, 129, 130) +	BRANCH_NODE(77, 131, 132) +	BRANCH_NODE(78, 133, 134) +	LEAF_NODE  (79, 112) +	LEAF_NODE  (80, 109) +	LEAF_NODE  (81, 104) +	LEAF_NODE  (82, 103) +	LEAF_NODE  (83, 102) +	LEAF_NODE  (84, 100) +	LEAF_NODE  (85, 99) +	LEAF_NODE  (86, 98) +	LEAF_NODE  (87, 84) +	LEAF_NODE  (88, 83) +	LEAF_NODE  (89, 82) +	LEAF_NODE  (90, 79) +	LEAF_NODE  (91, 78) +	LEAF_NODE  (92, 76) +	LEAF_NODE  (93, 73) +	LEAF_NODE  (94, 68) +	LEAF_NODE  (95, 67) +	LEAF_NODE  (96, 65) +	LEAF_NODE  (97, 49) +	LEAF_NODE  (98, 45) +	BRANCH_NODE(99, 135, 136) +	BRANCH_NODE(100, 137, 138) +	BRANCH_NODE(101, 139, 140) +	BRANCH_NODE(102, 141, 142) +	BRANCH_NODE(103, 143, 144) +	BRANCH_NODE(104, 145, 146) +	BRANCH_NODE(105, 147, 148) +	BRANCH_NODE(106, 149, 150) +	BRANCH_NODE(107, 151, 152) +	BRANCH_NODE(108, 153, 154) +	BRANCH_NODE(109, 155, 156) +	BRANCH_NODE(110, 157, 158) +	BRANCH_NODE(111, 159, 160) +	BRANCH_NODE(112, 161, 162) +	BRANCH_NODE(113, 163, 164) +	LEAF_NODE  (114, 119) +	LEAF_NODE  (115, 107) +	LEAF_NODE  (116, 85) +	LEAF_NODE  (117, 80) +	LEAF_NODE  (118, 77) +	LEAF_NODE  (119, 70) +	LEAF_NODE  (120, 66) +	LEAF_NODE  (121, 61) +	LEAF_NODE  (122, 56) +	LEAF_NODE  (123, 55) +	LEAF_NODE  (124, 53) +	LEAF_NODE  (125, 52) +	LEAF_NODE  (126, 51) +	LEAF_NODE  (127, 50) +	LEAF_NODE  (128, 48) +	LEAF_NODE  (129, 46) +	LEAF_NODE  (130, 44) +	LEAF_NODE  (131, 41) +	LEAF_NODE  (132, 40) +	LEAF_NODE  (133, 13) +	LEAF_NODE  (134, 10) +	BRANCH_NODE(135, 165, 166) +	BRANCH_NODE(136, 167, 168) +	BRANCH_NODE(137, 169, 170) +	BRANCH_NODE(138, 171, 172) +	BRANCH_NODE(139, 173, 174) +	BRANCH_NODE(140, 175, 176) +	BRANCH_NODE(141, 177, 178) +	BRANCH_NODE(142, 179, 180) +	BRANCH_NODE(143, 181, 182) +	BRANCH_NODE(144, 183, 184) +	BRANCH_NODE(145, 185, 186) +	BRANCH_NODE(146, 187, 188) +	BRANCH_NODE(147, 189, 190) +	BRANCH_NODE(148, 191, 192) +	LEAF_NODE  (149, 121) +	LEAF_NODE  (150, 120) +	LEAF_NODE  (151, 118) +	LEAF_NODE  (152, 95) +	LEAF_NODE  (153, 91) +	LEAF_NODE  (154, 87) +	LEAF_NODE  (155, 72) +	LEAF_NODE  (156, 71) +	LEAF_NODE  (157, 58) +	LEAF_NODE  (158, 57) +	LEAF_NODE  (159, 54) +	LEAF_NODE  (160, 47) +	LEAF_NODE  (161, 42) +	LEAF_NODE  (162, 39) +	LEAF_NODE  (163, 34) +	LEAF_NODE  (164, 9) +	BRANCH_NODE(165, 193, 194) +	BRANCH_NODE(166, 195, 196) +	BRANCH_NODE(167, 197, 198) +	BRANCH_NODE(168, 199, 200) +	BRANCH_NODE(169, 201, 202) +	BRANCH_NODE(170, 203, 204) +	BRANCH_NODE(171, 205, 206) +	BRANCH_NODE(172, 207, 208) +	BRANCH_NODE(173, 209, 210) +	BRANCH_NODE(174, 211, 212) +	BRANCH_NODE(175, 213, 214) +	BRANCH_NODE(176, 215, 216) +	BRANCH_NODE(177, 217, 218) +	BRANCH_NODE(178, 219, 220) +	BRANCH_NODE(179, 221, 222) +	BRANCH_NODE(180, 223, 224) +	BRANCH_NODE(181, 225, 226) +	BRANCH_NODE(182, 227, 228) +	BRANCH_NODE(183, 229, 230) +	BRANCH_NODE(184, 231, 232) +	BRANCH_NODE(185, 233, 234) +	LEAF_NODE  (186, 93) +	LEAF_NODE  (187, 89) +	LEAF_NODE  (188, 88) +	LEAF_NODE  (189, 86) +	LEAF_NODE  (190, 75) +	LEAF_NODE  (191, 62) +	LEAF_NODE  (192, 43) +	BRANCH_NODE(193, 235, 236) +	BRANCH_NODE(194, 237, 238) +	BRANCH_NODE(195, 239, 240) +	BRANCH_NODE(196, 241, 242) +	BRANCH_NODE(197, 243, 244) +	BRANCH_NODE(198, 245, 246) +	BRANCH_NODE(199, 247, 248) +	BRANCH_NODE(200, 249, 250) +	BRANCH_NODE(201, 251, 252) +	BRANCH_NODE(202, 253, 254) +	BRANCH_NODE(203, 255, 256) +	BRANCH_NODE(204, 257, 258) +	BRANCH_NODE(205, 259, 260) +	BRANCH_NODE(206, 261, 262) +	BRANCH_NODE(207, 263, 264) +	BRANCH_NODE(208, 265, 266) +	BRANCH_NODE(209, 267, 268) +	BRANCH_NODE(210, 269, 270) +	BRANCH_NODE(211, 271, 272) +	BRANCH_NODE(212, 273, 274) +	BRANCH_NODE(213, 275, 276) +	BRANCH_NODE(214, 277, 278) +	BRANCH_NODE(215, 279, 280) +	BRANCH_NODE(216, 281, 282) +	BRANCH_NODE(217, 283, 284) +	BRANCH_NODE(218, 285, 286) +	BRANCH_NODE(219, 287, 288) +	BRANCH_NODE(220, 289, 290) +	BRANCH_NODE(221, 291, 292) +	BRANCH_NODE(222, 293, 294) +	BRANCH_NODE(223, 295, 296) +	BRANCH_NODE(224, 297, 298) +	BRANCH_NODE(225, 299, 300) +	BRANCH_NODE(226, 301, 302) +	BRANCH_NODE(227, 303, 304) +	BRANCH_NODE(228, 305, 306) +	BRANCH_NODE(229, 307, 308) +	LEAF_NODE  (230, 122) +	LEAF_NODE  (231, 113) +	LEAF_NODE  (232, 38) +	LEAF_NODE  (233, 36) +	LEAF_NODE  (234, 33) +	BRANCH_NODE(235, 309, 310) +	BRANCH_NODE(236, 311, 312) +	BRANCH_NODE(237, 313, 314) +	BRANCH_NODE(238, 315, 316) +	BRANCH_NODE(239, 317, 318) +	BRANCH_NODE(240, 319, 320) +	BRANCH_NODE(241, 321, 322) +	BRANCH_NODE(242, 323, 324) +	BRANCH_NODE(243, 325, 326) +	BRANCH_NODE(244, 327, 328) +	BRANCH_NODE(245, 329, 330) +	BRANCH_NODE(246, 331, 332) +	BRANCH_NODE(247, 333, 334) +	BRANCH_NODE(248, 335, 336) +	BRANCH_NODE(249, 337, 338) +	BRANCH_NODE(250, 339, 340) +	BRANCH_NODE(251, 341, 342) +	BRANCH_NODE(252, 343, 344) +	BRANCH_NODE(253, 345, 346) +	BRANCH_NODE(254, 347, 348) +	BRANCH_NODE(255, 349, 350) +	BRANCH_NODE(256, 351, 352) +	BRANCH_NODE(257, 353, 354) +	BRANCH_NODE(258, 355, 356) +	BRANCH_NODE(259, 357, 358) +	BRANCH_NODE(260, 359, 360) +	BRANCH_NODE(261, 361, 362) +	BRANCH_NODE(262, 363, 364) +	BRANCH_NODE(263, 365, 366) +	BRANCH_NODE(264, 367, 368) +	BRANCH_NODE(265, 369, 370) +	BRANCH_NODE(266, 371, 372) +	BRANCH_NODE(267, 373, 374) +	BRANCH_NODE(268, 375, 376) +	BRANCH_NODE(269, 377, 378) +	BRANCH_NODE(270, 379, 380) +	BRANCH_NODE(271, 381, 382) +	BRANCH_NODE(272, 383, 384) +	BRANCH_NODE(273, 385, 386) +	BRANCH_NODE(274, 387, 388) +	BRANCH_NODE(275, 389, 390) +	BRANCH_NODE(276, 391, 392) +	BRANCH_NODE(277, 393, 394) +	BRANCH_NODE(278, 395, 396) +	BRANCH_NODE(279, 397, 398) +	BRANCH_NODE(280, 399, 400) +	BRANCH_NODE(281, 401, 402) +	BRANCH_NODE(282, 403, 404) +	BRANCH_NODE(283, 405, 406) +	BRANCH_NODE(284, 407, 408) +	BRANCH_NODE(285, 409, 410) +	BRANCH_NODE(286, 411, 412) +	BRANCH_NODE(287, 413, 414) +	BRANCH_NODE(288, 415, 416) +	BRANCH_NODE(289, 417, 418) +	BRANCH_NODE(290, 419, 420) +	BRANCH_NODE(291, 421, 422) +	BRANCH_NODE(292, 423, 424) +	BRANCH_NODE(293, 425, 426) +	BRANCH_NODE(294, 427, 428) +	BRANCH_NODE(295, 429, 430) +	BRANCH_NODE(296, 431, 432) +	BRANCH_NODE(297, 433, 434) +	BRANCH_NODE(298, 435, 436) +	LEAF_NODE  (299, 124) +	LEAF_NODE  (300, 123) +	LEAF_NODE  (301, 106) +	LEAF_NODE  (302, 92) +	LEAF_NODE  (303, 90) +	LEAF_NODE  (304, 81) +	LEAF_NODE  (305, 74) +	LEAF_NODE  (306, 63) +	LEAF_NODE  (307, 60) +	LEAF_NODE  (308, 0) +	BRANCH_NODE(309, 437, 438) +	BRANCH_NODE(310, 439, 440) +	BRANCH_NODE(311, 441, 442) +	BRANCH_NODE(312, 443, 444) +	BRANCH_NODE(313, 445, 446) +	BRANCH_NODE(314, 447, 448) +	BRANCH_NODE(315, 449, 450) +	BRANCH_NODE(316, 451, 452) +	BRANCH_NODE(317, 453, 454) +	BRANCH_NODE(318, 455, 456) +	BRANCH_NODE(319, 457, 458) +	BRANCH_NODE(320, 459, 460) +	BRANCH_NODE(321, 461, 462) +	BRANCH_NODE(322, 463, 464) +	BRANCH_NODE(323, 465, 466) +	BRANCH_NODE(324, 467, 468) +	BRANCH_NODE(325, 469, 470) +	BRANCH_NODE(326, 471, 472) +	BRANCH_NODE(327, 473, 474) +	BRANCH_NODE(328, 475, 476) +	BRANCH_NODE(329, 477, 478) +	BRANCH_NODE(330, 479, 480) +	BRANCH_NODE(331, 481, 482) +	BRANCH_NODE(332, 483, 484) +	BRANCH_NODE(333, 485, 486) +	BRANCH_NODE(334, 487, 488) +	BRANCH_NODE(335, 489, 490) +	BRANCH_NODE(336, 491, 492) +	BRANCH_NODE(337, 493, 494) +	BRANCH_NODE(338, 495, 496) +	BRANCH_NODE(339, 497, 498) +	BRANCH_NODE(340, 499, 500) +	BRANCH_NODE(341, 501, 502) +	BRANCH_NODE(342, 503, 504) +	BRANCH_NODE(343, 505, 506) +	BRANCH_NODE(344, 507, 508) +	BRANCH_NODE(345, 509, 510) +	LEAF_NODE  (346, 244) +	LEAF_NODE  (347, 243) +	LEAF_NODE  (348, 242) +	LEAF_NODE  (349, 238) +	LEAF_NODE  (350, 233) +	LEAF_NODE  (351, 229) +	LEAF_NODE  (352, 225) +	LEAF_NODE  (353, 223) +	LEAF_NODE  (354, 222) +	LEAF_NODE  (355, 221) +	LEAF_NODE  (356, 220) +	LEAF_NODE  (357, 219) +	LEAF_NODE  (358, 218) +	LEAF_NODE  (359, 217) +	LEAF_NODE  (360, 216) +	LEAF_NODE  (361, 215) +	LEAF_NODE  (362, 214) +	LEAF_NODE  (363, 213) +	LEAF_NODE  (364, 212) +	LEAF_NODE  (365, 211) +	LEAF_NODE  (366, 210) +	LEAF_NODE  (367, 209) +	LEAF_NODE  (368, 208) +	LEAF_NODE  (369, 207) +	LEAF_NODE  (370, 206) +	LEAF_NODE  (371, 205) +	LEAF_NODE  (372, 204) +	LEAF_NODE  (373, 203) +	LEAF_NODE  (374, 202) +	LEAF_NODE  (375, 201) +	LEAF_NODE  (376, 200) +	LEAF_NODE  (377, 199) +	LEAF_NODE  (378, 198) +	LEAF_NODE  (379, 197) +	LEAF_NODE  (380, 196) +	LEAF_NODE  (381, 195) +	LEAF_NODE  (382, 194) +	LEAF_NODE  (383, 193) +	LEAF_NODE  (384, 192) +	LEAF_NODE  (385, 191) +	LEAF_NODE  (386, 190) +	LEAF_NODE  (387, 189) +	LEAF_NODE  (388, 188) +	LEAF_NODE  (389, 187) +	LEAF_NODE  (390, 186) +	LEAF_NODE  (391, 185) +	LEAF_NODE  (392, 184) +	LEAF_NODE  (393, 183) +	LEAF_NODE  (394, 182) +	LEAF_NODE  (395, 181) +	LEAF_NODE  (396, 180) +	LEAF_NODE  (397, 179) +	LEAF_NODE  (398, 178) +	LEAF_NODE  (399, 177) +	LEAF_NODE  (400, 176) +	LEAF_NODE  (401, 127) +	LEAF_NODE  (402, 126) +	LEAF_NODE  (403, 125) +	LEAF_NODE  (404, 96) +	LEAF_NODE  (405, 94) +	LEAF_NODE  (406, 64) +	LEAF_NODE  (407, 59) +	LEAF_NODE  (408, 37) +	LEAF_NODE  (409, 35) +	LEAF_NODE  (410, 31) +	LEAF_NODE  (411, 30) +	LEAF_NODE  (412, 29) +	LEAF_NODE  (413, 28) +	LEAF_NODE  (414, 27) +	LEAF_NODE  (415, 25) +	LEAF_NODE  (416, 24) +	LEAF_NODE  (417, 23) +	LEAF_NODE  (418, 22) +	LEAF_NODE  (419, 21) +	LEAF_NODE  (420, 20) +	LEAF_NODE  (421, 19) +	LEAF_NODE  (422, 18) +	LEAF_NODE  (423, 17) +	LEAF_NODE  (424, 16) +	LEAF_NODE  (425, 15) +	LEAF_NODE  (426, 14) +	LEAF_NODE  (427, 12) +	LEAF_NODE  (428, 11) +	LEAF_NODE  (429, 8) +	LEAF_NODE  (430, 7) +	LEAF_NODE  (431, 6) +	LEAF_NODE  (432, 5) +	LEAF_NODE  (433, 4) +	LEAF_NODE  (434, 3) +	LEAF_NODE  (435, 2) +	LEAF_NODE  (436, 1) +	LEAF_NODE  (437, 255) +	LEAF_NODE  (438, 254) +	LEAF_NODE  (439, 253) +	LEAF_NODE  (440, 252) +	LEAF_NODE  (441, 251) +	LEAF_NODE  (442, 250) +	LEAF_NODE  (443, 249) +	LEAF_NODE  (444, 248) +	LEAF_NODE  (445, 247) +	LEAF_NODE  (446, 246) +	LEAF_NODE  (447, 245) +	LEAF_NODE  (448, 241) +	LEAF_NODE  (449, 240) +	LEAF_NODE  (450, 239) +	LEAF_NODE  (451, 237) +	LEAF_NODE  (452, 236) +	LEAF_NODE  (453, 235) +	LEAF_NODE  (454, 234) +	LEAF_NODE  (455, 232) +	LEAF_NODE  (456, 231) +	LEAF_NODE  (457, 230) +	LEAF_NODE  (458, 228) +	LEAF_NODE  (459, 227) +	LEAF_NODE  (460, 226) +	LEAF_NODE  (461, 224) +	LEAF_NODE  (462, 175) +	LEAF_NODE  (463, 174) +	LEAF_NODE  (464, 173) +	LEAF_NODE  (465, 172) +	LEAF_NODE  (466, 171) +	LEAF_NODE  (467, 170) +	LEAF_NODE  (468, 169) +	LEAF_NODE  (469, 168) +	LEAF_NODE  (470, 167) +	LEAF_NODE  (471, 166) +	LEAF_NODE  (472, 165) +	LEAF_NODE  (473, 164) +	LEAF_NODE  (474, 163) +	LEAF_NODE  (475, 162) +	LEAF_NODE  (476, 161) +	LEAF_NODE  (477, 160) +	LEAF_NODE  (478, 159) +	LEAF_NODE  (479, 158) +	LEAF_NODE  (480, 157) +	LEAF_NODE  (481, 156) +	LEAF_NODE  (482, 155) +	LEAF_NODE  (483, 154) +	LEAF_NODE  (484, 153) +	LEAF_NODE  (485, 152) +	LEAF_NODE  (486, 151) +	LEAF_NODE  (487, 150) +	LEAF_NODE  (488, 149) +	LEAF_NODE  (489, 148) +	LEAF_NODE  (490, 147) +	LEAF_NODE  (491, 146) +	LEAF_NODE  (492, 145) +	LEAF_NODE  (493, 144) +	LEAF_NODE  (494, 143) +	LEAF_NODE  (495, 142) +	LEAF_NODE  (496, 141) +	LEAF_NODE  (497, 140) +	LEAF_NODE  (498, 139) +	LEAF_NODE  (499, 138) +	LEAF_NODE  (500, 137) +	LEAF_NODE  (501, 136) +	LEAF_NODE  (502, 135) +	LEAF_NODE  (503, 134) +	LEAF_NODE  (504, 133) +	LEAF_NODE  (505, 132) +	LEAF_NODE  (506, 131) +	LEAF_NODE  (507, 130) +	LEAF_NODE  (508, 129) +	LEAF_NODE  (509, 128) +	LEAF_NODE  (510, 26) diff --git a/engines/sci/scicore/versions.c b/engines/sci/scicore/versions.c new file mode 100644 index 0000000000..06cab77cd1 --- /dev/null +++ b/engines/sci/scicore/versions.c @@ -0,0 +1,368 @@ +/*************************************************************************** + Copyright (C) 2005 Christoph Reichenbach <reichenb@colorado.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public Licence as + published by the Free Software Foundaton; either version 2 of the + Licence, or (at your option) any later version. + + It is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + merchantibility or fitness for a particular purpose. See the + GNU General Public Licence for more details. + + You should have received a copy of the GNU General Public Licence + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +***************************************************************************/ + +#define NEED_SCI_VERSIONS +#include <versions.h> +#include <engine.h> +#include <resource.h> +#include <ctype.h> +#include "games.h" +#include "exe.h" + +/* Maxmimum number of bytes to hash from start of file */ +#define VERSION_DETECT_HASH_SIZE 1000000 + +#define VERSION_DETECT_BUF_SIZE 4096 + +static int +scan_file(char *filename, sci_version_t *version) +{ +	char buf[VERSION_DETECT_BUF_SIZE]; +	char result_string[10]; /* string-encoded result, copied from buf */ +	int characters_left; +	int state = 0; +	/* 'state' encodes how far we have matched the version pattern +	**   "n.nnn.nnn" +	** +	**   n.nnn.nnn +	**  0123456789 +	** +	** Since we cannot be certain that the pattern does not begin with an +	** alphanumeric character, some states are ambiguous. +	** The pattern is expected to be terminated with a non-alphanumeric +	** character. +	*/ + +	exe_file_t *f = exe_open(filename); + +	if (!f) +		return 1; + +	do { +		int i; +		int accept; + +		characters_left = exe_read(f, buf, VERSION_DETECT_BUF_SIZE); + +		for (i = 0; i < characters_left; i++) { +			const char ch = buf[i]; +			accept = 0; /* By default, we don't like this character */ + +			if (isalnum((unsigned char) ch)) { +				accept = (state != 1 +					  && state != 5 +					  && state != 9); +			} else if (ch == '.') { +				accept = (state == 1 +					  || state == 5); +			} else if (state == 9) { +				result_string[9] = 0; /* terminate string */ + +				if (!version_parse(result_string, version)) +				{ +					exe_close(f); +					return 0; /* success! */ +				} + +				/* Continue searching. */ +			} + +			if (accept) +				result_string[state++] = ch; +			else +				state = 0; + +		} + +	} while (characters_left == VERSION_DETECT_BUF_SIZE); + +	exe_close(f); +	return 1; /* failure */ +} + +static guint32 +read_uint32(byte *data) +{ +	return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; +} + +static guint16 +read_uint16(byte *data) +{ +	return (data[0] << 8) | data[1]; +} + +static int +is_mac_exe(char *filename) +{ +	FILE *file; +	byte buf[4]; +	guint32 val; +	int i; + +	/* Mac executables have no extension */ +	if (strchr(filename, '.')) +		return 0; + +	file = fopen(filename, "rb"); +	if (!file) +		return 0; + +	if (fseek(file, 4, SEEK_SET) == -1) { +		fclose(file); +		return 0; +	} + +	/* Read resource map offset */ +	if (fread(buf, 1, 4, file) < 4) { +		fclose(file); +		return 0; +	} + +	val = read_uint32(buf); + +	if (fseek(file, val + 28, SEEK_SET) == -1) { +		fclose(file); +		return 0; +	} + +	/* Read number of types in map */ +	if (fread(buf, 1, 2, file) < 2) { +		fclose(file); +		return 0; +	} + +	val = read_uint16(buf) + 1; + +	for (i = 0; i < val; i++) { +		if (fread(buf, 1, 4, file) < 4) { +			fclose(file); +			return 0; +		} + +		/* Look for executable code */ +		if (!memcmp(buf, "CODE", 4)) { +			fclose(file); +			return 1; +		} + +		/* Skip to next list entry */ +		if (fseek(file, 4, SEEK_CUR) == -1) { +			fclose(file); +			return 0; +		} +	} + +	fclose(file); +	return 0; +} + +static int +is_exe(char *filename) +{ +	FILE *file; +	char buf[4]; +	char header[] = {0x00, 0x00, 0x03, 0xf3}; + +	/* PC and Atari ST executable extensions */ +	if (strstr(filename, ".exe") || strstr(filename, ".EXE") +	    || strstr(filename, ".prg") || strstr(filename, ".PRG")) +		return 1; + +	/* Check for Amiga executable */ +	if (strchr(filename, '.')) +		return 0; + +	file = fopen(filename, "rb"); +	if (!file) +		return 0; + +	if (fread(buf, 1, 4, file) < 4) { +		fclose(file); +		return 0; +	} + +	fclose(file); + +	/* Check header bytes */ +	return memcmp(buf, header, 4) == 0; +} + +void +version_require_earlier_than(state_t *s, sci_version_t version) +{ +	if (s->version_lock_flag) +		return; + +	if (version <= s->min_version) { +		sciprintf("Version autodetect conflict: Less than %d.%03d.%03d was requested, but " +			  "%d.%03d.%03d is the current minimum\n", +			  SCI_VERSION_MAJOR(version), SCI_VERSION_MINOR(version), SCI_VERSION_PATCHLEVEL(version), +			  SCI_VERSION_MAJOR(s->min_version), SCI_VERSION_MINOR(s->min_version), +			  SCI_VERSION_PATCHLEVEL(s->min_version)); +		return; +	} +	else if (version < s->max_version) { +		s->max_version = version -1; +		if (s->max_version < s->version) +			s->version = s->max_version; +	} +} + +void +version_require_later_than(state_t *s, sci_version_t version) +{ +	if (s->version_lock_flag) +		return; + +	if (version > s->max_version) { +		sciprintf("Version autodetect conflict: More than %d.%03d.%03d was requested, but less than" +			  "%d.%03d.%03d is required ATM\n", +			  SCI_VERSION_MAJOR(version), SCI_VERSION_MINOR(version), SCI_VERSION_PATCHLEVEL(version), +			  SCI_VERSION_MAJOR(s->max_version), SCI_VERSION_MINOR(s->max_version), +			  SCI_VERSION_PATCHLEVEL(s->max_version)); +		return; +	} +	else if (version > s->min_version) { +		s->min_version = version; +		if (s->min_version > s->version) +			s->version = s->min_version; +	} +} + +int +version_parse(char *vn, sci_version_t *result) +{ +	char *endptr[3]; +	int major = strtol(vn, &endptr[0], 10); +	int minor = strtol(vn + 2, &endptr[1], 10); +	int patchlevel = strtol(vn + 6, &endptr[2], 10); + +	if (endptr[0] != vn + 1 || endptr[1] != vn + 5 +	    || *endptr[2] != '\0') { +		sciprintf("Warning: Failed to parse version string '%s'\n", vn); +		return 1; +	} + +	*result = SCI_VERSION(major, minor, patchlevel); +	return 0; +} + +int +version_detect_from_executable(sci_version_t *result) +{ +	sci_dir_t dir; +	char *filename; +	int mac = 0; + +	/* For Mac versions we need to search the resource fork */ +	mac = !chdir(".rsrc"); + +	sci_init_dir(&dir); + +	filename = sci_find_first(&dir, "*"); + +	while (filename) { +		if (mac ? is_mac_exe(filename) : is_exe(filename)) +			if (!scan_file(filename, result)) { +				sci_finish_find(&dir); + +				if (mac) +					chdir(".."); + +				return 0; +			} + +		filename = sci_find_next(&dir); +	} + +	if (mac) +		chdir(".."); + +	return 1; +} + +#define HASHCODE_MAGIC_RESOURCE_000 0x55555555 +#define HASHCODE_MAGIC_RESOURCE_001 0x00000001 + +const char *  /* Original version by Solomon Peachy */ +version_guess_from_hashcode(sci_version_t *result, int *res_version, guint32 *code) +{ +	int i; +	int fd = -1; +	int left = VERSION_DETECT_HASH_SIZE; +	guint32 hash_code; +	guint8 buf[VERSION_DETECT_BUF_SIZE]; + +	if (IS_VALID_FD(fd = sci_open("resource.001", O_RDONLY|O_BINARY))) { +		hash_code = HASHCODE_MAGIC_RESOURCE_001; +	} else if (IS_VALID_FD(fd = sci_open("resource.000", O_RDONLY|O_BINARY))) { +		hash_code = HASHCODE_MAGIC_RESOURCE_000; +	} else { +		sciprintf("Warning: Could not find RESOURCE.000 or RESOURCE.001, cannot determine hash code\n"); +		*code = 0; +		/* complete and utter failure */ +		return NULL; +	} + +	while (left > 0) { +		int len = read(fd, buf, left < VERSION_DETECT_BUF_SIZE ? left : VERSION_DETECT_BUF_SIZE); + +		if (len == -1) { +			sciprintf("Warning: read error while computing hash code for resource file\n"); +			*code = 0; +			return NULL; +		} + +		if (len == 0) +			/* EOF */ +			break; + +		for (i = 0; i < len; i++) +			hash_code = (hash_code * 19) + *(buf + i); + +		/* This is the string hashing algorithm used by Objective Caml 3.08; the general idea +		** of multiplying the previous hash code with a prime number between 5 and 23 appears +		** to be generally considered to be a "good" approach to exhausting the entire 32 bit +		** number space in a somewhat equal distribution. For large chunks of data, such as +		** SCI resource files, this should both perform well and yield a good distribution, +		** or at least that's what standard library designers have been assuming for quite a +		** while. */ + +		left -= len; +	} + +	close(fd); + +	*code = hash_code; + +	for (i = 0 ; sci_games[i].name ; i++) { +		if (sci_games[i].id == hash_code) { +			*result = sci_games[i].version; +			*res_version = sci_games[i].res_version; +			return sci_games[i].name; +		} +	} + +	return NULL; /* Failed to find matching game */ +} + + +#undef VERSION_DETECT_BUF_SIZE diff --git a/engines/sci/scicore/vocab.c b/engines/sci/scicore/vocab.c new file mode 100644 index 0000000000..dd70cc2fbe --- /dev/null +++ b/engines/sci/scicore/vocab.c @@ -0,0 +1,713 @@ +/*************************************************************************** + vocab.c (C) 1999 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + +    Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ +/* Main vocabulary support functions and word lookup */ + + +#include <sciresource.h> +#include <engine.h> +#include <kernel.h> + +#include <ctype.h> + +int vocab_version; + +#define VOCAB_RESOURCE_PARSE_TREE_BRANCHES vocab_version==1 ? \ +					   VOCAB_RESOURCE_SCI1_PARSE_TREE_BRANCHES : \ +					   VOCAB_RESOURCE_SCI0_PARSE_TREE_BRANCHES + +#define VOCAB_RESOURCE_SUFFIX_VOCAB vocab_version==1 ? \ +				    VOCAB_RESOURCE_SCI1_SUFFIX_VOCAB : \ +				    VOCAB_RESOURCE_SCI0_SUFFIX_VOCAB + +const char *class_names[] = +  {"",               /* These strange names were taken from an SCI01 interpreter */ +   "", +   "conj",		/* conjunction */ +   "ass",		/* ? */ +   "pos",		/* preposition ? */ +   "art",		/* article */ +   "adj",		/* adjective */ +   "pron",		/* pronoun */ +   "noun",		/* noun */ +   "auxv",		/* auxillary verb */ +   "adv",		/* adverb */ +   "verb",		/* verb */ +   "", +   "", +   "", +   ""}; + + +int +_vocab_cmp_words(const void *word1, const void *word2) +{ +	return strcasecmp((*((word_t **) word1))->word, (*((word_t **) word2))->word); +} + + +word_t ** +vocab_get_words(resource_mgr_t *resmgr, int *word_counter) +{ +	int counter = 0; +	unsigned int seeker; +	word_t **words; + +	char currentword[256] = ""; /* They're not going to use words longer than 255 ;-) */ +	int currentwordpos = 0; + +	resource_t *resource; + +	/* First try to load the SCI0 vocab resource. */ +	resource = scir_find_resource(resmgr, sci_vocab, +						  VOCAB_RESOURCE_SCI0_MAIN_VOCAB, 0); +	vocab_version = 0; + +	if (!resource) { +		fprintf(stderr,"SCI0: Could not find a main vocabulary, trying SCI01.\n"); +		resource = scir_find_resource(resmgr, sci_vocab, +							  VOCAB_RESOURCE_SCI1_MAIN_VOCAB, 0); +		vocab_version = 1; +	} + +	if (!resource) { +		fprintf(stderr,"SCI1: Could not find a main vocabulary!\n"); +		return NULL; /* NOT critical: SCI1 games and some demos don't have one! */ +	} + +	if (vocab_version == 1) +		seeker = 255 * 2; /* vocab.900 starts with 255 16-bit pointers which we don't use */ +	else +		seeker = 26 * 2; /* vocab.000 starts with 26 16-bit pointers which we don't use */ + +	if (resource->size < seeker) { +		fprintf(stderr, "Invalid main vocabulary encountered: Too small\n"); +		return NULL; +		/* Now this ought to be critical, but it'll just cause parse() and said() not to work */ +	} + +	words = (word_t**)sci_malloc(sizeof(word_t *)); + +	while (seeker < resource->size) { +		byte c; + +		words = (word_t**)sci_realloc(words, (counter + 1) * sizeof(word_t *)); + +		currentwordpos = resource->data[seeker++]; /* Parts of previous words may be re-used */ + +		if (vocab_version == 1) { +			c = 1; +			while (seeker < resource->size +				   && currentwordpos < 255 +				   && c) { +				c = resource->data[seeker++]; +				currentword[currentwordpos++] = c; +			} +			if (seeker == resource->size) { +				fprintf(stderr, "SCI1: Vocabulary not usable, disabling.\n"); +				vocab_free_words(words, counter); +				return NULL; +			} +		} else { +			do { +				c = resource->data[seeker++]; +				currentword[currentwordpos++] = c & 0x7f; /* 0x80 is used to terminate the string */ +			} while (c < 0x80); +		} + +		currentword[currentwordpos] = 0; + +		words[counter] = (word_t*)sci_malloc(sizeof(word_t) + currentwordpos); +		/* Allocate more memory, so that the word fits into the structure */ + +		strcpy(&(words[counter]->word[0]), &(currentword[0])); /* Copy the word */ + +		/* Now decode class and group: */ +		c = resource->data[seeker + 1]; +		words[counter]->w_class = ((resource->data[seeker]) << 4) | ((c & 0xf0) >> 4); +		words[counter]->group = (resource->data[seeker + 2]) | ((c & 0x0f) << 8); + +		seeker += 3; +		++counter; + +	} + +	*word_counter = counter; + +	qsort(words, counter, sizeof(word_t *), _vocab_cmp_words); /* Sort entries */ + +	return words; +} + + +void +vocab_free_words(word_t **words, int words_nr) +{ +  int i; + +  for (i = 0; i < words_nr; i++) +    free(words[i]); + +  free(words); +} + + +const char * +vocab_get_any_group_word(int group, word_t **words, int words_nr) +{ +  int i; + +  if (group == VOCAB_MAGIC_NUMBER_GROUP) +    return "{number}"; + +  for (i = 0; i < words_nr; i++) +    if (words[i]->group == group) +      return words[i]->word; + +  return "{invalid}"; +} + + +static inline unsigned int +inverse_16(unsigned int foo) +{ +  return (((foo & 0xff) << 8) | ((foo & 0xff00) >> 8)); +} + +suffix_t ** +vocab_get_suffices(resource_mgr_t *resmgr, int *suffices_nr) +{ +  int counter = 0; +  suffix_t **suffices; +  resource_t *resource = scir_find_resource(resmgr, sci_vocab, VOCAB_RESOURCE_SUFFIX_VOCAB, 1); +  unsigned int seeker = 1; + +  if (!resource) { +    fprintf(stderr,"Could not find suffix vocabulary!\n"); +    return NULL; /* Not critical */ +  } + +  suffices = (suffix_t**)sci_malloc(sizeof(suffix_t *)); + +  while ((seeker < resource->size-1) && (resource->data[seeker + 1] != 0xff)) { + +    char *alt_suffix = (char *) resource->data + seeker; +    int alt_len = strlen(alt_suffix); +    char *word_suffix; +    int word_len; + +    suffices = (suffix_t**)sci_realloc(suffices, sizeof(suffix_t *) * (counter + 1)); + +    seeker += alt_len + 1; /* Hit end of string */ +    word_suffix = (char *) resource->data + seeker + 3; /* Beginning of next string +1 (ignore '*') */ +    word_len = strlen(word_suffix); + +    suffices[counter] = (suffix_t*)sci_malloc(sizeof(suffix_t)); +    /* allocate enough memory to store the strings */ + +    suffices[counter]->word_suffix = word_suffix; +    suffices[counter]->alt_suffix = alt_suffix; + +    suffices[counter]->alt_suffix_length = alt_len; +    suffices[counter]->word_suffix_length = word_len; +    suffices[counter]->class_mask = inverse_16(getInt16(resource->data + seeker)); /* Inverse endianness */ + +    seeker += word_len + 4; +    suffices[counter]->result_class = inverse_16(getInt16(resource->data + seeker)); +    seeker += 3; /* Next entry */ + +    ++counter; + +  } + +  *suffices_nr = counter; +  return suffices; +} + + + +void +vocab_free_suffices(resource_mgr_t *resmgr, suffix_t **suffices, int suffices_nr) +{ +  int i; + +  scir_unlock_resource(resmgr, scir_find_resource(resmgr, sci_vocab, VOCAB_RESOURCE_SUFFIX_VOCAB, 0), +		       VOCAB_RESOURCE_SUFFIX_VOCAB, sci_vocab); + +  for (i = 0; i < suffices_nr; i++) +    free(suffices[i]); + +  free(suffices); +} + + +void +vocab_free_branches(parse_tree_branch_t *parser_branches) +{ +  if (parser_branches) +    free(parser_branches); +} + + +parse_tree_branch_t * +vocab_get_branches(resource_mgr_t * resmgr, int *branches_nr) +{ +  resource_t *resource = scir_find_resource(resmgr, sci_vocab, +					    VOCAB_RESOURCE_PARSE_TREE_BRANCHES, 0); +  parse_tree_branch_t *retval; +  int i; + +  if (!resource) { +    fprintf(stderr,"No parser tree data found!\n"); +    return NULL; +  } + +  *branches_nr = resource->size / 20; + +  if (*branches_nr == 0) { +    fprintf(stderr,"Parser tree data is empty!\n"); +    return NULL; +  } + +  retval = (parse_tree_branch_t*)sci_malloc(sizeof(parse_tree_branch_t) * *branches_nr); + +  for (i = 0; i < *branches_nr; i++) { +    int k; + +    byte *base = resource->data + i*20; + +    retval[i].id = getInt16(base); + +    for (k = 0; k < 9; k++) +      retval[i].data[k] = getUInt16(base + 2 + 2*k); + +    retval[i].data[9] = 0; /* Always terminate */ +  } + +  if (!retval[*branches_nr - 1].id) /* branch lists may be terminated by empty rules */ +    --(*branches_nr); + +  return retval; +} + + +result_word_t * +vocab_lookup_word(char *word, int word_len, +		  word_t **words, int words_nr, +		  suffix_t **suffices, int suffices_nr) +{ +	word_t *tempword = (word_t*)sci_malloc(sizeof(word_t) + word_len + 256); +	/* 256: For suffices. Should suffice. */ +	word_t **dict_word; +	result_word_t *retval; +	char *tester; +	int i, word_len_tmp; + +	strncpy(&(tempword->word[0]), word, word_len); +	tempword->word[word_len] = 0; + +	word_len_tmp = word_len; +	while ((tester = strchr(tempword->word, '-'))) +		memmove(tester, tester + 1, (tempword->word + word_len_tmp--) - tester); + +	retval = (result_word_t*)sci_malloc(sizeof(result_word_t)); + +	dict_word = (word_t**)bsearch(&tempword, words, words_nr, sizeof(word_t *), _vocab_cmp_words); + +	if (dict_word) { +		free(tempword); + +		retval->w_class = (*dict_word)->w_class; +		retval->group = (*dict_word)->group; + +		return retval; +	} + +	/* Now try all suffices */ +	for (i = 0; i < suffices_nr; i++) +		if (suffices[i]->alt_suffix_length <= word_len) { + +			int suff_index = word_len - suffices[i]->alt_suffix_length; +				/* Offset of the start of the suffix */ + +			if (strncasecmp(suffices[i]->alt_suffix, word + suff_index, +					suffices[i]->alt_suffix_length) == 0) { /* Suffix matched! */ + +				strncpy(&(tempword->word[0]), word, word_len); +				tempword->word[suff_index] = 0; /* Terminate word at suffix start position... */ +				strncat(&(tempword->word[0]), suffices[i]->word_suffix, suffices[i]->word_suffix_length); /* ...and append "correct" suffix */ + +				dict_word = (word_t**)bsearch(&tempword, words, words_nr, sizeof(word_t *), _vocab_cmp_words); + +				if ((dict_word) && ((*dict_word)->w_class & suffices[i]->class_mask)) { /* Found it? */ +					free(tempword); + +					retval->w_class = suffices[i]->result_class; /* Use suffix class */ +					retval->group = (*dict_word)->group; + +					return retval; +				} +			} +		} + +	/* No match so far? Check if it's a number. */ + +	strncpy(&(tempword->word[0]), word, word_len); +	tempword->word[word_len] = 0; + +	word_len_tmp = word_len; +	while ((tester = strchr(tempword->word, '-'))) +		memmove(tester, tester + 1, (tempword->word + word_len--) - tester); + +	if ((strtol(&(tempword->word[0]), &tester, 10) >= 0) +	    && (*tester == '\0')) { /* Do we have a complete number here? */ +		free(tempword); + +		retval->group = VOCAB_MAGIC_NUMBER_GROUP; +		retval->w_class = VOCAB_CLASS_NUMBER; + +		return(retval); +	} + +	free(tempword); +	free(retval); +	return NULL; +} + +int +vocab_get_said_spec_length(byte *addr) +{ +	int result = 0; + +	while (*addr != 0xff) +	{ +		if (*addr < 0xf0) +		{ +			result += 2; +			addr += 2; +		} else +		{ +			result += 1; +			addr += 1; +		} +	} + +	return result + 1; +} + +void +vocab_decypher_said_block(state_t *s, byte *addr) +{ +	int nextitem; + +	do { +		nextitem = *addr++; + +		if (nextitem < 0xf0) { +			nextitem = nextitem << 8 | *addr++; +			sciprintf(" %s[%03x]", vocab_get_any_group_word(nextitem, s->parser_words, s->parser_words_nr), +				  nextitem); + +			nextitem = 42; /* Make sure that group 0xff doesn't abort */ +		} else switch(nextitem) { +		case 0xf0: sciprintf(" ,"); break; +		case 0xf1: sciprintf(" &"); break; +		case 0xf2: sciprintf(" /"); break; +		case 0xf3: sciprintf(" ("); break; +		case 0xf4: sciprintf(" )"); break; +		case 0xf5: sciprintf(" ["); break; +		case 0xf6: sciprintf(" ]"); break; +		case 0xf7: sciprintf(" #"); break; +		case 0xf8: sciprintf(" <"); break; +		case 0xf9: sciprintf(" >"); break; +		case 0xff: break; +		} +	} while (nextitem != 0xff); + +	sciprintf("\n"); +} + + +#ifdef SCI_SIMPLE_SAID_CODE + +static short _related_words[][2] = { /* 0 is backwards, 1 is forward */ +  {0x800, 0x180}, /* preposition */ +  {0x000, 0x180}, /* article */ +  {0x000, 0x180}, /* adjective */ +  {0x800, 0x000}, /* pronoun */ +  {0x800, 0x180}, /* noun */ +  {0x000, 0x800}, /* auxiliary verb */ +  {0x800, 0x800}, /* adverb */ +  {0x000, 0x180}, /* verb */ +  {0x000, 0x180} /* number */ +}; + +int +vocab_build_simple_parse_tree(parse_tree_node_t *nodes, result_word_t *words, int words_nr) +{ +  int i, length, pos = 0; + +  for (i = 0; i < words_nr; ++i) { +    if (words[i].class != VOCAB_CLASS_ANYWORD) { +      nodes[pos].type = words[i].class; +      nodes[pos].content.value = words[i].group; +      pos += 2; /* Link information is filled in below */ +    } +  } +  nodes[pos].type = -1; /* terminate */ +  length = pos >> 1; + +  /* now find all referenced words */ +#ifdef SCI_SIMPLE_SAID_DEBUG +  sciprintf("Semantic references:\n"); +#endif + +  for (i = 0; i < length; i++) { +    int j; +    int searchmask; +    int type; + +    pos = (i << 1); +    type = sci_ffs(nodes[pos].type); + +    if (type) { +      int found = -1; + +      type -= 5; /* 1 because ffs starts counting at 1, 4 because nodes[pos].type is a nibble off */ +      if (type < 0) +	type = 0; +#ifdef SCI_SIMPLE_SAID_DEBUG +      sciprintf("#%d: Word %04x: type %04x\n", i, nodes[pos].content.value, type); +#endif + +      /* search backwards */ +      searchmask = _related_words[type][0]; +      if (searchmask) { +	for (j = i-1; j >= 0; j--) +	  if (nodes[j << 1].type & searchmask) { +	    found = j << 1; +	    break; +	  } +      } +      nodes[pos+1].content.branches[0] = found; +#ifdef SCI_SIMPLE_SAID_DEBUG +      if (found > -1) +	sciprintf("  %d <\n", found >> 1); +#endif + +      /* search forward */ +      found = -1; +      searchmask = _related_words[type][1]; +      if (searchmask) { +	for (j = i+1; j < length; j++) +	  if (nodes[j << 1].type & searchmask) { +	    found = j << 1; +	    break; +	  } +      } +#ifdef SCI_SIMPLE_SAID_DEBUG +      if (found > -1) +	sciprintf("  > %d\n", found >> 1); +#endif + +    } else { +#ifdef SCI_SIMPLE_SAID_DEBUG +      sciprintf("#%d: Untypified word\n", i); /* Weird, but not fatal */ +#endif +      nodes[pos+1].content.branches[0] = -1; +      nodes[pos+1].content.branches[1] = -1; +    } +  } +#ifdef SCI_SIMPLE_SAID_DEBUG +  sciprintf("/Semantic references.\n"); +#endif + +  return 0; +} +#endif + +result_word_t * +vocab_tokenize_string(char *sentence, int *result_nr, +		      word_t **words, int words_nr, +		      suffix_t **suffices, int suffices_nr, +		      char **error) +{ +	char *lastword = sentence; +	int pos_in_sentence = 0; +	char c; +	int wordlen = 0; +	result_word_t *retval = (result_word_t*)sci_malloc(sizeof(result_word_t)); +	/* malloc'd size is always one result_word_t too big */ + +	result_word_t *lookup_result; + + +	*result_nr = 0; +	*error = NULL; + +	do { + +		c = sentence[pos_in_sentence++]; + +		if (isalnum(c) || (c=='-' && wordlen)) +			++wordlen; /* Continue on this word */ +			/* Words may contain a '-', but may not +			** start with one.  */ + +		else { + +			if (wordlen) { /* Finished a word? */ + +				lookup_result = +					vocab_lookup_word(lastword, wordlen, +							  words, words_nr, +							  suffices, suffices_nr); +				/* Look it up */ + +				if (!lookup_result) { /* Not found? */ +					*error = (char*)sci_calloc(wordlen + 1, 1); +					strncpy(*error, lastword, wordlen); /* Set the offending word */ +					free(retval); +					return NULL; /* And return with error */ +				} + +				memcpy(retval + *result_nr, lookup_result, sizeof(result_word_t)); +				/* Copy into list */ +				++(*result_nr); /* Increase number of resulting words */ +				free(lookup_result); + +				retval = (result_word_t*)sci_realloc(retval, sizeof(result_word_t) * (*result_nr + 1)); + +			} + +			lastword = sentence + pos_in_sentence; +			wordlen = 0; +		} + +	} while (c); /* Until terminator is hit */ + +	if (*result_nr == 0) { +		free(retval); +		return NULL; +	} + +	return retval; +} + + +void +_vocab_recursive_ptree_dump_treelike(parse_tree_node_t *nodes, int nr, int prevnr) +{ +  if ((nr > VOCAB_TREE_NODES)/* || (nr < prevnr)*/) { +    sciprintf("Error(%04x)", nr); +    return; +  } + +  if (nodes[nr].type == PARSE_TREE_NODE_LEAF) +    /*    sciprintf("[%03x]%04x", nr, nodes[nr].content.value); */ +    sciprintf("%x", nodes[nr].content.value); +  else { +    int lbranch = nodes[nr].content.branches[0]; +    int rbranch = nodes[nr].content.branches[1]; +    /*    sciprintf("<[%03x]",nr); */ +    sciprintf("<"); + +    if (lbranch) +      _vocab_recursive_ptree_dump_treelike(nodes, lbranch, nr); +    else sciprintf("NULL"); + +    sciprintf(","); + +    if (rbranch) +      _vocab_recursive_ptree_dump_treelike(nodes, rbranch, nr); +    else sciprintf("NULL"); + +    sciprintf(">"); +  } +} + +void +_vocab_recursive_ptree_dump(parse_tree_node_t *nodes, int nr, int prevnr, int blanks) +{ +  int lbranch = nodes[nr].content.branches[0]; +  int rbranch = nodes[nr].content.branches[1]; +  int i; + +  if (nodes[nr].type == PARSE_TREE_NODE_LEAF) { +    sciprintf("vocab_dump_parse_tree: Error: consp is nil for element %03x\n", nr); +    return; +  } + +  if ((nr > VOCAB_TREE_NODES)/* || (nr < prevnr)*/) { +    sciprintf("Error(%04x))", nr); +    return; +  } + +  if (lbranch) { +    if (nodes[lbranch].type == PARSE_TREE_NODE_BRANCH) { +      sciprintf("\n"); +      for (i = 0; i < blanks; i++) +	sciprintf("    "); +      sciprintf("("); +      _vocab_recursive_ptree_dump(nodes, lbranch, nr, blanks + 1); +      sciprintf(")\n"); +      for (i = 0; i < blanks; i++) +	sciprintf("    "); +    } else +      sciprintf("%x", nodes[lbranch].content.value); +    sciprintf(" "); +  }/* else sciprintf ("nil"); */ + +  if (rbranch) { +    if (nodes[rbranch].type == PARSE_TREE_NODE_BRANCH) +      _vocab_recursive_ptree_dump(nodes, rbranch, nr, blanks); +    else +      sciprintf("%x", nodes[rbranch].content.value); +  }/* else sciprintf("nil");*/ +} + +void +vocab_dump_parse_tree(const char *tree_name, parse_tree_node_t *nodes) +{ +  /*  _vocab_recursive_ptree_dump_treelike(nodes, 0, 0); */ +  sciprintf("(setq %s \n'(", tree_name); +  _vocab_recursive_ptree_dump(nodes, 0, 0, 1); +  sciprintf("))\n"); +} + +void +vocab_synonymize_tokens(result_word_t *words, int words_nr, synonym_t *synonyms, int synonyms_nr) +{ +	int i, sync; + +	if (!synonyms || !synonyms_nr) +		return; /* No synonyms: Nothing to check */ + +	for (i = 0; i < words_nr; i++) +		for(sync = 0; sync < synonyms_nr; sync++) +			if (words[i].group == synonyms[sync].replaceant) +				words[i].group = synonyms[sync].replacement; +} diff --git a/engines/sci/scicore/vocab_debug.c b/engines/sci/scicore/vocab_debug.c new file mode 100644 index 0000000000..1877037040 --- /dev/null +++ b/engines/sci/scicore/vocab_debug.c @@ -0,0 +1,421 @@ +/* Modified 07/18/99 by Christoph Reichenbach: +** - added vocabulary_free_snames(); +*/ + + +#include <string.h> +#include <engine.h> +#include <sciresource.h> + +/* Default kernel name table */ +#define SCI0_KNAMES_WELL_DEFINED 0x6e +#define SCI0_KNAMES_DEFAULT_ENTRIES_NR 0x72 + +const char *sci0_default_knames[SCI0_KNAMES_DEFAULT_ENTRIES_NR] = +{ +/*0x00*/ "Load", +/*0x01*/ "UnLoad", +/*0x02*/ "ScriptID", +/*0x03*/ "DisposeScript", +/*0x04*/ "Clone", +/*0x05*/ "DisposeClone", +/*0x06*/ "IsObject", +/*0x07*/ "RespondsTo", +/*0x08*/ "DrawPic", +/*0x09*/ "Show", +/*0x0a*/ "PicNotValid", +/*0x0b*/ "Animate", +/*0x0c*/ "SetNowSeen", +/*0x0d*/ "NumLoops", +/*0x0e*/ "NumCels", +/*0x0f*/ "CelWide", +/*0x10*/ "CelHigh", +/*0x11*/ "DrawCel", +/*0x12*/ "AddToPic", +/*0x13*/ "NewWindow", +/*0x14*/ "GetPort", +/*0x15*/ "SetPort", +/*0x16*/ "DisposeWindow", +/*0x17*/ "DrawControl", +/*0x18*/ "HiliteControl", +/*0x19*/ "EditControl", +/*0x1a*/ "TextSize", +/*0x1b*/ "Display", +/*0x1c*/ "GetEvent", +/*0x1d*/ "GlobalToLocal", +/*0x1e*/ "LocalToGlobal", +/*0x1f*/ "MapKeyToDir", +/*0x20*/ "DrawMenuBar", +/*0x21*/ "MenuSelect", +/*0x22*/ "AddMenu", +/*0x23*/ "DrawStatus", +/*0x24*/ "Parse", +/*0x25*/ "Said", +/*0x26*/ "SetSynonyms", +/*0x27*/ "HaveMouse", +/*0x28*/ "SetCursor", +/*0x29*/ "FOpen", +/*0x2a*/ "FPuts", +/*0x2b*/ "FGets", +/*0x2c*/ "FClose", +/*0x2d*/ "SaveGame", +/*0x2e*/ "RestoreGame", +/*0x2f*/ "RestartGame", +/*0x30*/ "GameIsRestarting", +/*0x31*/ "DoSound", +/*0x32*/ "NewList", +/*0x33*/ "DisposeList", +/*0x34*/ "NewNode", +/*0x35*/ "FirstNode", +/*0x36*/ "LastNode", +/*0x37*/ "EmptyList", +/*0x38*/ "NextNode", +/*0x39*/ "PrevNode", +/*0x3a*/ "NodeValue", +/*0x3b*/ "AddAfter", +/*0x3c*/ "AddToFront", +/*0x3d*/ "AddToEnd", +/*0x3e*/ "FindKey", +/*0x3f*/ "DeleteKey", +/*0x40*/ "Random", +/*0x41*/ "Abs", +/*0x42*/ "Sqrt", +/*0x43*/ "GetAngle", +/*0x44*/ "GetDistance", +/*0x45*/ "Wait", +/*0x46*/ "GetTime", +/*0x47*/ "StrEnd", +/*0x48*/ "StrCat", +/*0x49*/ "StrCmp", +/*0x4a*/ "StrLen", +/*0x4b*/ "StrCpy", +/*0x4c*/ "Format", +/*0x4d*/ "GetFarText", +/*0x4e*/ "ReadNumber", +/*0x4f*/ "BaseSetter", +/*0x50*/ "DirLoop", +/*0x51*/ "CanBeHere", +/*0x52*/ "OnControl", +/*0x53*/ "InitBresen", +/*0x54*/ "DoBresen", +/*0x55*/ "DoAvoider", +/*0x56*/ "SetJump", +/*0x57*/ "SetDebug", +/*0x58*/ "InspectObj", +/*0x59*/ "ShowSends", +/*0x5a*/ "ShowObjs", +/*0x5b*/ "ShowFree", +/*0x5c*/ "MemoryInfo", +/*0x5d*/ "StackUsage", +/*0x5e*/ "Profiler", +/*0x5f*/ "GetMenu", +/*0x60*/ "SetMenu", +/*0x61*/ "GetSaveFiles", +/*0x62*/ "GetCWD", +/*0x63*/ "CheckFreeSpace", +/*0x64*/ "ValidPath", +/*0x65*/ "CoordPri", +/*0x66*/ "StrAt", +/*0x67*/ "DeviceInfo", +/*0x68*/ "GetSaveDir", +/*0x69*/ "CheckSaveGame", +/*0x6a*/ "ShakeScreen", +/*0x6b*/ "FlushResources", +/*0x6c*/ "SinMult", +/*0x6d*/ "CosMult", +/*0x6e*/ "SinDiv", +/*0x6f*/ "CosDiv", +/*0x70*/ "Graph", +/*0x71*/ SCRIPT_UNKNOWN_FUNCTION_STRING +}; + + +int getInt(unsigned char* d) +{ +  return d[0] | (d[1]<<8); +} + +int* vocabulary_get_classes(resource_mgr_t *resmgr, int* count) +{ +  resource_t* r; +  int *c; +  unsigned int i; + +  if((r = scir_find_resource(resmgr, sci_vocab, 996, 0)) == NULL) return 0; + +  c= (int*)sci_malloc(sizeof(int)*r->size/2); +  for(i=2; i<r->size; i+=4) +    { +      c[i/4]=getInt(r->data+i); +    } +  *count=r->size/4; + +  return c; +} + +int vocabulary_get_class_count(resource_mgr_t *resmgr) +{ +  resource_t* r; +  if((r = scir_find_resource(resmgr, sci_vocab, 996, 0))==0) return 0; +  return r->size/4; +} + +char** vocabulary_get_snames(resource_mgr_t *resmgr, int* pcount, sci_version_t version) +{ +  char** t; +  int count; +  int i,j; +  int magic; + +  resource_t* r = scir_find_resource(resmgr, sci_vocab, 997, 0); + +  if (!r) /* No such resource? */ +    return NULL; + +  count=getInt(r->data) + 1; /* Counter is slightly off */ + +  magic=((version==0) || (version>=SCI_VERSION_FTU_NEW_SCRIPT_HEADER))? 1 : 2; + +  t= (char**)sci_malloc(sizeof(char*)*magic*(count+1)); + +  j=0; + +  for(i=0; i<count; i++) +    { +      int offset=getInt(r->data+2+i*2); +      int len=getInt(r->data+offset); +      t[j]= (char*)sci_malloc(len+1); +      memcpy(t[j], r->data+offset+2, len); +      t[j][len]='\0'; +      j++; +      if ((version!=0) && (version<SCI_VERSION_FTU_NEW_SCRIPT_HEADER)) +      { +        t[j]= (char*)sci_malloc(len+1); +        memcpy(t[j], r->data+offset+2, len); +        t[j][len]='\0'; +        j++; +      } +    } + +  t[j]=0; + +  if (pcount != NULL) *pcount=magic*count; + +  return t; +} + +int +vocabulary_lookup_sname(char **snames_list, char *sname) +{ +  int pos = 0; +  while (snames_list[pos]) +  { +	  if (!strcasecmp(sname, snames_list[pos])) return pos; +	  pos++; +  } + +  return -1; +} + +void +vocabulary_free_snames(char **snames_list) +{ +  int pos = 0; + +  while (snames_list[pos]) +    free(snames_list[pos++]); + +  free(snames_list); +} + +opcode* vocabulary_get_opcodes(resource_mgr_t *resmgr) +{ +	opcode* o; +	int count, i=0; +	resource_t* r = scir_find_resource(resmgr, sci_vocab, VOCAB_RESOURCE_OPCODES, 0); + +	/* if the resource couldn't be loaded, leave */ +	if (r == NULL) { +		fprintf(stderr,"unable to load vocab.%03d\n", VOCAB_RESOURCE_OPCODES); +		return NULL; +	} + +	count=getInt(r->data); + +	o= (opcode*)sci_malloc(sizeof(opcode)*256); +	for(i=0; i<count; i++) +		{ +			int offset=getInt(r->data+2+i*2); +			int len=getInt(r->data+offset)-2; +			o[i].type=getInt(r->data+offset+2); +			o[i].number=i; +			o[i].name= (char*)sci_malloc(len+1); +			memcpy(o[i].name, r->data+offset+4, len); +			o[i].name[len]='\0'; +#ifdef VOCABULARY_DEBUG +			printf("Opcode %02X: %s, %d\n", i, o[i].name, o[i].type); +#endif +		} +	for(i=count; i<256; i++) +		{ +			o[i].type=0; +			o[i].number=i; +			o[i].name= (char*)sci_malloc(strlen("undefined")+1); +			strcpy(o[i].name, "undefined"); +		} +	return o; +} + +void +vocabulary_free_opcodes(opcode *opcodes) +{ +	int i; +	if (!opcodes) +		return; + +	for (i = 0; i < 256; i++) { +		if (opcodes[i].name) +			free(opcodes[i].name); +	} +	free(opcodes); +} + + +/* Alternative kernel func names retriever. Required for KQ1/SCI (at least). */ +static char** _vocabulary_get_knames0alt(int *names, resource_t *r) +{ +  unsigned int mallocsize = 32; +  char **retval = (char**)sci_malloc(sizeof (char *) * mallocsize); +  unsigned int i = 0, index = 0; + +  while (index < r->size) { + +    int slen = strlen((char *) r->data + index) + 1; + +    retval[i] = (char*)sci_malloc(slen); +    memcpy(retval[i++], r->data + index, slen); +    /* Wouldn't normally read this, but the cleanup code wants to free() this */ + +    index += slen; + +    if (i == mallocsize) +      retval = (char**)sci_realloc(retval, sizeof(char *) * (mallocsize <<= 1)); + +  } + +  *names = i + 1; +  retval = (char**)sci_realloc(retval, sizeof(char *) * (i+2)); +  retval[i] = (char*)sci_malloc(strlen(SCRIPT_UNKNOWN_FUNCTION_STRING) + 1); +  strcpy(retval[i], SCRIPT_UNKNOWN_FUNCTION_STRING); +  /* The mystery kernel function- one in each SCI0 package */ + +  retval[i+1] = NULL; /* Required for cleanup */ + +  return retval; +} + + +static char** vocabulary_get_knames0(resource_mgr_t *resmgr, int* names) +{ +	char** t; +	int count, i, index=2, empty_to_add = 1; +	resource_t* r = scir_find_resource(resmgr, sci_vocab, VOCAB_RESOURCE_KNAMES, 0); + + +	if (!r) { /* No kernel name table found? Fall back to default table */ +		t = (char**)sci_malloc ((SCI0_KNAMES_DEFAULT_ENTRIES_NR + 1) * sizeof(char*)); +		*names = SCI0_KNAMES_DEFAULT_ENTRIES_NR - 1; /* index of last element */ + +		for (i = 0; i < SCI0_KNAMES_DEFAULT_ENTRIES_NR; i++) +			t[i] = sci_strdup(sci0_default_knames[i]); + +		t[SCI0_KNAMES_DEFAULT_ENTRIES_NR] = NULL; /* Terminate list */ + +		return t; +	} + +	count=getInt(r->data); + +	if (count > 1023) +		return _vocabulary_get_knames0alt(names, r); + +	if (count < SCI0_KNAMES_WELL_DEFINED) { +		empty_to_add = SCI0_KNAMES_WELL_DEFINED - count; +		sciprintf("Less than %d kernel functions; adding %d\n", SCI0_KNAMES_WELL_DEFINED, empty_to_add); +	} + +	t= (char**)sci_malloc(sizeof(char*)*(count+1 + empty_to_add)); +	for(i=0; i<count; i++) { +		int offset=getInt(r->data+index); +		int len=getInt(r->data+offset); +		/*fprintf(stderr,"Getting name %d of %d...\n", i, count);*/ +		index+=2; +		t[i]= (char*)sci_malloc(len+1); +		memcpy(t[i], r->data + offset + 2, len); +		t[i][len]='\0'; +	} + +	for (i = 0; i < empty_to_add; i++) { +		t[count + i] = (char*)sci_malloc(strlen(SCRIPT_UNKNOWN_FUNCTION_STRING) +1); +		strcpy(t[count + i], SCRIPT_UNKNOWN_FUNCTION_STRING); +	} + +	t[count+empty_to_add]=0; +	*names=count + empty_to_add; +	return t; +} + +/*NOTE: Untested*/ +static char** vocabulary_get_knames1(resource_mgr_t *resmgr, int *count) +{ +  char** t=NULL; +  unsigned int size=64, used=0, pos=0; +  resource_t* r = scir_find_resource(resmgr, sci_vocab, VOCAB_RESOURCE_KNAMES, 0); + +  while(pos<r->size) +    { +      int len; +      if ((used==size-1)||(!t)) +	{ +	  size*=2; +	  t= (char**)sci_realloc(t, size*sizeof(char*)); +	} +      len=strlen((char *) r->data+pos); +      t[used]= (char*)sci_malloc(len+1); +      strcpy(t[used], (char *) r->data+pos); +      used++; +      pos+=len+1; +    } +  *count=used; +  t= (char**)sci_realloc(t, (used+1)*sizeof(char*)); +  t[used]=NULL; +  return t; +} + +char** vocabulary_get_knames(resource_mgr_t *resmgr, int* count) +{ +	switch(resmgr->sci_version) +	{ +		case SCI_VERSION_0: +		case SCI_VERSION_01: +		case SCI_VERSION_01_VGA: +		case SCI_VERSION_01_VGA_ODD: return vocabulary_get_knames0(resmgr, count); +		case SCI_VERSION_1_EARLY: +	        case SCI_VERSION_1_LATE: +	        case SCI_VERSION_1_1: +		case SCI_VERSION_32: return vocabulary_get_knames1(resmgr, count); +		default: return 0; +	} +} + +void vocabulary_free_knames(char** names) +{ +	int i	= 0; +	while(names[i]) +	{ +		free(names[i]); +		i++; +	} + +	free(names); +}  | 
