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