aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/scicore
diff options
context:
space:
mode:
authorJordi Vilalta Prat2009-02-15 06:10:59 +0000
committerJordi Vilalta Prat2009-02-15 06:10:59 +0000
commitfa6e10e9cec163845aa29e7940c86e9c9ab8a2bc (patch)
treece87338830cc8c149e1de545246bcefe4f45da00 /engines/sci/scicore
parent7c148ddf021c990fa866b7600f979aac9a5b26c9 (diff)
downloadscummvm-rg350-fa6e10e9cec163845aa29e7940c86e9c9ab8a2bc.tar.gz
scummvm-rg350-fa6e10e9cec163845aa29e7940c86e9c9ab8a2bc.tar.bz2
scummvm-rg350-fa6e10e9cec163845aa29e7940c86e9c9ab8a2bc.zip
Import the SCI engine sources from the FreeSCI Glutton branch (it doesn't compile yet)
svn-id: r38192
Diffstat (limited to 'engines/sci/scicore')
-rw-r--r--engines/sci/scicore/Makefile.am11
-rw-r--r--engines/sci/scicore/aatree.c181
-rw-r--r--engines/sci/scicore/console.c151
-rw-r--r--engines/sci/scicore/decompress0.c372
-rw-r--r--engines/sci/scicore/decompress01.c655
-rw-r--r--engines/sci/scicore/decompress1.c443
-rw-r--r--engines/sci/scicore/decompress11.c167
-rw-r--r--engines/sci/scicore/exe.c86
-rw-r--r--engines/sci/scicore/exe.h60
-rw-r--r--engines/sci/scicore/exe_dec.h66
-rw-r--r--engines/sci/scicore/exe_lzexe.c342
-rw-r--r--engines/sci/scicore/exe_raw.c73
-rw-r--r--engines/sci/scicore/fnmatch.c849
-rw-r--r--engines/sci/scicore/games.h125
-rw-r--r--engines/sci/scicore/hashmap.c186
-rw-r--r--engines/sci/scicore/huffmake.pl157
-rw-r--r--engines/sci/scicore/hufftree.117
-rw-r--r--engines/sci/scicore/hufftree.269
-rw-r--r--engines/sci/scicore/hufftree.3257
-rw-r--r--engines/sci/scicore/int_hashmap.c33
-rw-r--r--engines/sci/scicore/makefile.dos19
-rw-r--r--engines/sci/scicore/modules.c153
-rw-r--r--engines/sci/scicore/old_objects.c716
-rw-r--r--engines/sci/scicore/reg_t_hashmap.c42
-rw-r--r--engines/sci/scicore/resource.c951
-rw-r--r--engines/sci/scicore/resource_map.c515
-rw-r--r--engines/sci/scicore/resource_patch.c224
-rw-r--r--engines/sci/scicore/resourcecheck.c38
-rw-r--r--engines/sci/scicore/sci_dos.c219
-rw-r--r--engines/sci/scicore/sci_memory.c311
-rw-r--r--engines/sci/scicore/script.c488
-rw-r--r--engines/sci/scicore/tools.c780
-rw-r--r--engines/sci/scicore/treedef.131
-rw-r--r--engines/sci/scicore/treedef.2127
-rw-r--r--engines/sci/scicore/treedef.3511
-rw-r--r--engines/sci/scicore/versions.c368
-rw-r--r--engines/sci/scicore/vocab.c713
-rw-r--r--engines/sci/scicore/vocab_debug.c421
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 &bottom;
+}
+
+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 = &bottom;
+ (*t)->right = &bottom;
+ (*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);
+}