aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/engine
diff options
context:
space:
mode:
authorJordi Vilalta Prat2009-02-15 06:10:59 +0000
committerJordi Vilalta Prat2009-02-15 06:10:59 +0000
commitfa6e10e9cec163845aa29e7940c86e9c9ab8a2bc (patch)
treece87338830cc8c149e1de545246bcefe4f45da00 /engines/sci/engine
parent7c148ddf021c990fa866b7600f979aac9a5b26c9 (diff)
downloadscummvm-rg350-fa6e10e9cec163845aa29e7940c86e9c9ab8a2bc.tar.gz
scummvm-rg350-fa6e10e9cec163845aa29e7940c86e9c9ab8a2bc.tar.bz2
scummvm-rg350-fa6e10e9cec163845aa29e7940c86e9c9ab8a2bc.zip
Import the SCI engine sources from the FreeSCI Glutton branch (it doesn't compile yet)
svn-id: r38192
Diffstat (limited to 'engines/sci/engine')
-rw-r--r--engines/sci/engine/Makefile.am11
-rw-r--r--engines/sci/engine/cfsml.pl1182
-rw-r--r--engines/sci/engine/game.c787
-rw-r--r--engines/sci/engine/gc.c334
-rw-r--r--engines/sci/engine/gc.h41
-rw-r--r--engines/sci/engine/grammar.c720
-rw-r--r--engines/sci/engine/heap.c298
-rw-r--r--engines/sci/engine/heap.h125
-rw-r--r--engines/sci/engine/kemu_old.c330
-rw-r--r--engines/sci/engine/kernel.c1092
-rw-r--r--engines/sci/engine/kernel_compat.h77
-rw-r--r--engines/sci/engine/kernel_types.h96
-rw-r--r--engines/sci/engine/kevent.c229
-rw-r--r--engines/sci/engine/kfile.c1174
-rw-r--r--engines/sci/engine/kgraphics.c3627
-rw-r--r--engines/sci/engine/klists.c608
-rw-r--r--engines/sci/engine/kmath.c201
-rw-r--r--engines/sci/engine/kmenu.c533
-rw-r--r--engines/sci/engine/kmovement.c580
-rw-r--r--engines/sci/engine/kpathing.c1734
-rw-r--r--engines/sci/engine/kscripts.c353
-rw-r--r--engines/sci/engine/ksound.c948
-rw-r--r--engines/sci/engine/kstring.c802
-rw-r--r--engines/sci/engine/makefile.dos22
-rw-r--r--engines/sci/engine/message.c226
-rw-r--r--engines/sci/engine/message.h83
-rw-r--r--engines/sci/engine/said.c2561
-rw-r--r--engines/sci/engine/said.y950
-rw-r--r--engines/sci/engine/savegame.c4894
-rw-r--r--engines/sci/engine/savegame.cfsml1226
-rw-r--r--engines/sci/engine/sci_graphics.h44
-rw-r--r--engines/sci/engine/scriptconsole.c1342
-rw-r--r--engines/sci/engine/scriptdebug.c3887
-rw-r--r--engines/sci/engine/seg_manager.c2063
-rw-r--r--engines/sci/engine/simplesaid.c312
-rw-r--r--engines/sci/engine/sys_strings.c119
-rw-r--r--engines/sci/engine/vm.c2410
37 files changed, 36021 insertions, 0 deletions
diff --git a/engines/sci/engine/Makefile.am b/engines/sci/engine/Makefile.am
new file mode 100644
index 0000000000..682f11efa2
--- /dev/null
+++ b/engines/sci/engine/Makefile.am
@@ -0,0 +1,11 @@
+INCLUDES = -I$(top_srcdir)/src/include @EXTRA_INCLUDES@
+EXTRA_DIST = cfsml.pl savegame.cfsml gc.h heap.h kernel_compat.h \
+ kernel_types.h sci_graphics.h
+noinst_LIBRARIES = libsciengine.a
+libsciengine_a_SOURCES = savegame.c kernel.c kscripts.c klists.c scriptconsole.c \
+ kfile.c kgraphics.c kmath.c kevent.c kstring.c kmenu.c \
+ kmovement.c kpathing.c ksound.c vm.c game.c scriptdebug.c \
+ said.y grammar.c seg_manager.c sys_strings.c gc.c message.c
+
+savegame.c: savegame.cfsml
+ cat savegame.cfsml | ./cfsml.pl -l @CFSML_FLAGS@ -f savegame.cfsml > savegame.c
diff --git a/engines/sci/engine/cfsml.pl b/engines/sci/engine/cfsml.pl
new file mode 100644
index 0000000000..f136ffce9a
--- /dev/null
+++ b/engines/sci/engine/cfsml.pl
@@ -0,0 +1,1182 @@
+#! /usr/bin/env perl
+# The C File Storage Meta Language "reference" implementation
+# This implementation is supposed to conform to version
+$version = "0.8.2";
+# of the spec. Please contact the maintainer if it doesn't.
+#
+# cfsml.pl Copyright (C) 1999, 2000, 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 (CJR) [jameson@linuxgames.com]
+#
+#
+# Warning: This is still a "bit" messy. Sorry for that.
+#
+
+#$debug = 1;
+
+$write_lines = "true";
+$source_file = "CFSML source file";
+$type_integer = "integer";
+$type_string = "string";
+$type_record = "RECORD";
+$type_pointer = "POINTER";
+$type_abspointer = "ABSPOINTER";
+
+%types; # Contains all type bindings
+%records; # Contains all record bindings
+
+$mode = undef;
+while ($op = shift @ARGV) {
+ if ($mode eq undef) {
+ if ($op eq "-f") {
+ $mode = "fname";
+ } elsif ($op eq "-l") {
+ $write_lines = undef;
+ } elsif ($op eq "-v") {
+ print "cfsml.pl, the CFSML code generator, version $version\n";
+ print "This program is provided WITHOUT WARRANTY of any kind. It may be\n";
+ print "copied and modified freely according to the terms of the GNU\n";
+ print "General Public License.\n";
+ exit(0);
+ } elsif ($op eq "-h") {
+ print "CFSML help:\n";
+ print "Usage: cat source | cfsml.pl [-v] [-h] [-l] [-f <filename>] > dest\n";
+ print " -h : help\n";
+ print " -v : print version\n";
+ print " -l : disable line number printing in dest file\n";
+ print " -f : specify file name for line number printing\n";
+ exit(0);
+ } else {
+ die "Unknown option '$op'\n";
+ }
+ } elsif ($mode eq "fname") {
+ $source_file = $op;
+ $mode = 0;
+ } else {
+ die "Invalid internal state '$mode'\n";
+ }
+}
+
+sub write_line_pp
+# write_line_pp(int line_nr, bool input_file?)
+{
+ my $line_nr = shift;
+ my $_file = shift;
+ my $filename = "cfsml.pl";
+
+ if (_file) {
+ $filename = $source_file;
+ }
+
+ if ($write_lines) {
+ print "#line $line_nr \"$filename\"\n";
+ }
+}
+
+sub create_string_functions
+ {
+ $firstline = __LINE__;
+ $firstline += 4;
+ write_line_pp($firstline, 0);
+ print <<'EOF';
+
+#include <stdarg.h> /* We need va_lists */
+#include <sci_memory.h>
+
+#ifdef CFSML_DEBUG_MALLOC
+/*
+#define free(p) dbg_sci_free(p)
+#define malloc(s) dbg_sci_malloc(s)
+#define calloc(n, s) dbg_sci_calloc(n, s)
+#define realloc(p, s) dbg_sci_realloc(p, s)
+*/
+#define free dbg_sci_free
+#define malloc dbg_sci_malloc
+#define calloc dbg_sci_calloc
+#define realloc dbg_sci_realloc
+#endif
+
+static void
+_cfsml_error(char *fmt, ...)
+{
+ va_list argp;
+
+ fprintf(stderr, "Error: ");
+ va_start(argp, fmt);
+ vfprintf(stderr, fmt, argp);
+ va_end(argp);
+
+}
+
+
+static struct _cfsml_pointer_refstruct {
+ struct _cfsml_pointer_refstruct *next;
+ void *ptr;
+} *_cfsml_pointer_references = NULL;
+
+static struct _cfsml_pointer_refstruct **_cfsml_pointer_references_current = &_cfsml_pointer_references;
+
+static char *_cfsml_last_value_retreived = NULL;
+static char *_cfsml_last_identifier_retreived = NULL;
+
+static void
+_cfsml_free_pointer_references_recursively(struct _cfsml_pointer_refstruct *refs, int free_pointers)
+{
+ if (!refs)
+ return;
+ #ifdef CFSML_DEBUG_MALLOC
+ SCI_MEMTEST;
+ #endif
+
+ _cfsml_free_pointer_references_recursively(refs->next, free_pointers);
+ #ifdef CFSML_DEBUG_MALLOC
+ SCI_MEMTEST;
+
+ fprintf(stderr,"Freeing ptrref %p [%p] %s\n", refs->ptr, refs, free_pointers?
+ "ALL": "cleanup only");
+ #endif
+
+ if (free_pointers)
+ free(refs->ptr);
+
+ #ifdef CFSML_DEBUG_MALLOC
+ SCI_MEMTEST;
+ #endif
+ free(refs);
+ #ifdef CFSML_DEBUG_MALLOC
+ SCI_MEMTEST;
+ #endif
+}
+
+static void
+_cfsml_free_pointer_references(struct _cfsml_pointer_refstruct **meta_ref, int free_pointers)
+{
+ _cfsml_free_pointer_references_recursively(*meta_ref, free_pointers);
+ *meta_ref = NULL;
+ _cfsml_pointer_references_current = meta_ref;
+}
+
+static struct _cfsml_pointer_refstruct **
+_cfsml_get_current_refpointer()
+{
+ return _cfsml_pointer_references_current;
+}
+
+static void _cfsml_register_pointer(void *ptr)
+{
+ struct _cfsml_pointer_refstruct *newref = (struct _cfsml_pointer_refstruct*)sci_malloc(sizeof (struct _cfsml_pointer_refstruct));
+ #ifdef CFSML_DEBUG_MALLOC
+ SCI_MEMTEST;
+ fprintf(stderr,"Registering ptrref %p [%p]\n", ptr, newref);
+ #endif
+ newref->next = *_cfsml_pointer_references_current;
+ newref->ptr = ptr;
+ *_cfsml_pointer_references_current = newref;
+}
+
+
+static char *
+_cfsml_mangle_string(char *s)
+{
+ char *source = s;
+ char c;
+ char *target = (char *) sci_malloc(1 + strlen(s) * 2); /* We will probably need less than that */
+ char *writer = target;
+
+ while ((c = *source++)) {
+
+ if (c < 32) { /* Special character? */
+ *writer++ = '\\'; /* Escape... */
+ c += ('a' - 1);
+ } else if (c == '\\' || c == '"')
+ *writer++ = '\\'; /* Escape, but do not change */
+ *writer++ = c;
+
+ }
+ *writer = 0; /* Terminate string */
+
+ return (char *) sci_realloc(target, strlen(target) + 1);
+}
+
+
+static char *
+_cfsml_unmangle_string(char *s)
+{
+ char *target = (char *) sci_malloc(1 + strlen(s));
+ char *writer = target;
+ char *source = s;
+ char c;
+
+ while ((c = *source++) && (c > 31)) {
+ if (c == '\\') { /* Escaped character? */
+ c = *source++;
+ if ((c != '\\') && (c != '"')) /* Un-escape 0-31 only */
+ c -= ('a' - 1);
+ }
+ *writer++ = c;
+ }
+ *writer = 0; /* Terminate string */
+
+ return (char *) sci_realloc(target, strlen(target) + 1);
+}
+
+
+static char *
+_cfsml_get_identifier(FILE *fd, int *line, int *hiteof, int *assignment)
+{
+ int c;
+ int mem = 32;
+ int pos = 0;
+ int done = 0;
+ char *retval = (char *) sci_malloc(mem);
+
+ if (_cfsml_last_identifier_retreived) {
+ free(_cfsml_last_identifier_retreived);
+ _cfsml_last_identifier_retreived = NULL;
+ }
+
+ while (isspace(c = fgetc(fd)) && (c != EOF));
+ if (c == EOF) {
+ _cfsml_error("Unexpected end of file at line %d\n", *line);
+ free(retval);
+ *hiteof = 1;
+ return NULL;
+ }
+
+ ungetc(c, fd);
+
+ while (((c = fgetc(fd)) != EOF) && ((pos == 0) || (c != '\n')) && (c != '=')) {
+
+ if (pos == mem - 1) /* Need more memory? */
+ retval = (char *) sci_realloc(retval, mem *= 2);
+
+ if (!isspace(c)) {
+ if (done) {
+ _cfsml_error("Single word identifier expected at line %d\n", *line);
+ free(retval);
+ return NULL;
+ }
+ retval[pos++] = c;
+ } else
+ if (pos != 0)
+ done = 1; /* Finished the variable name */
+ else if (c == '\n')
+ ++(*line);
+ }
+
+ if (c == EOF) {
+ _cfsml_error("Unexpected end of file at line %d\n", *line);
+ free(retval);
+ *hiteof = 1;
+ return NULL;
+ }
+
+ if (c == '\n') {
+ ++(*line);
+ if (assignment)
+ *assignment = 0;
+ } else
+ if (assignment)
+ *assignment = 1;
+
+ if (pos == 0) {
+ _cfsml_error("Missing identifier in assignment at line %d\n", *line);
+ free(retval);
+ return NULL;
+ }
+
+ if (pos == mem - 1) /* Need more memory? */
+ retval = (char *) sci_realloc(retval, mem += 1);
+
+ retval[pos] = 0; /* Terminate string */
+EOF
+
+if ($debug) {
+ print " printf(\"identifier is '%s'\\n\", retval);\n";
+}
+
+ $firstline = __LINE__;
+ $firstline += 4;
+ write_line_pp($firstline, 0);
+ print <<'EOF2';
+
+ return _cfsml_last_identifier_retreived = retval;
+}
+
+
+static char *
+_cfsml_get_value(FILE *fd, int *line, int *hiteof)
+{
+ int c;
+ int mem = 64;
+ int pos = 0;
+ char *retval = (char *) sci_malloc(mem);
+
+ if (_cfsml_last_value_retreived) {
+ free(_cfsml_last_value_retreived);
+ _cfsml_last_value_retreived = NULL;
+ }
+
+ while (((c = fgetc(fd)) != EOF) && (c != '\n')) {
+
+ if (pos == mem - 1) /* Need more memory? */
+ retval = (char *) sci_realloc(retval, mem *= 2);
+
+ if (pos || (!isspace(c)))
+ retval[pos++] = c;
+
+ }
+
+ while ((pos > 0) && (isspace(retval[pos - 1])))
+ --pos; /* Strip trailing whitespace */
+
+ if (c == EOF)
+ *hiteof = 1;
+
+ if (pos == 0) {
+ _cfsml_error("Missing value in assignment at line %d\n", *line);
+ free(retval);
+ return NULL;
+ }
+
+ if (c == '\n')
+ ++(*line);
+
+ if (pos == mem - 1) /* Need more memory? */
+ retval = (char *) sci_realloc(retval, mem += 1);
+
+ retval[pos] = 0; /* Terminate string */
+EOF2
+
+ if ($debug) {
+ print " printf(\"value is '%s'\\n\", retval);\n";
+ }
+
+ $firstline = __LINE__;
+ $firstline += 4;
+ write_line_pp($firstline, 0);
+ print <<'EOF3';
+ return (_cfsml_last_value_retreived = (char *) sci_realloc(retval, strlen(retval) + 1));
+ /* Re-allocate; this value might be used for quite some while (if we are
+ ** restoring a string)
+ */
+}
+EOF3
+ }
+
+
+# Call with $expression as a simple expression, like "tos + 1".
+# Returns (in this case) ("tos", "-1").
+sub lvaluize
+ {
+ my @retval;
+# print "//DEBUG: $expression [";
+ my @tokens = split (/([+-\/\*])/, $expression);
+# print join(",", @tokens);
+ $retval[0] = $tokens[0];
+
+ my $rightvalue = "";
+ for ($i = 1; $tokens[$i]; $i++) {
+
+ if ($tokens[$i] eq "+") {
+ $rightvalue .= "-";
+ } elsif ($tokens[$i] eq "-") {
+ $rightvalue .= "+";
+ } elsif ($tokens[$i] eq "/") {
+ $rightvalue .= "*";
+ } elsif ($tokens[$i] eq "*") {
+ $rightvalue .= "/";
+ } else {
+ $rightvalue .= $tokens[$i];
+ }
+ }
+
+ $retval[1] = $rightvalue;
+
+# print "] => ($retval[0];$retval[1])\n";
+
+ return @retval;
+ }
+
+
+
+sub create_declaration
+ {
+ $typename = $type;
+ $ctype = $types{$type}->{'ctype'};
+
+ if (not $types{$type}->{'external'}) {
+ $types{$type}{'writer'} = "_cfsml_write_" . $typename;
+ $types{$type}{'reader'} = "_cfsml_read_" . $typename;
+ write_line_pp(__LINE__, 0);
+ print "static void\n$types{$type}{'writer'}(FILE *fh, $ctype* save_struc);\n";
+ print "static int\n$types{$type}{'reader'}(FILE *fh, $ctype* save_struc, char *lastval,".
+ " int *line, int *hiteof);\n\n";
+ };
+
+ }
+
+sub create_writer
+ {
+ $typename = $type;
+ $ctype = $types{$type}{'ctype'};
+
+ write_line_pp(__LINE__, 0);
+ print "static void\n_cfsml_write_$typename(FILE *fh, $ctype* save_struc)\n{\n";
+ if ($types{$type}{'type'} eq $type_record) {
+ print " int min, max, i;\n\n";
+ }
+
+ if ($types{$type}{'type'} eq $type_integer) {
+ print " fprintf(fh, \"%li\", (long) *save_struc);\n";
+ }
+ elsif ($types{$type}{'type'} eq $type_string) {
+ write_line_pp(__LINE__, 0);
+ print " if (!(*save_struc))\n";
+ print " fprintf(fh, \"\\\\null\\\\\");\n";
+ print " else {\n";
+ print " char *token = _cfsml_mangle_string((char *) *save_struc);\n";
+ print " fprintf(fh, \"\\\"%s\\\"\", token);\n";
+ print " free(token);\n";
+ print " }\n";
+ }
+ elsif ($types{$type}{'type'} eq $type_record) {
+ write_line_pp(__LINE__, 0);
+ print " fprintf(fh, \"{\\n\");\n";
+
+ for $n (@{$records{$type}}) {
+
+ print " fprintf(fh, \"$n->{'name'} = \");\n";
+
+ if ($n->{'array'}) { # Check for arrays
+
+ if ($n->{'array'} eq 'static' or $n->{'size'} * 2) { # fixed integer value?
+ print " min = max = $n->{'size'};\n";
+ }
+ else { # No, a variable
+ print " min = max = save_struc->$n->{'size'};\n";
+ }
+
+ if ($n->{'maxwrite'}) { # A write limit?
+ print " if (save_struc->$n->{'maxwrite'} < min)\n";
+ print " min = save_struc->$n->{'maxwrite'};\n";
+ }
+
+ if ($n->{'array'} eq 'dynamic') {
+ print " if (!save_struc->$n->{'name'})\n";
+ print " min = max = 0; /* Don't write if it points to NULL */\n";
+ }
+
+ write_line_pp(__LINE__, 0);
+ print " fprintf(fh, \"[%d][\\n\", max);\n";
+ print " for (i = 0; i < min; i++) {\n";
+ print " $types{$n->{'type'}}{'writer'}";
+ my $subscribstr = "[i]"; # To avoid perl interpolation problems
+ print "(fh, &(save_struc->$n->{'name'}$subscribstr));\n";
+ print " fprintf(fh, \"\\n\");\n";
+ print " }\n";
+ print " fprintf(fh, \"]\");\n";
+
+ } elsif ($n->{'type'} eq $type_pointer) { # Relative pointer
+
+ print " fprintf(fh, \"%d\", save_struc->$n->{'name'} - save_struc->$n->{'anchor'});" .
+ " /* Relative pointer */\n";
+
+ } elsif ($n->{'type'} eq $type_abspointer) { # Absolute pointer
+
+ print " if (!save_struc->$n->{'name'})\n";
+ print " fprintf(fh, \"\\\\null\\\\\");\n";
+ print " else \n";
+ print " $types{$n->{'reftype'}}{'writer'}";
+ print "(fh, save_struc->$n->{'name'});\n";
+
+ } else { # Normal record entry
+
+ print " $types{$n->{'type'}}{'writer'}";
+ print "(fh, ($types{$n->{'type'}}{'ctype'}*) &(save_struc->$n->{'name'}));\n";
+
+ }
+
+ print " fprintf(fh, \"\\n\");\n";
+ }
+
+ print " fprintf(fh, \"}\");\n";
+ }
+ else {
+ print STDERR "Warning: Attempt to create_writer for invalid type '$types{$type}{'type'}'\n";
+ }
+ print "}\n\n";
+
+ }
+
+
+sub create_reader
+ {
+ $typename = $type;
+ $ctype = $types{$type}{'ctype'};
+
+ write_line_pp(__LINE__, 0);
+ print "static int\n_cfsml_read_$typename";
+ print "(FILE *fh, $ctype* save_struc, char *lastval, int *line, int *hiteof)\n{\n";
+
+ print " char *token;\n";
+ if ($types{$type}{'type'} eq $type_record) {
+ print "int min, max, i;\n";
+ }
+ my $reladdress_nr = 0; # Number of relative addresses needed
+ my $reladdress = 0; # Current relative address number
+ my $reladdress_resolver = ""; # Relative addresses are resolved after the main while block
+
+ if ($types{$type}{'type'} eq $type_record) {
+
+ foreach $n (@{$records{$type}}) { # Count relative addresses we need
+ if ($n->{'type'} eq $type_pointer) {
+ ++$reladdress_nr;
+ }
+ }
+
+ if ($reladdress_nr) { # Allocate stack space for all relative addresses needed
+ print " int reladdresses[$reladdress_nr] = {0};\n";
+ }
+ }
+
+ if ($types{$type}{'type'} eq $type_integer) {
+ write_line_pp(__LINE__, 0);
+ print "\n *save_struc = strtol(lastval, &token, 0);\n";
+ print " if ( (*save_struc == 0) && (token == lastval) ) {\n";
+ print " _cfsml_error(\"strtol failed at line %d\\n\", *line);\n";
+ print " return CFSML_FAILURE;\n";
+ print " }\n";
+ print " if (*token != 0) {\n";
+ print " _cfsml_error(\"Non-integer encountered while parsing int value at line %d\\n\",";
+ print " *line);\n";
+ print " return CFSML_FAILURE;\n";
+ print " }\n";
+ print " return CFSML_SUCCESS;\n";
+ } elsif ($types{$type}{'type'} eq $type_string) {
+ write_line_pp(__LINE__, 0);
+ print "\n";
+ print " if (strcmp(lastval, \"\\\\null\\\\\")) { /* null pointer? */\n";
+ print " if (*lastval == '\"') { /* Quoted string? */\n";
+ print " int seeker = strlen(lastval);\n\n";
+ print " while (lastval[seeker] != '\"')\n";
+ print " --seeker;\n\n";
+ print " if (!seeker) { /* No matching double-quotes? */\n";
+ print " _cfsml_error(\"Unbalanced quotes at line %d\\n\", *line);\n";
+ print " return CFSML_FAILURE;\n";
+ print " }\n\n";
+ print " lastval[seeker] = 0; /* Terminate string at closing quotes... */\n";
+ print " lastval++; /* ...and skip the opening quotes locally */\n";
+ print " }\n";
+ print " *save_struc = _cfsml_unmangle_string(lastval);\n";
+ print " _cfsml_register_pointer(*save_struc);\n";
+ print " return CFSML_SUCCESS;\n";
+ print " } else {\n";
+ print " *save_struc = NULL;\n";
+ print " return CFSML_SUCCESS;\n";
+ print " }\n";
+ } elsif ($types{$type}{'type'} eq $type_record) {
+ write_line_pp(__LINE__, 0);
+ print " int assignment, closed, done;\n\n";
+ print " if (strcmp(lastval, \"{\")) {\n";
+ print " _cfsml_error(\"Reading record $type; expected opening braces in line %d, got \\\"%s\\\"\\n\",";
+ print "*line, lastval);\n";
+ print " return CFSML_FAILURE;\n";
+ print " };\n";
+ print " closed = 0;\n";
+ print " do {\n";
+ print " char *value;\n";
+ print " token = _cfsml_get_identifier(fh, line, hiteof, &assignment);\n\n";
+ print " if (!token) {\n";
+ print " _cfsml_error(\"Expected token at line %d\\n\", *line);\n";
+ print " return CFSML_FAILURE;\n";
+ print " }\n";
+ print " if (!assignment) {\n";
+ print " if (!strcmp(token, \"}\")) \n";
+ print " closed = 1;\n";
+ print " else {\n";
+ print " _cfsml_error(\"Expected assignment or closing braces in line %d\\n\", *line);\n";
+ print " return CFSML_FAILURE;\n";
+ print " }\n";
+ print " } else {\n";
+ print " value = \"\";\n";
+ print " while (!value || !strcmp(value, \"\"))\n";
+ print " value = _cfsml_get_value(fh, line, hiteof);\n";
+ print " if (!value) {\n";
+ print " _cfsml_error(\"Expected token at line %d\\n\", *line);\n";
+ print " return CFSML_FAILURE;\n";
+ print " }\n";
+# print " }\n";
+
+
+ foreach $n (@{$records{$type}}) { # Now take care of all record elements
+
+ my $type = $n->{'type'};
+ my $reference = undef;
+ if ($type eq $type_abspointer) {
+ $reference = 1;
+ $type = $n->{'reftype'};
+ }
+ my $name = $n->{'name'};
+ my $reader = $types{$type}{'reader'};
+ my $size = $n->{'size'};
+
+ print " if (!strcmp(token, \"$name\")) {\n";
+
+ if ($type eq $type_pointer) { # A relative pointer
+
+ $reader = $types{'int'}{'reader'}; # Read relpointer as int
+
+ write_line_pp(__LINE__, 0);
+ print " if ($reader(fh, &(reladdresses[$reladdress]), value, line, hiteof)) {\n";
+ print " _cfsml_error(\"Expected token at line %d\\n\", *line);\n";
+ print " return CFSML_FAILURE;\n";
+ print " }\n";
+
+ # Make sure that the resulting variable is interpreted correctly
+ $reladdress_resolver .= " save_struc->$n->{'name'} =".
+ " save_struc->$n->{'anchor'} + reladdresses[$reladdress];\n";
+
+ ++$reladdress; # Prepare reladdress for next element
+
+ } elsif ($n->{'array'}) { # Is it an array?
+ write_line_pp(__LINE__, 0);
+ print " if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) {\n";
+ # The value must end with [, since we're starting array data, and it must also
+ # begin with [, since this is either the only character in the line, or it starts
+ # the "amount of memory to allocate" block
+ print " _cfsml_error(\"Opening brackets expected at line %d\\n\", *line);\n";
+ print " return CFSML_FAILURE;\n";
+ print " }\n";
+
+ if ($n->{'array'} eq 'dynamic') {
+ write_line_pp(__LINE__, 0);
+ # We need to allocate the array first
+ print " /* Prepare to restore dynamic array */\n";
+ # Read amount of memory to allocate
+ print " max = strtol(value + 1, NULL, 0);\n";
+ print " if (max < 0) {\n";
+ print " _cfsml_error(\"Invalid number of elements to allocate for dynamic ";
+ print "array '%s' at line %d\\n\", token, *line);\n";
+ print " return CFSML_FAILURE;\n";
+ print " }\n\n";
+
+ print " if (max) {\n";
+ print " save_struc->$name = ($n->{'type'} *) sci_malloc(max * sizeof($type));\n";
+ print "#ifdef SATISFY_PURIFY\n";
+ print " memset(save_struc->$name, 0, max * sizeof($type));\n";
+ print "#endif\n";
+ print " _cfsml_register_pointer(save_struc->$name);\n";
+ print " }\n";
+ print " else\n";
+ print " save_struc->$name = NULL;\n"
+
+ } else { # static array
+ print " /* Prepare to restore static array */\n";
+ print " max = $size;\n";
+ }
+
+ write_line_pp(__LINE__, 0);
+ print " done = i = 0;\n";
+ print " do {\n";
+ if ($type eq $type_record) {
+ print " if (!(value = _cfsml_get_value(fh, line, hiteof))) {\n";
+ } else {
+ print " if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) {\n";
+ }
+ write_line_pp(__LINE__, 0);
+
+ print " _cfsml_error(\"Token expected at line %d\\n\", *line);\n";
+ print " return 1;\n";
+ print " }\n";
+ print " if (strcmp(value, \"]\")) {\n";
+ print " if (i == max) {\n";
+ print " _cfsml_error(\"More elements than space available (%d) in '%s' at ";
+ print "line %d\\n\", max, token, *line);\n";
+ print " return CFSML_FAILURE;\n";
+ print " }\n";
+ my $helper = "[i++]";
+ print " if ($reader(fh, &(save_struc->$name$helper), value, line, hiteof)) {\n";
+ print " _cfsml_error(\"Token expected by $reader() for $name$helper at line %d\\n\", *line);\n";
+ print " return CFSML_FAILURE;\n";
+ print " }\n";
+ print " } else done = 1;\n";
+ print " } while (!done);\n";
+
+ if ($n->{'array'} eq "dynamic") {
+ my @xpr = lvaluize($expression = $n->{'size'});
+ print " save_struc->$xpr[0] = max $xpr[1]; /* Set array size accordingly */\n";
+ }
+
+ if ($n->{'maxwrite'}) {
+ my @xpr = lvaluize($expression = $n->{'maxwrite'});
+ print " save_struc->$xpr[0] = i $xpr[1]; /* Set number of elements */\n";
+ }
+
+ }
+ elsif ($reference) {
+ write_line_pp(__LINE__, 0);
+ print " if (strcmp(value, \"\\\\null\\\\\")) { /* null pointer? */\n";
+ print " save_struc->$name = sci_malloc(sizeof ($type));\n";
+ print " _cfsml_register_pointer(save_struc->$name);\n";
+ print " if ($reader(fh, save_struc->$name, value, line, hiteof)) {\n";
+ print " _cfsml_error(\"Token expected by $reader() for $name at line %d\\n\", *line);\n";
+ print " return CFSML_FAILURE;\n";
+ print " }\n";
+ print " } else save_struc->$name = NULL;\n";
+ }
+ else { # It's a simple variable or a struct
+ write_line_pp(__LINE__, 0);
+ print " if ($reader(fh, ($types{$type}{'ctype'}*) &(save_struc->$name), value, line, hiteof)) {\n";
+ print " _cfsml_error(\"Token expected by $reader() for $name at line %d\\n\", *line);\n";
+ print " return CFSML_FAILURE;\n";
+ print " }\n";
+ }
+ print " } else\n";
+
+ }
+ write_line_pp(__LINE__, 0);
+ print " {\n";
+ print " _cfsml_error(\"$type: Assignment to invalid identifier '%s' in line %d\\n\",";
+ print " token, *line);\n";
+ print " return CFSML_FAILURE;\n";
+ print " }\n";
+ print " }\n";
+
+ print " } while (!closed); /* Until closing braces are hit */\n";
+
+ print $reladdress_resolver; # Resolves any relative addresses
+
+ print " return CFSML_SUCCESS;\n";
+ } else {
+ print STDERR "Warning: Attempt to create_reader for invalid type '$types{$type}{'type'}'\n";
+ }
+
+ print "}\n\n";
+ }
+
+# Built-in types
+
+%types = (
+ 'int' => {
+ 'type' => $type_integer,
+ 'ctype' => "int",
+ },
+
+ 'string' => {
+ 'type' => $type_string,
+ 'ctype' => "char *",
+ },
+ );
+
+
+
+sub create_function_block {
+ print "\n/* Auto-generated CFSML declaration and function block */\n\n";
+ write_line_pp(__LINE__, 0);
+ print "#define CFSML_SUCCESS 0\n";
+ print "#define CFSML_FAILURE 1\n\n";
+ create_string_functions;
+
+ foreach $n ( keys %types ) {
+ create_declaration($type = $n);
+ }
+
+ foreach $n ( keys %types ) {
+ if (not $types{$n}->{'external'}) {
+ create_writer($type = $n);
+ create_reader($type = $n);
+ }
+ }
+ print "\n/* Auto-generated CFSML declaration and function block ends here */\n";
+ print "/* Auto-generation performed by cfsml.pl $version */\n";
+}
+
+
+# Gnerates code to read a data type
+# Parameters: $type: Type to read
+# $datap: Pointer to the write destination
+# $fh: Existing filehandle of an open file to use
+# $eofvar: Variable to store _cfsml_eof into
+sub insert_reader_code {
+ print "/* Auto-generated CFSML data reader code */\n";
+ write_line_pp(__LINE__, 0);
+ print " {\n";
+ if (!$linecounter) {
+ write_line_pp(__LINE__, 0);
+ print " int _cfsml_line_ctr = 0;\n";
+ $linecounter = '_cfsml_line_ctr';
+ }
+ if ($atomic) {
+ write_line_pp(__LINE__, 0);
+ print " struct _cfsml_pointer_refstruct **_cfsml_myptrrefptr = _cfsml_get_current_refpointer();\n";
+ }
+ write_line_pp(__LINE__, 0);
+ print " int _cfsml_eof = 0, _cfsml_error;\n";
+ print " int dummy;\n";
+
+ if ($firsttoken) {
+ write_line_pp(__LINE__, 0);
+ print " char *_cfsml_inp = $firsttoken;\n";
+ } else {
+ write_line_pp(__LINE__, 0);
+ print " char *_cfsml_inp =".
+ " _cfsml_get_identifier($fh, &($linecounter), &_cfsml_eof, &dummy);\n\n";
+ }
+
+ write_line_pp(__LINE__, 0);
+ print " _cfsml_error =".
+ " $types{$type}{'reader'}($fh, $datap, _cfsml_inp, &($linecounter), &_cfsml_eof);\n";
+
+ if ($eofvar) {
+ write_line_pp(__LINE__, 0);
+ print " $eofvar = _cfsml_error;\n";
+ }
+ if ($atomic) {
+ write_line_pp(__LINE__, 0);
+ print " _cfsml_free_pointer_references(_cfsml_myptrrefptr, _cfsml_error);\n";
+ }
+ write_line_pp(__LINE__, 0);
+ print " if (_cfsml_last_value_retreived) {\n";
+ print " free(_cfsml_last_value_retreived);\n";
+ print " _cfsml_last_value_retreived = NULL;\n";
+ print " }\n";
+ print " if (_cfsml_last_identifier_retreived) {\n";
+ print " free(_cfsml_last_identifier_retreived);\n";
+ print " _cfsml_last_identifier_retreived = NULL;\n";
+ print " }\n";
+ print " }\n";
+ print "/* End of auto-generated CFSML data reader code */\n";
+}
+
+# Generates code to write a data type
+# Parameters: $type: Type to write
+# $datap: Pointer to the write destination
+# $fh: Existing filehandle of an open file to use
+sub insert_writer_code {
+ write_line_pp(__LINE__, 0);
+ print "/* Auto-generated CFSML data writer code */\n";
+ print " $types{$type}{'writer'}($fh, $datap);\n";
+ print " fprintf($fh, \"\\n\");\n";
+ print "/* End of auto-generated CFSML data writer code */\n";
+}
+
+
+################
+# Main program #
+################
+
+$parsing = 0;
+$struct = undef; # Not working on a struct
+$commentmode = undef;
+$line = 0;
+
+while (<STDIN>) {
+
+ $line++;
+
+ if ($parsing) {
+ ($data) = split "#"; # Remove shell-style comments
+ @_ = ($data);
+
+ s/\/\*.*\*\///g; # Remove C-style one-line comments
+
+ ($data) = split "\/\/"; # Remove C++-style comments
+ @_ = ($data);
+
+ if ($commentmode) {
+
+ if (grep /\*\//, $_) {
+ ($empty, $_) = split /\*\//;
+ } else {
+ @_ = (); # Empty line
+ }
+
+ } else {
+ if (grep /\/\*/, $_) {
+ $commentmode = 1;
+ ($_) = split /\/\*/;
+ }
+ }
+
+
+ # Now tokenize:
+ s/;//;
+ split /(\".*\"|[,\[\]\(\)\{\}])|\s+/;
+
+ @items = @_;
+
+ @tokens = ();
+
+ $tokens_nr = 0;
+ for ($n = 0; $n < scalar @items; $n++) { # Get rid of all undefs
+ if ($_[$n]) {
+ $_ = $items[$n];
+ s/\"//g;
+ $tokens[$tokens_nr++] = $_;
+ }
+ }
+
+ # Now all tokens are in @tokens, and we have $tokens_nr of them.
+
+# print "//DEBUG: " . join ("|", @tokens) . "\n";
+
+ if ($tokens_nr) {
+ if ($tokens_nr == 2 && $tokens[0] eq "%END" && $tokens[1] eq "CFSML") {
+
+ $struct && die "Record $struct needs closing braces in intput file (line $line).";
+
+ $parsing = 0;
+ create_function_block;
+ my $linep = $line + 1;
+ write_line_pp($linep, 1);
+ } elsif ($struct) { # Parsing struct
+ if ($tokens_nr == 1) {
+ if ($tokens[0] eq "}") {
+ $struct = undef;
+ } else { die "Invalid declaration of $token[0] in input file (line $line)\n";};
+ } else { # Must be a member declaration
+
+ my @structrecs = (@{$records{$struct}});
+ my $newidx = (scalar @structrecs) or "0";
+ my %member = ();
+ $member{'name'} = $tokens[1];
+ $member{'type'} = $tokens[0];
+
+ if ($tokens_nr == 3 && $tokens[1] == "*") {
+ $tokens_nr = 2;
+ $member{'name'} = $tokens[2];
+ $member{'reftype'} = $tokens[0];
+ $member{'type'} = $type_abspointer;
+ }
+
+ if ($tokens_nr == 4 and $tokens[0] eq $type_pointer) { # Relative pointer
+
+ if (not $tokens[2] eq "RELATIVETO") {
+ die "Invalid relative pointer declaration in input file (line $line)\n";
+ }
+
+ $member{'anchor'} = $tokens[3]; # RelPointer anchor
+
+ } else { # Non-pointer
+
+ if (not $types{$tokens[0]}) {
+ die "Unknown type $tokens[0] used in input file (line $line)\n";
+ }
+
+ if ($tokens_nr > 2) { # Array
+
+ if ($tokens[2] ne "[") {
+ die "Invalid token '$tokens[2]' in input file (line $line)\n";
+ }
+
+ $member{'array'} = "static";
+
+ if ($tokens[$tokens_nr - 1] ne "]") {
+ die "Array declaration incorrectly terminated in input file (line $line)\n";
+ }
+
+ $parsepos = 3;
+
+ while ($parsepos < $tokens_nr) {
+
+ if ($tokens[$parsepos] eq ",") {
+
+ $parsepos++;
+
+ } elsif ($tokens[$parsepos] eq "STATIC") {
+
+ $member{'array'} = "static";
+ $parsepos++;
+
+ } elsif ($tokens[$parsepos] eq "DYNAMIC") {
+
+ $member{'array'} = "dynamic";
+ $parsepos++;
+
+ } elsif ($tokens[$parsepos] eq "MAXWRITE") {
+
+ $member{'maxwrite'} = $tokens[$parsepos + 1];
+ $parsepos += 2;
+
+ } elsif ($tokens[$parsepos] eq "]") {
+
+ $parsepos++;
+ if ($parsepos != $tokens_nr) {
+ die "Error: Invalid tokens after array declaration in input file (line $line)\n";
+
+ }
+ } else {
+
+ if ($member{'size'}) {
+ die "Attempt to use more than one array size in input file (line $line)\n" .
+ "(Original size was \"$member->{'size'}\", new size is \"$tokens[$parsepos]\"\n";
+ }
+
+ $member{'size'} = $tokens[$parsepos];
+ $parsepos++;
+ }
+ }
+
+
+ unless ($member{'size'}) {
+ die "Array declaration without size in input file (line $line)\n";
+ }
+ }
+ }
+
+ @{$records{$struct}}->[$newidx] = \%member;
+ }
+ } else { # not parsing struct; normal operation.
+
+ if ($tokens[0] eq "TYPE") { # Simple type declaration
+
+ my $newtype = $tokens[1];
+
+ $types{$newtype}->{'ctype'} = $tokens[2];
+
+ if ($tokens_nr == 5) { # must be ...LIKE...
+
+ unless ($tokens[3] eq "LIKE") {
+ die "Invalid TYPE declaration in input file (line $line)\n";
+ }
+
+ $types{$newtype}->{'type'} = $types{$tokens[4]}->{'type'};
+ $types{$newtype}->{'reader'} = $types{$tokens[4]}->{'reader'};
+ $types{$newtype}->{'writer'} = $types{$tokens[4]}->{'writer'};
+
+ } elsif ($tokens_nr == 6) { # must be ...USING...
+
+ unless ($tokens[3] eq "USING") {
+ die "Invalid TYPE declaration in input file (line $line)\n";
+ }
+
+ $types{$newtype}->{'writer'} = $tokens[4];
+ $types{$newtype}->{'reader'} = $tokens[5];
+ $types{$newtype}->{'external'} = 'T';
+
+ } else {
+ die "Invalid TYPE declaration in input file (line $line)\n";
+ }
+
+ } elsif ($tokens[0] eq "RECORD") {
+
+ $struct = $tokens[1];
+ if ($types{$struct}) {
+ die "Attempt to re-define existing type $struct as a struct in input file (line $line)";
+ }
+ $types{$struct}{'type'} = $type_record;
+ if ($tokens_nr < 3 or $tokens_nr > 6 or $tokens[$tokens_nr - 1] ne "{") {
+ die "Invalid record declaration in input file (line $line)";
+ }
+
+ my $extoffset = 2;
+
+ if ($tokens_nr > 3) {
+ if ($tokens[2] ne "EXTENDS") { # Record declaration with explicit c type
+ $types{$struct}{'ctype'} = $tokens[2];
+ $extoffset = 3;
+ } else { # Record name is the same as the c type name
+ $types{$struct}{'ctype'} = $struct;
+ }
+ } elsif ($tokens_nr == 3) {
+ $types{$struct}{'ctype'} = $struct;
+ }
+
+ if (($tokens_nr > $extoffset + 1) && ($extoffset + 1 <= $tokens_nr)) {
+ if ($tokens[$extoffset] ne "EXTENDS") {
+ die "Invalid or improper keyword \"$tokens[$extoffset]\" in input file (line $line)";
+ }
+ if ($extoffset + 2 >= $tokens_nr) {
+ die "RECORD \"$struct\" extends on unspecified type in input file (line $line)";
+ }
+ my $ext_type = $tokens[$extoffset + 1];
+
+ if (!($types{$ext_type}{type} eq $type_record)) {
+ print "$types{$ext_type}{type}";
+ die "RECORD \"$struct\" attempts to extend non-existing or non-record type \"$ext_type\" in input file (line $line)";
+ }
+
+ (@{$records{$struct}}) = (@{$records{$ext_type}}); # Copy type information from super type
+ }
+
+ } else {
+ die "Invalid declaration \"$tokens[0]\" in line $line";
+ }
+ }
+ }
+
+
+ } else {
+
+ ($subtoken) = split ";"; # Get rid of trailing ;s
+ $tokens_nr = @tokens = split " ", $subtoken;
+
+ if ($tokens_nr == 1 && $tokens[0] eq "%CFSML") {
+
+ $parsing = 1;
+
+ } elsif ($tokens[0] eq "%CFSMLWRITE" and $tokens[3] eq "INTO" and $tokens_nr >= 5) {
+
+ insert_writer_code($type = $tokens[1], $datap = $tokens[2], $fh = $tokens[4]);
+ my $templine = $line + 1;
+ write_line_pp($templine, 1); # Yes, this sucks.
+
+ } elsif (($tokens[0] eq "%CFSMLREAD") or ($tokens[0] eq "%CFSMLREAD-ATOMIC") and $tokens[3] eq "FROM" and $tokens_nr >= 5) {
+
+ my $myeofvar = 0;
+ my $myfirsttoken = 0;
+ my $mylinecounter = 0;
+
+ my $idcounter = 5;
+
+ while ($idcounter < $tokens_nr) {
+ if ($tokens[$idcounter] eq "ERRVAR" and $tokens_nr >= $idcounter + 2) {
+ $myeofvar = $tokens[$idcounter + 1];
+ $idcounter += 2;
+ } elsif ($tokens[$idcounter] eq "FIRSTTOKEN" and $tokens_nr >= $idcounter + 2) {
+ $myfirsttoken = $tokens[$idcounter + 1];
+ $idcounter += 2;
+ } elsif ($tokens[$idcounter] eq "LINECOUNTER" and $tokens_nr >= $idcounter + 2) {
+ $mylinecounter = $tokens[$idcounter + 1];
+ $idcounter += 2;
+ } else {
+ die "Unknown %CFSMLREAD operational token: $tokens[$idcounter]\n";
+ }
+ }
+ insert_reader_code($type = $tokens[1], $datap = $tokens[2],
+ $fh = $tokens[4], $eofvar = $myeofvar, $firsttoken = $myfirsttoken,
+ $linecounter = $mylinecounter, $atomic = ($tokens[0] eq "%CFSMLREAD-ATOMIC"));
+ my $templine = $line + 1;
+ write_line_pp($templine, 1); # Yes, this sucks, too.
+
+ } else {
+ print;
+ }
+ }
+
+}
+
+if ($parsing) {
+ print <STDERR>, "Warning: Missing %END CFSML\n";
+}
diff --git a/engines/sci/engine/game.c b/engines/sci/engine/game.c
new file mode 100644
index 0000000000..a6672326ef
--- /dev/null
+++ b/engines/sci/engine/game.c
@@ -0,0 +1,787 @@
+/***************************************************************************
+ game.c Copyright (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]
+
+***************************************************************************/
+
+
+/* Attempt to guess if recent version of Platform SDK */
+#ifdef _MSC_VER
+# pragma message("******************** IMPORTANT MESSAGE ********************")
+# pragma message("You must have installed a recent Platform and DirectX SDK")
+# pragma message("for this build to be successful. Download MS SDKs from:")
+# pragma message("www.microsoft.com/msdownload/platformsdk/sdkupdate")
+# pragma message("***********************************************************")
+# include <windows.h>
+# if (WINVER < 0x500)
+# error *** BUILD FAILED: Need more recent SDKs or fix your SDK paths in Tools Options ***
+# endif
+#endif
+
+#include <sciresource.h>
+#include <engine.h>
+#include <versions.h>
+#include <kernel.h>
+#include "kernel_types.h"
+
+/* Structures and data from vm.c: */
+extern calls_struct_t *send_calls;
+extern int send_calls_allocated;
+extern int bp_flag;
+
+
+
+static int
+_init_vocabulary(state_t *s) /* initialize vocabulary and related resources */
+{
+ s->parser_lastmatch_word = SAID_NO_MATCH;
+ s->parser_rules = NULL;
+
+ sciprintf("Initializing vocabulary\n");
+
+ if ((s->resmgr->sci_version < SCI_VERSION_01_VGA)&&(s->parser_words = vocab_get_words(s->resmgr, &(s->parser_words_nr)))) {
+ s->parser_suffices = vocab_get_suffices(s->resmgr, &(s->parser_suffices_nr));
+ if ((s->parser_branches = vocab_get_branches(s->resmgr, &(s->parser_branches_nr))))
+ /* Now build a GNF grammar out of this */
+ s->parser_rules = vocab_build_gnf(s->parser_branches, s->parser_branches_nr);
+
+ } else {
+ sciprintf("Assuming that this game does not use a parser.\n");
+ s->parser_rules = NULL;
+ }
+
+
+ s->opcodes = vocabulary_get_opcodes(s->resmgr);
+
+ if (!(s->selector_names = vocabulary_get_snames(s->resmgr, NULL, s->version))) {
+ sciprintf("_init_vocabulary(): Could not retreive selector names (vocab.997)!\n");
+ return 1;
+ }
+
+ for (s->selector_names_nr = 0; s->selector_names[s->selector_names_nr]; s->selector_names_nr++);
+ /* Counts the number of selector names */
+
+ script_map_selectors(s, &(s->selector_map));
+ /* Maps a few special selectors for later use */
+
+ return 0;
+}
+
+extern int _allocd_rules;
+static void
+_free_vocabulary(state_t *s)
+{
+ sciprintf("Freeing vocabulary\n");
+
+ if (s->parser_words) {
+ vocab_free_words(s->parser_words, s->parser_words_nr);
+ vocab_free_suffices(s->resmgr, s->parser_suffices, s->parser_suffices_nr);
+ vocab_free_branches(s->parser_branches);
+ vocab_free_rule_list(s->parser_rules);
+ }
+
+ vocabulary_free_snames(s->selector_names);
+ vocabulary_free_knames(s->kernel_names);
+ vocabulary_free_opcodes(s->opcodes);
+ s->opcodes = NULL;
+
+ s->selector_names = NULL;
+ s->kernel_names = NULL;
+ s->opcodes = NULL;
+}
+
+
+static int
+_init_graphics_input(state_t *s)
+{
+ s->pic_priority_table = NULL;
+ s->pics = NULL;
+ s->pics_nr = 0;
+ return 0;
+}
+
+static void
+_sci1_alloc_system_colors(state_t *s)
+{
+ gfx_color_t white;
+ gfx_color_t black;
+
+ white.visual.global_index = 255;
+ white.visual.r = white.visual.g = white.visual.b = 255;
+ white.alpha = 0;
+ white.priority = white.control = 0;
+ white.mask = GFX_MASK_VISUAL;
+ gfxop_set_system_color(s->gfx_state, &white);
+
+ black.visual.global_index = 0;
+ black.visual.r = black.visual.g = black.visual.b = 0;
+ black.alpha = 0;
+ black.priority = black.control = 0;
+ black.mask = GFX_MASK_VISUAL;
+ gfxop_set_system_color(s->gfx_state, &black);
+}
+
+int
+_reset_graphics_input(state_t *s)
+{
+ resource_t *resource;
+ int font_nr;
+ gfx_color_t transparent;
+ sciprintf("Initializing graphics\n");
+
+ if (s->resmgr->sci_version <= SCI_VERSION_01) {
+ int i;
+
+ for (i = 0; i < 16; i++) {
+ if (gfxop_set_color(s->gfx_state, &(s->ega_colors[i]),
+ gfx_sci0_image_colors[sci0_palette][i].r,
+ gfx_sci0_image_colors[sci0_palette][i].g,
+ gfx_sci0_image_colors[sci0_palette][i].b,
+ 0, -1, -1))
+ return 1;
+ gfxop_set_system_color(s->gfx_state, &(s->ega_colors[i]));
+ }
+ } else
+ {
+ /* Check for Amiga palette file. */
+ FILE *f = sci_fopen("spal", "rb");
+ if (f) {
+ s->gfx_state->resstate->static_palette =
+ gfxr_read_pal1_amiga(&s->gfx_state->resstate->static_palette_entries, f);
+ fclose(f);
+ _sci1_alloc_system_colors(s);
+ } else {
+ resource = scir_find_resource(s->resmgr, sci_palette, 999, 1);
+ if (resource) {
+ if (s->version < SCI_VERSION(1,001,000))
+ s->gfx_state->resstate->static_palette =
+ gfxr_read_pal1(999, &s->gfx_state->resstate->static_palette_entries,
+ resource->data, resource->size);
+ else
+ s->gfx_state->resstate->static_palette =
+ gfxr_read_pal11(999, &s->gfx_state->resstate->static_palette_entries,
+ resource->data, resource->size);
+ _sci1_alloc_system_colors(s);
+ scir_unlock_resource(s->resmgr, resource, sci_palette, 999);
+ } else
+ sciprintf("Couldn't find the default palette!\n");
+ }
+ }
+ transparent.mask = 0;
+
+ gfxop_fill_box(s->gfx_state, gfx_rect(0, 0, 320, 200), s->ega_colors[0]); /* Fill screen black */
+ gfxop_update(s->gfx_state);
+
+ s->mouse_pointer_view = s->mouse_pointer_loop = s->mouse_pointer_cel = -1; /* No mouse pointer resource */
+ s->save_mouse_pointer_view = s->save_mouse_pointer_loop = s->save_mouse_pointer_cel = -1; /* No mouse pointer resource */
+ gfxop_set_pointer_position(s->gfx_state, gfx_point(160, 150));
+
+ s->mouse_pointer_view = s->mouse_pointer_loop = s->mouse_pointer_cel = -1; /* No mouse pointer resource */
+ s->save_mouse_pointer_view = s->save_mouse_pointer_loop = s->save_mouse_pointer_cel = -1; /* No mouse pointer resource */
+
+
+ s->pic_is_new = 0;
+ s->pic_visible_map = GFX_MASK_NONE; /* Other values only make sense for debugging */
+ s->dyn_views = NULL; /* no DynViews */
+ s->drop_views = NULL; /* And, consequently, no list for dropped views */
+
+ s->priority_first = 42; /* Priority zone 0 ends here */
+
+ if (s->version < SCI_VERSION_FTU_PRIORITY_14_ZONES)
+ s->priority_last = 200; else
+ s->priority_last = 190;
+
+ font_nr = -1;
+ do {
+ resource = scir_test_resource(s->resmgr, sci_font, ++font_nr);
+ } while ((!resource) && (font_nr < sci_max_resource_nr[s->resmgr->sci_version]));
+
+ if (!resource) {
+ sciprintf("No text font was found.\n");
+ return 1;
+ }
+
+ s->visual = gfxw_new_visual(s->gfx_state, font_nr);
+
+ s->wm_port = gfxw_new_port(s->visual, NULL, s->gfx_state->options->pic_port_bounds, s->ega_colors[0], transparent);
+ s->iconbar_port = gfxw_new_port(s->visual, NULL, gfx_rect(0, 0, 320, 200), s->ega_colors[0], transparent);
+ s->iconbar_port->flags |= GFXW_FLAG_NO_IMPLICIT_SWITCH;
+
+ if (s->resmgr->sci_version >= SCI_VERSION_01_VGA)
+ {
+ gfx_color_t fgcolor;
+ gfx_color_t bgcolor;
+
+#if 0
+ fgcolor.visual = s->gfx_state->resstate->static_palette[0];
+ fgcolor.mask = GFX_MASK_VISUAL;
+ bgcolor.visual = s->gfx_state->resstate->static_palette[255];
+ bgcolor.mask = GFX_MASK_VISUAL;
+#endif
+ s->titlebar_port = gfxw_new_port(s->visual, NULL, gfx_rect(0, 0, 320, 10),
+ fgcolor, bgcolor);
+ } else
+ s->titlebar_port = gfxw_new_port(s->visual, NULL, gfx_rect(0, 0, 320, 10),
+ s->ega_colors[0], s->ega_colors[15]);
+ s->titlebar_port->color.mask |= GFX_MASK_PRIORITY;
+ s->titlebar_port->color.priority = 11;
+ s->titlebar_port->bgcolor.mask |= GFX_MASK_PRIORITY;
+ s->titlebar_port->bgcolor.priority = 11;
+ s->titlebar_port->flags |= GFXW_FLAG_NO_IMPLICIT_SWITCH;
+
+ /* but this is correct */
+ s->picture_port = gfxw_new_port(s->visual, NULL, s->gfx_state->options->pic_port_bounds, s->ega_colors[0], transparent);
+
+ s->pics_drawn_nr = 0;
+
+ s->visual->add(GFXWC(s->visual), GFXW(s->wm_port));
+ s->visual->add(GFXWC(s->visual), GFXW(s->titlebar_port));
+ s->visual->add(GFXWC(s->visual), GFXW(s->picture_port));
+ s->visual->add(GFXWC(s->visual), GFXW(s->iconbar_port));
+ /* Add ports to visual */
+
+ s->port = s->picture_port; /* Currently using the picture port */
+
+#if 0
+ s->titlebar_port->bgcolor.mask |= GFX_MASK_PRIORITY;
+ s->titlebar_port->bgcolor.priority = 11; /* Standard priority for the titlebar port */
+#endif
+
+ return 0;
+}
+
+int
+game_init_graphics(state_t *s)
+{
+#ifndef WITH_PIC_SCALING
+ if (s->gfx_state->options->pic0_unscaled == 0)
+ sciprintf("WARNING: Pic scaling was disabled; your version of FreeSCI has no support for scaled pic drawing built in.\n");
+
+ s->gfx_state->options->pic0_unscaled = 1;
+#endif
+ return _reset_graphics_input(s);
+}
+
+
+static void
+_free_graphics_input(state_t *s)
+{
+ sciprintf("Freeing graphics\n");
+
+ s->visual->widfree(GFXW(s->visual));
+
+ s->wm_port = s->titlebar_port = s->picture_port = NULL;
+ s->visual = NULL;
+ s->dyn_views = NULL;
+ s->port = NULL;
+
+ if (s->pics)
+ sci_free(s->pics);
+ s->pics = NULL;
+}
+
+/*------------------------------------------------------------*/
+
+int
+game_init_sound(state_t *s, int sound_flags)
+{
+ if (s->resmgr->sci_version >= SCI_VERSION_01)
+ sound_flags |= SFX_STATE_FLAG_MULTIPLAY;
+
+ s->sfx_init_flags = sound_flags;
+ sfx_init(&s->sound, s->resmgr, sound_flags);
+ return 0;
+}
+
+
+/* Maps a class ID to the script the corresponding class is contained in */
+/* Returns the script number suggested by vocab.996, or -1 if there's none */
+static int
+suggested_script(resource_t *res, unsigned int class)
+{
+ int offset;
+ if (!res || class >= res->size >> 2)
+ return -1;
+
+ offset = 2 + (class << 2);
+
+ return getInt16(res->data + offset);
+}
+
+
+int
+test_cursor_style(state_t *s)
+{
+ int resource_nr = 0;
+ int ok = 0;
+
+ do {
+ ok |= scir_test_resource(s->resmgr, sci_cursor, resource_nr++) != NULL;
+ } while (resource_nr < 1000 && !ok);
+
+ return ok;
+}
+
+int
+create_class_table_sci11(state_t *s)
+{
+ int scriptnr;
+ unsigned int seeker_offset;
+ char *seeker_ptr;
+ int classnr;
+
+ resource_t *vocab996 = scir_find_resource(s->resmgr, sci_vocab, 996, 1);
+
+ if (!vocab996)
+ s->classtable_size = 20;
+ else
+ s->classtable_size = vocab996->size >> 2;
+
+ s->classtable = (class_t*)sci_calloc(sizeof(class_t), s->classtable_size);
+
+ for (scriptnr = 0; scriptnr < 1000; scriptnr++) {
+ resource_t *heap = scir_find_resource(s->resmgr, sci_heap,
+ scriptnr, 0);
+
+ if (heap) {
+ int global_vars = getUInt16(heap->data + 2);
+
+ seeker_ptr = (char*)heap->data + 4 + global_vars*2;
+ seeker_offset = 4 + global_vars*2;
+
+ while (getUInt16((byte*)seeker_ptr) == SCRIPT_OBJECT_MAGIC_NUMBER)
+ {
+ if (getUInt16((byte*)seeker_ptr + 14) & SCRIPT_INFO_CLASS)
+ {
+ classnr = getUInt16((byte*)seeker_ptr + 10);
+ if (classnr >= s->classtable_size) {
+
+ if (classnr >= SCRIPT_MAX_CLASSTABLE_SIZE) {
+ fprintf(stderr,"Invalid class number 0x%x in script.%d(0x%x), offset %04x\n",
+ classnr, scriptnr, scriptnr, seeker_offset);
+ return 1;
+ }
+
+ s->classtable = (class_t*)sci_realloc(s->classtable, sizeof(class_t) * (classnr + 1));
+ memset(&(s->classtable[s->classtable_size]), 0,
+ sizeof(class_t) * (1 + classnr - s->classtable_size)); /* Clear after resize */
+
+ s->classtable_size = classnr + 1; /* Adjust maximum number of entries */
+ }
+
+ s->classtable[classnr].reg.offset = seeker_offset;
+ s->classtable[classnr].reg.segment = 0;
+ s->classtable[classnr].script = scriptnr;
+ }
+
+ seeker_ptr += getUInt16((byte*)seeker_ptr + 2) * 2;
+ seeker_offset += getUInt16((byte*)seeker_ptr + 2) * 2;
+ }
+ }
+ }
+
+ return 0;
+}
+static int
+create_class_table_sci0(state_t *s)
+{
+ int scriptnr;
+ unsigned int seeker;
+ int classnr;
+ int magic_offset; /* For strange scripts in older SCI versions */
+
+ resource_t *vocab996 = scir_find_resource(s->resmgr, sci_vocab, 996, 1);
+
+ if (!vocab996)
+ s->classtable_size = 20;
+ else
+ s->classtable_size = vocab996->size >> 2;
+
+ s->classtable = (class_t*)sci_calloc(sizeof(class_t), s->classtable_size);
+
+ for (scriptnr = 0; scriptnr < 1000; scriptnr++) {
+ int objtype = 0;
+ resource_t *script = scir_find_resource(s->resmgr, sci_script,
+ scriptnr, 0);
+
+ if (script) {
+ if (s->version < SCI_VERSION_FTU_NEW_SCRIPT_HEADER)
+ magic_offset = seeker = 2;
+ else
+ magic_offset = seeker = 0;
+
+ do {
+
+ while (seeker < script->size) {
+ unsigned int lastseeker = seeker;
+ objtype = getInt16(script->data + seeker);
+ if (objtype == sci_obj_class || objtype == sci_obj_terminator)
+ break;
+ seeker += getInt16(script->data + seeker + 2);
+ if (seeker <= lastseeker) {
+ sciprintf("Warning: Script version is invalid.\n");
+ sci_free(s->classtable);
+ return SCI_ERROR_INVALID_SCRIPT_VERSION;
+ }
+ }
+
+ if (objtype == sci_obj_class) {
+ int sugg_script;
+
+ seeker -= SCRIPT_OBJECT_MAGIC_OFFSET; /* Adjust position; script home is base +8 bytes */
+
+ classnr = getInt16(script->data + seeker + 4 + SCRIPT_SPECIES_OFFSET);
+ if (classnr >= s->classtable_size) {
+
+ if (classnr >= SCRIPT_MAX_CLASSTABLE_SIZE) {
+ fprintf(stderr,"Invalid class number 0x%x in script.%d(0x%x), offset %04x\n",
+ classnr, scriptnr, scriptnr, seeker);
+ return 1;
+ }
+
+ s->classtable = (class_t*)sci_realloc(s->classtable, sizeof(class_t) * (classnr + 1));
+ memset(&(s->classtable[s->classtable_size]), 0,
+ sizeof(class_t) * (1 + classnr - s->classtable_size)); /* Clear after resize */
+
+ s->classtable_size = classnr + 1; /* Adjust maximum number of entries */
+ }
+
+ sugg_script = suggested_script(vocab996, classnr);
+
+ /* First, test whether the script hasn't been claimed, or if it's been claimed by the wrong script */
+
+ if (sugg_script == -1 || scriptnr == sugg_script /*|| !s->classtable[classnr].reg.segment*/) {
+ /* Now set the home script of the class */
+ s->classtable[classnr].reg.offset = seeker + 4 - magic_offset;
+ s->classtable[classnr].reg.segment = 0;
+ s->classtable[classnr].script = scriptnr;
+ }
+
+ seeker += SCRIPT_OBJECT_MAGIC_OFFSET; /* Re-adjust position */
+
+ seeker += getInt16(script->data + seeker + 2); /* Move to next */
+ }
+
+ } while (objtype != sci_obj_terminator && seeker <= script->size);
+
+ }
+ }
+ scir_unlock_resource(s->resmgr, vocab996, sci_vocab, 996);
+ vocab996 = NULL;
+ return 0;
+}
+
+/* Architectural stuff: Init/Unintialize engine */
+int
+script_init_engine(state_t *s, sci_version_t version)
+{
+ int result;
+
+ s->max_version = SCI_VERSION(9,999,999); /* :-) */
+ s->min_version = 0; /* Set no real limits */
+ s->version = SCI_VERSION_DEFAULT_SCI0;
+ s->kernel_opt_flags = 0;
+
+ if (!version) {
+ s->version_lock_flag = 0;
+ } else {
+ s->version = version;
+ s->version_lock_flag = 1; /* Lock version */
+ }
+
+ script_detect_versions(s);
+
+ if (s->version >= SCI_VERSION(1,001,000))
+ result = create_class_table_sci11(s);
+ else
+ result = create_class_table_sci0(s);
+
+ sm_init(&s->seg_manager, s->version >= SCI_VERSION(1,001,000));
+ s->gc_countdown = GC_INTERVAL - 1;
+
+ if (result)
+ {
+ sciprintf("Failed to initialize class table\n");
+ return 1;
+ }
+
+ s->script_000_segment = script_get_segment(s, 0, SCRIPT_GET_LOCK);
+
+ if (s->script_000_segment <= 0) {
+ sciprintf("Failed to instantiate script.000\n");
+ return 1;
+ }
+
+ s->script_000 = &(s->seg_manager.heap[s->script_000_segment]->data.script);
+
+
+ s->sys_strings = sm_allocate_sys_strings(&s->seg_manager,
+ &s->sys_strings_segment);
+ /* Allocate static buffer for savegame and CWD directories */
+ sys_string_acquire(s->sys_strings, SYS_STRING_SAVEDIR, "savedir", MAX_SAVE_DIR_SIZE);
+
+ s->save_dir_copy = make_reg(s->sys_strings_segment, SYS_STRING_SAVEDIR);
+ s->save_dir_edit_offset = 0;
+
+ s->r_acc = s->r_prev = NULL_REG;
+ s->r_amp_rest = 0;
+
+ s->execution_stack = NULL; /* Start without any execution stack */
+ s->execution_stack_base = -1; /* No vm is running yet */
+ s->execution_stack_pos = -1; /* Start at execution stack position 0 */
+
+
+ s->kernel_names = vocabulary_get_knames(s->resmgr, &s->kernel_names_nr);
+ script_map_kernel(s);
+ /* Maps the kernel functions */
+
+ if (_init_vocabulary(s)) return 1;
+ if (s->selector_map.cantBeHere != -1)
+ version_require_later_than(s, SCI_VERSION_FTU_INVERSE_CANBEHERE);
+
+ s->restarting_flags = SCI_GAME_IS_NOT_RESTARTING;
+
+ s->bp_list = NULL; /* No breakpoints defined */
+ s->have_bp = 0;
+
+ s->file_handles_nr = 5;
+ s->file_handles = (FILE**)sci_calloc(sizeof(FILE *), s->file_handles_nr);
+ /* Allocate memory for file handles */
+
+ sci_init_dir(&(s->dirseeker));
+ s->dirseeker_outbuffer = NULL_REG;
+ /* Those two are used by FileIO for FIND_FIRST, FIND_NEXT */
+
+ if (s->version >= SCI_VERSION_FTU_LOFS_ABSOLUTE &&
+ s->version < SCI_VERSION(1,001,000))
+ sm_set_export_width(&s->seg_manager, 1);
+ else
+ sm_set_export_width(&s->seg_manager, 0);
+
+ sciprintf("Engine initialized\n");
+
+ if (_init_graphics_input(s))
+ return 1;
+
+ return 0;
+}
+
+
+void
+script_set_gamestate_save_dir(state_t *s, const char* path)
+{
+ sys_string_set(s->sys_strings, SYS_STRING_SAVEDIR, path);
+}
+
+void
+script_free_vm_memory(state_t *s)
+{
+ int i;
+
+ sciprintf("Freeing VM memory\n");
+ s->save_dir_copy_buf = NULL;
+
+ sci_free(s->classtable);
+ s->classtable = NULL;
+
+ /* Close all opened file handles */
+#ifndef _DOS
+ for (i = 1; i < s->file_handles_nr; i++)
+ if (s->file_handles[i])
+ fclose(s->file_handles[i]);
+#endif
+
+ sci_free(s->file_handles);
+ s->file_handles = NULL;
+
+ /* FIXME: file handles will NOT be closed under DOS. DJGPP generates an
+ exception fault whenever you try to close a never-opened file */
+}
+
+extern void
+free_kfunct_tables(state_t *s);
+/* From kernel.c */
+
+void
+script_free_engine(state_t *s)
+{
+ script_free_vm_memory(s);
+
+ sciprintf("Freeing state-dependant data\n");
+
+ free_kfunct_tables(s);
+
+ _free_vocabulary(s);
+
+}
+
+void
+script_free_breakpoints(state_t *s)
+{
+ breakpoint_t *bp, *bp_next;
+
+ /* Free breakpoint list */
+ bp = s->bp_list;
+ while (bp) {
+ bp_next = bp->next;
+ if (bp->type == BREAK_SELECTOR) sci_free (bp->data.name);
+ free (bp);
+ bp = bp_next;
+ }
+
+ s->bp_list = NULL;
+}
+
+/*************************************************************/
+/* Game instance stuff: Init/Unitialize state-dependant data */
+/*************************************************************/
+
+
+int
+game_init(state_t *s)
+{
+#ifdef __GNUC__
+# warning "Fixme: Use new VM instantiation code all over the place"
+#endif
+ reg_t game_obj; /* Address of the game object */
+ dstack_t *stack;
+
+ stack = sm_allocate_stack(&s->seg_manager, VM_STACK_SIZE,
+ &s->stack_segment);
+ s->stack_base = stack->entries;
+ s->stack_top = s->stack_base + VM_STACK_SIZE;
+
+ if (!script_instantiate(s, 0)) {
+ sciprintf("game_init(): Could not instantiate script 0\n");
+ return 1;
+ }
+
+ s->parser_valid = 0; /* Invalidate parser */
+ s->parser_event = NULL_REG; /* Invalidate parser event */
+
+ s->synonyms = NULL;
+ s->synonyms_nr = 0; /* No synonyms */
+
+ /* Initialize send_calls buffer */
+
+ if (!send_calls_allocated)
+ send_calls = (calls_struct_t*)sci_calloc(sizeof(calls_struct_t), send_calls_allocated = 16);
+
+ if (s->gfx_state && _reset_graphics_input(s))
+ return 1;
+
+ s->successor = NULL; /* No successor */
+ s->status_bar_text = NULL; /* Status bar is blank */
+ s->status_bar_foreground = 0;
+ s->status_bar_background = s->resmgr->sci_version >= SCI_VERSION_01_VGA ? 255 : 15;
+
+ sys_string_acquire(s->sys_strings, SYS_STRING_PARSER_BASE, "parser-base", MAX_PARSER_BASE);
+ s->parser_base = make_reg(s->sys_strings_segment, SYS_STRING_PARSER_BASE);
+
+ sci_get_current_time(&(s->game_start_time)); /* Get start time */
+ memcpy(&(s->last_wait_time), &(s->game_start_time), sizeof(GTimeVal));
+ /* Use start time as last_wait_time */
+
+ s->debug_mode = 0x0; /* Disable all debugging */
+ s->onscreen_console = 0; /* No onscreen console unless explicitly requested */
+
+ srand(time(NULL)); /* Initialize random number generator */
+
+ /* script_dissect(0, s->selector_names, s->selector_names_nr); */
+ game_obj = script_lookup_export(s, 0, 0);
+ /* The first entry in the export table of script 0 points to the game object */
+
+ s->game_name = sci_strdup(obj_get_name(s, game_obj));
+
+ if (!s->game_name) {
+ sciprintf("Error: script.000, export 0 ("PREG") does not\n"
+ " yield an object with a name -> sanity check failed\n",
+ PRINT_REG(game_obj));
+ return 1;
+ }
+
+ sciprintf(" \"%s\" at "PREG"\n", s->game_name, PRINT_REG(game_obj));
+
+ if (strlen((char *) s->game_name) >= MAX_GAMEDIR_SIZE) {
+
+ s->game_name[MAX_GAMEDIR_SIZE - 1] = 0; /* Fix length with brute force */
+ sciprintf(" Designation too long; was truncated to \"%s\"\n", s->game_name);
+ }
+
+ s->game_obj = game_obj;
+
+ /* Mark parse tree as unused */
+ s->parser_nodes[0].type = PARSE_TREE_NODE_LEAF;
+ s->parser_nodes[0].content.value = 0;
+
+ s->menubar = menubar_new(); /* Create menu bar */
+
+ return 0;
+}
+
+int
+game_exit(state_t *s)
+{
+ if (s->execution_stack) {
+ sci_free(s->execution_stack);
+ }
+
+#if 0
+ sfx_exit(&s->sound);
+/* Reinit because some other code depends on having a valid state */
+ game_init_sound(s, SFX_STATE_FLAG_NOSOUND);
+#else
+#endif
+
+ sm_destroy(&s->seg_manager);
+
+ if (s->synonyms_nr) {
+ sci_free(s->synonyms);
+ s->synonyms = NULL;
+ s->synonyms_nr = 0;
+ }
+
+ sciprintf("Freeing miscellaneous data...\n");
+
+#ifdef __GNUC__
+#warning "Free parser segment here"
+#endif
+ if (send_calls_allocated) {
+ sci_free(send_calls);
+ send_calls_allocated = 0;
+ }
+
+#ifdef __GNUC__
+#warning "Free scripts here"
+#endif
+
+ menubar_free(s->menubar);
+
+ _free_graphics_input(s);
+
+ sci_free(s->game_name);
+
+ return 0;
+}
+
diff --git a/engines/sci/engine/gc.c b/engines/sci/engine/gc.c
new file mode 100644
index 0000000000..063c58a076
--- /dev/null
+++ b/engines/sci/engine/gc.c
@@ -0,0 +1,334 @@
+/***************************************************************************
+ 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.
+
+***************************************************************************/
+
+#include "gc.h"
+
+#define WORKLIST_CHUNK_SIZE 32
+
+/*#define DEBUG_GC*/
+/*#define DEBUG_GC_VERBOSE*/
+
+typedef struct _worklist {
+ int used;
+ reg_t entries[WORKLIST_CHUNK_SIZE];
+ struct _worklist *next;
+} worklist_t;
+
+static worklist_t *
+fresh_worklist(worklist_t *old)
+{
+ worklist_t *retval = (worklist_t*)sci_malloc(sizeof(worklist_t));
+ retval->used = 0;
+ retval->next = old;
+ return retval;
+}
+
+static worklist_t *
+new_worklist()
+{
+ return fresh_worklist(NULL);
+}
+
+static void
+worklist_push(worklist_t **wlp, reg_t_hash_map_ptr hashmap, reg_t reg)
+{
+ worklist_t *wl = *wlp;
+ char added;
+
+ if (!reg.segment) /* No numbers */
+ return;
+
+#ifdef DEBUG_GC_VERBOSE
+ sciprintf("[GC] Adding "PREG"\n", PRINT_REG(reg));
+#endif
+
+ reg_t_hash_map_check_value(hashmap, reg, 1, &added);
+
+ if (!added)
+ return; /* already dealt with it */
+
+ if (!wl || wl->used == WORKLIST_CHUNK_SIZE)
+ *wlp = wl = fresh_worklist(wl);
+
+ wl->entries[wl->used++] = reg;
+}
+
+static int
+worklist_has_next(worklist_t *wl)
+{
+ return (wl && wl->used);
+}
+
+static reg_t
+worklist_pop(worklist_t **wlp)
+{
+ worklist_t *wl = *wlp;
+ reg_t retval;
+
+ if (!wl || !wl->used) {
+ fprintf(stderr, "Attempt to pop from empty worklist");
+ exit(1);
+ }
+
+ retval = wl->entries[--wl->used];
+
+ if (!wl->used) {
+ *wlp = wl->next;
+ sci_free(wl);
+ }
+
+ return retval;
+}
+
+static void
+free_worklist(worklist_t *wl)
+{
+ if (wl) {
+ if (wl->next)
+ free_worklist(wl->next);
+ sci_free(wl);
+ }
+}
+
+typedef struct {
+ seg_interface_t **interfaces;
+ int interfaces_nr;
+ reg_t_hash_map_ptr normal_map;
+} normaliser_t;
+
+void
+store_normalised(void *pre_normaliser, reg_t reg, int _)
+{
+ seg_interface_t *interfce;
+ normaliser_t *normaliser = (normaliser_t *) pre_normaliser;
+ interfce = (reg.segment < normaliser->interfaces_nr)
+ ? normaliser->interfaces[reg.segment]
+ : NULL;
+
+ if (interfce) {
+ reg = interfce->find_canonic_address(interfce, reg);
+ reg_t_hash_map_check_value(normaliser->normal_map, reg, 1, NULL);
+ }
+}
+
+static reg_t_hash_map_ptr
+normalise_hashmap_ptrs(reg_t_hash_map_ptr nonnormal_map, seg_interface_t **interfaces, int interfaces_nr)
+{
+ normaliser_t normaliser;
+
+ normaliser.normal_map = new_reg_t_hash_map();
+ normaliser.interfaces_nr = interfaces_nr;
+ normaliser.interfaces = interfaces;
+ apply_to_reg_t_hash_map(nonnormal_map, &normaliser, &store_normalised);
+
+ return normaliser.normal_map;
+}
+
+
+typedef struct {
+ reg_t_hash_map_ptr nonnormal_map;
+ worklist_t **worklist_ref;
+} worklist_manager_t;
+
+void
+add_outgoing_refs(void *pre_wm, reg_t addr)
+{
+ worklist_manager_t *wm = (worklist_manager_t *) pre_wm;
+ worklist_push(wm->worklist_ref, wm->nonnormal_map, addr);
+}
+
+reg_t_hash_map_ptr
+find_all_used_references(state_t *s)
+{
+ seg_manager_t *sm = &(s->seg_manager);
+ seg_interface_t **interfaces = (seg_interface_t**)sci_calloc(sizeof(seg_interface_t *), sm->heap_size);
+ reg_t_hash_map_ptr nonnormal_map = new_reg_t_hash_map();
+ reg_t_hash_map_ptr normal_map = NULL;
+ worklist_t *worklist = new_worklist();
+ worklist_manager_t worklist_manager;
+ int i;
+
+ worklist_manager.worklist_ref = &worklist;
+ worklist_manager.nonnormal_map = nonnormal_map;
+
+ for (i = 1; i < sm->heap_size; i++)
+ if (sm->heap[i] == NULL)
+ interfaces[i] = NULL;
+ else
+ interfaces[i] = get_seg_interface(sm, i);
+
+ /* Initialise */
+ /* Init: Registers */
+ worklist_push(&worklist, nonnormal_map, s->r_acc);
+ worklist_push(&worklist, nonnormal_map, s->r_prev);
+ /* Init: Value Stack */
+ /* We do this one by hand since the stack doesn't know the current execution stack */
+ {
+ exec_stack_t *xs = s->execution_stack + s->execution_stack_pos;
+ reg_t *pos;
+
+ for (pos = s->stack_base; pos < xs->sp; pos++)
+ worklist_push(&worklist, nonnormal_map, *pos);
+ }
+#ifdef DEBUG_GC_VERBOSE
+ sciprintf("[GC] -- Finished adding value stack");
+#endif
+
+
+ /* Init: Execution Stack */
+ for (i = 0; i <= s->execution_stack_pos; i++) {
+ exec_stack_t *es = s->execution_stack + i;
+
+ if (es->type != EXEC_STACK_TYPE_KERNEL) {
+ worklist_push(&worklist, nonnormal_map, es->objp);
+ worklist_push(&worklist, nonnormal_map, es->sendp);
+ if (es->type == EXEC_STACK_TYPE_VARSELECTOR)
+ worklist_push(&worklist, nonnormal_map, *(es->addr.varp));
+ }
+ }
+#ifdef DEBUG_GC_VERBOSE
+ sciprintf("[GC] -- Finished adding execution stack");
+#endif
+
+ /* Init: Explicitly loaded scripts */
+ for (i = 1; i < sm->heap_size; i++)
+ if (interfaces[i]
+ && interfaces[i]->type_id == MEM_OBJ_SCRIPT) {
+ script_t *script = &(interfaces[i]->mobj->data.script);
+
+ if (script->lockers) { /* Explicitly loaded? */
+ int obj_nr;
+
+ /* Locals, if present */
+ worklist_push(&worklist, nonnormal_map, make_reg(script->locals_segment, 0));
+
+ /* All objects (may be classes, may be indirectly reachable) */
+ for (obj_nr = 0; obj_nr < script->objects_nr; obj_nr++) {
+ object_t *obj = script->objects + obj_nr;
+ worklist_push(&worklist,
+ nonnormal_map,
+ obj->pos);
+ }
+ }
+ }
+#ifdef DEBUG_GC_VERBOSE
+ sciprintf("[GC] -- Finished explicitly loaded scripts, done with root set");
+#endif
+
+
+ /* Run Worklist Algorithm */
+ while (worklist_has_next(worklist)) {
+ reg_t reg = worklist_pop(&worklist);
+ if (reg.segment != s->stack_segment) { /* No need to repeat this one */
+#ifdef DEBUG_GC_VERBOSE
+ sciprintf("[GC] Checking "PREG"\n", PRINT_REG(reg));
+#endif
+ if (reg.segment < sm->heap_size
+ && interfaces[reg.segment])
+ interfaces[reg.segment]->list_all_outgoing_references(interfaces[reg.segment],
+ s,
+ reg,
+ &worklist_manager,
+ add_outgoing_refs);
+ }
+ }
+
+ /* Normalise */
+ normal_map = normalise_hashmap_ptrs(nonnormal_map, interfaces, sm->heap_size);
+
+ /* Cleanup */
+ for (i = 1; i < sm->heap_size; i++)
+ if (interfaces[i])
+ interfaces[i]->deallocate_self(interfaces[i]);
+ sci_free(interfaces);
+ free_reg_t_hash_map(nonnormal_map);
+ return normal_map;
+}
+
+
+typedef struct {
+ seg_interface_t *interfce;
+#ifdef DEBUG_GC
+ char *segnames[MEM_OBJ_MAX + 1];
+ int segcount[MEM_OBJ_MAX + 1];
+#endif
+ reg_t_hash_map_ptr use_map;
+} deallocator_t;
+
+void
+free_unless_used (void *pre_use_map, reg_t addr)
+{
+ deallocator_t *deallocator = (deallocator_t *) pre_use_map;
+ reg_t_hash_map_ptr use_map = deallocator->use_map;
+
+ if (0 > reg_t_hash_map_check_value(use_map, addr, 0, NULL)) {
+ /* Not found -> we can free it */
+ deallocator->interfce->free_at_address(deallocator->interfce, addr);
+#ifdef DEBUG_GC
+ sciprintf("[GC] Deallocating "PREG"\n", PRINT_REG(addr));
+ deallocator->segcount[deallocator->interfce->type_id]++;
+#endif
+ }
+
+}
+
+void
+run_gc(state_t *s)
+{
+ int seg_nr;
+ deallocator_t deallocator;
+ seg_manager_t *sm = &(s->seg_manager);
+
+#ifdef DEBUG_GC
+ c_segtable(s);
+ sciprintf("[GC] Running...\n");
+ memset(&(deallocator.segcount), 0, sizeof(int) * (MEM_OBJ_MAX + 1));
+#endif
+
+ deallocator.use_map = find_all_used_references(s);
+
+ for (seg_nr = 1; seg_nr < sm->heap_size; seg_nr++)
+ if (sm->heap[seg_nr] != NULL) {
+ deallocator.interfce = get_seg_interface(sm, seg_nr);
+#ifdef DEBUG_GC
+ deallocator.segnames[deallocator.interfce->type_id] = deallocator.interfce->type;
+#endif
+
+ deallocator.interfce->list_all_deallocatable(deallocator.interfce,
+ &deallocator,
+ free_unless_used);
+
+ deallocator.interfce->deallocate_self(deallocator.interfce);
+ }
+
+ free_reg_t_hash_map(deallocator.use_map);
+
+#ifdef DEBUG_GC
+ {
+ int i;
+ sciprintf("[GC] Summary:\n");
+ for (i = 0; i <= MEM_OBJ_MAX; i++)
+ if (deallocator.segcount[i])
+ sciprintf("\t%d\t* %s\n",
+ deallocator.segcount[i],
+ deallocator.segnames[i]);
+ }
+#endif
+}
diff --git a/engines/sci/engine/gc.h b/engines/sci/engine/gc.h
new file mode 100644
index 0000000000..b0368ee8c2
--- /dev/null
+++ b/engines/sci/engine/gc.h
@@ -0,0 +1,41 @@
+/***************************************************************************
+ 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.
+
+***************************************************************************/
+
+
+#ifndef GC_H_
+#define GC_H_
+
+#include <reg_t_hashmap.h>
+#include <engine.h>
+
+reg_t_hash_map_ptr
+find_all_used_references(state_t *s);
+/* Finds all used references and normalises them to their memory addresses
+** Parameters: (state_t *) s: The state to gather all information from
+** Returns : (reg_t_hash_map_ptr) A hash map containing entries for all used references
+*/
+
+void
+run_gc(state_t *s);
+/* Runs garbage collection on the current system state
+** Parameters: (state_t *) s: The state in which we should gc
+*/
+
+#endif /* !defined(GC_H_) */
diff --git a/engines/sci/engine/grammar.c b/engines/sci/engine/grammar.c
new file mode 100644
index 0000000000..557c535b77
--- /dev/null
+++ b/engines/sci/engine/grammar.c
@@ -0,0 +1,720 @@
+/**************************************************************************
+ grammar.c Copyright (C) 2000 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.
+
+***************************************************************************/
+
+/* Functionality to transform the context-free SCI grammar rules into
+** strict Greibach normal form (strict GNF), and to test SCI input against
+** that grammar, writing an appropriate node tree if successful.
+*/
+
+#include <resource.h>
+#include <vocabulary.h>
+#include <console.h>
+#include <stdarg.h>
+#include <string.h>
+
+#define TOKEN_OPAREN 0xff000000
+#define TOKEN_CPAREN 0xfe000000
+#define TOKEN_TERMINAL_CLASS 0x10000
+#define TOKEN_TERMINAL_GROUP 0x20000
+#define TOKEN_STUFFING_WORD 0x40000
+#define TOKEN_NON_NT (TOKEN_OPAREN | TOKEN_TERMINAL_CLASS | TOKEN_TERMINAL_GROUP | TOKEN_STUFFING_WORD)
+#define TOKEN_TERMINAL (TOKEN_TERMINAL_CLASS | TOKEN_TERMINAL_GROUP)
+
+ int _allocd_rules = 0;
+
+static void
+vocab_print_rule(parse_rule_t *rule)
+{
+ int i;
+ int wspace = 0;
+
+ if (!rule) {
+ sciprintf("NULL rule");
+ return;
+ }
+
+ sciprintf("[%03x] -> ", rule->id);
+
+ if (!rule->length)
+ sciprintf("e");
+
+ for(i = 0; i < rule->length; i++) {
+ int token = rule->data[i];
+
+ if (token == TOKEN_OPAREN) {
+
+ if (i == rule->first_special)
+ sciprintf("_");
+
+ sciprintf("(");
+ wspace = 0;
+ } else if (token == TOKEN_CPAREN) {
+
+ if (i == rule->first_special)
+ sciprintf("_");
+
+ sciprintf(")");
+ wspace = 0;
+ } else {
+ if (wspace)
+ sciprintf(" ");
+
+ if (i == rule->first_special)
+ sciprintf("_");
+ if (token & TOKEN_TERMINAL_CLASS)
+ sciprintf("C(%04x)", token & 0xffff);
+ else if (token & TOKEN_TERMINAL_GROUP)
+ sciprintf("G(%04x)", token & 0xffff);
+ else if (token & TOKEN_STUFFING_WORD)
+ sciprintf("%03x", token & 0xffff);
+ else
+ sciprintf("[%03x]", token); /* non-terminal */
+ wspace = 1;
+ }
+
+ if (i == rule->first_special)
+ sciprintf("_");
+ }
+ sciprintf(" [%d specials]", rule->specials_nr);
+}
+
+
+static void
+_vfree(parse_rule_t *rule)
+{
+ free(rule);
+ --_allocd_rules;
+ rule = NULL;
+}
+
+static parse_rule_t *
+_vbuild(int id, int argc, ...)
+{
+ va_list args;
+ int i;
+ parse_rule_t *rule = (parse_rule_t*)sci_malloc(sizeof(int) * (argc + 4));
+
+ ++_allocd_rules;
+ rule->id = id;
+ rule->first_special = 0;
+ rule->specials_nr = 0;
+ rule->length = argc;
+ va_start(args, argc);
+ for (i = 0; i < argc; i++) {
+ int v;
+ rule->data[i] = v = va_arg(args, int);
+ if ((v & TOKEN_TERMINAL)
+ || !(v & TOKEN_NON_NT)) {
+
+ ++rule->specials_nr;
+
+ if (!rule->first_special)
+ rule->first_special = i;
+ }
+ }
+ va_end(args);
+ return rule;
+}
+
+static parse_rule_t *
+_vcat(int id, parse_rule_t *a, parse_rule_t *b)
+{
+ parse_rule_t *rule = (parse_rule_t*)sci_malloc(sizeof(int) * (a->length + b->length + 4));
+
+ rule->id = id;
+ rule->length = a->length + b->length;
+ rule->specials_nr = a->specials_nr + b->specials_nr;
+ rule->first_special = a->first_special;
+ ++_allocd_rules;
+
+ memcpy(rule->data, a->data, sizeof(int) * a->length);
+ memcpy(&(rule->data[a->length]), b->data, sizeof(int) * b->length);
+
+ return rule;
+}
+
+static parse_rule_t *
+_vdup(parse_rule_t *a)
+{
+ parse_rule_t *rule = (parse_rule_t*)sci_malloc(sizeof(int) * (a->length + 4));
+
+ rule->id = a->id;
+ rule->length = a->length;
+ rule->specials_nr = a->specials_nr;
+ rule->first_special = a->first_special;
+ ++_allocd_rules;
+
+ memcpy(rule->data, a->data, sizeof(int) * a->length);
+
+ return rule;
+}
+
+static parse_rule_t *
+_vinsert(parse_rule_t *turkey, parse_rule_t *stuffing)
+{
+ int firstnt = turkey->first_special;
+ parse_rule_t *rule;
+
+ while ((firstnt < turkey->length)
+ && (turkey->data[firstnt] & TOKEN_NON_NT))
+ firstnt++;
+
+ if ((firstnt == turkey->length)
+ || (turkey->data[firstnt] != stuffing->id))
+ return NULL;
+
+ rule = (parse_rule_t*)sci_malloc(sizeof(int) * (turkey->length - 1 + stuffing->length + 4));
+ rule->id = turkey->id;
+ rule->specials_nr = turkey->specials_nr + stuffing->specials_nr - 1;
+ rule->first_special = firstnt + stuffing->first_special;
+ rule->length = turkey->length - 1 + stuffing->length;
+ ++_allocd_rules;
+
+ if (firstnt > 0)
+ memcpy(rule->data, turkey->data, sizeof(int) * firstnt);
+ memcpy(&(rule->data[firstnt]), stuffing->data, sizeof(int) * stuffing->length);
+ if (firstnt < turkey->length - 1)
+ memcpy(&(rule->data[firstnt + stuffing->length]), &(turkey->data[firstnt + 1]),
+ sizeof(int) * (turkey->length - firstnt - 1));
+
+ return rule;
+}
+
+
+static int
+_greibach_rule_p(parse_rule_t *rule)
+{
+ int pos = rule->first_special;
+ while (pos < rule->length
+ && (rule->data[pos] & TOKEN_NON_NT)
+ && !(rule->data[pos] & TOKEN_TERMINAL))
+ ++pos;
+
+ if (pos == rule->length)
+ return 0;
+
+ return (rule->data[pos] & TOKEN_TERMINAL);
+}
+
+static parse_rule_t *
+_vbuild_rule(parse_tree_branch_t *branch)
+{
+ parse_rule_t *rule;
+ int tokens = 0, tokenpos = 0, i;
+
+ while (tokenpos < 10 && branch->data[tokenpos]) {
+ int type = branch->data[tokenpos];
+ tokenpos += 2;
+
+ if ((type == VOCAB_TREE_NODE_COMPARE_TYPE)
+ || (type == VOCAB_TREE_NODE_COMPARE_GROUP)
+ || (type == VOCAB_TREE_NODE_FORCE_STORAGE))
+ ++tokens;
+ else if (type > VOCAB_TREE_NODE_LAST_WORD_STORAGE)
+ tokens += 5;
+ else return NULL; /* invalid */
+ }
+
+ rule = (parse_rule_t*)sci_malloc(sizeof(int) * (4 + tokens));
+
+ ++_allocd_rules;
+ rule->id = branch->id;
+ rule->specials_nr = tokenpos >> 1;
+ rule->length = tokens;
+ rule->first_special = 0;
+
+ tokens = 0;
+ for (i = 0; i < tokenpos; i += 2) {
+ int type = branch->data[i];
+ int value = branch->data[i + 1];
+
+ if (type == VOCAB_TREE_NODE_COMPARE_TYPE)
+ rule->data[tokens++] = value | TOKEN_TERMINAL_CLASS;
+ else if (type == VOCAB_TREE_NODE_COMPARE_GROUP)
+ rule->data[tokens++] = value | TOKEN_TERMINAL_GROUP;
+ else if (type == VOCAB_TREE_NODE_FORCE_STORAGE)
+ rule->data[tokens++] = value | TOKEN_STUFFING_WORD;
+ else { /* normal inductive rule */
+ rule->data[tokens++] = TOKEN_OPAREN;
+ rule->data[tokens++] = type | TOKEN_STUFFING_WORD;
+ rule->data[tokens++] = value | TOKEN_STUFFING_WORD;
+
+ if (i == 0)
+ rule->first_special = tokens;
+
+ rule->data[tokens++] = value; /* The non-terminal */
+ rule->data[tokens++] = TOKEN_CPAREN;
+ }
+ }
+
+ return rule;
+}
+
+
+static parse_rule_t *
+_vsatisfy_rule(parse_rule_t *rule, result_word_t *input)
+{
+ int dep;
+
+ if (!rule->specials_nr)
+ return NULL;
+
+ dep = rule->data[rule->first_special];
+
+ if (((dep & TOKEN_TERMINAL_CLASS)
+ && ((dep & 0xffff) & input->w_class))
+ ||
+ ((dep & TOKEN_TERMINAL_GROUP)
+ && ((dep & 0xffff) & input->group))) {
+ parse_rule_t *retval = (parse_rule_t*)sci_malloc(sizeof(int) * (4 + rule->length));
+ ++_allocd_rules;
+ retval->id = rule->id;
+ retval->specials_nr = rule->specials_nr - 1;
+ retval->length = rule->length;
+ memcpy(retval->data, rule->data, sizeof(int) * retval->length);
+ retval->data[rule->first_special] = TOKEN_STUFFING_WORD | input->group;
+ retval->first_special = 0;
+
+ if (retval->specials_nr) { /* find first special, if it exists */
+ int tmp, i = rule->first_special;
+
+ while ((i < rule->length)
+ && ((tmp = retval->data[i]) & TOKEN_NON_NT)
+ && !(tmp & TOKEN_TERMINAL))
+ ++i;
+
+ if (i < rule->length)
+ retval->first_special = i;
+ }
+
+ return retval;
+ }
+ else return NULL;
+}
+
+/************** Rule lists **************/
+
+void
+vocab_free_rule_list(parse_rule_list_t *list)
+{
+ if (list) {
+ _vfree(list->rule);
+ vocab_free_rule_list(list->next); /* Yep, this is slow and memory-intensive. */
+ free(list);
+ }
+}
+
+static inline int
+_rules_equal_p(parse_rule_t *r1, parse_rule_t *r2)
+{
+ if ((r1->id != r2->id)
+ || (r1->length != r2->length)
+ || (r1->first_special != r2->first_special))
+ return 0;
+
+ return !(memcmp(r1->data, r2->data, sizeof(int) * r1->length));
+}
+
+static parse_rule_list_t *
+_vocab_add_rule(parse_rule_list_t *list, parse_rule_t *rule)
+{
+ parse_rule_list_t *new_elem;
+ int term;
+
+ if (!rule)
+ return list;
+
+ new_elem = (parse_rule_list_t*)sci_malloc(sizeof(parse_rule_list_t));
+ term = rule->data[rule->first_special];
+
+ new_elem->rule = rule;
+ new_elem->next = NULL;
+ new_elem->terminal = term = ((term & TOKEN_TERMINAL)? term : 0);
+
+ if (!list)
+ return new_elem;
+ else/* if (term < list->terminal) {
+ new_elem->next = list;
+ return new_elem;
+ } else*/ {
+ parse_rule_list_t *seeker = list;
+
+ while (seeker->next/* && seeker->next->terminal <= term*/) {
+ if (seeker->next->terminal == term)
+ if (_rules_equal_p(seeker->next->rule, rule)) {
+ _vfree(rule);
+ free(new_elem);
+ return list; /* No duplicate rules */
+ }
+ seeker = seeker->next;
+ }
+
+ new_elem->next = seeker->next;
+ seeker->next = new_elem;
+ return list;
+ }
+}
+
+static void
+_vprl(parse_rule_list_t *list, int pos)
+{
+ if (list) {
+ sciprintf("R%03d: ", pos);
+ vocab_print_rule(list->rule);
+ sciprintf("\n");
+ _vprl(list->next, pos+1);
+ } else {
+ sciprintf("%d rules total.\n", pos);
+ }
+}
+
+void
+vocab_print_rule_list(parse_rule_list_t *list)
+{
+ _vprl(list, 0);
+}
+
+static parse_rule_list_t *
+_vocab_split_rule_list(parse_rule_list_t *list)
+{
+ if (!list->next
+ || (list->next->terminal)) {
+ parse_rule_list_t *tmp = list->next;
+ list->next = NULL;
+ return tmp;
+ }
+ else return _vocab_split_rule_list(list->next);
+}
+
+static void
+_vocab_free_empty_rule_list(parse_rule_list_t *list)
+{
+ if (list->next)
+ _vocab_free_empty_rule_list(list->next);
+
+ free(list);
+}
+
+static parse_rule_list_t *
+_vocab_merge_rule_lists(parse_rule_list_t *l1, parse_rule_list_t *l2)
+{
+ parse_rule_list_t *retval = l1, *seeker = l2;
+ while (seeker) {
+ retval = _vocab_add_rule(retval, seeker->rule);
+ seeker = seeker->next;
+ }
+ _vocab_free_empty_rule_list(l2);
+
+ return retval;
+}
+
+static int
+_vocab_rule_list_length(parse_rule_list_t *list)
+{
+ return ((list)? _vocab_rule_list_length(list->next) + 1 : 0);
+}
+
+
+static parse_rule_list_t *
+_vocab_clone_rule_list_by_id(parse_rule_list_t *list, int id)
+{
+ parse_rule_list_t *result = NULL;
+ parse_rule_list_t *seeker = list;
+
+ while (seeker) {
+ if (seeker->rule->id == id) {
+ result = _vocab_add_rule(result, _vdup(seeker->rule));
+ }
+ seeker = seeker->next;
+ }
+
+ return result;
+}
+
+
+parse_rule_list_t *
+_vocab_build_gnf(parse_tree_branch_t *branches, int branches_nr, int verbose)
+{
+ int i;
+ int iterations = 0;
+ int last_termrules, termrules = 0;
+ int ntrules_nr;
+ parse_rule_list_t *ntlist = NULL;
+ parse_rule_list_t *tlist, *new_tlist;
+
+ for (i = 1; i < branches_nr; i++) { /* branch rule 0 is treated specially */
+ parse_rule_t *rule = _vbuild_rule(branches + i);
+
+ if (!rule) return NULL;
+ ntlist = _vocab_add_rule(ntlist, rule);
+ }
+
+ tlist = _vocab_split_rule_list(ntlist);
+ ntrules_nr = _vocab_rule_list_length(ntlist);
+
+ if (verbose)
+ sciprintf("Starting with %d rules\n", ntrules_nr);
+
+ new_tlist = tlist;
+ tlist = NULL;
+
+ do {
+ parse_rule_list_t *new_new_tlist = NULL;
+ parse_rule_list_t *ntseeker, *tseeker;
+ last_termrules = termrules;
+
+ ntseeker = ntlist;
+ while (ntseeker) {
+ tseeker = new_tlist;
+
+ while (tseeker) {
+ parse_rule_t *newrule = _vinsert(ntseeker->rule, tseeker->rule);
+ if (newrule)
+ new_new_tlist = _vocab_add_rule(new_new_tlist, newrule);
+ tseeker = tseeker->next;
+ }
+
+ ntseeker = ntseeker->next;
+ }
+
+ tlist = _vocab_merge_rule_lists(tlist, new_tlist);
+
+ new_tlist = new_new_tlist;
+
+ termrules = _vocab_rule_list_length(new_new_tlist);
+
+ if (verbose)
+ sciprintf("After iteration #%d: %d new term rules\n", ++iterations, termrules);
+ } while (termrules && (iterations < 30));
+
+ vocab_free_rule_list(ntlist);
+
+ if (verbose) {
+ sciprintf("\nGNF rules:\n");
+ vocab_print_rule_list(tlist);
+ }
+
+ return tlist;
+}
+
+parse_rule_list_t *
+vocab_build_gnf(parse_tree_branch_t *branches, int branches_nr)
+{
+ return _vocab_build_gnf(branches, branches_nr, 0);
+}
+
+
+void
+vocab_gnf_dump(parse_tree_branch_t *branches, int branches_nr)
+{
+ parse_rule_list_t *tlist = _vocab_build_gnf(branches, branches_nr, 1);
+
+ sciprintf("%d allocd rules\n", _allocd_rules);
+ vocab_free_rule_list(tlist);
+}
+
+
+int
+vocab_build_parse_tree(parse_tree_node_t *nodes, result_word_t *words, int words_nr,
+ parse_tree_branch_t *branch0, parse_rule_list_t *rules)
+{
+ return vocab_gnf_parse(nodes, words, words_nr, branch0, rules, 0);
+}
+
+
+static int
+_vbpt_pareno(parse_tree_node_t *nodes, int *pos, int base)
+/* Opens parentheses */
+{
+ nodes[base].content.branches[0] = (*pos)+1;
+ nodes[++(*pos)].type = PARSE_TREE_NODE_BRANCH;
+ nodes[*pos].content.branches[0] = 0;
+ nodes[*pos].content.branches[1] = 0;
+ return *pos;
+}
+
+
+static int
+_vbpt_parenc(parse_tree_node_t *nodes, int *pos, int paren)
+/* Closes parentheses for appending */
+{
+ nodes[paren].content.branches[1] = ++(*pos);
+ nodes[*pos].type = PARSE_TREE_NODE_BRANCH;
+ nodes[*pos].content.branches[0] = 0;
+ nodes[*pos].content.branches[1] = 0;
+ return *pos;
+}
+
+
+static int
+_vbpt_append(parse_tree_node_t *nodes, int *pos, int base, int value)
+/* writes one value to an existing base node and creates a successor node for writing */
+{
+ nodes[base].content.branches[0] = ++(*pos);
+ nodes[*pos].type = PARSE_TREE_NODE_LEAF;
+ nodes[*pos].content.value = value;
+ nodes[base].content.branches[1] = ++(*pos);
+ nodes[*pos].type = PARSE_TREE_NODE_BRANCH;
+ nodes[*pos].content.branches[0] = 0;
+ nodes[*pos].content.branches[1] = 0;
+ return *pos;
+}
+
+
+static int
+_vbpt_terminate(parse_tree_node_t *nodes, int *pos, int base, int value)
+ /* Terminates, overwriting a nextwrite forknode */
+{
+ nodes[base].type = PARSE_TREE_NODE_LEAF;
+ nodes[base].content.value = value;
+ return *pos;
+}
+
+static int
+_vbpt_write_subexpression(parse_tree_node_t *nodes, int *pos,
+ parse_rule_t *rule, int rulepos, int writepos)
+{
+ int token;
+ while ((token = ((rulepos < rule->length)? rule->data[rulepos++] : TOKEN_CPAREN)) != TOKEN_CPAREN) {
+ int nexttoken = (rulepos < rule->length)? rule->data[rulepos] : TOKEN_CPAREN;
+ if (token == TOKEN_OPAREN) {
+ int wpold;
+ int writepos2 = _vbpt_pareno(nodes, pos, wpold = writepos);
+ rulepos = _vbpt_write_subexpression(nodes, pos, rule, rulepos, writepos2);
+ nexttoken = (rulepos < rule->length)? rule->data[rulepos] : TOKEN_CPAREN;
+ if (nexttoken != TOKEN_CPAREN)
+ writepos = _vbpt_parenc(nodes, pos, wpold);
+ } else if (token & TOKEN_STUFFING_WORD) {
+ if (nexttoken == TOKEN_CPAREN)
+ writepos = _vbpt_terminate(nodes, pos, writepos, token & 0xffff);
+ else
+ writepos = _vbpt_append(nodes, pos, writepos, token & 0xffff);
+ } else {
+ sciprintf("\nError in parser (grammar.c, _vbpt_write_subexpression()): Rule data broken in rule ");
+ vocab_print_rule(rule);
+ sciprintf(", at token position %d\n", *pos);
+ return rulepos;
+ }
+ }
+
+ return rulepos;
+}
+
+int
+vocab_gnf_parse(parse_tree_node_t *nodes, result_word_t *words, int words_nr,
+ parse_tree_branch_t *branch0, parse_rule_list_t *tlist, int verbose)
+{
+ /* Get the start rules: */
+ parse_rule_list_t *work = _vocab_clone_rule_list_by_id(tlist, branch0->data[1]);
+ parse_rule_list_t *results = NULL;
+ int word;
+
+ for (word = 0; word < words_nr; word++) {
+ parse_rule_list_t *new_work = NULL;
+ parse_rule_list_t *reduced_rules = NULL;
+ parse_rule_list_t *seeker, *subseeker;
+
+ if (verbose)
+ sciprintf("Adding word %d...\n", word);
+
+ seeker = work;
+ while (seeker) {
+
+ if (seeker->rule->specials_nr <= (words_nr - word))
+ reduced_rules = _vocab_add_rule(reduced_rules, _vsatisfy_rule(seeker->rule, words + word));
+
+ seeker = seeker->next;
+ }
+
+ if (reduced_rules == NULL) {
+ vocab_free_rule_list(work);
+ if (verbose)
+ sciprintf("No results.\n");
+ return 1;
+ }
+
+ vocab_free_rule_list(work);
+
+ if (word +1 < words_nr) {
+ seeker = reduced_rules;
+
+ while (seeker) {
+ if (seeker->rule->specials_nr) {
+ int my_id = seeker->rule->data[seeker->rule->first_special];
+
+ subseeker = tlist;
+ while (subseeker) {
+ if (subseeker->rule->id == my_id)
+ new_work = _vocab_add_rule(new_work, _vinsert(seeker->rule, subseeker->rule));
+
+ subseeker = subseeker->next;
+ }
+ }
+
+ seeker = seeker->next;
+ }
+ vocab_free_rule_list(reduced_rules);
+ } else /* last word */
+ new_work = reduced_rules;
+
+ work = new_work;
+ if (verbose)
+ sciprintf("Now at %d candidates\n", _vocab_rule_list_length(work));
+ if (work == NULL) {
+ if (verbose)
+ sciprintf("No results.\n");
+ return 1;
+ }
+ }
+
+ results = work;
+
+ if (verbose) {
+ sciprintf("All results (excluding the surrounding '(141 %03x' and ')'):\n",
+ branch0->id);
+ vocab_print_rule_list(results);
+ sciprintf("\n");
+ }
+
+ /* now use the first result */
+ {
+ int temp, pos;
+
+ nodes[0].type = PARSE_TREE_NODE_BRANCH;
+ nodes[0].content.branches[0] = 1;
+ nodes[0].content.branches[1] = 2;
+
+ nodes[1].type = PARSE_TREE_NODE_LEAF;
+ nodes[1].content.value = 0x141;
+
+ nodes[2].type = PARSE_TREE_NODE_BRANCH;
+ nodes[2].content.branches[0] = 0;
+ nodes[2].content.branches[1] = 0;
+
+ pos = 2;
+
+ temp = _vbpt_append(nodes, &pos, 2, branch0->id);
+ /* _vbpt_write_subexpression(nodes, &pos, results[_vocab_rule_list_length(results)].rule, 0, temp); */
+ _vbpt_write_subexpression(nodes, &pos, results->rule, 0, temp);
+ }
+
+ vocab_free_rule_list(results);
+ return 0;
+}
diff --git a/engines/sci/engine/heap.c b/engines/sci/engine/heap.c
new file mode 100644
index 0000000000..cb5cacaffd
--- /dev/null
+++ b/engines/sci/engine/heap.c
@@ -0,0 +1,298 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <engine.h>
+#include <console.h>
+#include "heap.h"
+
+#define assert_in_range(pos) assert(pos>=1000 && pos<=0xffff)
+
+static void set_size(heap_t *h, int block_pos, int size)
+{
+ assert_in_range(block_pos);
+ assert(size<=0xffff-1000);
+ putInt16(h->start+block_pos, size);
+}
+
+static void set_next(heap_t* h, int block_pos, int next)
+{
+ assert_in_range(block_pos);
+ assert_in_range(next);
+ putInt16(h->start+block_pos+2, next);
+}
+
+
+static unsigned int get_size(heap_t* h, int block_pos)
+{
+ assert_in_range(block_pos);
+ return (guint16)getInt16(h->start+block_pos);
+}
+
+static unsigned int get_next(heap_t* h, int block_pos)
+{
+ assert_in_range(block_pos);
+ return (guint16)getInt16(h->start+block_pos+2);
+}
+
+/*Allocates a new heap*/
+heap_t* heap_new()
+{
+ heap_t* h;
+ if((h= (heap_t*)sci_malloc(sizeof(heap_t)))==0) return 0;
+
+ if((h->start= sci_calloc(SCI_HEAP_SIZE, 1))==0)
+ {
+ free(h);
+ return 0;
+ }
+
+ h->base=h->start+1000;
+ h->first_free=1000;
+ h->old_ff=-1;
+ set_size(h, 1000, 0xffff-1000);
+ set_next(h, 1000, 0xffff);
+
+ return h;
+}
+
+/*Deletes a heap*/
+void heap_del(heap_t* h)
+{
+ free(h->start);
+ free(h);
+}
+
+
+int heap_meminfo(heap_t* h)
+{
+ heap_ptr current = h->first_free;
+ int total = 0;
+
+ while (current != 0xffff) {
+ total += get_size(h, current);
+ current = get_next(h, current);
+ }
+
+ return total;
+}
+
+
+int heap_largest(heap_t* h)
+{
+ int current=h->first_free;
+ int best_pos=-1, best_size=0;
+
+ while(current!=0xffff)
+ {
+ int size=get_size(h, current);
+ int next=get_next(h, current);
+
+ if(size>best_size)
+ {
+ best_pos=current;
+ best_size=size;
+ }
+
+ current=next;
+ }
+
+ return best_size;
+}
+
+heap_ptr heap_allocate(heap_t* h, int size)
+{
+ unsigned int previous=h->first_free;
+ unsigned int current=previous;
+
+ if (!size) {
+ fprintf(stderr,"Warning: heap_alloc'd zero bytes!\n");
+ size += 2;
+ }
+
+ size+=2+(size&1);
+
+ while(current<0xffff)
+ {
+ int block_size=get_size(h, current);
+ int next=get_next(h, current);
+
+ /*Is this block large enough?*/
+ if(block_size>=size)
+ {
+ /*Swallow the block whole*/
+ if(block_size<=size+4)
+ {
+ size=block_size;
+ set_next(h, previous, next);
+ }
+ else {
+ /*Split the block*/
+ int rest=current+size;
+
+ set_next(h, previous, rest);
+ set_size(h, rest, block_size-size);
+ set_next(h, rest, next);
+ next=rest;
+ }
+ set_size(h, current, size);
+ if(current==h->first_free) h->first_free=next;
+ return current;
+ }
+ previous=current;
+ current=next;
+ }
+
+ /*No large enough block was found.*/
+ return 0;
+}
+
+void heap_free(heap_t* h, unsigned int m)
+{
+ unsigned int previous, next;
+ assert_in_range(m);
+ previous=next=h->first_free;
+
+ /*Find the previous and next blocks*/
+ while(next < m)
+ {
+ previous = next;
+ assert(previous<0xffff);
+ next=get_next(h, previous);
+ if (next <= previous) {
+ sciprintf("Heap corrupt. Aborting heap_free()...\n");
+ return;
+ }
+ }
+
+ if (h->first_free > m)
+ h->first_free = m; /* Guarantee that first_free is correct */
+
+ if(previous==next)
+ {
+ if(m<previous)
+ {
+ h->first_free=m;
+ if(m+get_size(h, m)==previous)
+ {
+ set_size(h, m, get_size(h, m)+get_size(h, previous));
+ set_next(h, m, get_next(h, previous));
+ }
+ else set_next(h, m, previous);
+ }
+ else
+ {
+ if(previous+get_size(h, previous)==m)
+ {
+ set_size(h, previous, get_size(h, previous)+get_size(h, m));
+ set_next(h, previous, 0xffff);
+ }
+ else
+ {
+ set_next(h, previous, m);
+ set_next(h, m, next);
+ }
+ }
+ }
+ else
+ {
+ set_next(h, previous, m);
+ set_next(h, m, next);
+
+ /*Try to merge with previous*/
+ if(previous+get_size(h, previous)==m)
+ {
+ set_size(h, previous, get_size(h, previous)+get_size(h, m));
+ set_next(h, previous, next);
+ m=previous;
+ }
+
+ /*Try to merge with next*/
+ if(m+get_size(h, m)==next)
+ {
+ set_size(h, m, get_size(h, m)+get_size(h, next));
+ set_next(h, m, get_next(h, next));
+ }
+ }
+}
+
+void save_ff(heap_t* h)
+{
+ h->old_ff=h->first_free;
+}
+
+void restore_ff(heap_t* h)
+{
+ h->first_free=h->old_ff;
+ set_size(h, h->first_free, 0xffff-h->first_free);
+ set_next(h, h->first_free, 0xffff);
+}
+
+
+
+void heap_dump_free(heap_t *h)
+{
+ int freedomseeker;
+
+ printf("\tfirst_free= %#x (oldff= %#x)\n\tFree Blocks:\n", h->first_free, h->old_ff);
+
+ freedomseeker = h->first_free;
+ while (freedomseeker != 0xffff) {
+ printf("\t %#04x: size: %#04x\n", freedomseeker, get_size(h, freedomseeker));
+ freedomseeker = get_next(h, freedomseeker);
+ }
+}
+
+void heap_dump_all(heap_t *h)
+{
+ int seeker = 1000;
+ int free_seeker = h->first_free;
+
+ while (seeker < 0xffff) {
+ int is_free = (seeker == free_seeker);
+ int size = get_size(h, seeker);
+
+ if (is_free)
+ free_seeker = get_next(h, free_seeker);
+
+ printf("%04x\t%d\t%s\n", seeker, size, is_free? "FREE": "");
+ seeker += size;
+ }
+}
+
+/*
+
+int main(int argc, char **argv) {
+ heap_t *h = heap_new();
+ int a,b,c,d,e;
+
+ printf("Running heap tests:\nHeap initialization:\n");
+ heap_dump_free(h);
+
+ printf("[a] Allocating 0x1: position is %#x\n", a = heap_allocate(h, 1));
+ heap_dump_free(h);
+
+ printf("[b] Allocating 0x10: position is %#x\n", b = heap_allocate(h, 0x10));
+ printf("[c] Allocating 0x10: position is %#x\n", c = heap_allocate(h, 0x10));
+ printf("[d] Allocating 0x10: position is %#x\n", d = heap_allocate(h, 0x10));
+ printf("[e] Allocating 0x1000: position is %#x\n", e = heap_allocate(h, 0x1000));
+ heap_dump_free(h);
+
+ printf("Freeing [b]:\n");
+ heap_free(h, b);
+ heap_dump_free(h);
+
+ printf("Freeing [d]:\n");
+ heap_free(h, d);
+ heap_dump_free(h);
+
+ printf("Freeing [c]:\n");
+ heap_free(h, c);
+ heap_dump_free(h);
+
+ heap_del(h);
+
+ return 0;
+}
+
+*/
diff --git a/engines/sci/engine/heap.h b/engines/sci/engine/heap.h
new file mode 100644
index 0000000000..afd2db5c58
--- /dev/null
+++ b/engines/sci/engine/heap.h
@@ -0,0 +1,125 @@
+/***************************************************************************
+ heap.h Copyright (C) 1999,2000,01 Magnus Reftel, 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]
+
+***************************************************************************/
+
+#ifndef _SCI_HEAP_H
+#define _SCI_HEAP_H
+
+#include <resource.h>
+
+#define SCI_HEAP_SIZE 0x10000
+
+typedef guint16 heap_ptr;
+
+
+typedef struct
+{
+ byte* start;
+ byte* base;
+ unsigned int first_free;
+ int old_ff;
+} heap_t;
+
+heap_t*
+heap_new();
+/* Allocates a new heap.
+** Parameters: (void)
+** Returns : (heap_t *) A new 0xffff-sized heap
+*/
+
+void
+heap_del(heap_t* h);
+/* Frees an allocated heap
+** Parameters: (heap_t *) h: The heap to unallocate
+** Returns : (void)
+*/
+
+int
+heap_meminfo(heap_t* h);
+/* Returns the total number of free bytes on the heap
+** Parameters: (heap_t *) h: The heap to check
+** Returns : (int) The total free space in bytes
+*/
+
+int
+heap_largest(heap_t* h);
+/* Returns the block size of the largest free block on the heap
+** Parameters: (heap_t *) h: The heap to check
+** Returns : (int) The size of the largest free block
+*/
+
+heap_ptr
+heap_allocate(heap_t* h, int size);
+/* Allocates memory on a heap.
+** Parameters: (heap_t *) h: The heap to work with
+** (int) size: The block size to allocate
+** Returns : (heap_ptr): The heap pointer to the new block, or 0 on failure
+*/
+
+void
+heap_free(heap_t* h, unsigned int m);
+/* Frees allocated heap memory.
+** Parameters: (heap_t *) h: The heap to work with
+** (int) m: The handle at which memory is to be unallocated
+** Returns : (void)
+** This function automatically prevents fragmentation from happening.
+*/
+
+void
+save_ff(heap_t* h);
+/* Stores the current first free position
+** Parameters: (heap_t *) h: The heap which is to be manipulated
+** Returns : (void)
+** This function can be used to save the heap state for later restoration (see
+** the next function)
+*/
+
+void
+restore_ff(heap_t* h);
+/* Restores the first free heap state
+** Parameters: (heap_t *) h: The heap to restore
+** Returns : (void)
+** Restoring the first free state will reset the heap to the position stored
+** when save_ff() was called, if and only if none of the blocks allocated before
+** save_ff() was called was ever freed ("ever" includes "before save_ff() was
+** called").
+*/
+
+void
+heap_dump_free(heap_t *h);
+/* Dumps debugging information about the stack
+** Parameters: (heap_t *) h: The heap to check
+** Returns : (void)
+*/
+
+void
+heap_dump_all(heap_t *h);
+/* Dumps all allocated/unallocated zones on the heap
+** Parameters: (heap_t *) h: The heap to check
+** Returns : (void)
+*/
+
+#endif /* !_SCI_HEAP_H */
diff --git a/engines/sci/engine/kemu_old.c b/engines/sci/engine/kemu_old.c
new file mode 100644
index 0000000000..4ec6c0a106
--- /dev/null
+++ b/engines/sci/engine/kemu_old.c
@@ -0,0 +1,330 @@
+/***************************************************************************
+ kemu_old.c Copyright (C) 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 (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+/* Emulation code for old kernel functions */
+
+#include <engine.h>
+#include "kernel_compat.h"
+#include "kernel_types.h"
+#include "heap.h"
+
+#define EMU_HEAP_START_ADDR 1000
+
+#define EMU_TYPE_VERBATIM 0 /* Arithmetic values */
+#define EMU_TYPE_REGISTERS 1 /* Must re-interpret afterwards. Also used for objects. */
+#define EMU_TYPE_BLOCK 2 /* Bulk data; string or similar. May physically point to stack */
+
+static int funct_nr;
+
+typedef struct {
+ heap_ptr start; /* Beginning of the block */
+ reg_t pos; /* Needed for resolving conflicts */
+ int length; /* Number of bytes occupied on the heap */
+ object_t *obj; /* For object types: Pointer to the physical object */
+
+ /* Emulation data part */
+ int emudat_type; /* EMU_TYPE_... */
+ heap_ptr emudat_location; /* Only for 1 and 2 */
+ union {
+ reg_t *regs; /* registers, for type 1 */
+ byte *block; /* Bulk data location, for type 2 */
+ } emu_data;
+ int emudat_size; /* Amount of bytes or number of entries */
+} emu_param_t;
+
+static inline emu_param_t
+identify_value(state_t *s, reg_t reg, heap_ptr firstfree)
+{
+ emu_param_t retval;
+ int type = determine_reg_type(s, reg);
+
+ retval.start = firstfree;
+ retval.pos = reg;
+
+ retval.obj = NULL;
+ retval.length = 0;
+ retval.emudat_type = EMU_TYPE_VERBATIM; /* To allow error aborts */
+
+ if (type & KSIG_NULL)
+ type &= KSIG_ARITHMETIC;
+
+ switch (type) {
+
+ case KSIG_ARITHMETIC: /* trivial */
+ retval.emudat_size = 0;
+ break;
+
+ case KSIG_OBJECT:
+ retval.emudat_type = EMU_TYPE_REGISTERS;
+ retval.obj = obj_get(s, reg);
+ if (!retval.obj) { BREAKPOINT(); }
+ retval.length = -SCRIPT_OBJECT_MAGIC_OFFSET
+ + retval.obj->variables_nr * 4 /* values and selectors */
+ + 2; /* Extra magic selector word (is this really needed?) */
+ retval.emu_data.regs = retval.obj->variables;
+ retval.emudat_size = retval.obj->variables_nr;
+ retval.emudat_location = retval.start - SCRIPT_OBJECT_MAGIC_OFFSET;
+ break;
+
+ case KSIG_REF: {
+ retval.emudat_type = EMU_TYPE_BLOCK;
+
+ retval.emu_data.block =
+ s->seg_manager.dereference(&s->seg_manager,
+ reg, &retval.emudat_size);
+
+ if (!retval.emu_data.block) {
+ SCIkdebug(SCIkERROR, "Cannot handle references into segment type %02x"
+ " (from "PREG") during emulation\n",
+ type, PRINT_REG(reg));
+ retval.emudat_type = EMU_TYPE_VERBATIM;
+ retval.length = 0;
+ }
+
+ retval.length = retval.emudat_size;
+ retval.emudat_location = retval.start;
+
+ break;
+ }
+
+ default:
+ SCIkdebug(SCIkERROR, "Cannot handle argument type %02x (from "PREG
+ ") during emulation\n",
+ type, PRINT_REG(reg));
+ }
+
+ return retval;
+}
+
+static inline void
+writeout_value(state_t *s, emu_param_t *p)
+{
+ if (p->obj) /* First copy object; don't need to read back this part later */
+ memcpy(s->heap + p->start, p->obj->base_obj + SCRIPT_OBJECT_MAGIC_OFFSET,
+ p->length);
+
+ switch (p->emudat_type) {
+
+ case EMU_TYPE_VERBATIM:
+ SCIkdebug(SCIkEMU, "\tVerbatim/Arithmetic\n");
+ return;
+
+ case EMU_TYPE_REGISTERS: {
+ int i, max = p->emudat_size;
+
+ SCIkdebug(SCIkEMU, "\tObject, %d selectors\n", max);
+
+ for (i = 0; i < max; i++) {
+ byte *dest = s->heap + p->emudat_location + (i << 1);
+
+ dest[0] = p->emu_data.regs[i].offset & 0xff;
+ dest[1] = (p->emu_data.regs[i].offset >> 8) & 0xff;
+ /* Assume they're all numeric values */
+ }
+ return;
+ }
+
+ case EMU_TYPE_BLOCK:
+ SCIkdebug(SCIkEMU, "\tBulk\n");
+ memcpy(s->heap + p->emudat_location, p->emu_data.block, p->emudat_size);
+ return;
+
+ default:
+ BREAKPOINT();
+ }
+}
+
+static inline void
+recover_value(state_t *s, emu_param_t *p)
+{ /* Writes back the value */
+ switch (p->emudat_type) {
+
+ case EMU_TYPE_VERBATIM:
+ return;
+
+ case EMU_TYPE_REGISTERS: {
+ int i, max = p->emudat_size;
+ for (i = 0; i < max; i++) {
+ int val = GET_HEAP(p->emudat_location + (i << 1));
+ if (p->emu_data.regs[i].offset != val) {
+ SCIkdebug(SCIkEMU, " Recovering property #%d from %04x: 0:%04x\n",
+ i, p->emudat_location + (i << 1), val);
+ p->emu_data.regs[i] = make_reg(0, val);
+ } else {
+ SCIkdebug(SCIkEMU, " Property #%d from %04x is unchanged (%04x vs "PREG")\n",
+ i, p->emudat_location + (i << 1), val, PRINT_REG(p->emu_data.regs[i]));
+ }
+ /* Don't overwrite unless something changed, to preserve pointers */
+ }
+ return;
+ }
+
+ case EMU_TYPE_BLOCK:
+ memcpy(p->emu_data.block, s->heap + p->emudat_location, p->emudat_size);
+ SCIkdebug(SCIkEMU, " Recovering %d raw bytes from %04x\n",
+ p->emudat_size, p->emudat_location);
+ return;
+
+ default:
+ BREAKPOINT();
+ }
+}
+
+static void
+_restrict_against(state_t *s, emu_param_t *self, emu_param_t *other)
+{
+ if (self->pos.segment != other->pos.segment)
+ return;
+
+ if (self->pos.offset <= other->pos.offset
+ && self->pos.offset + self->emudat_size > other->pos.offset) {
+ mem_obj_t *mobj = GET_SEGMENT_ANY(s->seg_manager, self->pos.segment);
+
+ self->emudat_size = other->pos.offset - self->pos.offset;
+
+ if (mobj && mobj->type == MEM_OBJ_STACK)
+ self->emudat_size *= sizeof(reg_t); /* Accomodate for size differences */
+ }
+}
+
+static void
+resolve_conflicts(state_t *s, emu_param_t *params, int count)
+{
+ int i, j;
+ for (i = 0; i < count; i++)
+ for (j = 0; j < count; j++)
+ if (i != j)
+ _restrict_against(s, params + i, params + j);
+}
+
+reg_t
+kFsciEmu(state_t *s, int _funct_nr, int argc, reg_t *argv)
+{
+ emu_param_t *shadow_args;
+ funct_nr = _funct_nr;
+
+ if (!s->kfunct_emu_table[funct_nr]) {
+ SCIkwarn(SCIkERROR, "Attempt to emulate unknown kernel function %x\n",
+ funct_nr);
+ return NULL_REG;
+ } else {
+ heap_ptr argp = EMU_HEAP_START_ADDR; /* arguments go here */
+ heap_ptr datap = argp + argc * 2; /* copied stuff goes here */
+ int i;
+
+ shadow_args = sci_malloc(sizeof(emu_param_t) * (argc + 1
+ /* Prevents stupid warning */));
+ memset(shadow_args, 0, sizeof(emu_param_t) * (argc + 1));
+
+ SCIkdebug(SCIkEMU, "Emulating kernel call %s[%x] w/ %d arguments at HP:%04x\n",
+ s->kernel_names[funct_nr], funct_nr, argc, argp);
+
+ for (i = 0; i < argc; i++) {
+ int emu_value = argv[i].offset; /* Value we'll pass to the function */
+
+ SCIkdebug(SCIkEMU, " %3d : ["PREG"] ->\n", i, PRINT_REG(argv[i]));
+
+ shadow_args[i] = identify_value(s, argv[i], datap);
+
+ switch (shadow_args[i].emudat_type) {
+
+ case EMU_TYPE_VERBATIM:
+ break;
+
+ case EMU_TYPE_REGISTERS:
+ case EMU_TYPE_BLOCK:
+ emu_value = shadow_args[i].emudat_location;
+ break;
+
+ default:
+ BREAKPOINT();
+
+ }
+
+ SCIkdebug(SCIkEMU, "\t%04x [%d at %04x]\n",
+ emu_value, shadow_args[i].length, shadow_args[i].start);
+
+ PUT_HEAP(argp, emu_value);
+ argp += 2;
+
+ if (0xffff - shadow_args[i].length < datap) {
+ SCIkdebug(SCIkERROR, "Out of heap space while emulating!\n");
+ return NULL_REG;
+ }
+ datap += shadow_args[i].length; /* Step over last block we wrote */
+ }
+
+ for (i = 0; i < argc; i++)
+ writeout_value(s, shadow_args + i);
+
+ resolve_conflicts(s, shadow_args, argc);
+
+ s->kfunct_emu_table[funct_nr](s, funct_nr, argc, EMU_HEAP_START_ADDR);
+
+ for (i = 0; i < argc; i++)
+ recover_value(s, shadow_args + i);
+ free(shadow_args);
+ return make_reg(0, s->acc);
+ }
+}
+
+
+int
+read_selector16(state_t *s, heap_ptr object, selector_t selector_id, const char *file, int line)
+{
+ int slc_count = GET_HEAP(object + SCRIPT_SELECTORCTR_OFFSET);
+ int i;
+ heap_ptr slc_names = object + slc_count * 2;
+
+ for (i = 0; i < slc_count; i++) {
+ if (GET_HEAP(slc_names + (i<<1)) == selector_id)
+ return GET_HEAP(object + (i<<1));
+ }
+
+ SCIkdebug(SCIkWARNING, "[EMU] Could not read selector %d from HP:%04x (%s, L%d)\n",
+ selector_id, object, file, line);
+ return 0;
+}
+
+void
+write_selector16(state_t *s, heap_ptr object, selector_t selector_id,
+ int value, const char *fname, int line)
+{
+ int slc_count = GET_HEAP(object + SCRIPT_SELECTORCTR_OFFSET);
+ int i;
+ heap_ptr slc_names = object + slc_count * 2;
+
+ for (i = 0; i < slc_count; i++) {
+ if (GET_HEAP(slc_names + (i<<1)) == selector_id) {
+ PUT_HEAP(object + (i<<1), value);
+ return;
+ }
+ }
+
+ SCIkdebug(SCIkWARNING, "[EMU] Could not write selector %d from HP:%04x (%s, L%d)\n",
+ selector_id, object, fname, line);
+}
+
diff --git a/engines/sci/engine/kernel.c b/engines/sci/engine/kernel.c
new file mode 100644
index 0000000000..df3690f467
--- /dev/null
+++ b/engines/sci/engine/kernel.c
@@ -0,0 +1,1092 @@
+/***************************************************************************
+ kernel.c Copyright (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]
+
+***************************************************************************/
+
+#include "gc.h"
+#include <sciresource.h>
+#include <engine.h>
+#ifdef _WIN32
+# include <windows.h>
+#endif /* _WIN32 */
+
+#include <gfx_operations.h>
+#include "kernel_types.h"
+
+
+/* New kernel functions */
+reg_t kStrLen(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kGetFarText(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kReadNumber(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kStrCat(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kStrCmp(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kSetSynonyms(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kLock(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kPalette(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kNumCels(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kNumLoops(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kDrawCel(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kCoordPri(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kPriCoord(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kShakeScreen(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kSetCursor(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kMoveCursor(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kShow(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kPicNotValid(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kOnControl(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kDrawPic(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kGetPort(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kSetPort(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kNewWindow(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kDisposeWindow(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kCelWide(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kCelHigh(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kSetJump(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kDirLoop(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kDoAvoider(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kGetAngle(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kGetDistance(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kRandom(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kAbs(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kSqrt(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kTimesSin(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kTimesCos(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kCosMult(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kSinMult(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kTimesTan(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kTimesCot(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kCosDiv(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kSinDiv(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kValidPath(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kFOpen(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kFPuts(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kFGets(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kFClose(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kMapKeyToDir(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kGlobalToLocal(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kLocalToGlobal(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kWait(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kRestartGame(struct _state *s, int funct_nr, int argc, reg_t *argv);
+#ifdef _WIN32
+reg_t kDeviceInfo_Win32(struct _state *s, int funct_nr, int argc, reg_t *argv);
+#else
+reg_t kDeviceInfo_Unix(struct _state *s, int funct_nr, int argc, reg_t *argv);
+#endif
+reg_t kGetEvent(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kCheckFreeSpace(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kFlushResources(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kGetSaveFiles(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kSetDebug(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kCheckSaveGame(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kSaveGame(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kRestoreGame(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kFileIO(struct _state *s, int funct_nr, int argc, reg_t *argp);
+reg_t kGetTime(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kHaveMouse(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kJoystick(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kGameIsRestarting(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kGetCWD(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kSort(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kStrEnd(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kMemory(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kAvoidPath(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kParse(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kSaid(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kStrCpy(struct _state *s, int funct_nr, int argc, reg_t *argp);
+reg_t kStrAt(struct _state *s, int funct_nr, int argc, reg_t *argp);
+reg_t kEditControl(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kDrawControl(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kHiliteControl(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kClone(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kDisposeClone(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kCanBeHere(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kSetNowSeen(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kInitBresen(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kDoBresen(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kBaseSetter(struct _state *s, int funct_nr, int argc, reg_t *argp);
+reg_t kAddToPic(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kAnimate(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kDisplay(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kGraph(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kFormat(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kDoSound(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kAddMenu(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kSetMenu(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kGetMenu(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kDrawStatus(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kDrawMenuBar(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kMenuSelect(struct _state *s, int funct_nr, int argc, reg_t *argv);
+
+reg_t kLoad(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kUnLoad(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kScriptID(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kDisposeScript(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kIsObject(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kRespondsTo(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kNewList(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kDisposeList(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kNewNode(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kFirstNode(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kLastNode(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kEmptyList(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kNextNode(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kPrevNode(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kNodeValue(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kAddAfter(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kAddToFront(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kAddToEnd(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kFindKey(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kDeleteKey(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kMemoryInfo(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kGetSaveDir(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kTextSize(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kIsItSkip(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kMessage(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t kDoAudio(struct _state *s, int funct_nr, int argc, reg_t *argv);
+reg_t k_Unknown(struct _state *s, int funct_nr, int argc, reg_t *argv);
+
+/* The Unknown/Unnamed kernel function */
+reg_t kstub(struct _state *s, int funct_nr, int argc, reg_t *argv);
+/* for unimplemented kernel functions */
+reg_t kNOP(struct _state *s, int funct_nr, int argc, reg_t *argv);
+/* for kernel functions that don't do anything */
+reg_t kFsciEmu(struct _state *s, int funct_nr, int argc, reg_t *argv);
+/* Emulating "old" kernel functions on the heap */
+
+
+#define SCI_MAPPED_UNKNOWN_KFUNCTIONS_NR 0x75
+/* kfunct_mappers below doubles for unknown kfunctions */
+
+static int sci_max_allowed_unknown_kernel_functions[] = {
+ 0,
+ 0x72, /* SCI0 */
+ 0x7b, /* SCI01/EGA */
+ 0x7b, /* SCI01/VGA */
+ 0x7b, /* SCI1/EARLY */
+ 0x7b, /* SCI1/LATE */
+ 0x7b, /* SCI1.1 */
+ 0x0, /* SCI32 */
+};
+
+#define DEFUN(nm, cname, sig) {KF_NEW, nm, {cname, sig, NULL}}
+#define NOFUN(nm) {KF_NONE, nm, {NULL, NULL, NULL}}
+
+sci_kernel_function_t kfunct_mappers[] = {
+/*00*/ DEFUN("Load", kLoad, "iii*"),
+/*01*/ DEFUN("UnLoad", kUnLoad, "i.*"),
+/*02*/ DEFUN("ScriptID", kScriptID, "Ioi*"),
+/*03*/ DEFUN("DisposeScript", kDisposeScript, "Oi"), /* Work around QfG1 bug */
+/*04*/ DEFUN("Clone", kClone, "o"),
+/*05*/ DEFUN("DisposeClone", kDisposeClone, "o"),
+/*06*/ DEFUN("IsObject", kIsObject, "."),
+/*07*/ DEFUN("RespondsTo", kRespondsTo, ".i"),
+/*08*/ DEFUN("DrawPic", kDrawPic, "i*"),
+/*09*/ DEFUN("Show", kShow, "i"),
+/*0a*/ DEFUN("PicNotValid", kPicNotValid, "i*"),
+/*0b*/ DEFUN("Animate", kAnimate, "LI*"), /* More like (li?)? */
+/*0c*/ DEFUN("SetNowSeen", kSetNowSeen, "oi*"), /* The second parameter is ignored */
+/*0d*/ DEFUN("NumLoops", kNumLoops, "o"),
+/*0e*/ DEFUN("NumCels", kNumCels, "o"),
+/*0f*/ DEFUN("CelWide", kCelWide, "iOiOi"),
+/*10*/ DEFUN("CelHigh", kCelHigh, "iOiOi"),
+/*11*/ DEFUN("DrawCel", kDrawCel, "iiiiii*"),
+/*12*/ DEFUN("AddToPic", kAddToPic, "Il*"),
+/*13*/ DEFUN("NewWindow", kNewWindow, "iiiiZRi*"),
+/*14*/ DEFUN("GetPort", kGetPort, ""),
+/*15*/ DEFUN("SetPort", kSetPort, "ii*"),
+/*16*/ DEFUN("DisposeWindow", kDisposeWindow, "ii*"),
+/*17*/ DEFUN("DrawControl", kDrawControl, "o"),
+/*18*/ DEFUN("HiliteControl", kHiliteControl, "o"),
+/*19*/ DEFUN("EditControl", kEditControl, "ZoZo"),
+/*1a*/ DEFUN("TextSize", kTextSize, "rZrii*r*"),
+/*1b*/ DEFUN("Display", kDisplay, ".*"),
+/*1c*/ DEFUN("GetEvent", kGetEvent, "io"),
+/*1d*/ DEFUN("GlobalToLocal", kGlobalToLocal, "o"),
+/*1e*/ DEFUN("LocalToGlobal", kLocalToGlobal, "o"),
+/*1f*/ DEFUN("MapKeyToDir", kMapKeyToDir, "o"),
+/*20*/ DEFUN("DrawMenuBar", kDrawMenuBar, "i"),
+/*21*/ DEFUN("MenuSelect", kMenuSelect, "oi*"),
+/*22*/ DEFUN("AddMenu", kAddMenu, "rr"),
+/*23*/ DEFUN("DrawStatus", kDrawStatus, "Zri*"),
+/*24*/ DEFUN("Parse", kParse, "ro"),
+/*25*/ DEFUN("Said", kSaid, "Zr"),
+/*26*/ DEFUN("SetSynonyms", kSetSynonyms, "o"),
+/*27*/ DEFUN("HaveMouse", kHaveMouse, ""),
+/*28*/ DEFUN("SetCursor", kSetCursor, "i*"),
+/*28*/ DEFUN("MoveCursor", kMoveCursor, "ii*"),
+/*29*/ DEFUN("FOpen", kFOpen, "ri"),
+/*2a*/ DEFUN("FPuts", kFPuts, "ir"),
+/*2b*/ DEFUN("FGets", kFGets, "rii"),
+/*2c*/ DEFUN("FClose", kFClose, "i"),
+/*2d*/ DEFUN("SaveGame", kSaveGame, "rirr*"),
+/*2e*/ DEFUN("RestoreGame", kRestoreGame, "rir*"),
+/*2f*/ DEFUN("RestartGame", kRestartGame, ""),
+/*30*/ DEFUN("GameIsRestarting", kGameIsRestarting, "i*"),
+/*31*/ DEFUN("DoSound", kDoSound, "iIo*"),
+/*32*/ DEFUN("NewList", kNewList, ""),
+/*33*/ DEFUN("DisposeList", kDisposeList, "l"),
+/*34*/ DEFUN("NewNode", kNewNode, ".."),
+/*35*/ DEFUN("FirstNode", kFirstNode, "Zl"),
+/*36*/ DEFUN("LastNode", kLastNode, "l"),
+/*37*/ DEFUN("EmptyList", kEmptyList, "l"),
+/*38*/ DEFUN("NextNode", kNextNode, "n!"),
+/*39*/ DEFUN("PrevNode", kPrevNode, "n"),
+/*3a*/ DEFUN("NodeValue", kNodeValue, "Zn!"),
+/*3b*/ DEFUN("AddAfter", kAddAfter, "lnn"),
+/*3c*/ DEFUN("AddToFront", kAddToFront, "ln"),
+/*3d*/ DEFUN("AddToEnd", kAddToEnd, "ln"),
+/*3e*/ DEFUN("FindKey", kFindKey, "l."),
+/*3f*/ DEFUN("DeleteKey", kDeleteKey, "l."),
+/*40*/ DEFUN("Random", kRandom, "i*"),
+/*41*/ DEFUN("Abs", kAbs, "Oi"),
+/*42*/ DEFUN("Sqrt", kSqrt, "i"),
+/*43*/ DEFUN("GetAngle", kGetAngle, "iiii"),
+/*44*/ DEFUN("GetDistance", kGetDistance, "iiiii*"),
+/*45*/ DEFUN("Wait", kWait, "i"),
+/*46*/ DEFUN("GetTime", kGetTime, "i*"),
+/*47*/ DEFUN("StrEnd", kStrEnd, "r"),
+/*48*/ DEFUN("StrCat", kStrCat, "rr"),
+/*49*/ DEFUN("StrCmp", kStrCmp, "rri*"),
+/*4a*/ DEFUN("StrLen", kStrLen, "r"),
+/*4b*/ DEFUN("StrCpy", kStrCpy, "rri*"),
+/*4c*/ DEFUN("Format", kFormat, "r.*"),
+/*4d*/ DEFUN("GetFarText", kGetFarText, "iir"),
+/*4e*/ DEFUN("ReadNumber", kReadNumber, "r"),
+/*4f*/ DEFUN("BaseSetter", kBaseSetter, "o"),
+/*50*/ DEFUN("DirLoop", kDirLoop, "oi"),
+/*51*/ DEFUN("CanBeHere", kCanBeHere, "ol*"),
+/*51*/ DEFUN("CantBeHere", kCanBeHere, "ol*"),
+/*52*/ DEFUN("OnControl", kOnControl, "i*"),
+/*53*/ DEFUN("InitBresen", kInitBresen, "oi*"),
+/*54*/ DEFUN("DoBresen", kDoBresen, "o"),
+/*55*/ DEFUN("DoAvoider", kDoAvoider, "o"),
+/*56*/ DEFUN("SetJump", kSetJump, "oiii"),
+/*57*/ DEFUN("SetDebug", kSetDebug, "i*"),
+/*58*/ NOFUN("InspectObj"),
+/*59*/ NOFUN("ShowSends"),
+/*5a*/ NOFUN("ShowObjs"),
+/*5b*/ NOFUN("ShowFree"),
+/*5c*/ DEFUN("MemoryInfo", kMemoryInfo, "i"),
+/*5d*/ NOFUN("StackUsage"),
+/*5e*/ NOFUN("Profiler"),
+/*5f*/ DEFUN("GetMenu", kGetMenu, "i."),
+/*60*/ DEFUN("SetMenu", kSetMenu, "i.*"),
+/*61*/ DEFUN("GetSaveFiles", kGetSaveFiles, "rrr"),
+/*62*/ DEFUN("GetCWD", kGetCWD, "r"),
+/*63*/ DEFUN("CheckFreeSpace", kCheckFreeSpace, "r"),
+/*64*/ DEFUN("ValidPath", kValidPath, "r"),
+/*65*/ DEFUN("CoordPri", kCoordPri, "i"),
+/*66*/ DEFUN("StrAt", kStrAt, "rii*"),
+#ifdef _WIN32
+/*67*/ DEFUN("DeviceInfo", kDeviceInfo_Win32, "i.*"),
+#else /* !_WIN32 */
+/*67*/ DEFUN("DeviceInfo", kDeviceInfo_Unix, "i.*"),
+#endif
+/*68*/ DEFUN("GetSaveDir", kGetSaveDir, ""),
+/*69*/ DEFUN("CheckSaveGame", kCheckSaveGame, ".*"),
+/*6a*/ DEFUN("ShakeScreen", kShakeScreen, "ii*"),
+/*6b*/ DEFUN("FlushResources", kFlushResources, "i"),
+/*6c*/ DEFUN("TimesSin", kTimesSin, "ii"),
+/*6d*/ DEFUN("TimesCos", kTimesCos, "ii"),
+#if 0
+/*6e*/ NOFUN(NULL),
+/*6f*/ NOFUN(NULL),
+#else
+/*6e*/ DEFUN("6e", kTimesSin, "ii"),
+/*6f*/ DEFUN("6f", kTimesCos, "ii"),
+#endif
+/*70*/ DEFUN("Graph", kGraph, ".*"),
+/*71*/ DEFUN("Joystick", kJoystick, ".*"),
+/*72*/ NOFUN(NULL),
+/*73*/ NOFUN(NULL),
+
+ /* Experimental functions */
+/*74*/ DEFUN("FileIO", kFileIO, "i.*"),
+/*(?)*/ DEFUN("Memory", kMemory, "i.*"),
+/*(?)*/ DEFUN("Sort", kSort, "ooo"),
+/*(?)*/ DEFUN("AvoidPath", kAvoidPath, "ii.*"),
+/*(?)*/ DEFUN("Lock", kLock, "iii*"),
+/*(?)*/ DEFUN("Palette", kPalette, "i*"),
+/*(?)*/ DEFUN("IsItSkip", kIsItSkip, "iiiii"),
+
+ /* Non-experimental Functions without a fixed ID */
+
+ DEFUN("CosMult", kTimesCos, "ii"),
+ DEFUN("SinMult", kTimesSin, "ii"),
+/*(?)*/ DEFUN("CosDiv", kCosDiv, "ii"),
+/*(?)*/ DEFUN("PriCoord", kPriCoord, "i"),
+/*(?)*/ DEFUN("SinDiv", kSinDiv, "ii"),
+/*(?)*/ DEFUN("TimesCot", kTimesCot, "ii"),
+/*(?)*/ DEFUN("TimesTan", kTimesTan, "ii"),
+DEFUN("Message", kMessage, ".*"),
+DEFUN("DoAudio", kDoAudio, ".*"),
+
+
+ /* Special and NOP stuff */
+ {KF_NEW, NULL, {k_Unknown, NULL, NULL}},
+
+ {KF_TERMINATOR, NULL, {NULL, NULL, NULL}} /* Terminator */
+};
+
+static const char *argtype_description[] = {"Undetermined (WTF?)", "List","Node","Object","Reference","Arithmetic"};
+
+
+/******************** Kernel Oops ********************/
+
+int
+kernel_oops(state_t *s, const char *file, int line, const char *reason)
+{
+ sciprintf("Kernel Oops in file %s, line %d: %s\n", file, line, reason);
+ fprintf(stderr,"Kernel Oops in file %s, line %d: %s\n", file, line, reason);
+ script_debug_flag = script_error_flag = 1;
+ return 0;
+}
+
+
+/* Allocates a set amount of memory for a specified use and returns a handle to it. */
+reg_t
+kalloc(state_t *s, const char *type, int space)
+{
+ reg_t reg;
+
+ sm_alloc_hunk_entry(&s->seg_manager, type, space, &reg);
+ SCIkdebug(SCIkMEM, "Allocated %d at hunk "PREG" (%s)\n", space, PRINT_REG(reg), type);
+
+ return reg;
+}
+
+int
+has_kernel_function(state_t *s, const char *kname)
+{
+ int i = 0;
+
+ while (s->kernel_names[i])
+ {
+ if (!strcmp(s->kernel_names[i], kname))
+ return 1;
+ i++;
+ }
+
+ return 0;
+}
+
+/* Returns a pointer to the memory indicated by the specified handle */
+byte *
+kmem(state_t *s, reg_t handle)
+{
+ mem_obj_t *mobj = GET_SEGMENT(s->seg_manager, handle.segment, MEM_OBJ_HUNK);
+ hunk_table_t *ht = &(mobj->data.hunks);
+
+ if (!mobj || !ENTRY_IS_VALID(ht, handle.offset)) {
+ SCIkwarn(SCIkERROR, "Error: kmem() with invalid handle\n");
+ return NULL;
+ }
+
+ return (byte *) ht->table[handle.offset].entry.mem;
+}
+
+/* Frees the specified handle. Returns 0 on success, 1 otherwise. */
+int
+kfree(state_t *s, reg_t handle)
+{
+ sm_free_hunk_entry(&s->seg_manager, handle);
+
+ return 0;
+}
+
+
+/*****************************************/
+/************* Kernel functions **********/
+/*****************************************/
+
+char *old_save_dir;
+
+reg_t
+kRestartGame(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ char *deref_save_dir = (char*)kernel_dereference_bulk_pointer(s, s->save_dir_copy, 1);
+
+ old_save_dir = strdup(deref_save_dir);
+ s->restarting_flags |= SCI_GAME_IS_RESTARTING_NOW;
+ s->restarting_flags &= ~SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE; /* This appears to help */
+ s->execution_stack_pos = s->execution_stack_base;
+ script_abort_flag = 1; /* Force vm to abort ASAP */
+ return NULL_REG;
+}
+
+
+/* kGameIsRestarting():
+** Returns the restarting_flag in acc
+*/
+reg_t
+kGameIsRestarting(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ char *deref_save_dir = (char*)kernel_dereference_bulk_pointer(s, s->save_dir_copy, 1);
+
+ if (old_save_dir&&deref_save_dir)
+ {
+ strcpy(deref_save_dir, old_save_dir);
+ free(old_save_dir);
+ old_save_dir = NULL;
+ }
+
+ s->r_acc = make_reg(0, (s->restarting_flags & SCI_GAME_WAS_RESTARTED));
+
+ if (argc) {/* Only happens during replay */
+ if (!UKPV(0)) /* Set restarting flag */
+ s->restarting_flags &= ~SCI_GAME_WAS_RESTARTED;
+ }
+
+ return s->r_acc;
+}
+
+reg_t
+kHaveMouse(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+
+ return make_reg (0, (s->have_mouse_flag
+ && gfxop_have_mouse(s->gfx_state))? -1 : 0);
+}
+
+
+
+reg_t
+kMemoryInfo(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ switch (argv[0].offset) {
+ case 0: /* Total free heap memory */
+ case 1: /* Largest heap block available */
+ case 2: /* Largest available hunk memory block */
+ case 3: /* Total amount of hunk memory */
+ case 4: /* Amount of free DOS paragraphs- SCI01 */
+ return make_reg(0, 0x7fff); /* Must not be 0xffff, or some memory calculations will overflow */
+
+ default: SCIkwarn(SCIkWARNING, "Unknown MemoryInfo operation: %04x\n", argv[0].offset);
+ }
+ return NULL_REG;
+}
+
+
+reg_t
+k_Unknown(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ if (funct_nr >= SCI_MAPPED_UNKNOWN_KFUNCTIONS_NR) {
+ SCIkwarn(SCIkSTUB, "Unhandled Unknown function %04x\n", funct_nr);
+ return NULL_REG;
+ } else switch(kfunct_mappers[funct_nr].type) {
+
+ case KF_NEW:
+ return kfunct_mappers[funct_nr].new.fun(s, funct_nr, argc, argv);
+
+ case KF_NONE:
+ default:
+ SCIkwarn(SCIkSTUB, "Unhandled Unknown function %04x\n", funct_nr);
+ return NULL_REG;
+ }
+}
+
+
+reg_t
+kFlushResources(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ run_gc(s);
+ SCIkdebug(SCIkROOM, "Entering room number %d\n", UKPV(0));
+ return s->r_acc;
+}
+
+reg_t
+kSetDebug(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ sciprintf("Debug mode activated\n");
+
+ script_debug_flag = 1; /* Enter debug mode */
+ _debug_seeking = _debug_step_running = 0;
+ return s->r_acc;
+}
+
+#define _K_NEW_GETTIME_TICKS 0
+#define _K_NEW_GETTIME_TIME_12HOUR 1
+#define _K_NEW_GETTIME_TIME_24HOUR 2
+#define _K_NEW_GETTIME_DATE 3
+
+reg_t
+kGetTime(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ struct tm* loc_time;
+ GTimeVal time_prec;
+ time_t the_time;
+ int retval = 0; /* Avoid spurious warning */
+
+#if 0
+ /* Reset optimization flags: If this function is called,
+ ** the game may be waiting for a timeout */
+ s->kernel_opt_flags &= ~(KERNEL_OPT_FLAG_GOT_EVENT
+ | KERNEL_OPT_FLAG_GOT_2NDEVENT);
+#endif
+
+#ifdef _WIN32
+ if (TIMERR_NOERROR != timeBeginPeriod(1))
+ {
+ fprintf(stderr, "timeBeginPeriod(1) failed in kGetTime!\n");
+ }
+#endif /* _WIN32 */
+
+ the_time = time(NULL);
+ loc_time = localtime(&the_time);
+
+#ifdef _WIN32
+ if (TIMERR_NOERROR != timeEndPeriod(1))
+ {
+ fprintf(stderr, "timeEndPeriod(1) failed in kGetTime!\n");
+ }
+#endif /* _WIN32 */
+
+ if (s->version<SCI_VERSION_FTU_NEW_GETTIME) { /* Use old semantics */
+ if (argc) { /* Get seconds since last am/pm switch */
+ retval = loc_time->tm_sec + loc_time->tm_min * 60 + (loc_time->tm_hour % 12) * 3600;
+ SCIkdebug(SCIkTIME, "GetTime(timeofday) returns %d\n", retval);
+ } else { /* Get time since game started */
+ sci_get_current_time (&time_prec);
+ retval = ((time_prec.tv_usec - s->game_start_time.tv_usec) * 60 / 1000000) +
+ (time_prec.tv_sec - s->game_start_time.tv_sec) * 60;
+ SCIkdebug(SCIkTIME, "GetTime(elapsed) returns %d\n", retval);
+ }
+ } else {
+ int mode = UKPV_OR_ALT(0, 0); /* The same strange method is still used for distinguishing
+ mode 0 and the others. We assume that this is safe, though */
+ switch (mode) {
+ case _K_NEW_GETTIME_TICKS : {
+ sci_get_current_time (&time_prec);
+ retval = ((time_prec.tv_usec - s->game_start_time.tv_usec) * 60 / 1000000) +
+ (time_prec.tv_sec - s->game_start_time.tv_sec) * 60;
+ SCIkdebug(SCIkTIME, "GetTime(elapsed) returns %d\n", retval);
+ break;
+ }
+ case _K_NEW_GETTIME_TIME_12HOUR : {
+ loc_time->tm_hour %= 12;
+ retval=(loc_time->tm_min<<6)|(loc_time->tm_hour<<12)|(loc_time->tm_sec);
+ SCIkdebug(SCIkTIME, "GetTime(12h) returns %d\n", retval);
+ break;
+ }
+ case _K_NEW_GETTIME_TIME_24HOUR : {
+ retval=(loc_time->tm_min<<5)|(loc_time->tm_sec>>1)|(loc_time->tm_hour<<11);
+ SCIkdebug(SCIkTIME, "GetTime(24h) returns %d\n", retval);
+ break;
+ }
+ case _K_NEW_GETTIME_DATE : {
+ retval=(loc_time->tm_mon<<5)|loc_time->tm_mday|(loc_time->tm_year<<9);
+ SCIkdebug(SCIkTIME, "GetTime(date) returns %d\n", retval);
+ break;
+ }
+ default: {
+ SCIkdebug(SCIkWARNING, "Attempt to use unknown GetTime mode %d\n", mode);
+ break;
+ }
+ }
+ }
+
+ return make_reg(0, retval);
+}
+
+#define K_MEMORY_ALLOCATE_CRITICAL 1
+#define K_MEMORY_ALLOCATE_NONCRITICAL 2
+#define K_MEMORY_FREE 3
+#define K_MEMORY_MEMCPY 4
+#define K_MEMORY_PEEK 5
+#define K_MEMORY_POKE 6
+
+reg_t
+kMemory(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+
+ switch (UKPV(0)) {
+
+ case K_MEMORY_ALLOCATE_CRITICAL :
+
+ if (!sm_alloc_dynmem(&s->seg_manager, UKPV(1),
+ "kMemory() critical", &s->r_acc)) {
+ SCIkwarn(SCIkERROR, "Critical heap allocation failed\n");
+ script_error_flag = script_debug_flag = 1;
+ }
+
+ return s->r_acc;
+ break;
+
+ case K_MEMORY_ALLOCATE_NONCRITICAL :
+
+ sm_alloc_dynmem(&s->seg_manager, UKPV(1),
+ "kMemory() non-critical", &s->r_acc);
+ break;
+
+ case K_MEMORY_FREE :
+
+ if (sm_free_dynmem(&s->seg_manager, argv[1])) {
+ SCIkwarn(SCIkERROR, "Attempt to kMemory::free() non-dynmem pointer "PREG"!\n",
+ PRINT_REG(argv[1]));
+ }
+ break;
+
+ case K_MEMORY_MEMCPY : {
+ int size = UKPV(3);
+ byte *dest = kernel_dereference_bulk_pointer(s, argv[1], size);
+ byte *src = kernel_dereference_bulk_pointer(s, argv[2], size);
+
+ if (dest && src)
+ memcpy(dest, src, size);
+ else {
+ SCIkdebug(SCIkWARNING, "Warning: Could not execute kMemory:memcpy of %d bytes:\n", size);
+ if (!dest) {
+ SCIkdebug(SCIkWARNING, " dest ptr ("PREG") invalid/memory region too small\n", PRINT_REG(argv[1]));
+ }
+ if (!src) {
+ SCIkdebug(SCIkWARNING, " src ptr ("PREG") invalid/memory region too small\n", PRINT_REG(argv[2]));
+ }
+ }
+
+ break;
+ }
+
+ case K_MEMORY_PEEK : {
+ byte *ref = kernel_dereference_bulk_pointer(s, argv[1], 2);
+
+ if (!ref) {
+ SCIkdebug(SCIkERROR, "Attempt to poke invalid memory at "PREG"!\n",
+ PRINT_REG(argv[1]));
+ return s->r_acc;
+ }
+ if (s->seg_manager.heap[argv[1].segment]->type == MEM_OBJ_LOCALS)
+ return *((reg_t *) ref); else
+ return make_reg(0, getInt16(ref));
+ }
+ break;
+
+ case K_MEMORY_POKE : {
+ byte *ref = kernel_dereference_bulk_pointer(s, argv[1], 2);
+
+ if (!ref) {
+ SCIkdebug(SCIkERROR, "Attempt to poke invalid memory at "PREG"!\n",
+ PRINT_REG(argv[1]));
+ return s->r_acc;
+ }
+
+ if (s->seg_manager.heap[argv[1].segment]->type == MEM_OBJ_LOCALS)
+ *((reg_t *) ref) = argv[2]; else
+ {
+ if (argv[2].segment) {
+ SCIkdebug(SCIkERROR, "Attempt to poke memory reference "PREG" to "PREG"!\n",
+ PRINT_REG(argv[2]), PRINT_REG(argv[1]));
+ return s->r_acc;
+ putInt16(ref, argv[2].offset);
+ }
+
+ }
+ return s->r_acc;
+ break;
+
+ }
+ }
+
+ return s->r_acc;
+}
+
+reg_t
+kstub(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int i;
+
+ SCIkwarn(SCIkWARNING, "Unimplemented syscall: %s[%x](",
+ s->kernel_names[funct_nr], funct_nr);
+
+ for (i = 0; i < argc; i++) {
+ sciprintf(PREG, PRINT_REG(argv[i]));
+ if (i+1 < argc) sciprintf(", ");
+ }
+ sciprintf(")\n");
+ return NULL_REG;
+}
+
+
+reg_t
+kNOP(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ char *problem = (char*)(s->kfunct_table[funct_nr].orig_name ?
+ "unmapped" : "NOP");
+
+ SCIkwarn(SCIkWARNING, "Warning: Kernel function 0x%02x invoked: %s", funct_nr, problem);
+
+ if (s->kfunct_table[funct_nr].orig_name &&
+ strcmp(s->kfunct_table[funct_nr].orig_name, SCRIPT_UNKNOWN_FUNCTION_STRING))
+ {
+ sciprintf(" (but its name is known to be %s)", s->kfunct_table[funct_nr].orig_name);
+ }
+
+ sciprintf("\n");
+ return NULL_REG;
+}
+
+
+
+void
+kernel_compile_signature(const char **s)
+{
+ const char *src = *s;
+ char *result;
+ int ellipsis = 0;
+ char v;
+ int index = 0;
+
+ if (!src)
+ return; /* NULL signature: Nothing to do */
+
+ result = (char*)sci_malloc(strlen(*s) + 1);
+
+ while (*src) {
+ char c;
+ v = 0;
+
+ if (ellipsis) {
+ sciprintf("INTERNAL ERROR when compiling kernel"
+ " function signature '%s': non-terminal ellipsis\n", *s,
+ *src);
+ exit(1);
+ }
+
+ do {
+ char cc;
+ cc = c = *src++;
+ if (c >= 'A' || c <= 'Z')
+ cc = c | KSIG_SPEC_SUM_DONE;
+
+ switch (cc) {
+
+ case KSIG_SPEC_LIST:
+ v |= KSIG_LIST;
+ break;
+
+ case KSIG_SPEC_NODE:
+ v |= KSIG_NODE;
+ break;
+
+ case KSIG_SPEC_REF:
+ v |= KSIG_REF;
+ break;
+
+ case KSIG_SPEC_OBJECT:
+ v |= KSIG_OBJECT;
+ break;
+
+ case KSIG_SPEC_ARITHMETIC:
+ v |= KSIG_ARITHMETIC;
+ break;
+
+ case KSIG_SPEC_NULL:
+ v |= KSIG_NULL;
+ break;
+
+ case KSIG_SPEC_ANY:
+ v |= KSIG_ANY;
+ break;
+
+ case KSIG_SPEC_ALLOW_INV:
+ v |= KSIG_ALLOW_INV;
+ break;
+
+ case KSIG_SPEC_ELLIPSIS:
+ v |= KSIG_ELLIPSIS;
+ ellipsis = 1;
+ break;
+
+ default: {
+ sciprintf("INTERNAL ERROR when compiling kernel"
+ " function signature '%s': (%02x) not understood (aka"
+ " '%c')\n",
+ *s, c, c);
+ exit(1);
+ }
+
+ }
+ } while (*src && (*src == KSIG_SPEC_ALLOW_INV ||
+ *src == KSIG_SPEC_ELLIPSIS ||
+ (c < 'a' && c != KSIG_SPEC_ANY)));
+
+ /* To handle sum types */
+ result[index++] = v;
+ }
+
+ result[index] = 0;
+ *s = result; /* Write back */
+}
+
+int
+script_map_kernel(state_t *s)
+{
+ int functnr;
+ int mapped = 0;
+ int ignored = 0;
+ int functions_nr = s->kernel_names_nr;
+ int max_functions_nr
+ = sci_max_allowed_unknown_kernel_functions[s->resmgr
+ ->sci_version];
+
+ if (functions_nr < max_functions_nr) {
+ sciprintf("Warning: SCI version believed to have %d kernel"
+ " functions, but only %d reported-- filling up"
+ " remaining %d\n",
+ max_functions_nr, functions_nr,
+ max_functions_nr - functions_nr);
+
+ functions_nr = max_functions_nr;
+ }
+
+ s->kfunct_table = (kfunct_sig_pair_t*)sci_malloc(sizeof(kfunct_sig_pair_t) * functions_nr);
+
+
+ s->kfunct_nr = functions_nr;
+
+ for (functnr = 0; functnr < functions_nr; functnr++) {
+ int seeker, found = -1;
+ char *sought_name = NULL;
+
+ if (functnr < s->kernel_names_nr)
+ sought_name = s->kernel_names[functnr];
+
+ if (sought_name)
+ for (seeker = 0; (found == -1)
+ && kfunct_mappers[seeker].type != KF_TERMINATOR; seeker++)
+ if (kfunct_mappers[seeker].name
+ && strcmp(kfunct_mappers[seeker].name, sought_name) == 0)
+ found = seeker; /* Found a kernel function with the same name! */
+
+ if (found == -1) {
+ if (sought_name)
+ {
+ sciprintf("Warning: Kernel function %s[%x] unmapped\n",
+ s->kernel_names[functnr], functnr);
+ s->kfunct_table[functnr].fun = kNOP;
+ } else
+ {
+ sciprintf("Warning: Flagging kernel function %x as unknown\n",
+ functnr);
+ s->kfunct_table[functnr].fun = k_Unknown;
+ }
+
+ s->kfunct_table[functnr].signature = NULL;
+ s->kfunct_table[functnr].orig_name = sought_name;
+ } else switch (kfunct_mappers[found].type) {
+
+ case KF_OLD:
+ sciprintf("Emulated kernel function found.\nThis shouldn't happen anymore.");
+ return 1;
+
+ case KF_NONE:
+ s->kfunct_table[functnr].signature = NULL;
+ ++ignored;
+ break;
+
+ case KF_NEW:
+ s->kfunct_table[functnr] = kfunct_mappers[found].new;
+ kernel_compile_signature(&(s->kfunct_table[functnr].signature));
+ ++mapped;
+ break;
+ }
+
+ } /* for all functions requesting to be mapped */
+
+ sciprintf("Handled %d/%d kernel functions, mapping %d",
+ mapped+ignored, s->kernel_names_nr, mapped);
+ if (ignored)
+ sciprintf(" and ignoring %d", ignored);
+ sciprintf(".\n");
+ return 0;
+}
+
+void
+free_kfunct_tables(state_t *s)
+{
+ sci_free(s->kfunct_table);
+ s->kfunct_table = NULL;
+
+}
+int
+determine_reg_type(state_t *s, reg_t reg, int allow_invalid)
+{
+ mem_obj_t *mobj;
+
+ if (!reg.segment) {
+ if (!reg.offset)
+ return KSIG_ARITHMETIC | KSIG_NULL;
+ return KSIG_ARITHMETIC;
+ }
+
+ if ((reg.segment >= s->seg_manager.heap_size)
+ || !s->seg_manager.heap[reg.segment])
+ return 0; /* Invalid */
+
+ mobj = s->seg_manager.heap[reg.segment];
+
+ switch (mobj->type) {
+
+ case MEM_OBJ_SCRIPT:
+ if (reg.offset <= mobj->data.script.buf_size
+ && reg.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET
+ && RAW_IS_OBJECT(mobj->data.script.buf + reg.offset)) {
+ int idx = RAW_GET_CLASS_INDEX(&(mobj->data.script), reg);
+ if (idx >= 0 && idx < mobj->data.script.objects_nr)
+ return KSIG_OBJECT;
+ else
+ return KSIG_REF;
+ } else
+ return KSIG_REF;
+
+ case MEM_OBJ_CLONES:
+ if (allow_invalid || ENTRY_IS_VALID(&(mobj->data.clones), reg.offset))
+ return KSIG_OBJECT;
+ else
+ return KSIG_OBJECT | KSIG_INVALID;
+
+ case MEM_OBJ_LOCALS:
+ if (allow_invalid || reg.offset < mobj->data.locals.nr * sizeof(reg_t))
+ return KSIG_REF;
+ else
+ return KSIG_REF | KSIG_INVALID;
+
+ case MEM_OBJ_STACK:
+ if (allow_invalid || reg.offset < mobj->data.stack.nr * sizeof(reg_t))
+ return KSIG_REF;
+ else
+ return KSIG_REF | KSIG_INVALID;
+
+ case MEM_OBJ_SYS_STRINGS:
+ if (allow_invalid || (reg.offset < SYS_STRINGS_MAX
+ && mobj->data.sys_strings.strings[reg.offset].name))
+ return KSIG_REF;
+ else
+ return KSIG_REF | KSIG_INVALID;
+
+ case MEM_OBJ_LISTS:
+ if (allow_invalid || ENTRY_IS_VALID(&(mobj->data.lists), reg.offset))
+ return KSIG_LIST;
+ else
+ return KSIG_LIST | KSIG_INVALID;
+
+ case MEM_OBJ_NODES:
+ if (allow_invalid || ENTRY_IS_VALID(&(mobj->data.nodes), reg.offset))
+ return KSIG_NODE;
+ else
+ return KSIG_NODE | KSIG_INVALID;
+
+ case MEM_OBJ_DYNMEM:
+ if (allow_invalid || reg.offset < mobj->data.dynmem.size)
+ return KSIG_REF;
+ else
+ return KSIG_REF | KSIG_INVALID;
+
+ default:
+ return 0;
+
+ }
+}
+
+const char *
+kernel_argtype_description(int type)
+{
+ type &= ~KSIG_INVALID;
+
+ return argtype_description[sci_ffs(type)];
+}
+
+int
+kernel_matches_signature(state_t *s, const char *sig, int argc, reg_t *argv)
+{
+ if (!sig)
+ return 1;
+
+ while (*sig && argc) {
+ if ((*sig & KSIG_ANY) != KSIG_ANY) {
+ int type = determine_reg_type(s, *argv, *sig & KSIG_ALLOW_INV);
+
+ if (!type) {
+ sciprintf("[KERN] Could not determine type of ref "PREG";"
+ " failing signature check\n",
+ PRINT_REG(*argv));
+ return 0;
+ }
+
+ if (type & KSIG_INVALID) {
+ sciprintf("[KERN] ref "PREG" was determined to be a %s, "
+ "but the reference itself is invalid\n",
+ PRINT_REG(*argv), kernel_argtype_description(type));
+ return 0;
+ }
+
+ if (!(type & *sig))
+ return 0;
+
+ }
+ if (!(*sig & KSIG_ELLIPSIS))
+ ++sig;
+ ++argv;
+ --argc;
+ }
+
+ if (argc)
+ return 0; /* Too many arguments */
+ else
+ return (*sig == 0 || (*sig & KSIG_ELLIPSIS));
+}
+
+static inline void *
+_kernel_dereference_pointer(struct _state *s, reg_t pointer, int entries, int align)
+{
+ int maxsize;
+ void *retval = sm_dereference(&s->seg_manager, pointer, &maxsize);
+
+ if (pointer.offset & (align-1)) {
+ SCIkdebug(SCIkERROR, "Unaligned pointer read: "PREG" expected with %d alignment!\n",
+ PRINT_REG(pointer), align);
+ return NULL;
+ }
+
+ if (entries > maxsize) {
+ SCIkdebug(SCIkERROR, "Trying to dereference pointer "PREG" beyond end of segment!\n",
+ PRINT_REG(pointer));
+ return NULL;
+ }
+ return retval;
+
+}
+
+byte *
+kernel_dereference_bulk_pointer(struct _state *s, reg_t pointer, int entries)
+{
+ return (byte*)_kernel_dereference_pointer(s, pointer, entries, 1);
+}
+
+
+reg_t *
+kernel_dereference_reg_pointer(struct _state *s, reg_t pointer, int entries)
+{
+ return (reg_t*)_kernel_dereference_pointer(s, pointer, entries, sizeof(reg_t));
+}
diff --git a/engines/sci/engine/kernel_compat.h b/engines/sci/engine/kernel_compat.h
new file mode 100644
index 0000000000..0a3468282a
--- /dev/null
+++ b/engines/sci/engine/kernel_compat.h
@@ -0,0 +1,77 @@
+/***************************************************************************
+ kernel_compat.h Copyright (C) 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 (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+/* Kernel compatibility #defines to pre-GLUTTON times */
+
+#ifndef _SCI_KERNEL_COMPAT_
+#define _SCI_KERNEL_COMPAT_
+
+#ifdef __GNUC__
+#warning "Old kernel compatibility crap"
+#endif
+
+/* Minimal heap position */
+#define HEAP_MIN 800
+
+#define GET_HEAP(address) ((((guint16)(address)) < HEAP_MIN)? \
+KERNEL_OOPS("Heap address space violation on read") \
+: getHeapInt16(s->heap, ((guint16)(address))))
+/* Reads a heap value if allowed */
+
+#define UGET_HEAP(address) ((((guint16)(address)) < HEAP_MIN)? \
+KERNEL_OOPS("Heap address space violation on read") \
+: getHeapUInt16(s->heap, ((guint16)(address))))
+/* Reads a heap value if allowed */
+
+#define PUT_HEAP(address, value) { if (((guint16)(address)) < HEAP_MIN) \
+KERNEL_OOPS("Heap address space violation on write"); \
+else { s->heap[((guint16)(address))] = (value) &0xff; \
+ s->heap[((guint16)(address)) + 1] = ((value) >> 8) & 0xff;} \
+if ((address) & 1) \
+ sciprintf("Warning: Unaligned write to %04x\n", (address) & 0xffff); }
+/* Sets a heap value if allowed */
+
+static inline int
+getHeapInt16(unsigned char *base, int address)
+{
+ if (address & 1)
+ sciprintf("Warning: Unaligned read from %04x\n", (address) & 0xffff);
+
+ return getInt16(base + address);
+}
+
+static inline unsigned int
+getHeapUInt16(unsigned char *base, int address)
+{
+ if (address & 1)
+ sciprintf("Warning: Unaligned unsigned read from %04x\n", (address) & 0xffff);
+
+ return getUInt16(base + address);
+}
+
+
+
+#endif /* !_SCI_KERNEL_COMPAT_ */
diff --git a/engines/sci/engine/kernel_types.h b/engines/sci/engine/kernel_types.h
new file mode 100644
index 0000000000..0ac56bcf6a
--- /dev/null
+++ b/engines/sci/engine/kernel_types.h
@@ -0,0 +1,96 @@
+/***************************************************************************
+ kernel_types.h Copyright (C) 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 (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+
+#ifndef _FREESCI_KERNEL_TYPES_H_
+#define _FREESCI_KERNEL_TYPES_H_
+
+#include "vm_types.h"
+
+#define KSIG_TERMINATOR 0
+
+/* Uncompiled signatures */
+#define KSIG_SPEC_ARITMETIC 'i'
+#define KSIG_SPEC_LIST 'l'
+#define KSIG_SPEC_NODE 'n'
+#define KSIG_SPEC_OBJECT 'o'
+#define KSIG_SPEC_REF 'r' /* Said Specs and strings */
+#define KSIG_SPEC_ARITHMETIC 'i'
+#define KSIG_SPEC_NULL 'z'
+#define KSIG_SPEC_ANY '.'
+#define KSIG_SPEC_ALLOW_INV '!' /* Allow invalid pointers */
+#define KSIG_SPEC_ELLIPSIS '*' /* Arbitrarily more TYPED arguments */
+
+#define KSIG_SPEC_SUM_DONE ('a' - 'A') /* Use small letters to indicate end of sum type */
+/* Use capital letters for sum types, e.g.
+** "LNoLr" for a function which takes two arguments:
+** (1) list, node or object
+** (2) list or ref
+*/
+
+/* Compiled signatures */
+#define KSIG_LIST 0x01
+#define KSIG_NODE 0x02
+#define KSIG_OBJECT 0x04
+#define KSIG_REF 0x08
+#define KSIG_ARITHMETIC 0x10
+
+#define KSIG_NULL 0x40
+#define KSIG_ANY 0x5f
+#define KSIG_ELLIPSIS 0x80
+#define KSIG_ALLOW_INV 0x20
+#define KSIG_INVALID KSIG_ALLOW_INV
+
+
+int
+kernel_matches_signature(state_t *s, const char *sig, int argc, reg_t *argv);
+/* Determines whether a list of registers matches a given signature
+** Parameters: (state_t *) s: The state to operate on
+** (char *) sig: The signature to test against
+** (int) argc: Number of arguments to test
+** (reg_t *) argv: Argument list
+** Returns : (int) 0 iff the signature was not matched
+*/
+
+int
+determine_reg_type(state_t *s, reg_t reg, int allow_invalid);
+/* Determines the type of the object indicated by reg
+** Parameters: (state_t *) s: The state to operate on
+** (reg_t) reg: The register to check
+** (int) allow_invalid: Allow invalid pointer values
+** Returns : one of KSIG_* below KSIG_NULL.
+** KSIG_INVALID set if the type of reg can be determined, but is invalid.
+** 0 on error.
+*/
+
+const char *
+kernel_argtype_description(int type);
+/* Returns a textual description of the type of an object
+** Parameters: (int) type: The type value to describe
+** Returns: (const char *) Pointer to a (static) descriptive string
+*/
+
+#endif /* ! _FREESCI_KERNEL_TYPES_H_ */
diff --git a/engines/sci/engine/kevent.c b/engines/sci/engine/kevent.c
new file mode 100644
index 0000000000..9054aded41
--- /dev/null
+++ b/engines/sci/engine/kevent.c
@@ -0,0 +1,229 @@
+/***************************************************************************
+ kevent.c Copyright (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]
+
+***************************************************************************/
+
+#include <engine.h>
+
+int stop_on_event;
+
+#define SCI_VARIABLE_GAME_SPEED 3
+
+reg_t
+kGetEvent(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int mask = UKPV(0);
+ reg_t obj = argv[1];
+ sci_event_t e;
+ int oldx, oldy;
+ int modifier_mask = SCI_VERSION_MAJOR(s->version)==0 ? SCI_EVM_ALL
+ : SCI_EVM_NO_FOOLOCK;
+
+ if (s->kernel_opt_flags & KERNEL_OPT_FLAG_GOT_2NDEVENT) {
+ /* Penalty time- too many requests to this function without
+ ** waiting! */
+ int delay = s->script_000->locals_block->locals[SCI_VARIABLE_GAME_SPEED].offset;
+
+ gfxop_usleep(s->gfx_state, (1000000 * delay) / 60);
+ }
+
+ /*If there's a simkey pending, and the game wants a keyboard event, use the
+ *simkey instead of a normal event*/
+ if (_kdebug_cheap_event_hack && (mask & SCI_EVT_KEYBOARD)) {
+ PUT_SEL32V(obj, type, SCI_EVT_KEYBOARD); /*Keyboard event*/
+ PUT_SEL32V(obj, message, _kdebug_cheap_event_hack);
+ PUT_SEL32V(obj, modifiers, SCI_EVM_NUMLOCK); /*Numlock on*/
+ PUT_SEL32V(obj, x, s->gfx_state->pointer_pos.x);
+ PUT_SEL32V(obj, y, s->gfx_state->pointer_pos.y);
+ _kdebug_cheap_event_hack = 0;
+ return make_reg(0, 1);
+ }
+
+ oldx = s->gfx_state->pointer_pos.x;
+ oldy = s->gfx_state->pointer_pos.y;
+ e = gfxop_get_event(s->gfx_state, mask);
+
+ s->parser_event = NULL_REG; /* Invalidate parser event */
+
+ PUT_SEL32V(obj, x, s->gfx_state->pointer_pos.x);
+ PUT_SEL32V(obj, y, s->gfx_state->pointer_pos.y);
+
+ /* gfxop_set_pointer_position(s->gfx_state, gfx_point(s->gfx_state->pointer_pos.x, s->gfx_state->pointer_pos.y)); */
+
+
+ if (e.type)
+ s->kernel_opt_flags &= ~(KERNEL_OPT_FLAG_GOT_EVENT
+ | KERNEL_OPT_FLAG_GOT_2NDEVENT);
+ else {
+ if (s->kernel_opt_flags & KERNEL_OPT_FLAG_GOT_EVENT)
+ s->kernel_opt_flags |= KERNEL_OPT_FLAG_GOT_2NDEVENT;
+ else
+ s->kernel_opt_flags |= KERNEL_OPT_FLAG_GOT_EVENT;
+ }
+
+ switch(e.type)
+ {
+ case SCI_EVT_QUIT:
+ quit_vm();
+ break;
+
+ case SCI_EVT_KEYBOARD: {
+
+ if ((e.buckybits & SCI_EVM_LSHIFT) && (e.buckybits & SCI_EVM_RSHIFT)
+ && (e.data == '-')) {
+
+ sciprintf("Debug mode activated\n");
+
+ script_debug_flag = 1; /* Enter debug mode */
+ _debug_seeking = _debug_step_running = 0;
+ s->onscreen_console = 0;
+
+ } else if ((e.buckybits & SCI_EVM_CTRL) && (e.data == '`')) {
+
+ script_debug_flag = 1; /* Enter debug mode */
+ _debug_seeking = _debug_step_running = 0;
+ s->onscreen_console = 1;
+
+ } else if ((e.buckybits & SCI_EVM_CTRL) && (e.data == '1')) {
+
+ if (s->visual)
+ s->visual->print(GFXW(s->visual), 0);
+
+ } else {
+ PUT_SEL32V(obj, type, SCI_EVT_KEYBOARD); /*Keyboard event*/
+ s->r_acc=make_reg(0, 1);
+ PUT_SEL32V(obj, message, e.character);
+ /* We only care about the translated
+ ** character */
+ PUT_SEL32V(obj, modifiers, e.buckybits&modifier_mask);
+
+ }
+ } break;
+
+ case SCI_EVT_MOUSE_RELEASE:
+ case SCI_EVT_MOUSE_PRESS: {
+ int extra_bits=0;
+
+ if(mask & e.type) {
+ switch(e.data) {
+ case 2: extra_bits=SCI_EVM_LSHIFT|SCI_EVM_RSHIFT; break;
+ case 3: extra_bits=SCI_EVM_CTRL;
+ default:break;
+ }
+
+ PUT_SEL32V(obj, type, e.type);
+ PUT_SEL32V(obj, message, 1);
+ PUT_SEL32V(obj, modifiers, (e.buckybits|extra_bits)&modifier_mask);
+ s->r_acc = make_reg(0, 1);
+ }
+ } break;
+
+ default: {
+ s->r_acc = NULL_REG; /* Unknown or no event */
+ }
+ }
+
+ if ((s->r_acc.offset) && (stop_on_event)) {
+ stop_on_event = 0;
+ script_debug_flag = 1;
+ }
+
+ return s->r_acc;
+}
+
+reg_t
+kMapKeyToDir(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ reg_t obj = argv[0];
+
+ if (GET_SEL32V(obj, type) == SCI_EVT_KEYBOARD) { /* Keyboard */
+ int mover = -1;
+ switch (GET_SEL32V(obj, message)) {
+ case SCI_K_HOME: mover = 8; break;
+ case SCI_K_UP: mover = 1; break;
+ case SCI_K_PGUP: mover = 2; break;
+ case SCI_K_LEFT: mover = 7; break;
+ case SCI_K_CENTER:
+ case 76: mover = 0; break;
+ case SCI_K_RIGHT: mover = 3; break;
+ case SCI_K_END: mover = 6; break;
+ case SCI_K_DOWN: mover = 5; break;
+ case SCI_K_PGDOWN: mover = 4; break;
+ default: break;
+ }
+
+ if (mover >= 0) {
+ PUT_SEL32V(obj, type, SCI_EVT_JOYSTICK);
+ PUT_SEL32V(obj, message, mover);
+ return make_reg(0, 1);
+ } else return NULL_REG;
+ }
+
+ return s->r_acc;
+}
+
+
+reg_t
+kGlobalToLocal(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ reg_t obj = argc ? argv[0] : NULL_REG; /* Can this really happen? Lars */
+
+ if (obj.segment) {
+ int x = GET_SEL32V(obj, x);
+ int y = GET_SEL32V(obj, y);
+
+ PUT_SEL32V(obj, x, x - s->port->zone.x);
+ PUT_SEL32V(obj, y, y - s->port->zone.y);
+ }
+
+ return s->r_acc;
+
+}
+
+
+reg_t
+kLocalToGlobal(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ reg_t obj = argc ? argv[0] : NULL_REG; /* Can this really happen? Lars */
+
+ if (obj.segment) {
+ int x = GET_SEL32V(obj, x);
+ int y = GET_SEL32V(obj, y);
+
+ PUT_SEL32V(obj, x, x + s->port->zone.x);
+ PUT_SEL32V(obj, y, y + s->port->zone.y);
+ }
+
+ return s->r_acc;
+}
+
+reg_t /* Not implemented */
+kJoystick(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ SCIkdebug(SCIkSTUB, "Unimplemented syscall 'Joystick()'\n", funct_nr);
+ return NULL_REG;
+}
+
+
diff --git a/engines/sci/engine/kfile.c b/engines/sci/engine/kfile.c
new file mode 100644
index 0000000000..c4f6c63ac2
--- /dev/null
+++ b/engines/sci/engine/kfile.c
@@ -0,0 +1,1174 @@
+/***************************************************************************
+ Kfile.c Copyright (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]
+
+***************************************************************************/
+
+#include <engine.h>
+
+#ifdef _WIN32
+# ifndef PATH_MAX
+# define PATH_MAX 255
+# endif
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+# include <sys/types.h>
+# include <sys/stat.h>
+# define stat _stat
+#elif defined (_DREAMCAST)
+# include <dc.h>
+#endif
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+static int _savegame_indices_nr = -1; /* means 'uninitialized' */
+
+static struct _savegame_index_struct {
+ int id;
+ long timestamp;
+} _savegame_indices[MAX_SAVEGAME_NR];
+
+/* This assumes modern stream implementations. It may break on DOS. */
+
+
+/* Attempts to mirror a file by copying it from the resource firectory
+** to the working directory. Returns NULL if the file didn't exist.
+** Otherwise, the new file is then opened for reading or writing.
+*/
+static FILE *
+f_open_mirrored(state_t *s, char *fname)
+{
+ int fd;
+ char *buf = NULL;
+ int fsize;
+
+ chdir(s->resource_dir);
+ fd = sci_open(fname, O_RDONLY | O_BINARY);
+ if (!IS_VALID_FD(fd)) {
+ chdir(s->work_dir);
+ return NULL;
+ }
+
+ fsize = sci_fd_size(fd);
+ if (fsize > 0) {
+ buf = (char*)sci_malloc(fsize);
+ read(fd, buf, fsize);
+ }
+
+ close(fd);
+
+ chdir(s->work_dir);
+
+ /* Visual C++ doesn't allow to specify O_BINARY with creat() */
+#ifdef _MSC_VER
+ fd = _open(fname, O_CREAT | O_BINARY | O_RDWR, S_IREAD | S_IWRITE);
+#else
+ fd = creat(fname, 0600);
+#endif
+
+ if (!IS_VALID_FD(fd) && buf) {
+ free(buf);
+ sciprintf("kfile.c: f_open_mirrored(): Warning: Could not create '%s' in '%s' (%d bytes to copy)\n",
+ fname, s->work_dir, fsize);
+ return NULL;
+ }
+
+ if (fsize) {
+ int ret;
+ ret = write(fd, buf, fsize);
+ if (ret < fsize) {
+ sciprintf("kfile.c: f_open_mirrored(): Warning: Could not write all %ld bytes to '%s' in '%s' (only wrote %ld)\n",
+ (long)fsize, fname, s->work_dir, ret);
+ }
+
+ free(buf);
+ }
+
+ close(fd);
+
+ return sci_fopen(fname, "r" FO_BINARY "+");
+}
+
+
+#define _K_FILE_MODE_OPEN_OR_CREATE 0
+#define _K_FILE_MODE_OPEN_OR_FAIL 1
+#define _K_FILE_MODE_CREATE 2
+
+
+void
+file_open(state_t *s, char *filename, int mode)
+{
+ int retval = 1; /* Ignore file_handles[0] */
+ FILE *file = NULL;
+
+ SCIkdebug(SCIkFILE, "Opening file %s with mode %d\n", filename, mode);
+ if ((mode == _K_FILE_MODE_OPEN_OR_FAIL) || (mode == _K_FILE_MODE_OPEN_OR_CREATE)) {
+ file = sci_fopen(filename, "r" FO_BINARY "+"); /* Attempt to open existing file */
+ SCIkdebug(SCIkFILE, "Opening file %s with mode %d\n", filename, mode);
+ if (!file) {
+ SCIkdebug(SCIkFILE, "Failed. Attempting to copy from resource dir...\n");
+ file = f_open_mirrored(s, filename);
+ if (file)
+ SCIkdebug(SCIkFILE, "Success!\n");
+ else
+ SCIkdebug(SCIkFILE, "Not found.\n");
+ }
+ }
+
+ if ((!file) && ((mode == _K_FILE_MODE_OPEN_OR_CREATE) || (mode == _K_FILE_MODE_CREATE))) {
+ file = sci_fopen(filename, "w" FO_BINARY "+"); /* Attempt to create file */
+ SCIkdebug(SCIkFILE, "Creating file %s with mode %d\n", filename, mode);
+ }
+ if (!file) { /* Failed */
+ SCIkdebug(SCIkFILE, "file_open() failed\n");
+ s->r_acc = make_reg(0, 0xffff);
+ return;
+ }
+
+ while (s->file_handles[retval] && (retval < s->file_handles_nr))
+ retval++;
+
+ if (retval == s->file_handles_nr) /* Hit size limit => Allocate more space */
+ s->file_handles = (FILE**)sci_realloc(s->file_handles, sizeof(FILE *) * ++(s->file_handles_nr));
+
+ s->file_handles[retval] = file;
+
+ s->r_acc = make_reg(0, retval);
+}
+
+reg_t
+kFOpen(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ char *name = kernel_dereference_char_pointer(s, argv[0], 0);
+ int mode = UKPV(1);
+
+ file_open(s, name, mode);
+ return s->r_acc;
+}
+
+void file_close(state_t *s, int handle)
+{
+ SCIkdebug(SCIkFILE, "Closing file %d\n", handle);
+
+ if (handle == 0) {
+ SCIkwarn(SCIkERROR, "Attempt to close file handle 0\n");
+ return;
+ }
+
+ if ((handle >= s->file_handles_nr) || (s->file_handles[handle] == NULL)) {
+ SCIkwarn(SCIkERROR, "Attempt to close invalid/unused file handle %d\n", handle);
+ return;
+ }
+
+ fclose(s->file_handles[handle]);
+
+ s->file_handles[handle] = NULL;
+}
+
+reg_t
+kFClose(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ file_close(s, UKPV(0));
+ return s->r_acc;
+}
+
+void fputs_wrapper(state_t *s, int handle, int size, char *data)
+{
+ SCIkdebug(SCIkFILE, "FPuts'ing \"%s\" to handle %d\n", data, handle);
+
+ if (handle == 0) {
+ SCIkwarn(SCIkERROR, "Attempt to write to file handle 0\n");
+ return;
+ }
+
+ if ((handle >= s->file_handles_nr) || (s->file_handles[handle] == NULL)) {
+ SCIkwarn(SCIkERROR, "Attempt to write to invalid/unused file handle %d\n", handle);
+ return;
+ }
+
+ fwrite(data, 1, size, s->file_handles[handle]);
+}
+
+void fwrite_wrapper(state_t *s, int handle, char *data, int length)
+{
+ SCIkdebug(SCIkFILE, "fwrite()'ing \"%s\" to handle %d\n", data, handle);
+
+ if (handle == 0) {
+ SCIkwarn(SCIkERROR, "Attempt to write to file handle 0\n");
+ return;
+ }
+
+ if ((handle >= s->file_handles_nr) || (s->file_handles[handle] == NULL)) {
+ SCIkwarn(SCIkERROR, "Attempt to write to invalid/unused file handle %d\n", handle);
+ return;
+ }
+
+ fwrite(data, 1, length, s->file_handles[handle]);
+}
+
+
+reg_t kFPuts(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int handle = UKPV(0);
+ char *data = kernel_dereference_char_pointer(s, argv[1], 0);
+
+ fputs_wrapper(s, handle, strlen(data), data);
+ return s->r_acc;
+}
+
+static void
+fgets_wrapper(state_t *s, char *dest, int maxsize, int handle)
+{
+ SCIkdebug(SCIkFILE, "FGets'ing %d bytes from handle %d\n", maxsize, handle);
+
+
+ if (handle == 0) {
+ SCIkwarn(SCIkERROR, "Attempt to read from file handle 0\n");
+ return;
+ }
+
+ if ((handle >= s->file_handles_nr) || (s->file_handles[handle] == NULL)) {
+ SCIkwarn(SCIkERROR, "Attempt to read from invalid/unused file handle %d\n", handle);
+ return;
+ }
+
+ fgets(dest, maxsize, s->file_handles[handle]);
+
+ SCIkdebug(SCIkFILE, "FGets'ed \"%s\"\n", dest);
+}
+
+
+static void
+fread_wrapper(state_t *s, char *dest, int bytes, int handle)
+{
+ SCIkdebug(SCIkFILE, "fread()'ing %d bytes from handle %d\n", bytes, handle);
+
+ if (handle == 0) {
+ SCIkwarn(SCIkERROR, "Attempt to read from file handle 0\n");
+ return;
+ }
+
+ if ((handle >= s->file_handles_nr) || (s->file_handles[handle] == NULL)) {
+ SCIkwarn(SCIkERROR, "Attempt to read from invalid/unused file handle %d\n", handle);
+ return;
+ }
+
+ s->r_acc=make_reg(0,fread(dest, 1, bytes, s->file_handles[handle]));
+}
+
+
+static void
+fseek_wrapper(state_t *s, int handle, int offset, int whence)
+{
+
+ if (handle == 0) {
+ SCIkwarn(SCIkERROR, "Attempt seek on file handle 0\n");
+ return;
+ }
+
+ if ((handle >= s->file_handles_nr) || (s->file_handles[handle] == NULL)) {
+ SCIkwarn(SCIkERROR, "Attempt seek on invalid/unused file handle %d\n", handle);
+ return;
+ }
+
+ s->r_acc=make_reg(0, fseek(s->file_handles[handle], offset, whence));
+}
+
+
+static char *
+_chdir_savedir(state_t *s)
+{
+ char *cwd = sci_getcwd();
+ char *save_dir = kernel_dereference_char_pointer(s,
+ make_reg(s->sys_strings_segment, SYS_STRING_SAVEDIR), 0);
+
+ if (chdir(save_dir) && sci_mkpath(save_dir)) {
+
+ sciprintf(__FILE__": Can't chdir to savegame dir '%s' or "
+ "create it\n", save_dir);
+
+ sci_free(cwd);
+ return NULL;
+ }
+
+ if (!cwd)
+ cwd = strdup(s->work_dir);
+
+ return cwd; /* Potentially try again */
+}
+
+static void
+_chdir_restoredir(char *dir)
+{
+ if (chdir(dir)) {
+ sciprintf(__FILE__": Can't seem to return to previous homedir '%s'\n",
+ dir);
+ }
+ free(dir);
+}
+
+#define TEST_DIR_OR_QUIT(dir) if (!dir) { return NULL_REG; }
+
+reg_t
+kFGets(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ char *dest = kernel_dereference_char_pointer(s, argv[0], 0);
+ int maxsize = UKPV(1);
+ int handle = UKPV(2);
+
+ fgets_wrapper(s, dest, maxsize, handle);
+ return argv[0];
+}
+
+
+/* kGetCWD(address):
+** Writes the cwd to the supplied address and returns the address in acc.
+*/
+reg_t
+kGetCWD(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ char *wd = sci_getcwd();
+ char *targetaddr = kernel_dereference_char_pointer(s, argv[0], 0);
+
+ strncpy(targetaddr, wd, MAX_SAVE_DIR_SIZE - 1);
+ targetaddr[MAX_SAVE_DIR_SIZE - 1] = 0; /* Terminate */
+
+ SCIkdebug(SCIkFILE, "Copying cwd='%s'(%d chars) to %p",
+ wd, strlen(wd), targetaddr);
+
+ free(wd);
+ return argv[0];
+}
+
+/* Returns a dynamically allocated pointer to the name of the requested save dir */
+char *
+_k_get_savedir_name(int nr)
+{
+ char suffices[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+ char *savedir_name = (char*)sci_malloc(strlen(FREESCI_SAVEDIR_PREFIX) + 2);
+ assert(nr >= 0);
+ assert(nr < MAX_SAVEGAME_NR);
+ strcpy(savedir_name, FREESCI_SAVEDIR_PREFIX);
+ savedir_name[strlen(FREESCI_SAVEDIR_PREFIX)] = suffices[nr];
+ savedir_name[strlen(FREESCI_SAVEDIR_PREFIX) + 1] = 0;
+
+ return savedir_name;
+}
+
+void
+delete_savegame(state_t *s, int savedir_nr)
+{
+ char *workdir = _chdir_savedir(s);
+ char *savedir = _k_get_savedir_name(savedir_nr);
+ char buffer[256];
+
+ sciprintf("Deleting savegame '%s'\n", savedir);
+
+ sprintf(buffer, "%s/%s.id", savedir, s->game_name);
+ sci_unlink(buffer);
+
+ sprintf(buffer, "%s/state", savedir);
+ sci_unlink(buffer);
+
+ sci_rmdir(savedir);
+
+ free(savedir);
+ _chdir_restoredir(workdir);
+}
+
+#define K_DEVICE_INFO_GET_DEVICE 0
+#define K_DEVICE_INFO_GET_CURRENT_DEVICE 1
+#define K_DEVICE_INFO_PATHS_EQUAL 2
+#define K_DEVICE_INFO_IS_FLOPPY 3
+#define K_DEVICE_INFO_GET_SAVECAT_NAME 7
+#define K_DEVICE_INFO_GET_SAVEFILE_NAME 8
+
+#ifdef _WIN32
+
+reg_t
+kDeviceInfo_Win32(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ char dir_buffer [MAX_PATH], dir_buffer2 [MAX_PATH];
+ int mode = UKPV(0);
+
+
+ switch(mode) {
+
+ case K_DEVICE_INFO_GET_DEVICE: {
+ char *input_s = (char*)kernel_dereference_bulk_pointer(s, argv[1], 0);
+ char *output_s = (char*)kernel_dereference_bulk_pointer(s, argv[2], 0);
+
+ GetFullPathName (input_s, sizeof (dir_buffer)-1, dir_buffer, NULL);
+ strncpy(output_s, dir_buffer, 2);
+ output_s [2] = 0;
+ }
+ break;
+
+ case K_DEVICE_INFO_GET_CURRENT_DEVICE: {
+ char *output_s = (char*)kernel_dereference_bulk_pointer(s, argv[1], 0);
+
+ _getcwd (dir_buffer, sizeof (dir_buffer)-1);
+ strncpy(output_s, dir_buffer, 2);
+ output_s [2] = 0;
+ }
+ break;
+
+ case K_DEVICE_INFO_PATHS_EQUAL: {
+ char *path1_s = (char*)kernel_dereference_bulk_pointer(s, argv[1], 0);
+ char *path2_s = (char*)kernel_dereference_bulk_pointer(s, argv[2], 0);
+
+ GetFullPathName (path1_s, sizeof (dir_buffer)-1, dir_buffer, NULL);
+ GetFullPathName (path2_s, sizeof (dir_buffer2)-1, dir_buffer2, NULL);
+
+#ifdef _MSC_VER
+ return make_reg(0, !stricmp (path1_s, path2_s));
+#else
+ return make_reg(0, !strcasecmp (path1_s, path2_s));
+#endif
+ }
+ break;
+
+ case K_DEVICE_INFO_IS_FLOPPY: {
+ char *input_s = (char*)kernel_dereference_bulk_pointer(s, argv[1], 0);
+
+ GetFullPathName (input_s, sizeof (dir_buffer)-1, dir_buffer, NULL);
+ dir_buffer [3] = 0; /* leave X:\ */
+
+ return make_reg(0, GetDriveType (dir_buffer) == DRIVE_REMOVABLE);
+ }
+ break;
+
+/* SCI uses these in a less-than-portable way to delete savegames.
+** Read http://www-plan.cs.colorado.edu/creichen/freesci-logs/2005.10/log20051019.html
+** for more information on our workaround for this.
+*/
+ case K_DEVICE_INFO_GET_SAVECAT_NAME: {
+ char *output_buffer = kernel_dereference_char_pointer(s, argv[1], 0);
+ char *game_prefix = kernel_dereference_char_pointer(s, argv[2], 0);
+
+ sprintf(output_buffer, "%s/__throwaway", s->work_dir);
+ }
+
+ break;
+ case K_DEVICE_INFO_GET_SAVEFILE_NAME: {
+ char *output_buffer = kernel_dereference_char_pointer(s, argv[1], 0);
+ char *game_prefix = kernel_dereference_char_pointer(s, argv[2], 0);
+ int savegame_id = UKPV(3);
+ sprintf(output_buffer, "%s/__throwaway", s->work_dir);
+ delete_savegame(s, savegame_id);
+ }
+ break;
+ default: {
+ SCIkwarn(SCIkERROR, "Unknown DeviceInfo() sub-command: %d\n", mode);
+ }
+ }
+ return s->r_acc;
+}
+
+#else /* !_WIN32 */
+
+reg_t
+kDeviceInfo_Unix(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int mode = UKPV(0);
+
+ switch(mode) {
+
+ case K_DEVICE_INFO_GET_DEVICE: {
+ char *output_s = kernel_dereference_char_pointer(s, argv[2], 0);
+
+ strcpy(output_s, "/");
+ }
+ break;
+
+ case K_DEVICE_INFO_GET_CURRENT_DEVICE: {
+ char *output_s = kernel_dereference_char_pointer(s, argv[1], 0);
+
+ strcpy(output_s, "/");
+ }
+ break;
+
+ case K_DEVICE_INFO_PATHS_EQUAL: {
+ char *path1_s = kernel_dereference_char_pointer(s, argv[1], 0);
+ char *path2_s = kernel_dereference_char_pointer(s, argv[2], 0);
+
+#ifndef HAVE_FNMATCH_H
+#ifndef _DOS
+# warning "File matches will be unprecise!"
+#endif
+ return make_reg(0, !strcmp(path1_s, path2_s));
+#else
+ return make_reg(0, fnmatch(path1_s, path2_s, FNM_PATHNAME) /* POSIX.2 */);
+#endif
+ }
+ break;
+
+ case K_DEVICE_INFO_IS_FLOPPY: {
+
+ return NULL_REG; /* Never */
+ }
+ break;
+
+/* SCI uses these in a less-than-portable way to delete savegames.
+** Read http://www-plan.cs.colorado.edu/creichen/freesci-logs/2005.10/log20051019.html
+** for more information on our workaround for this.
+*/
+ case K_DEVICE_INFO_GET_SAVECAT_NAME: {
+ char *output_buffer = kernel_dereference_char_pointer(s, argv[1], 0);
+/* char *game_prefix = kernel_dereference_char_pointer(s, argv[2], 0);*/
+
+ sprintf(output_buffer, "%s/__throwaway", s->work_dir);
+ }
+
+ break;
+ case K_DEVICE_INFO_GET_SAVEFILE_NAME: {
+ char *output_buffer = kernel_dereference_char_pointer(s, argv[1], 0);
+/* char *game_prefix = kernel_dereference_char_pointer(s, argv[2], 0);*/
+ int savegame_id = UKPV(3);
+ sprintf(output_buffer, "%s/__throwaway", s->work_dir);
+ delete_savegame(s, savegame_id);
+ }
+ break;
+ default: {
+ SCIkwarn(SCIkERROR, "Unknown DeviceInfo() sub-command: %d\n", mode);
+ }
+ }
+
+ return s->r_acc;
+}
+
+#endif /* !_WIN32 */
+
+
+reg_t
+kGetSaveDir(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ return make_reg(s->sys_strings_segment, SYS_STRING_SAVEDIR);
+}
+
+
+reg_t
+kCheckFreeSpace(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ char *path = kernel_dereference_char_pointer(s, argv[0], 0);
+ char *testpath = (char*)sci_malloc(strlen(path) + 15);
+ char buf[1024];
+ int i;
+ int fd;
+ int failed = 0;
+ int pathlen;
+
+ strcpy(testpath, path);
+ strcat(testpath, "freesci.foo");
+ pathlen = strlen(testpath);
+
+ while (IS_VALID_FD(fd = open(testpath, O_RDONLY))) {
+ close(fd);
+ if (testpath[pathlen - 2] == 'z') { /* Failed. */
+ SCIkwarn(SCIkWARNING, "Failed to find non-existing file for free space test\n");
+ free(testpath);
+ return NULL_REG;
+ }
+
+ /* If this file couldn't be created, try freesci.fop, freesci.foq etc.,
+ ** then freesci.fpa, freesci.fpb. Stop at freesci.fza.
+ ** Yes, this is extremely arbitrary and very strange.
+ */
+ if (testpath[pathlen - 1] == 'z') {
+ testpath[pathlen - 1] = 'a';
+ ++testpath[pathlen - 2];
+ }
+ else
+ ++testpath[pathlen - 1];
+ }
+
+ fd = creat(testpath, 0600);
+
+ if (!IS_VALID_FD(fd)) {
+ SCIkwarn(SCIkWARNING,"Could not test for disk space: %s\n", strerror(errno));
+ SCIkwarn(SCIkWARNING,"Test path was '%s'\n", testpath);
+ free(testpath);
+ return NULL_REG;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ for (i = 0; i < 1024; i++) /* Check for 1 MB */
+ if (write(fd, buf, 1024) < 1024)
+ failed = 1;
+
+ close(fd);
+
+ remove(testpath);
+
+ sci_free(testpath);
+
+ return make_reg(0, !failed);
+}
+
+
+
+int
+_k_check_file(char *filename, int minfilesize)
+ /* Returns 0 if the file exists and is big enough */
+{
+ return (sci_file_size(filename) < minfilesize);
+}
+
+int
+_k_find_savegame_by_name(char *game_id_file, char *name)
+{
+ int savedir_nr = -1;
+ int i;
+ char *buf = NULL;
+
+ for (i = 0; i < MAX_SAVEGAME_NR; i++) {
+ if (!chdir((buf = _k_get_savedir_name(i)))) {
+ char namebuf[32]; /* Save game name buffer */
+ FILE *idfile = sci_fopen(game_id_file, "r");
+
+ if (idfile) {
+ fgets(namebuf, 31, idfile);
+ if (strlen(namebuf) > 0)
+ if (namebuf[strlen(namebuf) - 1] == '\n')
+ namebuf[strlen(namebuf) - 1] = 0; /* Remove trailing newlines */
+
+ if (strcmp(name, namebuf) == 0) {
+ sciprintf("Save game name matched entry %d\n", i);
+ savedir_nr = i;
+ }
+
+ fclose(idfile);
+ }
+
+ chdir("..");
+ }
+ free(buf);
+ }
+ return 0;
+}
+
+#ifdef _DREAMCAST
+static long
+get_file_mtime(int fd)
+{
+ /* FIXME (Dreamcast): Not yet implemented */
+ return 0;
+}
+
+#else
+
+#define get_file_mtime(fd) get_file_mtime_Unix(fd)
+/* Returns the time of the specified file's last modification
+** Parameters: (int) fd: The file descriptor of the file in question
+** Returns : (long) An integer value describing the time of the
+** file's last modification.
+** The only thing that must be ensured is that
+** get_file_mtime(f1) > get_file_mtime(f2)
+** <=>
+** if f1 was modified at a later point in time than the last time
+** f2 was modified.
+*/
+
+static long
+get_file_mtime_Unix(int fd) /* returns the */
+{
+ struct stat fd_stat;
+ fstat(fd, &fd_stat);
+
+ return fd_stat.st_ctime;
+}
+#endif
+
+static int
+_savegame_index_struct_compare(const void *a, const void *b)
+{
+ return ((struct _savegame_index_struct *)b)->timestamp
+ - ((struct _savegame_index_struct *)a)->timestamp;
+}
+
+static void
+update_savegame_indices(char *gfname)
+{
+ int i;
+
+ _savegame_indices_nr = 0;
+
+ for (i = 0; i < MAX_SAVEGAME_NR; i++) {
+ char *dirname = _k_get_savedir_name(i);
+ int fd;
+
+ if (!chdir(dirname)) {
+
+ if (IS_VALID_FD(fd = sci_open(gfname, O_RDONLY))) {
+ _savegame_indices[_savegame_indices_nr].id = i;
+ _savegame_indices[_savegame_indices_nr++].timestamp = get_file_mtime(fd);
+ close(fd);
+ }
+ chdir("..");
+ }
+
+ free(dirname);
+ }
+
+ qsort(_savegame_indices, _savegame_indices_nr, sizeof(struct _savegame_index_struct),
+ _savegame_index_struct_compare);
+
+}
+
+int
+test_savegame(state_t *s, char *savegame_id, char *savegame_name, int savegame_name_length)
+{
+ FILE *f;
+ char buffer[80];
+ int version = -1;
+
+ chdir(savegame_id);
+ f = fopen("state", "r");
+
+ if (!f) return 0;
+ while (!feof(f))
+ {
+ char *seeker;
+ fgets(buffer, sizeof(buffer), f);
+ if ((seeker = strstr(buffer, "savegame_version = ")) != NULL)
+ {
+ seeker += strlen("savegame_version = ");
+ version = strtol(seeker, NULL, 10);
+ break;
+ }
+ }
+
+ fclose(f);
+ return version >= FREESCI_MINIMUM_SAVEGAME_VERSION &&
+ version <= FREESCI_CURRENT_SAVEGAME_VERSION;
+}
+
+reg_t
+kCheckSaveGame(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ char *game_id = kernel_dereference_char_pointer(s, argv[0], 0);
+ int savedir_nr = UKPV(1);
+ char *buf = NULL;
+ char *workdir = _chdir_savedir(s);
+ TEST_DIR_OR_QUIT(workdir);
+
+ if (_savegame_indices_nr < 0) {
+ char *game_id_file_name = (char*)sci_malloc(strlen(game_id) + strlen(FREESCI_ID_SUFFIX) + 1);
+
+ strcpy(game_id_file_name, game_id);
+ strcat(game_id_file_name, FREESCI_ID_SUFFIX);
+ SCIkwarn(SCIkWARNING, "Savegame index list not initialized!\n");
+ update_savegame_indices(game_id_file_name);
+ }
+
+ savedir_nr = _savegame_indices[savedir_nr].id;
+
+
+ if (savedir_nr > MAX_SAVEGAME_NR-1) {
+ _chdir_restoredir(workdir);
+ return NULL_REG;
+ }
+
+ s->r_acc = make_reg(0, test_savegame(s, (buf = _k_get_savedir_name(savedir_nr)), NULL, 0));
+
+ _chdir_restoredir(workdir);
+ free(buf);
+
+ return s->r_acc;
+}
+
+
+reg_t
+kGetSaveFiles(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ char *game_id = kernel_dereference_char_pointer(s, argv[0], 0);
+ char *nametarget = kernel_dereference_char_pointer(s, argv[1], 0);
+ reg_t nametarget_base = argv[1];
+ reg_t *nameoffsets = kernel_dereference_reg_pointer(s, argv[2], 0);
+ int gfname_len = strlen(game_id) + strlen(FREESCI_ID_SUFFIX) + 1;
+ char *gfname = (char*)sci_malloc(gfname_len);
+ int i;
+ char *workdir = _chdir_savedir(s);
+ TEST_DIR_OR_QUIT(workdir);
+
+ strcpy(gfname, game_id);
+ strcat(gfname, FREESCI_ID_SUFFIX); /* This file is used to identify in-game savegames */
+
+ update_savegame_indices(gfname);
+
+ s->r_acc = NULL_REG;
+
+ for (i = 0; i < _savegame_indices_nr; i++) {
+ char *savedir_name = _k_get_savedir_name(_savegame_indices[i].id);
+ FILE *idfile;
+
+ if (!chdir(savedir_name)) {
+
+
+ if ((idfile = sci_fopen(gfname, "r"))) { /* Valid game ID file: Assume valid game */
+ char namebuf[SCI_MAX_SAVENAME_LENGTH]; /* Save game name buffer */
+ fgets(namebuf, SCI_MAX_SAVENAME_LENGTH-1, idfile);
+ if (strlen(namebuf) > 0) {
+
+ if (namebuf[strlen(namebuf) - 1] == '\n')
+ namebuf[strlen(namebuf) - 1] = 0; /* Remove trailing newline */
+
+ *nameoffsets = s->r_acc; /* Store savegame ID */
+ ++s->r_acc.offset; /* Increase number of files found */
+
+ nameoffsets++; /* Make sure the next ID string address is written to the next pointer */
+ strncpy(nametarget, namebuf, SCI_MAX_SAVENAME_LENGTH); /* Copy identifier string */
+ *(nametarget + SCI_MAX_SAVENAME_LENGTH - 1) = 0; /* Make sure it's terminated */
+ nametarget += SCI_MAX_SAVENAME_LENGTH; /* Increase name offset pointer accordingly */
+ nametarget_base.offset += SCI_MAX_SAVENAME_LENGTH;
+
+ fclose(idfile);
+ }
+ }
+ chdir("..");
+ }
+ free(savedir_name);
+ }
+
+ free(gfname);
+ *nametarget = 0; /* Terminate list */
+
+ _chdir_restoredir(workdir);
+ return s->r_acc;
+}
+
+reg_t
+kSaveGame(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ char *game_id = (char*)kernel_dereference_bulk_pointer(s, argv[0], 0);
+ char *savegame_dir;
+ int savedir_nr = UKPV(1);
+ int savedir_id; /* Savegame ID, derived from savedir_nr and the savegame ID list */
+ char *game_id_file_name = (char*)sci_malloc(strlen(game_id) + strlen(FREESCI_ID_SUFFIX) + 1);
+ char *game_description = (char*)kernel_dereference_bulk_pointer(s, argv[2], 0);
+ char *workdir = _chdir_savedir(s);
+ char *version = argc > 3 ? strdup((char*)kernel_dereference_bulk_pointer(s, argv[3], 0)) : NULL;
+ TEST_DIR_OR_QUIT(workdir);
+
+ s->game_version = version;
+
+ strcpy(game_id_file_name, game_id);
+ strcat(game_id_file_name, FREESCI_ID_SUFFIX);
+
+ update_savegame_indices(game_id_file_name);
+
+ if (savedir_nr >= 0 && savedir_nr < _savegame_indices_nr)
+ /* Overwrite */
+ savedir_id = _savegame_indices[savedir_nr].id;
+ else if (savedir_nr >= 0 && savedir_nr < MAX_SAVEGAME_NR) {
+ int i = 0;
+
+ savedir_id = 0;
+
+ /* First, look for holes */
+ while (i < _savegame_indices_nr)
+ if (_savegame_indices[i].id == savedir_id) {
+ ++savedir_id;
+ i = 0;
+ } else ++i;
+
+ if (savedir_id >= MAX_SAVEGAME_NR) {
+ sciprintf("Internal error: Free savegame ID is %d, shouldn't happen!\n",
+ savedir_id);
+ return NULL_REG;
+ }
+
+ /* This loop terminates when savedir_id is not in [x | ex. n. _savegame_indices[n].id = x] */
+ } else {
+ sciprintf("Savegame ID %d is not allowed!\n", savedir_nr);
+ return NULL_REG;
+ }
+
+ savegame_dir = _k_get_savedir_name(savedir_id);
+
+ if (gamestate_save(s, savegame_dir)) {
+ sciprintf("Saving the game failed.\n");
+ s->r_acc = NULL_REG;
+ } else {
+ FILE *idfile;
+
+ chdir(savegame_dir);
+
+ if ((idfile = sci_fopen(game_id_file_name, "w"))) {
+
+ fprintf(idfile, game_description);
+ fclose(idfile);
+
+ } else {
+ sciprintf("Creating the game ID file failed.\n");
+ sciprintf("You can still restore from inside the debugger with \"restore_game %s\"\n", savegame_dir);
+ s->r_acc = NULL_REG;
+ }
+
+ chdir ("..");
+ s->r_acc = make_reg(0, 1);
+ }
+ free(game_id_file_name);
+ _chdir_restoredir(workdir);
+
+ free(s->game_version);
+ s->game_version = NULL;
+ return s->r_acc;
+}
+
+
+reg_t
+kRestoreGame(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ char *game_id = (char*)kernel_dereference_bulk_pointer(s, argv[0], 0);
+ int savedir_nr = UKPV(1);
+ char *workdir = _chdir_savedir(s);
+ TEST_DIR_OR_QUIT(workdir);
+
+ if (_savegame_indices_nr < 0) {
+ char *game_id_file_name = (char*)sci_malloc(strlen(game_id) + strlen(FREESCI_ID_SUFFIX) + 1);
+
+ strcpy(game_id_file_name, game_id);
+ strcat(game_id_file_name, FREESCI_ID_SUFFIX);
+ SCIkwarn(SCIkWARNING, "Savegame index list not initialized!\n");
+ update_savegame_indices(game_id_file_name);
+ }
+
+ savedir_nr = _savegame_indices[savedir_nr].id;
+
+
+ if (savedir_nr > -1) {
+ char *savedir_name = _k_get_savedir_name(savedir_nr);
+ state_t *newstate = gamestate_restore(s, savedir_name);
+
+ free(savedir_name);
+
+ if (newstate) {
+
+ s->successor = newstate;
+ script_abort_flag = SCRIPT_ABORT_WITH_REPLAY; /* Abort current game */
+ s->execution_stack_pos = s->execution_stack_base;
+
+ } else {
+ s->r_acc = make_reg(0, 1);
+ sciprintf("Restoring failed (game_id = '%s').\n", game_id);
+ }
+
+ } else {
+ s->r_acc = make_reg(0, 1);
+ sciprintf("Savegame #%d not found!\n", savedir_nr);
+ }
+
+ _chdir_restoredir(workdir);
+ return s->r_acc;
+}
+
+
+reg_t
+kValidPath(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ char *pathname = kernel_dereference_char_pointer(s, argv[0], 0);
+ char cpath[PATH_MAX + 1];
+ getcwd(cpath, PATH_MAX + 1);
+
+ s->r_acc = make_reg(0, !chdir(pathname)); /* Try to go there. If it works, return 1, 0 otherwise. */
+
+ chdir(cpath);
+
+ return s->r_acc;
+}
+
+#define K_FILEIO_OPEN 0
+#define K_FILEIO_CLOSE 1
+#define K_FILEIO_READ_RAW 2
+#define K_FILEIO_WRITE_RAW 3
+#define K_FILEIO_UNLINK 4
+#define K_FILEIO_READ_STRING 5
+#define K_FILEIO_WRITE_STRING 6
+#define K_FILEIO_SEEK 7
+#define K_FILEIO_FIND_FIRST 8
+#define K_FILEIO_FIND_NEXT 9
+#define K_FILEIO_STAT 10
+
+
+char *
+write_filename_to_mem(state_t *s, reg_t address, char *string)
+{
+ char *mem = kernel_dereference_char_pointer(s, address, 0);
+
+ if (string) {
+ memset(mem, 0, 13);
+ strncpy(mem, string, 12);
+ }
+
+ return string;
+}
+
+void
+next_file(state_t *s)
+{
+ if (write_filename_to_mem(s, s->dirseeker_outbuffer,
+ sci_find_next(&(s->dirseeker))))
+ s->r_acc = s->dirseeker_outbuffer;
+ else
+ s->r_acc = NULL_REG;
+}
+
+void
+first_file(state_t *s, const char *dir, char *mask, reg_t buffer)
+{
+ if (!buffer.segment) {
+ sciprintf("Warning: first_file(state,\"%s\",\"%s\", 0) invoked!\n",
+ dir, mask);
+ s->r_acc = NULL_REG;
+ return;
+ }
+
+ if (strcmp(dir, ".")) {
+ sciprintf("%s L%d: Non-local first_file: Not implemented yet\n",
+ __FILE__, __LINE__);
+ s->r_acc = NULL_REG;
+ return;
+ }
+
+ /* Get rid of the old find structure */
+ if (s->dirseeker_outbuffer.segment)
+ sci_finish_find(&(s->dirseeker));
+
+ s->dirseeker_outbuffer = buffer;
+
+ if (write_filename_to_mem(s, s->dirseeker_outbuffer,
+ sci_find_first(&(s->dirseeker), mask)))
+ s->r_acc = s->dirseeker_outbuffer;
+ else
+ s->r_acc = NULL_REG;
+}
+
+reg_t
+kFileIO(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int func_nr = UKPV(0);
+
+ switch (func_nr) {
+
+ case K_FILEIO_OPEN :
+ {
+ char *name = kernel_dereference_char_pointer(s, argv[1], 0);
+ int mode = UKPV(2);
+
+ file_open(s, name, mode);
+ break;
+ }
+ case K_FILEIO_CLOSE :
+ {
+ int handle = UKPV(1);
+
+ file_close(s, handle);
+ break;
+ }
+ case K_FILEIO_READ_RAW :
+ {
+ int handle = UKPV(1);
+ char *dest = kernel_dereference_char_pointer(s, argv[2], 0);
+ int size = UKPV(3);
+
+ fread_wrapper(s, dest, size, handle);
+ break;
+ }
+ case K_FILEIO_WRITE_RAW :
+ {
+ int handle = UKPV(1);
+ char *buf = kernel_dereference_char_pointer(s, argv[2], 0);
+ int size = UKPV(3);
+
+ fwrite_wrapper(s, handle, buf, size);
+ break;
+ }
+ case K_FILEIO_UNLINK :
+ {
+ char *name = kernel_dereference_char_pointer(s, argv[1], 0);
+
+ unlink(name);
+ break;
+ }
+ case K_FILEIO_READ_STRING :
+ {
+ char *dest = kernel_dereference_char_pointer(s, argv[1], 0);
+ int size = UKPV(2);
+ int handle = UKPV(3);
+
+ fgets_wrapper(s, dest, size, handle);
+ return argv[1];
+ }
+ case K_FILEIO_WRITE_STRING :
+ {
+ int handle = UKPV(1);
+ int size = UKPV(3);
+ char *buf = kernel_dereference_char_pointer(s, argv[2], size);
+
+ if (buf)
+ fputs_wrapper(s, handle, size, buf);
+ break;
+ }
+ case K_FILEIO_SEEK :
+ {
+ int handle = UKPV(1);
+ int offset = UKPV(2);
+ int whence = UKPV(3);
+
+ fseek_wrapper(s, handle, offset, whence);
+ break;
+ }
+ case K_FILEIO_FIND_FIRST :
+ {
+ char *mask = kernel_dereference_char_pointer(s, argv[1], 0);
+ reg_t buf = argv[2];
+ /* int attr = UKPV(3); */ /* We won't use this, Win32 might, though... */
+
+#ifndef _WIN32
+ if (strcmp(mask, "*.*")==0) strcpy(mask, "*"); /* For UNIX */
+#endif
+ first_file(s, ".", mask, buf);
+
+ break;
+ }
+ case K_FILEIO_FIND_NEXT :
+ {
+ next_file(s);
+ break;
+ }
+ case K_FILEIO_STAT :
+ {
+ char *name = kernel_dereference_char_pointer(s, argv[1], 0);
+ s->r_acc=make_reg(0, 1-_k_check_file(name, 0));
+ break;
+ }
+ default :
+ SCIkwarn(SCIkERROR, "Unknown FileIO() sub-command: %d\n", func_nr);
+ }
+
+ return s->r_acc;
+}
diff --git a/engines/sci/engine/kgraphics.c b/engines/sci/engine/kgraphics.c
new file mode 100644
index 0000000000..2f15877ac8
--- /dev/null
+++ b/engines/sci/engine/kgraphics.c
@@ -0,0 +1,3627 @@
+/***************************************************************************
+ kgraphics.c Copyright (C) 1999,2000..04 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) [creichen@gmail.com]
+
+***************************************************************************/
+
+#include <sciresource.h>
+#include <engine.h>
+#include <gfx_widgets.h>
+#include "sci_graphics.h"
+#include <sci_widgets.h>
+
+#undef DEBUG_LSRECT
+
+/* Graph subfunctions */
+#define K_GRAPH_GET_COLORS_NR 2
+#define K_GRAPH_DRAW_LINE 4
+#define K_GRAPH_SAVE_BOX 7
+#define K_GRAPH_RESTORE_BOX 8
+#define K_GRAPH_FILL_BOX_BACKGROUND 9
+#define K_GRAPH_FILL_BOX_FOREGROUND 10
+#define K_GRAPH_FILL_BOX_ANY 11
+#define K_GRAPH_UPDATE_BOX 12
+#define K_GRAPH_REDRAW_BOX 13
+#define K_GRAPH_ADJUST_PRIORITY 14
+
+/* Control types and flags */
+#define K_CONTROL_BUTTON 1
+#define K_CONTROL_TEXT 2
+#define K_CONTROL_EDIT 3
+#define K_CONTROL_ICON 4
+#define K_CONTROL_CONTROL 6
+#define K_CONTROL_CONTROL_ALIAS 7
+#define K_CONTROL_BOX 10
+
+
+#define ADD_TO_CURRENT_PORT(widget) \
+ {if (s->port) \
+ s->port->add(GFXWC(s->port), GFXW(widget)); \
+ else \
+ s->picture_port->add(GFXWC(s->visual), GFXW(widget));}
+
+#define ADD_TO_CURRENT_PICTURE_PORT(widget) \
+ {if (s->port) \
+ s->port->add(GFXWC(s->port), GFXW(widget)); \
+ else \
+ s->picture_port->add(GFXWC(s->picture_port), GFXW(widget));}
+
+#define ADD_TO_WINDOW_PORT(widget) \
+ s->wm_port->add(GFXWC(s->wm_port), GFXW(widget));
+
+#define ADD_TO_CURRENT_FG_WIDGETS(widget) \
+ ADD_TO_CURRENT_PICTURE_PORT(widget)
+
+#define ADD_TO_CURRENT_BG_WIDGETS(widget) \
+ ADD_TO_CURRENT_PICTURE_PORT(widget)
+
+#define FULL_REDRAW()\
+ if (s->visual) \
+ s->visual->draw(GFXW(s->visual), gfxw_point_zero); \
+ gfxop_update(s->gfx_state);
+
+#define FULL_INSPECTION()\
+ if (s->visual) \
+ s->visual->print(GFXW(s->visual), 0);
+
+
+#define GFX_ASSERT(x) { \
+ int val = !!(x); \
+ if (val) { \
+ if (val == GFX_ERROR) \
+ SCIkwarn(SCIkWARNING, "GFX subsystem returned error on \"" #x "\"!\n"); \
+ else {\
+ SCIkwarn(SCIkERROR, "GFX subsystem fatal error condition on \"" #x "\"!\n"); \
+ vm_handle_fatal_error(s, __LINE__, __FILE__); \
+ } \
+ }\
+}
+
+#define ASSERT(x) { \
+ int val = !!(x); \
+ if (!val) { \
+ SCIkwarn(SCIkERROR, "Fatal error condition on \"" #x "\"!\n"); \
+ BREAKPOINT(); \
+ vm_handle_fatal_error(s, __LINE__, __FILE__); \
+ } \
+}
+
+
+static inline int
+sign_extend_byte(int value)
+{
+ if (value & 0x80)
+ return value - 256;
+ else
+ return value;
+}
+
+
+static void
+assert_primary_widget_lists(state_t *s)
+{
+ if (!s->dyn_views) {
+ rect_t bounds = s->picture_port->bounds;
+
+ s->dyn_views = gfxw_new_list(bounds, GFXW_LIST_SORTED);
+ s->dyn_views->flags |= GFXW_FLAG_IMMUNE_TO_SNAPSHOTS;
+ ADD_TO_CURRENT_PICTURE_PORT(s->dyn_views);
+ }
+
+ if (!s->drop_views) {
+ rect_t bounds = s->picture_port->bounds;
+
+ s->drop_views = gfxw_new_list(bounds, GFXW_LIST_SORTED);
+ s->drop_views->flags |= GFXW_FLAG_IMMUNE_TO_SNAPSHOTS;
+ ADD_TO_CURRENT_PICTURE_PORT(s->drop_views);
+ }
+}
+
+static void
+reparentize_primary_widget_lists(state_t *s, gfxw_port_t *newport)
+{
+ if (!newport)
+ newport = s->picture_port;
+
+ if (s->dyn_views) {
+ gfxw_remove_widget_from_container(s->dyn_views->parent, GFXW(s->dyn_views));
+
+ newport->add(GFXWC(newport), GFXW(s->dyn_views));
+ }
+}
+
+int
+_find_view_priority(state_t *s, int y)
+{
+ /* if (s->version <= SCI_VERSION_LTU_PRIORITY_OB1)
+ ++y; */
+
+ if (s->pic_priority_table) { /* SCI01 priority table set? */
+ int j;
+ for (j = 0; j < 15; j++)
+ if (y < s->pic_priority_table[j+1])
+ return j;
+ return 14; /* Maximum */
+ } else
+ {
+ if (s->version >= SCI_VERSION_FTU_PRIORITY_14_ZONES)
+ return SCI0_VIEW_PRIORITY_14_ZONES(y);
+ else
+ return SCI0_VIEW_PRIORITY(y) == 15 ? 14 :
+ SCI0_VIEW_PRIORITY(y);
+ }
+}
+
+inline int
+_find_priority_band(state_t *s, int nr)
+{
+ if (s->version >= SCI_VERSION_FTU_PRIORITY_14_ZONES && (nr < 0 || nr > 14)) {
+ if (nr == 15)
+ return 0xffff;
+ else {
+ SCIkwarn(SCIkWARNING, "Attempt to get priority band %d\n", nr);
+ }
+ return 0;
+ }
+
+ if (s->version < SCI_VERSION_FTU_PRIORITY_14_ZONES && (nr < 0 || nr > 15)) {
+ SCIkwarn(SCIkWARNING, "Attempt to get priority band %d\n", nr);
+ return 0;
+ }
+
+ if (s->pic_priority_table) /* SCI01 priority table set? */
+ return s->pic_priority_table[nr];
+ else {
+ int retval;
+
+ if (s->version >= SCI_VERSION_FTU_PRIORITY_14_ZONES)
+ retval = SCI0_PRIORITY_BAND_FIRST_14_ZONES(nr);
+ else
+ retval = SCI0_PRIORITY_BAND_FIRST(nr);
+
+ /* if (s->version <= SCI_VERSION_LTU_PRIORITY_OB1)
+ --retval; */
+ return retval;
+ }
+}
+
+reg_t
+graph_save_box(state_t *s, rect_t area)
+{
+ reg_t handle = kalloc(s, "graph_save_box()", sizeof(gfxw_snapshot_t *));
+ gfxw_snapshot_t **ptr = (gfxw_snapshot_t **) kmem(s, handle);
+
+ *ptr = gfxw_make_snapshot(s->visual, area);
+
+ return handle;
+}
+
+
+void
+graph_restore_box(state_t *s, reg_t handle)
+{
+ gfxw_snapshot_t **ptr;
+ int port_nr = s->port->ID;
+
+ if (!handle.segment) {
+ SCIkwarn(SCIkWARNING, "Attempt to restore box with zero handle\n");
+ return;
+ }
+
+ ptr = (gfxw_snapshot_t **) kmem(s, handle);
+
+ if (!ptr) {
+ SCIkwarn(SCIkWARNING, "Attempt to restore invalid handle %04x\n", handle);
+ return;
+ }
+
+ while (port_nr > 2 && !(s->port->flags & GFXW_FLAG_IMMUNE_TO_SNAPSHOTS)
+ &&(gfxw_widget_matches_snapshot(*ptr, GFXW(s->port)))) {
+ /* This shouldn't ever happen, actually, since windows (ports w/ ID > 2) should all be immune */
+ gfxw_port_t *newport = gfxw_find_port(s->visual, port_nr);
+ SCIkwarn(SCIkERROR, "Port %d is not immune against snapshots!\n", s->port->ID);
+ port_nr--;
+ if (newport)
+ s->port = newport;
+ }
+
+ if (s->dyn_views && gfxw_widget_matches_snapshot(*ptr, GFXW(s->dyn_views->parent))) {
+ gfxw_container_t *parent = s->dyn_views->parent;
+
+ do {
+ parent = parent->parent;
+ } while (parent && (gfxw_widget_matches_snapshot(*ptr, GFXW(parent))));
+
+ if (!parent) {
+ SCIkwarn(SCIkERROR, "Attempted widget mass destruction by a snapshot\n");
+ BREAKPOINT();
+ }
+
+ reparentize_primary_widget_lists(s, (gfxw_port_t *) parent);
+ }
+
+
+ if (!ptr) {
+ SCIkwarn(SCIkERROR, "Attempt to restore invalid snaphot with handle %04x!\n", handle);
+ return;
+ }
+
+ gfxw_restore_snapshot(s->visual, *ptr);
+ free(*ptr);
+ *ptr = NULL;
+
+ kfree(s, handle);
+}
+
+#if 0
+#define KERNEL_COLOR_PALETTE s->gfx_state->pic->visual_map->colors
+#define KERNEL_COLORS_NR s->gfx_state->pic->visual_map->colors_nr
+#else
+#define KERNEL_COLOR_PALETTE s->gfx_state->resstate->static_palette
+#define KERNEL_COLORS_NR s->gfx_state->resstate->static_palette_entries
+#endif
+
+static gfx_pixmap_color_t white = {GFX_COLOR_INDEX_UNMAPPED, 255, 255, 255};
+
+gfx_pixmap_color_t *
+get_pic_color(state_t *s, int color)
+{
+ if (s->resmgr->sci_version < SCI_VERSION_01_VGA)
+ return &(s->ega_colors[color].visual);
+
+ if (color == 255)
+ return &white;
+ else if (color < KERNEL_COLORS_NR)
+ return &(KERNEL_COLOR_PALETTE[color]); else
+ {
+ SCIkwarn(SCIkERROR, "Color index %d out of bounds for pic %d (%d max)",
+ color, s->gfx_state->pic_nr, KERNEL_COLORS_NR);
+ BREAKPOINT();
+ return NULL; /* Well, rather, not return. But gcc gets scared here. */
+ }
+}
+
+static gfx_color_t
+graph_map_color(state_t *s, int color, int priority, int control)
+{
+ gfx_color_t retval;
+
+ if (s->resmgr->sci_version < SCI_VERSION_01_VGA)
+ {
+ retval = s->ega_colors[(color >=0 && color < 16)? color : 0];
+ gfxop_set_color(s->gfx_state, &retval, (color < 0)? -1 : retval.visual.r, retval.visual.g, retval.visual.b,
+ (color == -1)? 255 : 0, priority, control);
+ } else
+ {
+ retval.visual = *(get_pic_color(s, color));
+ retval.alpha = 0;
+ retval.priority = priority;
+ retval.control = control;
+ retval.mask =
+ GFX_MASK_VISUAL |
+ ((priority >= 0)? GFX_MASK_PRIORITY : 0) |
+ ((control >= 0)? GFX_MASK_CONTROL : 0);
+ };
+
+ return retval;
+}
+
+/* --- */
+
+
+reg_t
+kSetCursor_SCI11(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ switch (argc)
+ {
+ case 1 :
+ if (UKPV(0) == 0)
+ {
+ s->save_mouse_pointer_view = s->mouse_pointer_view;
+ s->save_mouse_pointer_loop = s->mouse_pointer_loop;
+ s->save_mouse_pointer_cel = s->mouse_pointer_cel;
+ s->mouse_pointer_view = s->mouse_pointer_loop = s->mouse_pointer_cel = -1;
+ gfxop_set_pointer_cursor(s->gfx_state, GFXOP_NO_POINTER);
+ }
+ else
+ {
+ s->mouse_pointer_view = s->save_mouse_pointer_view;
+ s->mouse_pointer_loop = s->save_mouse_pointer_loop;
+ s->mouse_pointer_cel = s->save_mouse_pointer_cel;
+ }
+ case 2 :
+ {
+ point_t pt;
+ pt.x = UKPV(0);
+ pt.y = UKPV(1);
+
+ GFX_ASSERT(gfxop_set_pointer_position(s->gfx_state, pt));
+ break;
+ }
+ case 3 :
+ GFX_ASSERT(gfxop_set_pointer_view(s->gfx_state, UKPV(0), UKPV(1), UKPV(2), NULL));
+ s->mouse_pointer_view = UKPV(0);
+ s->mouse_pointer_loop = UKPV(1);
+ s->mouse_pointer_cel = UKPV(2);
+ break;
+ case 9 : {
+ point_t hotspot = gfx_point(SKPV(3), SKPV(4));
+
+// sciprintf("Setting hotspot at %d/%d\n", hotspot.x, hotspot.y);
+
+ gfxop_set_pointer_view(s->gfx_state, UKPV(0), UKPV(1), UKPV(2), &hotspot);
+ break;
+ }
+ default :
+ SCIkwarn(SCIkERROR, "kSetCursor: Unhandled case: %d arguments given!\n", argc);
+ break;
+ }
+ return s->r_acc;
+}
+
+reg_t
+kSetCursor(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ if (s->version >= SCI_VERSION(1,001,000)||
+ has_kernel_function(s, "MoveCursor"))
+ {
+ return kSetCursor_SCI11(s, funct_nr, argc, argv);
+ }
+
+ if (SKPV_OR_ALT(1,1)) {
+ s->mouse_pointer_view = SKPV(0);
+ } else
+ s->mouse_pointer_view = GFXOP_NO_POINTER;
+
+ s->mouse_pointer_loop = s->mouse_pointer_cel = 0; /* Not used with cursor-format pointers */
+
+ GFX_ASSERT(gfxop_set_pointer_cursor(s->gfx_state, s->mouse_pointer_view));
+
+ if (argc > 2) {
+ point_t newpos = gfx_point(SKPV(2) + s->port->bounds.x,
+ SKPV(3) + s->port->bounds.y);
+
+ GFX_ASSERT(gfxop_set_pointer_position(s->gfx_state, newpos));
+ }
+
+ return s->r_acc;
+
+}
+
+extern int oldx, oldy;
+
+reg_t
+kMoveCursor(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ point_t newpos;
+ static point_t oldpos = {0};
+
+ newpos = s->gfx_state->pointer_pos;
+
+ if (argc == 1)
+ {
+ /* Case ignored on IBM PC */
+ } else
+ {
+ newpos.x = SKPV(0)+s->port->zone.x;
+ newpos.y = SKPV(1)+s->port->zone.y;
+
+ if (newpos.x > s->port->zone.x+s->port->zone.xl)
+ newpos.x = s->port->zone.x+s->port->zone.xl;
+ if (newpos.y > s->port->zone.y+s->port->zone.yl)
+ newpos.y = s->port->zone.y+s->port->zone.yl;
+
+ if (newpos.x < 0) newpos.x=0;
+ if (newpos.y < 0) newpos.y=0;
+
+ oldpos = newpos;
+ }
+
+ GFX_ASSERT(gfxop_set_pointer_position(s->gfx_state, newpos));
+
+ return s->r_acc;
+}
+
+static inline void
+_ascertain_port_contents(gfxw_port_t *port)
+{
+ if (!port->contents)
+ port->contents = (gfxw_widget_t *) gfxw_new_list(port->bounds, 0);
+}
+
+reg_t
+kShow(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int old_map = s->pic_visible_map;
+
+ s->pic_visible_map = (gfx_map_mask_t) UKPV_OR_ALT(0, 1);
+
+ switch (s->pic_visible_map) {
+
+ case GFX_MASK_VISUAL:
+ case GFX_MASK_PRIORITY:
+ case GFX_MASK_CONTROL:
+ gfxop_set_visible_map(s->gfx_state, s->pic_visible_map);
+ if (old_map != s->pic_visible_map) {
+
+ if (s->pic_visible_map == GFX_MASK_VISUAL) /* Full widget redraw */
+ s->visual->draw(GFXW(s->visual), gfx_point(0,0));
+
+ gfxop_update(s->gfx_state);
+ sciprintf("Switching visible map to %x\n", s->pic_visible_map);
+ }
+ break;
+
+ default:
+ SCIkwarn(SCIkWARNING, "Show(%x) selects unknown map\n", s->pic_visible_map);
+
+ }
+
+ s->pic_not_valid = 2;
+ return s->r_acc;
+}
+
+
+reg_t
+kPicNotValid(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ s->r_acc = make_reg(0, s->pic_not_valid);
+ if (argc)
+ s->pic_not_valid = (byte)UKPV(0);
+
+ return s->r_acc;
+}
+
+void
+_k_redraw_box(state_t *s, int x1, int y1, int x2, int y2)
+{
+ sciprintf("_k_redraw_box(): Unimplemented!\n");
+#if 0
+ int i;
+ view_object_t *list = s->dyn_views;
+
+ sciprintf("Reanimating views\n", s->dyn_views_nr);
+
+
+ for (i=0;i<s->dyn_views_nr;i++) {
+ *(list[i].underBitsp) = graph_save_box(s,
+ list[i].nsLeft,
+ list[i].nsTop,
+ list[i].nsRight-list[i].nsLeft,
+ list[i].nsBottom-list[i].nsTop,
+ SCI_MAP_VISUAL | SCI_MAP_PRIORITY);
+ draw_view0(s->pic, s->ports[0],
+ list[i].nsLeft, list[i].nsTop,
+ list[i].priority, list[i].loop,
+ list[i].cel, 0, list[i].view);
+ }
+
+ graph_update_box(s, x1, y1, x2-x1, y2-y1);
+
+ for (i=0;i<s->dyn_views_nr;i++) {
+ graph_restore_box(s, *(list[i].underBitsp));
+ list[i].underBits=0;
+ }
+#endif
+}
+
+void
+_k_graph_rebuild_port_with_color(state_t *s, gfx_color_t newbgcolor)
+{
+ gfxw_port_t *port = s->port;
+ gfxw_port_t *newport;
+
+ newport = sciw_new_window(s, port->zone, port->font_nr, port->color, newbgcolor,
+ s->titlebar_port->font_nr, s->ega_colors[15], s->ega_colors[8],
+ port->title_text, port->port_flags & ~WINDOW_FLAG_TRANSPARENT);
+
+ if (s->dyn_views) {
+ int found = 0;
+ gfxw_container_t *parent = s->dyn_views->parent;
+
+ while (parent && !(found |= (GFXW(parent) == GFXW(port))))
+ parent = parent->parent;
+
+ s->dyn_views = NULL;
+ }
+
+ port->parent->add(GFXWC(port->parent), GFXW(newport));
+ port->widfree(GFXW(port));
+}
+
+static int activated_icon_bar;
+static int port_origin_x;
+static int port_origin_y;
+
+reg_t
+kGraph(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ rect_t area;
+ gfxw_port_t *port = s->port;
+ int redraw_port = 0;
+
+ area = gfx_rect(SKPV(2), SKPV(1) , SKPV(4), SKPV(3));
+
+ area.xl = area.xl - area.x; /* Since the actual coordinates are absolute */
+ area.yl = area.yl - area.y;
+
+ switch(SKPV(0)) {
+
+ case K_GRAPH_GET_COLORS_NR:
+
+ return make_reg(0, (s->resmgr->sci_version < SCI_VERSION_01_VGA) ? 0x10 : 0x100);
+ break;
+
+ case K_GRAPH_DRAW_LINE: {
+
+ gfx_color_t gfxcolor = graph_map_color(s, SKPV(5) & 0xf, SKPV_OR_ALT(6, -1), SKPV_OR_ALT(7, -1));
+
+ SCIkdebug(SCIkGRAPHICS, "draw_line((%d, %d), (%d, %d), col=%d, p=%d, c=%d, mask=%d)\n",
+ SKPV(2), SKPV(1), SKPV(4), SKPV(3), SKPV(5), SKPV_OR_ALT(6, -1), SKPV_OR_ALT(7, -1),
+ gfxcolor.mask);
+
+ redraw_port = 1;
+ ADD_TO_CURRENT_BG_WIDGETS(GFXW(gfxw_new_line(gfx_point(SKPV(2), SKPV(1)),
+ gfx_point(SKPV(4), SKPV(3)),
+ gfxcolor, GFX_LINE_MODE_CORRECT, GFX_LINE_STYLE_NORMAL)));
+
+ }
+ break;
+
+ case K_GRAPH_SAVE_BOX:
+
+ area.x += s->port->zone.x + port_origin_x;
+ area.y += s->port->zone.y + port_origin_y;
+ area.xl += -port_origin_x;
+ area.yl += -port_origin_y;
+
+ return(graph_save_box(s, area));
+ break;
+
+ case K_GRAPH_RESTORE_BOX:
+
+ graph_restore_box(s, argv[1]);
+ break;
+
+ case K_GRAPH_FILL_BOX_BACKGROUND:
+
+ _k_graph_rebuild_port_with_color(s, port->bgcolor);
+ port = s->port;
+
+ redraw_port = 1;
+ break;
+
+ case K_GRAPH_FILL_BOX_FOREGROUND:
+
+ _k_graph_rebuild_port_with_color(s, port->color);
+ port = s->port;
+
+ redraw_port = 1;
+ break;
+
+ case K_GRAPH_FILL_BOX_ANY: {
+
+ gfx_color_t color = graph_map_color(s, SKPV(6), SKPV_OR_ALT(7, -1), SKPV_OR_ALT(8, -1));
+
+ color.mask = (byte)UKPV(5);
+
+ SCIkdebug(SCIkGRAPHICS, "fill_box_any((%d, %d), (%d, %d), col=%d, p=%d, c=%d, mask=%d)\n",
+ SKPV(2), SKPV(1), SKPV(4), SKPV(3), SKPV(6), SKPV_OR_ALT(7, -1), SKPV_OR_ALT(8, -1),
+ UKPV(5));
+
+ ADD_TO_CURRENT_BG_WIDGETS(gfxw_new_box(s->gfx_state, area, color, color, GFX_BOX_SHADE_FLAT));
+
+ }
+ break;
+
+ case K_GRAPH_UPDATE_BOX: {
+
+ SCIkdebug(SCIkGRAPHICS, "update_box(%d, %d, %d, %d)\n",
+ SKPV(1), SKPV(2), SKPV(3), SKPV(4));
+
+ area.x += s->port->zone.x;
+ area.y += s->port->zone.y;
+
+ gfxop_update_box(s->gfx_state, area);
+
+ }
+ break;
+
+ case K_GRAPH_REDRAW_BOX: {
+
+
+ SCIkdebug(SCIkGRAPHICS, "redraw_box(%d, %d, %d, %d)\n",
+ SKPV(1), SKPV(2), SKPV(3), SKPV(4));
+
+ area.x += s->port->zone.x;
+ area.y += s->port->zone.y;
+
+ if (s->dyn_views && s->dyn_views->parent == GFXWC(s->port))
+ s->dyn_views->draw(GFXW(s->dyn_views), gfx_point(0, 0));
+
+ gfxop_update_box(s->gfx_state, area);
+
+ }
+
+ break;
+
+ case K_GRAPH_ADJUST_PRIORITY:
+
+ SCIkdebug(SCIkGRAPHICS, "adjust_priority(%d, %d)\n", SKPV(1), SKPV(2));
+ s->priority_first = SKPV(1) - 10;
+ s->priority_last = SKPV(2) - 10;
+ break;
+
+ default:
+
+ SCIkdebug(SCIkSTUB, "Unhandled Graph() operation %04x\n", SKPV(0));
+
+ }
+
+ if (redraw_port)
+ FULL_REDRAW();
+
+ gfxop_update(s->gfx_state);
+ return s->r_acc;
+}
+
+
+reg_t
+kTextSize(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int width, height;
+ char *text = argv[1].segment ? (char *) kernel_dereference_bulk_pointer(s, argv[1], 0) : NULL;
+ reg_t *dest = kernel_dereference_reg_pointer(s, argv[0], 4);
+ int maxwidth = KP_UINT(KP_ALT(3, NULL_REG));
+ int font_nr = KP_UINT(argv[2]);
+
+ if (maxwidth < 0)
+ maxwidth = 0;
+
+ dest[0] = dest[1] = NULL_REG;
+
+ if (!text || !*text || !dest) { /* Empty text */
+ dest[2] = dest[3] = make_reg(0, 0);
+
+ SCIkdebug(SCIkSTRINGS, "GetTextSize: Empty string\n");
+ return s->r_acc;
+ }
+
+ GFX_ASSERT(gfxop_get_text_params(s->gfx_state, font_nr, text,
+ maxwidth? maxwidth : MAX_TEXT_WIDTH_MAGIC_VALUE,
+ &width, &height, 0,
+ NULL, NULL, NULL));
+ SCIkdebug(SCIkSTRINGS, "GetTextSize '%s' -> %dx%d\n", text, width, height);
+
+ dest[2] = make_reg(0, height);
+// dest[3] = make_reg(0, maxwidth? maxwidth : width);
+ dest[3] = make_reg(0, width);
+
+ return s->r_acc;
+}
+
+
+int debug_sleeptime_factor = 1;
+
+reg_t
+kWait(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ GTimeVal time;
+ int sleep_time = UKPV(0);
+
+ sci_get_current_time (&time);
+
+ s->r_acc = make_reg(0, ((time.tv_usec - s->last_wait_time.tv_usec) * 60 / 1000000) +
+ (time.tv_sec - s->last_wait_time.tv_sec) * 60);
+
+ memcpy(&(s->last_wait_time), &time, sizeof(GTimeVal));
+
+ /* Reset optimization flags: Game is playing along nicely anyway */
+ s->kernel_opt_flags &= ~(KERNEL_OPT_FLAG_GOT_EVENT
+ | KERNEL_OPT_FLAG_GOT_2NDEVENT);
+
+ sleep_time *= debug_sleeptime_factor;
+ GFX_ASSERT(gfxop_usleep(s->gfx_state, sleep_time * 1000000 / 60));
+
+ return s->r_acc;
+}
+
+
+reg_t
+kCoordPri(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int y = SKPV(0);
+
+ return make_reg(0, VIEW_PRIORITY(y));
+}
+
+
+reg_t
+kPriCoord(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int priority = SKPV(0);
+
+ return make_reg(0, PRIORITY_BAND_FIRST(priority));
+}
+
+
+
+void
+_k_dirloop(reg_t obj, word angle, state_t *s, int funct_nr,
+ int argc, reg_t *argv)
+{
+ int view = GET_SEL32V(obj, view);
+ int signal = GET_SEL32V(obj, signal);
+ int loop;
+ int maxloops;
+
+ if (signal & _K_VIEW_SIG_FLAG_DOESNT_TURN)
+ return;
+
+ angle %= 360;
+
+ if (s->version >= SCI_VERSION_FTU_2ND_ANGLES) {
+ if (angle < 45)
+ loop = 3;
+ else if (angle < 136)
+ loop = 0;
+ else if (angle < 225)
+ loop = 2;
+ else if (angle < 316)
+ loop = 1;
+ else
+ loop = 3;
+ } else {
+ if (angle >= 330 || angle <= 30)
+ loop = 3;
+ else if (angle <= 150)
+ loop = 0;
+ else if (angle <= 210)
+ loop = 2;
+ else if (angle < 330)
+ loop = 1;
+ else loop = 0xffff;
+ }
+
+ maxloops = gfxop_lookup_view_get_loops(s->gfx_state, view);
+
+ if (maxloops == GFX_ERROR) {
+ SCIkwarn(SCIkERROR, "Invalid view.%03d\n", view);
+ return;
+ } else if ((loop>1)&&(maxloops < 4))
+ return;
+
+ PUT_SEL32V(obj, loop, loop);
+}
+
+
+reg_t
+kDirLoop(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ _k_dirloop(argv[0], UKPV(1), s, funct_nr, argc, argv);
+
+ return s->r_acc;
+}
+
+#define GASEOUS_VIEW_MASK_ACTIVE (_K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_IGNORE_ACTOR)
+#define GASEOUS_VIEW_MASK_PASSIVE (_K_VIEW_SIG_FLAG_NO_UPDATE | _K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_IGNORE_ACTOR)
+
+abs_rect_t
+set_base(struct _state *s, reg_t object);
+
+inline abs_rect_t
+get_nsrect(struct _state *s, reg_t object, byte clip);
+
+static inline abs_rect_t
+nsrect_clip(state_t *s, int y, abs_rect_t retval, int priority);
+
+static int
+collides_with(state_t *s, abs_rect_t area, reg_t other_obj, int use_nsrect, int view_mask, int funct_nr, int argc,
+ reg_t *argv)
+{
+ int other_signal = GET_SEL32V(other_obj, signal);
+ int other_priority = GET_SEL32V(other_obj, priority);
+ int y = GET_SEL32SV(other_obj, y);
+ abs_rect_t other_area;
+
+ if (use_nsrect) {
+ other_area = get_nsrect(s, other_obj, 0);
+ other_area = nsrect_clip(s, y, other_area, other_priority);
+ } else {
+ other_area.x = GET_SEL32V(other_obj, brLeft);
+ other_area.xend = GET_SEL32V(other_obj, brRight);
+ other_area.y = GET_SEL32V(other_obj, brTop);
+ other_area.yend = GET_SEL32V(other_obj, brBottom);
+ }
+
+ if (other_area.xend < 0 || other_area.yend < 0 || area.xend < 0 || area.yend < 0)
+ return 0; /* Out of scope */
+
+ if (other_area.x >= 320 || other_area.y >= 190 || area.xend >= 320 || area.yend >= 190)
+ return 0; /* Out of scope */
+
+ SCIkdebug(SCIkBRESEN, "OtherSignal=%04x, z=%04x obj="PREG"\n", other_signal,
+ (other_signal & view_mask), PRINT_REG(other_obj));
+
+ if ((other_signal & (view_mask)) == 0) {
+ /* check whether the other object ignores actors */
+
+ SCIkdebug(SCIkBRESEN, " against (%d,%d) to (%d,%d)\n",
+ other_area.x, other_area.y, other_area.xend, other_area.yend);
+
+
+ if (((other_area.xend > area.x)
+ && (other_area.x < area.xend)) /* [other_x, other_xend] intersects [x, xend])? */
+ &&
+ ((other_area.yend > area.y)
+ && (other_area.y < area.yend))) /* [other_y, other_yend] intersects [y, yend]? */
+ return 1;
+ /* CR (from :Bob Heitman:) Collision rects have Mac semantics, ((0,0),(1,1)) only
+ ** covers the coordinate (0,0) */
+
+
+ }
+
+ SCIkdebug(SCIkBRESEN, " (no)\n");
+ return 0;
+}
+
+
+reg_t
+kCanBeHere(state_t *s, int funct_nr, int argc, reg_t * argv)
+{
+ reg_t obj = argv[0];
+ reg_t cliplist_ref = KP_ALT(1, NULL_REG);
+ list_t *cliplist = NULL;
+ gfxw_port_t *port = s->picture_port;
+ word signal;
+ int retval;
+
+ abs_rect_t abs_zone;
+ rect_t zone;
+ word edgehit;
+ word illegal_bits;
+
+
+ abs_zone.x = GET_SEL32SV(obj, brLeft);
+ abs_zone.xend = GET_SEL32SV(obj, brRight);
+ abs_zone.y = GET_SEL32SV(obj, brTop);
+ abs_zone.yend = GET_SEL32SV(obj, brBottom);
+
+ zone = gfx_rect(abs_zone.x + port->zone.x, abs_zone.y + port->zone.y,
+ abs_zone.xend - abs_zone.x, abs_zone.yend - abs_zone.y);
+
+ signal = GET_SEL32V(obj, signal);
+ SCIkdebug(SCIkBRESEN,"Checking collision: (%d,%d) to (%d,%d) ([%d..%d]x[%d..%d]), obj="PREG", sig=%04x, cliplist="PREG"\n",
+ GFX_PRINT_RECT(zone),
+ abs_zone.x, abs_zone.xend, abs_zone.y, abs_zone.yend,
+ PRINT_REG(obj), signal, PRINT_REG(cliplist_ref));
+
+ illegal_bits = GET_SEL32V(obj, illegalBits);
+
+ retval = !(illegal_bits
+ & (edgehit = gfxop_scan_bitmask(s->gfx_state, zone, GFX_MASK_CONTROL)));
+
+ SCIkdebug(SCIkBRESEN, "edgehit = %04x (illegalBits %04x)\n", edgehit, illegal_bits);
+ if (retval == 0) {
+ SCIkdebug(SCIkBRESEN, " -> %04x\n", retval);
+ return not_register(s, NULL_REG); /* Can'tBeHere */
+ }
+
+ retval = 0;
+
+ if ((illegal_bits & 0x8000) /* If we are vulnerable to those views at all... */
+ && s->dyn_views) { /* ...check against all stop-updated dynviews */
+ gfxw_dyn_view_t *widget = (gfxw_dyn_view_t *) s->dyn_views->contents;
+
+ SCIkdebug(SCIkBRESEN, "Checking vs dynviews:\n");
+
+ while (widget) {
+ if (widget->ID
+ && (widget->signal & _K_VIEW_SIG_FLAG_FREESCI_STOPUPD)
+ && ((widget->ID != obj.segment) || (widget->subID != obj.offset))
+ && is_object(s, make_reg(widget->ID, widget->subID)))
+ if (collides_with(s, abs_zone, make_reg(widget->ID, widget->subID), 1,
+ GASEOUS_VIEW_MASK_ACTIVE, funct_nr, argc, argv))
+ return not_register(s, NULL_REG);
+
+ widget = (gfxw_dyn_view_t *) widget->next;
+ }
+ }
+
+ if (signal & GASEOUS_VIEW_MASK_ACTIVE) {
+ retval = signal & GASEOUS_VIEW_MASK_ACTIVE; /* CanBeHere- it's either being disposed, or it ignores actors anyway */
+ SCIkdebug(SCIkBRESEN, " -> %04x\n", retval);
+ return not_register(s, make_reg(0, retval)); /* CanBeHere */
+ }
+
+ if (cliplist_ref.segment)
+ cliplist = LOOKUP_LIST(cliplist_ref);
+
+ if (cliplist) {
+ node_t *node = LOOKUP_NODE(cliplist->first);
+
+ retval = 0; /* Assume that we Can'tBeHere... */
+
+ while (node) { /* Check each object in the list against our bounding rectangle */
+ reg_t other_obj = node->value;
+ SCIkdebug(SCIkBRESEN, " comparing against "PREG"\n", PRINT_REG(other_obj));
+
+ if (!is_object(s, other_obj)) {
+ SCIkdebug(SCIkWARNING, "CanBeHere() cliplist contains non-object %04x\n", other_obj);
+ } else if (!REG_EQ(other_obj, obj)) { /* Clipping against yourself is not recommended */
+
+ if (collides_with(s, abs_zone, other_obj, 0, GASEOUS_VIEW_MASK_PASSIVE, funct_nr, argc, argv)) {
+ SCIkdebug(SCIkBRESEN, " -> %04x\n", retval);
+ return not_register(s, NULL_REG);
+ }
+
+ } /* if (other_obj != obj) */
+ node = LOOKUP_NODE(node->succ); /* move on */
+ }
+ }
+
+ if (!retval)
+ retval = 1;
+ SCIkdebug(SCIkBRESEN, " -> %04x\n", retval);
+
+ return not_register(s, make_reg(0, retval));
+} /* CanBeHere */
+
+reg_t
+kIsItSkip(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int view = SKPV(0);
+ int loop = SKPV(1);
+ int cel = SKPV(2);
+ int x = UKPV(3);
+ int y = UKPV(4);
+ gfxr_view_t *res = NULL;
+ gfx_pixmap_t *pxm = NULL;
+
+ if (!(res = gfxr_get_view(s->gfx_state->resstate, view, &loop, &cel, 0))) {
+ GFXWARN("Attempt to get cel parameters for invalid view %d\n", view);
+ return make_reg(0, -1);
+ }
+
+ pxm = res->loops[loop].cels[cel];
+ if (x > pxm->index_xl) x = pxm->index_xl-1;
+ if (y > pxm->index_yl) y = pxm->index_yl-1;
+
+ return make_reg(0,
+ pxm->index_data[y*pxm->index_xl+x] ==
+ pxm->color_key);
+}
+
+reg_t
+kCelHigh(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int view = SKPV(0);
+ int loop = SKPV(1);
+ int cel = SKPV(2);
+ int height, width;
+ point_t offset;
+
+ if (argc != 3) {
+ SCIkwarn(SCIkWARNING, "CelHigh called with %d parameters!\n", argc);
+ }
+
+ if (gfxop_get_cel_parameters(s->gfx_state, view, loop, cel, &width, &height, &offset)) {
+ SCIkwarn(SCIkERROR, "Invalid loop (%d) or cel (%d) in view.%d (0x%x), or view invalid\n", loop, cel, view, view);
+ return NULL_REG;
+ } else
+ return make_reg(0, height);
+}
+
+reg_t
+kCelWide(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int view = SKPV(0);
+ int loop = SKPV(1);
+ int cel = SKPV(2);
+ int height, width;
+ point_t offset;
+
+ if (argc != 3) {
+ SCIkwarn(SCIkWARNING, "CelHigh called with %d parameters!\n", argc);
+ }
+
+ if (gfxop_get_cel_parameters(s->gfx_state, view, loop, cel, &width, &height, &offset)) {
+ SCIkwarn(SCIkERROR, "Invalid loop (%d) or cel (%d) in view.%d (0x%x), or view invalid\n", loop, cel, view, view);
+ return NULL_REG;
+ } else
+ return make_reg(0, width);
+}
+
+reg_t
+kNumLoops(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ reg_t obj = argv[0];
+ int view = GET_SEL32V(obj, view);
+ int loops_nr = gfxop_lookup_view_get_loops(s->gfx_state, view);
+
+
+ if (loops_nr < 0) {
+ SCIkwarn(SCIkERROR, "view.%d (0x%x) not found\n", view, view);
+ return NULL_REG;
+ }
+
+ SCIkdebug(SCIkGRAPHICS, "NumLoops(view.%d) = %d\n", view, loops_nr);
+
+ return make_reg(0, loops_nr);
+}
+
+
+reg_t
+kNumCels(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ reg_t obj = argv[0];
+ int loop = GET_SEL32V(obj, loop);
+ int view = GET_SEL32V(obj, view);
+ int cel = 0xffff;
+
+
+ if (gfxop_check_cel(s->gfx_state, view, &loop, &cel)) { /* OK, this is a hack and there's a
+ ** real function to calculate cel numbers... */
+ SCIkwarn(SCIkERROR, "view.%d (0x%x) not found\n", view, view);
+ return NULL_REG;
+ }
+
+ SCIkdebug(SCIkGRAPHICS, "NumCels(view.%d, %d) = %d\n", view, loop, cel+1);
+
+ return make_reg(0, cel + 1);
+}
+
+reg_t
+kOnControl(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int arg = 0;
+ gfx_map_mask_t map;
+ int xstart, ystart;
+ int xlen = 1, ylen = 1;
+
+
+ if (argc == 2 || argc == 4)
+ map = GFX_MASK_CONTROL;
+ else {
+ arg = 1;
+ map = (gfx_map_mask_t) SKPV(0);
+ }
+
+ ystart = SKPV(arg+1);
+ xstart = SKPV(arg);
+
+ if (argc > 3) {
+ ylen = SKPV(arg+3) - ystart;
+ xlen = SKPV(arg+2) - xstart;
+ }
+
+ return make_reg(0, gfxop_scan_bitmask(s->gfx_state, gfx_rect(xstart, ystart + 10, xlen, ylen), map));
+}
+
+void
+_k_view_list_free_backgrounds(state_t *s, view_object_t *list, int list_nr);
+
+int sci01_priority_table_flags = 0;
+
+reg_t
+kDrawPic(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int pic_nr = SKPV(0);
+ int add_to_pic = 1;
+ int palette = SKPV_OR_ALT(3, 0);
+ gfx_color_t transparent = s->wm_port->bgcolor;
+
+ CHECK_THIS_KERNEL_FUNCTION;
+
+ if (s->version < SCI_VERSION_FTU_NEWER_DRAWPIC_PARAMETERS) {
+ if (!SKPV_OR_ALT(2, 0))
+ add_to_pic = 0;
+ } else
+ if (SKPV_OR_ALT(2, 1))
+ add_to_pic = 0;
+
+ gfxop_disable_dirty_frames(s->gfx_state);
+
+ if (NULL != s->old_screen) {
+ gfxop_free_pixmap(s->gfx_state, s->old_screen);
+ }
+
+ s->old_screen = gfxop_grab_pixmap(s->gfx_state, gfx_rect(0, 10, 320, 190));
+
+ SCIkdebug(SCIkGRAPHICS,"Drawing pic.%03d\n", SKPV(0));
+
+ if (!s->pics) {
+ s->pics = (drawn_pic_t*)sci_malloc(sizeof(drawn_pic_t) * (s->pics_nr = 8));
+ s->pics_drawn_nr = 0;
+ }
+
+ if (add_to_pic) {
+ if (s->pics_nr == s->pics_drawn_nr) {
+ s->pics_nr += 4;
+ s->pics = (drawn_pic_t*)sci_realloc(s->pics, sizeof(drawn_pic_t) * s->pics_nr);
+ }
+ s->pics[s->pics_drawn_nr].palette = palette;
+ s->pics[s->pics_drawn_nr++].nr = pic_nr;
+ GFX_ASSERT(gfxop_add_to_pic(s->gfx_state, pic_nr, 1, palette));
+ } else {
+ s->pics_drawn_nr = 1;
+ s->pics[0].nr = pic_nr;
+ s->pics[0].palette = palette;
+ GFX_ASSERT(gfxop_new_pic(s->gfx_state, pic_nr, 1, palette));
+ }
+
+ gfxw_widget_kill_chrono(s->visual, 0);
+ s->wm_port->widfree(GFXW(s->wm_port));
+ s->picture_port->widfree(GFXW(s->picture_port));
+ s->iconbar_port->widfree(GFXW(s->iconbar_port));
+
+ s->wm_port = gfxw_new_port(s->visual, NULL, s->gfx_state->options->pic_port_bounds, s->ega_colors[0], transparent);
+ s->picture_port = gfxw_new_port(s->visual, NULL, s->gfx_state->options->pic_port_bounds, s->ega_colors[0], transparent);
+ s->iconbar_port = gfxw_new_port(s->visual, NULL, gfx_rect(0, 0, 320, 200), s->ega_colors[0], transparent);
+ s->iconbar_port->flags |= GFXW_FLAG_NO_IMPLICIT_SWITCH;
+
+ s->visual->add(GFXWC(s->visual), GFXW(s->picture_port));
+ s->visual->add(GFXWC(s->visual), GFXW(s->wm_port));
+ s->visual->add(GFXWC(s->visual), GFXW(s->iconbar_port));
+
+ s->port = s->picture_port;
+
+ s->pic_priority_table = (int*)gfxop_get_pic_metainfo(s->gfx_state);
+
+ if (sci01_priority_table_flags & 0x2) {
+ if (s->pic_priority_table) {
+ int i;
+ fprintf(stderr,"---------------------------\nPriority table:\n");
+ for (i = 0; i < 16; i++)
+ fprintf(stderr,"\t%d:\t%d\n", i, s->pic_priority_table[i]);
+ fprintf(stderr,"---------------------------\n");
+ }
+ }
+ if (sci01_priority_table_flags & 0x1)
+ s->pic_priority_table = NULL;
+
+ if (argc > 1)
+ s->pic_animate = SKPV(1); /* The animation used during kAnimate() later on */
+
+ s->dyn_views = NULL;
+ s->drop_views = NULL;
+
+ s->priority_first = 42;
+
+ if (s->version < SCI_VERSION_FTU_PRIORITY_14_ZONES)
+ s->priority_last = 200;
+ else
+ s->priority_last = 190;
+
+ s->pic_not_valid = 1;
+ s->pic_is_new = 1;
+
+ return s->r_acc;
+
+}
+
+
+
+
+abs_rect_t
+set_base(state_t *s, reg_t object)
+{
+ int x, y, original_y, z, ystep, xsize, ysize;
+ int xbase, ybase, xend, yend;
+ int view, loop, cel;
+ int oldloop, oldcel;
+ int xmod = 0, ymod = 0;
+ abs_rect_t retval;
+
+ x = GET_SEL32SV(object, x);
+ original_y = y = GET_SEL32SV(object, y);
+
+ if (s->selector_map.z > -1)
+ z = GET_SEL32SV(object, z);
+ else
+ z = 0;
+
+ y -= z; /* Subtract z offset */
+
+ ystep = GET_SEL32SV(object, yStep);
+
+ view = GET_SEL32SV(object, view);
+ oldloop = loop = sign_extend_byte(GET_SEL32V(object, loop));
+ oldcel = cel = sign_extend_byte(GET_SEL32V(object, cel));
+
+ if (gfxop_check_cel(s->gfx_state, view, &loop, &cel)) {
+ xsize = ysize = xmod = ymod = 0;
+ } else {
+ point_t offset = gfx_point(0, 0);
+
+ if (loop != oldloop) {
+ loop = 0;
+ PUT_SEL32V(object, loop, 0);
+ SCIkdebug(SCIkGRAPHICS, "Resetting loop for "PREG"!\n", PRINT_REG(object));
+ }
+
+ if (cel != oldcel) {
+ cel = 0;
+ PUT_SEL32V(object, cel, 0);
+ }
+
+ gfxop_get_cel_parameters(s->gfx_state, view, loop, cel,
+ &xsize, &ysize, &offset);
+
+ xmod = offset.x;
+ ymod = offset.y;
+ }
+
+
+ xbase = x - xmod - (xsize >> 1);
+ xend = xbase + xsize;
+ yend = y /* - ymod */ + 1;
+ ybase = yend - ystep;
+
+ SCIkdebug(SCIkBASESETTER, "(%d,%d)+/-(%d,%d), (%d x %d) -> (%d, %d) to (%d, %d)\n",
+ x, y, xmod, ymod, xsize, ysize, xbase, ybase, xend, yend);
+
+ retval.x = xbase;
+ retval.y = ybase;
+ retval.xend = xend;
+ retval.yend = yend;
+
+ return retval;
+}
+
+
+void
+_k_base_setter(state_t *s, reg_t object)
+{
+ abs_rect_t absrect = set_base(s, object);
+
+ if (lookup_selector(s, object, s->selector_map.brLeft, NULL, NULL)
+ != SELECTOR_VARIABLE)
+ return; /* non-fatal */
+
+ if (s->version <= SCI_VERSION_LTU_BASE_OB1)
+ --absrect.y; /* Compensate for early SCI OB1 'bug' */
+
+ PUT_SEL32V(object, brLeft, absrect.x);
+ PUT_SEL32V(object, brRight, absrect.xend);
+ PUT_SEL32V(object, brTop, absrect.y);
+ PUT_SEL32V(object, brBottom, absrect.yend);
+}
+
+reg_t
+kBaseSetter(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ reg_t object = argv[0];
+
+
+ _k_base_setter(s, object);
+
+ return s->r_acc;
+} /* kBaseSetter */
+
+
+static inline abs_rect_t
+nsrect_clip(state_t *s, int y, abs_rect_t retval, int priority)
+{
+ int pri_top;
+
+ if (priority == -1)
+ priority = VIEW_PRIORITY(y);
+
+ pri_top = PRIORITY_BAND_FIRST(priority) + 1;
+ /* +1: Don't know why, but this seems to be happening */
+
+ if (retval.y < pri_top)
+ retval.y = pri_top;
+
+ if (retval.yend < retval.y)
+ retval.y = retval.yend - 1;
+
+ return retval;
+}
+
+inline abs_rect_t
+calculate_nsrect(state_t *s, int x, int y, int view, int loop, int cel)
+{
+ int xbase, ybase, xend, yend, xsize, ysize;
+ int xmod = 0, ymod = 0;
+ abs_rect_t retval = {0,0,0,0};
+
+ if (gfxop_check_cel(s->gfx_state, view, &loop, &cel)) {
+ xsize = ysize = xmod = ymod = 0;
+ } else {
+ point_t offset = gfx_point(0, 0);
+
+ gfxop_get_cel_parameters(s->gfx_state, view, loop, cel,
+ &xsize, &ysize, &offset);
+
+ xmod = offset.x;
+ ymod = offset.y;
+ }
+
+ xbase = x - xmod - (xsize >> 1);
+ xend = xbase + xsize;
+ yend = y - ymod + 1; /* +1: magic modifier */
+ ybase = yend - ysize;
+
+ retval.x = xbase;
+ retval.y = ybase;
+ retval.xend = xend;
+ retval.yend = yend;
+
+ return retval;
+}
+
+inline abs_rect_t
+get_nsrect(state_t *s, reg_t object, byte clip)
+{
+ int x, y, z;
+ int view, loop, cel;
+ abs_rect_t retval;
+
+ x = GET_SEL32SV(object, x);
+ y = GET_SEL32SV(object, y);
+
+ if (s->selector_map.z > -1)
+ z = GET_SEL32SV(object, z);
+ else
+ z = 0;
+
+ y -= z; /* Subtract z offset */
+
+ view = GET_SEL32SV(object, view);
+ loop = sign_extend_byte(GET_SEL32SV(object, loop));
+ cel = sign_extend_byte(GET_SEL32SV(object, cel));
+
+ retval = calculate_nsrect(s, x, y, view, loop, cel);
+
+ if (clip) {
+ int priority = GET_SEL32SV(object, priority);
+ return nsrect_clip(s, y, retval, priority);
+ }
+
+ return retval;
+}
+
+static void
+_k_set_now_seen(state_t *s, reg_t object)
+{
+ abs_rect_t absrect = get_nsrect(s, object, 0);
+
+ if (lookup_selector(s, object, s->selector_map.nsTop, NULL, NULL)
+ != SELECTOR_VARIABLE) { return; } /* This isn't fatal */
+
+ PUT_SEL32V(object, nsLeft, absrect.x);
+ PUT_SEL32V(object, nsRight, absrect.xend);
+ PUT_SEL32V(object, nsTop, absrect.y);
+ PUT_SEL32V(object, nsBottom, absrect.yend);
+}
+
+
+reg_t
+kSetNowSeen(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ reg_t object = argv[0];
+
+ _k_set_now_seen(s, object);
+
+ return s->r_acc;
+} /* kSetNowSeen */
+
+reg_t
+kPalette(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ switch (UKPV(0))
+ {
+ case 5 : {
+ int r = UKPV(1);
+ int g = UKPV(2);
+ int b = UKPV(3);
+
+ int i, delta, bestindex = -1, bestdelta = 200000;
+
+ for (i = 0; i < KERNEL_COLORS_NR; i++) {
+ int dr = abs (KERNEL_COLOR_PALETTE[i].r - r);
+ int dg = abs (KERNEL_COLOR_PALETTE[i].g - g);
+ int db = abs (KERNEL_COLOR_PALETTE[i].b - b);
+
+ delta = dr*dr + dg * dg + db * db;
+
+ if (delta < bestdelta)
+ {
+ bestdelta = delta;
+ bestindex = i;
+ }
+ }
+
+ /* Don't warn about inexact mappings -- it's actually the
+ ** rule rather than the exception */
+ return make_reg(0, bestindex);
+ }
+
+ case 4 :
+ case 6 :
+ break;
+ default :
+ SCIkdebug(SCIkWARNING, "Unimplemented subfunction: %d\n", UKPV(0));
+ }
+ return s->r_acc;
+}
+
+static void
+_k_draw_control(state_t *s, reg_t obj, int inverse);
+
+
+static void
+_k_disable_delete_for_now(state_t *s, reg_t obj)
+{
+ reg_t text_pos = GET_SEL32(obj, text);
+ char *text = IS_NULL_REG(text_pos)? NULL : (char *) sm_dereference(&s->seg_manager, text_pos, NULL);
+ int type = GET_SEL32V(obj, type);
+ int state = GET_SEL32V(obj, state);
+
+ if (type == K_CONTROL_BUTTON && text &&
+ !strcmp(s->game_name, "sq4") &&
+ s->version < SCI_VERSION(1,001,000) &&
+ !strcmp(text, " Delete "))
+ PUT_SEL32V(obj, state, (state | CONTROL_STATE_GRAY) & ~CONTROL_STATE_ENABLED);
+}
+
+reg_t
+kDrawControl(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ reg_t obj = argv[0];
+
+ _k_disable_delete_for_now(s, obj);
+ _k_draw_control(s, obj, 0);
+ FULL_REDRAW();
+ return NULL_REG;
+}
+
+
+reg_t
+kHiliteControl(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ reg_t obj = argv[0];
+
+
+ _k_draw_control(s, obj, 1);
+ return s->r_acc;
+}
+
+
+void
+update_cursor_limits(int *display_offset, int *cursor, int max_displayed)
+{
+ if (*cursor < *display_offset + 4) {
+ if (*cursor < 8)
+ *display_offset = 0;
+ else
+ *display_offset = *cursor - 8;
+
+ } else if (*cursor - *display_offset > max_displayed - 8)
+ *display_offset = 12 + *cursor - max_displayed;
+
+}
+
+#define _K_EDIT_DELETE \
+ if (cursor < textlen) { \
+ memmove(text + cursor, text + cursor + 1, textlen - cursor +1); \
+}
+
+#define _K_EDIT_BACKSPACE \
+ if (cursor) { \
+ --cursor; \
+ memmove(text + cursor, text + cursor + 1, textlen - cursor +1); \
+ --textlen; \
+}
+
+
+
+reg_t
+kEditControl(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ reg_t obj = argv[0];
+ reg_t event = argv[1];
+
+
+ if (obj.segment) {
+ word ct_type = GET_SEL32V(obj, type);
+ switch (ct_type) {
+
+ case 0: break; /* NOP */
+
+ case K_CONTROL_EDIT:
+ if (event.segment && ((GET_SEL32V(event, type)) == SCI_EVT_KEYBOARD)) {
+ int max_displayed = GET_SEL32V(obj, max);
+ int max = max_displayed;
+ int cursor = GET_SEL32V(obj, cursor);
+ int modifiers = GET_SEL32V(event, modifiers);
+ int key = GET_SEL32V(event, message);
+ reg_t text_pos = GET_SEL32(obj, text);
+ int display_offset = 0;
+
+ char *text = (char *) sm_dereference(&s->seg_manager, text_pos, NULL);
+ int textlen;
+
+ if (!text) {
+ SCIkdebug(SCIkWARNING, "Could not draw control: "PREG" does not reference text!\n",
+ PRINT_REG(text_pos));
+ return s->r_acc;
+ }
+
+ if (REG_EQ(text_pos, s->save_dir_copy)) {
+ max = MAX_SAVE_DIR_SIZE - 1;
+ display_offset = s->save_dir_edit_offset;
+ }
+ textlen = strlen(text);
+
+ cursor += display_offset;
+
+ if (cursor > textlen)
+ cursor = textlen;
+
+ if (modifiers & SCI_EVM_CTRL) {
+
+ switch (tolower((char)key)) {
+ case 'a': cursor = 0; break;
+ case 'e': cursor = textlen; break;
+ case 'f': if (cursor < textlen) ++cursor; break;
+ case 'b': if (cursor > 0) --cursor; break;
+ case 'k': text[cursor] = 0; break; /* Terminate string */
+ case 'h': _K_EDIT_BACKSPACE; break;
+ case 'd': _K_EDIT_DELETE; break;
+ }
+ PUT_SEL32V(event, claimed, 1);
+
+ } else if (modifiers & SCI_EVM_ALT) { /* Ctrl has precedence over Alt */
+
+ switch (key) {
+ case 0x2100 /* A-f */: while ((cursor < textlen) && (text[cursor++] != ' ')); break;
+ case 0x3000 /* A-b */: while ((cursor > 0) && (text[--cursor - 1] != ' ')); break;
+ case 0x2000 /* A-d */: {
+
+ while ((cursor < textlen) && (text[cursor] == ' ')) {
+ _K_EDIT_DELETE;
+ textlen--;
+ }
+
+ while ((cursor < textlen) && (text[cursor] != ' ')) {
+ _K_EDIT_DELETE;
+ textlen--;
+ }
+ break;
+ }
+ }
+ PUT_SEL32V(event, claimed, 1);
+
+ } else if (key < 31) {
+
+ PUT_SEL32V(event, claimed, 1);
+
+ switch(key) {
+ case SCI_K_BACKSPACE: _K_EDIT_BACKSPACE; break;
+ default:
+ PUT_SEL32V(event, claimed, 0);
+ }
+
+ } else if (key & 0xff00) {
+
+ switch(key) {
+ case SCI_K_HOME: cursor = 0; break;
+ case SCI_K_END: cursor = textlen; break;
+ case SCI_K_RIGHT: if (cursor + 1 <= textlen) ++cursor; break;
+ case SCI_K_LEFT: if (cursor > 0) --cursor; break;
+ case SCI_K_DELETE: _K_EDIT_DELETE; break;
+ }
+
+ PUT_SEL32V(event, claimed, 1);
+ } else if ((key > 31) && (key < 128)) {
+ int inserting = (modifiers & SCI_EVM_INSERT);
+
+ modifiers &= ~(SCI_EVM_RSHIFT | SCI_EVM_LSHIFT | SCI_EVM_CAPSLOCK);
+
+ if (cursor == textlen) {
+ if (textlen < max) {
+ text[cursor++] = key;
+ text[cursor] = 0; /* Terminate string */
+ }
+ } else if (inserting) {
+ if (textlen < max) {
+ int i;
+
+ for (i = textlen + 2; i >= cursor; i--)
+ text[i] = text[i - 1];
+ text[cursor++] = key;
+
+ }
+ } else { /* Overwriting */
+ text[cursor++] = key;
+ }
+
+ if (max_displayed < max)
+ update_cursor_limits(&display_offset, &cursor, max_displayed);
+
+ if (REG_EQ(text_pos, s->save_dir_copy))
+ s->save_dir_edit_offset = display_offset;
+
+ cursor -= display_offset;
+
+ PUT_SEL32V(event, claimed, 1);
+ }
+
+ PUT_SEL32V(obj, cursor, cursor); /* Write back cursor position */
+ }
+
+ case K_CONTROL_ICON:
+ case K_CONTROL_BOX:
+ case K_CONTROL_BUTTON:
+ if (event.segment) PUT_SEL32V(event, claimed, 1);
+ _k_draw_control(s, obj, 0);
+ return NULL_REG;
+ break;
+
+ case K_CONTROL_TEXT: {
+ int state = GET_SEL32V(obj, state);
+ PUT_SEL32V(obj, state, state | CONTROL_STATE_DITHER_FRAMED);
+ _k_draw_control(s, obj, 0);
+ PUT_SEL32V(obj, state, state);
+ }
+ break;
+
+ default:
+ SCIkwarn(SCIkWARNING, "Attempt to edit control type %d\n", ct_type);
+ }
+ }
+
+ return s->r_acc;
+}
+
+
+static void
+_k_draw_control(state_t *s, reg_t obj, int inverse)
+{
+ int x = GET_SEL32SV(obj, nsLeft);
+ int y = GET_SEL32SV(obj, nsTop);
+ int xl = GET_SEL32SV(obj, nsRight) - x;
+ int yl = GET_SEL32SV(obj, nsBottom) - y;
+ rect_t area = gfx_rect(x, y, xl, yl);
+
+ int font_nr = GET_SEL32V(obj, font);
+ reg_t text_pos = GET_SEL32(obj, text);
+ char *text = IS_NULL_REG(text_pos)? NULL : (char *) sm_dereference(&s->seg_manager, text_pos, NULL);
+ int view = GET_SEL32V(obj, view);
+ int cel = sign_extend_byte(GET_SEL32V(obj, cel));
+ int loop = sign_extend_byte(GET_SEL32V(obj, loop));
+ gfx_alignment_t mode;
+
+ int type = GET_SEL32V(obj, type);
+ int state = GET_SEL32V(obj, state);
+ int cursor;
+ int max;
+
+ if (REG_EQ(text_pos, s->save_dir_copy)) {
+ SCIkdebug(SCIkGRAPHICS, "Displaying the save_dir copy\n");
+ }
+
+ switch (type) {
+
+ case K_CONTROL_BUTTON:
+
+ SCIkdebug(SCIkGRAPHICS, "drawing button "PREG" to %d,%d\n", PRINT_REG(obj), x, y);
+ ADD_TO_CURRENT_BG_WIDGETS(sciw_new_button_control(s->port, obj, area, text, font_nr,
+ (gint8)(state & CONTROL_STATE_FRAMED),
+ (gint8)inverse, (gint8)(state & CONTROL_STATE_GRAY)));
+ break;
+
+ case K_CONTROL_TEXT:
+ mode = (gfx_alignment_t) GET_SEL32V(obj, mode);
+
+ SCIkdebug(SCIkGRAPHICS, "drawing text "PREG" to %d,%d, mode=%d\n", PRINT_REG(obj), x, y, mode);
+
+ ADD_TO_CURRENT_BG_WIDGETS(
+ sciw_new_text_control(s->port, obj, area, text, font_nr, mode,
+ (gint8)(!!(state & CONTROL_STATE_DITHER_FRAMED)),
+ (gint8)inverse));
+ break;
+
+ case K_CONTROL_EDIT:
+ SCIkdebug(SCIkGRAPHICS, "drawing edit control "PREG" to %d,%d\n", PRINT_REG(obj), x, y);
+
+ max = GET_SEL32V(obj, max);
+ cursor = GET_SEL32V(obj, cursor);
+
+ if (cursor > (signed)strlen(text))
+ cursor = strlen(text);
+
+ if (REG_EQ(text_pos, s->save_dir_copy))
+ update_cursor_limits(&s->save_dir_edit_offset, &cursor, max);
+
+ update_cursor_limits(&s->save_dir_edit_offset, &cursor, max);
+ ADD_TO_CURRENT_BG_WIDGETS(sciw_new_edit_control(s->port, obj, area, text, font_nr, (unsigned)cursor, (gint8)inverse));
+ break;
+
+ case K_CONTROL_ICON:
+
+ SCIkdebug(SCIkGRAPHICS, "drawing icon control "PREG" to %d,%d\n", PRINT_REG(obj), x, y -1);
+
+ ADD_TO_CURRENT_BG_WIDGETS(sciw_new_icon_control(s->port, obj, area, view, loop, cel,
+ (gint8)(state & CONTROL_STATE_FRAMED), (gint8)inverse));
+ break;
+
+ case K_CONTROL_CONTROL:
+ case K_CONTROL_CONTROL_ALIAS: {
+ char **entries_list = NULL;
+ char *seeker;
+ int entries_nr;
+ int lsTop = GET_SEL32V(obj, lsTop)-text_pos.offset;
+ int list_top = 0;
+ int selection = 0;
+ int entry_size = GET_SEL32V(obj, x);
+ int i;
+
+ SCIkdebug(SCIkGRAPHICS, "drawing list control %04x to %d,%d, diff %d\n", obj, x, y,
+ SCI_MAX_SAVENAME_LENGTH);
+ cursor = GET_SEL32V(obj, cursor) - text_pos.offset;
+
+ entries_nr = 0;
+ seeker = text;
+ while (seeker[0]) { /* Count string entries in NULL terminated string list */
+ ++entries_nr;
+ seeker += entry_size;
+ }
+
+ if (entries_nr) { /* determine list_top, selection, and the entries_list */
+ seeker = text;
+ entries_list = (char**)sci_malloc(sizeof(char *) * entries_nr);
+ for (i = 0; i < entries_nr; i++) {
+ entries_list[i] = seeker;
+ seeker += entry_size ;
+ if ((seeker - text) == lsTop)
+ list_top = i + 1;
+ if ((seeker - text) == cursor)
+ selection = i + 1;
+ }
+ }
+
+ ADD_TO_CURRENT_BG_WIDGETS(sciw_new_list_control(s->port, obj, area, font_nr, entries_list, entries_nr,
+ list_top, selection, (gint8)inverse));
+ if (entries_nr)
+ free(entries_list);
+ }
+ break;
+
+ case K_CONTROL_BOX:
+ break;
+
+ default:
+ SCIkwarn(SCIkWARNING, "Unknown control type: %d at "PREG", at (%d, %d) size %d x %d\n",
+ type, PRINT_REG(obj), x, y, xl, yl);
+ }
+
+ if (!s->pic_not_valid) {
+ FULL_REDRAW();
+ }
+}
+
+
+static void
+draw_rect_to_control_map(state_t *s, abs_rect_t abs_zone)
+{
+ gfxw_box_t *box;
+ gfx_color_t color;
+
+ gfxop_set_color(s->gfx_state, &color, -1, -1, -1, -1, -1, 0xf);
+
+ SCIkdebug(SCIkGRAPHICS," adding control block (%d,%d)to(%d,%d)\n",
+ abs_zone.x, abs_zone.y, abs_zone.xend, abs_zone.yend);
+
+ box = gfxw_new_box(s->gfx_state,
+ gfx_rect(abs_zone.x, abs_zone.y,
+ abs_zone.xend - abs_zone.x,
+ abs_zone.yend - abs_zone.y),
+ color, color, GFX_BOX_SHADE_FLAT);
+
+ assert_primary_widget_lists(s);
+
+ ADD_TO_CURRENT_PICTURE_PORT(box);
+}
+
+static inline void
+draw_obj_to_control_map(state_t *s, gfxw_dyn_view_t *view)
+{
+ reg_t obj = make_reg(view->ID, view->subID);
+
+ if (!is_object(s, obj))
+ SCIkwarn(SCIkWARNING, "View %d does not contain valid object reference "PREG"\n", view->ID, PRINT_REG(obj));
+
+ if (!(view->signalp && (((reg_t *)view->signalp)->offset & _K_VIEW_SIG_FLAG_IGNORE_ACTOR))) {
+ abs_rect_t abs_zone = get_nsrect(s, make_reg(view->ID, view->subID), 1);
+ draw_rect_to_control_map (s, abs_zone);
+ }
+}
+
+
+static void
+_k_view_list_do_postdraw(state_t *s, gfxw_list_t *list)
+{
+ gfxw_dyn_view_t *widget = (gfxw_dyn_view_t *) list->contents;
+
+ while (widget) {
+ reg_t obj = make_reg(widget->ID, widget->subID);
+
+ if (widget->type == GFXW_SORTED_LIST)
+ _k_view_list_do_postdraw(s, GFXWC(widget));
+
+ if (widget->type != GFXW_DYN_VIEW) {
+ widget = (gfxw_dyn_view_t *) widget->next;
+ continue;
+ }
+
+ /*
+ * this fixes a few problems, but doesn't match SSCI's logic.
+ * The semantics of the private flag need to be verified before this can be uncommented.
+ * Fixes bug #326 (CB1, ego falls down stairs)
+ * if ((widget->signal & (_K_VIEW_SIG_FLAG_FREESCI_PRIVATE | _K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_NO_UPDATE)) == _K_VIEW_SIG_FLAG_FREESCI_PRIVATE) {
+ */
+ if ((widget->signal & (_K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_NO_UPDATE)) == 0) {
+ int has_nsrect = lookup_selector(s, obj, s->selector_map.nsBottom, NULL, NULL) == SELECTOR_VARIABLE;
+
+ if (has_nsrect) {
+ int temp;
+
+ temp = GET_SEL32V(obj, nsLeft);
+ PUT_SEL32V(obj, lsLeft, temp);
+
+ temp = GET_SEL32V(obj, nsRight);
+ PUT_SEL32V(obj, lsRight, temp);
+
+ temp = GET_SEL32V(obj, nsTop);
+ PUT_SEL32V(obj, lsTop, temp);
+
+ temp = GET_SEL32V(obj, nsBottom);
+ PUT_SEL32V(obj, lsBottom, temp);
+#ifdef DEBUG_LSRECT
+ fprintf(stderr, "lsRected "PREG"\n", PRINT_REG(obj));
+#endif
+ }
+#ifdef DEBUG_LSRECT
+ else fprintf(stderr, "Not lsRecting "PREG" because %d\n", PRINT_REG(obj),
+ lookup_selector(s, obj, s->selector_map.nsBottom, NULL, NULL));
+#endif
+
+ if (widget->signal & _K_VIEW_SIG_FLAG_HIDDEN)
+ widget->signal |= _K_VIEW_SIG_FLAG_REMOVE;
+ }
+#ifdef DEBUG_LSRECT
+ fprintf(stderr, "obj "PREG" has pflags %x\n", PRINT_REG(obj), (widget->signal & (_K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_NO_UPDATE)));
+#endif
+
+ if (widget->signalp) {
+ *((reg_t *)(widget->signalp)) = make_reg(0, widget->signal & 0xffff); /* Write back signal */
+ }
+
+ widget = (gfxw_dyn_view_t *) widget->next;
+ }
+}
+
+void
+_k_view_list_mark_free(state_t *s, reg_t off)
+{
+ if (s->dyn_views) {
+
+ gfxw_dyn_view_t *w = (gfxw_dyn_view_t *) s->dyn_views->contents;
+
+ while (w) {
+ if (w->ID == off.segment
+ && w->subID == off.offset) {
+ w->under_bitsp = NULL;
+ }
+
+ w = (gfxw_dyn_view_t *) w->next;
+ }
+ }
+}
+
+static int _k_animate_ran = 0;
+
+int
+_k_view_list_dispose_loop(state_t *s, list_t *list, gfxw_dyn_view_t *widget,
+ int funct_nr, int argc, reg_t *argv)
+ /* disposes all list members flagged for disposal; funct_nr is the invoking kfunction */
+ /* returns non-zero IFF views were dropped */
+{
+ int signal;
+ int dropped = 0;
+
+ _k_animate_ran = 0;
+
+ if (widget) {
+ int retval;
+ /* Recurse: */
+ retval = _k_view_list_dispose_loop(s, list, (gfxw_dyn_view_t *) widget->next, funct_nr, argc, argv);
+
+ if (retval == -1) /* Bail out on annihilation, rely on re-start from Animate() */
+ return -1;
+
+ if (GFXW_IS_DYN_VIEW(widget) && (widget->ID != GFXW_NO_ID)) {
+ signal = ((reg_t *)widget->signalp)->offset;
+ if (signal & _K_VIEW_SIG_FLAG_DISPOSE_ME) {
+ reg_t obj = make_reg(widget->ID, widget->subID);
+ reg_t under_bits = NULL_REG;
+
+ if (!is_object(s, obj)) {
+ SCIkwarn(SCIkERROR, "Non-object "PREG" present"
+ " in view list during delete time\n",
+ PRINT_REG(obj));
+ obj = NULL_REG;
+ } else
+
+ if (widget->under_bitsp) { /* Is there a bg picture left to clean? */
+
+ reg_t mem_handle = *((reg_t*)(widget->under_bitsp));
+
+ if (mem_handle.segment) {
+ if (!kfree(s, mem_handle)) {
+ *((reg_t*)(widget->under_bitsp)) = make_reg(0, widget->under_bits = 0);
+ } else {
+ SCIkwarn(SCIkWARNING,
+ "Treating viewobj "PREG
+ " as no longer"
+ " present\n", PRINT_REG(obj));
+ obj = NULL_REG;
+ }
+ }
+ }
+
+ if (is_object(s, obj)) {
+ if (invoke_selector(INV_SEL(obj, delete, 1), 0))
+ SCIkwarn(SCIkWARNING, "Object at "PREG" requested deletion, but does not have"
+ " a delete funcselector\n", PRINT_REG(obj));
+ if (_k_animate_ran) {
+ SCIkwarn(SCIkWARNING, "Object at "PREG" invoked kAnimate() during deletion!\n",
+ PRINT_REG(obj));
+ return dropped;
+ }
+
+ if (widget->under_bitsp)
+ under_bits = *((reg_t*)(widget->under_bitsp));
+
+ if (under_bits.segment) {
+ *((reg_t*)(widget->under_bitsp)) = make_reg(0, 0);
+ graph_restore_box(s, under_bits);
+ }
+
+ SCIkdebug(SCIkGRAPHICS, "Freeing "PREG" with signal=%04x\n",
+ PRINT_REG(obj), signal);
+
+ if (!(signal & _K_VIEW_SIG_FLAG_HIDDEN)) {
+ SCIkdebug(SCIkGRAPHICS, "Adding view at "PREG" to background\n",
+ PRINT_REG(obj));
+ if (!(gfxw_remove_id(widget->parent, widget->ID, widget->subID) == GFXW(widget))) {
+ SCIkwarn(SCIkERROR, "Attempt to remove view with ID %x:%x from list failed!\n",
+ widget->ID, widget->subID);
+ BREAKPOINT();
+ }
+
+ s->drop_views->add(GFXWC(s->drop_views), GFXW(gfxw_picviewize_dynview(widget)));
+
+ draw_obj_to_control_map(s, widget);
+ widget->draw_bounds.y += s->dyn_views->bounds.y - widget->parent->bounds.y;
+ widget->draw_bounds.x += s->dyn_views->bounds.x - widget->parent->bounds.x;
+ dropped = 1;
+ }
+ else {
+ SCIkdebug(SCIkGRAPHICS, "Deleting view at "PREG"\n", PRINT_REG(obj));
+ widget->flags |= GFXW_FLAG_VISIBLE;
+ gfxw_annihilate(GFXW(widget));
+ return -1; /* restart: Done in Animate() */
+ }
+ }
+ }
+ }
+
+ }
+
+ return dropped;
+}
+
+
+#define _K_MAKE_VIEW_LIST_CYCLE 1
+#define _K_MAKE_VIEW_LIST_CALC_PRIORITY 2
+#define _K_MAKE_VIEW_LIST_DRAW_TO_CONTROL_MAP 4
+
+static gfxw_dyn_view_t *
+_k_make_dynview_obj(state_t *s, reg_t obj, int options, int nr, int funct_nr, int argc, reg_t *argv)
+{
+ short oldloop, oldcel;
+ int cel, loop, view_nr = GET_SEL32SV(obj, view);
+ int palette;
+ int signal;
+ reg_t under_bits;
+ reg_t *under_bitsp, *signalp;
+ point_t pos;
+ int z;
+ gfxw_dyn_view_t *widget;
+
+ SCIkdebug(SCIkGRAPHICS, " - Adding "PREG"\n", PRINT_REG(obj));
+
+ obj = obj;
+
+ pos.x = GET_SEL32SV(obj, x);
+ pos.y = GET_SEL32SV(obj, y);
+
+ pos.y++; /* magic: Sierra appears to do something like this */
+
+ z = GET_SEL32SV(obj, z);
+
+ /* !-- nsRect used to be checked here! */
+ loop = oldloop = sign_extend_byte(GET_SEL32V(obj, loop));
+ cel = oldcel = sign_extend_byte(GET_SEL32V(obj, cel));
+
+ if (s->selector_map.palette)
+ palette = GET_SEL32V(obj, palette); else
+ palette = 0;
+
+ /* Clip loop and cel, write back if neccessary */
+ if (gfxop_check_cel(s->gfx_state, view_nr, &loop, &cel)) {
+ return NULL;
+ }
+
+ if (loop != oldloop)
+ loop = 0;
+ if (cel != oldcel)
+ cel = 0;
+
+ if (oldloop != loop)
+ PUT_SEL32V(obj, loop, loop);
+
+ if (oldcel != cel) {
+ PUT_SEL32V(obj, cel, cel);
+ }
+
+ if (lookup_selector(s, obj, s->selector_map.underBits, &(under_bitsp), NULL)
+ != SELECTOR_VARIABLE) {
+ under_bitsp = NULL;
+ under_bits = NULL_REG;
+ SCIkdebug(SCIkGRAPHICS, "Object at "PREG" has no underBits\n", PRINT_REG(obj));
+ } else
+ under_bits = *((reg_t *)under_bitsp);
+
+ if (lookup_selector(s, obj, s->selector_map.signal, &(signalp), NULL)
+ != SELECTOR_VARIABLE) {
+ signalp = NULL;
+ signal = 0;
+ SCIkdebug(SCIkGRAPHICS, "Object at "PREG" has no signal selector\n", PRINT_REG(obj));
+ } else {
+ signal = signalp->offset;
+ SCIkdebug(SCIkGRAPHICS, " with signal = %04x\n", signal);
+ }
+
+ widget = gfxw_new_dyn_view(s->gfx_state, pos, z, view_nr, loop, cel, palette,
+ -1, -1, ALIGN_CENTER, ALIGN_BOTTOM, nr);
+
+ if (widget) {
+
+ widget = (gfxw_dyn_view_t *) gfxw_set_id(GFXW(widget), obj.segment, obj.offset);
+ widget = gfxw_dyn_view_set_params(widget, under_bits.segment,
+ under_bitsp, signal, signalp);
+ widget->flags |= GFXW_FLAG_IMMUNE_TO_SNAPSHOTS; /* Only works the first time 'round */
+
+ return widget;
+ } else {
+ SCIkwarn(SCIkWARNING, "Could not generate dynview widget for %d/%d/%d\n", view_nr, loop, cel);
+ return NULL;
+ }
+}
+
+
+static void
+_k_make_view_list(state_t *s, gfxw_list_t **widget_list, list_t *list, int options,
+ int funct_nr, int argc, reg_t *argv)
+ /* Creates a view_list from a node list in heap space. Returns the list, stores the
+ ** number of list entries in *list_nr. Calls doit for each entry if cycle is set.
+ ** argc, argv, funct_nr should be the same as in the calling kernel function.
+ */
+{
+ node_t *node;
+ int sequence_nr = 0;
+ gfxw_dyn_view_t *widget;
+
+ if (!*widget_list) {
+ SCIkwarn(SCIkERROR, "make_view_list with widget_list == ()\n");
+ BREAKPOINT();
+ };
+
+ assert_primary_widget_lists(s);
+ /* In case one of the views' doit() does a DrawPic... */
+ /* Yes, this _does_ happen! */
+
+ if (!list) { /* list sanity check */
+ SCIkwarn(SCIkERROR, "Attempt to make list from non-list!\n");
+ BREAKPOINT();
+ }
+
+ node = LOOKUP_NODE(list->first);
+ while (node) {
+ reg_t obj = node->value; /* The object we're using */
+ reg_t next_node;
+ gfxw_dyn_view_t *widget;
+
+ if (options & _K_MAKE_VIEW_LIST_CYCLE) {
+ unsigned int signal = GET_SEL32V(obj, signal);
+
+ if (!(signal & _K_VIEW_SIG_FLAG_FROZEN)) {
+
+ SCIkdebug(SCIkGRAPHICS, " invoking "PREG"::doit()\n", PRINT_REG(obj));
+ invoke_selector(INV_SEL(obj, doit, 1), 0); /* Call obj::doit() if neccessary */
+ }
+ }
+
+ next_node = node->succ; /* In case the cast list was changed */
+
+ if (list->first.segment == 0 &&
+ list->first.offset == 0) /* The cast list was completely emptied! */
+ break;
+
+ widget = _k_make_dynview_obj(s, obj, options, sequence_nr--,
+ funct_nr, argc, argv);
+ if (widget)
+ GFX_ASSERT((*widget_list)->add(GFXWC(*widget_list), GFXW(widget)));
+
+ node = LOOKUP_NODE(next_node); /* Next node */
+ }
+
+
+ widget = (gfxw_dyn_view_t *) (*widget_list)->contents;
+
+ while(widget) { /* Read back widget values */
+ if (widget->signalp)
+ widget->signal = ((reg_t *)(widget->signalp))->offset;
+
+ widget = (gfxw_dyn_view_t *) widget->next;
+ }
+}
+
+
+static void
+_k_prepare_view_list(state_t *s, gfxw_list_t *list, int options)
+{
+ gfxw_dyn_view_t *view = (gfxw_dyn_view_t *) list->contents;
+ while (view) {
+ reg_t obj = make_reg(view->ID, view->subID);
+ int priority, _priority;
+ int has_nsrect = (view->ID <=0)? 0 : lookup_selector(s, obj, s->selector_map.nsBottom, NULL, NULL) == SELECTOR_VARIABLE;
+ int oldsignal = view->signal;
+
+ _k_set_now_seen(s, obj);
+ _priority = /*GET_SELECTOR(obj, y); */((view->pos.y));/**/
+ _priority = _find_view_priority(s, _priority - 1);
+
+ if (options & _K_MAKE_VIEW_LIST_DRAW_TO_CONTROL_MAP) { /* Picview */
+ priority = GET_SEL32SV(obj, priority);
+ if (priority < 0)
+ priority = _priority; /* Always for picviews */
+ } else { /* Dynview */
+ if (has_nsrect
+ && !(view->signal & _K_VIEW_SIG_FLAG_FIX_PRI_ON)) { /* Calculate priority */
+
+ if (options & _K_MAKE_VIEW_LIST_CALC_PRIORITY)
+ PUT_SEL32V(obj, priority, _priority);
+
+ priority = _priority;
+
+ } else /* DON'T calculate the priority */
+ priority = GET_SEL32SV(obj, priority);
+ }
+
+
+ view->color.priority = priority;
+
+ if (priority > -1)
+ view->color.mask |= GFX_MASK_PRIORITY;
+ else
+ view->color.mask &= ~GFX_MASK_PRIORITY;
+
+ /* CR (from :Bob Heitman:) stopupdated views (like pic views) have
+ ** their clipped nsRect drawn to the control map */
+ if (view->signal & _K_VIEW_SIG_FLAG_STOP_UPDATE) {
+ view->signal |= _K_VIEW_SIG_FLAG_FREESCI_STOPUPD;
+ SCIkdebug(SCIkGRAPHICS, "Setting magic STOP_UPD for "PREG"\n", PRINT_REG(obj));
+ }
+
+ if ((options & _K_MAKE_VIEW_LIST_DRAW_TO_CONTROL_MAP))
+ draw_obj_to_control_map(s, view);
+
+
+ /* Extreme Pattern Matching ugliness ahead... */
+ if (view->signal & _K_VIEW_SIG_FLAG_NO_UPDATE) {
+ if (((view->signal & (_K_VIEW_SIG_FLAG_UPDATED | _K_VIEW_SIG_FLAG_FORCE_UPDATE))) /* 9.1.1.1 */
+ || ((view->signal & (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_REMOVE)) == _K_VIEW_SIG_FLAG_HIDDEN)
+ || ((view->signal & (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_REMOVE)) == _K_VIEW_SIG_FLAG_REMOVE) /* 9.1.1.2 */
+ || ((view->signal & (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_ALWAYS_UPDATE)) == _K_VIEW_SIG_FLAG_ALWAYS_UPDATE) /* 9.1.1.3 */
+ || ((view->signal & (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_ALWAYS_UPDATE)) == (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_ALWAYS_UPDATE))) /* 9.1.1.4 */
+ {
+ s->pic_not_valid++;
+ view->signal &= ~_K_VIEW_SIG_FLAG_STOP_UPDATE;
+ }
+
+ else if (((view->signal & (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_ALWAYS_UPDATE)) == 0)
+ || ((view->signal & (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_ALWAYS_UPDATE)) == (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_REMOVE))
+ || ((view->signal & (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_ALWAYS_UPDATE)) == (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_ALWAYS_UPDATE))
+ || ((view->signal & (_K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_ALWAYS_UPDATE)) == _K_VIEW_SIG_FLAG_HIDDEN))
+ {
+ view->signal &= ~_K_VIEW_SIG_FLAG_STOP_UPDATE;
+ }
+ }
+ else {
+ if (view->signal & _K_VIEW_SIG_FLAG_STOP_UPDATE) {
+ s->pic_not_valid++;
+ view->signal &= ~_K_VIEW_SIG_FLAG_FORCE_UPDATE;
+ }
+ else { /* if not STOP_UPDATE */
+ if (view->signal & _K_VIEW_SIG_FLAG_ALWAYS_UPDATE)
+ s->pic_not_valid++;
+ view->signal &= ~_K_VIEW_SIG_FLAG_FORCE_UPDATE;
+ }
+ }
+
+ SCIkdebug(SCIkGRAPHICS, " dv["PREG"]: signal %04x -> %04x\n", PRINT_REG(obj), oldsignal, view->signal);
+
+ /* Never happens
+ if (view->signal & 0) {
+ view->signal &= ~_K_VIEW_SIG_FLAG_FREESCI_STOPUPD;
+ fprintf(stderr, "Unsetting magic StopUpd for view "PREG"\n", PRINT_REG(obj));
+ } */
+
+ view = (gfxw_dyn_view_t *) view->next;
+ }
+}
+
+static void
+_k_update_signals_in_view_list(gfxw_list_t *old_list, gfxw_list_t *new_list)
+{ /* O(n^2)... a bit painful, but much faster than the redraws it helps prevent */
+ gfxw_dyn_view_t *old_widget = (gfxw_dyn_view_t *) old_list->contents;
+
+ /* Traverses all old widgets, updates them with signals from the new widgets.
+ ** This is done to avoid evil hacks in widget.c; widgets with unique IDs are
+ ** replaced there iff they are NOT equal_to a new widget with the same ID.
+ ** If they were replaced every time, we'd be doing far too many redraws.
+ */
+
+ while (old_widget) {
+ gfxw_dyn_view_t *new_widget = (gfxw_dyn_view_t *) new_list->contents;
+
+ while (new_widget
+ && (new_widget->ID != old_widget->ID
+ || new_widget->subID != old_widget->subID))
+ new_widget = (gfxw_dyn_view_t *) new_widget->next;
+
+ if (new_widget) {
+ int carry = old_widget->signal & _K_VIEW_SIG_FLAG_FREESCI_STOPUPD;
+ /* Transfer 'stopupd' flag */
+
+ if ((new_widget->pos.x != old_widget->pos.x)
+ || (new_widget->pos.y != old_widget->pos.y)
+ /* ** No idea why this is supposed to be bad **
+ || (new_widget->z != old_widget->z)
+ || (new_widget->view != old_widget->view)
+ || (new_widget->loop != old_widget->loop)
+ || (new_widget->cel != old_widget->cel)
+ */
+ )
+ carry = 0;
+
+ old_widget->signal = new_widget->signal |= carry;
+ }
+
+ old_widget = (gfxw_dyn_view_t *) old_widget->next;
+ }
+}
+
+static void
+_k_view_list_kryptonize(gfxw_widget_t *v)
+{
+ if (v) {
+ v->flags &= ~GFXW_FLAG_IMMUNE_TO_SNAPSHOTS;
+ _k_view_list_kryptonize(v->next);
+ }
+}
+
+static void
+_k_raise_topmost_in_view_list(state_t *s, gfxw_list_t *list, gfxw_dyn_view_t *view)
+{
+ if (view) {
+ gfxw_dyn_view_t *next = (gfxw_dyn_view_t *) view->next;
+
+ /* step 11 */
+ if ((view->signal & (_K_VIEW_SIG_FLAG_NO_UPDATE | _K_VIEW_SIG_FLAG_HIDDEN | _K_VIEW_SIG_FLAG_ALWAYS_UPDATE)) == 0) {
+ SCIkdebug(SCIkGRAPHICS, "Forcing precedence 2 at ["PREG"] with %04x\n", PRINT_REG(make_reg(view->ID, view->subID)), view->signal);
+ view->force_precedence = 2;
+
+ if ((view->signal & (_K_VIEW_SIG_FLAG_REMOVE | _K_VIEW_SIG_FLAG_HIDDEN)) == _K_VIEW_SIG_FLAG_REMOVE) {
+ view->signal &= ~_K_VIEW_SIG_FLAG_REMOVE;
+ }
+ }
+
+ gfxw_remove_widget_from_container(view->parent, GFXW(view));
+
+ gfxw_widget_reparent_chrono(s->visual, GFXW(view), GFXWC(list));
+
+ if (view->signal & _K_VIEW_SIG_FLAG_HIDDEN)
+ gfxw_hide_widget(GFXW(view));
+ else
+ gfxw_show_widget(GFXW(view));
+
+ list->add(GFXWC(list), GFXW(view));
+
+ _k_raise_topmost_in_view_list(s, list, next);
+ }
+}
+
+
+static void
+_k_redraw_view_list(state_t *s, gfxw_list_t *list)
+{
+ gfxw_dyn_view_t *view = (gfxw_dyn_view_t *) list->contents;
+ while (view) {
+
+ SCIkdebug(SCIkGRAPHICS, " dv["PREG"]: signal %04x\n", make_reg(view->ID, view->subID), view->signal);
+
+ /* step 1 of subalgorithm */
+ if (view->signal & _K_VIEW_SIG_FLAG_NO_UPDATE) {
+ if (view->signal & _K_VIEW_SIG_FLAG_FORCE_UPDATE)
+ view->signal &= ~_K_VIEW_SIG_FLAG_FORCE_UPDATE;
+
+ if (view->signal & _K_VIEW_SIG_FLAG_UPDATED)
+ view->signal &= ~(_K_VIEW_SIG_FLAG_UPDATED | _K_VIEW_SIG_FLAG_NO_UPDATE);
+ } else { /* NO_UPD is not set */
+ if (view->signal & _K_VIEW_SIG_FLAG_STOP_UPDATE) {
+ view->signal &= ~_K_VIEW_SIG_FLAG_STOP_UPDATE;
+ view->signal |= _K_VIEW_SIG_FLAG_NO_UPDATE;
+ }
+ }
+
+ SCIkdebug(SCIkGRAPHICS, " at substep 6: signal %04x\n", view->signal);
+
+ if (view->signal & _K_VIEW_SIG_FLAG_ALWAYS_UPDATE)
+ view->signal &= ~(_K_VIEW_SIG_FLAG_STOP_UPDATE | _K_VIEW_SIG_FLAG_UPDATED
+ | _K_VIEW_SIG_FLAG_NO_UPDATE | _K_VIEW_SIG_FLAG_FORCE_UPDATE);
+
+ SCIkdebug(SCIkGRAPHICS, " at substep 11/14: signal %04x\n", view->signal);
+
+ if (view->signal & _K_VIEW_SIG_FLAG_NO_UPDATE) {
+ if (view->signal & _K_VIEW_SIG_FLAG_HIDDEN)
+ view->signal |= _K_VIEW_SIG_FLAG_REMOVE;
+ else
+ view->signal &= ~_K_VIEW_SIG_FLAG_REMOVE;
+ } else if (!(view->signal & _K_VIEW_SIG_FLAG_HIDDEN))
+ view->force_precedence = 1;
+
+ SCIkdebug(SCIkGRAPHICS, " -> signal %04x\n", view->signal);
+
+ view = (gfxw_dyn_view_t *) view->next;
+ }
+}
+
+
+/* Flags for _k_draw_view_list */
+/* Whether some magic with the base object's "signal" selector should be done: */
+#define _K_DRAW_VIEW_LIST_USE_SIGNAL 1
+/* This flag draws all views with the "DISPOSE_ME" flag set: */
+#define _K_DRAW_VIEW_LIST_DISPOSEABLE 2
+/* Use this one to draw all views with "DISPOSE_ME" NOT set: */
+#define _K_DRAW_VIEW_LIST_NONDISPOSEABLE 4
+/* Draw as picviews */
+#define _K_DRAW_VIEW_LIST_PICVIEW 8
+
+
+void
+_k_draw_view_list(state_t *s, gfxw_list_t *list, int flags)
+ /* Draws list_nr members of list to s->pic. */
+{
+ gfxw_dyn_view_t *widget = (gfxw_dyn_view_t *) list->contents;
+
+ if (GFXWC(s->port) != GFXWC(s->dyn_views->parent))
+ return; /* Return if the pictures are meant for a different port */
+
+ while (widget) {
+
+ if (flags & _K_DRAW_VIEW_LIST_PICVIEW)
+ widget = gfxw_picviewize_dynview(widget);
+
+ if (GFXW_IS_DYN_VIEW(widget) && widget->ID) {
+ word signal = (flags & _K_DRAW_VIEW_LIST_USE_SIGNAL)? ((reg_t *)(widget->signalp))->offset : 0;
+
+ if (signal & _K_VIEW_SIG_FLAG_HIDDEN)
+ gfxw_hide_widget(GFXW(widget));
+ else
+ gfxw_show_widget(GFXW(widget));
+
+ if (!(flags & _K_DRAW_VIEW_LIST_USE_SIGNAL)
+ || ((flags & _K_DRAW_VIEW_LIST_DISPOSEABLE) && (signal & _K_VIEW_SIG_FLAG_DISPOSE_ME))
+ || ((flags & _K_DRAW_VIEW_LIST_NONDISPOSEABLE) && !(signal & _K_VIEW_SIG_FLAG_DISPOSE_ME))) {
+
+ if (flags & _K_DRAW_VIEW_LIST_USE_SIGNAL) {
+ signal &= ~(_K_VIEW_SIG_FLAG_STOP_UPDATE | _K_VIEW_SIG_FLAG_UPDATED |
+ _K_VIEW_SIG_FLAG_NO_UPDATE | _K_VIEW_SIG_FLAG_FORCE_UPDATE);
+ /* Clear all of those flags */
+
+ if (signal & _K_VIEW_SIG_FLAG_HIDDEN)
+ gfxw_hide_widget(GFXW(widget));
+ else
+ gfxw_show_widget(GFXW(widget));
+
+ *((reg_t *)(widget->signalp)) = make_reg(0, signal); /* Write the changes back */
+ };
+
+ } /* ...if we're drawing disposeables and this one is disposeable, or if we're drawing non-
+ ** disposeables and this one isn't disposeable */
+ }
+
+ widget = (gfxw_dyn_view_t *) widget->next;
+ } /* while (widget) */
+
+}
+
+reg_t
+kAddToPic(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ gfxw_list_t *pic_views;
+ reg_t list_ref = argv[0];
+
+ assert_primary_widget_lists(s);
+
+ if (argc > 1) {
+ int view, cel, loop, x, y, priority, control;
+ gfxw_widget_t *widget;
+
+ view = KP_UINT(argv[0]);
+ loop = KP_UINT(argv[1]);
+ cel = KP_UINT(argv[2]);
+ x = KP_SINT(argv[3]);
+ y = KP_SINT(argv[4]) + 1 /* magic + 1 */;
+ priority = KP_SINT(argv[5]);
+ control = KP_SINT(argv[6]);
+
+ widget = GFXW(gfxw_new_dyn_view(s->gfx_state, gfx_point(x, y), 0, view, loop, cel, 0,
+ priority, -1 /* No priority */ , ALIGN_CENTER, ALIGN_BOTTOM, 0));
+
+ if (!widget) {
+ SCIkwarn(SCIkERROR, "Attempt to single-add invalid picview (%d/%d/%d)\n", view, loop, cel);
+ } else {
+ widget->ID = -1;
+ if (control >= 0) {
+ abs_rect_t abs_zone = nsrect_clip(s, y,
+ calculate_nsrect(s, x, y,
+ view, loop, cel),
+ priority);
+
+ draw_rect_to_control_map(s, abs_zone);
+ }
+ ADD_TO_CURRENT_PICTURE_PORT(gfxw_picviewize_dynview((gfxw_dyn_view_t *) widget));
+ }
+
+ } else {
+ list_t *list;
+
+ if (!list_ref.segment) {
+ SCIkdebug(SCIkWARNING, "Attempt to AddToPic single non-list: "PREG"\n",
+ PRINT_REG(list_ref));
+ return s->r_acc;
+ }
+
+
+ list = LOOKUP_LIST(list_ref);
+
+ pic_views = gfxw_new_list(s->picture_port->bounds, 1);
+
+ SCIkdebug(SCIkGRAPHICS, "Preparing picview list...\n");
+ _k_make_view_list(s, &pic_views, list, 0, funct_nr, argc, argv);
+ _k_prepare_view_list(s, pic_views, _K_MAKE_VIEW_LIST_DRAW_TO_CONTROL_MAP);
+ /* Store pic views for later re-use */
+
+ SCIkdebug(SCIkGRAPHICS, "Drawing picview list...\n");
+ ADD_TO_CURRENT_PICTURE_PORT(pic_views);
+ _k_draw_view_list(s, pic_views, _K_DRAW_VIEW_LIST_NONDISPOSEABLE | _K_DRAW_VIEW_LIST_DISPOSEABLE | _K_DRAW_VIEW_LIST_PICVIEW);
+ /* Draw relative to the bottom center */
+ SCIkdebug(SCIkGRAPHICS, "Returning.\n");
+ }
+ reparentize_primary_widget_lists(s, s->port);
+
+ return s->r_acc;
+}
+
+
+reg_t
+kGetPort(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ return make_reg(0, s->port->ID);
+}
+
+
+reg_t
+kSetPort(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ if (activated_icon_bar && argc == 6)
+ {
+ port_origin_x = port_origin_y = 0;
+ activated_icon_bar = 0;
+ return s->r_acc;
+ }
+
+ switch (argc) {
+ case 1 : {
+ unsigned int port_nr = SKPV(0);
+ gfxw_port_t *new_port;
+
+ /* We depart from official semantics here, sorry!
+ Reasoning: Sierra SCI does not clip ports while we do.
+ Therefore a draw to the titlebar port (which is the
+ official semantics) would cut off the lower part of the
+ icons in an SCI1 icon bar. Instead we have an
+ iconbar_port that does not exist in SSCI. */
+ if (port_nr == -1) port_nr = s->iconbar_port->ID;
+
+ new_port = gfxw_find_port(s->visual, port_nr);
+
+ if (!new_port) {
+ SCIkwarn(SCIkERROR, "Invalid port %04x requested\n", port_nr);
+ return NULL_REG;
+ }
+
+ s->port->draw(GFXW(s->port), gfxw_point_zero); /* Update the port we're leaving */
+ s->port = new_port;
+ return s->r_acc;
+ }
+ case 6 : {
+ port_origin_y = SKPV(0);
+ port_origin_x = SKPV(1);
+
+ if (SKPV(0) == -10)
+ {
+ s->port->draw(GFXW(s->port), gfxw_point_zero); /* Update the port we're leaving */
+ s->port = s->iconbar_port;
+ activated_icon_bar = 1;
+ return s->r_acc;
+ }
+
+ s->gfx_state->options->pic_port_bounds = gfx_rect(UKPV(5), UKPV(4),
+ UKPV(3), UKPV(2));
+ /* FIXME: Should really only invalidate all loaded pic resources here;
+ this is overkill */
+ gfxr_free_all_resources(s->gfx_state->driver, s->gfx_state->resstate);
+
+ break;
+ }
+ default :
+ SCIkwarn(SCIkERROR, "SetPort was called with %d parameters\n", argc);
+ break;
+ }
+
+ return NULL_REG;
+}
+
+static inline void
+add_to_chrono(state_t *s, gfxw_widget_t *widget)
+{
+ gfxw_port_t *chrono_port;
+ gfxw_list_t *tw;
+
+ chrono_port = gfxw_get_chrono_port(s->visual, &tw, 0);
+ tw->add(GFXWC(tw), widget);
+
+ if (!chrono_port->parent)
+ ADD_TO_CURRENT_PORT(chrono_port);
+}
+
+reg_t
+kDrawCel(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int view = SKPV(0);
+ int loop = SKPV(1);
+ int cel = SKPV(2);
+ int x = SKPV(3);
+ int y = SKPV(4);
+ int priority = SKPV_OR_ALT(5, -1);
+ gfxw_view_t *new_view;
+
+/*
+ if (!view) {
+ SCIkwarn(SCIkERROR, "Attempt to draw non-existing view.%03d\n", view);
+ return;
+ }
+*/
+
+ if (gfxop_check_cel(s->gfx_state, view, &loop, &cel)) {
+ SCIkwarn(SCIkERROR, "Attempt to draw non-existing view.%03d\n", view);
+ return s->r_acc;
+ }
+
+ SCIkdebug(SCIkGRAPHICS, "DrawCel((%d,%d), (view.%d, %d, %d), p=%d)\n", x, y, view, loop,
+ cel, priority);
+
+ new_view = gfxw_new_view(s->gfx_state, gfx_point(x, y), view, loop, cel, 0, priority, -1,
+ ALIGN_LEFT, ALIGN_TOP, GFXW_VIEW_FLAG_DONT_MODIFY_OFFSET);
+
+#if 0
+ add_to_chrono(s, GFXW(new_view));
+#else
+ ADD_TO_CURRENT_PICTURE_PORT(GFXW(new_view));
+#endif
+ FULL_REDRAW();
+
+
+
+ return s->r_acc;
+}
+
+reg_t
+kDisposeWindow(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ unsigned int goner_nr = SKPV(0);
+ gfxw_port_t *goner;
+ gfxw_port_t *pred;
+ int id = s->visual->port_refs_nr;
+
+ gfxw_widget_kill_chrono(s->visual, goner_nr);
+ goner = gfxw_find_port(s->visual, goner_nr);
+ if ((goner_nr < 3) || (goner == NULL)) {
+ SCIkwarn(SCIkERROR, "Removal of invalid window %04x requested\n", goner_nr);
+ return s->r_acc;
+ }
+
+ if (s->dyn_views && GFXWC(s->dyn_views->parent) == GFXWC(goner)) {
+ reparentize_primary_widget_lists(s, (gfxw_port_t *) goner->parent);
+ }
+
+ if (s->drop_views && GFXWC(s->drop_views->parent) == GFXWC(goner))
+ s->drop_views = NULL; /* Kill it */
+
+ pred = gfxw_remove_port(s->visual, goner);
+
+ if (goner == s->port) /* Did we kill the active port? */
+ s->port = pred;
+
+/* Find the last port that exists and that isn't marked no-switch */
+ while ((!s->visual->port_refs[id] && id >= 0) ||
+ (s->visual->port_refs[id]->flags & GFXW_FLAG_NO_IMPLICIT_SWITCH))
+ id--;
+
+ sciprintf("Activating port %d after disposing window %d\n", id, goner_nr);
+ s->port = s->visual->port_refs[id];
+
+ if (!s->port)
+ s->port = gfxw_find_default_port(s->visual);
+
+ gfxop_update(s->gfx_state);
+ return s->r_acc;
+}
+
+reg_t
+kNewWindow(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ gfxw_port_t *window;
+ int x, y, xl, yl, flags;
+ gfx_color_t bgcolor;
+ gfx_color_t fgcolor;
+ gfx_color_t black;
+ gfx_color_t white;
+ int priority;
+ int argextra = argc == 13 ? 4 : 0; /* Triggers in PQ3 */
+
+ y = SKPV(0);
+ x = SKPV(1);
+ yl = SKPV(2) - y;
+ xl = SKPV(3) - x;
+
+ y += s->wm_port->bounds.y;
+
+ if (x+xl > 319)
+ x -= ((x+xl) - 319);
+
+ flags = SKPV(5+argextra);
+
+ priority = SKPV_OR_ALT(6+argextra, -1);
+ bgcolor.mask = 0;
+
+ if (SKPV_OR_ALT(8+argextra, 255) >= 0) {
+ if (s->resmgr->sci_version < SCI_VERSION_01_VGA)
+ bgcolor.visual = *(get_pic_color(s, SKPV_OR_ALT(8+argextra, 15)));
+ else
+ bgcolor.visual = *(get_pic_color(s, SKPV_OR_ALT(8+argextra, 255)));
+ bgcolor.mask = GFX_MASK_VISUAL;
+ }
+
+ bgcolor.priority = priority;
+ bgcolor.mask |= priority >= 0 ? GFX_MASK_PRIORITY : 0;
+ bgcolor.alpha = 0;
+ SCIkdebug(SCIkGRAPHICS, "New window with params %d, %d, %d, %d\n", SKPV(0), SKPV(1), SKPV(2), SKPV(3));
+
+ fgcolor.visual = *(get_pic_color(s, SKPV_OR_ALT(7+argextra, 0)));
+ fgcolor.mask = GFX_MASK_VISUAL;
+ fgcolor.alpha = 0;
+ black.visual = *(get_pic_color(s, 0));
+ black.mask = GFX_MASK_VISUAL;
+ black.alpha = 0;
+ white.visual = *(get_pic_color(s, s->resmgr->sci_version < SCI_VERSION_01_VGA ? 15 : 255)),
+ white.mask = GFX_MASK_VISUAL;
+ white.alpha = 0;
+
+ window = sciw_new_window(s, gfx_rect(x, y, xl, yl), s->titlebar_port->font_nr,
+ fgcolor, bgcolor, s->titlebar_port->font_nr,
+ white,
+ black,
+ argv[4+argextra].segment ? kernel_dereference_char_pointer(s, argv[4+argextra], 0) : NULL,
+ flags);
+
+ /* PQ3 has the interpreter store underBits implicitly.
+ The feature was promptly removed after its release, never to be seen again. */
+ if (argextra)
+ gfxw_port_auto_restore_background(s->visual, window,
+ gfx_rect(SKPV(5), SKPV(4),
+ SKPV(7)-SKPV(5), SKPV(6)-SKPV(4)));
+
+ ADD_TO_WINDOW_PORT(window);
+ FULL_REDRAW();
+
+ window->draw(GFXW(window), gfxw_point_zero);
+ gfxop_update(s->gfx_state);
+
+ s->port = window; /* Set active port */
+
+ return make_reg(0, window->ID);
+}
+
+
+#define K_ANIMATE_CENTER_OPEN_H 0 /* horizontally open from center */
+#define K_ANIMATE_CENTER_OPEN_V 1 /* vertically open from center */
+#define K_ANIMATE_RIGHT_OPEN 2 /* open from right */
+#define K_ANIMATE_LEFT_OPEN 3 /* open from left */
+#define K_ANIMATE_BOTTOM_OPEN 4 /* open from bottom */
+#define K_ANIMATE_TOP_OPEN 5 /* open from top */
+#define K_ANIMATE_BORDER_OPEN_F 6 /* open from edges to center */
+#define K_ANIMATE_CENTER_OPEN_F 7 /* open from center to edges */
+#define K_ANIMATE_OPEN_CHECKERS 8 /* open random checkboard */
+#define K_ANIMATE_BORDER_CLOSE_H_CENTER_OPEN_H 9 /* horizontally close to center,reopen from center */
+#define K_ANIMATE_BORDER_CLOSE_V_CENTER_OPEN_V 10 /* vertically close to center, reopen from center */
+#define K_ANIMATE_LEFT_CLOSE_RIGHT_OPEN 11 /* close to right, reopen from right */
+#define K_ANIMATE_RIGHT_CLOSE_LEFT_OPEN 12 /* close to left, reopen from left */
+#define K_ANIMATE_TOP_CLOSE_BOTTOM_OPEN 13 /* close to bottom, reopen from bottom */
+#define K_ANIMATE_BOTTOM_CLOSE_TOP_OPEN 14 /* close to top, reopen from top */
+#define K_ANIMATE_CENTER_CLOSE_F_BORDER_OPEN_F 15 /* close from center to edges,
+ ** reopen from edges to center */
+#define K_ANIMATE_BORDER_CLOSE_F_CENTER_OPEN_F 16 /* close from edges to center, reopen from
+ ** center to edges */
+#define K_ANIMATE_CLOSE_CHECKERS_OPEN_CHECKERS 17 /* close random checkboard, reopen */
+#define K_ANIMATE_SCROLL_LEFT 0x28
+#define K_ANIMATE_SCROLL_RIGHT 0x29
+#define K_ANIMATE_SCROLL_DOWN 0x2a
+#define K_ANIMATE_SCROLL_UP 0x2b
+
+#define K_ANIMATE_OPEN_SIMPLE 100 /* No animation */
+
+
+#define GRAPH_BLANK_BOX(s, x, y, xl, yl, color) GFX_ASSERT(gfxop_fill_box(s->gfx_state, \
+ gfx_rect(x, (((y) < 10)? 10 : (y)), xl, (((y) < 10)? ((y) - 10) : 0) + (yl)), s->ega_colors[color]));
+
+#define GRAPH_UPDATE_BOX(s, x, y, xl, yl) GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, newscreen, \
+ gfx_rect(x, (((y) < 10)? 10 : (y)) - 10, xl, (((y) < 10)? ((y) - 10) : 0) + (yl)), gfx_point(x, ((y) < 10)? 10 : (y) )));
+
+
+static void
+animate_do_animation(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int i, remaining_checkers;
+ int update_counter;
+ int granularity0 = s->animation_granularity << 1;
+ int granularity1 = s->animation_granularity;
+ int granularity2 = s->animation_granularity >> 2;
+ int granularity3 = s->animation_granularity >> 4;
+ char checkers[32 * 19];
+ gfx_pixmap_t *newscreen = gfxop_grab_pixmap(s->gfx_state, gfx_rect(0, 10, 320, 190));
+
+ if (!granularity2)
+ granularity2 = 1;
+ if (!granularity3)
+ granularity3 = 1;
+
+ gfxop_set_clip_zone(s->gfx_state, gfx_rect_fullscreen);
+
+ if (!newscreen) {
+ SCIkwarn(SCIkERROR, "Failed to allocate 'newscreen'!\n");
+ return;
+ }
+
+ GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, s->old_screen, gfx_rect(0, 0, 320, 190), gfx_point(0, 10)));
+ gfxop_update_box(s->gfx_state, gfx_rect(0, 0, 320, 200));
+
+ /*SCIkdebug(SCIkGRAPHICS, "Animating pic opening type %x\n", s->pic_animate);*/
+
+ gfxop_enable_dirty_frames(s->gfx_state);
+
+ if (s->animation_delay < 1)
+ s->pic_animate = K_ANIMATE_OPEN_SIMPLE;
+
+
+ switch(s->pic_animate) {
+ case K_ANIMATE_BORDER_CLOSE_H_CENTER_OPEN_H :
+
+ for (i = 0; i < 159 + granularity1; i += granularity1) {
+ GRAPH_BLANK_BOX(s, i, 10, granularity1, 190, 0);
+ gfxop_update(s->gfx_state);
+ GRAPH_BLANK_BOX(s, 319-i, 10, granularity1, 190, 0);
+ gfxop_update(s->gfx_state);
+ gfxop_usleep(s->gfx_state, s->animation_delay);
+ process_sound_events(s);
+ }
+ GRAPH_BLANK_BOX(s, 0, 10, 320, 190, 0);
+
+ case K_ANIMATE_CENTER_OPEN_H :
+
+ for (i = 159; i >= 1-granularity1; i -= granularity1) {
+ GRAPH_UPDATE_BOX(s, i, 10, granularity1, 190);
+ gfxop_update(s->gfx_state);
+ GRAPH_UPDATE_BOX(s, 319-i, 10, granularity1, 190);
+ gfxop_update(s->gfx_state);
+ gfxop_usleep(s->gfx_state, s->animation_delay);
+ process_sound_events(s);
+ }
+ break;
+
+
+ case K_ANIMATE_BORDER_CLOSE_V_CENTER_OPEN_V :
+
+ for (i = 0; i < 94 + granularity2; i += granularity2) {
+ GRAPH_BLANK_BOX(s, 0, i + 10, 320, granularity2, 0);
+ gfxop_update(s->gfx_state);
+ GRAPH_BLANK_BOX(s, 0, 199 - i, 320, granularity2, 0);
+ gfxop_update(s->gfx_state);
+ gfxop_usleep(s->gfx_state, 2 * s->animation_delay);
+ process_sound_events(s);
+ }
+ GRAPH_BLANK_BOX(s, 0, 10, 320, 190, 0);
+
+ case K_ANIMATE_CENTER_OPEN_V :
+
+ for (i = 94; i >= 1 - granularity2; i -= granularity2) {
+ GRAPH_UPDATE_BOX(s, 0, i + 10, 320, granularity2);
+ gfxop_update(s->gfx_state);
+ GRAPH_UPDATE_BOX(s, 0, 199 - i, 320, granularity2);
+ gfxop_update(s->gfx_state);
+ gfxop_usleep(s->gfx_state, 2 * s->animation_delay);
+ process_sound_events(s);
+ }
+ break;
+
+
+ case K_ANIMATE_LEFT_CLOSE_RIGHT_OPEN :
+
+ for(i = 0; i < 319 + granularity0; i += granularity0) {
+ GRAPH_BLANK_BOX(s, i, 10, granularity0, 190, 0);
+ gfxop_update(s->gfx_state);
+ gfxop_usleep(s->gfx_state, s->animation_delay / 2);
+ process_sound_events(s);
+ }
+ GRAPH_BLANK_BOX(s, 0, 10, 320, 190, 0);
+
+ case K_ANIMATE_RIGHT_OPEN :
+ for(i = 319; i >= 1 - granularity0; i -= granularity0) {
+ GRAPH_UPDATE_BOX(s, i, 10, granularity0, 190);
+ gfxop_update(s->gfx_state);
+ gfxop_usleep(s->gfx_state, s->animation_delay / 2);
+ process_sound_events(s);
+ }
+ break;
+
+
+ case K_ANIMATE_RIGHT_CLOSE_LEFT_OPEN :
+
+ for(i = 319; i >= 1-granularity0; i -= granularity0) {
+ GRAPH_BLANK_BOX(s, i, 10, granularity0, 190, 0);
+ gfxop_update(s->gfx_state);
+ gfxop_usleep(s->gfx_state, s->animation_delay / 2);
+ process_sound_events(s);
+ }
+ GRAPH_BLANK_BOX(s, 0, 10, 320, 190, 0);
+
+ case K_ANIMATE_LEFT_OPEN :
+
+ for(i = 0; i < 319 + granularity0; i+= granularity0) {
+ GRAPH_UPDATE_BOX(s, i, 10, granularity0, 190);
+ gfxop_update(s->gfx_state);
+ gfxop_usleep(s->gfx_state, s->animation_delay / 2);
+ process_sound_events(s);
+ }
+ break;
+
+
+ case K_ANIMATE_TOP_CLOSE_BOTTOM_OPEN :
+
+ for (i = 10; i < 199 + granularity1; i += granularity1) {
+ GRAPH_BLANK_BOX(s, 0, i, 320, granularity1, 0);
+ gfxop_update(s->gfx_state);
+ gfxop_usleep(s->gfx_state, s->animation_delay);
+ process_sound_events(s);
+ }
+ GRAPH_BLANK_BOX(s, 0, 10, 320, 190, 0);
+
+ case K_ANIMATE_BOTTOM_OPEN :
+
+ for (i = 199; i >= 11 - granularity1; i-= granularity1) {
+ GRAPH_UPDATE_BOX(s, 0, i, 320, granularity1);
+ gfxop_update(s->gfx_state);
+ gfxop_usleep(s->gfx_state, s->animation_delay);
+ process_sound_events(s);
+ }
+ break;
+
+
+ case K_ANIMATE_BOTTOM_CLOSE_TOP_OPEN :
+
+ for (i = 199; i >= 11 - granularity1; i-= granularity1) {
+ GRAPH_BLANK_BOX(s, 0, i, 320, granularity1, 0);
+ gfxop_update(s->gfx_state);
+ gfxop_usleep(s->gfx_state, s->animation_delay);
+ process_sound_events(s);
+ }
+ GRAPH_BLANK_BOX(s, 0, 10, 320, 190, 0);
+
+ case K_ANIMATE_TOP_OPEN :
+
+ for (i = 10; i < 199 + granularity1; i+= granularity1) {
+ GRAPH_UPDATE_BOX(s, 0, i, 320, granularity1);
+ gfxop_update(s->gfx_state);
+ gfxop_usleep(s->gfx_state, s->animation_delay);
+ process_sound_events(s);
+ }
+ break;
+
+
+ case K_ANIMATE_CENTER_CLOSE_F_BORDER_OPEN_F :
+
+ for (i = 31; i >= 1-granularity3; i -= granularity3) {
+ int real_i = (i < 0)? 0 : i;
+ int height_l = 3 * (granularity3 - real_i + i);
+ int width_l = 5 * (granularity3 - real_i + i);
+ int height = real_i * 3;
+ int width = real_i * 5;
+
+ GRAPH_BLANK_BOX(s, width, 10 + height,
+ width_l, 190 - 2*height, 0);
+ gfxop_update(s->gfx_state);
+ GRAPH_BLANK_BOX(s, 320 - width_l - width,
+ 10 + height, width_l, 190 - 2*height, 0);
+ gfxop_update(s->gfx_state);
+
+ GRAPH_BLANK_BOX(s, width, 10 + height,
+ 320 - 2*width, height_l, 0);
+ gfxop_update(s->gfx_state);
+ GRAPH_BLANK_BOX(s, width, 200 - height_l - height,
+ 320 - 2*width, height_l, 0);
+ gfxop_update(s->gfx_state);
+
+ gfxop_usleep(s->gfx_state, 4 * s->animation_delay);
+ process_sound_events(s);
+ }
+
+
+ case K_ANIMATE_BORDER_OPEN_F :
+
+ for (i = 0; i < 31+granularity3; i += granularity3) {
+ int real_i = (i < 0)? 0 : i;
+ int height_l = 3 * (granularity3 - real_i + i);
+ int width_l = 5 * (granularity3 - real_i + i);
+ int height = real_i * 3;
+ int width = real_i * 5;
+
+ GRAPH_UPDATE_BOX(s, width, 10 + height,
+ width_l, 190 - 2*height);
+ gfxop_update(s->gfx_state);
+ GRAPH_UPDATE_BOX(s, 320 - width_l - width,
+ 10 + height, width_l, 190 - 2*height);
+ gfxop_update(s->gfx_state);
+
+ GRAPH_UPDATE_BOX(s, width, 10 + height,
+ 320 - 2*width, height_l);
+ gfxop_update(s->gfx_state);
+ GRAPH_UPDATE_BOX(s, width, 200 - height_l - height,
+ 320 - 2*width, height_l);
+ gfxop_update(s->gfx_state);
+
+ gfxop_usleep(s->gfx_state, 4 * s->animation_delay);
+ process_sound_events(s);
+ }
+
+ break;
+
+
+ case K_ANIMATE_BORDER_CLOSE_F_CENTER_OPEN_F :
+
+ for (i = 0; i < 31+granularity3; i += granularity3) {
+ int real_i = (i < 0)? 0 : i;
+ int height_l = 3 * (granularity3 - real_i + i);
+ int width_l = 5 * (granularity3 - real_i + i);
+ int height = real_i * 3;
+ int width = real_i * 5;
+
+ GRAPH_BLANK_BOX(s, width, 10 + height,
+ width_l, 190 - 2*height, 0);
+ gfxop_update(s->gfx_state);
+ GRAPH_BLANK_BOX(s, 320 - width_l - width,
+ 10 + height, width_l, 190 - 2*height, 0);
+ gfxop_update(s->gfx_state);
+
+ GRAPH_BLANK_BOX(s, width, 10 + height,
+ 320 - 2*width, height_l, 0);
+ gfxop_update(s->gfx_state);
+ GRAPH_BLANK_BOX(s, width, 200 - height_l - height,
+ 320 - 2*width, height_l, 0);
+ gfxop_update(s->gfx_state);
+
+ gfxop_usleep(s->gfx_state, 7 * s->animation_delay);
+ process_sound_events(s);
+ }
+
+
+ case K_ANIMATE_CENTER_OPEN_F :
+
+ for (i = 31; i >= 1-granularity3; i -= granularity3) {
+ int real_i = (i < 0)? 0 : i;
+ int height_l = 3 * (granularity3 - real_i + i);
+ int width_l = 5 * (granularity3 - real_i + i);
+ int height = real_i * 3;
+ int width = real_i * 5;
+
+ GRAPH_UPDATE_BOX(s, width, 10 + height,
+ width_l, 190 - 2*height);
+ gfxop_update(s->gfx_state);
+ GRAPH_UPDATE_BOX(s, 320 - width_l - width,
+ 10 + height, width_l, 190 - 2*height);
+ gfxop_update(s->gfx_state);
+
+ GRAPH_UPDATE_BOX(s, width, 10 + height,
+ 320 - 2 * width, height_l);
+ gfxop_update(s->gfx_state);
+ GRAPH_UPDATE_BOX(s, width, 200 - height_l - height,
+ 320 - 2 * width, height_l);
+ gfxop_update(s->gfx_state);
+
+ gfxop_usleep(s->gfx_state, 7 * s->animation_delay);
+ process_sound_events(s);
+ }
+
+ break;
+
+
+ case K_ANIMATE_CLOSE_CHECKERS_OPEN_CHECKERS :
+
+ memset(checkers, 0, sizeof(checkers));
+ remaining_checkers = 19 * 32;
+ update_counter = granularity1;
+
+ while (remaining_checkers) {
+ int x, y, checker = 1 + (int) (1.0 * remaining_checkers*rand()/(RAND_MAX+1.0));
+ i = -1;
+
+ while (checker)
+ if (checkers[++i] == 0) --checker;
+ checkers[i] = 1; /* Mark checker as used */
+
+ x = i % 32;
+ y = i / 32;
+
+ GRAPH_BLANK_BOX(s, x * 10, 10 + y * 10, 10, 10, 0);
+ if (!(update_counter--) || (remaining_checkers == 1)) {
+ gfxop_update(s->gfx_state);
+ update_counter = granularity1;
+ }
+
+ if (remaining_checkers & 1) {
+ gfxop_usleep(s->gfx_state, s->animation_delay / 4);
+ }
+
+ --remaining_checkers;
+ process_sound_events(s);
+ }
+
+ case K_ANIMATE_OPEN_CHECKERS :
+
+ memset(checkers, 0, sizeof(checkers));
+ remaining_checkers = 19 * 32;
+ update_counter = granularity1;
+
+ while (remaining_checkers) {
+ int x, y, checker = 1 + (int) (1.0 * remaining_checkers * rand()/(RAND_MAX+1.0));
+ i = -1;
+
+ while (checker)
+ if (checkers[++i] == 0) --checker;
+ checkers[i] = 1; /* Mark checker as used */
+
+ x = i % 32;
+ y = i / 32;
+
+ GRAPH_UPDATE_BOX(s, x * 10, 10 + y * 10, 10, 10);
+
+ if (!(update_counter--) || (remaining_checkers == 1)) {
+ gfxop_update(s->gfx_state);
+ update_counter = granularity1;
+ }
+
+ if (remaining_checkers & 1) {
+ gfxop_usleep(s->gfx_state, s->animation_delay / 4);
+ }
+
+ --remaining_checkers;
+ process_sound_events(s);
+ }
+ break;
+
+
+ case K_ANIMATE_SCROLL_LEFT :
+
+ for (i = 0; i < 319; i += granularity0) {
+ GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, newscreen,
+ gfx_rect(320 - i, 0, i, 190),
+ gfx_point(0, 10)));
+ GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, s->old_screen,
+ gfx_rect(0, 0, 320 - i, 190),
+ gfx_point(i, 10)));
+ gfxop_update(s->gfx_state);
+
+ gfxop_usleep(s->gfx_state, s->animation_delay >> 3);
+ }
+ GRAPH_UPDATE_BOX(s, 0, 10, 320, 190);
+ break;
+
+ case K_ANIMATE_SCROLL_RIGHT :
+
+ for (i = 0; i < 319; i += granularity0) {
+ GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, newscreen,
+ gfx_rect(0, 0, i, 190),
+ gfx_point(319-i, 10)));
+ GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, s->old_screen,
+ gfx_rect(i, 0, 320 - i, 190),
+ gfx_point(0, 10)));
+ gfxop_update(s->gfx_state);
+
+ gfxop_usleep(s->gfx_state, s->animation_delay >> 3);
+ }
+ GRAPH_UPDATE_BOX(s, 0, 10, 320, 190);
+ break;
+
+ case K_ANIMATE_SCROLL_UP :
+
+ for (i = 0; i < 189; i += granularity0) {
+ GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, newscreen,
+ gfx_rect(0, 190 - i, 320, i),
+ gfx_point(0, 10)));
+ GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, s->old_screen,
+ gfx_rect(0, 0, 320, 190 - i),
+ gfx_point(0, 10 + i)));
+ gfxop_update(s->gfx_state);
+
+ gfxop_usleep(s->gfx_state, s->animation_delay >> 3);
+ }
+ GRAPH_UPDATE_BOX(s, 0, 10, 320, 190);
+ break;
+
+ case K_ANIMATE_SCROLL_DOWN :
+
+ for (i = 0; i < 189; i += granularity0) {
+ GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, newscreen,
+ gfx_rect(0, 0, 320, i),
+ gfx_point(0, 200 - i)));
+ GFX_ASSERT(gfxop_draw_pixmap(s->gfx_state, s->old_screen,
+ gfx_rect(0, i, 320, 190 - i),
+ gfx_point(0, 10)));
+ gfxop_update(s->gfx_state);
+
+ gfxop_usleep(s->gfx_state, s->animation_delay >> 3);
+ }
+ GRAPH_UPDATE_BOX(s, 0, 10, 320, 190);
+ break;
+
+ default:
+ if (s->pic_animate != K_ANIMATE_OPEN_SIMPLE)
+ SCIkwarn(SCIkWARNING, "Unknown opening animation 0x%02x\n", s->pic_animate);
+ GRAPH_UPDATE_BOX(s, 0, 10, 320, 190);
+
+ }
+
+ GFX_ASSERT(gfxop_free_pixmap(s->gfx_state, s->old_screen));
+ GFX_ASSERT(gfxop_free_pixmap(s->gfx_state, newscreen));
+ s->old_screen = NULL;
+
+}
+
+
+reg_t
+kAnimate(state_t *s, int funct_nr, int argc, reg_t *argv)
+ /* Animations are supposed to take a maximum of s->animation_delay milliseconds. */
+{
+ reg_t cast_list_ref = KP_ALT(0, NULL_REG);
+ int cycle = (KP_ALT(1, NULL_REG)).offset;
+ list_t *cast_list = NULL;
+ int open_animation = 0;
+
+
+ process_sound_events(s); /* Take care of incoming events (kAnimate is called semi-regularly) */
+ _k_animate_ran = 1; /* Used by some of the invoked functions to check for recursion, which may,
+ ** after all, damage the cast list */
+
+ if (cast_list_ref.segment) {
+ cast_list = LOOKUP_LIST(cast_list_ref);
+ if (!cast_list)
+ return s->r_acc;
+ }
+
+
+ open_animation = (s->pic_is_new) && (s->pic_not_valid);
+ s->pic_is_new = 0;
+
+ assert_primary_widget_lists(s);
+
+ if (!s->dyn_views->contents /* Only reparentize empty dynview list */
+ && ((GFXWC(s->port) != GFXWC(s->dyn_views->parent)) /* If dynviews are on other port... */
+ || (s->dyn_views->next))) /* ... or not on top of the view list */
+ reparentize_primary_widget_lists(s, s->port);
+
+ if (cast_list) {
+ gfxw_list_t *templist = gfxw_new_list(s->dyn_views->bounds, 0);
+
+ _k_make_view_list(s, &(templist), cast_list, (cycle? _K_MAKE_VIEW_LIST_CYCLE : 0)
+ | _K_MAKE_VIEW_LIST_CALC_PRIORITY, funct_nr, argc, argv);
+
+ /* Make sure that none of the doits() did something evil */
+ assert_primary_widget_lists(s);
+
+ if (!s->dyn_views->contents /* Only reparentize empty dynview list */
+ && ((GFXWC(s->port) != GFXWC(s->dyn_views->parent)) /* If dynviews are on other port... */
+ || (s->dyn_views->next))) /* ... or not on top of the view list */
+ reparentize_primary_widget_lists(s, s->port);
+ /* End of doit() recovery code */
+
+
+ if (s->pic_is_new) { /* Happens if DrawPic() is executed by a dynview (yes, that happens) */
+ kAnimate(s, funct_nr, argc, argv); /* Tail-recurse */
+ return s->r_acc;
+ }
+
+ SCIkdebug(SCIkGRAPHICS, "Handling Dynviews (..step 9 inclusive):\n");
+ _k_prepare_view_list(s, templist, _K_MAKE_VIEW_LIST_CALC_PRIORITY);
+
+ if (s->pic_not_valid) {
+ SCIkdebug(SCIkGRAPHICS, "PicNotValid=%d -> Subalgorithm:\n");
+ _k_redraw_view_list(s, templist);
+ }
+
+ _k_update_signals_in_view_list(s->dyn_views, templist);
+ s->dyn_views->tag(GFXW(s->dyn_views));
+
+ _k_raise_topmost_in_view_list(s, s->dyn_views, (gfxw_dyn_view_t *) templist->contents);
+
+ templist->widfree(GFXW(templist));
+ s->dyn_views->free_tagged(GFXWC(s->dyn_views)); /* Free obsolete dynviews */
+ } /* if (cast_list) */
+
+ if (open_animation) {
+ gfxop_clear_box(s->gfx_state, gfx_rect(0, 10, 320, 190)); /* Propagate pic */
+ s->visual->add_dirty_abs(GFXWC(s->visual), gfx_rect_fullscreen, 0);
+ /* Mark screen as dirty so picviews will be drawn correctly */
+ FULL_REDRAW();
+
+ animate_do_animation(s, funct_nr, argc, argv);
+ } /* if (open_animation) */
+
+ if (cast_list) {
+ int retval;
+ int reparentize = 0;
+
+ s->pic_not_valid = 0;
+
+ _k_view_list_do_postdraw(s, s->dyn_views);
+
+ /* _k_view_list_dispose_loop() returns -1 if it requested a re-start, so we do just that. */
+ while ((retval = _k_view_list_dispose_loop(s, cast_list, (gfxw_dyn_view_t *) s->dyn_views->contents, funct_nr, argc, argv) < 0))
+ reparentize = 1;
+
+ if (s->drop_views->contents) {
+ s->drop_views = gfxw_new_list(s->dyn_views->bounds, GFXW_LIST_SORTED);
+ s->drop_views->flags |= GFXW_FLAG_IMMUNE_TO_SNAPSHOTS;
+ ADD_TO_CURRENT_PICTURE_PORT(s->drop_views);
+ } else {
+ assert(s->drop_views);
+ gfxw_remove_widget_from_container(s->drop_views->parent, GFXW(s->drop_views));
+ ADD_TO_CURRENT_PICTURE_PORT(s->drop_views);
+ }
+
+ if ((reparentize | retval)
+ && (GFXWC(s->port) == GFXWC(s->dyn_views->parent)) /* If dynviews are on the same port... */
+ && (s->dyn_views->next)) /* ... and not on top of the view list... */
+ reparentize_primary_widget_lists(s, s->port); /* ...then reparentize. */
+
+ _k_view_list_kryptonize(s->dyn_views->contents);
+ }
+
+ FULL_REDRAW();
+ return s->r_acc;
+}
+
+#define SHAKE_DOWN 1
+#define SHAKE_RIGHT 2
+
+reg_t
+kShakeScreen(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int shakes = SKPV_OR_ALT(0, 1);
+ int directions = SKPV_OR_ALT(1, 1);
+ gfx_pixmap_t *screen = gfxop_grab_pixmap(s->gfx_state, gfx_rect(0, 0, 320, 200));
+ int i;
+
+ if (directions & ~3)
+ SCIkdebug(SCIkGRAPHICS, "ShakeScreen(): Direction bits are %x (unknown)\n", directions);
+
+ gfxop_set_clip_zone(s->gfx_state, gfx_rect_fullscreen);
+
+ for (i = 0; i < shakes; i++) {
+ int shake_down = (directions & SHAKE_DOWN)? 10 : 0;
+ int shake_right = (directions & SHAKE_RIGHT)? 10 : 0;
+
+ if (directions & SHAKE_DOWN)
+ gfxop_draw_box(s->gfx_state, gfx_rect(0, 0, 320, 10), s->ega_colors[0], s->ega_colors[0], GFX_BOX_SHADE_FLAT);
+
+ if (directions & SHAKE_RIGHT)
+ gfxop_draw_box(s->gfx_state, gfx_rect(0, 0, 10, 200), s->ega_colors[0], s->ega_colors[0], GFX_BOX_SHADE_FLAT);
+
+ gfxop_draw_pixmap(s->gfx_state, screen, gfx_rect(0, 0, 320 - shake_right, 200 - shake_down),
+ gfx_point(shake_right, shake_down));
+
+ gfxop_update(s->gfx_state);
+ gfxop_usleep(s->gfx_state, 50000);
+
+
+ gfxop_draw_pixmap(s->gfx_state, screen, gfx_rect(0, 0, 320, 200), gfx_point(0, 0));
+ gfxop_update(s->gfx_state);
+ gfxop_usleep(s->gfx_state, 50000);
+ }
+
+ gfxop_free_pixmap(s->gfx_state, screen);
+ gfxop_update(s->gfx_state);
+ return s->r_acc;
+}
+
+#define K_DISPLAY_SET_COORDS 100
+#define K_DISPLAY_SET_ALIGNMENT 101
+#define K_DISPLAY_SET_COLOR 102
+#define K_DISPLAY_SET_BGCOLOR 103
+#define K_DISPLAY_SET_GRAYTEXT 104
+#define K_DISPLAY_SET_FONT 105
+#define K_DISPLAY_WIDTH 106
+#define K_DISPLAY_SAVE_UNDER 107
+#define K_DISPLAY_RESTORE_UNDER 108
+#define K_DONT_UPDATE_IMMEDIATELY 121
+
+
+reg_t
+kDisplay(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int argpt;
+ reg_t textp = argv[0];
+ int index = UKPV_OR_ALT(1, 0);
+ int temp;
+ int save_under = 0;
+ gfx_color_t transparent;
+ char *text;
+ gfxw_port_t *port = (s->port)? s->port : s->picture_port;
+ int update_immediately = 1;
+
+ gfx_color_t color0, *color1, bg_color;
+ gfx_alignment_t halign = ALIGN_LEFT;
+ rect_t area = gfx_rect(port->draw_pos.x, port->draw_pos.y,
+ 320 - port->draw_pos.x, 200 - port->draw_pos.y);
+ int gray = port->gray_text;
+ int font_nr = port->font_nr;
+ gfxw_text_t *text_handle;
+
+ transparent.mask = 0;
+
+ color0 = port->color;
+ bg_color = port->bgcolor;
+
+
+ if (textp.segment) {
+ argpt = 1;
+ text = (char *) kernel_dereference_bulk_pointer(s, textp, 0);
+ } else {
+ argpt = 2;
+ text = kernel_lookup_text(s, textp, index);
+ }
+
+ if (!text) {
+ SCIkdebug(SCIkERROR, "Display with invalid reference "PREG"!\n", PRINT_REG(textp));
+ return NULL_REG;
+ }
+
+ while (argpt < argc) {
+
+ switch(UKPV(argpt++)) {
+
+ case K_DISPLAY_SET_COORDS:
+
+ area.x = UKPV(argpt++);
+ area.y = UKPV(argpt++);
+ SCIkdebug(SCIkGRAPHICS, "Display: set_coords(%d, %d)\n", area.x, area.y);
+ break;
+
+ case K_DISPLAY_SET_ALIGNMENT:
+
+ halign = (gfx_alignment_t)KP_SINT(argv[argpt++]);
+ SCIkdebug(SCIkGRAPHICS, "Display: set_align(%d)\n", halign);
+ break;
+
+ case K_DISPLAY_SET_COLOR:
+
+ temp = KP_SINT(argv[argpt++]);
+ SCIkdebug(SCIkGRAPHICS, "Display: set_color(%d)\n", temp);
+ if ((s->resmgr->sci_version < SCI_VERSION_01_VGA) && temp >= 0 && temp <= 15)
+ color0 = (s->ega_colors[temp]);
+ else
+ if ((s->resmgr->sci_version >= SCI_VERSION_01_VGA) && temp >= 0 && temp < 256) {
+ color0.visual = *(get_pic_color(s, temp));
+ color0.mask = GFX_MASK_VISUAL;
+ } else
+ if (temp == -1)
+ color0 = transparent;
+ else
+ SCIkwarn(SCIkWARNING, "Display: Attempt to set invalid fg color %d\n", temp);
+ break;
+
+ case K_DISPLAY_SET_BGCOLOR:
+
+ temp = KP_SINT(argv[argpt++]);
+ SCIkdebug(SCIkGRAPHICS, "Display: set_bg_color(%d)\n", temp);
+ if ((s->resmgr->sci_version < SCI_VERSION_01_VGA) && temp >= 0 && temp <= 15)
+ bg_color = s->ega_colors[temp];
+ else
+ if ((s->resmgr->sci_version >= SCI_VERSION_01_VGA) && temp >= 0 && temp <= 256) {
+ bg_color.visual = *get_pic_color(s, temp);
+ bg_color.mask = GFX_MASK_VISUAL;
+ } else
+ if (temp == -1)
+ bg_color = transparent;
+ else
+ SCIkwarn(SCIkWARNING, "Display: Attempt to set invalid fg color %d\n", temp);
+ break;
+
+ case K_DISPLAY_SET_GRAYTEXT:
+
+ gray = KP_SINT(argv[argpt++]);
+ SCIkdebug(SCIkGRAPHICS, "Display: set_graytext(%d)\n", gray);
+ break;
+
+ case K_DISPLAY_SET_FONT:
+
+ font_nr = KP_UINT(argv[argpt++]);
+
+ SCIkdebug(SCIkGRAPHICS, "Display: set_font(\"font.%03d\")\n", font_nr);
+ break;
+
+ case K_DISPLAY_WIDTH:
+
+ area.xl = UKPV(argpt++);
+ if (area.xl == 0)
+ area.xl = MAX_TEXT_WIDTH_MAGIC_VALUE;
+
+ SCIkdebug(SCIkGRAPHICS, "Display: set_width(%d)\n", area.xl);
+ break;
+
+ case K_DISPLAY_SAVE_UNDER:
+
+ save_under = 1;
+ SCIkdebug(SCIkGRAPHICS, "Display: set_save_under()\n");
+ break;
+
+ case K_DISPLAY_RESTORE_UNDER:
+
+ SCIkdebug(SCIkGRAPHICS, "Display: restore_under(%04x)\n", UKPV(argpt));
+ graph_restore_box(s, argv[argpt++]);
+ update_immediately = 1;
+ argpt++;
+ return s->r_acc;
+
+ case K_DONT_UPDATE_IMMEDIATELY:
+
+ update_immediately=0;
+ SCIkdebug(SCIkGRAPHICS, "Display: set_dont_update()\n");
+ argpt++;
+ break;
+
+ default:
+ SCIkdebug(SCIkGRAPHICS, "Unknown Display() command %x\n", UKPV(argpt-1));
+ return NULL_REG;
+ }
+ }
+
+ if (s->version >= SCI_VERSION_FTU_DISPLAY_COORDS_FUZZY) {
+ if (halign == ALIGN_LEFT)
+ GFX_ASSERT(gfxop_get_text_params(s->gfx_state, font_nr, text,
+ area.xl, &area.xl, &area.yl, 0,
+ NULL, NULL, NULL));
+
+ /* Make the text fit on the screen */
+ if (area.x + area.xl > 320)
+ area.x += 320 - area.x - area.xl; /* Plus negative number = subtraction */
+
+ if (area.y + area.yl > 200)
+ {
+ area.y += 200 - area.y - area.yl; /* Plus negative number = subtraction */
+ }
+ }
+
+ if (gray)
+ color1 = &bg_color;
+ else
+ color1 = &color0;
+
+ assert_primary_widget_lists(s);
+
+ text_handle = gfxw_new_text(s->gfx_state, area, font_nr, text, halign,
+ ALIGN_TOP, color0, *color1, bg_color, 0);
+
+ if (!text_handle) {
+ SCIkwarn(SCIkERROR, "Display: Failed to create text widget!\n");
+ return NULL_REG;
+ }
+
+ if (save_under) { /* Backup */
+ rect_t save_area = text_handle->bounds;
+ save_area.x += port->bounds.x;
+ save_area.y += port->bounds.y;
+
+ s->r_acc = graph_save_box(s, save_area);
+ text_handle->serial++; /* This is evil! */
+
+ SCIkdebug(SCIkGRAPHICS, "Saving (%d, %d) size (%d, %d) as "PREG"\n",
+ save_area.x, save_area.y, save_area.xl, save_area.yl, s->r_acc);
+
+ }
+
+
+ SCIkdebug(SCIkGRAPHICS, "Display: Commiting text '%s'\n", text);
+
+ /*
+ ADD_TO_CURRENT_FG_WIDGETS(text_handle);
+ */
+
+ ADD_TO_CURRENT_FG_WIDGETS(GFXW(text_handle));
+ if ((!s->pic_not_valid)&&update_immediately) { /* Refresh if drawn to valid picture */
+ FULL_REDRAW();
+ SCIkdebug(SCIkGRAPHICS, "Refreshing display...\n");
+ }
+
+ return s->r_acc;
+}
diff --git a/engines/sci/engine/klists.c b/engines/sci/engine/klists.c
new file mode 100644
index 0000000000..7637ddd403
--- /dev/null
+++ b/engines/sci/engine/klists.c
@@ -0,0 +1,608 @@
+/***************************************************************************
+ klists.c Copyright (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]
+
+***************************************************************************/
+
+#include <engine.h>
+
+#ifdef LOOKUP_NODE
+# undef LOOKUP_NODE
+# define LOOKUP_NODE(addr) inline_lookup_node(s, (addr), __FILE__, __LINE__)
+#endif
+
+inline node_t *
+inline_lookup_node(state_t *s, reg_t addr, const char *file, int line)
+{
+ mem_obj_t *mobj;
+ node_table_t *nt;
+
+ if (!addr.offset && !addr.segment)
+ return NULL; /* Non-error null */
+
+ mobj = GET_SEGMENT(s->seg_manager, addr.segment, MEM_OBJ_NODES);
+ if (!mobj) {
+ sciprintf("%s, L%d: Attempt to use non-node "PREG" as list node\n",
+ __FILE__, __LINE__, PRINT_REG(addr));
+ script_debug_flag = script_error_flag = 1;
+ return NULL;
+ }
+
+ nt = &(mobj->data.nodes);
+
+ if (!ENTRY_IS_VALID(nt, addr.offset)) {
+ sciprintf("%s, L%d: Attempt to use non-node "PREG" as list node\n",
+ __FILE__, __LINE__, PRINT_REG(addr));
+ script_debug_flag = script_error_flag = 1;
+ return NULL;
+ }
+
+ return &(nt->table[addr.offset].entry);
+}
+
+node_t *
+lookup_node(state_t *s, reg_t addr, const char *file, int line)
+{
+ return inline_lookup_node(s, addr, file, line);
+}
+
+#define LOOKUP_NULL_LIST(addr) _lookup_list(s, addr, __FILE__, __LINE__, 1)
+
+
+inline list_t *
+_lookup_list(state_t *s, reg_t addr, const char *file, int line, int may_be_null)
+{
+ mem_obj_t *mobj;
+ list_table_t *lt;
+
+ if (may_be_null && !addr.segment && !addr.offset)
+ return NULL;
+
+ mobj = GET_SEGMENT(s->seg_manager, addr.segment, MEM_OBJ_LISTS);
+
+ if (!mobj) {
+ sciprintf("%s, L%d: Attempt to use non-list "PREG" as list\n",
+ __FILE__, __LINE__, PRINT_REG(addr));
+ script_debug_flag = script_error_flag = 1;
+ return NULL;
+ }
+
+ lt = &(mobj->data.lists);
+
+ if (!ENTRY_IS_VALID(lt, addr.offset)) {
+ sciprintf("%s, L%d: Attempt to use non-list "PREG" as list\n",
+ __FILE__, __LINE__, PRINT_REG(addr));
+ script_debug_flag = script_error_flag = 1;
+ return NULL;
+ }
+
+ return &(lt->table[addr.offset].entry);
+}
+
+list_t *
+lookup_list(state_t *s, reg_t addr, const char *file, int line)
+{
+ return _lookup_list(s, addr, file, line, 0);
+}
+
+int
+listp(state_t *s, reg_t addr)
+{
+ return (s->seg_manager.heap[addr.segment]->type == MEM_OBJ_LISTS
+ && ENTRY_IS_VALID(&(s->seg_manager.heap[addr.segment]->data.lists), addr.offset));
+}
+
+#ifdef DISABLE_VALIDATIONS
+
+#define sane_nodep(a, b) 1
+#define sane_listp(a, b) 1
+
+#else
+
+static inline int
+sane_nodep(state_t *s, reg_t addr)
+{
+ int have_prev = 0;
+ reg_t prev;
+
+ do {
+ node_t *node = LOOKUP_NODE(addr);
+
+ if (!node)
+ return 0;
+
+ if ((have_prev)
+ && !REG_EQ(node->pred, prev))
+ return 0;
+
+ prev = addr;
+ addr = node->succ;
+ have_prev = 1;
+
+ } while (!IS_NULL_REG(addr));
+
+ return 1;
+}
+
+
+int
+sane_listp(state_t *s, reg_t addr)
+{
+ list_t *l = LOOKUP_LIST(addr);
+ int empties = 0;
+
+ if (IS_NULL_REG(l->first))
+ ++empties;
+ if (IS_NULL_REG(l->last))
+ ++empties;
+
+ /* Either none or both must be set */
+ if (empties == 1)
+ return 0;
+
+ if (!empties) {
+ node_t *node_a, *node_z;
+
+ node_a = LOOKUP_NODE(l->first);
+ node_z = LOOKUP_NODE(l->last);
+
+ if (!node_a || !node_z)
+ return 0;
+
+ if (!IS_NULL_REG(node_a->pred))
+ return 0;
+
+ if (!IS_NULL_REG(node_z->succ))
+ return 0;
+
+ return sane_nodep(s, l->first);
+ }
+
+ return 1; /* Empty list is fine */
+}
+#endif
+
+
+reg_t
+kNewList(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ reg_t listbase;
+ list_t *l;
+ l = sm_alloc_list(&s->seg_manager, &listbase);
+ l->first = l->last = NULL_REG;
+ SCIkdebug(SCIkNODES, "New listbase at "PREG"\n", PRINT_REG(listbase));
+
+ return listbase; /* Return list base address */
+}
+
+reg_t
+kDisposeList(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ list_t *l = LOOKUP_LIST(argv[0]);
+
+ if (!l) {
+ SCIkwarn(SCIkERROR, "Attempt to dispose non-list at "PREG"!\n",
+ PRINT_REG(argv[0]));
+ return NULL_REG;
+ }
+
+ if (!sane_listp(s, argv[0]))
+ SCIkwarn(SCIkERROR,"List at "PREG" is not sane anymore!\n", PRINT_REG(argv[0]));
+
+/* if (!IS_NULL_REG(l->first)) {
+ reg_t n_addr = l->first;
+
+ while (!IS_NULL_REG(n_addr)) { /-* Free all nodes *-/
+ node_t *n = LOOKUP_NODE(n_addr);
+ sm_free_node(&s->seg_manager, n_addr);
+ n_addr = n->succ;
+ }
+ }
+
+ sm_free_list(&s->seg_manager, argv[0]);
+*/
+
+ return s->r_acc;
+}
+
+
+inline reg_t
+_k_new_node(state_t *s, reg_t value, reg_t key)
+{
+ reg_t nodebase;
+ node_t *n = sm_alloc_node(&s->seg_manager, &nodebase);
+
+ if (!n) {
+ KERNEL_OOPS("Out of memory while creating a node");
+ return NULL_REG;
+ }
+
+ n->pred = n->succ = NULL_REG;
+ n->key = key;
+ n->value = value;
+
+ return nodebase;
+}
+
+reg_t
+kNewNode(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ s->r_acc = _k_new_node(s, argv[0], argv[1]);
+
+ SCIkdebug(SCIkNODES, "New nodebase at "PREG"\n", PRINT_REG(s->r_acc));
+
+ return s->r_acc;
+}
+
+
+
+reg_t
+kFirstNode(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ list_t *l = LOOKUP_NULL_LIST(argv[0]);
+
+ if (l && !sane_listp(s, argv[0]))
+ SCIkwarn(SCIkERROR,"List at "PREG" is not sane anymore!\n",
+ PRINT_REG(argv[0]));
+
+ if (l)
+ return l->first;
+ else
+ return NULL_REG;
+}
+
+
+reg_t
+kLastNode(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ list_t *l = LOOKUP_LIST(argv[0]);
+
+ if (l&&!sane_listp(s, argv[0]))
+ SCIkwarn(SCIkERROR,"List at "PREG" is not sane anymore!\n",
+ PRINT_REG(argv[0]));
+
+ if (l)
+ return l->last;
+ else
+ return NULL_REG;
+}
+
+
+reg_t
+kEmptyList(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ list_t *l = LOOKUP_LIST(argv[0]);
+
+ if (!l || !sane_listp(s, argv[0]))
+ SCIkwarn(SCIkERROR,"List at "PREG" is invalid or not sane anymore!\n",
+ PRINT_REG(argv[0]));
+
+ return make_reg(0, ((l)? IS_NULL_REG(l->first) : 0));
+}
+
+inline void
+_k_add_to_front(state_t *s, reg_t listbase, reg_t nodebase)
+{
+ list_t *l = LOOKUP_LIST(listbase);
+ node_t *new_n = LOOKUP_NODE(nodebase);
+
+ SCIkdebug(SCIkNODES, "Adding node "PREG" to end of list "PREG"\n",
+ PRINT_REG(nodebase), PRINT_REG(listbase));
+
+ if (!new_n)
+ SCIkwarn(SCIkERROR, "Attempt to add non-node ("PREG") to list at "PREG"\n",
+ PRINT_REG(nodebase), PRINT_REG(listbase));
+ if (!l || !sane_listp(s, listbase))
+ SCIkwarn(SCIkERROR,"List at "PREG" is not sane anymore!\n", PRINT_REG(listbase));
+
+ new_n->succ = l->first;
+ new_n->pred = NULL_REG;
+ /* Set node to be the first and last node if it's the only node of the list */
+ if (IS_NULL_REG(l->first))
+ l->last = nodebase;
+ else {
+ node_t *old_n = LOOKUP_NODE(l->first);
+ old_n->pred = nodebase;
+ }
+ l->first = nodebase;
+}
+
+inline void
+_k_add_to_end(state_t *s, reg_t listbase, reg_t nodebase)
+{
+ list_t *l = LOOKUP_LIST(listbase);
+ node_t *new_n = LOOKUP_NODE(nodebase);
+
+ SCIkdebug(SCIkNODES, "Adding node "PREG" to end of list "PREG"\n",
+ PRINT_REG(nodebase), PRINT_REG(listbase));
+
+ if (!new_n)
+ SCIkwarn(SCIkERROR, "Attempt to add non-node ("PREG") to list at "PREG"\n",
+ PRINT_REG(nodebase), PRINT_REG(listbase));
+ if (!l || !sane_listp(s, listbase))
+ SCIkwarn(SCIkERROR,"List at "PREG" is not sane anymore!\n", PRINT_REG(listbase));
+
+ new_n->succ = NULL_REG;
+ new_n->pred = l->last;
+ /* Set node to be the first and last node if it's the only node of the list */
+ if (IS_NULL_REG(l->last))
+ l->first = nodebase;
+ else {
+ node_t *old_n = LOOKUP_NODE(l->last);
+ old_n->succ = nodebase;
+ }
+ l->last = nodebase;
+}
+
+
+reg_t
+kNextNode(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ node_t *n = LOOKUP_NODE(argv[0]);
+ if (!sane_nodep(s, argv[0]))
+ {
+ SCIkwarn(SCIkERROR,"List node at "PREG" is not sane anymore!\n",
+ PRINT_REG(argv[0]));
+ script_error_flag = script_debug_flag = 0;
+ return NULL_REG;
+ }
+
+ return n->succ;
+}
+
+reg_t
+kPrevNode(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ node_t *n = LOOKUP_NODE(argv[0]);
+ if (!sane_nodep(s, argv[0]))
+ SCIkwarn(SCIkERROR,"List node at "PREG" is not sane anymore!\n",
+ PRINT_REG(argv[0]));
+
+ return n->pred;
+}
+
+
+reg_t
+kNodeValue(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ node_t *n = LOOKUP_NODE(argv[0]);
+ if (!sane_nodep(s, argv[0]))
+ {
+ SCIkwarn(SCIkERROR,"List node at "PREG" is not sane!\n",
+ PRINT_REG(argv[0]));
+ script_debug_flag = script_error_flag = 0;
+ return NULL_REG;
+ }
+
+ return n->value;
+}
+
+
+reg_t
+kAddToFront(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ _k_add_to_front(s, argv[0], argv[1]);
+ return s->r_acc;
+}
+
+reg_t
+kAddAfter(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ list_t *l = LOOKUP_LIST(argv[0]);
+ node_t *firstnode = IS_NULL_REG(argv[1])? NULL : LOOKUP_NODE(argv[1]);
+ node_t *newnode = LOOKUP_NODE(argv[2]);
+
+ if (!l || !sane_listp(s, argv[0]))
+ SCIkwarn(SCIkERROR,"List at "PREG" is not sane anymore!\n",
+ PRINT_REG(argv[0]));
+
+ if (!newnode) {
+ SCIkwarn(SCIkERROR,"New 'node' "PREG" is not a node!\n",
+ argv[1], argv[2]);
+ return NULL_REG;
+ }
+
+ if (argc != 3) {
+ SCIkdebug(SCIkWARNING, "Aborting.\n");
+ return NULL_REG;
+ }
+
+ if (firstnode) { /* We're really appending after */
+ reg_t oldnext = firstnode->succ;
+
+ newnode->pred = argv[1];
+ firstnode->succ = argv[2];
+ newnode->succ = oldnext;
+
+ if (IS_NULL_REG(oldnext)) /* Appended after last node? */
+ /* Set new node as last list node */
+ l->last = argv[2];
+ else
+ LOOKUP_NODE(oldnext)->pred = argv[2];
+
+ return s->r_acc;
+
+ } else { /* !firstnode */
+ /* Prepare call to AddToFront... */
+ argv[1] = argv[0];
+ return kAddToFront(s, funct_nr, 2, argv + 1);/* Set as initial list node */
+ }
+}
+
+
+reg_t
+kAddToEnd(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ _k_add_to_end(s, argv[0], argv[1]);
+ return s->r_acc;
+}
+
+
+reg_t
+kFindKey(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ reg_t node_pos;
+ reg_t key = argv[1];
+ reg_t list_pos = argv[0];
+
+ SCIkdebug(SCIkNODES, "Looking for key "PREG" in list "PREG"\n",
+ PRINT_REG(key), PRINT_REG(list_pos));
+
+ if (!sane_listp(s, list_pos))
+ SCIkwarn(SCIkERROR,"List at "PREG" is not sane anymore!\n",
+ PRINT_REG(list_pos));
+
+ node_pos = LOOKUP_LIST(list_pos)->first;
+
+ SCIkdebug(SCIkNODES, "First node at "PREG"\n", PRINT_REG(node_pos));
+
+ while (!IS_NULL_REG(node_pos)) {
+ node_t *n = LOOKUP_NODE(node_pos);
+ if (REG_EQ(n->key, key)) {
+ SCIkdebug(SCIkNODES, " Found key at "PREG"\n", PRINT_REG(node_pos));
+ return node_pos;
+ }
+
+ node_pos = n->succ;
+ SCIkdebug(SCIkNODES, "NextNode at "PREG"\n", PRINT_REG(node_pos));
+ }
+
+ SCIkdebug(SCIkNODES, "Looking for key without success\n");
+ return NULL_REG;
+}
+
+
+reg_t
+kDeleteKey(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ reg_t node_pos = kFindKey(s, funct_nr, 2, argv);
+ node_t *n;
+ list_t *l = LOOKUP_LIST(argv[0]);
+
+ if (IS_NULL_REG(node_pos))
+ return NULL_REG; /* Signal falure */
+
+ n = LOOKUP_NODE(node_pos);
+ if (REG_EQ(l->first, node_pos))
+ l->first = n->succ;
+ if (REG_EQ(l->last, node_pos))
+ l->last = n->pred;
+
+ if (!IS_NULL_REG(n->pred))
+ LOOKUP_NODE(n->pred)->succ = n->succ;
+ if (!IS_NULL_REG(n->succ))
+ LOOKUP_NODE(n->succ)->pred = n->pred;
+
+/* sm_free_node(&s->seg_manager, node_pos);*/
+
+ return make_reg(0, 1); /* Signal success */
+}
+
+
+
+
+typedef struct
+{
+ reg_t key, value;
+ reg_t order;
+} sort_temp_t;
+
+int
+sort_temp_cmp(const void *p1, const void *p2)
+{
+ sort_temp_t *st1 = (sort_temp_t *) p1;
+ sort_temp_t *st2 = (sort_temp_t *) p2;
+
+ if (st1->order.segment < st1->order.segment ||
+ (st1->order.segment == st1->order.segment &&
+ st1->order.offset < st2->order.offset))
+ return -1;
+
+ if (st1->order.segment > st2->order.segment ||
+ (st1->order.segment == st2->order.segment &&
+ st1->order.offset > st2->order.offset))
+ return 1;
+
+ return 0;
+}
+
+reg_t
+kSort(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ reg_t source = argv[0];
+ reg_t dest = argv[1];
+ reg_t order_func = argv[2];
+
+ int input_size = GET_SEL32SV(source, size);
+
+ int i;
+
+ sort_temp_t *temp_array = (sort_temp_t *)
+ malloc(sizeof(sort_temp_t)*input_size);
+
+ reg_t input_data = GET_SEL32(source, elements);
+ reg_t output_data = GET_SEL32(dest, elements);
+
+ list_t *list;
+ node_t *node;
+
+ if (!input_size)
+ return s->r_acc;
+
+ if (IS_NULL_REG(output_data))
+ {
+ list = sm_alloc_list(&s->seg_manager, &output_data);
+ list->first = list->last = NULL_REG;
+ PUT_SEL32(dest, elements, output_data);
+ }
+
+ PUT_SEL32V(dest, size, input_size);
+
+ list = LOOKUP_LIST(input_data);
+ node = LOOKUP_NODE(list->first);
+
+ i = 0;
+ while (node)
+ {
+ invoke_selector(INV_SEL(order_func, doit, 0), 1, node->value);
+ temp_array[i].key = node->key;
+ temp_array[i].value = node->value;
+ temp_array[i].order = s->r_acc;
+ i++;
+ node = LOOKUP_NODE(node->succ);
+ }
+
+ qsort(temp_array, input_size, sizeof(sort_temp_t), sort_temp_cmp);
+
+ for (i=0;i<input_size;i++)
+ {
+ reg_t node = _k_new_node(s, temp_array[i].key,
+ temp_array[i].value);
+ _k_add_to_end(s, output_data, node);
+ }
+
+ free(temp_array);
+
+ return s->r_acc;
+}
diff --git a/engines/sci/engine/kmath.c b/engines/sci/engine/kmath.c
new file mode 100644
index 0000000000..94480ab3bd
--- /dev/null
+++ b/engines/sci/engine/kmath.c
@@ -0,0 +1,201 @@
+/***************************************************************************
+ kmath.c Copyright (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]
+
+***************************************************************************/
+
+#include <engine.h>
+
+
+reg_t
+kRandom(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ return make_reg(0,
+ SKPV(0) + (int) ((SKPV(1) + 1.0 - SKPV(0)) * (rand() / (RAND_MAX + 1.0))));
+}
+
+
+reg_t
+kAbs(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ /* This is a hack, but so is the code in Hoyle1 that needs it. */
+ if (argv[0].segment)
+ return make_reg(0, 0x3e8); /* Yes people, this is an object */
+ return make_reg(0, abs(SKPV(0)));
+}
+
+
+reg_t
+kSqrt(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ return make_reg(0, (gint16) sqrt((float) abs(SKPV(0))));
+}
+
+
+int
+get_angle(int xrel, int yrel)
+{
+ if ((xrel == 0) && (yrel == 0))
+ return 0;
+ else {
+ int val = (int) (180.0/PI * atan2(xrel, -yrel));
+ if (val < 0)
+ val += 360;
+
+ /* Take care of OB1 differences between SSCI and
+ FSCI. SCI games sometimes check for equality with
+ "round" angles */
+ if (val % 45 == 44)
+ val++;
+ else if (val % 45 == 1)
+ val--;
+
+ return val;
+ }
+}
+
+reg_t
+kGetAngle(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ /* Based on behavior observed with a test program created with
+ ** SCI Studio.
+ */
+ int x1 = SKPV(0);
+ int y1 = SKPV(1);
+ int x2 = SKPV(2);
+ int y2 = SKPV(3);
+ int xrel = x2 - x1;
+ int yrel = y1 - y2; /* y-axis is mirrored. */
+ int angle;
+
+ /* Move (xrel, yrel) to first quadrant. */
+ if (y1 < y2)
+ yrel = -yrel;
+ if (x2 < x1)
+ xrel = -xrel;
+
+ /* Compute angle in grads. */
+ if (yrel == 0 && xrel == 0)
+ angle = 0;
+ else
+ angle = 100 * xrel / (xrel + yrel);
+
+ /* Fix up angle for actual quadrant of (xrel, yrel). */
+ if (y1 < y2)
+ angle = 200 - angle;
+ if (x2 < x1)
+ angle = 400 - angle;
+
+ /* Convert from grads to degrees by merging grad 0 with grad 1,
+ ** grad 10 with grad 11, grad 20 with grad 21, etc. This leads to
+ ** "degrees" that equal either one or two grads.
+ */
+ angle -= (angle + 9) / 10;
+
+ return make_reg(0, angle);
+}
+
+
+reg_t
+kGetDistance(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int xrel = (int) (((float) SKPV(1) - SKPV_OR_ALT(3, 0))/cos(SKPV_OR_ALT(5, 0)* PI / 180.0)); /* This works because cos(0)==1 */
+ int yrel = SKPV(0) - SKPV_OR_ALT(2, 0);
+
+ return make_reg(0, (gint16)sqrt((float) xrel*xrel + yrel*yrel));
+}
+
+reg_t
+kTimesSin(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int angle = SKPV(0);
+ int factor = SKPV(1);
+
+ return make_reg(0, (int) (factor * 1.0 * sin(angle * PI / 180.0)));
+}
+
+
+reg_t
+kTimesCos(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int angle = SKPV(0);
+ int factor = SKPV(1);
+
+ return make_reg(0, (int) (factor * 1.0 * cos(angle * PI / 180.0)));
+}
+
+reg_t
+kCosDiv(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int angle = SKPV(0);
+ int value = SKPV(1);
+ double cosval = cos(angle * PI / 180.0);
+
+ if ((cosval < 0.0001) && (cosval > 0.0001)) {
+ SCIkwarn(SCIkWARNING,"Attepted division by zero\n");
+ return make_reg(0, (gint16)0x8000);
+ } else
+ return make_reg(0, (gint16) (value/cosval));
+}
+
+reg_t
+kSinDiv(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int angle = SKPV(0);
+ int value = SKPV(1);
+ double sinval = sin(angle * PI / 180.0);
+
+ if ((sinval < 0.0001) && (sinval > 0.0001)) {
+ SCIkwarn(SCIkWARNING,"Attepted division by zero\n");
+ return make_reg(0, (gint16)0x8000);
+ } else
+ return make_reg(0, (gint16) (value/sinval));
+}
+
+reg_t
+kTimesTan(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int param = SKPV(0);
+ int scale = SKPV_OR_ALT(1, 1);
+
+ param -= 90;
+ if ((param % 90) == 0) {
+ SCIkwarn(SCIkWARNING, "Attempted tan(pi/2)");
+ return make_reg(0, (gint16)0x8000);
+ } else
+ return make_reg(0, (gint16) -(tan(param * PI / 180.0) * scale));
+}
+
+reg_t
+kTimesCot(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int param = SKPV(0);
+ int scale = SKPV_OR_ALT(1, 1);
+
+ if ((param % 90) == 0) {
+ SCIkwarn(SCIkWARNING, "Attempted tan(pi/2)");
+ return make_reg(0, (gint16)0x8000);
+ } else
+ return make_reg(0, (gint16) (tan(param * PI / 180.0) * scale));
+}
diff --git a/engines/sci/engine/kmenu.c b/engines/sci/engine/kmenu.c
new file mode 100644
index 0000000000..4530d5c34f
--- /dev/null
+++ b/engines/sci/engine/kmenu.c
@@ -0,0 +1,533 @@
+/***************************************************************************
+ kmenu.c Copyright (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]
+
+***************************************************************************/
+
+#include <sciresource.h>
+#include <engine.h>
+#include <sci_widgets.h>
+
+
+reg_t
+kAddMenu(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ char *name = kernel_dereference_char_pointer(s, argv[0], 0);
+ char *contents = kernel_dereference_char_pointer(s, argv[1], 0);
+
+ menubar_add_menu(s->gfx_state, s->menubar, name,
+ contents, s->titlebar_port->font_nr, argv[1]);
+
+ return s->r_acc;
+
+}
+
+
+reg_t
+kSetMenu(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int index = UKPV(0);
+ int i = 2;
+
+ while (i < argc) {
+ menubar_set_attribute(s, (index >> 8) - 1, (index & 0xff) - 1, UKPV(i - 1), argv[i]);
+ i += 2;
+ }
+
+ return s->r_acc;
+}
+
+reg_t
+kGetMenu(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int index = UKPV(0);
+
+ return menubar_get_attribute(s, (index >> 8) - 1, (index & 0xff) - 1, UKPV(1));
+}
+
+
+reg_t
+kDrawStatus(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ reg_t text = argv[0];
+ int fgcolor = SKPV_OR_ALT(1, s->status_bar_foreground);
+ int bgcolor = SKPV_OR_ALT(2, s->status_bar_background);
+
+ s->titlebar_port->color.visual = *(get_pic_color(s, fgcolor));
+ s->titlebar_port->color.mask = GFX_MASK_VISUAL;
+ s->titlebar_port->bgcolor.visual = *(get_pic_color(s, bgcolor));
+ s->titlebar_port->bgcolor.mask = GFX_MASK_VISUAL;
+
+ s->status_bar_foreground=fgcolor;
+ s->status_bar_background=bgcolor;
+
+ if (NULL != s->status_bar_text) {
+ sci_free(s->status_bar_text);
+ s->status_bar_text = NULL;
+ }
+
+ if (text.segment)
+ s->status_bar_text = sci_strdup(kernel_dereference_char_pointer(s, text, 0));
+
+ sciw_set_status_bar(s, s->titlebar_port, s->status_bar_text, fgcolor, bgcolor);
+
+ gfxop_update(s->gfx_state);
+
+ return s->r_acc;
+}
+
+
+reg_t
+kDrawMenuBar(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+
+ if (SKPV(0))
+ sciw_set_menubar(s, s->titlebar_port, s->menubar, -1);
+ else
+ sciw_set_status_bar(s, s->titlebar_port, NULL, 0, 0);
+
+ s->titlebar_port->draw(GFXW(s->titlebar_port), gfx_point(0, 0));
+ gfxop_update(s->gfx_state);
+
+ return s->r_acc;
+}
+
+
+#define ABOUT_FREESCI_PAGES_NR 7
+
+static struct {
+ const char *title;
+ const char *body;
+ int fgcolor, bgcolor;
+} _about_freesci_pages[ABOUT_FREESCI_PAGES_NR] = {
+ {"FreeSCI hackers and contributors",
+ "Alexander R. Angas\nDirectX 8 driver, Event sound server, Generic memory functions"
+ "\n\n"
+ "Anders Baden Nielsen\nPPC testing"
+ "\n\n"
+ "Bas Zoetekouw\nMan pages, debian package management, CVS maintenance"
+ "\n\n"
+ "Carl Muckenhoupt\nSources to the SCI resource viewer tools that started it all"
+ "\n\n"
+ "Chris Kehler\nMakefile enhancements"
+ "\n\n"
+ "Christoph Reichenbach\nUN*X code, VM/Graphics/Sound/other infrastructure"
+ "\n\n"
+ "Christopher T. Lansdown\nOriginal CVS maintainer, Alpha compatibility fixes"
+ ,0, 15},
+ {"More FreeSCI hackers and contributors",
+ "Claudio Matsuoka\nCVS snapshots, daily builds, BeOS and cygwin ports"
+ "\n\n"
+ "Dark Minister\nSCI research (bytecode and parser)"
+ "\n\n"
+ "Dmitry Jemerov\nPort to the Win32 platform, numerous bugfixes"
+ "\n\n"
+ "Emmanuel Jeandel\nBugfixes and bug reports"
+ "\n\n"
+ "Francois-R Boyer\nMT-32 information and mapping code"
+ "\n\n"
+ "George Reid\nFreeBSD package management"
+ "\n\n"
+ "Hubert Maier\nAmigaOS 4 port"
+ ,0, 15},
+ {"Even more FreeSCI hackers & contributors",
+ "Hugues Valois\nGame selection menu"
+ "\n\n"
+ "Johannes Manhave\nDocument format translation"
+ "\n\n"
+ "Jordi Vilalta\nNumerous code and website clean-up patches"
+ "\n\n"
+ "Lars Skovlund\nProject maintenance, most documentation, bugfixes, SCI1 support"
+ "\n\n"
+ "Magnus Reftel\nHeap implementation, Python class viewer, bugfixes"
+ "\n\n"
+ "Matt Hargett\nClean-ups, bugfixes, Hardcore QA, Win32"
+ "\n\n"
+ "Max Horn\nSetJump implementation"
+ ,0, 15},
+ {"Still more of them",
+ "Paul David Doherty\nGame version information"
+ "\n\n"
+ "Petr Vyhnak\nThe DCL-INFLATE algorithm, many Win32 improvements"
+ "\n\n"
+ "Rainer Canavan\nIRIX MIDI driver and bug fixes"
+ "\n\n"
+ "Rainer De Temple\nSCI research"
+ "\n\n"
+ "Ravi I.\nSCI0 sound resource specification"
+ "\n\n"
+ "Ruediger Hanke\nPort to the MorphOS platform"
+ "\n\n"
+ "Rune Orsval\nConfiguration file editor"
+ ,0, 15},
+ {"Is there no end to these contributors?",
+ "Rickard Lind\nMT32->GM MIDI mapping magic, sound research"
+ "\n\n"
+ "Rink Springer\nPort to the DOS platform, several bug fixes"
+ "\n\n"
+ "Robey Pointer\nBug tracking system hosting"
+ "\n\n"
+ "Sergey Lapin\nPort of Carl's type 2 decompression code"
+ "\n\n"
+ "Solomon Peachy\nSDL ports and much of the sound subsystem"
+ "\n\n"
+ "Vyacheslav Dikonov\nConfig script improvements"
+ "\n\n"
+ "Walter van Niftrik\nPorts to the Dreamcast and GP32 platforms",
+ 0, 15},
+ {"The CSCI5573 Team at CU Boulder",
+ "Xiaojun Chen\nSean Terrell\nChristoph Reichenbach\n\n"
+ "Special thanks to Prof. Dr. Gary Nutt\n\nfor allowing the FreeSCI VM extension as a\ncourse project in his Advanced OS course"
+ ,0, 15},
+ {"Special Thanks",
+ "Special Thanks as well\n\n\nto the linuxgames.com and telefragged.com crew\nfor hosting us\n\n"
+ "To the savannah.gnu.org staff\nfor hosting our mailing list\n\n"
+ "To Bob Heitman and Corey Cole for their support"
+ ,0, 15}
+};
+
+
+void
+about_freesci(state_t *s)
+{
+ int page;
+ gfxw_port_t *port;
+ int bodyfont, titlefont;
+ resource_t *bodyfont_res = NULL;
+ int i;
+
+ titlefont = s->titlebar_port->font_nr;
+
+ i = 999;
+ while (!bodyfont_res && (i > -1))
+ bodyfont_res = scir_test_resource(s->resmgr, sci_font, i--);
+
+ if (i == -1) {
+ sciprintf("Sorry, couldn't find a font...\n");
+ return;
+ }
+
+ bodyfont = i+1;
+ for (page = 0; page < ABOUT_FREESCI_PAGES_NR; ++page) {
+ sci_event_t event;
+ int cont = 2;
+ int width, height, width2, foo;
+
+ _about_freesci_pages[page].fgcolor = 0;
+ _about_freesci_pages[page].bgcolor = 15;
+
+ gfxop_get_text_params(s->gfx_state, bodyfont, _about_freesci_pages[page].body, 300, &width, &height, 0,
+ NULL, NULL, NULL);
+ gfxop_get_text_params(s->gfx_state, titlefont, _about_freesci_pages[page].title, 300, &width2, &foo, 0,
+ NULL, NULL, NULL);
+
+ width += 4;
+ width2 += 4;
+ height += 12;
+
+ if (width2 > width)
+ width = width2;
+
+ port = sciw_new_window(s, gfx_rect(156 - (width >> 1), 100 - (height >> 1), width, height),
+ bodyfont, s->ega_colors[_about_freesci_pages[page].fgcolor],
+ s->ega_colors[_about_freesci_pages[page].bgcolor],
+ titlefont, s->ega_colors[15], s->ega_colors[0],
+ _about_freesci_pages[page].title, WINDOW_FLAG_TITLE);
+
+ port->add(GFXWC(port), GFXW(gfxw_new_text(s->gfx_state, gfx_rect(0,0,width,height), bodyfont,
+ _about_freesci_pages[page].body,
+ ALIGN_CENTER, ALIGN_CENTER, port->color, port->color,
+ port->bgcolor, 0)
+ ));
+
+ s->visual->add(GFXWC(s->visual), GFXW(port));
+
+ port->add_dirty_abs(GFXWC(port), gfx_rect_fullscreen, 1);
+ s->visual->draw(GFXW(s->visual), gfx_point(0,0));
+ gfxop_update(s->gfx_state);
+
+ while (cont) {
+ event = gfxop_get_event(s->gfx_state, SCI_EVT_ANY);
+
+ if (event.type == SCI_EVT_MOUSE_RELEASE || event.type == SCI_EVT_MOUSE_PRESS)
+ --cont;
+
+ if (event.type == SCI_EVT_QUIT) {
+ quit_vm();
+ return;
+ }
+
+ if (event.type == SCI_EVT_KEYBOARD)
+ cont = 0;
+
+ gfxop_usleep(s->gfx_state, 25000);
+ }
+
+
+ port->widfree(GFXW(port));
+ s->visual->draw(GFXW(s->visual), gfx_point(0,0));
+ gfxop_update(s->gfx_state);
+
+ }
+}
+
+
+static inline int
+_menu_go_down(state_t *s, int menu_nr, int item_nr)
+{
+ int seeker, max = s->menubar->menus[menu_nr].items_nr;
+ seeker = item_nr + 1;
+
+ while ((seeker < max) && !menubar_item_valid(s, menu_nr, seeker))
+ ++seeker;
+
+ if (seeker != max)
+ return seeker;
+ else return item_nr;
+}
+
+#define FULL_REDRAW \
+ s->visual->draw(GFXW(s->visual), gfx_point(0, 0)); \
+ gfxop_update(s->gfx_state);
+
+
+reg_t
+kMenuSelect(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ reg_t event = argv[0];
+ /*int pause_sound = UKPV_OR_ALT(1, 1);*/ /* FIXME: Do this eventually */
+ int claimed = 0;
+ int type = GET_SEL32V(event, type);
+ int message = GET_SEL32V(event, message);
+ int modifiers = GET_SEL32V(event, modifiers);
+ int menu_nr = -1, item_nr;
+ menu_item_t *item;
+ int menu_mode = 0; /* Menu is active */
+ int mouse_down = 0;
+
+ gfxop_set_clip_zone(s->gfx_state, gfx_rect_fullscreen);
+
+ /* Check whether we can claim the event directly as a keyboard or said event */
+ if (type & (SCI_EVT_KEYBOARD | SCI_EVT_SAID)) {
+ int menuc, itemc;
+
+ if ((type == SCI_EVT_KEYBOARD)
+ && (message == SCI_K_ESC))
+ menu_mode = 1;
+
+ else if ((type == SCI_EVT_SAID) || message) { /* Don't claim 0 keyboard event */
+ SCIkdebug(SCIkMENU,"Menu: Got %s event: %04x/%04x\n",
+ ((type == SCI_EVT_SAID)? "SAID":"KBD"), message, modifiers);
+
+ for (menuc = 0; menuc < s->menubar->menus_nr; menuc++)
+ for (itemc = 0; itemc < s->menubar->menus[menuc].items_nr; itemc++) {
+ item = s->menubar->menus[menuc].items + itemc;
+
+ SCIkdebug(SCIkMENU,"Menu: Checking against %s: %04x/%04x (type %d, %s)\n",
+ item->text? item->text : "--bar--", item->key, item->modifiers,
+ item->type, item->enabled? "enabled":"disabled");
+
+ if (((item->type == MENU_TYPE_NORMAL)
+ && (item->enabled))
+ && (((type == SCI_EVT_KEYBOARD) /* keyboard event */
+ && menubar_match_key(item, message, modifiers))
+ || ((type == SCI_EVT_SAID) /* Said event */
+ && (item->flags & MENU_ATTRIBUTE_FLAGS_SAID)
+ && (said(s, item->said, (s->debug_mode & (1 << SCIkPARSER_NR))) != SAID_NO_MATCH)
+ )
+ )
+ ) {
+ /* Claim the event */
+ SCIkdebug(SCIkMENU,"Menu: Event CLAIMED for %d/%d\n", menuc, itemc);
+ claimed = 1;
+ menu_nr = menuc;
+ item_nr = itemc;
+ }
+ }
+ }
+ }
+
+ if ((type == SCI_EVT_MOUSE_PRESS) && (s->gfx_state->pointer_pos.y < 10)) {
+ menu_mode = 1;
+ mouse_down = 1;
+ }
+
+ if (menu_mode) {
+ int old_item;
+ int old_menu;
+ gfxw_port_t *port = NULL;
+
+ item_nr = -1;
+
+ /* Default to menu 0, unless the mouse was used to generate this effect */
+ if (mouse_down)
+ menubar_map_pointer(s, &menu_nr, &item_nr, port);
+ else
+ menu_nr = 0;
+
+ sciw_set_menubar(s, s->titlebar_port, s->menubar, menu_nr);
+ FULL_REDRAW;
+
+ old_item = -1;
+ old_menu = -1;
+
+ while (menu_mode) {
+ sci_event_t event = gfxop_get_event(s->gfx_state, SCI_EVT_ANY);
+
+ claimed = 0;
+
+ switch (event.type) {
+ case SCI_EVT_QUIT:
+ quit_vm();
+ return NULL_REG;
+
+ case SCI_EVT_KEYBOARD:
+ switch (event.data) {
+
+ case '`':
+ if (event.buckybits & SCI_EVM_CTRL)
+ s->visual->print(GFXW(s->visual), 0);
+ break;
+
+ case SCI_K_ESC:
+ menu_mode = 0;
+ break;
+
+ case SCI_K_ENTER:
+ menu_mode = 0;
+ if ((item_nr >= 0) && (menu_nr >= 0))
+ claimed = 1;
+ break;
+
+ case SCI_K_LEFT:
+ if (menu_nr > 0)
+ --menu_nr;
+ else
+ menu_nr = s->menubar->menus_nr - 1;
+
+ item_nr = _menu_go_down(s, menu_nr, -1);
+ break;
+
+ case SCI_K_RIGHT:
+ if (menu_nr < (s->menubar->menus_nr - 1))
+ ++menu_nr;
+ else
+ menu_nr = 0;
+
+ item_nr = _menu_go_down(s, menu_nr, -1);
+ break;
+
+ case SCI_K_UP:
+ if (item_nr > -1) {
+
+ do { --item_nr; }
+ while ((item_nr > -1) && !menubar_item_valid(s, menu_nr, item_nr));
+ }
+ break;
+
+ case SCI_K_DOWN: {
+ item_nr = _menu_go_down(s, menu_nr, item_nr);
+ }
+ break;
+
+ }
+ break;
+
+ case SCI_EVT_MOUSE_RELEASE:
+ menu_mode = (s->gfx_state->pointer_pos.y < 10);
+ claimed = !menu_mode && !menubar_map_pointer(s, &menu_nr, &item_nr, port);
+ mouse_down = 0;
+ break;
+
+ case SCI_EVT_MOUSE_PRESS:
+ mouse_down = 1;
+ break;
+
+ case SCI_EVT_NONE:
+ gfxop_usleep(s->gfx_state, 2500);
+ break;
+ }
+
+ if (mouse_down)
+ menubar_map_pointer(s, &menu_nr, &item_nr, port);
+
+ if ((item_nr > -1 && old_item == -1) || (menu_nr != old_menu)) { /* Update menu */
+
+ sciw_set_menubar(s, s->titlebar_port, s->menubar, menu_nr);
+
+ if (port)
+ port->widfree(GFXW(port));
+
+ port = sciw_new_menu(s, s->titlebar_port, s->menubar, menu_nr);
+ s->wm_port->add(GFXWC(s->wm_port), GFXW(port));
+
+ if (item_nr > -1)
+ old_item = -42; /* Enforce redraw in next step */
+ else {
+ FULL_REDRAW;
+ }
+
+ } /* ...if the menu changed. */
+
+ /* Remove the active menu item, if neccessary */
+ if (item_nr != old_item) {
+ port = sciw_unselect_item(s, port, s->menubar->menus + menu_nr, old_item);
+ port = sciw_select_item(s, port, s->menubar->menus + menu_nr, item_nr);
+ FULL_REDRAW;
+ }
+
+ old_item = item_nr;
+ old_menu = menu_nr;
+
+ } /* while (menu_mode) */
+
+ if (port) {
+ port->widfree(GFXW(port));
+ port = NULL;
+
+ sciw_set_status_bar(s, s->titlebar_port, s->status_bar_text, s->status_bar_foreground, s->status_bar_background);
+ gfxop_update(s->gfx_state);
+ }
+ FULL_REDRAW;
+ }
+
+ if (claimed) {
+ PUT_SEL32(event, claimed, make_reg(0, 1));
+
+ if (menu_nr > -1) {
+ s->r_acc = make_reg(0, ((menu_nr + 1) << 8) | (item_nr + 1));
+#ifdef MENU_FREESCI_BLATANT_PLUG
+ if (s->menubar->menus[menu_nr].items[item_nr].flags == MENU_FREESCI_BLATANT_PLUG)
+ about_freesci(s);
+#endif
+
+ } else
+ s->r_acc = NULL_REG;
+
+ SCIkdebug(SCIkMENU, "Menu: Claim -> %04x\n", s->r_acc.offset);
+ }
+ else s->r_acc = NULL_REG; /* Not claimed */
+
+ return s->r_acc;
+}
diff --git a/engines/sci/engine/kmovement.c b/engines/sci/engine/kmovement.c
new file mode 100644
index 0000000000..5f7d1a7281
--- /dev/null
+++ b/engines/sci/engine/kmovement.c
@@ -0,0 +1,580 @@
+/***************************************************************************
+ kmovement.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 <engine.h>
+
+/*
+Compute "velocity" vector (xStep,yStep)=(vx,vy) for a jump from (0,0) to (dx,dy), with gravity gy.
+The gravity is assumed to be non-negative.
+
+If this was ordinary continuous physics, we would compute the desired (floating point!)
+velocity vector (vx,vy) as follows, under the assumption that vx and vy are linearly correlated
+by some constant factor c, i.e. vy = c * vx:
+ dx = t * vx
+ dy = t * vy + gy * t^2 / 2
+=> dy = c * dx + gy * (dx/vx)^2 / 2
+=> |vx| = sqrt( gy * dx^2 / (2 * (dy - c * dx)) )
+Here, the sign of vx must be chosen equal to the sign of dx, obviously.
+
+Clearly, this square root only makes sense in our context if the denominator is positive,
+or equivalently, (dy - c * dx) must be positive. For simplicity and by symmetry
+along the x-axis, we assume dx to be positive for all computations, and only adjust for
+its sign in the end. Switching the sign of c appropriately, we set tmp := (dy + c * dx)
+and compute c so that this term becomes positive.
+
+Remark #1: If the jump is straight up, i.e. dx == 0, then we should not assume the above
+linear correlation vy = c * vx of the velocities (as vx will be 0, but vy shouldn't be,
+unless we drop).
+
+
+Remark #2: We are actually in a discrete setup. The motion is computed iteratively: each iteration,
+we add vx and vy to the position, then add gy to vy. So the real formula is the following
+(where t is ideally close to an int):
+
+ dx = t * vx
+ dy = t * vy + gy * t*(t-1) / 2
+
+But the solution resulting from that is a lot more complicated, so we use the above approximation instead.
+
+Still, what we compute in the end is of course not a real velocity anymore, but an integer approximation,
+used in an iterative stepping algorithm
+*/
+reg_t
+kSetJump(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ // Input data
+ reg_t object = argv[0];
+ int dx = SKPV(1);
+ int dy = SKPV(2);
+ int gy = SKPV(3);
+
+ // Derived data
+ int c;
+ int tmp;
+ int vx = 0; // x velocity
+ int vy = 0; // y velocity
+
+ int dxWasNegative = (dx < 0);
+ dx = abs(dx);
+
+ assert(gy >= 0);
+
+ if (dx == 0) {
+ // Upward jump. Value of c doesn't really matter
+ c = 1;
+ } else {
+ // Compute a suitable value for c respectively tmp.
+ // The important thing to consider here is that we want the resulting
+ // *discrete* x/y velocities to be not-too-big integers, for a smooth
+ // curve (i.e. we could just set vx=dx, vy=dy, and be done, but that
+ // is hardly what you would call a parabolic jump, would ya? ;-).
+ //
+ // So, we make sure that 2.0*tmp will be bigger than dx (that way,
+ // we ensure vx will be less than sqrt(gy * dx)).
+ if (dx + dy < 0) {
+ // dy is negative and |dy| > |dx|
+ c = (2*abs(dy)) / dx;
+ //tmp = abs(dy); // ALMOST the resulting value, except for obvious rounding issues
+ } else {
+ // dy is either positive, or |dy| <= |dx|
+ c = (dx*3/2 - dy) / dx;
+
+ // We force c to be strictly positive
+ if (c < 1)
+ c = 1;
+
+ //tmp = dx*3/2; // ALMOST the resulting value, except for obvious rounding issues
+
+ // FIXME: Where is the 3 coming from? Maybe they hard/coded, by "accident", that usually gy=3 ?
+ // Then this choice of will make t equal to roughly sqrt(dx)
+ }
+ }
+ // POST: c >= 1
+ tmp = c * dx + dy;
+ // POST: (dx != 0) ==> abs(tmp) > abs(dx)
+ // POST: (dx != 0) ==> abs(tmp) ~>=~ abs(dy)
+
+
+ SCIkdebug(SCIkBRESEN, "c: %d, tmp: %d\n", c, tmp);
+
+ // Compute x step
+ if (tmp != 0)
+ vx = (int)(dx * sqrt(gy / (2.0 * tmp)));
+ else
+ vx = 0;
+
+ // Restore the left/right direction: dx and vx should have the same sign.
+ if (dxWasNegative)
+ vx = -vx;
+
+ if ((dy < 0) && (vx == 0)) {
+ // Special case: If this was a jump (almost) straight upward, i.e. dy < 0 (upward),
+ // and vx == 0 (i.e. no horizontal movement, at least not after rounding), then we
+ // compute vy directly.
+ // For this, we drop the assumption on the linear correlation of vx and vy (obviously).
+
+ // FIXME: This choice of vy makes t roughly (2+sqrt(2))/gy * sqrt(dy);
+ // so if gy==3, then t is roughly sqrt(dy)...
+ vy = (int)sqrt(gy * abs(2 * dy)) + 1;
+ } else {
+ // As stated above, the vertical direction is correlated to the horizontal by the
+ // (non-zero) factor c.
+ // Strictly speaking, we should probably be using the value of vx *before* rounding
+ // it to an integer... Ah well
+ vy = c * vx;
+ }
+
+ // Always force vy to be upwards
+ vy = -abs(vy);
+
+ SCIkdebug(SCIkBRESEN, "SetJump for object at "PREG"\n", PRINT_REG(object));
+ SCIkdebug(SCIkBRESEN, "xStep: %d, yStep: %d\n", vx, vy);
+
+ PUT_SEL32V(object, xStep, vx);
+ PUT_SEL32V(object, yStep, vy);
+
+ return s->r_acc;
+}
+
+#define _K_BRESEN_AXIS_X 0
+#define _K_BRESEN_AXIS_Y 1
+
+void
+initialize_bresen(state_t *s, int funct_nr, int argc, reg_t *argv, reg_t mover, int step_factor,
+ int deltax, int deltay)
+{
+ reg_t client = GET_SEL32(mover, client);
+ int stepx = GET_SEL32SV(client, xStep) * step_factor;
+ int stepy = GET_SEL32SV(client, yStep) * step_factor;
+ int numsteps_x = stepx? (abs(deltax) + stepx-1) / stepx : 0;
+ int numsteps_y = stepy? (abs(deltay) + stepy-1) / stepy : 0;
+ int bdi, i1;
+ int numsteps;
+ int deltax_step;
+ int deltay_step;
+
+ if (numsteps_x > numsteps_y) {
+ numsteps = numsteps_x;
+ deltax_step = (deltax < 0)? -stepx : stepx;
+ deltay_step = numsteps? deltay / numsteps : deltay;
+ } else { /* numsteps_x <= numsteps_y */
+ numsteps = numsteps_y;
+ deltay_step = (deltay < 0)? -stepy : stepy;
+ deltax_step = numsteps? deltax / numsteps : deltax;
+ }
+
+ /* if (abs(deltax) > abs(deltay)) {*/ /* Bresenham on y */
+ if (numsteps_y < numsteps_x) {
+
+ PUT_SEL32V(mover, b_xAxis, _K_BRESEN_AXIS_Y);
+ PUT_SEL32V(mover, b_incr, (deltay < 0)? -1 : 1);
+ /*
+ i1 = 2 * (abs(deltay) - abs(deltay_step * numsteps)) * abs(deltax_step);
+ bdi = -abs(deltax);
+ */
+ i1 = 2*(abs(deltay) - abs(deltay_step * (numsteps - 1))) * abs(deltax_step);
+ bdi = -abs(deltax);
+
+ } else { /* Bresenham on x */
+
+ PUT_SEL32V(mover, b_xAxis, _K_BRESEN_AXIS_X);
+ PUT_SEL32V(mover, b_incr, (deltax < 0)? -1 : 1);
+ /*
+ i1= 2 * (abs(deltax) - abs(deltax_step * numsteps)) * abs(deltay_step);
+ bdi = -abs(deltay);
+ */
+ i1 = 2*(abs(deltax) - abs(deltax_step * (numsteps - 1))) * abs(deltay_step);
+ bdi = -abs(deltay);
+
+ }
+
+ PUT_SEL32V(mover, dx, deltax_step);
+ PUT_SEL32V(mover, dy, deltay_step);
+
+ SCIkdebug(SCIkBRESEN, "Init bresen for mover "PREG": d=(%d,%d)\n", PRINT_REG(mover), deltax, deltay);
+ SCIkdebug(SCIkBRESEN, " steps=%d, mv=(%d, %d), i1= %d, i2=%d\n",
+ numsteps, deltax_step, deltay_step, i1, bdi*2);
+
+/* PUT_SEL32V(mover, b_movCnt, numsteps); *//* Needed for HQ1/Ogre? */
+ PUT_SEL32V(mover, b_di, bdi);
+ PUT_SEL32V(mover, b_i1, i1);
+ PUT_SEL32V(mover, b_i2, bdi * 2);
+
+}
+
+reg_t
+kInitBresen(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ reg_t mover = argv[0];
+ reg_t client = GET_SEL32(mover, client);
+
+ int deltax = GET_SEL32SV(mover, x) - GET_SEL32SV(client, x);
+ int deltay = GET_SEL32SV(mover, y) - GET_SEL32SV(client, y);
+
+ initialize_bresen(s, funct_nr, argc, argv, mover, KP_UINT(KP_ALT(1, make_reg(0, 1))), deltax, deltay);
+
+ return s->r_acc;
+}
+
+
+#define MOVING_ON_X (((axis == _K_BRESEN_AXIS_X)&&bi1) || dx)
+#define MOVING_ON_Y (((axis == _K_BRESEN_AXIS_Y)&&bi1) || dy)
+
+static enum {
+ IGNORE_MOVECNT,
+ INCREMENT_MOVECNT,
+ UNINITIALIZED
+} handle_movecnt = UNINITIALIZED;
+
+int parse_reg_t(state_t *s, char *str, reg_t *dest); /* In scriptconsole.c */
+
+static int
+checksum_bytes(byte *data, int size)
+{
+ int result = 0;
+ int i;
+
+ for (i = 0; i < size; i++)
+ {
+ result += *data;
+ data++;
+ }
+
+ return result;
+}
+
+static void
+bresenham_autodetect(state_t *s)
+{
+ reg_t motion_class;
+
+ if (!parse_reg_t(s, "?Motion", &motion_class))
+ {
+ object_t *obj = obj_get(s, motion_class);
+ reg_t fptr;
+ byte *buf;
+
+ if (obj == NULL)
+ {
+ SCIkwarn(SCIkWARNING,"bresenham_autodetect failed!");
+ handle_movecnt = INCREMENT_MOVECNT; /* Most games do this, so best guess */
+ return;
+ }
+
+ if (lookup_selector(s, motion_class, s->selector_map.doit, NULL, &fptr) != SELECTOR_METHOD)
+ {
+ SCIkwarn(SCIkWARNING,"bresenham_autodetect failed!");
+ handle_movecnt = INCREMENT_MOVECNT; /* Most games do this, so best guess */
+ return;
+ }
+
+ buf = s->seg_manager.heap[fptr.segment]->data.script.buf + fptr.offset;
+ handle_movecnt = (SCI_VERSION_MAJOR(s->version) == 0 ||
+ checksum_bytes(buf, 8) == 0x216) ? INCREMENT_MOVECNT : IGNORE_MOVECNT;
+ sciprintf("b-moveCnt action based on checksum: %s\n", handle_movecnt == IGNORE_MOVECNT ?
+ "ignore" : "increment");
+ } else
+ {
+ SCIkwarn(SCIkWARNING,"bresenham_autodetect failed!");
+ handle_movecnt = INCREMENT_MOVECNT; /* Most games do this, so best guess */
+ }
+}
+
+reg_t
+kDoBresen(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ reg_t mover = argv[0];
+ reg_t client = GET_SEL32(mover, client);
+
+ int x = GET_SEL32SV(client, x);
+ int y = GET_SEL32SV(client, y);
+ int oldx, oldy, destx, desty, dx, dy, bdi, bi1, bi2, movcnt, bdelta, axis;
+ word signal = GET_SEL32V(client, signal);
+ int completed = 0;
+ int max_movcnt = GET_SEL32V(client, moveSpeed);
+
+ if (SCI_VERSION_MAJOR(s->version)>0)
+ signal&=~_K_VIEW_SIG_FLAG_HIT_OBSTACLE;
+
+ if (handle_movecnt == UNINITIALIZED)
+ bresenham_autodetect(s);
+
+ PUT_SEL32(client, signal, make_reg(0, signal)); /* This is a NOP for SCI0 */
+ oldx = x;
+ oldy = y;
+ destx = GET_SEL32SV(mover, x);
+ desty = GET_SEL32SV(mover, y);
+ dx = GET_SEL32SV(mover, dx);
+ dy = GET_SEL32SV(mover, dy);
+ bdi = GET_SEL32SV(mover, b_di);
+ bi1 = GET_SEL32SV(mover, b_i1);
+ bi2 = GET_SEL32SV(mover, b_i2);
+ movcnt = GET_SEL32V(mover, b_movCnt);
+ bdelta = GET_SEL32SV(mover, b_incr);
+ axis = GET_SEL32SV(mover, b_xAxis);
+
+// sciprintf("movecnt %d, move speed %d\n", movcnt, max_movcnt);
+
+ if (handle_movecnt)
+ {
+ if (max_movcnt > movcnt)
+ {
+ ++movcnt;
+ PUT_SEL32V(mover, b_movCnt, movcnt); /* Needed for HQ1/Ogre? */
+ return NULL_REG;
+ }
+ else
+ {
+ movcnt = 0;
+ PUT_SEL32V(mover, b_movCnt, movcnt); /* Needed for HQ1/Ogre? */
+ }
+ }
+
+ if ((bdi += bi1) > 0) {
+ bdi += bi2;
+
+ if (axis == _K_BRESEN_AXIS_X)
+ dx += bdelta;
+ else
+ dy += bdelta;
+ }
+
+ PUT_SEL32V(mover, b_di, bdi);
+
+ x += dx;
+ y += dy;
+
+ if ((MOVING_ON_X
+ && (((x < destx) && (oldx >= destx)) /* Moving left, exceeded? */
+ ||
+ ((x > destx) && (oldx <= destx)) /* Moving right, exceeded? */
+ ||
+ ((x == destx) && (abs(dx) > abs(dy))) /* Moving fast, reached? */
+ /* Treat this last case specially- when doing sub-pixel movements
+ ** on the other axis, we could still be far away from the destination */
+ )
+ )
+ || (MOVING_ON_Y
+ && (((y < desty) && (oldy >= desty)) /* Moving upwards, exceeded? */
+ ||
+ ((y > desty) && (oldy <= desty)) /* Moving downwards, exceeded? */
+ ||
+ ((y == desty) && (abs(dy) >= abs(dx))) /* Moving fast, reached? */
+ )
+ )
+ )
+ /* Whew... in short: If we have reached or passed our target position */
+ {
+ x = destx;
+ y = desty;
+ completed = 1;
+
+ SCIkdebug(SCIkBRESEN, "Finished mover "PREG"\n", PRINT_REG(mover));
+ }
+
+
+ PUT_SEL32V(client, x, x);
+ PUT_SEL32V(client, y, y);
+
+ SCIkdebug(SCIkBRESEN, "New data: (x,y)=(%d,%d), di=%d\n", x, y, bdi);
+
+ if (s->version >= SCI_VERSION_FTU_INVERSE_CANBEHERE)
+ invoke_selector(INV_SEL(client, cantBeHere, 0), 0); else
+ invoke_selector(INV_SEL(client, canBeHere, 0), 0);
+
+ s->r_acc = not_register(s, s->r_acc);
+
+ if (!s->r_acc.offset) { /* Contains the return value */
+
+ signal = GET_SEL32V(client, signal);
+
+ PUT_SEL32V(client, x, oldx);
+ PUT_SEL32V(client, y, oldy);
+
+ PUT_SEL32V(client, signal, (signal | _K_VIEW_SIG_FLAG_HIT_OBSTACLE));
+
+ SCIkdebug(SCIkBRESEN, "Finished mover "PREG" by collision\n", PRINT_REG(mover));
+ completed = 1;
+ }
+
+ if (SCI_VERSION_MAJOR(s->version)>0)
+ if (completed)
+ invoke_selector(INV_SEL(mover, moveDone, 0), 0);
+
+ return make_reg(0, completed);
+}
+
+extern void
+_k_dirloop(reg_t obj, word angle, state_t *s, int funct_nr,
+ int argc, reg_t *argv);
+/* From kgraphics.c, used as alternative looper */
+
+int
+is_heap_object(state_t *s, reg_t pos);
+/* From kscripts.c */
+
+extern int
+get_angle(int xrel, int yrel);
+/* from kmath.c, used for calculating angles */
+
+
+reg_t
+kDoAvoider(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ reg_t avoider = argv[0];
+ reg_t client, looper, mover;
+ int angle;
+ int dx, dy;
+ int destx, desty;
+
+
+ s->r_acc = make_reg(0, -1);
+
+ if (!is_heap_object(s, avoider)) {
+ SCIkwarn(SCIkWARNING, "DoAvoider() where avoider "PREG" is not an object\n", PRINT_REG(avoider));
+ return NULL_REG;
+ }
+
+ client = GET_SEL32(avoider, client);
+
+ if (!is_heap_object(s, client)) {
+ SCIkwarn(SCIkWARNING, "DoAvoider() where client "PREG" is not an object\n", PRINT_REG(client));
+ return NULL_REG;
+ }
+
+ looper = GET_SEL32(client, looper);
+
+ mover = GET_SEL32(client, mover);
+
+ if (!is_heap_object(s, mover)) {
+ if (mover.segment) {
+ SCIkwarn(SCIkWARNING, "DoAvoider() where mover "PREG" is not an object\n", PRINT_REG(mover));
+ }
+ return s->r_acc;
+ }
+
+ destx = GET_SEL32V(mover, x);
+ desty = GET_SEL32V(mover, y);
+
+ SCIkdebug(SCIkBRESEN, "Doing avoider %04x (dest=%d,%d)\n", avoider, destx, desty);
+
+ if (invoke_selector(INV_SEL(mover, doit, 1) , 0)) {
+ SCIkwarn(SCIkERROR, "Mover "PREG" of avoider "PREG
+ " doesn't have a doit() funcselector\n",
+ PRINT_REG(mover), PRINT_REG(avoider));
+ return NULL_REG;
+ }
+
+ mover = GET_SEL32(client, mover);
+ if (!mover.segment) /* Mover has been disposed? */
+ return s->r_acc; /* Return gracefully. */
+
+ if (invoke_selector(INV_SEL(client, isBlocked, 1) , 0)) {
+ SCIkwarn(SCIkERROR, "Client "PREG" of avoider "PREG" doesn't"
+ " have an isBlocked() funcselector\n", PRINT_REG(client), PRINT_REG(avoider));
+ return NULL_REG;
+ }
+
+ dx = destx - GET_SEL32V(client, x);
+ dy = desty - GET_SEL32V(client, y);
+ angle = get_angle(dx, dy);
+
+ SCIkdebug(SCIkBRESEN, "Movement (%d,%d), angle %d is %sblocked\n",
+ dx, dy, angle, (s->r_acc.offset)? " ": "not ");
+
+ if (s->r_acc.offset) { /* isBlocked() returned non-zero */
+ int rotation = (rand() & 1)? 45 : (360-45); /* Clockwise/counterclockwise */
+ int oldx = GET_SEL32V(client, x);
+ int oldy = GET_SEL32V(client, y);
+ int xstep = GET_SEL32V(client, xStep);
+ int ystep = GET_SEL32V(client, yStep);
+ int moves;
+
+ SCIkdebug(SCIkBRESEN, " avoider "PREG"\n", PRINT_REG(avoider));
+
+ for (moves = 0; moves < 8; moves++) {
+ int move_x = (int) (sin(angle * PI / 180.0) * (xstep));
+ int move_y = (int) (-cos(angle * PI / 180.0) * (ystep));
+
+ PUT_SEL32V(client, x, oldx + move_x);
+ PUT_SEL32V(client, y, oldy + move_y);
+
+ SCIkdebug(SCIkBRESEN, "Pos (%d,%d): Trying angle %d; delta=(%d,%d)\n",
+ oldx, oldy, angle, move_x, move_y);
+
+ if (invoke_selector(INV_SEL(client, canBeHere, 1) , 0)) {
+ SCIkwarn(SCIkERROR, "Client "PREG" of avoider "PREG" doesn't"
+ " have a canBeHere() funcselector\n",
+ PRINT_REG(client), PRINT_REG(avoider));
+ return NULL_REG;
+ }
+
+ PUT_SEL32V(client, x, oldx);
+ PUT_SEL32V(client, y, oldy);
+
+ if (s->r_acc.offset) { /* We can be here */
+ SCIkdebug(SCIkBRESEN, "Success\n");
+ PUT_SEL32V(client, heading, angle);
+
+ return make_reg(0, angle);
+ }
+
+ angle += rotation;
+
+ if (angle > 360)
+ angle -= 360;
+ }
+
+ SCIkwarn(SCIkWARNING, "DoAvoider failed for avoider "PREG"\n",
+ PRINT_REG(avoider));
+
+ } else {
+ int heading = GET_SEL32V(client, heading);
+
+ if (heading == -1)
+ return s->r_acc; /* No change */
+
+ PUT_SEL32V(client, heading, angle);
+
+ s->r_acc = make_reg(0, angle);
+
+ if (looper.segment) {
+ if (invoke_selector(INV_SEL(looper, doit, 1), 2, angle, client)) {
+ SCIkwarn(SCIkERROR, "Looper "PREG" of avoider "PREG" doesn't"
+ " have a doit() funcselector\n",
+ PRINT_REG(looper), PRINT_REG(avoider));
+ } else return s->r_acc;
+ } else
+ /* No looper? Fall back to DirLoop */
+
+ _k_dirloop(client, (word)angle, s, funct_nr, argc, argv);
+ }
+
+ return s->r_acc;
+}
+
diff --git a/engines/sci/engine/kpathing.c b/engines/sci/engine/kpathing.c
new file mode 100644
index 0000000000..aab4986462
--- /dev/null
+++ b/engines/sci/engine/kpathing.c
@@ -0,0 +1,1734 @@
+/***************************************************************************
+ kpathing.c Copyright (C) 2002-2006 Lars Skovlund, 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]
+
+***************************************************************************/
+
+/* Detailed information on the implementation can be found in the report
+** which can be downloaded from FIXME.
+*/
+
+#include <math.h>
+
+#include <engine.h>
+#include <aatree.h>
+#include <list.h>
+
+#define POLY_LAST_POINT 0x7777
+#define POLY_POINT_SIZE 4
+
+#define POLY_GET_POINT(p, i, x, y) do { x = getInt16((p) + (i) * POLY_POINT_SIZE); \
+ y = getInt16((p) + 2 + (i) * POLY_POINT_SIZE); \
+} while (0)
+#define POLY_SET_POINT(p, i, x, y) do { putInt16((p) + (i) * POLY_POINT_SIZE, x); \
+ putInt16((p) + 2 + (i) * POLY_POINT_SIZE, y); \
+} while (0)
+#define POLY_GET_POINT_REG_T(p, i, x, y) do { x = KP_SINT((p)[(i) * 2]); \
+ y = KP_SINT((p)[(i) * 2 + 1]); \
+} while (0)
+
+/* SCI-defined polygon types */
+#define POLY_TOTAL_ACCESS 0
+#define POLY_NEAREST_ACCESS 1
+#define POLY_BARRED_ACCESS 2
+#define POLY_CONTAINED_ACCESS 3
+
+/* Polygon containment types */
+#define CONT_OUTSIDE 0
+#define CONT_ON_EDGE 1
+#define CONT_INSIDE 2
+
+#define POINT_EQUAL(A, B) (((A).x == (B).x) && ((A).y == (B).y))
+
+#define HUGE_DISTANCE 1e10
+
+/* Visibility matrix */
+#define VIS_MATRIX_ROW_SIZE(N) (((N) / 8) + ((N) % 8 ? 1 : 0))
+#define SET_VISIBLE(S, P, Q) ((S)->vis_matrix)[(P) * VIS_MATRIX_ROW_SIZE((S)->vertices) \
+ + (Q) / 8] |= (1 << ((Q) % 8))
+#define IS_VISIBLE(S, P, Q) (((S)->vis_matrix[(P) * VIS_MATRIX_ROW_SIZE((S)->vertices) \
+ + (Q) / 8] & (1 << ((Q) % 8))) != 0)
+
+#define VERTEX_HAS_EDGES(V) ((V) != CLIST_NEXT(V, entries))
+
+/* Error codes */
+#define PF_OK 0
+#define PF_ERROR -1
+#define PF_FATAL -2
+
+/* Floating point struct */
+typedef struct pointf
+{
+ float x, y;
+} pointf_t;
+
+pointf_t
+pointf(float x, float y)
+{
+ pointf_t p;
+
+ p.x = x;
+ p.y = y;
+
+ return p;
+}
+
+pointf_t
+to_pointf(point_t p)
+{
+ return pointf(p.x, p.y);
+}
+
+typedef struct vertex
+{
+ /* Location */
+ point_t v;
+
+ /* Index */
+ int idx;
+
+ /* Vertex list entry */
+ CLIST_ENTRY(vertex) entries;
+
+ /* Dijkstra list entry */
+ LIST_ENTRY(vertex) dijkstra;
+
+ /* Distance from starting vertex */
+ float dist;
+
+ /* Previous vertex in shortest path */
+ struct vertex *path_prev;
+} vertex_t;
+
+typedef CLIST_HEAD(vertices_head, vertex) vertices_head_t;
+
+typedef struct polygon
+{
+ /* Circular list of vertices */
+ vertices_head_t vertices;
+
+ /* Polygon list entry */
+ LIST_ENTRY(polygon) entries;
+
+ /* SCI polygon type */
+ int type;
+} polygon_t;
+
+/* Pathfinding state */
+typedef struct pf_state
+{
+ /* List of all polygons */
+ LIST_HEAD(polygons_head, polygon) polygons;
+
+ /* Original start and end points */
+ point_t start, end;
+
+ /* Flags for adding original points to final path */
+ char keep_start, keep_end;
+
+ /* Start and end points for pathfinding */
+ vertex_t *vertex_start, *vertex_end;
+
+ /* Array to quickly find vertices by idx */
+ vertex_t **vertex_index;
+
+ /* Visibility matrix */
+ char *vis_matrix;
+
+ /* Total number of vertices */
+ int vertices;
+} pf_state_t;
+
+static vertex_t *vertex_cur;
+
+/* Temporary hack to deal with points in reg_ts */
+static int
+polygon_is_reg_t(unsigned char *list, int size)
+{
+ int i;
+
+ /* Check the first three reg_ts */
+ for (i = 0; i < (size < 3 ? size : 3); i++)
+ if ((((reg_t *) list) + i)->segment)
+ /* Non-zero segment, cannot be reg_ts */
+ return 0;
+
+ /* First three segments were zero, assume reg_ts */
+ return 1;
+}
+
+static point_t
+read_point(unsigned char *list, int is_reg_t, int offset)
+{
+ point_t point;
+
+ if (!is_reg_t) {
+ POLY_GET_POINT(list, offset, point.x, point.y);
+ } else {
+ POLY_GET_POINT_REG_T((reg_t *) list, offset, point.x, point.y);
+ }
+
+ return point;
+}
+
+
+ /*** Debug functions ***/
+
+static void
+draw_line(state_t *s, point_t p1, point_t p2, int type)
+{
+ /* Colors for polygon debugging.
+ ** Green: Total access
+ ** Red : Barred access
+ ** Blue: Near-point access
+ ** Yellow: Contained access
+ */
+ int poly_colors[][3] = {{0, 255, 0}, {0, 0, 255}, {255, 0, 0}, {255, 255, 0}};
+ gfx_color_t col;
+ gfxw_list_t *decorations = s->picture_port->decorations;
+ gfxw_primitive_t *line;
+
+ col.visual.global_index = GFX_COLOR_INDEX_UNMAPPED;
+ col.visual.r = poly_colors[type][0];
+ col.visual.g = poly_colors[type][1];
+ col.visual.b = poly_colors[type][2];
+ col.alpha = 0;
+ col.priority = -1;
+ col.control = 0;
+ col.mask = GFX_MASK_VISUAL | GFX_MASK_PRIORITY;
+
+ p1.y += 10;
+ p2.y += 10;
+
+ line = gfxw_new_line(p1, p2, col, GFX_LINE_MODE_CORRECT, GFX_LINE_STYLE_NORMAL);
+ decorations->add((gfxw_container_t *) decorations, (gfxw_widget_t *) line);
+}
+
+static void
+draw_point(state_t *s, point_t p, int start)
+{
+ /* Colors for starting and end point
+ ** Green: End point
+ ** Blue: Starting point
+ */
+ int point_colors[][3] = {{0, 255, 0}, {0, 0, 255}};
+ gfx_color_t col;
+ gfxw_list_t *decorations = s->picture_port->decorations;
+ gfxw_box_t *box;
+
+ col.visual.global_index = GFX_COLOR_INDEX_UNMAPPED;
+ col.visual.r = point_colors[start][0];
+ col.visual.g = point_colors[start][1];
+ col.visual.b = point_colors[start][2];
+ col.alpha = 0;
+ col.priority = -1;
+ col.control = 0;
+ col.mask = GFX_MASK_VISUAL | GFX_MASK_PRIORITY;
+
+ box = gfxw_new_box(s->gfx_state, gfx_rect(p.x - 1, p.y - 1 + 10, 3, 3), col, col, GFX_BOX_SHADE_FLAT);
+ decorations->add((gfxw_container_t *) decorations, (gfxw_widget_t *) box);
+}
+
+static void
+draw_polygon(state_t *s, reg_t polygon)
+{
+ reg_t points = GET_SEL32(polygon, points);
+ int size = KP_UINT(GET_SEL32(polygon, size));
+ int type = KP_UINT(GET_SEL32(polygon, type));
+ point_t first, prev;
+ unsigned char *list = kernel_dereference_bulk_pointer(s, points, size * POLY_POINT_SIZE);
+ int is_reg_t = polygon_is_reg_t(list, size);
+ int i;
+
+ prev = first = read_point(list, is_reg_t, 0);
+
+ for (i = 1; i < size; i++) {
+ point_t point = read_point(list, is_reg_t, i);
+ draw_line(s, prev, point, type);
+ prev = point;
+ }
+
+ draw_line(s, prev, first, type % 3);
+}
+
+static void
+draw_input(state_t *s, reg_t poly_list, point_t start, point_t end, int opt)
+{
+ list_t *list;
+ node_t *node;
+
+ draw_point(s, start, 1);
+ draw_point(s, end, 0);
+
+ if (!poly_list.segment)
+ return;
+
+ list = LOOKUP_LIST(poly_list);
+
+ if (!list) {
+ SCIkwarn(SCIkWARNING, "Could not obtain polygon list\n");
+ return;
+ }
+
+ node = LOOKUP_NODE(list->first);
+
+ while (node) {
+ draw_polygon(s, node->value);
+ node = LOOKUP_NODE(node->succ);
+ }
+}
+
+static void
+print_polygon(state_t *s, reg_t polygon)
+{
+ reg_t points = GET_SEL32(polygon, points);
+ int size = KP_UINT(GET_SEL32(polygon, size));
+ int type = KP_UINT(GET_SEL32(polygon, type));
+ int i;
+ unsigned char *point_array = kernel_dereference_bulk_pointer(s, points, size * POLY_POINT_SIZE);
+ int is_reg_t = polygon_is_reg_t(point_array, size);
+ point_t point;
+
+ sciprintf("%i:", type);
+
+ for (i = 0; i < size; i++) {
+ point = read_point(point_array, is_reg_t, i);
+ sciprintf(" (%i, %i)", point.x, point.y);
+ }
+
+ point = read_point(point_array, is_reg_t, 0);
+ sciprintf(" (%i, %i);\n", point.x, point.y);
+}
+
+static void
+print_input(state_t *s, reg_t poly_list, point_t start, point_t end, int opt)
+{
+ list_t *list;
+ node_t *node;
+
+ sciprintf("Start point: (%i, %i)\n", start.x, start.y);
+ sciprintf("End point: (%i, %i)\n", end.x, end.y);
+ sciprintf("Optimization level: %i\n", opt);
+
+ if (!poly_list.segment)
+ return;
+
+ list = LOOKUP_LIST(poly_list);
+
+ if (!list) {
+ SCIkwarn(SCIkWARNING, "Could not obtain polygon list\n");
+ return;
+ }
+
+ sciprintf("Polygons:\n");
+ node = LOOKUP_NODE(list->first);
+
+ while (node) {
+ print_polygon(s, node->value);
+ node = LOOKUP_NODE(node->succ);
+ }
+}
+
+
+ /*** Basic geometry functions ***/
+
+static int
+area(point_t a, point_t b, point_t c)
+/* Computes the area of a triangle
+** Parameters: (point_t) a, b, c: The points of the triangle
+** Returns : (int) The area multiplied by two
+*/
+{
+ return (b.x - a.x) * (a.y - c.y) - (c.x - a.x) * (a.y - b.y);
+}
+
+static int
+left(point_t a, point_t b, point_t c)
+/* Determines whether or not a point is to the left of a directed line
+** Parameters: (point_t) a, b: The directed line (a, b)
+** (point_t) c: The query point
+** Returns : (int) 1 if c is to the left of (a, b), 0 otherwise
+*/
+{
+ return area(a, b, c) > 0;
+}
+
+static int
+left_on(point_t a, point_t b, point_t c)
+/* Determines whether or not a point is to the left of or collinear with a
+** directed line
+** Parameters: (point_t) a, b: The directed line (a, b)
+** (point_t) c: The query point
+** Returns : (int) 1 if c is to the left of or collinear with (a, b), 0
+** otherwise
+*/
+{
+ return area(a, b, c) >= 0;
+}
+
+static int
+collinear(point_t a, point_t b, point_t c)
+/* Determines whether or not three points are collinear
+** Parameters: (point_t) a, b, c: The three points
+** Returns : (int) 1 if a, b, and c are collinear, 0 otherwise
+*/
+{
+ return area(a, b, c) == 0;
+}
+
+static int
+between(point_t a, point_t b, point_t c)
+/* Determines whether or not a point lies on a line segment
+** Parameters: (point_t) a, b: The line segment (a, b)
+** (point_t) c: The query point
+** Returns : (int) 1 if c lies on (a, b), 0 otherwise
+*/
+{
+ if (!collinear(a, b, c))
+ return 0;
+
+ /* Assumes a != b. */
+ if (a.x != b.x)
+ return ((a.x <= c.x) && (c.x <= b.x)) || ((a.x >= c.x) && (c.x >= b.x));
+ else
+ return ((a.y <= c.y) && (c.y <= b.y)) || ((a.y >= c.y) && (c.y >= b.y));
+}
+
+static int
+intersect_proper(point_t a, point_t b, point_t c, point_t d)
+/* Determines whether or not two line segments properly intersect
+** Parameters: (point_t) a, b: The line segment (a, b)
+** (point_t) c, d: The line segment (c, d)
+** Returns : (int) 1 if (a, b) properly intersects (c, d), 0 otherwise
+*/
+{
+ int ab = (left(a, b, c) && left(b, a, d))
+ || (left(a, b, d) && left(b, a, c));
+ int cd = (left(c, d, a) && left(d, c, b))
+ || (left(c, d, b) && left(d, c, a));
+
+ return ab && cd;
+}
+
+static int
+intersect(point_t a, point_t b, point_t c, point_t d)
+/* Determines whether or not two line segments intersect
+** Parameters: (point_t) a, b: The line segment (a, b)
+** (point_t) c, d: The line segment (c, d)
+** Returns : (int) 1 if (a, b) intersects (c, d), 0 otherwise
+*/
+{
+ if (intersect_proper(a, b, c, d))
+ return 1;
+
+ return between(a, b, c) || between(a, b, d)
+ || between (c, d, a) || between(c, d, b);
+}
+
+
+ /*** Pathfinding ***/
+
+static vertex_t *
+vertex_new(point_t p)
+/* Allocates and initialises a new vertex
+** Parameters: (point_t) p: The position of the vertex
+** Returns : (vertex_t *) A newly allocated vertex
+*/
+{
+ vertex_t *vertex = (vertex_t*)sci_malloc(sizeof(vertex_t));
+
+ vertex->v = p;
+ vertex->dist = HUGE_DISTANCE;
+ vertex->path_prev = NULL;
+
+ return vertex;
+}
+
+static polygon_t *
+polygon_new(int type)
+/* Allocates and initialises a new polygon
+** Parameters: (int) type: The SCI polygon type
+** Returns : (polygon_t *) A newly allocated polygon
+*/
+{
+ polygon_t *polygon = (polygon_t*)sci_malloc(sizeof(polygon_t));
+
+ CLIST_INIT(&polygon->vertices);
+ polygon->type = type;
+
+ return polygon;
+}
+
+static int
+contained(point_t p, polygon_t *polygon)
+/* Polygon containment test
+** Parameters: (point_t) p: The point
+** (polygon_t *) polygon: The polygon
+** Returns : (int) CONT_INSIDE if p is strictly contained in polygon,
+** CONT_ON_EDGE if p lies on an edge of polygon,
+** CONT_OUTSIDE otherwise
+*/
+{
+ /* Number of ray crossing left and right */
+ int lcross = 0, rcross = 0;
+ vertex_t *vertex;
+
+ /* Iterate over edges */
+ CLIST_FOREACH(vertex, &polygon->vertices, entries) {
+ point_t v1 = vertex->v;
+ point_t v2 = CLIST_NEXT(vertex, entries)->v;
+
+ /* Flags for ray straddling left and right */
+ int rstrad, lstrad;
+
+ /* Check if p is a vertex */
+ if (POINT_EQUAL(p, v1))
+ return CONT_ON_EDGE;
+
+ /* Check if edge straddles the ray */
+ rstrad = (v1.y < p.y) != (v2.y < p.y);
+ lstrad = (v1.y > p.y) != (v2.y > p.y);
+
+ if (lstrad || rstrad) {
+ /* Compute intersection point x / xq */
+ int x = v2.x * v1.y - v1.x * v2.y + (v1.x - v2.x) * p.y;
+ int xq = v1.y - v2.y;
+
+ /* Multiply by -1 if xq is negative (for comparison
+ ** that follows)
+ */
+ if (xq < 0) {
+ x = -x;
+ xq = -xq;
+ }
+
+ /* Avoid floats by multiplying instead of dividing */
+ if (rstrad && (x > xq * p.x))
+ rcross++;
+ else if (lstrad && (x < xq * p.x))
+ lcross++;
+ }
+ }
+
+ /* If we counted an odd number of total crossings the point is on an
+ ** edge
+ */
+ if ((lcross + rcross) % 2 == 1)
+ return CONT_ON_EDGE;
+
+ /* If there are an odd number of crossings to one side the point is
+ ** contained in the polygon
+ */
+ if (rcross % 2 == 1) {
+ /* Invert result for contained access polygons. */
+ if (polygon->type == POLY_CONTAINED_ACCESS)
+ return CONT_OUTSIDE;
+ return CONT_INSIDE;
+ }
+
+ /* Point is outside polygon. Invert result for contained access
+ ** polygons
+ */
+ if (polygon->type == POLY_CONTAINED_ACCESS)
+ return CONT_INSIDE;
+
+ return CONT_OUTSIDE;
+}
+
+static int
+polygon_area(polygon_t *polygon)
+/* Computes polygon area
+** Parameters: (polygon_t *) polygon: The polygon
+** Returns : (int) The area multiplied by two
+*/
+{
+ vertex_t *first = CLIST_FIRST(&polygon->vertices);
+ vertex_t *v;
+ int size = 0;
+
+ v = CLIST_NEXT(first, entries);
+
+ while (CLIST_NEXT(v, entries) != first) {
+ size += area(first->v, v->v, CLIST_NEXT(v, entries)->v);
+ v = CLIST_NEXT(v, entries);
+ }
+
+ return size;
+}
+
+static void
+fix_vertex_order(polygon_t *polygon)
+/* Fixes the vertex order of a polygon if incorrect. Contained access
+** polygons should have their vertices ordered clockwise, all other types
+** anti-clockwise
+** Parameters: (polygon_t *) polygon: The polygon
+** Returns : (void)
+*/
+{
+ int area = polygon_area(polygon);
+
+ /* When the polygon area is positive the vertices are ordered
+ ** anti-clockwise. When the area is negative the vertices are ordered
+ ** clockwise
+ */
+ if (((area > 0) && (polygon->type == POLY_CONTAINED_ACCESS))
+ || ((area < 0) && (polygon->type != POLY_CONTAINED_ACCESS))) {
+ vertices_head_t vertices;
+
+ /* Create a new circular list */
+ CLIST_INIT(&vertices);
+
+ while (!CLIST_EMPTY(&polygon->vertices)) {
+ /* Put first vertex in new list */
+ vertex_t *vertex = CLIST_FIRST(&polygon->vertices);
+ CLIST_REMOVE(&polygon->vertices, vertex, entries);
+ CLIST_INSERT_HEAD(&vertices, vertex, entries);
+ }
+
+ polygon->vertices = vertices;
+ }
+}
+
+static int
+vertex_compare(const void *a, const void *b)
+/* Compares two vertices by angle (first) and distance (second) in relation
+** to vertex_cur. The angle is relative to the horizontal line extending
+** right from vertex_cur, and increases clockwise
+** Parameters: (const void *) a, b: The vertices
+** Returns : (int) -1 if a is smaller than b, 1 if a is larger than b, and
+** 0 if a and b are equal
+*/
+{
+ point_t p0 = vertex_cur->v;
+ point_t p1 = (*(vertex_t **) a)->v;
+ point_t p2 = (*(vertex_t **) b)->v;
+
+ if (POINT_EQUAL(p1, p2))
+ return 0;
+
+ /* Points above p0 have larger angle than points below p0 */
+ if ((p1.y < p0.y) && (p2.y >= p0.y))
+ return 1;
+
+ if ((p2.y < p0.y) && (p1.y >= p0.y))
+ return -1;
+
+ /* Handle case where all points have the same y coordinate */
+ if ((p0.y == p1.y) && (p0.y == p2.y)) {
+ /* Points left of p0 have larger angle than points right of
+ ** p0
+ */
+ if ((p1.x < p0.x) && (p2.x >= p0.x))
+ return 1;
+ if ((p1.x >= p0.x) && (p2.x < p0.x))
+ return -1;
+ }
+
+ if (collinear(p0, p1, p2)) {
+ /* At this point collinear points must have the same angle,
+ ** so compare distance to p0
+ */
+ if (abs(p1.x - p0.x) < abs(p2.x - p0.x))
+ return -1;
+ if (abs(p1.y - p0.y) < abs(p2.y - p0.y))
+ return -1;
+
+ return 1;
+ }
+
+ /* If p2 is left of the directed line (p0, p1) then p1 has greater
+ ** angle
+ */
+ if (left(p0, p1, p2))
+ return 1;
+
+ return -1;
+}
+
+static void
+clockwise(vertex_t *v, point_t *p1, point_t *p2)
+/* Orders the points of an edge clockwise around vertex_cur. If all three
+** points are collinear the original order is used
+** Parameters: (vertex_t *) v: The first vertex of the edge
+** Returns : (void)
+** (point_t) *p1: The first point in clockwise order
+** (point_t) *p2: The second point in clockwise order
+*/
+{
+ vertex_t *w = CLIST_NEXT(v, entries);
+
+ if (left_on(vertex_cur->v, w->v, v->v)) {
+ *p1 = v->v;
+ *p2 = w->v;
+ return;
+ }
+
+ *p1 = w->v;
+ *p2 = v->v;
+ return;
+}
+
+static int
+edge_compare(const void *a, const void *b)
+/* Compares two edges that are intersected by the sweeping line by distance
+** from vertex_cur
+** Parameters: (const void *) a, b: The first vertices of the edges
+** Returns : (int) -1 if a is closer than b, 1 if b is closer than a, and
+** 0 if a and b are equal
+*/
+{
+ point_t v1, v2, w1, w2;
+
+ /* We can assume that the sweeping line intersects both edges and
+ ** that the edges do not properly intersect
+ */
+
+ if (a == b)
+ return 0;
+
+ /* Order vertices clockwise so we know vertex_cur is to the right of
+ ** directed edges (v1, v2) and (w1, w2)
+ */
+ clockwise((vertex_t *) a, &v1, &v2);
+ clockwise((vertex_t *) b, &w1, &w2);
+
+ /* As the edges do not properly intersect one edge must lie entirely
+ ** to one side of another. Note that the special case where edges are
+ ** collinear does not need to be handled as those edges will never be
+ ** in the tree simultaneously
+ */
+
+ /* b is left of a */
+ if (left_on(v1, v2, w1) && left_on(v1, v2, w2))
+ return -1;
+
+ /* b is right of a */
+ if (left_on(v2, v1, w1) && left_on(v2, v1, w2))
+ return 1;
+
+ /* a is left of b */
+ if (left_on(w1, w2, v1) && left_on(w1, w2, v2))
+ return 1;
+
+ /* a is right of b */
+ return -1;
+}
+
+static int
+inside(point_t p, vertex_t *vertex)
+/* Determines whether or not a line from a point to a vertex intersects the
+** interior of the polygon, locally at that vertex
+** Parameters: (point_t) p: The point
+** (vertex_t *) vertex: The vertex
+** Returns : (int) 1 if the line (p, vertex->v) intersects the interior of
+** the polygon, locally at the vertex. 0 otherwise
+*/
+{
+ /* Check that it's not a single-vertex polygon */
+ if (VERTEX_HAS_EDGES(vertex)) {
+ point_t prev = CLIST_PREV(vertex, entries)->v;
+ point_t next = CLIST_NEXT(vertex, entries)->v;
+ point_t cur = vertex->v;
+
+ if (left(prev, cur, next)) {
+ /* Convex vertex, line (p, cur) intersects the inside
+ ** if p is located left of both edges
+ */
+ if (left(cur, next, p) && left(prev, cur, p))
+ return 1;
+ } else {
+ /* Non-convex vertex, line (p, cur) intersects the
+ ** inside if p is located left of either edge
+ */
+ if (left(cur, next, p) || left(prev, cur, p))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+visible(vertex_t *vertex, vertex_t *vertex_prev, int visible, aatree_t *tree)
+/* Determines whether or not a vertex is visible from vertex_cur
+** Parameters: (vertex_t *) vertex: The vertex
+** (vertex_t *) vertex_prev: The previous vertex in the sort
+** order, or NULL
+** (int) visible: 1 if vertex_prev is visible, 0 otherwise
+** (aatree_t *) tree: The tree of edges intersected by the
+** sweeping line
+** Returns : (int) 1 if vertex is visible from vertex_cur, 0 otherwise
+*/
+{
+ vertex_t *edge;
+ point_t p = vertex_cur->v;
+ point_t w = vertex->v;
+ aatree_t *tree_n = tree;
+
+ /* Check if sweeping line intersects the interior of the polygon
+ ** locally at vertex
+ */
+ if (inside(p, vertex))
+ return 0;
+
+ /* If vertex_prev is on the sweeping line, then vertex is invisible
+ ** if vertex_prev is invisible
+ */
+ if (vertex_prev && !visible && between(p, w, vertex_prev->v))
+ return 0;
+
+ /* Find leftmost node of tree */
+ while ((tree_n = aatree_walk(tree_n, AATREE_WALK_LEFT)))
+ tree = tree_n;
+ edge = (vertex_t*)aatree_get_data(tree);
+
+ if (edge) {
+ point_t p1, p2;
+
+ /* Check for intersection with sweeping line before vertex */
+ clockwise(edge, &p1, &p2);
+ if (left(p2, p1, p) && left(p1, p2, w))
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+visible_vertices(pf_state_t *s, vertex_t *vert)
+/* Determines all vertices that are visible from a particular vertex and
+** updates the visibility matrix
+** Parameters: (pf_state_t *) s: The pathfinding state
+** (vertex_t *) vert: The vertex
+** Returns : (void)
+*/
+{
+ aatree_t *tree = aatree_new();
+ point_t p = vert->v;
+ polygon_t *polygon;
+ int i;
+ int is_visible;
+ vertex_t **vert_sorted = (vertex_t**)sci_malloc(sizeof(vertex_t *) * s->vertices);
+
+ /* Sort vertices by angle (first) and distance (second) */
+ memcpy(vert_sorted, s->vertex_index, sizeof(vertex_t *) * s->vertices);
+ vertex_cur = vert;
+ qsort(vert_sorted, s->vertices, sizeof(vertex_t *), vertex_compare);
+
+ LIST_FOREACH(polygon, &s->polygons, entries) {
+ vertex_t *vertex;
+
+ vertex = CLIST_FIRST(&polygon->vertices);
+
+ /* Check that there is more than one vertex. */
+ if (VERTEX_HAS_EDGES(vertex))
+ CLIST_FOREACH(vertex, &polygon->vertices, entries) {
+ point_t high, low;
+
+ /* Add edges that intersect the initial position of the sweeping line */
+ clockwise(vertex, &high, &low);
+
+ if ((high.y < p.y) && (low.y >= p.y) && !POINT_EQUAL(low, p))
+ aatree_insert(vertex, &tree, edge_compare);
+ }
+ }
+
+ is_visible = 1;
+
+ /* The first vertex will be vertex_cur, so we skip it */
+ for (i = 1; i < s->vertices; i++) {
+ vertex_t *v1;
+
+ /* Compute visibility of vertex_index[i] */
+ is_visible = visible(vert_sorted[i], vert_sorted[i - 1], is_visible, tree);
+
+ /* Update visibility matrix */
+ if (is_visible)
+ SET_VISIBLE(s, vert->idx, vert_sorted[i]->idx);
+
+ /* Delete anti-clockwise edges from tree */
+ v1 = CLIST_PREV(vert_sorted[i], entries);
+ if (left(p, vert_sorted[i]->v, v1->v)) {
+ if (aatree_delete(v1, &tree, edge_compare))
+ sciprintf("[avoidpath] Error: failed to remove edge from tree\n");
+ }
+
+ v1 = CLIST_NEXT(vert_sorted[i], entries);
+ if (left(p, vert_sorted[i]->v, v1->v)) {
+ if (aatree_delete(vert_sorted[i], &tree, edge_compare))
+ sciprintf("[avoidpath] Error: failed to remove edge from tree\n");
+ }
+
+ /* Add clockwise edges of collinear vertices when sweeping line moves */
+ if ((i < s->vertices - 1) && !collinear(p, vert_sorted[i]->v, vert_sorted[i + 1]->v)) {
+ int j;
+ for (j = i; (j >= 1) && collinear(p, vert_sorted[i]->v, vert_sorted[j]->v); j--) {
+ v1 = CLIST_PREV(vert_sorted[j], entries);
+ if (left(vert_sorted[j]->v, p, v1->v))
+ aatree_insert(v1, &tree, edge_compare);
+
+ v1 = CLIST_NEXT(vert_sorted[j], entries);
+ if (left(vert_sorted[j]->v, p, v1->v))
+ aatree_insert(vert_sorted[j], &tree, edge_compare);
+ }
+ }
+ }
+
+ sci_free(vert_sorted);
+
+ /* Free tree */
+ aatree_free(tree);
+}
+
+static float
+distance(pointf_t a, pointf_t b)
+/* Computes the distance between two pointfs
+** Parameters: (point_t) a, b: The two pointfs
+** Returns : (int) The distance between a and b, rounded to int
+*/
+{
+ float w = a.x - b.x;
+ float h = a.y - b.y;
+
+ return sqrt(w * w + h * h);
+}
+
+static int
+point_on_screen_border(point_t p)
+/* Determines if a point lies on the screen border
+** Parameters: (point_t) p: The point
+** Returns : (int) 1 if p lies on the screen border, 0 otherwise
+*/
+{
+ /* FIXME get dimensions from somewhere? */
+ return (p.x == 0) || (p.x == 319) || (p.y == 0) || (p.y == 189);
+}
+
+static int
+edge_on_screen_border(point_t p, point_t q)
+/* Determines if an edge lies on the screen border
+** Parameters: (point_t) p, q: The edge (p, q)
+** Returns : (int) 1 if (p, q) lies on the screen border, 0 otherwise
+*/
+{
+ /* FIXME get dimensions from somewhere? */
+ return ((p.x == 0 && q.x == 0)
+ || (p.x == 319 && q.x == 319)
+ || (p.y == 0 && q.y == 0)
+ || (p.y == 189 && q.y == 189));
+}
+
+static int
+find_free_point(pointf_t f, polygon_t *polygon, point_t *ret)
+/* Searches for a nearby point that is not contained in a polygon
+** Parameters: (pointf_t) f: The pointf to search nearby
+** (polygon_t *) polygon: The polygon
+** Returns : (int) PF_OK on success, PF_FATAL otherwise
+** (point_t) *ret: The non-contained point on success
+*/
+{
+ point_t p;
+
+ /* Try nearest point first */
+ p = gfx_point((int) floor(f.x + 0.5),
+ (int) floor(f.y + 0.5));
+
+ if (contained(p, polygon) != CONT_INSIDE) {
+ *ret = p;
+ return PF_OK;
+ }
+
+ p = gfx_point((int) floor(f.x),
+ (int) floor(f.y));
+
+ /* Try (x, y), (x + 1, y), (x , y + 1) and (x + 1, y + 1) */
+ if (contained(p, polygon) == CONT_INSIDE) {
+ p.x++;
+ if (contained(p, polygon) == CONT_INSIDE) {
+ p.y++;
+ if (contained(p, polygon) == CONT_INSIDE) {
+ p.x--;
+ if (contained(p, polygon) == CONT_INSIDE)
+ return PF_FATAL;
+ }
+ }
+ }
+
+ *ret = p;
+ return PF_OK;
+}
+
+static int
+near_point(point_t p, polygon_t *polygon, point_t *ret)
+/* Computes the near point of a point contained in a polygon
+** Parameters: (point_t) p: The point
+** (polygon_t *) polygon: The polygon
+** Returns : (int) PF_OK on success, PF_FATAL otherwise
+** (point_t) *ret: The near point of p in polygon on success
+*/
+{
+ vertex_t *vertex;
+ pointf_t near_p;
+ float dist = HUGE_DISTANCE;
+
+ CLIST_FOREACH(vertex, &polygon->vertices, entries) {
+ point_t p1 = vertex->v;
+ point_t p2 = CLIST_NEXT(vertex, entries)->v;
+ float w, h, l, u;
+ pointf_t new_point;
+ float new_dist;
+
+ /* Ignore edges on the screen border */
+ if (edge_on_screen_border(p1, p2))
+ continue;
+
+ /* Compute near point */
+ w = p2.x - p1.x;
+ h = p2.y - p1.y;
+ l = sqrt(w * w + h * h);
+ u = ((p.x - p1.x) * (p2.x - p1.x) + (p.y - p1.y) * (p2.y - p1.y)) / (l * l);
+
+ /* Clip to edge */
+ if (u < 0.0f)
+ u = 0.0f;
+ if (u > 1.0f)
+ u = 1.0f;
+
+ new_point.x = p1.x + u * (p2.x - p1.x);
+ new_point.y = p1.y + u * (p2.y - p1.y);
+
+ new_dist = distance(to_pointf(p), new_point);
+
+ if (new_dist < dist) {
+ near_p = new_point;
+ dist = new_dist;
+ }
+ }
+
+ /* Find point not contained in polygon */
+ return find_free_point(near_p, polygon, ret);
+}
+
+static int
+intersection(point_t a, point_t b, vertex_t *vertex, pointf_t *ret)
+/* Computes the intersection point of a line segment and an edge (not
+** including the vertices themselves)
+** Parameters: (point_t) a, b: The line segment (a, b)
+** (vertex_t *) vertex: The first vertex of the edge
+** Returns : (int) FP_OK on success, PF_ERROR otherwise
+** (pointf_t) *ret: The intersection point
+*/
+{
+ /* Parameters of parametric equations */
+ float s, t;
+ /* Numerator and denominator of equations */
+ float num, denom;
+ point_t c = vertex->v;
+ point_t d = CLIST_NEXT(vertex, entries)->v;
+
+ denom = a.x * (float) (d.y - c.y) +
+ b.x * (float) (c.y - d.y) +
+ d.x * (float) (b.y - a.y) +
+ c.x * (float) (a.y - b.y);
+
+ if (denom == 0.0)
+ /* Segments are parallel, no intersection */
+ return PF_ERROR;
+
+ num = a.x * (float) (d.y - c.y) +
+ c.x * (float) (a.y - d.y) +
+ d.x * (float) (c.y - a.y);
+
+ s = num / denom;
+
+ num = -(a.x * (float) (c.y - b.y) +
+ b.x * (float) (a.y - c.y) +
+ c.x * (float) (b.y - a.y));
+
+ t = num / denom;
+
+ if ((0.0 <= s) && (s <= 1.0) && (0.0 < t) && (t < 1.0)) {
+ /* Intersection found */
+ ret->x = a.x + s * (b.x - a.x);
+ ret->y = a.y + s * (b.y - a.y);
+ return PF_OK;
+ }
+
+ return PF_ERROR;
+}
+
+static int
+nearest_intersection(pf_state_t *s, point_t p, point_t q, point_t *ret)
+/* Computes the nearest intersection point of a line segment and the polygon
+** set. Intersection points that are reached from the inside of a polygon
+** are ignored as are improper intersections which do not obstruct
+** visibility
+** Parameters: (pf_state_t *) s: The pathfinding state
+** (point_t) p, q: The line segment (p, q)
+** Returns : (int) PF_OK on success, PF_ERROR when no intersections were
+** found, PF_FATAL otherwise
+** (point_t) *ret: On success, the closest intersection point
+*/
+{
+ polygon_t *polygon;
+ pointf_t isec;
+ polygon_t *ipolygon;
+ float dist = HUGE_DISTANCE;
+
+ LIST_FOREACH(polygon, &s->polygons, entries) {
+ vertex_t *vertex;
+
+ CLIST_FOREACH(vertex, &polygon->vertices, entries) {
+ float new_dist;
+ pointf_t new_isec;
+
+ /* Check for intersection with vertex */
+ if (between(p, q, vertex->v)) {
+ /* Skip this vertex if we hit it from the
+ ** inside of the polygon
+ */
+ if (inside(q, vertex)) {
+ new_isec.x = vertex->v.x;
+ new_isec.y = vertex->v.y;
+ } else
+ continue;
+ } else {
+ /* Check for intersection with edges */
+
+ /* Skip this edge if we hit it from the
+ ** inside of the polygon
+ */
+ if (!left(vertex->v, CLIST_NEXT(vertex, entries)->v, q))
+ continue;
+
+ if (intersection(p, q, vertex, &new_isec) != PF_OK)
+ continue;
+ }
+
+ new_dist = distance(to_pointf(p), new_isec);
+ if (new_dist < dist) {
+ ipolygon = polygon;
+ isec = new_isec;
+ dist = new_dist;
+ }
+ }
+ }
+
+ if (dist == HUGE_DISTANCE)
+ return PF_ERROR;
+
+ /* Find point not contained in polygon */
+ return find_free_point(isec, ipolygon, ret);
+}
+
+static int
+fix_point(pf_state_t *s, point_t p, point_t *ret, polygon_t **ret_pol)
+/* Checks a point for containment in any of the polygons in the polygon set.
+** If the point is contained in a totally accessible polygon that polygon
+** is removed from the set. If the point is contained in a polygon of another
+** type the near point is returned. Otherwise the original point is returned
+** Parameters: (point_t) p: The point
+** Returns : (int) PF_OK on success, PF_FATAL otherwise
+** (point_t) *ret: A valid input point for pathfinding
+** (polygon_t *) *ret_pol: The polygon p was contained in if p
+** != *ret, NULL otherwise
+*/
+{
+ polygon_t *polygon;
+ *ret_pol = NULL;
+
+ /* Check for polygon containment */
+ LIST_FOREACH(polygon, &s->polygons, entries) {
+ if (contained(p, polygon) != CONT_OUTSIDE)
+ break;
+ }
+
+ if (polygon) {
+ point_t near_p;
+
+ if (polygon->type == POLY_TOTAL_ACCESS) {
+ /* Remove totally accessible polygon if it contains
+ ** p
+ */
+ LIST_REMOVE(polygon, entries);
+ *ret = p;
+ return PF_OK;
+ }
+
+ /* Otherwise, compute near point */
+ if (near_point(p, polygon, &near_p) == PF_OK) {
+ *ret = near_p;
+
+ if (!POINT_EQUAL(p, *ret))
+ *ret_pol = polygon;
+
+ return PF_OK;
+ }
+
+ return PF_FATAL;
+ }
+
+ /* p is not contained in any polygon */
+ *ret = p;
+ return PF_OK;
+}
+
+static vertex_t *
+merge_point(pf_state_t *s, point_t v)
+/* Merges a point into the polygon set. A new vertex is allocated for this
+** point, unless a matching vertex already exists. If the point is on an
+** already existing edge that edge is split up into two edges connected by
+** the new vertex
+** Parameters: (pf_state_t *) s: The pathfinding state
+** (point_t) v: The point to merge
+** Returns : (vertex_t *) The vertex corresponding to v
+*/
+{
+ vertex_t *vertex;
+ vertex_t *v_new;
+ polygon_t *polygon;
+
+ /* Check for already existing vertex */
+ LIST_FOREACH(polygon, &s->polygons, entries) {
+ CLIST_FOREACH(vertex, &polygon->vertices, entries)
+ if (POINT_EQUAL(vertex->v, v))
+ return vertex;
+ }
+
+ v_new = vertex_new(v);
+
+ /* Check for point being on an edge */
+ LIST_FOREACH(polygon, &s->polygons, entries)
+ /* Skip single-vertex polygons */
+ if (VERTEX_HAS_EDGES(CLIST_FIRST(&polygon->vertices)))
+ CLIST_FOREACH(vertex, &polygon->vertices, entries) {
+ vertex_t *next = CLIST_NEXT(vertex, entries);
+
+ if (between(vertex->v, next->v, v)) {
+ /* Split edge by adding vertex */
+ CLIST_INSERT_AFTER(vertex, v_new, entries);
+ return v_new;
+ }
+ }
+
+ /* Add point as single-vertex polygon */
+ polygon = polygon_new(POLY_BARRED_ACCESS);
+ CLIST_INSERT_HEAD(&polygon->vertices, v_new, entries);
+ LIST_INSERT_HEAD(&s->polygons, polygon, entries);
+
+ return v_new;
+}
+
+static polygon_t *
+convert_polygon(state_t *s, reg_t polygon)
+/* Converts an SCI polygon into a polygon_t
+** Parameters: (state_t *) s: The game state
+** (reg_t) polygon: The SCI polygon to convert
+** Returns : (polygon_t *) The converted polygon
+*/
+{
+ int i;
+ reg_t points = GET_SEL32(polygon, points);
+ int size = KP_UINT(GET_SEL32(polygon, size));
+ unsigned char *list = kernel_dereference_bulk_pointer(s, points, size * POLY_POINT_SIZE);
+ polygon_t *poly = polygon_new(KP_UINT(GET_SEL32(polygon, type)));
+ int is_reg_t = polygon_is_reg_t(list, size);
+
+ for (i = 0; i < size; i++) {
+ vertex_t *vertex = vertex_new(read_point(list, is_reg_t, i));
+ CLIST_INSERT_HEAD(&poly->vertices, vertex, entries);
+ }
+
+ fix_vertex_order(poly);
+
+ return poly;
+}
+
+static void
+free_polygon(polygon_t *polygon)
+/* Frees a polygon and its vertices
+** Parameters: (polygon_t *) polygons: The polygon
+** Returns : (void)
+*/
+{
+ while (!CLIST_EMPTY(&polygon->vertices)) {
+ vertex_t *vertex = CLIST_FIRST(&polygon->vertices);
+ CLIST_REMOVE(&polygon->vertices, vertex, entries);
+ sci_free(vertex);
+ }
+
+ sci_free(polygon);
+}
+
+static void
+free_pf_state(pf_state_t *p)
+/* Frees a pathfinding state
+** Parameters: (pf_state_t *) p: The pathfinding state
+** Returns : (void)
+*/
+{
+ if (p->vertex_index)
+ sci_free(p->vertex_index);
+
+ if (p->vis_matrix)
+ sci_free(p->vis_matrix);
+
+ while (!LIST_EMPTY(&p->polygons)) {
+ polygon_t *polygon = LIST_FIRST(&p->polygons);
+ LIST_REMOVE(polygon, entries);
+ free_polygon(polygon);
+ }
+
+ sci_free(p);
+}
+
+static void
+change_polygons_opt_0(pf_state_t *s)
+/* Changes the polygon list for optimization level 0 (used for keyboard
+** support). Totally accessible polygons are removed and near-point
+** accessible polygons are changed into totally accessible polygons.
+** Parameters: (pf_state_t *) s: The pathfinding state
+** Returns : (void)
+*/
+{
+ polygon_t *polygon = LIST_FIRST(&s->polygons);
+
+ while (polygon) {
+ polygon_t *next = LIST_NEXT(polygon, entries);
+
+ if (polygon->type == POLY_NEAREST_ACCESS)
+ polygon->type = POLY_TOTAL_ACCESS;
+ else if (polygon->type == POLY_TOTAL_ACCESS) {
+ LIST_REMOVE(polygon, entries);
+ free_polygon(polygon);
+ }
+
+ polygon = next;
+ }
+}
+
+static pf_state_t *
+convert_polygon_set(state_t *s, reg_t poly_list, point_t start, point_t end, int opt)
+/* Converts the SCI input data for pathfinding
+** Parameters: (state_t *) s: The game state
+** (reg_t) poly_list: Polygon list
+** (point_t) start: The start point
+** (point_t) end: The end point
+** (int) opt: Optimization level (0, 1 or 2)
+** Returns : (pf_state_t *) On success a newly allocated pathfinding state,
+** NULL otherwise
+*/
+{
+ polygon_t *polygon;
+ int err;
+ int count = 0;
+ pf_state_t *pf_s = (pf_state_t*)sci_malloc(sizeof(pf_state_t));
+
+ LIST_INIT(&pf_s->polygons);
+ pf_s->start = start;
+ pf_s->end = end;
+ pf_s->keep_start = 0;
+ pf_s->keep_end = 0;
+ pf_s->vertex_index = NULL;
+
+ /* Convert all polygons */
+ if (poly_list.segment) {
+ list_t *list = LOOKUP_LIST(poly_list);
+ node_t *node = LOOKUP_NODE(list->first);
+
+ while (node) {
+ polygon = convert_polygon(s, node->value);
+ LIST_INSERT_HEAD(&pf_s->polygons, polygon, entries);
+ count += KP_UINT(GET_SEL32(node->value, size));
+ node = LOOKUP_NODE(node->succ);
+ }
+ }
+
+ if (opt == 0) {
+ /* Keyboard support */
+ change_polygons_opt_0(pf_s);
+
+ /* Find nearest intersection */
+ err = nearest_intersection(pf_s, start, end, &start);
+
+ if (err == PF_FATAL) {
+ sciprintf("[avoidpath] Error: fatal error finding nearest intersecton\n");
+ free_pf_state(pf_s);
+ return NULL;
+ }
+ else if (err == PF_OK)
+ /* Keep original start position if intersection
+ ** was found
+ */
+ pf_s->keep_start = 1;
+ } else {
+ if (fix_point(pf_s, start, &start, &polygon) != PF_OK) {
+ sciprintf("[avoidpath] Error: couldn't fix start position for pathfinding\n");
+ free_pf_state(pf_s);
+ return NULL;
+ }
+ else if (polygon) {
+ /* Start position has moved */
+ pf_s->keep_start = 1;
+ if ((polygon->type != POLY_NEAREST_ACCESS))
+ sciprintf("[avoidpath] Warning: start position at unreachable location\n");
+ }
+ }
+
+ if (fix_point(pf_s, end, &end, &polygon) != PF_OK) {
+ sciprintf("[avoidpath] Error: couldn't fix end position for pathfinding\n");
+ free_pf_state(pf_s);
+ return NULL;
+ }
+ else {
+ /* Keep original end position if it is contained in a
+ ** near-point accessible polygon
+ */
+ if (polygon && (polygon->type == POLY_NEAREST_ACCESS))
+ pf_s->keep_end = 1;
+ }
+
+ /* Merge start and end points into polygon set */
+ pf_s->vertex_start = merge_point(pf_s, start);
+ pf_s->vertex_end = merge_point(pf_s, end);
+
+ /* Allocate and build vertex index */
+ pf_s->vertex_index = (vertex_t**)sci_malloc(sizeof(vertex_t *) * (count + 2));
+
+ count = 0;
+
+ LIST_FOREACH(polygon, &pf_s->polygons, entries) {
+ vertex_t *vertex;
+
+ CLIST_FOREACH(vertex, &polygon->vertices, entries) {
+ vertex->idx = count;
+ pf_s->vertex_index[count++] = vertex;
+ }
+ }
+
+ pf_s->vertices = count;
+
+ /* Allocate and clear visibility matrix */
+ pf_s->vis_matrix = (char*)sci_calloc(pf_s->vertices * VIS_MATRIX_ROW_SIZE(pf_s->vertices), 1);
+
+ return pf_s;
+}
+
+static void
+visibility_graph(pf_state_t *s)
+/* Computes the visibility graph
+** Parameters: (pf_state_t *) s: The pathfinding state
+** Returns : (void)
+*/
+{
+ polygon_t *polygon;
+
+ LIST_FOREACH(polygon, &s->polygons, entries) {
+ vertex_t *vertex;
+
+ CLIST_FOREACH(vertex, &polygon->vertices, entries)
+ visible_vertices(s, vertex);
+ }
+}
+
+static int
+intersecting_polygons(pf_state_t *s)
+/* Detects (self-)intersecting polygons
+** Parameters: (pf_state_t *) s: The pathfinding state
+** Returns : (int) 1 if s contains (self-)intersecting polygons, 0 otherwise
+*/
+{
+ int i, j;
+
+ for (i = 0; i < s->vertices; i++) {
+ vertex_t *v1 = s->vertex_index[i];
+ if (!VERTEX_HAS_EDGES(v1))
+ continue;
+ for (j = i + 1; j < s->vertices; j++) {
+ vertex_t *v2 = s->vertex_index[j];
+ if (!VERTEX_HAS_EDGES(v2))
+ continue;
+
+ /* Skip neighbouring edges */
+ if ((CLIST_NEXT(v1, entries) == v2)
+ || CLIST_PREV(v1, entries) == v2)
+ continue;
+
+ if (intersect(v1->v, CLIST_NEXT(v1, entries)->v,
+ v2->v, CLIST_NEXT(v2, entries)->v))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void
+dijkstra(pf_state_t *s)
+/* Computes a shortest path from vertex_start to vertex_end. The caller can
+** construct the resulting path by following the path_prev links from
+** vertex_end back to vertex_start. If no path exists vertex_end->path_prev
+** will be NULL
+** Parameters: (pf_state_t *) s: The pathfinding state
+** Returns : (void)
+*/
+{
+ polygon_t *polygon;
+ /* Vertices of which the shortest path is known */
+ LIST_HEAD(done_head, vertex) done;
+ /* The remaining vertices */
+ LIST_HEAD(remain_head, vertex) remain;
+
+ LIST_INIT(&remain);
+ LIST_INIT(&done);
+
+ /* Start out with all vertices in set remain */
+ LIST_FOREACH(polygon, &s->polygons, entries) {
+ vertex_t *vertex;
+
+ CLIST_FOREACH(vertex, &polygon->vertices, entries)
+ LIST_INSERT_HEAD(&remain, vertex, dijkstra);
+ }
+
+ s->vertex_start->dist = 0.0f;
+
+ /* Loop until we find vertex_end */
+ while (1) {
+ int i;
+ vertex_t *vertex, *vertex_min;
+ float min = HUGE_DISTANCE;
+
+ /* Find vertex at shortest distance from set done */
+ LIST_FOREACH(vertex, &remain, dijkstra) {
+ if (vertex->dist < min) {
+ vertex_min = vertex;
+ min = vertex->dist;
+ }
+ }
+
+ if (min == HUGE_DISTANCE) {
+ sciprintf("[avoidpath] Warning: end point (%i, %i) is unreachable\n", s->vertex_end->v.x, s->vertex_end->v.y);
+ return;
+ }
+
+ /* If vertex_end is at shortest distance we can stop */
+ if (vertex_min == s->vertex_end)
+ return;
+
+ /* Move vertex from set remain to set done */
+ LIST_REMOVE(vertex_min, dijkstra);
+ LIST_INSERT_HEAD(&done, vertex_min, dijkstra);
+
+ for (i = 0; i < s->vertices; i++) {
+ /* Adjust upper bound for all vertices that are visible from vertex_min */
+ if (IS_VISIBLE(s, vertex_min->idx, i)) {
+ float new_dist;
+
+ /* Avoid plotting path along screen edge */
+ if ((s->vertex_index[i] != s->vertex_end) && point_on_screen_border(s->vertex_index[i]->v))
+ continue;
+
+ new_dist = vertex_min->dist + distance(to_pointf(vertex_min->v),
+ to_pointf(s->vertex_index[i]->v));
+ if (new_dist < s->vertex_index[i]->dist) {
+ s->vertex_index[i]->dist = new_dist;
+ s->vertex_index[i]->path_prev = vertex_min;
+ }
+ }
+ }
+ }
+}
+
+static reg_t
+output_path(pf_state_t *p, state_t *s)
+/* Stores the final path in newly allocated dynmem
+** Parameters: (pf_state_t *) p: The pathfinding state
+** (state_t *) s: The game state
+** Returns : (reg_t) Pointer to dynmem containing path
+*/
+{
+ int path_len = 0;
+ byte *oref;
+ reg_t output;
+ vertex_t *vertex = p->vertex_end;
+ int i;
+ int unreachable = vertex->path_prev == NULL;
+
+ if (unreachable) {
+ /* If pathfinding failed we only return the path up to vertex_start */
+ oref = sm_alloc_dynmem(&s->seg_manager, POLY_POINT_SIZE * 3,
+ AVOIDPATH_DYNMEM_STRING, &output);
+
+ if (p->keep_start)
+ POLY_SET_POINT(oref, 0, p->start.x, p->start.y);
+ else
+ POLY_SET_POINT(oref, 0, p->vertex_start->v.x, p->vertex_start->v.y);
+ POLY_SET_POINT(oref, 1, p->vertex_start->v.x, p->vertex_start->v.y);
+ POLY_SET_POINT(oref, 2, POLY_LAST_POINT, POLY_LAST_POINT);
+
+ return output;
+ }
+
+ while (vertex) {
+ /* Compute path length */
+ path_len++;
+ vertex = vertex->path_prev;
+ }
+
+ oref = sm_alloc_dynmem(&s->seg_manager, POLY_POINT_SIZE * (path_len + 1 + p->keep_start + p->keep_end),
+ AVOIDPATH_DYNMEM_STRING, &output);
+
+ /* Sentinel */
+ POLY_SET_POINT(oref, path_len + p->keep_start + p->keep_end, POLY_LAST_POINT, POLY_LAST_POINT);
+
+ /* Add original start and end points if needed */
+ if (p->keep_end)
+ POLY_SET_POINT(oref, path_len + p->keep_start, p->end.x, p->end.y);
+ if (p->keep_start)
+ POLY_SET_POINT(oref, 0, p->start.x, p->start.y);
+
+ i = path_len + p->keep_start - 1;
+
+ if (unreachable) {
+ /* Return straight trajectory from start to end */
+ POLY_SET_POINT(oref, i - 1, p->vertex_start->v.x, p->vertex_start->v.y);
+ POLY_SET_POINT(oref, i, p->vertex_end->v.x, p->vertex_end->v.y);
+ return output;
+ }
+
+ vertex = p->vertex_end;
+ while (vertex) {
+ POLY_SET_POINT(oref, i, vertex->v.x, vertex->v.y);
+ vertex = vertex->path_prev;
+ i--;
+ }
+
+ if (s->debug_mode & (1 << SCIkAVOIDPATH_NR)) {
+ sciprintf("[avoidpath] Returning path:");
+ for (i = 0; i < path_len + p->keep_start + p->keep_end; i++) {
+ point_t pt;
+ POLY_GET_POINT(oref, i, pt.x, pt.y);
+ sciprintf(" (%i, %i)", pt.x, pt.y);
+ }
+ sciprintf("\n");
+ }
+
+ return output;
+}
+
+reg_t
+kAvoidPath(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ point_t start = gfx_point(SKPV(0), SKPV(1));
+
+ if (s->debug_mode & (1 << SCIkAVOIDPATH_NR)) {
+ gfxw_port_t *port= s->picture_port;
+
+ if (!port->decorations) {
+ port->decorations = gfxw_new_list(gfx_rect(0, 0, 320, 200), 0);
+ port->decorations->set_visual(GFXW(port->decorations), port->visual);
+ } else {
+ port->decorations->free_contents(port->decorations);
+ }
+ }
+
+ switch (argc) {
+
+ case 3 :
+ {
+ reg_t retval;
+ polygon_t *polygon = convert_polygon(s, argv[2]);
+
+ if (polygon->type == POLY_CONTAINED_ACCESS) {
+ sciprintf("[avoidpath] Warning: containment test performed on contained access polygon\n");
+
+ /* Semantics unknown, assume barred access semantics */
+ polygon->type = POLY_BARRED_ACCESS;
+ }
+
+ retval = make_reg(0, contained(start, polygon) != CONT_OUTSIDE);
+ free_polygon(polygon);
+ return retval;
+ }
+ case 6 :
+ case 7 :
+ {
+ point_t end = gfx_point(SKPV(2), SKPV(3));
+ reg_t poly_list = argv[4];
+ /* int poly_list_size = UKPV(5); */
+ int opt = UKPV_OR_ALT(6, 1);
+ reg_t output;
+ pf_state_t *p;
+
+ if (s->debug_mode & (1 << SCIkAVOIDPATH_NR)) {
+ sciprintf("[avoidpath] Pathfinding input:\n");
+ draw_point(s, start, 1);
+ draw_point(s, end, 0);
+
+ if (poly_list.segment) {
+ print_input(s, poly_list, start, end, opt);
+ draw_input(s, poly_list, start, end, opt);
+ }
+ }
+
+ p = convert_polygon_set(s, poly_list, start, end, opt);
+
+ if (intersecting_polygons(p)) {
+ sciprintf("[avoidpath] Error: input set contains (self-)intersecting polygons\n");
+ free_pf_state(p);
+ p = NULL;
+ }
+
+ if (!p) {
+ byte *oref;
+ sciprintf("[avoidpath] Error: pathfinding failed for following input:\n");
+ print_input(s, poly_list, start, end, opt);
+ sciprintf("[avoidpath] Returning direct path from start point to end point\n");
+ oref = sm_alloc_dynmem(&s->seg_manager, POLY_POINT_SIZE*3,
+ AVOIDPATH_DYNMEM_STRING, &output);
+
+ POLY_SET_POINT(oref, 0, start.x, start.y);
+ POLY_SET_POINT(oref, 1, end.x, end.y);
+ POLY_SET_POINT(oref, 2, POLY_LAST_POINT, POLY_LAST_POINT);
+
+ return output;
+ }
+
+ visibility_graph(p);
+ dijkstra(p);
+
+ output = output_path(p, s);
+ free_pf_state(p);
+
+ /* Memory is freed by explicit calls to Memory */
+ return output;
+ }
+
+ default:
+ SCIkwarn(SCIkWARNING, "Unknown AvoidPath subfunction %d\n",
+ argc);
+ return NULL_REG;
+ break;
+ }
+}
diff --git a/engines/sci/engine/kscripts.c b/engines/sci/engine/kscripts.c
new file mode 100644
index 0000000000..1fd7fd9a85
--- /dev/null
+++ b/engines/sci/engine/kscripts.c
@@ -0,0 +1,353 @@
+/***************************************************************************
+ kscripts.c.c Copyright (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]
+
+***************************************************************************/
+
+#include <sciresource.h>
+#include <engine.h>
+#include "kernel_types.h"
+
+reg_t
+read_selector(state_t *s, reg_t object, selector_t selector_id, const char *file, int line)
+{
+ reg_t *address;
+
+ if (lookup_selector(s, object, selector_id, &address, NULL) != SELECTOR_VARIABLE)
+ return NULL_REG;
+ else
+ return *address;
+}
+
+
+void
+write_selector(state_t *s, reg_t object, selector_t selector_id, reg_t value,
+ const char *fname, int line)
+{
+ reg_t *address;
+
+ if ((selector_id < 0) || (selector_id > s->selector_names_nr)) {
+ SCIkwarn(SCIkWARNING, "Attempt to write to invalid selector %d of"
+ " object at "PREG" (%s L%d).\n", selector_id,
+ PRINT_REG(object), fname, line);
+ return;
+ }
+
+ if (lookup_selector(s, object, selector_id, &address, NULL) != SELECTOR_VARIABLE)
+ SCIkwarn(SCIkWARNING, "Selector '%s' of object at %04x could not be"
+ " written to (%s L%d)\n",
+ s->selector_names[selector_id], object, fname, line);
+ else
+ *address = value;
+
+}
+
+int
+invoke_selector(state_t *s, reg_t object, int selector_id, int noinvalid, int kfunct,
+ stack_ptr_t k_argp, int k_argc, /* Kernel function argp/argc */
+ const char *fname, int line, int argc, ...)
+{
+ va_list argp;
+ int i;
+ int framesize = 2 + 1 * argc;
+ reg_t address;
+ int slc_type;
+ stack_ptr_t stackframe = k_argp + k_argc;
+
+ exec_stack_t *xstack; /* Execution stack */
+
+ stackframe[0] = make_reg(0, selector_id); /* The selector we want to call */
+ stackframe[1] = make_reg(0, argc); /* Argument count */
+
+ slc_type = lookup_selector(s, object, selector_id, NULL, &address);
+
+ if (slc_type == SELECTOR_NONE) {
+ SCIkwarn(SCIkERROR, "Selector '%s' of object at "PREG" could not be invoked (%s L%d)\n",
+ s->selector_names[selector_id], PRINT_REG(object), fname, line);
+ if (noinvalid == 0)
+ KERNEL_OOPS("Not recoverable: VM was halted\n");
+ return 1;
+ }
+ if (slc_type == SELECTOR_VARIABLE) /* Swallow silently */
+ return 0;
+
+ va_start(argp, argc);
+ for (i = 0; i < argc; i++) {
+ reg_t arg = va_arg(argp, reg_t);
+ stackframe[2 + i] = arg; /* Write each argument */
+ }
+ va_end(argp);
+
+ /* Write "kernel" call to the stack, for debugging: */
+ xstack = add_exec_stack_entry(s, NULL_REG, NULL, NULL_REG,
+ k_argc, k_argp - 1, 0, NULL_REG,
+ s->execution_stack_pos, SCI_XS_CALLEE_LOCALS);
+ xstack->selector = -42 - kfunct; /* Evil debugging hack to identify kernel function */
+ xstack->type = EXEC_STACK_TYPE_KERNEL;
+
+ /* Now commit the actual function: */
+ xstack = send_selector(s, object, object,
+ stackframe, framesize, stackframe);
+
+ xstack->sp += argc+2;
+ xstack->fp += argc+2;
+
+ run_vm(s, 0); /* Start a new vm */
+
+ --(s->execution_stack_pos); /* Get rid of the extra stack entry */
+
+ return 0;
+}
+
+
+int
+is_object(state_t *s, reg_t object)
+{
+ return obj_get(s, object) != NULL;
+}
+
+
+/* kLoad(restype, resnrs ... ):
+** Loads arbitrary resources of type 'restype' with resource numbers 'resnrs'
+** This implementation ignores all resource numbers except the first one.
+*/
+reg_t
+kLoad(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int restype = KP_UINT(argv[0]);
+ int resnr = KP_UINT(argv[1]);
+
+ if (restype == sci_memory)/* Request to dynamically allocate hunk memory for later use */
+ return kalloc(s, "kLoad()", resnr);
+
+ return make_reg(0, ((restype << 11) | resnr)); /* Return the resource identifier as handle */
+}
+
+reg_t
+kLock(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int restype = UKPV(0)&0x7f;
+ int resnr = UKPV(1);
+ int state = argc > 2 ? UKPV(2) : 1;
+
+ resource_t *which;
+
+ switch (state)
+ {
+ case 1 :
+ scir_find_resource(s->resmgr, restype, resnr, 1);
+ break;
+ case 0 :
+ which = scir_find_resource(s->resmgr, restype, resnr, 0);
+ scir_unlock_resource(s->resmgr, which, resnr, restype);
+ break;
+ }
+ return s->r_acc;
+}
+
+/* kUnload():
+** Unloads an arbitrary resource of type 'restype' with resource numbber 'resnr'
+*/
+reg_t
+kUnLoad(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int restype = KP_UINT(argv[0]);
+ reg_t resnr = argv[1];
+
+ if (restype == sci_memory)
+ kfree(s, resnr);
+
+ return s->r_acc;
+}
+
+
+reg_t
+kClone(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ reg_t parent_addr = argv[0];
+ object_t *parent_obj = obj_get(s, parent_addr);
+ reg_t clone_addr;
+ clone_t *clone_obj; /* same as object_t* */
+ int varblock_size;
+
+
+ if (!parent_obj) {
+ SCIkwarn(SCIkERROR, "Attempt to clone non-object/class at "PREG" failed", PRINT_REG(parent_addr));
+ return NULL_REG;
+ }
+
+ SCIkdebug(SCIkMEM, "Attempting to clone from "PREG"\n", PRINT_REG(parent_addr));
+
+ clone_obj = sm_alloc_clone(&s->seg_manager, &clone_addr);
+
+ if (!clone_obj) {
+ SCIkwarn(SCIkERROR, "Cloning "PREG" failed-- internal error!\n", PRINT_REG(parent_addr));
+ return NULL_REG;
+ }
+
+ memcpy(clone_obj, parent_obj, sizeof(clone_t));
+ clone_obj->flags = 0;
+ varblock_size = parent_obj->variables_nr * sizeof(reg_t);
+ clone_obj->variables = (reg_t*)sci_malloc(varblock_size);
+ memcpy(clone_obj->variables, parent_obj->variables, varblock_size);
+
+ /* Mark as clone */
+ clone_obj->variables[SCRIPT_INFO_SELECTOR].offset = SCRIPT_INFO_CLONE;
+ clone_obj->variables[SCRIPT_SPECIES_SELECTOR] = clone_obj->pos;
+ if (IS_CLASS(parent_obj))
+ clone_obj->variables[SCRIPT_SUPERCLASS_SELECTOR] = parent_obj->pos;
+ sm_increment_lockers(&s->seg_manager, parent_obj->pos.segment, SEG_ID);
+ sm_increment_lockers(&s->seg_manager, clone_obj->pos.segment, SEG_ID);
+
+ return clone_addr;
+}
+
+
+extern void
+_k_view_list_mark_free(state_t *s, reg_t off); /* kgraphics.c */
+
+reg_t
+kDisposeClone(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ reg_t victim_addr = argv[0];
+ clone_t *victim_obj = obj_get(s, victim_addr);
+ word underBits;
+
+ if (!victim_obj) {
+ SCIkwarn(SCIkERROR, "Attempt to dispose non-class/object at "PREG"\n",
+ PRINT_REG(victim_addr));
+ return s->r_acc;
+ }
+
+ if (victim_obj->variables[SCRIPT_INFO_SELECTOR].offset != SCRIPT_INFO_CLONE) {
+ /* SCIkwarn("Attempt to dispose something other than a clone at %04x\n", offset); */
+ /* SCI silently ignores this behaviour; some games actually depend on it */
+ return s->r_acc;
+ }
+
+ underBits = GET_SEL32V(victim_addr, underBits);
+ if (underBits) {
+ SCIkwarn(SCIkWARNING,"Clone "PREG" was cleared with underBits set\n", PRINT_REG(victim_addr));
+ }
+#if 0
+ if (s->dyn_views) { /* Free any widget associated with the clone */
+ gfxw_widget_t *widget = gfxw_set_id(gfxw_remove_ID(s->dyn_views, offset), GFXW_NO_ID);
+
+ if (widget && s->bg_widgets)
+ s->bg_widgets->add(GFXWC(s->bg_widgets), widget);
+ }
+#endif
+
+ victim_obj->flags |= OBJECT_FLAG_FREED;
+
+ _k_view_list_mark_free(s, victim_addr); /* Free on view list, if neccessary */
+
+ return s->r_acc;
+}
+
+
+/* kScriptID(script, index):
+** Returns script dispatch address index in the supplied script
+*/
+reg_t
+kScriptID(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int script = KP_UINT(argv[0]);
+ int index = KP_UINT(KP_ALT(1, NULL_REG));
+
+ seg_id_t scriptid = script_get_segment(s, script, SCRIPT_GET_LOAD);
+ script_t *scr;
+
+ if (argv[0].segment)
+ return argv[0];
+
+ if (!scriptid)
+ return NULL_REG;
+
+ scr = &(s->seg_manager.heap[scriptid]->data.script);
+
+ if (!scr->exports_nr) {
+ SCIkdebug(SCIkERROR, "Script 0x%x does not have a dispatch table\n", script);
+ return NULL_REG;
+ }
+
+ if (index > scr->exports_nr) {
+ SCIkwarn(SCIkERROR, "Dispatch index too big: %d > %d\n",
+ index, scr->exports_nr);
+ return NULL_REG;
+ }
+
+ return make_reg(scriptid, sm_validate_export_func(&s->seg_manager, index, scriptid));
+}
+
+
+reg_t
+kDisposeScript(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int script = argv[0].offset;
+
+ /* Work around QfG1 graveyard bug */
+ if (argv[0].segment) return s->r_acc;
+
+ if (sm_script_is_loaded(&(s->seg_manager), script, SCRIPT_ID))
+ {
+ int id = sm_seg_get(&(s->seg_manager), script);
+
+ if (s->execution_stack[s->execution_stack_pos].addr.pc.segment != id)
+ sm_set_lockers(&(s->seg_manager), 1, script, SCRIPT_ID);
+ }
+
+ script_uninstantiate(s, script);
+ s->execution_stack_pos_changed = 1;
+ return s->r_acc;
+}
+
+int
+is_heap_object(state_t *s, reg_t pos)
+{
+ object_t *obj = obj_get(s, pos);
+ return (obj != NULL
+ && (!(obj->flags & OBJECT_FLAG_FREED))
+ && (!sm_script_is_marked_as_deleted(&s->seg_manager, pos.segment)));
+}
+
+reg_t
+kIsObject(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ if (argv[0].offset == 0xffff) /* Treated specially */
+ return NULL_REG;
+ else
+ return make_reg(0, is_heap_object(s, argv[0]));
+}
+
+reg_t
+kRespondsTo(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ reg_t obj = argv[0];
+ int selector = KP_UINT(argv[1]);
+
+ return make_reg(0, is_heap_object(s, obj)
+ && lookup_selector(s, obj, selector, NULL, NULL) != SELECTOR_NONE);
+}
+
diff --git a/engines/sci/engine/ksound.c b/engines/sci/engine/ksound.c
new file mode 100644
index 0000000000..e95f219e4b
--- /dev/null
+++ b/engines/sci/engine/ksound.c
@@ -0,0 +1,948 @@
+/***************************************************************************
+ ksound.c Copyright (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]
+
+***************************************************************************/
+
+#include <engine.h>
+#include <sfx_player.h>
+
+#define _K_SCI0_SOUND_INIT_HANDLE 0
+#define _K_SCI0_SOUND_PLAY_HANDLE 1
+#define _K_SCI0_SOUND_NOP 2
+#define _K_SCI0_SOUND_DISPOSE_HANDLE 3
+#define _K_SCI0_SOUND_MUTE_SOUND 4
+#define _K_SCI0_SOUND_STOP_HANDLE 5
+#define _K_SCI0_SOUND_SUSPEND_HANDLE 6
+#define _K_SCI0_SOUND_RESUME_HANDLE 7
+#define _K_SCI0_SOUND_VOLUME 8
+#define _K_SCI0_SOUND_UPDATE_VOL_PRI 9
+#define _K_SCI0_SOUND_FADE_HANDLE 10
+#define _K_SCI0_SOUND_GET_POLYPHONY 11
+#define _K_SCI0_SOUND_PLAY_NEXT 12
+
+#define _K_SCI01_SOUND_MASTER_VOLME 0 /* Set/Get */
+#define _K_SCI01_SOUND_MUTE_SOUND 1
+#define _K_SCI01_SOUND_UNUSED 2
+#define _K_SCI01_SOUND_GET_POLYPHONY 3
+#define _K_SCI01_SOUND_UPDATE_HANDLE 4
+#define _K_SCI01_SOUND_INIT_HANDLE 5
+#define _K_SCI01_SOUND_DISPOSE_HANDLE 6
+#define _K_SCI01_SOUND_PLAY_HANDLE 7
+#define _K_SCI01_SOUND_STOP_HANDLE 8
+#define _K_SCI01_SOUND_SUSPEND_HANDLE 9 /* or resume */
+#define _K_SCI01_SOUND_FADE_HANDLE 10
+#define _K_SCI01_SOUND_UPDATE_CUES 11
+#define _K_SCI01_SOUND_MIDI_SEND 12
+#define _K_SCI01_SOUND_REVERB 13 /* Get/Set */
+#define _K_SCI01_SOUND_HOLD 14
+
+#define _K_SCI1_SOUND_MASTER_VOLME 0 /* Set/Get */
+#define _K_SCI1_SOUND_MUTE_SOUND 1
+#define _K_SCI1_SOUND_UNUSED1 2
+#define _K_SCI1_SOUND_GET_POLYPHONY 3
+#define _K_SCI1_SOUND_GET_AUDIO_CAPABILITY 4
+#define _K_SCI1_SOUND_SUSPEND_SOUND 5
+#define _K_SCI1_SOUND_INIT_HANDLE 6
+#define _K_SCI1_SOUND_DISPOSE_HANDLE 7
+#define _K_SCI1_SOUND_PLAY_HANDLE 8
+#define _K_SCI1_SOUND_STOP_HANDLE 9
+#define _K_SCI1_SOUND_SUSPEND_HANDLE 10 /* or resume */
+#define _K_SCI1_SOUND_FADE_HANDLE 11
+#define _K_SCI1_SOUND_HOLD_HANDLE 12
+#define _K_SCI1_SOUND_UNUSED2 13
+#define _K_SCI1_SOUND_SET_HANDLE_VOLUME 14
+#define _K_SCI1_SOUND_SET_HANDLE_PRIORITY 15
+#define _K_SCI1_SOUND_SET_HANDLE_LOOP 16
+#define _K_SCI1_SOUND_UPDATE_CUES 17
+#define _K_SCI1_SOUND_MIDI_SEND 18
+#define _K_SCI1_SOUND_REVERB 19 /* Get/Set */
+#define _K_SCI1_SOUND_UPDATE_VOL_PRI 20
+
+#define _K_SCI1_AUDIO_POSITION 6 /* Return current position in audio stream */
+
+#define SCI1_SOUND_FLAG_MAY_PAUSE 1 /* Only here for completeness; The interpreter doesn't touch this bit */
+#define SCI1_SOUND_FLAG_SCRIPTED_PRI 2 /* but does touch this */
+
+#define FROBNICATE_HANDLE(reg) ((reg).segment << 16 | (reg).offset)
+#define DEFROBNICATE_HANDLE(handle) (make_reg((handle >> 16) & 0xffff, handle & 0xffff))
+#define SCRIPT_ASSERT_ZERO(fun) if (fun) script_debug_flag = script_error_flag = 1;
+
+
+static void
+script_set_priority(state_t *s, reg_t obj, int priority)
+{
+ int song_nr = GET_SEL32V(obj, number);
+ resource_t *song = scir_find_resource(s->resmgr, sci_sound, song_nr, 0);
+ int flags = GET_SEL32V(obj, flags);
+
+ if (priority == -1)
+ {
+ if (song->data[0] == 0xf0)
+ priority = song->data[1]; else
+ SCIkdebug(SCIkWARNING, "Attempt to unset song priority when there is no built-in value!\n");
+
+ flags &= ~SCI1_SOUND_FLAG_SCRIPTED_PRI;
+ } else flags |= SCI1_SOUND_FLAG_SCRIPTED_PRI;
+
+ sfx_song_renice(&s->sound, FROBNICATE_HANDLE(obj), priority);
+ PUT_SEL32V(obj, flags, flags);
+}
+
+song_iterator_t *
+build_iterator(state_t *s, int song_nr, int type, songit_id_t id)
+{
+ resource_t *song = scir_find_resource(s->resmgr, sci_sound, song_nr, 0);
+
+ if (!song)
+ return NULL;
+
+ return songit_new(song->data, song->size, type, id);
+}
+
+
+void
+process_sound_events(state_t *s) /* Get all sound events, apply their changes to the heap */
+{
+ int result;
+ song_handle_t handle;
+ int cue;
+
+ if (s->version>=SCI_VERSION_FTU_DOSOUND_VARIANT_1)
+ return;
+ /* SCI01 and later explicitly poll for everything */
+
+ while ((result = sfx_poll(&s->sound, &handle, &cue))) {
+ reg_t obj = DEFROBNICATE_HANDLE(handle);
+ if (!is_object(s, obj)) {
+ SCIkdebug(SCIkWARNING, "Non-object "PREG" received sound signal (%d/%d)\n", PRINT_REG(obj), result, cue);
+ return;
+ }
+
+ switch (result) {
+
+ case SI_LOOP:
+ SCIkdebug(SCIkSOUND, "[process-sound] Song "PREG" looped (to %d)\n",
+ PRINT_REG(obj), cue);
+ /* PUT_SEL32V(obj, loops, GET_SEL32V(obj, loop) - 1);*/
+ PUT_SEL32V(obj, signal, -1);
+ break;
+
+ case SI_RELATIVE_CUE:
+ SCIkdebug(SCIkSOUND, "[process-sound] Song "PREG" received relative cue %d\n",
+ PRINT_REG(obj), cue);
+ PUT_SEL32V(obj, signal, cue + 0x7f);
+ break;
+
+ case SI_ABSOLUTE_CUE:
+ SCIkdebug(SCIkSOUND, "[process-sound] Song "PREG" received absolute cue %d\n",
+ PRINT_REG(obj), cue);
+ PUT_SEL32V(obj, signal, cue);
+ break;
+
+ case SI_FINISHED:
+ SCIkdebug(SCIkSOUND, "[process-sound] Song "PREG" finished\n",
+ PRINT_REG(obj));
+ PUT_SEL32V(obj, signal, -1);
+ PUT_SEL32V(obj, state, _K_SOUND_STATUS_STOPPED);
+ break;
+
+ default:
+ sciprintf("Unexpected result from sfx_poll: %d\n", result);
+ break;
+ }
+ }
+}
+
+
+reg_t
+kDoSound_SCI0(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ reg_t obj = KP_ALT(1, NULL_REG);
+ word command = UKPV(0);
+ song_handle_t handle = FROBNICATE_HANDLE(obj);
+ int number = obj.segment ?
+ GET_SEL32V(obj, number) :
+ -1; /* We were not going to use it anyway */
+
+ if (s->debug_mode & (1 << SCIkSOUNDCHK_NR)) {
+ int i;
+
+ SCIkdebug(SCIkSOUND, "Command 0x%x", command);
+ switch (command) {
+ case 0: sciprintf("[InitObj]"); break;
+ case 1: sciprintf("[Play]"); break;
+ case 2: sciprintf("[NOP]"); break;
+ case 3: sciprintf("[DisposeHandle]"); break;
+ case 4: sciprintf("[SetSoundOn(?)]"); break;
+ case 5: sciprintf("[Stop]"); break;
+ case 6: sciprintf("[Suspend]"); break;
+ case 7: sciprintf("[Resume]"); break;
+ case 8: sciprintf("[Get(Set?)Volume]"); break;
+ case 9: sciprintf("[Signal: Obj changed]"); break;
+ case 10: sciprintf("[Fade(?)]"); break;
+ case 11: sciprintf("[ChkDriver]"); break;
+ case 12: sciprintf("[PlayNextSong (formerly StopAll)]"); break;
+ default: sciprintf("[unknown]"); break;
+ }
+
+ sciprintf("(");
+ for (i = 1; i < argc; i++) {
+ sciprintf(PREG, PRINT_REG(argv[i]));
+ if (i + 1 < argc)
+ sciprintf(", ");
+ }
+ sciprintf(")\n");
+ }
+
+
+ switch (command) {
+ case _K_SCI0_SOUND_INIT_HANDLE:
+ if (obj.segment) {
+ sciprintf("Initializing song number %d\n", GET_SEL32V(obj, number));
+ SCRIPT_ASSERT_ZERO(sfx_add_song(&s->sound,
+ build_iterator(s, number,
+ SCI_SONG_ITERATOR_TYPE_SCI0,
+ handle),
+ 0, handle, number));
+ PUT_SEL32V(obj, state, _K_SOUND_STATUS_INITIALIZED);
+ PUT_SEL32(obj, handle, obj); /* ``sound handle'': we use the object address */
+ }
+ break;
+
+ case _K_SCI0_SOUND_PLAY_HANDLE:
+ if (obj.segment) {
+ sfx_song_set_status(&s->sound,
+ handle, SOUND_STATUS_PLAYING);
+ sfx_song_set_loops(&s->sound,
+ handle, GET_SEL32V(obj, loop));
+ PUT_SEL32V(obj, state, _K_SOUND_STATUS_PLAYING);
+ }
+ break;
+
+ case _K_SCI0_SOUND_NOP:
+ break;
+
+ case _K_SCI0_SOUND_DISPOSE_HANDLE:
+ if (obj.segment) {
+ sfx_remove_song(&s->sound, handle);
+ }
+ PUT_SEL32V(obj, handle, 0x0000);
+ break;
+
+ case _K_SCI0_SOUND_STOP_HANDLE:
+ if (obj.segment) {
+ sfx_song_set_status(&s->sound,
+ handle, SOUND_STATUS_STOPPED);
+ PUT_SEL32V(obj, state, SOUND_STATUS_STOPPED);
+ }
+ break;
+
+ case _K_SCI0_SOUND_SUSPEND_HANDLE:
+ if (obj.segment) {
+ sfx_song_set_status(&s->sound,
+ handle, SOUND_STATUS_SUSPENDED);
+ PUT_SEL32V(obj, state, SOUND_STATUS_SUSPENDED);
+ }
+ break;
+
+ case _K_SCI0_SOUND_RESUME_HANDLE:
+ if (obj.segment) {
+ sfx_song_set_status(&s->sound,
+ handle, SOUND_STATUS_PLAYING);
+ PUT_SEL32V(obj, state, SOUND_STATUS_PLAYING);
+ }
+ break;
+
+ case _K_SCI0_SOUND_MUTE_SOUND: {
+ /* if there's a parameter, we're setting it. Otherwise,
+ we're querying it. */
+ /*int param = UPARAM_OR_ALT(1,-1);
+
+ if (param != -1)
+ s->acc = s->sound_server->command(s, SOUND_COMMAND_SET_MUTE, 0, param);
+ else
+ s->acc = s->sound_server->command(s, SOUND_COMMAND_GET_MUTE, 0, 0);*/
+
+ }
+ break;
+
+ case _K_SCI0_SOUND_VOLUME: {
+ /* range from 0x0 to 0xf */
+ /* parameter optional. If present, set.*/
+ int vol = SKPV_OR_ALT(1, -1);
+
+ if (vol != -1)
+ sfx_set_volume(&s->sound, vol << 0xf);
+ else
+ s->r_acc = make_reg(0, sfx_get_volume(&s->sound) >> 0xf);
+ }
+ break;
+
+ case _K_SCI0_SOUND_UPDATE_VOL_PRI:
+ if (obj.segment) {
+ sfx_song_set_loops(&s->sound,
+ handle, GET_SEL32V(obj, loop));
+ script_set_priority(s, obj, GET_SEL32V(obj, pri));
+ }
+ break;
+
+ case _K_SCI0_SOUND_FADE_HANDLE:
+ /*s->sound_server->command(s, SOUND_COMMAND_FADE_HANDLE, obj, 120);*/ /* Fade out in 2 secs */
+ /* FIXME: The next couple of lines actually STOP the handle, rather
+ ** than fading it! */
+ if (obj.segment) {
+ sfx_song_set_status(&s->sound,
+ handle, SOUND_STATUS_STOPPED);
+ PUT_SEL32V(obj, state, SOUND_STATUS_STOPPED);
+ PUT_SEL32V(obj, signal, -1);
+ }
+ break;
+
+ case _K_SCI0_SOUND_GET_POLYPHONY:
+ s->r_acc = make_reg(0, sfx_get_player_polyphony());
+ break;
+
+ case _K_SCI0_SOUND_PLAY_NEXT:
+ /* sfx_all_stop(&s->sound);*/
+ break;
+
+ default:
+ SCIkwarn(SCIkWARNING, "Unhandled DoSound command: %x\n", command);
+
+ }
+ // process_sound_events(s); /* Take care of incoming events */
+
+ return s->r_acc;
+}
+
+int
+sfx_send_midi(sfx_state_t *self, song_handle_t handle, int channel,
+ int command, int arg1, int arg2);
+
+reg_t
+kDoSound_SCI01(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ word command = UKPV(0);
+ reg_t obj = KP_ALT(1, NULL_REG);
+ song_handle_t handle = FROBNICATE_HANDLE(obj);
+ int number = obj.segment ?
+ GET_SEL32V(obj, number) :
+ -1; /* We were not going to use it anyway */
+
+ if ((s->debug_mode & (1 << SCIkSOUNDCHK_NR))
+ && command != _K_SCI01_SOUND_UPDATE_CUES) {
+ int i;
+
+ SCIkdebug(SCIkSOUND, "Command 0x%x", command);
+ switch (command) {
+ case 0: sciprintf("[MasterVolume]"); break;
+ case 1: sciprintf("[Mute]"); break;
+ case 2: sciprintf("[NOP(2)]"); break;
+ case 3: sciprintf("[GetPolyphony]"); break;
+ case 4: sciprintf("[Update]"); break;
+ case 5: sciprintf("[Init]"); break;
+ case 6: sciprintf("[Dispose]"); break;
+ case 7: sciprintf("[Play]"); break;
+ case 8: sciprintf("[Stop]"); break;
+ case 9: sciprintf("[Suspend]"); break;
+ case 10: sciprintf("[Fade]"); break;
+ case 11: sciprintf("[UpdateCues]"); break;
+ case 12: sciprintf("[MidiSend]"); break;
+ case 13: sciprintf("[Reverb]"); break;
+ case 14: sciprintf("[Hold]"); break;
+ default: sciprintf("[unknown]"); break;
+ }
+
+ sciprintf("(");
+ for (i = 1; i < argc; i++) {
+ sciprintf(PREG, PRINT_REG(argv[i]));
+ if (i + 1 < argc)
+ sciprintf(", ");
+ }
+ sciprintf(")\n");
+ }
+
+ switch (command)
+ {
+ case _K_SCI01_SOUND_MASTER_VOLME :
+ {
+ int vol = SKPV_OR_ALT(1, -1);
+
+ if (vol != -1)
+ sfx_set_volume(&s->sound, vol << 0xf);
+ else
+ s->r_acc = make_reg(0, sfx_get_volume(&s->sound) >> 0xf);
+ break;
+ }
+ case _K_SCI01_SOUND_MUTE_SOUND :
+ {
+ /* if there's a parameter, we're setting it. Otherwise,
+ we're querying it. */
+ /*int param = UPARAM_OR_ALT(1,-1);
+
+ if (param != -1)
+ s->acc = s->sound_server->command(s, SOUND_COMMAND_SET_MUTE, 0, param);
+ else
+ s->acc = s->sound_server->command(s, SOUND_COMMAND_GET_MUTE, 0, 0);*/
+
+ break;
+ }
+ case _K_SCI01_SOUND_UNUSED :
+ {
+ break;
+ }
+ case _K_SCI01_SOUND_GET_POLYPHONY :
+ {
+ s->r_acc = make_reg(0, sfx_get_player_polyphony());
+ break;
+ }
+ case _K_SCI01_SOUND_PLAY_HANDLE :
+ {
+ int looping = GET_SEL32V(obj, loop);
+ int vol = GET_SEL32V(obj, vol);
+ int pri = GET_SEL32V(obj, pri);
+ RESTORE_BEHAVIOR rb = (RESTORE_BEHAVIOR) UKPV(2); /* Too lazy to look up a default value for this */
+
+ if (obj.segment) {
+ sfx_song_set_status(&s->sound,
+ handle, SOUND_STATUS_PLAYING);
+ sfx_song_set_loops(&s->sound,
+ handle, looping);
+ sfx_song_renice(&s->sound,
+ handle, pri);
+ song_lib_set_restore_behavior(s->sound.songlib, handle, rb);
+ }
+
+ break;
+ }
+ case _K_SCI01_SOUND_INIT_HANDLE :
+ {
+ int looping = GET_SEL32V(obj, loop);
+ int vol = GET_SEL32V(obj, vol);
+ int pri = GET_SEL32V(obj, pri);
+
+ if (obj.segment && (scir_test_resource(s->resmgr, sci_sound, number)))
+ {
+ sciprintf("Initializing song number %d\n", number);
+ SCRIPT_ASSERT_ZERO(sfx_add_song(&s->sound,
+ build_iterator(s, number,
+ SCI_SONG_ITERATOR_TYPE_SCI1,
+ handle),
+ 0, handle, number));
+ PUT_SEL32(obj, nodePtr, obj);
+ PUT_SEL32(obj, handle, obj);
+ }
+ break;
+ }
+ case _K_SCI01_SOUND_DISPOSE_HANDLE :
+ {
+ if (obj.segment) {
+ sfx_song_set_status(&s->sound,
+ handle, SOUND_STATUS_STOPPED);
+ sfx_remove_song(&s->sound, handle);
+ }
+ break;
+ }
+ case _K_SCI01_SOUND_UPDATE_HANDLE :
+ {
+ /* FIXME: Get these from the sound server */
+ int signal = 0;
+ int min = 0;
+ int sec = 0;
+ int frame = 0;
+
+ /* FIXME: Update the sound server state with 'vol' */
+ int looping = GET_SEL32V(obj, loop);
+ int vol = GET_SEL32V(obj, vol);
+ int pri = GET_SEL32V(obj, pri);
+
+ sfx_song_set_loops(&s->sound,
+ handle, looping);
+ sfx_song_renice(&s->sound, handle, pri);
+
+ SCIkdebug(SCIkSOUND, "[sound01-update-handle] -- CUE "PREG);
+
+ PUT_SEL32V(obj, signal, signal);
+ PUT_SEL32V(obj, min, min);
+ PUT_SEL32V(obj, sec, sec);
+ PUT_SEL32V(obj, frame, frame);
+
+ break;
+ }
+ case _K_SCI01_SOUND_STOP_HANDLE :
+ {
+ PUT_SEL32V(obj, signal, -1);
+ if (obj.segment) {
+ sfx_song_set_status(&s->sound,
+ handle, SOUND_STATUS_STOPPED);
+ }
+ break;
+ }
+ case _K_SCI01_SOUND_SUSPEND_HANDLE :
+ {
+ int state = UKPV(2);
+ int setstate = (state)?
+ SOUND_STATUS_SUSPENDED : SOUND_STATUS_PLAYING;
+
+ if (obj.segment) {
+ sfx_song_set_status(&s->sound,
+ handle, setstate);
+ }
+ break;
+ }
+ case _K_SCI01_SOUND_FADE_HANDLE :
+ {
+ /* There are four parameters that control the fade here.
+ * TODO: Figure out the exact semantics */
+
+ /* FIXME: The next couple of lines actually STOP the song right away */
+ PUT_SEL32V(obj, signal, -1);
+ if (obj.segment) {
+ sfx_song_set_status(&s->sound,
+ handle, SOUND_STATUS_STOPPED);
+ }
+ break;
+ }
+ case _K_SCI01_SOUND_UPDATE_CUES :
+ {
+ int signal = 0;
+ int min = 0;
+ int sec = 0;
+ int frame = 0;
+ int result = SI_LOOP; /* small hack */
+ int cue;
+
+ while (result == SI_LOOP)
+ result = sfx_poll_specific(&s->sound, handle, &cue);
+
+ switch (result) {
+
+ case SI_ABSOLUTE_CUE:
+ signal = cue;
+ SCIkdebug(SCIkSOUND, "--- [CUE] "PREG" Absolute Cue: %d\n",
+ PRINT_REG(obj), signal);
+
+ PUT_SEL32V(obj, signal, signal);
+ break;
+
+ case SI_RELATIVE_CUE:
+ signal = cue;
+ SCIkdebug(SCIkSOUND, "--- [CUE] "PREG" Relative Cue: %d\n",
+ PRINT_REG(obj), cue);
+
+ /* FIXME to match commented-out semantics
+ * below, with proper storage of dataInc and
+ * signal in the iterator code. */
+ PUT_SEL32V(obj, dataInc, signal);
+ PUT_SEL32V(obj, signal, signal);
+ break;
+
+ case SI_FINISHED:
+ SCIkdebug(SCIkSOUND, "--- [FINISHED] "PREG"\n", PRINT_REG(obj));
+ PUT_SEL32V(obj, signal, 0xffff);
+ break;
+
+ case SI_LOOP:
+ break; /* Doesn't happen */
+ }
+
+/* switch (signal) */
+/* { */
+/* case 0x00: */
+/* if (dataInc!=GET_SEL32V(obj, dataInc)) */
+/* { */
+/* PUT_SEL32V(obj, dataInc, dataInc); */
+/* PUT_SEL32V(obj, signal, dataInc+0x7f); */
+/* } else */
+/* { */
+/* PUT_SEL32V(obj, signal, signal); */
+/* } */
+/* break; */
+/* case 0xFF: /\* May be unnecessary *\/ */
+/* sfx_song_set_status(&s->sound, */
+/* handle, SOUND_STATUS_STOPPED); */
+/* break; */
+/* default : */
+/* if (dataInc!=GET_SEL32V(obj, dataInc)) */
+/* { */
+/* PUT_SEL32V(obj, dataInc, dataInc); */
+/* PUT_SEL32V(obj, signal, dataInc+0x7f); */
+/* } else */
+/* { */
+/* PUT_SEL32V(obj, signal, signal); */
+/* } */
+/* break; */
+/* } */
+
+ PUT_SEL32V(obj, min, min);
+ PUT_SEL32V(obj, sec, sec);
+ PUT_SEL32V(obj, frame, frame);
+ break;
+ }
+ case _K_SCI01_SOUND_MIDI_SEND :
+ {
+ int channel = SKPV(2);
+ int command = UKPV(3) == 0xff ?
+ 0xe0 : /* Pitch wheel */
+ 0xb0; /* UKPV(3) is actually a controller number */
+ int controller = UKPV(3);
+ int param = UKPV(4);
+
+ sfx_send_midi(&s->sound, handle,
+ channel, command, controller, param);
+ break;
+ }
+ case _K_SCI01_SOUND_REVERB :
+ {
+ break;
+ }
+ case _K_SCI01_SOUND_HOLD :
+ {
+ int flag = SKPV(2);
+ break;
+ }
+ }
+
+ return s->r_acc;
+}
+
+int
+sfx_send_midi(sfx_state_t *self, song_handle_t handle, int channel,
+ int command, int arg1, int arg2);
+
+reg_t
+kDoSound_SCI1(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ word command = UKPV(0);
+ reg_t obj = KP_ALT(1, NULL_REG);
+ song_handle_t handle = FROBNICATE_HANDLE(obj);
+ int number = obj.segment ?
+ GET_SEL32V(obj, number) :
+ -1; /* We were not going to use it anyway */
+
+ CHECK_THIS_KERNEL_FUNCTION;
+
+ if ((s->debug_mode & (1 << SCIkSOUNDCHK_NR))
+ && command != _K_SCI1_SOUND_UPDATE_CUES) {
+ int i;
+
+ SCIkdebug(SCIkSOUND, "Command 0x%x", command);
+ switch (command) {
+ case 0: sciprintf("[MasterVolume]"); break;
+ case 1: sciprintf("[Mute]"); break;
+ case 2: sciprintf("[NOP(2)]"); break;
+ case 3: sciprintf("[GetPolyphony]"); break;
+ case 4: sciprintf("[GetAudioCapability]"); break;
+ case 5: sciprintf("[GlobalSuspend]"); break;
+ case 6: sciprintf("[Init]"); break;
+ case 7: sciprintf("[Dispose]"); break;
+ case 8: sciprintf("[Play]"); break;
+ case 9: sciprintf("[Stop]"); break;
+ case 10: sciprintf("[SuspendHandle]"); break;
+ case 11: sciprintf("[Fade]"); break;
+ case 12: sciprintf("[Hold]"); break;
+ case 13: sciprintf("[Unused(13)]"); break;
+ case 14: sciprintf("[SetVolume]"); break;
+ case 15: sciprintf("[SetPriority]"); break;
+ case 16: sciprintf("[SetLoop]"); break;
+ case 17: sciprintf("[UpdateCues]"); break;
+ case 18: sciprintf("[MidiSend]"); break;
+ case 19: sciprintf("[Reverb]"); break;
+ case 20: sciprintf("[UpdateVolPri]"); break;
+ default: sciprintf("[unknown]"); break;
+ }
+
+ sciprintf("(");
+ for (i = 1; i < argc; i++) {
+ sciprintf(PREG, PRINT_REG(argv[i]));
+ if (i + 1 < argc)
+ sciprintf(", ");
+ }
+ sciprintf(")\n");
+ }
+
+ switch (command)
+ {
+ case _K_SCI1_SOUND_MASTER_VOLME :
+ {
+ /*int vol = UPARAM_OR_ALT (1, -1);
+
+ if (vol != -1)
+ s->acc = s->sound_server->command(s, SOUND_COMMAND_SET_VOLUME, 0, vol);
+ else
+ s->acc = s->sound_server->command(s, SOUND_COMMAND_GET_VOLUME, 0, 0);
+ break;*/
+ }
+ case _K_SCI1_SOUND_MUTE_SOUND :
+ {
+ /* if there's a parameter, we're setting it. Otherwise,
+ we're querying it. */
+ /*int param = UPARAM_OR_ALT(1,-1);
+
+ if (param != -1)
+ s->acc = s->sound_server->command(s, SOUND_COMMAND_SET_MUTE, 0, param);
+ else
+ s->acc = s->sound_server->command(s, SOUND_COMMAND_GET_MUTE, 0, 0);
+ break;*/
+ }
+ case _K_SCI1_SOUND_UNUSED1 :
+ {
+ break;
+ }
+ case _K_SCI1_SOUND_GET_POLYPHONY :
+ {
+ /*s->acc = s->sound_server->command(s, SOUND_COMMAND_TEST, 0, 0);*/
+ break;
+ }
+ case _K_SCI1_SOUND_GET_AUDIO_CAPABILITY :
+ {
+ return NULL_REG;
+ }
+ case _K_SCI1_SOUND_PLAY_HANDLE :
+ {
+ int looping = GET_SEL32V(obj, loop);
+ int vol = GET_SEL32V(obj, vol);
+ int pri = GET_SEL32V(obj, pri);
+ song_t *song = song_lib_find(s->sound.songlib, handle);
+
+ if (GET_SEL32V(obj, nodePtr) && (song && number != song->resource_num))
+ {
+ sfx_song_set_status(&s->sound,
+ handle, SOUND_STATUS_STOPPED);
+ sfx_remove_song(&s->sound, handle);
+ PUT_SEL32(obj, nodePtr, NULL_REG);
+ }
+
+ if (!GET_SEL32V(obj, nodePtr) && obj.segment)
+ {
+ if (!scir_test_resource(s->resmgr, sci_sound, number))
+ {
+ sciprintf("Could not open song number %d\n", number);
+ return NULL_REG;
+ }
+
+ sciprintf("Initializing song number %d\n", number);
+ SCRIPT_ASSERT_ZERO(sfx_add_song(&s->sound,
+ build_iterator(s, number,
+ SCI_SONG_ITERATOR_TYPE_SCI1,
+ handle),
+ 0, handle, number));
+ PUT_SEL32(obj, nodePtr, obj);
+ PUT_SEL32(obj, handle, obj);
+ }
+
+ if (obj.segment) {
+ sfx_song_set_status(&s->sound,
+ handle, SOUND_STATUS_PLAYING);
+ sfx_song_set_loops(&s->sound,
+ handle, looping);
+ sfx_song_renice(&s->sound,
+ handle, pri);
+ }
+
+ break;
+ }
+ case _K_SCI1_SOUND_INIT_HANDLE :
+ {
+ int looping = GET_SEL32V(obj, loop);
+ int vol = GET_SEL32V(obj, vol);
+ int pri = GET_SEL32V(obj, pri);
+
+ if (GET_SEL32V(obj, nodePtr))
+ {
+ sfx_song_set_status(&s->sound,
+ handle, SOUND_STATUS_STOPPED);
+ sfx_remove_song(&s->sound, handle);
+ }
+
+ if (obj.segment && (scir_test_resource(s->resmgr, sci_sound, number))) {
+ sciprintf("Initializing song number %d\n", number);
+ SCRIPT_ASSERT_ZERO(sfx_add_song(&s->sound,
+ build_iterator(s, number,
+ SCI_SONG_ITERATOR_TYPE_SCI1,
+ handle),
+ 0, handle, number));
+ PUT_SEL32(obj, nodePtr, obj);
+ PUT_SEL32(obj, handle, obj);
+ }
+ break;
+ }
+ case _K_SCI1_SOUND_DISPOSE_HANDLE :
+ {
+ if (obj.segment) {
+ sfx_song_set_status(&s->sound,
+ handle, SOUND_STATUS_STOPPED);
+ sfx_remove_song(&s->sound, handle);
+ }
+ break;
+ }
+ case _K_SCI1_SOUND_STOP_HANDLE :
+ {
+ PUT_SEL32V(obj, signal, -1);
+ if (obj.segment) {
+ sfx_song_set_status(&s->sound,
+ handle, SOUND_STATUS_STOPPED);
+ }
+ break;
+ }
+ case _K_SCI1_SOUND_SUSPEND_HANDLE :
+ {
+ break;
+ }
+ case _K_SCI1_SOUND_FADE_HANDLE :
+ {
+ fade_params_t fade;
+ if (obj.segment) {
+ fade.final_volume = UKPV(2);
+ fade.ticks_per_step = UKPV(3);
+ fade.step_size = UKPV(4);
+ fade.action = UKPV(5) ?
+ FADE_ACTION_FADE_AND_STOP :
+ FADE_ACTION_FADE_AND_CONT;
+
+ sfx_song_set_fade(&s->sound,
+ handle,
+ &fade);
+
+ /* FIXME: The next couple of lines actually STOP the handle, rather
+ ** than fading it! */
+ if (UKPV(5))
+ {
+ PUT_SEL32V(obj, signal, -1);
+ PUT_SEL32V(obj, nodePtr, 0);
+ PUT_SEL32V(obj, handle, 0);
+ sfx_song_set_status(&s->sound,
+ handle, SOUND_STATUS_STOPPED);
+ }
+ }
+ break;
+ }
+ case _K_SCI1_SOUND_HOLD_HANDLE :
+ {
+ int value = SKPV(2);
+
+ sfx_song_set_hold(&s->sound,
+ handle, value);
+ break;
+ }
+ case _K_SCI1_SOUND_UNUSED2 :
+ {
+ break;
+ }
+ case _K_SCI1_SOUND_SET_HANDLE_VOLUME :
+ {
+ break;
+ }
+ case _K_SCI1_SOUND_SET_HANDLE_PRIORITY :
+ {
+ int value = SKPV(2);
+
+ script_set_priority(s, obj, value);
+ break;
+ }
+ case _K_SCI1_SOUND_SET_HANDLE_LOOP :
+ {
+ break;
+ }
+ case _K_SCI1_SOUND_UPDATE_CUES :
+ {
+ int signal = 0;
+ int min = 0;
+ int sec = 0;
+ int frame = 0;
+ int result = SI_LOOP; /* small hack */
+ int cue;
+
+ while (result == SI_LOOP)
+ result = sfx_poll_specific(&s->sound, handle, &cue);
+
+ switch (result) {
+
+ case SI_ABSOLUTE_CUE:
+ signal = cue;
+ fprintf(stderr, "[CUE] "PREG" Absolute Cue: %d\n",
+ PRINT_REG(obj), signal);
+
+ PUT_SEL32V(obj, signal, signal);
+ break;
+
+ case SI_RELATIVE_CUE:
+ fprintf(stderr, "[CUE] "PREG" Relative Cue: %d\n",
+ PRINT_REG(obj), cue);
+
+ PUT_SEL32V(obj, dataInc, cue);
+ PUT_SEL32V(obj, signal, cue + 127);
+ break;
+
+ case SI_FINISHED:
+ PUT_SEL32V(obj, signal, 0xffff);
+ break;
+
+ case SI_LOOP:
+ break; /* Doesn't happen */
+ }
+ break;
+ }
+ case _K_SCI1_SOUND_MIDI_SEND :
+ {
+ sfx_send_midi(&s->sound, handle,
+ UKPV(2), UKPV(3), UKPV(4), UKPV(5));
+ break;
+ }
+ case _K_SCI1_SOUND_REVERB :
+ {
+ break;
+ }
+ case _K_SCI1_SOUND_UPDATE_VOL_PRI :
+ {
+ break;
+ }
+ }
+ return s->r_acc;
+}
+
+reg_t
+kDoSound(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ if (s->version>=SCI_VERSION_FTU_DOSOUND_VARIANT_2)
+ return kDoSound_SCI1(s, funct_nr, argc, argv);
+ else if (s->version>=SCI_VERSION_FTU_DOSOUND_VARIANT_1)
+ return kDoSound_SCI01(s, funct_nr, argc, argv);
+ else
+ return kDoSound_SCI0(s, funct_nr, argc, argv);
+}
+
+reg_t
+kDoAudio(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ switch (UKPV(0))
+ {
+ case _K_SCI1_AUDIO_POSITION :
+ return make_reg(0, -1); /* Finish immediately */
+ }
+
+ return s->r_acc;
+}
+
diff --git a/engines/sci/engine/kstring.c b/engines/sci/engine/kstring.c
new file mode 100644
index 0000000000..c4b5426329
--- /dev/null
+++ b/engines/sci/engine/kstring.c
@@ -0,0 +1,802 @@
+/***************************************************************************
+ kstring.c Copyright (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]
+
+***************************************************************************/
+/* String and parser handling */
+
+#include <sciresource.h>
+#include <engine.h>
+#include "message.h"
+
+#define CHECK_OVERFLOW1(pt, size, rv) \
+ if (((pt) - (str_base)) + (size) > maxsize) { \
+ SCIkwarn(SCIkERROR, "String expansion exceeded heap boundaries\n"); \
+ return rv;\
+ }
+
+char *
+kernel_lookup_text(state_t *s, reg_t address, int index)
+ /* Returns the string the script intended to address */
+{
+ char *seeker;
+ resource_t *textres;
+
+ if (address.segment)
+ return (char *) kernel_dereference_bulk_pointer(s, address, 0);
+ else {
+ int textlen;
+ int _index = index;
+ textres = scir_find_resource(s->resmgr, sci_text, address.offset, 0);
+
+ if (!textres) {
+ SCIkwarn(SCIkERROR, "text.%03d not found\n", address);
+ return NULL; /* Will probably segfault */
+ }
+
+ textlen = textres->size;
+ seeker = (char *) textres->data;
+
+ while (index--)
+ while ((textlen--) && (*seeker++));
+
+ if (textlen)
+ return seeker;
+ else {
+ SCIkwarn(SCIkERROR, "Index %d out of bounds in text.%03d\n", _index, address);
+ return 0;
+ }
+
+ }
+}
+
+
+/*************************************************************/
+/* Parser */
+/**********/
+
+#ifdef SCI_SIMPLE_SAID_CODE
+int
+vocab_match_simple(state_t *s, heap_ptr addr)
+{
+ int nextitem;
+ int listpos = 0;
+
+ if (!s->parser_valid)
+ return SAID_NO_MATCH;
+
+ if (s->parser_valid == 2) { /* debug mode: sim_said */
+ do {
+ sciprintf("DEBUGMATCH: ");
+ nextitem = s->heap[addr++];
+
+ if (nextitem < 0xf0) {
+ nextitem = nextitem << 8 | s->heap[addr++];
+ if (s->parser_nodes[listpos].type
+ || nextitem != s->parser_nodes[listpos++].content.value)
+ return SAID_NO_MATCH;
+ } else {
+
+ if (nextitem == 0xff)
+ return (s->parser_nodes[listpos++].type == -1)? SAID_FULL_MATCH : SAID_NO_MATCH; /* Finished? */
+
+ if (s->parser_nodes[listpos].type != 1
+ || nextitem != s->parser_nodes[listpos++].content.value)
+ return SAID_NO_MATCH;
+
+ }
+ } while (42);
+ } else { /* normal simple match mode */
+ return vocab_simple_said_test(s, addr);
+ }
+}
+#endif /* SCI_SIMPLE_SAID_CODE */
+
+
+reg_t
+kSaid(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ reg_t heap_said_block = argv[0];
+ byte *said_block;
+ int new_lastmatch;
+
+ if (!heap_said_block.segment)
+ return NULL_REG;
+
+ said_block = (byte *) kernel_dereference_bulk_pointer(s, heap_said_block, 0);
+
+ if (!said_block) {
+ SCIkdebug(SCIkWARNING, "Said on non-string, pointer "PREG"\n", PRINT_REG(heap_said_block));
+ return NULL_REG;
+ }
+
+ if (s->debug_mode & (1 << SCIkPARSER_NR)) {
+ SCIkdebug(SCIkPARSER, "Said block:", 0);
+ vocab_decypher_said_block(s, said_block);
+ }
+
+ if (IS_NULL_REG(s->parser_event) || (GET_SEL32V(s->parser_event, claimed))) {
+ return NULL_REG;
+ }
+
+#ifdef SCI_SIMPLE_SAID_CODE
+
+ s->acc = 0;
+
+ if (s->parser_lastmatch_word == SAID_FULL_MATCH)
+ return; /* Matched before; we're not doing any more matching work today. */
+
+ if ((new_lastmatch = vocab_match_simple(s, said_block)) != SAID_NO_MATCH) {
+
+ if (s->debug_mode & (1 << SCIkPARSER_NR))
+ sciprintf("Match (simple).\n");
+ s->acc = 1;
+
+ if (new_lastmatch == SAID_FULL_MATCH) /* Finished matching? */
+ PUT_SELECTOR(s->parser_event, claimed, 1); /* claim event */
+ /* otherwise, we have a partial match: Set new lastmatch word in all cases. */
+
+ s->parser_lastmatch_word = new_lastmatch;
+ }
+
+#else /* !SCI_SIMPLE_SAID_CODE */
+ if ((new_lastmatch = said(s, said_block, (s->debug_mode & (1 << SCIkPARSER_NR))))
+ != SAID_NO_MATCH) { /* Build and possibly display a parse tree */
+
+ if (s->debug_mode & (1 << SCIkPARSER_NR))
+ sciprintf("Match.\n");
+
+ s->r_acc = make_reg(0, 1);
+
+ if (new_lastmatch != SAID_PARTIAL_MATCH)
+ PUT_SEL32V(s->parser_event, claimed, 1);
+
+ s->parser_lastmatch_word = new_lastmatch;
+
+ } else {
+ s->parser_lastmatch_word = SAID_NO_MATCH;
+ return NULL_REG;
+ }
+#endif /* !SCI_SIMPLE_SAID_CODE */
+ return s->r_acc;
+}
+
+
+reg_t
+kSetSynonyms(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ reg_t object = argv[0];
+ list_t *list;
+ node_t *node;
+ int script;
+ int synpos = 0;
+
+ if (s->synonyms_nr)
+ free(s->synonyms);
+
+ s->synonyms_nr = 0;
+
+ list = LOOKUP_LIST(GET_SEL32(object, elements));
+ node = LOOKUP_NODE(list->first);
+
+ while (node) {
+ reg_t objpos = node->value;
+ int seg;
+ int synonyms_nr;
+
+ script = GET_SEL32V(objpos, number);
+ seg = sm_seg_get(&(s->seg_manager), script);
+
+ if (seg >= 0) synonyms_nr = sm_get_synonyms_nr(&(s->seg_manager), seg, SEG_ID);
+
+ if (synonyms_nr) {
+ byte *synonyms;
+
+ synonyms = sm_get_synonyms(&(s->seg_manager), seg, SEG_ID);
+ if (synonyms) {
+ int i;
+ if (s->synonyms_nr)
+ s->synonyms = (synonym_t*)sci_realloc(s->synonyms,
+ sizeof(synonym_t) * (s->synonyms_nr + synonyms_nr));
+ else
+ s->synonyms = (synonym_t*)sci_malloc(sizeof(synonym_t) * synonyms_nr);
+
+ s->synonyms_nr += synonyms_nr;
+
+ SCIkdebug(SCIkPARSER, "Setting %d synonyms for script.%d\n",
+ synonyms_nr, script);
+
+ if (synonyms_nr > 16384) {
+ SCIkwarn(SCIkERROR, "Segtable corruption: script.%03d has %d synonyms!\n",
+ script, synonyms_nr);
+ /* We used to reset the corrupted value here. I really don't think it's appropriate.
+ * Lars */
+ } else
+ for (i = 0; i < synonyms_nr; i++) {
+ s->synonyms[synpos].replaceant = getInt16(synonyms + i * 4);
+ s->synonyms[synpos].replacement = getInt16(synonyms + i * 4 + 2);
+
+ synpos++;
+ }
+ } else SCIkwarn(SCIkWARNING, "Synonyms of script.%03d were requested, but script is not available\n");
+
+ }
+
+ node = LOOKUP_NODE(node->succ);
+ }
+
+ SCIkdebug(SCIkPARSER, "A total of %d synonyms are active now.\n", s->synonyms_nr);
+
+ if (!s->synonyms_nr)
+ s->synonyms = NULL;
+ return s->r_acc;
+}
+
+
+
+reg_t
+kParse(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ reg_t stringpos = argv[0];
+ char *string = kernel_dereference_char_pointer(s, stringpos, 0);
+ int words_nr;
+ char *error;
+ result_word_t *words;
+ reg_t event = argv[1];
+
+ s->parser_event = event;
+
+ s->parser_lastmatch_word = SAID_NO_MATCH;
+
+ if (s->parser_valid == 2) {
+ sciprintf("Parsing skipped: Parser in simparse mode\n");
+ return s->r_acc;
+ }
+
+ words = vocab_tokenize_string(string, &words_nr,
+ s->parser_words, s->parser_words_nr,
+ s->parser_suffices, s->parser_suffices_nr,
+ &error);
+ s->parser_valid = 0; /* not valid */
+
+ if (words) {
+
+ int syntax_fail = 0;
+
+ vocab_synonymize_tokens(words, words_nr, s->synonyms, s->synonyms_nr);
+
+ s->r_acc = make_reg(0, 1);
+
+ if (s->debug_mode & (1 << SCIkPARSER_NR)) {
+ int i;
+
+ SCIkdebug(SCIkPARSER, "Parsed to the following blocks:\n", 0);
+
+ for (i = 0; i < words_nr; i++)
+ SCIkdebug(SCIkPARSER, " Type[%04x] Group[%04x]\n", words[i].w_class, words[i].group);
+ }
+
+ if (vocab_build_parse_tree(&(s->parser_nodes[0]), words, words_nr, s->parser_branches,
+ s->parser_rules))
+ syntax_fail = 1; /* Building a tree failed */
+
+#ifdef SCI_SIMPLE_SAID_CODE
+ vocab_build_simple_parse_tree(&(s->parser_nodes[0]), words, words_nr);
+#endif /* SCI_SIMPLE_SAID_CODE */
+
+ free(words);
+
+ if (syntax_fail) {
+
+ s->r_acc = make_reg(0, 1);
+ PUT_SEL32V(event, claimed, 1);
+
+ invoke_selector(INV_SEL(s->game_obj, syntaxFail, 0), 2, s->parser_base, stringpos);
+ /* Issue warning */
+
+ SCIkdebug(SCIkPARSER, "Tree building failed\n");
+
+ } else {
+ s->parser_valid = 1;
+ PUT_SEL32V(event, claimed, 0);
+#ifndef SCI_SIMPLE_SAID_CODE
+ if (s->debug_mode & (1 << SCIkPARSER_NR))
+ vocab_dump_parse_tree("Parse-tree", s->parser_nodes);
+#endif /* !SCI_SIMPLE_SAID_CODE */
+ }
+
+ } else {
+
+ s->r_acc = make_reg(0, 0);
+ PUT_SEL32V(event, claimed, 1);
+ if (error) {
+ char *pbase_str = kernel_dereference_char_pointer(s, s->parser_base, 0);
+ strcpy(pbase_str, error);
+ SCIkdebug(SCIkPARSER,"Word unknown: %s\n", error);
+ /* Issue warning: */
+
+ invoke_selector(INV_SEL(s->game_obj, wordFail, 0), 2, s->parser_base, stringpos);
+ free(error);
+ return make_reg(0, 1); /* Tell them that it dind't work */
+ }
+ }
+
+ return s->r_acc;
+}
+
+
+reg_t
+kStrEnd(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ reg_t address = argv[0];
+ char *seeker = kernel_dereference_char_pointer(s, address, 0);
+
+ while (*seeker++)
+ ++address.offset;
+
+ return address;
+}
+
+reg_t
+kStrCat(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ char *s1 = kernel_dereference_char_pointer(s, argv[0], 0);
+ char *s2 = kernel_dereference_char_pointer(s, argv[1], 0);
+
+ strcat(s1, s2);
+ return argv[0];
+}
+
+reg_t
+kStrCmp(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ char *s1 = kernel_dereference_char_pointer(s, argv[0], 0);
+ char *s2 = kernel_dereference_char_pointer(s, argv[1], 0);
+
+ if (argc > 2)
+ return make_reg(0, strncmp(s1, s2, UKPV(2)));
+ else
+ return make_reg(0, strcmp(s1, s2));
+}
+
+
+reg_t
+kStrCpy(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ char *dest = (char *) kernel_dereference_bulk_pointer(s, argv[0], 0);
+ char *src = (char *) kernel_dereference_bulk_pointer(s, argv[1], 0);
+
+ if (!dest) {
+ SCIkdebug(SCIkWARNING, "Attempt to strcpy TO invalid pointer "PREG"!\n",
+ PRINT_REG(argv[0]));
+ return NULL_REG;
+ }
+ if (!src) {
+ SCIkdebug(SCIkWARNING, "Attempt to strcpy FROM invalid pointer "PREG"!\n",
+ PRINT_REG(argv[1]));
+ return NULL_REG;
+ }
+
+ if (argc > 2)
+ {
+ int length = SKPV(2);
+
+ if (length>=0)
+ strncpy(dest, src, length);
+ else {
+ if (s->seg_manager.heap[argv[0].segment]->type == MEM_OBJ_DYNMEM) {
+ reg_t *srcp = (reg_t *) src;
+
+ int i;
+ SCIkdebug(SCIkWARNING, "Performing reg_t to raw conversion for AvoidPath\n");
+ for (i = 0; i < -length / 2; i++) {
+ dest[2 * i] = srcp->offset & 0xff;
+ dest[2 * i + 1] = srcp->offset >> 8;
+ srcp++;
+ }
+ } else
+ memcpy(dest, src, -length);
+ }
+ }
+ else
+ strcpy(dest, src);
+
+ return argv[0];
+}
+
+
+reg_t
+kStrAt(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ unsigned char *dest = (unsigned char *) kernel_dereference_bulk_pointer(s, argv[0], 0);
+ reg_t *dest2;
+
+ if (!dest) {
+ SCIkdebug(SCIkWARNING, "Attempt to StrAt at invalid pointer "PREG"!\n",
+ PRINT_REG(argv[0]));
+ return NULL_REG;
+ }
+
+ if ((argc == 2) &&
+/* Our pathfinder already works around the issue we're trying to fix */
+ (strcmp(sm_get_description(&(s->seg_manager), argv[0]),
+ AVOIDPATH_DYNMEM_STRING) != 0) &&
+ ((strlen((const char*)dest) < 2) || (!is_print_str((char*)dest))))
+ /* SQ4 array handling detected */
+ {
+#ifndef WORDS_BIGENDIAN
+ int odd = KP_UINT(argv[1]) & 1;
+#else
+ int odd = !(KP_UINT(argv[1]) & 1);
+#endif
+ dest2 = ((reg_t *) dest)+(KP_UINT(argv[1])/2);
+ dest = ((unsigned char *) (&dest2->offset))+odd;
+ } else dest += KP_UINT(argv[1]);
+
+ s->r_acc = make_reg(0, *dest);
+
+ if (argc > 2)
+ *dest = KP_SINT(argv[2]); /* Request to modify this char */
+
+ return s->r_acc;
+}
+
+
+reg_t
+kReadNumber(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ char *source = kernel_dereference_char_pointer(s, argv[0], 0);
+
+ while (isspace(*source))
+ source++; /* Skip whitespace */
+
+ if (*source == '$') /* SCI uses this for hex numbers */
+ return make_reg(0, (gint16)strtol(source + 1, NULL, 16)); /* Hex */
+ else
+ return make_reg(0, (gint16)strtol(source, NULL, 10)); /* Force decimal */
+}
+
+
+#define ALIGN_NONE 0
+#define ALIGN_RIGHT 1
+#define ALIGN_LEFT -1
+#define ALIGN_CENTRE 2
+
+/* Format(targ_address, textresnr, index_inside_res, ...)
+** or
+** Format(targ_address, heap_text_addr, ...)
+** Formats the text from text.textresnr (offset index_inside_res) or heap_text_addr according to
+** the supplied parameters and writes it to the targ_address.
+*/
+reg_t
+kFormat(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ int *arguments;
+ reg_t dest = argv[0];
+ char *target = (char *) kernel_dereference_bulk_pointer(s, dest, 0);
+ reg_t position = argv[1]; /* source */
+ int index = UKPV(2);
+ char *source;
+ char *str_base = target;
+ int mode = 0;
+ int paramindex = 0; /* Next parameter to evaluate */
+ char xfer;
+ int i;
+ int startarg;
+ int str_leng = 0; /* Used for stuff like "%13s" */
+ int unsigned_var = 0;
+ int maxsize = 4096; /* Arbitrary... */
+
+
+ if (position.segment)
+ startarg = 2;
+ else
+ startarg = 3; /* First parameter to use for formatting */
+
+ source = kernel_lookup_text(s, position, index);
+
+ SCIkdebug(SCIkSTRINGS, "Formatting \"%s\"\n", source);
+
+
+ arguments = (int*)sci_malloc(sizeof(int) * argc);
+#ifdef SATISFY_PURIFY
+ memset(arguments, 0, sizeof(int) * argc);
+#endif
+
+ for (i = startarg; i < argc; i++)
+ arguments[i-startarg] = UKPV(i); /* Parameters are copied to prevent overwriting */
+
+ while ((xfer = *source++)) {
+ if (xfer == '%') {
+ if (mode == 1) {
+ CHECK_OVERFLOW1(target, 2, NULL_REG);
+ *target++ = '%'; /* Literal % by using "%%" */
+ mode = 0;
+ } else {
+ mode = 1;
+ str_leng = 0;
+ }
+ } else if (mode == 1) { /* xfer != '%' */
+ char fillchar = ' ';
+ int align = ALIGN_NONE;
+
+ char *writestart = target; /* Start of the written string, used after the switch */
+
+ /* int writelength; -- unused atm */
+
+ if (xfer && (isdigit(xfer) || xfer == '-' || xfer == '=')) {
+ char *destp;
+
+ if (xfer == '0')
+ fillchar = '0'; else
+
+ if (xfer == '=') {
+ align = ALIGN_CENTRE;
+ source++;
+ } else
+
+ if (isdigit(xfer))
+ source--; /* Stepped over length argument */
+
+ str_leng = strtol(source, &destp, 10);
+
+ if (destp > source)
+ source = destp;
+
+ if (str_leng < 0) {
+ align = ALIGN_LEFT;
+ str_leng = -str_leng;
+ } else if (align != ALIGN_CENTRE)
+ align = ALIGN_RIGHT;
+
+ xfer = *source++;
+ } else
+ str_leng = 0;
+
+ CHECK_OVERFLOW1(target, str_leng + 1, NULL_REG);
+
+ switch (xfer) {
+ case 's': { /* Copy string */
+ reg_t reg = argv[startarg + paramindex];
+ char *tempsource = kernel_lookup_text(s, reg,
+ arguments[paramindex + 1]);
+ int slen = strlen(tempsource);
+ int extralen = str_leng - slen;
+ CHECK_OVERFLOW1(target, extralen, NULL_REG);
+ if (extralen < 0)
+ extralen = 0;
+
+ if (reg.segment) /* Heap address? */
+ paramindex++;
+ else
+ paramindex += 2; /* No, text resource address */
+
+ switch (align) {
+
+ case ALIGN_NONE:
+ case ALIGN_RIGHT:
+ while (extralen-- > 0)
+ *target++ = ' '; /* Format into the text */
+ break;
+
+ case ALIGN_CENTRE: {
+ int half_extralen = extralen >> 1;
+ while (half_extralen-- > 0)
+ *target++ = ' '; /* Format into the text */
+ break;}
+
+ default: break;
+
+ }
+
+ strcpy(target, tempsource);
+ target += slen;
+
+ switch (align) {
+
+ case ALIGN_CENTRE: {
+ int half_extralen;
+ align = 0;
+ half_extralen = extralen - (extralen >> 1);
+ while (half_extralen-- > 0)
+ *target++ = ' '; /* Format into the text */
+ break;}
+
+ default: break;
+
+ }
+
+ mode = 0;
+ }
+ break;
+
+ case 'c': { /* insert character */
+ CHECK_OVERFLOW1(target, 2, NULL_REG);
+ if (align >= 0)
+ while (str_leng-- > 1)
+ *target++ = ' '; /* Format into the text */
+
+ *target++ = arguments[paramindex++];
+ mode = 0;
+ }
+ break;
+
+ case 'x':
+ case 'u': unsigned_var = 1;
+ case 'd': { /* Copy decimal */
+ /* int templen; -- unused atm */
+ const char *format_string = "%d";
+
+ if (xfer == 'x')
+ format_string = "%x";
+
+ if (!unsigned_var)
+ if (arguments[paramindex] & 0x8000)
+ /* sign extend */
+ arguments[paramindex] = (~0xffff) | arguments[paramindex];
+
+ target += sprintf(target, format_string, arguments[paramindex++]);
+ CHECK_OVERFLOW1(target, 0, NULL_REG);
+
+ unsigned_var = 0;
+
+ mode = 0;
+ }
+ break;
+ default:
+ *target = '%';
+ target++;
+ *target = xfer;
+ target++;
+ mode = 0;
+ }
+
+ if (align) {
+ int written = target - writestart;
+ int padding = str_leng - written;
+
+ if (padding > 0) {
+ if (align > 0) {
+ memmove(writestart + padding,
+ writestart, written);
+ memset(writestart, fillchar, padding);
+ } else {
+ memset(target, ' ', padding);
+ }
+ target += padding;
+ }
+ }
+ }else { /* mode != 1 */
+ *target = xfer;
+ target++;
+ }
+ }
+
+ free(arguments);
+
+ *target = 0; /* Terminate string */
+ return dest; /* Return target addr */
+}
+
+
+reg_t
+kStrLen(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ char *str = kernel_dereference_char_pointer(s, argv[0], 0);
+
+ return make_reg(0, strlen(str));
+}
+
+
+reg_t
+kGetFarText(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ resource_t *textres = scir_find_resource(s->resmgr, sci_text, UKPV(0), 0);
+ char *seeker;
+ int counter = UKPV(1);
+
+
+ if (!textres) {
+ SCIkwarn(SCIkERROR, "text.%d does not exist\n", UKPV(0));
+ return NULL_REG;
+ }
+
+ seeker = (char *) textres->data;
+
+ while (counter--)
+ while (*seeker++);
+ /* The second parameter (counter) determines the number of the string inside the text
+ ** resource.
+ */
+
+ strcpy(kernel_dereference_char_pointer(s, argv[2], 0), seeker); /* Copy the string and get return value */
+ return argv[2];
+}
+
+#define DUMMY_MESSAGE "No MESSAGE support in FreeSCI yet"
+
+static message_state_t state;
+
+reg_t
+kMessage(state_t *s, int funct_nr, int argc, reg_t *argv)
+{
+ if (!state.initialized)
+ message_state_initialize(s->resmgr, &state);
+
+ switch (UKPV(0))
+ {
+ case 0 :
+ {
+ char *buffer = argc == 7 ? kernel_dereference_char_pointer(s, argv[6], 0) : NULL;
+ message_tuple_t tuple;
+ int module = UKPV(1);
+
+ tuple.noun = UKPV(2);
+ tuple.verb = UKPV(3);
+ tuple.cond = UKPV(4);
+ tuple.seq = UKPV(5);
+
+ if (message_state_load_res(&state, module) && message_get_specific(&state, &tuple))
+ {
+ if (buffer)
+ message_get_text(&state, buffer, 100);
+ return make_reg(0, message_get_talker(&state)); /* Talker id */
+ } else
+ {
+ if (buffer) strcpy(buffer, DUMMY_MESSAGE);
+ return NULL_REG;
+ }
+ }
+ case 1 :
+ {
+ char *buffer = argc == 7 ? kernel_dereference_char_pointer(s, argv[6], 0) : NULL;
+
+ if (message_get_next(&state))
+ {
+ if (buffer)
+ message_get_text(&state, buffer, 100);
+ return make_reg(0, message_get_talker(&state)); /* Talker id */
+ } else
+ {
+ if (buffer) strcpy(buffer, DUMMY_MESSAGE);
+ return NULL_REG;
+ }
+ }
+ case 2 :
+ {
+ message_tuple_t tuple;
+ int module = UKPV(1);
+ tuple.noun = UKPV(2);
+ tuple.verb = UKPV(3);
+ tuple.cond = UKPV(4);
+ tuple.seq = UKPV(5);
+
+ if (message_state_load_res(&state, module) && message_get_specific(&state, &tuple))
+ return make_reg(0, message_get_length(&state)+1);
+ else return NULL_REG;
+ }
+ }
+
+ return NULL_REG;
+}
diff --git a/engines/sci/engine/makefile.dos b/engines/sci/engine/makefile.dos
new file mode 100644
index 0000000000..0d4c5fac8e
--- /dev/null
+++ b/engines/sci/engine/makefile.dos
@@ -0,0 +1,22 @@
+#
+# FreeSCI/DOS Makefile
+#
+# 19991220 rink created this file
+# 20000615 rink updated this file
+#
+#
+TARGET : libsciengine.a
+
+FILES = savegame.o kernel.o kscripts.o klists.o kfile.o kgraphics.o \
+ kmath.o kevent.o kstring.o kmenu.o ksound.o vm.o game.o \
+ scriptdebug.o heap.o simplesaid.o said.o grammar.o
+
+
+CC = gcc
+CFLAGS = -g -c -I../include -I../.. -D_DOS -DHAVE_LIBPNG -DHAVE_UNISTD_H
+
+clean:
+ del *.o *.a
+
+libsciengine.a: ${FILES}
+ ar r libsciengine.a ${FILES}
diff --git a/engines/sci/engine/message.c b/engines/sci/engine/message.c
new file mode 100644
index 0000000000..97171a9763
--- /dev/null
+++ b/engines/sci/engine/message.c
@@ -0,0 +1,226 @@
+/***************************************************************************
+ message.c Copyright (C) 2008 Lars Skovlund
+
+
+ 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:
+
+ Lars Skovlund (LS) [lskovlun@image.dk]
+
+***************************************************************************/
+
+#include "message.h"
+
+static
+int get_talker_trivial(index_record_cursor_t *cursor)
+{
+ return -1;
+}
+
+/* Version 2.101 and later code ahead */
+
+static
+void index_record_parse_2101(index_record_cursor_t *cursor, message_tuple_t *t)
+{
+ int noun = *(cursor->index_record + 0);
+ int verb = *(cursor->index_record + 1);
+
+ t->noun = noun;
+ t->verb = verb;
+ t->cond = t->seq = 0;
+}
+
+static
+void index_record_get_text_2101(index_record_cursor_t *cursor, char *buffer, int buffer_size)
+{
+ int offset = getUInt16(cursor->index_record + 2);
+ char *stringptr = (char *)cursor->resource_beginning + offset;
+
+ strncpy(buffer, stringptr, buffer_size);
+}
+
+static
+int header_get_index_record_count_2101(byte *header)
+{
+ return getUInt16(header + 4);
+}
+
+/* Version 3.411 and later code ahead */
+
+static
+void index_record_parse_3411(index_record_cursor_t *cursor, message_tuple_t *t)
+{
+ int noun = *(cursor->index_record + 0);
+ int verb = *(cursor->index_record + 1);
+ int cond = *(cursor->index_record + 2);
+ int seq = *(cursor->index_record + 3);
+
+ t->noun = noun;
+ t->verb = verb;
+ t->cond = cond;
+ t->seq = seq;
+}
+
+static
+int index_record_get_talker_3411(index_record_cursor_t *cursor)
+{
+ return *(cursor->index_record + 4);
+}
+
+static
+void index_record_get_text_3411(index_record_cursor_t *cursor, char *buffer, int buffer_size)
+{
+ int offset = getUInt16(cursor->index_record + 5);
+ char *stringptr = (char *)cursor->resource_beginning + offset;
+
+ strncpy(buffer, stringptr, buffer_size);
+}
+
+static
+int header_get_index_record_count_3411(byte *header)
+{
+ return getUInt16(header + 8);
+}
+
+/* Generic code from here on */
+
+static
+int four_tuple_match(message_tuple_t *t1, message_tuple_t *t2)
+{
+ return
+ t1->noun == t2->noun &&
+ t1->verb == t2->verb &&
+ t1->cond == t2->cond &&
+ t1->seq == t2->seq;
+}
+
+static
+void index_record_cursor_initialize(message_state_t *state, index_record_cursor_t *cursor)
+{
+ cursor->resource_beginning = state->current_res->data;
+ cursor->index_record = state->index_records;
+ cursor->index = 1;
+}
+
+static
+int index_record_next(message_state_t *state, index_record_cursor_t *cursor)
+{
+ if (cursor->index == state->record_count)
+ return 0;
+ cursor->index_record += state->handler->index_record_size;
+ cursor->index ++;
+ return 1;
+}
+
+static
+int index_record_find(message_state_t *state, message_tuple_t *t, index_record_cursor_t *cursor)
+{
+ message_tuple_t looking_at;
+ int found = 0;
+
+ index_record_cursor_initialize(state, cursor);
+
+ do
+ {
+ state->handler->parse(cursor, &looking_at);
+ if (four_tuple_match(t, &looking_at))
+ found = 1;
+ } while (!found && index_record_next(state, cursor));
+
+ // FIXME: Recursion not handled yet
+
+ return found;
+}
+
+int message_get_specific(message_state_t *state, message_tuple_t *t)
+{
+ return index_record_find(state, t, &state->engine_cursor);
+}
+
+int message_get_next(message_state_t *state)
+{
+ return index_record_next(state, &state->engine_cursor);
+}
+
+int message_get_talker(message_state_t *state)
+{
+ return state->handler->get_talker(&state->engine_cursor);
+}
+
+int message_get_text(message_state_t *state, char *buffer, int length)
+{
+ state->handler->get_text(&state->engine_cursor, buffer, length);
+ return strlen(buffer);
+}
+
+int message_get_length(message_state_t *state)
+{
+ char buffer[500];
+
+ state->handler->get_text(&state->engine_cursor, buffer, sizeof(buffer));
+ return strlen(buffer);
+}
+
+int message_state_load_res(message_state_t *state, int module)
+{
+ if (state->module == module)
+ return 1;
+
+ state->module = module;
+ state->current_res = scir_find_resource(state->resmgr, sci_message, module, 0);
+
+ if (state->current_res == NULL ||
+ state->current_res->data == NULL)
+ {
+ sciprintf("Message subsystem: Failed to load %d.MSG\n", module);
+ return 0;
+ }
+
+ state->record_count = state->handler->index_record_count(state->current_res->data);
+ state->index_records = state->current_res->data + state->handler->header_size;
+
+ index_record_cursor_initialize(state, &state->engine_cursor);
+ return 1;
+}
+
+static message_handler_t fixed_handler = {3411,
+ index_record_parse_3411,
+ index_record_get_talker_3411,
+ index_record_get_text_3411,
+ header_get_index_record_count_3411,
+
+ 10,
+ 11};
+
+void message_state_initialize(resource_mgr_t *resmgr, message_state_t *state)
+{
+ resource_t *tester = scir_find_resource(resmgr, sci_message, 0, 0);
+ int version;
+
+// if (tester == NULL) return;
+
+// version = getUInt16(tester->data);
+
+ state->initialized = 1;
+ state->module = -1;
+ state->resmgr = resmgr;
+ state->current_res = NULL;
+ state->record_count = 0;
+ state->handler = &fixed_handler;
+}
diff --git a/engines/sci/engine/message.h b/engines/sci/engine/message.h
new file mode 100644
index 0000000000..7618e55429
--- /dev/null
+++ b/engines/sci/engine/message.h
@@ -0,0 +1,83 @@
+/***************************************************************************
+ message.h Copyright (C) 2008 Lars Skovlund
+
+
+ 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:
+
+ Lars Skovlund (LS) [lskovlun@image.dk]
+
+***************************************************************************/
+
+#include <sciresource.h>
+
+typedef struct
+{
+ int noun;
+ int verb;
+ int cond;
+ int seq;
+} message_tuple_t;
+
+typedef struct
+{
+ byte *index_record;
+ int index;
+ byte *resource_beginning;
+} index_record_cursor_t;
+
+typedef int index_record_size_t(void);
+typedef void parse_index_record_t(index_record_cursor_t *index_record, message_tuple_t *t);
+typedef int get_talker_t(index_record_cursor_t *cursor);
+typedef void get_text_t(index_record_cursor_t *cursor, char *buffer, int buffer_size);
+typedef int index_record_count_t(byte *header);
+
+typedef struct
+{
+ int version_id;
+ parse_index_record_t *parse;
+ get_talker_t *get_talker;
+ get_text_t *get_text;
+ index_record_count_t *index_record_count;
+
+ int header_size;
+ int index_record_size;
+} message_handler_t;
+
+typedef struct
+{
+ int initialized;
+ message_handler_t *handler;
+ resource_mgr_t *resmgr;
+ resource_t *current_res;
+ int module;
+ int record_count;
+ byte *index_records;
+ index_record_cursor_t engine_cursor;
+} message_state_t;
+
+int message_get_specific(message_state_t *state, message_tuple_t *t);
+int message_get_next(message_state_t *state);
+int message_get_talker(message_state_t *state);
+int message_get_length(message_state_t *state);
+int message_get_text(message_state_t *state, char *buffer, int length);
+int message_state_load_res(message_state_t *state, int module);
+void message_state_initialize(resource_mgr_t *resmgr, message_state_t *state);
+
+
diff --git a/engines/sci/engine/said.c b/engines/sci/engine/said.c
new file mode 100644
index 0000000000..2e90920add
--- /dev/null
+++ b/engines/sci/engine/said.c
@@ -0,0 +1,2561 @@
+/* A Bison parser, made by GNU Bison 2.3. */
+
+/* Skeleton implementation for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+ This program 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.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.3"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ WGROUP = 258,
+ YY_COMMA = 259,
+ YY_AMP = 260,
+ YY_SLASH = 261,
+ YY_PARENO = 262,
+ YY_PARENC = 263,
+ YY_BRACKETSO = 264,
+ YY_BRACKETSC = 265,
+ YY_HASH = 266,
+ YY_LT = 267,
+ YY_GT = 268,
+ YY_BRACKETSO_LT = 269,
+ YY_BRACKETSO_SLASH = 270,
+ YY_LT_BRACKETSO = 271,
+ YY_LT_PARENO = 272
+ };
+#endif
+/* Tokens. */
+#define WGROUP 258
+#define YY_COMMA 259
+#define YY_AMP 260
+#define YY_SLASH 261
+#define YY_PARENO 262
+#define YY_PARENC 263
+#define YY_BRACKETSO 264
+#define YY_BRACKETSC 265
+#define YY_HASH 266
+#define YY_LT 267
+#define YY_GT 268
+#define YY_BRACKETSO_LT 269
+#define YY_BRACKETSO_SLASH 270
+#define YY_LT_BRACKETSO 271
+#define YY_LT_PARENO 272
+
+
+
+
+/* Copy the first part of user declarations. */
+#line 28 "said.y"
+
+
+#include <engine.h>
+
+#define SAID_BRANCH_NULL 0
+
+#define MAX_SAID_TOKENS 128
+
+/* Maximum number of words to be expected in a parsed sentence */
+#define AUGMENT_MAX_WORDS 64
+
+
+#define ANYWORD 0xfff
+
+#define WORD_TYPE_BASE 0x141
+#define WORD_TYPE_REF 0x144
+#define WORD_TYPE_SYNTACTIC_SUGAR 0x145
+
+#define AUGMENT_SENTENCE_PART_BRACKETS 0x152
+
+/* Minor numbers */
+#define AUGMENT_SENTENCE_MINOR_MATCH_PHRASE 0x14c
+#define AUGMENT_SENTENCE_MINOR_MATCH_WORD 0x153
+#define AUGMENT_SENTENCE_MINOR_RECURSE 0x144
+#define AUGMENT_SENTENCE_MINOR_PARENTHESES 0x14f
+
+
+#undef YYDEBUG /*1*/
+/*#define SAID_DEBUG*/
+/*#define SCI_DEBUG_PARSE_TREE_AUGMENTATION*/ /* uncomment to debug parse tree augmentation*/
+
+
+#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION
+#define scidprintf sciprintf
+#else
+#define scidprintf if (0) sciprintf
+#endif
+
+
+static char *said_parse_error;
+
+static int said_token;
+static int said_tokens_nr;
+static int said_tokens[MAX_SAID_TOKENS];
+
+static int said_blessed; /* increminated by said_top_branch */
+
+static int said_tree_pos; /* Set to 0 if we're out of space */
+#define SAID_TREE_START 4; /* Reserve space for the 4 top nodes */
+
+#define VALUE_IGNORE -424242
+
+static parse_tree_node_t said_tree[VOCAB_TREE_NODES];
+
+typedef int wgroup_t;
+typedef int tree_t;
+typedef int said_spec_t;
+
+static tree_t
+said_aug_branch(int, int, tree_t, tree_t);
+
+static tree_t
+said_attach_branch(tree_t, tree_t);
+/*
+static tree_t
+said_wgroup_branch(wgroup_t);
+*/
+static said_spec_t
+said_top_branch(tree_t);
+
+static tree_t
+said_paren(tree_t, tree_t);
+
+static tree_t
+said_value(int, tree_t);
+
+static tree_t
+said_terminal(int);
+
+
+static int
+yylex(void);
+
+static int
+yyerror(char *s)
+{
+ said_parse_error = sci_strdup(s);
+ return 1; /* Abort */
+}
+
+
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef int YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 216 of yacc.c. */
+#line 232 "said.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int i)
+#else
+static int
+YYID (i)
+ int i;
+#endif
+{
+ return i;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined _STDLIB_H \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss;
+ YYSTYPE yyvs;
+ };
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack, Stack, yysize); \
+ Stack = &yyptr->Stack; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 23
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 80
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 18
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 13
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 35
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 69
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 272
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint8 yyprhs[] =
+{
+ 0, 0, 3, 6, 10, 15, 16, 18, 19, 21,
+ 24, 29, 31, 34, 39, 41, 43, 45, 49, 51,
+ 55, 59, 64, 70, 73, 75, 77, 79, 83, 88,
+ 92, 97, 100, 105, 109, 112
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int8 yyrhs[] =
+{
+ 19, 0, -1, 21, 20, -1, 21, 22, 20, -1,
+ 21, 22, 23, 20, -1, -1, 13, -1, -1, 27,
+ -1, 6, 27, -1, 15, 6, 27, 10, -1, 6,
+ -1, 6, 27, -1, 15, 6, 27, 10, -1, 6,
+ -1, 3, -1, 26, -1, 9, 26, 10, -1, 24,
+ -1, 7, 27, 8, -1, 26, 4, 26, -1, 26,
+ 14, 29, 10, -1, 26, 4, 9, 26, 10, -1,
+ 25, 28, -1, 25, -1, 28, -1, 29, -1, 14,
+ 29, 10, -1, 29, 14, 29, 10, -1, 12, 24,
+ 30, -1, 17, 7, 27, 8, -1, 12, 26, -1,
+ 16, 9, 26, 10, -1, 12, 26, 30, -1, 12,
+ 26, -1, 17, 7, 27, 8, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint8 yyrline[] =
+{
+ 0, 138, 138, 140, 142, 148, 149, 156, 157, 163,
+ 165, 167, 173, 175, 177, 183, 188, 190, 195, 197,
+ 199, 201, 203, 209, 211, 213, 219, 221, 223, 229,
+ 231, 233, 235, 241, 243, 245
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "WGROUP", "YY_COMMA", "YY_AMP",
+ "YY_SLASH", "YY_PARENO", "YY_PARENC", "YY_BRACKETSO", "YY_BRACKETSC",
+ "YY_HASH", "YY_LT", "YY_GT", "YY_BRACKETSO_LT", "YY_BRACKETSO_SLASH",
+ "YY_LT_BRACKETSO", "YY_LT_PARENO", "$accept", "saidspec", "optcont",
+ "leftspec", "midspec", "rightspec", "word", "cwordset", "wordset",
+ "expr", "cwordrefset", "wordrefset", "recref", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 18, 19, 19, 19, 20, 20, 21, 21, 22,
+ 22, 22, 23, 23, 23, 24, 25, 25, 26, 26,
+ 26, 26, 26, 27, 27, 27, 28, 28, 28, 29,
+ 29, 29, 29, 30, 30, 30
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 2, 3, 4, 0, 1, 0, 1, 2,
+ 4, 1, 2, 4, 1, 1, 1, 3, 1, 3,
+ 3, 4, 5, 2, 1, 1, 1, 3, 4, 3,
+ 4, 2, 4, 3, 2, 4
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 7, 15, 0, 0, 0, 0, 0, 0, 0, 5,
+ 18, 24, 16, 8, 25, 26, 0, 0, 18, 31,
+ 0, 0, 0, 1, 11, 6, 0, 2, 5, 23,
+ 0, 0, 0, 19, 17, 0, 0, 29, 27, 0,
+ 0, 9, 0, 14, 0, 3, 5, 0, 20, 0,
+ 0, 34, 0, 32, 30, 0, 12, 0, 4, 0,
+ 21, 28, 33, 0, 10, 0, 22, 35, 13
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int8 yydefgoto[] =
+{
+ -1, 8, 27, 9, 28, 46, 10, 11, 12, 13,
+ 14, 15, 37
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -24
+static const yytype_int8 yypact[] =
+{
+ -1, -24, -1, 62, 62, 54, 1, 5, 18, 38,
+ -24, 47, 3, -24, -24, 12, 23, 15, -3, 3,
+ 28, 62, -1, -24, -1, -24, 42, -24, 39, -24,
+ 53, 54, 54, -24, -24, 62, 50, -24, -24, 29,
+ 41, -24, -1, -1, 52, -24, 55, 62, 3, 57,
+ 63, 20, -1, -24, -24, 64, -24, -1, -24, 32,
+ -24, -24, -24, 67, -24, 66, -24, -24, -24
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int8 yypgoto[] =
+{
+ -24, -24, -23, -24, -24, -24, 68, -24, 0, -2,
+ 69, -4, 26
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If zero, do what YYDEFACT says.
+ If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -1
+static const yytype_uint8 yytable[] =
+{
+ 16, 20, 1, 17, 19, 45, 2, 30, 3, 35,
+ 21, 4, 22, 5, 36, 6, 7, 31, 23, 30,
+ 40, 39, 41, 58, 30, 34, 32, 49, 50, 31,
+ 48, 33, 35, 30, 31, 51, 30, 36, 38, 53,
+ 55, 56, 66, 31, 24, 43, 31, 59, 42, 54,
+ 63, 25, 25, 26, 44, 65, 1, 52, 57, 4,
+ 2, 5, 47, 6, 7, 1, 4, 60, 25, 2,
+ 6, 7, 18, 61, 64, 67, 68, 62, 0, 0,
+ 29
+};
+
+static const yytype_int8 yycheck[] =
+{
+ 2, 5, 3, 3, 4, 28, 7, 4, 9, 12,
+ 9, 12, 7, 14, 17, 16, 17, 14, 0, 4,
+ 22, 21, 24, 46, 4, 10, 14, 31, 32, 14,
+ 30, 8, 12, 4, 14, 35, 4, 17, 10, 10,
+ 42, 43, 10, 14, 6, 6, 14, 47, 6, 8,
+ 52, 13, 13, 15, 15, 57, 3, 7, 6, 12,
+ 7, 14, 9, 16, 17, 3, 12, 10, 13, 7,
+ 16, 17, 4, 10, 10, 8, 10, 51, -1, -1,
+ 11
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 3, 7, 9, 12, 14, 16, 17, 19, 21,
+ 24, 25, 26, 27, 28, 29, 27, 26, 24, 26,
+ 29, 9, 7, 0, 6, 13, 15, 20, 22, 28,
+ 4, 14, 14, 8, 10, 12, 17, 30, 10, 26,
+ 27, 27, 6, 6, 15, 20, 23, 9, 26, 29,
+ 29, 26, 7, 10, 8, 27, 27, 6, 20, 26,
+ 10, 10, 30, 27, 10, 27, 10, 8, 10
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+
+#define YYFAIL goto yyerrlab
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ yytoken = YYTRANSLATE (yychar); \
+ YYPOPSTACK (1); \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (YYID (N)) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (YYID (0))
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+ This macro was not mandated originally: define only if we know
+ we won't break user code: when these are the locations we know. */
+
+#ifndef YY_LOCATION_PRINT
+# if YYLTYPE_IS_TRIVIAL
+# define YY_LOCATION_PRINT(File, Loc) \
+ fprintf (File, "%d.%d-%d.%d", \
+ (Loc).first_line, (Loc).first_column, \
+ (Loc).last_line, (Loc).last_column)
+# else
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (!yyvaluep)
+ return;
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+#else
+static void
+yy_stack_print (bottom, top)
+ yytype_int16 *bottom;
+ yytype_int16 *top;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; bottom <= top; ++bottom)
+ YYFPRINTF (stderr, " %d", *bottom);
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+ YYSTYPE *yyvsp;
+ int yyrule;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ fprintf (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ );
+ fprintf (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into YYRESULT an error message about the unexpected token
+ YYCHAR while in state YYSTATE. Return the number of bytes copied,
+ including the terminating null byte. If YYRESULT is null, do not
+ copy anything; just return the number of bytes that would be
+ copied. As a special case, return 0 if an ordinary "syntax error"
+ message will do. Return YYSIZE_MAXIMUM if overflow occurs during
+ size calculation. */
+static YYSIZE_T
+yysyntax_error (char *yyresult, int yystate, int yychar)
+{
+ int yyn = yypact[yystate];
+
+ if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
+ return 0;
+ else
+ {
+ int yytype = YYTRANSLATE (yychar);
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ int yysize_overflow = 0;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ int yyx;
+
+# if 0
+ /* This is so xgettext sees the translatable formats that are
+ constructed on the fly. */
+ YY_("syntax error, unexpected %s");
+ YY_("syntax error, unexpected %s, expecting %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+# endif
+ char *yyfmt;
+ char const *yyf;
+ static char const yyunexpected[] = "syntax error, unexpected %s";
+ static char const yyexpecting[] = ", expecting %s";
+ static char const yyor[] = " or %s";
+ char yyformat[sizeof yyunexpected
+ + sizeof yyexpecting - 1
+ + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+ * (sizeof yyor - 1))];
+ char const *yyprefix = yyexpecting;
+
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yycount = 1;
+
+ yyarg[0] = yytname[yytype];
+ yyfmt = yystpcpy (yyformat, yyunexpected);
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ yyformat[sizeof yyunexpected - 1] = '\0';
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+ yyfmt = yystpcpy (yyfmt, yyprefix);
+ yyprefix = yyor;
+ }
+
+ yyf = YY_(yyformat);
+ yysize1 = yysize + yystrlen (yyf);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+
+ if (yysize_overflow)
+ return YYSIZE_MAXIMUM;
+
+ if (yyresult)
+ {
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ char *yyp = yyresult;
+ int yyi = 0;
+ while ((*yyp = *yyf) != '\0')
+ {
+ if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyf += 2;
+ }
+ else
+ {
+ yyp++;
+ yyf++;
+ }
+ }
+ }
+ return yysize;
+ }
+}
+#endif /* YYERROR_VERBOSE */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ YYUSE (yyvaluep);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+/* The look-ahead symbol. */
+int yychar;
+
+/* The semantic value of the look-ahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+
+ int yystate;
+ int yyn;
+ int yyresult;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+ /* Look-ahead token as an internal (translated) token number. */
+ int yytoken = 0;
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+ /* Three stacks and their tools:
+ `yyss': related to states,
+ `yyvs': related to semantic values,
+ `yyls': related to locations.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss = yyssa;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs = yyvsa;
+ YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+
+ YYSIZE_T yystacksize = YYINITDEPTH;
+
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss);
+ YYSTACK_RELOCATE (yyvs);
+
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ look-ahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to look-ahead token. */
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a look-ahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the look-ahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 2:
+#line 139 "said.y"
+ { (yyval) = said_top_branch(said_attach_branch((yyvsp[(1) - (2)]), (yyvsp[(2) - (2)]))); }
+ break;
+
+ case 3:
+#line 141 "said.y"
+ { (yyval) = said_top_branch(said_attach_branch((yyvsp[(1) - (3)]), said_attach_branch((yyvsp[(2) - (3)]), (yyvsp[(3) - (3)])))); }
+ break;
+
+ case 4:
+#line 143 "said.y"
+ { (yyval) = said_top_branch(said_attach_branch((yyvsp[(1) - (4)]), said_attach_branch((yyvsp[(2) - (4)]), said_attach_branch((yyvsp[(3) - (4)]), (yyvsp[(4) - (4)]))))); }
+ break;
+
+ case 5:
+#line 148 "said.y"
+ { (yyval) = SAID_BRANCH_NULL; }
+ break;
+
+ case 6:
+#line 150 "said.y"
+ { (yyval) = said_paren(said_value(0x14b, said_value(0xf900, said_terminal(0xf900))), SAID_BRANCH_NULL); }
+ break;
+
+ case 7:
+#line 156 "said.y"
+ { (yyval) = SAID_BRANCH_NULL; }
+ break;
+
+ case 8:
+#line 158 "said.y"
+ { (yyval) = said_paren(said_value(0x141, said_value(0x149, (yyvsp[(1) - (1)]))), SAID_BRANCH_NULL); }
+ break;
+
+ case 9:
+#line 164 "said.y"
+ { (yyval) = said_aug_branch(0x142, 0x14a, (yyvsp[(2) - (2)]), SAID_BRANCH_NULL); }
+ break;
+
+ case 10:
+#line 166 "said.y"
+ { (yyval) = said_aug_branch(0x152, 0x142, said_aug_branch(0x142, 0x14a, (yyvsp[(3) - (4)]), SAID_BRANCH_NULL), SAID_BRANCH_NULL); }
+ break;
+
+ case 11:
+#line 168 "said.y"
+ { (yyval) = SAID_BRANCH_NULL; }
+ break;
+
+ case 12:
+#line 174 "said.y"
+ { (yyval) = said_aug_branch(0x143, 0x14a, (yyvsp[(2) - (2)]), SAID_BRANCH_NULL); }
+ break;
+
+ case 13:
+#line 176 "said.y"
+ { (yyval) = said_aug_branch(0x152, 0x143, said_aug_branch(0x143, 0x14a, (yyvsp[(3) - (4)]), SAID_BRANCH_NULL), SAID_BRANCH_NULL); }
+ break;
+
+ case 14:
+#line 178 "said.y"
+ { (yyval) = SAID_BRANCH_NULL; }
+ break;
+
+ case 15:
+#line 184 "said.y"
+ { (yyval) = said_paren(said_value(0x141, said_value(0x153, said_terminal((yyvsp[(1) - (1)])))), SAID_BRANCH_NULL); }
+ break;
+
+ case 16:
+#line 189 "said.y"
+ { (yyval) = said_aug_branch(0x141, 0x14f, (yyvsp[(1) - (1)]), SAID_BRANCH_NULL); }
+ break;
+
+ case 17:
+#line 191 "said.y"
+ { (yyval) = said_aug_branch(0x141, 0x14f, said_aug_branch(0x152, 0x14c, said_aug_branch(0x141, 0x14f, (yyvsp[(2) - (3)]), SAID_BRANCH_NULL), SAID_BRANCH_NULL), SAID_BRANCH_NULL); }
+ break;
+
+ case 18:
+#line 196 "said.y"
+ { (yyval) = (yyvsp[(1) - (1)]); }
+ break;
+
+ case 19:
+#line 198 "said.y"
+ { (yyval) = (yyvsp[(1) - (3)]); }
+ break;
+
+ case 20:
+#line 200 "said.y"
+ { (yyval) = said_attach_branch((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); }
+ break;
+
+ case 21:
+#line 202 "said.y"
+ { (yyval) = said_attach_branch((yyvsp[(1) - (4)]), (yyvsp[(3) - (4)])); }
+ break;
+
+ case 22:
+#line 204 "said.y"
+ { (yyval) = said_attach_branch((yyvsp[(1) - (5)]), (yyvsp[(3) - (5)])); }
+ break;
+
+ case 23:
+#line 210 "said.y"
+ { (yyval) = said_attach_branch((yyvsp[(1) - (2)]), (yyvsp[(2) - (2)])); }
+ break;
+
+ case 24:
+#line 212 "said.y"
+ { (yyval) = (yyvsp[(1) - (1)]); }
+ break;
+
+ case 25:
+#line 214 "said.y"
+ { (yyval) = (yyvsp[(1) - (1)]); }
+ break;
+
+ case 26:
+#line 220 "said.y"
+ { (yyval) = (yyvsp[(1) - (1)]); }
+ break;
+
+ case 27:
+#line 222 "said.y"
+ { (yyval) = said_aug_branch(0x152, 0x144, (yyvsp[(2) - (3)]), SAID_BRANCH_NULL); }
+ break;
+
+ case 28:
+#line 224 "said.y"
+ { (yyval) = said_attach_branch((yyvsp[(1) - (4)]), said_aug_branch(0x152, 0x144, (yyvsp[(3) - (4)]), SAID_BRANCH_NULL)); }
+ break;
+
+ case 29:
+#line 230 "said.y"
+ { (yyval) = said_aug_branch(0x144, 0x14f, (yyvsp[(2) - (3)]), (yyvsp[(3) - (3)])); }
+ break;
+
+ case 30:
+#line 232 "said.y"
+ { (yyval) = said_aug_branch(0x144, 0x14f, said_aug_branch(0x141, 0x144, (yyvsp[(2) - (4)]), SAID_BRANCH_NULL), SAID_BRANCH_NULL); }
+ break;
+
+ case 31:
+#line 234 "said.y"
+ { (yyval) = said_aug_branch(0x144, 0x14f, (yyvsp[(2) - (2)]), SAID_BRANCH_NULL); }
+ break;
+
+ case 32:
+#line 236 "said.y"
+ { (yyval) = said_aug_branch(0x152, 0x144, said_aug_branch(0x144, 0x14f, (yyvsp[(3) - (4)]), SAID_BRANCH_NULL), SAID_BRANCH_NULL); }
+ break;
+
+ case 33:
+#line 242 "said.y"
+ { (yyval) = said_aug_branch(0x141, 0x144, said_aug_branch(0x144, 0x14f, (yyvsp[(2) - (3)]), SAID_BRANCH_NULL), (yyvsp[(3) - (3)])); }
+ break;
+
+ case 34:
+#line 244 "said.y"
+ { (yyval) = said_aug_branch(0x141, 0x144, said_aug_branch(0x144, 0x14f, (yyvsp[(2) - (2)]), SAID_BRANCH_NULL), SAID_BRANCH_NULL); }
+ break;
+
+ case 35:
+#line 246 "said.y"
+ { (yyval) = said_aug_branch(0x141, 0x14c, (yyvsp[(2) - (4)]), SAID_BRANCH_NULL); }
+ break;
+
+
+/* Line 1267 of yacc.c. */
+#line 1647 "said.c"
+ default: break;
+ }
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (YY_("syntax error"));
+#else
+ {
+ YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
+ if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
+ {
+ YYSIZE_T yyalloc = 2 * yysize;
+ if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
+ yyalloc = YYSTACK_ALLOC_MAXIMUM;
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yyalloc);
+ if (yymsg)
+ yymsg_alloc = yyalloc;
+ else
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ }
+ }
+
+ if (0 < yysize && yysize <= yymsg_alloc)
+ {
+ (void) yysyntax_error (yymsg, yystate, yychar);
+ yyerror (yymsg);
+ }
+ else
+ {
+ yyerror (YY_("syntax error"));
+ if (yysize != 0)
+ goto yyexhaustedlab;
+ }
+ }
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse look-ahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse look-ahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEOF && yychar != YYEMPTY)
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
+}
+
+
+#line 251 "said.y"
+
+
+
+int
+parse_yy_token_lookup[] = {YY_COMMA, YY_AMP, YY_SLASH, YY_PARENO, YY_PARENC, YY_BRACKETSO, YY_BRACKETSC,
+ YY_HASH, YY_LT, YY_GT};
+
+static int
+yylex(void)
+{
+ int retval = said_tokens[said_token++];
+
+ if (retval < SAID_LONG(SAID_FIRST)) {
+ yylval = retval;
+ retval = WGROUP;
+ } else {
+ retval >>= 8;
+
+ if (retval == SAID_TERM)
+ retval = 0;
+ else {
+ assert(retval >= SAID_FIRST);
+ retval = parse_yy_token_lookup[retval - SAID_FIRST];
+ if (retval == YY_BRACKETSO) {
+ if ((said_tokens[said_token] >> 8) == SAID_LT)
+ retval = YY_BRACKETSO_LT;
+ else
+ if ((said_tokens[said_token] >> 8) == SAID_SLASH)
+ retval = YY_BRACKETSO_SLASH;
+ } else if (retval == YY_LT && (said_tokens[said_token] >> 8) == SAID_BRACKO) {
+ retval = YY_LT_BRACKETSO;
+ } else if (retval == YY_LT && (said_tokens[said_token] >> 8) == SAID_PARENO) {
+ retval = YY_LT_PARENO;
+ }
+ }
+ }
+
+ return retval;
+}
+
+#define SAID_NEXT_NODE ((said_tree_pos == 0) || (said_tree_pos >= VOCAB_TREE_NODES))? said_tree_pos = 0 : said_tree_pos++
+
+static inline int
+said_leaf_node(tree_t pos, int value)
+{
+ said_tree[pos].type = PARSE_TREE_NODE_LEAF;
+
+ if (value != VALUE_IGNORE)
+ said_tree[pos].content.value = value;
+
+ return pos;
+}
+
+static inline int
+said_branch_node(tree_t pos, int left, int right)
+{
+ said_tree[pos].type = PARSE_TREE_NODE_BRANCH;
+
+ if (left != VALUE_IGNORE)
+ said_tree[pos].content.branches[0] = left;
+
+ if (right != VALUE_IGNORE)
+ said_tree[pos].content.branches[1] = right;
+
+ return pos;
+}
+
+
+static tree_t
+said_paren(tree_t t1, tree_t t2)
+{
+ if (t1)
+ return said_branch_node(SAID_NEXT_NODE,
+ t1,
+ t2
+ );
+ else
+ return t2;
+}
+
+static tree_t
+said_value(int val, tree_t t)
+{
+ return said_branch_node(SAID_NEXT_NODE,
+ said_leaf_node(SAID_NEXT_NODE, val),
+ t
+ );
+
+}
+
+static tree_t
+said_terminal(int val)
+{
+ return said_leaf_node(SAID_NEXT_NODE, val);
+}
+
+
+static tree_t
+said_aug_branch(int n1, int n2, tree_t t1, tree_t t2)
+{
+ int retval;
+
+ retval = said_branch_node(SAID_NEXT_NODE,
+ said_branch_node(SAID_NEXT_NODE,
+ said_leaf_node(SAID_NEXT_NODE, n1),
+ said_branch_node(SAID_NEXT_NODE,
+ said_leaf_node(SAID_NEXT_NODE, n2),
+ t1
+ )
+ ),
+ t2
+ );
+
+#ifdef SAID_DEBUG
+ fprintf(stderr,"AUG(0x%x, 0x%x, [%04x], [%04x]) = [%04x]\n", n1, n2, t1, t2, retval);
+#endif
+
+ return retval;
+}
+
+static tree_t
+said_attach_branch(tree_t base, tree_t attacheant)
+{
+#ifdef SAID_DEBUG
+ fprintf(stderr,"ATT2([%04x], [%04x]) = [%04x]\n", base, attacheant, base);
+#endif
+
+ if (!attacheant)
+ return base;
+ if (!base)
+ return attacheant;
+
+ if (!base)
+ return 0; /* Happens if we're out of space */
+
+ said_branch_node(base, VALUE_IGNORE, attacheant);
+
+ return base;
+}
+
+static said_spec_t
+said_top_branch(tree_t first)
+{
+#ifdef SAID_DEBUG
+ fprintf(stderr, "TOP([%04x])\n", first);
+#endif
+ said_branch_node(0, 1, 2);
+ said_leaf_node(1, 0x141); /* Magic number #1 */
+ said_branch_node(2, 3, first);
+ said_leaf_node(3, 0x13f); /* Magic number #2 */
+
+ ++said_blessed;
+
+ return 0;
+}
+
+
+int
+said_parse_spec(state_t *s, byte *spec)
+{
+ int nextitem;
+
+ said_parse_error = NULL;
+ said_token = 0;
+ said_tokens_nr = 0;
+ said_blessed = 0;
+
+ said_tree_pos = SAID_TREE_START;
+
+ do {
+ nextitem = *spec++;
+ if (nextitem < SAID_FIRST)
+ said_tokens[said_tokens_nr++] = nextitem << 8 | *spec++;
+ else
+ said_tokens[said_tokens_nr++] = SAID_LONG(nextitem);
+
+ } while ((nextitem != SAID_TERM) && (said_tokens_nr < MAX_SAID_TOKENS));
+
+ if (nextitem == SAID_TERM)
+ yyparse();
+ else {
+ sciprintf("Error: SAID spec is too long\n");
+ return 1;
+ }
+
+ if (said_parse_error) {
+ sciprintf("Error while parsing SAID spec: %s\n", said_parse_error);
+ free(said_parse_error);
+ return 1;
+ }
+
+ if (said_tree_pos == 0) {
+ sciprintf("Error: Out of tree space while parsing SAID spec\n");
+ return 1;
+ }
+
+ if (said_blessed != 1) {
+ sciprintf("Error: Found %d top branches\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+/**********************/
+/**** Augmentation ****/
+/**********************/
+
+
+/** primitive functions **/
+
+#define AUG_READ_BRANCH(a, br, p) \
+ if (tree[p].type != PARSE_TREE_NODE_BRANCH) \
+ return 0; \
+ a = tree[p].content.branches[br];
+
+#define AUG_READ_VALUE(a, p) \
+ if (tree[p].type != PARSE_TREE_NODE_LEAF) \
+ return 0; \
+ a = tree[p].content.value;
+
+#define AUG_ASSERT(i) \
+ if (!i) return 0;
+
+static int
+aug_get_next_sibling(parse_tree_node_t *tree, int pos, int *first, int *second)
+ /* Returns the next sibling relative to the specified position in 'tree',
+ ** sets *first and *second to its augment node values, returns the new position
+ ** or 0 if there was no next sibling
+ */
+{
+ int seek, valpos;
+
+ AUG_READ_BRANCH(pos, 1, pos);
+ AUG_ASSERT(pos);
+ AUG_READ_BRANCH(seek, 0, pos);
+ AUG_ASSERT(seek);
+
+ /* Now retreive first value */
+ AUG_READ_BRANCH(valpos, 0, seek);
+ AUG_ASSERT(valpos);
+ AUG_READ_VALUE(*first, valpos);
+
+ /* Get second value */
+ AUG_READ_BRANCH(seek, 1, seek);
+ AUG_ASSERT(seek);
+ AUG_READ_BRANCH(valpos, 0, seek);
+ AUG_ASSERT(valpos);
+ AUG_READ_VALUE(*second, valpos);
+
+ return pos;
+}
+
+
+static int
+aug_get_wgroup(parse_tree_node_t *tree, int pos)
+ /* Returns 0 if pos in tree is not the root of a 3-element list, otherwise
+ ** it returns the last element (which, in practice, is the word group
+ */
+{
+ int val;
+
+ AUG_READ_BRANCH(pos, 0, pos);
+ AUG_ASSERT(pos);
+ AUG_READ_BRANCH(pos, 1, pos);
+ AUG_ASSERT(pos);
+ AUG_READ_BRANCH(pos, 1, pos);
+ AUG_ASSERT(pos);
+ AUG_READ_VALUE(val, pos);
+
+ return val;
+}
+
+
+static int
+aug_get_base_node(parse_tree_node_t *tree)
+{
+ int startpos = 0;
+ AUG_READ_BRANCH(startpos, 1, startpos);
+ return startpos;
+}
+
+
+/** semi-primitive functions **/
+
+
+static int
+aug_get_first_child(parse_tree_node_t *tree, int pos, int *first, int *second)
+ /* like aug_get_next_sibling, except that it recurses into the tree and
+ ** finds the first child (usually *not* Ayanami Rei) of the current branch
+ ** rather than its next sibling.
+ */
+{
+ AUG_READ_BRANCH(pos, 0, pos);
+ AUG_ASSERT(pos);
+ AUG_READ_BRANCH(pos, 1, pos);
+ AUG_ASSERT(pos);
+
+ return aug_get_next_sibling(tree, pos, first, second);
+}
+
+static void
+aug_find_words_recursively(parse_tree_node_t *tree, int startpos,
+ int *base_words, int *base_words_nr,
+ int *ref_words, int *ref_words_nr,
+ int maxwords, int refbranch)
+ /* Finds and lists all base (141) and reference (144) words */
+{
+ int major, minor;
+ int word;
+ int pos = aug_get_first_child(tree, startpos, &major, &minor);
+
+ /* if (major == WORD_TYPE_REF)
+ refbranch = 1;*/
+
+ while (pos) {
+ if ((word = aug_get_wgroup(tree, pos))) { /* found a word */
+
+ if (!refbranch && major == WORD_TYPE_BASE) {
+ if ((*base_words_nr) == maxwords) {
+ sciprintf("Out of regular words\n");
+ return; /* return gracefully */
+ }
+
+ base_words[*base_words_nr] = word; /* register word */
+ ++(*base_words_nr);
+
+ }
+ if (major == WORD_TYPE_REF || refbranch) {
+ if ((*ref_words_nr) == maxwords) {
+ sciprintf("Out of reference words\n");
+ return; /* return gracefully */
+ }
+
+ ref_words[*ref_words_nr] = word; /* register word */
+ ++(*ref_words_nr);
+
+ }
+ if (major != WORD_TYPE_SYNTACTIC_SUGAR && major != WORD_TYPE_BASE && major != WORD_TYPE_REF)
+ sciprintf("aug_find_words_recursively(): Unknown word type %03x\n", major);
+
+ } else /* Did NOT find a word group: Attempt to recurse */
+ aug_find_words_recursively(tree, pos, base_words, base_words_nr,
+ ref_words, ref_words_nr, maxwords, refbranch || major == WORD_TYPE_REF);
+
+ pos = aug_get_next_sibling(tree, pos, &major, &minor);
+ }
+}
+
+
+static void
+aug_find_words(parse_tree_node_t *tree, int startpos,
+ int *base_words, int *base_words_nr,
+ int *ref_words, int *ref_words_nr,
+ int maxwords)
+ /* initializing wrapper for aug_find_words_recursively() */
+{
+ *base_words_nr = 0;
+ *ref_words_nr = 0;
+
+ aug_find_words_recursively(tree, startpos, base_words, base_words_nr, ref_words, ref_words_nr, maxwords, 0);
+}
+
+
+static inline int
+aug_contains_word(int *list, int length, int word)
+{
+ int i;
+ if (word == ANYWORD)
+ return (length);
+
+ for (i = 0; i < length; i++)
+ if (list[i] == word)
+ return 1;
+
+ return 0;
+}
+
+
+static int
+augment_sentence_expression(parse_tree_node_t *saidt, int augment_pos,
+ parse_tree_node_t *parset, int parse_branch,
+ int major, int minor,
+ int *base_words, int base_words_nr,
+ int *ref_words, int ref_words_nr);
+
+static int
+augment_match_expression_p(parse_tree_node_t *saidt, int augment_pos,
+ parse_tree_node_t *parset, int parse_basepos,
+ int major, int minor,
+ int *base_words, int base_words_nr,
+ int *ref_words, int ref_words_nr)
+{
+ int cmajor, cminor, cpos;
+ cpos = aug_get_first_child(saidt, augment_pos, &cmajor, &cminor);
+ if (!cpos) {
+ sciprintf("augment_match_expression_p(): Empty condition\n");
+ return 1;
+ }
+
+ scidprintf("Attempting to match (%03x %03x (%03x %03x\n", major, minor, cmajor, cminor);
+
+ if ((major == WORD_TYPE_BASE) && (minor == AUGMENT_SENTENCE_MINOR_RECURSE))
+ return augment_match_expression_p(saidt, cpos,
+ parset, parse_basepos,
+ cmajor, cminor,
+ base_words, base_words_nr,
+ ref_words, ref_words_nr);
+
+
+ switch (major) {
+
+ case WORD_TYPE_BASE:
+ while (cpos) {
+ if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_WORD) {
+ int word = aug_get_wgroup(saidt, cpos);
+ scidprintf("Looking for word %03x\n", word);
+
+ if (aug_contains_word(base_words, base_words_nr, word))
+ return 1;
+ } else if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_PHRASE) {
+ if (augment_sentence_expression(saidt, cpos,
+ parset, parse_basepos,
+ cmajor, cminor,
+ base_words, base_words_nr,
+ ref_words, ref_words_nr))
+ return 1;
+ } else if (cminor == AUGMENT_SENTENCE_MINOR_PARENTHESES) {
+ int gc_major, gc_minor;
+ int gchild = aug_get_first_child(saidt, cpos, &gc_major, &gc_minor);
+
+ while (gchild) {
+ if (augment_match_expression_p(saidt, cpos,
+ parset, parse_basepos,
+ major, minor,
+ base_words, base_words_nr,
+ ref_words, ref_words_nr))
+ return 1;
+ gchild = aug_get_next_sibling(saidt, gchild, &gc_major, &gc_minor);
+ }
+ } else
+ sciprintf("augment_match_expression_p(): Unknown type 141 minor number %3x\n", cminor);
+
+ cpos = aug_get_next_sibling(saidt, cpos, &cmajor, &cminor);
+
+ }
+ break;
+
+ case WORD_TYPE_REF:
+ while (cpos) {
+ if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_WORD) {
+ int word = aug_get_wgroup(saidt, cpos);
+ scidprintf("Looking for refword %03x\n", word);
+
+ if (aug_contains_word(ref_words, ref_words_nr, word))
+ return 1;
+ } else if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_PHRASE) {
+ if (augment_match_expression_p(saidt, cpos,
+ parset, parse_basepos,
+ cmajor, cminor,
+ base_words, base_words_nr,
+ ref_words, ref_words_nr))
+ return 1;
+ } else if (cminor == AUGMENT_SENTENCE_MINOR_PARENTHESES) {
+ int gc_major, gc_minor;
+ int gchild = aug_get_first_child(saidt, cpos, &gc_major, &gc_minor);
+
+ while (gchild) {
+ if (augment_match_expression_p(saidt, cpos,
+ parset, parse_basepos,
+ major, minor,
+ base_words, base_words_nr,
+ ref_words, ref_words_nr))
+ return 1;
+ gchild = aug_get_next_sibling(saidt, gchild, &gc_major, &gc_minor);
+ }
+ } else
+ sciprintf("augment_match_expression_p(): Unknown type 144 minor number %3x\n", cminor);
+
+ cpos = aug_get_next_sibling(saidt, cpos, &cmajor, &cminor);
+
+ }
+ break;
+
+ case AUGMENT_SENTENCE_PART_BRACKETS:
+ if (augment_match_expression_p(saidt, cpos,
+ parset, parse_basepos,
+ cmajor, cminor,
+ base_words, base_words_nr,
+ ref_words, ref_words_nr))
+ return 1;
+
+ scidprintf("Didn't match subexpression; checking sub-bracked predicate %03x\n", cmajor);
+
+ switch (cmajor) {
+ case WORD_TYPE_BASE:
+ if (!base_words_nr)
+ return 1;
+ break;
+
+ case WORD_TYPE_REF:
+ if (!ref_words_nr)
+ return 1;
+ break;
+
+ default:
+ sciprintf("augment_match_expression_p(): (subp1) Unkonwn sub-bracket predicate %03x\n", cmajor);
+ }
+
+ break;
+
+ default:
+ sciprintf("augment_match_expression_p(): Unknown predicate %03x\n", major);
+
+ }
+
+ scidprintf("Generic failure\n");
+ return 0;
+}
+
+static int
+augment_sentence_expression(parse_tree_node_t *saidt, int augment_pos,
+ parse_tree_node_t *parset, int parse_branch,
+ int major, int minor,
+ int *base_words, int base_words_nr,
+ int *ref_words, int ref_words_nr)
+{
+ int check_major, check_minor;
+ int check_pos = aug_get_first_child(saidt, augment_pos, &check_major, &check_minor);
+ do {
+ if (!(augment_match_expression_p(saidt, check_pos, parset, parse_branch,
+ check_major, check_minor, base_words, base_words_nr,
+ ref_words, ref_words_nr)))
+ return 0;
+ } while ((check_pos = aug_get_next_sibling(saidt, check_pos, &check_major, &check_minor)));
+ return 1;
+}
+
+
+
+static int
+augment_sentence_part(parse_tree_node_t *saidt, int augment_pos,
+ parse_tree_node_t *parset, int parse_basepos,
+ int major, int minor)
+{
+ int pmajor, pminor;
+ int parse_branch = parse_basepos;
+ int optional = 0;
+ int foundwords = 0;
+
+ scidprintf("Augmenting (%03x %03x\n", major, minor);
+
+ if (major == AUGMENT_SENTENCE_PART_BRACKETS) { /* '[/ foo]' is true if '/foo' or if there
+ ** exists no x for which '/x' is true
+ */
+ if ((augment_pos = aug_get_first_child(saidt, augment_pos, &major, &minor))) {
+ scidprintf("Optional part: Now augmenting (%03x %03x\n", major, minor);
+ optional = 1;
+ } else {
+ scidprintf("Matched empty optional expression\n");
+ return 1;
+ }
+ }
+
+ if ((major < 0x141)
+ || (major > 0x143)) {
+ scidprintf("augment_sentence_part(): Unexpected sentence part major number %03x\n", major);
+ return 0;
+ }
+
+ while ((parse_branch = aug_get_next_sibling(parset, parse_branch, &pmajor, &pminor)))
+ if (pmajor == major) { /* found matching sentence part */
+ int success;
+ int base_words_nr;
+ int ref_words_nr;
+ int base_words[AUGMENT_MAX_WORDS];
+ int ref_words[AUGMENT_MAX_WORDS];
+#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION
+ int i;
+#endif
+
+ scidprintf("Found match with pminor = %03x\n", pminor);
+ aug_find_words(parset, parse_branch, base_words, &base_words_nr,
+ ref_words, &ref_words_nr, AUGMENT_MAX_WORDS);
+ foundwords |= (ref_words_nr | base_words_nr);
+#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION
+ sciprintf("%d base words:", base_words_nr);
+ for (i = 0; i < base_words_nr; i++)
+ sciprintf(" %03x", base_words[i]);
+ sciprintf("\n%d reference words:", ref_words_nr);
+ for (i = 0; i < ref_words_nr; i++)
+ sciprintf(" %03x", ref_words[i]);
+ sciprintf("\n");
+#endif
+
+ success = augment_sentence_expression(saidt, augment_pos,
+ parset, parse_basepos, major, minor,
+ base_words, base_words_nr,
+ ref_words, ref_words_nr);
+
+ if (success) {
+ scidprintf("SUCCESS on augmenting (%03x %03x\n", major, minor);
+ return 1;
+ }
+ }
+
+ if (optional && (foundwords == 0)) {
+ scidprintf("Found no words and optional branch => SUCCESS on augmenting (%03x %03x\n", major, minor);
+ return 1;
+ }
+ scidprintf("FAILURE on augmenting (%03x %03x\n", major, minor);
+ return 0;
+}
+
+static int
+augment_parse_nodes(parse_tree_node_t *parset, parse_tree_node_t *saidt)
+{
+ int augment_basepos = 0;
+ int parse_basepos;
+ int major, minor;
+ int dontclaim = 0;
+
+ parse_basepos = aug_get_base_node(parset);
+ if (!parse_basepos) {
+ sciprintf("augment_parse_nodes(): Parse tree is corrupt\n");
+ return 0;
+ }
+
+ augment_basepos = aug_get_base_node(saidt);
+ if (!augment_basepos) {
+ sciprintf("augment_parse_nodes(): Said tree is corrupt\n");
+ return 0;
+ }
+ while ((augment_basepos = aug_get_next_sibling(saidt, augment_basepos, &major, &minor))) {
+
+ if ((major == 0x14b)
+ && (minor == SAID_LONG(SAID_GT)))
+ dontclaim = 1; /* special case */
+ else /* normal sentence part */
+ if (!(augment_sentence_part(saidt, augment_basepos, parset, parse_basepos, major, minor))) {
+ scidprintf("Returning failure\n");
+ return 0; /* fail */
+ }
+ }
+
+ scidprintf("Returning success with dontclaim=%d\n", dontclaim);
+
+ if (dontclaim)
+ return SAID_PARTIAL_MATCH;
+ else return 1; /* full match */
+}
+
+
+/*******************/
+/**** Main code ****/
+/*******************/
+
+int
+said(state_t *s, byte *spec, int verbose)
+{
+ int retval;
+
+ parse_tree_node_t *parse_tree_ptr = s->parser_nodes;
+
+ if (s->parser_valid) {
+
+ if (said_parse_spec(s, spec)) {
+ sciprintf("Offending spec was: ");
+ vocab_decypher_said_block(s, spec);
+ return SAID_NO_MATCH;
+ }
+
+ if (verbose)
+ vocab_dump_parse_tree("Said-tree", said_tree); /* Nothing better to do yet */
+ retval = augment_parse_nodes(parse_tree_ptr, &(said_tree[0]));
+
+ if (!retval)
+ return SAID_NO_MATCH;
+ else if (retval != SAID_PARTIAL_MATCH)
+ return SAID_FULL_MATCH;
+ else return SAID_PARTIAL_MATCH;
+ }
+
+ return SAID_NO_MATCH;
+}
+
+
+
+#ifdef SAID_DEBUG_PROGRAM
+int
+main (int argc, char *argv)
+{
+ byte block[] = {0x01, 0x00, 0xf8, 0xf5, 0x02, 0x01, 0xf6, 0xf2, 0x02, 0x01, 0xf2, 0x01, 0x03, 0xff};
+ state_t s;
+ con_passthrough = 1;
+
+ s.parser_valid = 1;
+ said(&s, block);
+}
+#endif
+
diff --git a/engines/sci/engine/said.y b/engines/sci/engine/said.y
new file mode 100644
index 0000000000..fdef2360ae
--- /dev/null
+++ b/engines/sci/engine/said.y
@@ -0,0 +1,950 @@
+/***************************************************************************
+ said.y Copyright (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]
+
+***************************************************************************/
+
+%{
+
+#include <engine.h>
+
+#define SAID_BRANCH_NULL 0
+
+#define MAX_SAID_TOKENS 128
+
+/* Maximum number of words to be expected in a parsed sentence */
+#define AUGMENT_MAX_WORDS 64
+
+
+#define ANYWORD 0xfff
+
+#define WORD_TYPE_BASE 0x141
+#define WORD_TYPE_REF 0x144
+#define WORD_TYPE_SYNTACTIC_SUGAR 0x145
+
+#define AUGMENT_SENTENCE_PART_BRACKETS 0x152
+
+/* Minor numbers */
+#define AUGMENT_SENTENCE_MINOR_MATCH_PHRASE 0x14c
+#define AUGMENT_SENTENCE_MINOR_MATCH_WORD 0x153
+#define AUGMENT_SENTENCE_MINOR_RECURSE 0x144
+#define AUGMENT_SENTENCE_MINOR_PARENTHESES 0x14f
+
+
+#undef YYDEBUG /*1*/
+/*#define SAID_DEBUG*/
+/*#define SCI_DEBUG_PARSE_TREE_AUGMENTATION*/ /* uncomment to debug parse tree augmentation*/
+
+
+#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION
+#define scidprintf sciprintf
+#else
+#define scidprintf if (0) sciprintf
+#endif
+
+
+static char *said_parse_error;
+
+static int said_token;
+static int said_tokens_nr;
+static int said_tokens[MAX_SAID_TOKENS];
+
+static int said_blessed; /* increminated by said_top_branch */
+
+static int said_tree_pos; /* Set to 0 if we're out of space */
+#define SAID_TREE_START 4; /* Reserve space for the 4 top nodes */
+
+#define VALUE_IGNORE -424242
+
+static parse_tree_node_t said_tree[VOCAB_TREE_NODES];
+
+typedef int wgroup_t;
+typedef int tree_t;
+typedef int said_spec_t;
+
+static tree_t
+said_aug_branch(int, int, tree_t, tree_t);
+
+static tree_t
+said_attach_branch(tree_t, tree_t);
+/*
+static tree_t
+said_wgroup_branch(wgroup_t);
+*/
+static said_spec_t
+said_top_branch(tree_t);
+
+static tree_t
+said_paren(tree_t, tree_t);
+
+static tree_t
+said_value(int, tree_t);
+
+static tree_t
+said_terminal(int);
+
+
+static int
+yylex(void);
+
+static int
+yyerror(char *s)
+{
+ said_parse_error = sci_strdup(s);
+ return 1; /* Abort */
+}
+
+%}
+
+%token WGROUP /* Word group */
+%token YY_COMMA /* 0xf0 */
+%token YY_AMP /* 0xf1 */
+%token YY_SLASH /* 0xf2 */
+%token YY_PARENO /* 0xf3 */
+%token YY_PARENC /* 0xf4 */
+%token YY_BRACKETSO /* 0xf5 */
+%token YY_BRACKETSC /* 0xf6 */
+%token YY_HASH /* 0xf7 */
+%token YY_LT /* 0xf8 */
+%token YY_GT /* 0xf9 */
+%token YY_BRACKETSO_LT /* special token used to imitate LR(2) behaviour */
+%token YY_BRACKETSO_SLASH /* special token used to imitate LR(2) behaviour */
+%token YY_LT_BRACKETSO /* special token used to imitate LR(2) behaviour */
+%token YY_LT_PARENO /* special token used to imitate LR(2) behaviour */
+
+%%
+
+saidspec : leftspec optcont
+ { $$ = said_top_branch(said_attach_branch($1, $2)); }
+ | leftspec midspec optcont
+ { $$ = said_top_branch(said_attach_branch($1, said_attach_branch($2, $3))); }
+ | leftspec midspec rightspec optcont
+ { $$ = said_top_branch(said_attach_branch($1, said_attach_branch($2, said_attach_branch($3, $4)))); }
+ ;
+
+
+optcont : /* empty */
+ { $$ = SAID_BRANCH_NULL; }
+ | YY_GT
+ { $$ = said_paren(said_value(0x14b, said_value(0xf900, said_terminal(0xf900))), SAID_BRANCH_NULL); }
+ ;
+
+
+
+leftspec : /* empty */
+ { $$ = SAID_BRANCH_NULL; }
+ | expr
+ { $$ = said_paren(said_value(0x141, said_value(0x149, $1)), SAID_BRANCH_NULL); }
+ ;
+
+
+
+midspec : YY_SLASH expr
+ { $$ = said_aug_branch(0x142, 0x14a, $2, SAID_BRANCH_NULL); }
+ | YY_BRACKETSO_SLASH YY_SLASH expr YY_BRACKETSC
+ { $$ = said_aug_branch(0x152, 0x142, said_aug_branch(0x142, 0x14a, $3, SAID_BRANCH_NULL), SAID_BRANCH_NULL); }
+ | YY_SLASH
+ { $$ = SAID_BRANCH_NULL; }
+ ;
+
+
+
+rightspec : YY_SLASH expr
+ { $$ = said_aug_branch(0x143, 0x14a, $2, SAID_BRANCH_NULL); }
+ | YY_BRACKETSO_SLASH YY_SLASH expr YY_BRACKETSC
+ { $$ = said_aug_branch(0x152, 0x143, said_aug_branch(0x143, 0x14a, $3, SAID_BRANCH_NULL), SAID_BRANCH_NULL); }
+ | YY_SLASH
+ { $$ = SAID_BRANCH_NULL; }
+ ;
+
+
+
+word : WGROUP
+ { $$ = said_paren(said_value(0x141, said_value(0x153, said_terminal($1))), SAID_BRANCH_NULL); }
+ ;
+
+
+cwordset : wordset
+ { $$ = said_aug_branch(0x141, 0x14f, $1, SAID_BRANCH_NULL); }
+ | YY_BRACKETSO wordset YY_BRACKETSC
+ { $$ = said_aug_branch(0x141, 0x14f, said_aug_branch(0x152, 0x14c, said_aug_branch(0x141, 0x14f, $2, SAID_BRANCH_NULL), SAID_BRANCH_NULL), SAID_BRANCH_NULL); }
+ ;
+
+
+wordset : word
+ { $$ = $1; }
+ | YY_PARENO expr YY_PARENC
+ { $$ = $1; }
+ | wordset YY_COMMA wordset
+ { $$ = said_attach_branch($1, $3); }
+ | wordset YY_BRACKETSO_LT wordrefset YY_BRACKETSC
+ { $$ = said_attach_branch($1, $3); }
+ | wordset YY_COMMA YY_BRACKETSO wordset YY_BRACKETSC
+ { $$ = said_attach_branch($1, $3); }
+ ;
+
+
+
+expr : cwordset cwordrefset
+ { $$ = said_attach_branch($1, $2); }
+ | cwordset
+ { $$ = $1; }
+ | cwordrefset
+ { $$ = $1; }
+ ;
+
+
+
+cwordrefset : wordrefset
+ { $$ = $1; }
+ | YY_BRACKETSO_LT wordrefset YY_BRACKETSC
+ { $$ = said_aug_branch(0x152, 0x144, $2, SAID_BRANCH_NULL); }
+ | wordrefset YY_BRACKETSO_LT wordrefset YY_BRACKETSC
+ { $$ = said_attach_branch($1, said_aug_branch(0x152, 0x144, $3, SAID_BRANCH_NULL)); }
+ ;
+
+
+
+wordrefset : YY_LT word recref
+ { $$ = said_aug_branch(0x144, 0x14f, $2, $3); }
+ | YY_LT_PARENO YY_PARENO expr YY_PARENC
+ { $$ = said_aug_branch(0x144, 0x14f, said_aug_branch(0x141, 0x144, $2, SAID_BRANCH_NULL), SAID_BRANCH_NULL); }
+ | YY_LT wordset
+ { $$ = said_aug_branch(0x144, 0x14f, $2, SAID_BRANCH_NULL); }
+ | YY_LT_BRACKETSO YY_BRACKETSO wordset YY_BRACKETSC
+ { $$ = said_aug_branch(0x152, 0x144, said_aug_branch(0x144, 0x14f, $3, SAID_BRANCH_NULL), SAID_BRANCH_NULL); }
+ ;
+
+
+
+recref : YY_LT wordset recref
+ { $$ = said_aug_branch(0x141, 0x144, said_aug_branch(0x144, 0x14f, $2, SAID_BRANCH_NULL), $3); }
+ | YY_LT wordset
+ { $$ = said_aug_branch(0x141, 0x144, said_aug_branch(0x144, 0x14f, $2, SAID_BRANCH_NULL), SAID_BRANCH_NULL); }
+ | YY_LT_PARENO YY_PARENO expr YY_PARENC
+ { $$ = said_aug_branch(0x141, 0x14c, $2, SAID_BRANCH_NULL); }
+ ;
+
+
+
+%%
+
+
+int
+parse_yy_token_lookup[] = {YY_COMMA, YY_AMP, YY_SLASH, YY_PARENO, YY_PARENC, YY_BRACKETSO, YY_BRACKETSC,
+ YY_HASH, YY_LT, YY_GT};
+
+static int
+yylex(void)
+{
+ int retval = said_tokens[said_token++];
+
+ if (retval < SAID_LONG(SAID_FIRST)) {
+ yylval = retval;
+ retval = WGROUP;
+ } else {
+ retval >>= 8;
+
+ if (retval == SAID_TERM)
+ retval = 0;
+ else {
+ assert(retval >= SAID_FIRST);
+ retval = parse_yy_token_lookup[retval - SAID_FIRST];
+ if (retval == YY_BRACKETSO) {
+ if ((said_tokens[said_token] >> 8) == SAID_LT)
+ retval = YY_BRACKETSO_LT;
+ else
+ if ((said_tokens[said_token] >> 8) == SAID_SLASH)
+ retval = YY_BRACKETSO_SLASH;
+ } else if (retval == YY_LT && (said_tokens[said_token] >> 8) == SAID_BRACKO) {
+ retval = YY_LT_BRACKETSO;
+ } else if (retval == YY_LT && (said_tokens[said_token] >> 8) == SAID_PARENO) {
+ retval = YY_LT_PARENO;
+ }
+ }
+ }
+
+ return retval;
+}
+
+#define SAID_NEXT_NODE ((said_tree_pos == 0) || (said_tree_pos >= VOCAB_TREE_NODES))? said_tree_pos = 0 : said_tree_pos++
+
+static inline int
+said_leaf_node(tree_t pos, int value)
+{
+ said_tree[pos].type = PARSE_TREE_NODE_LEAF;
+
+ if (value != VALUE_IGNORE)
+ said_tree[pos].content.value = value;
+
+ return pos;
+}
+
+static inline int
+said_branch_node(tree_t pos, int left, int right)
+{
+ said_tree[pos].type = PARSE_TREE_NODE_BRANCH;
+
+ if (left != VALUE_IGNORE)
+ said_tree[pos].content.branches[0] = left;
+
+ if (right != VALUE_IGNORE)
+ said_tree[pos].content.branches[1] = right;
+
+ return pos;
+}
+
+
+static tree_t
+said_paren(tree_t t1, tree_t t2)
+{
+ if (t1)
+ return said_branch_node(SAID_NEXT_NODE,
+ t1,
+ t2
+ );
+ else
+ return t2;
+}
+
+static tree_t
+said_value(int val, tree_t t)
+{
+ return said_branch_node(SAID_NEXT_NODE,
+ said_leaf_node(SAID_NEXT_NODE, val),
+ t
+ );
+
+}
+
+static tree_t
+said_terminal(int val)
+{
+ return said_leaf_node(SAID_NEXT_NODE, val);
+}
+
+
+static tree_t
+said_aug_branch(int n1, int n2, tree_t t1, tree_t t2)
+{
+ int retval;
+
+ retval = said_branch_node(SAID_NEXT_NODE,
+ said_branch_node(SAID_NEXT_NODE,
+ said_leaf_node(SAID_NEXT_NODE, n1),
+ said_branch_node(SAID_NEXT_NODE,
+ said_leaf_node(SAID_NEXT_NODE, n2),
+ t1
+ )
+ ),
+ t2
+ );
+
+#ifdef SAID_DEBUG
+ fprintf(stderr,"AUG(0x%x, 0x%x, [%04x], [%04x]) = [%04x]\n", n1, n2, t1, t2, retval);
+#endif
+
+ return retval;
+}
+
+static tree_t
+said_attach_branch(tree_t base, tree_t attacheant)
+{
+#ifdef SAID_DEBUG
+ fprintf(stderr,"ATT2([%04x], [%04x]) = [%04x]\n", base, attacheant, base);
+#endif
+
+ if (!attacheant)
+ return base;
+ if (!base)
+ return attacheant;
+
+ if (!base)
+ return 0; /* Happens if we're out of space */
+
+ said_branch_node(base, VALUE_IGNORE, attacheant);
+
+ return base;
+}
+
+static said_spec_t
+said_top_branch(tree_t first)
+{
+#ifdef SAID_DEBUG
+ fprintf(stderr, "TOP([%04x])\n", first);
+#endif
+ said_branch_node(0, 1, 2);
+ said_leaf_node(1, 0x141); /* Magic number #1 */
+ said_branch_node(2, 3, first);
+ said_leaf_node(3, 0x13f); /* Magic number #2 */
+
+ ++said_blessed;
+
+ return 0;
+}
+
+
+int
+said_parse_spec(state_t *s, byte *spec)
+{
+ int nextitem;
+
+ said_parse_error = NULL;
+ said_token = 0;
+ said_tokens_nr = 0;
+ said_blessed = 0;
+
+ said_tree_pos = SAID_TREE_START;
+
+ do {
+ nextitem = *spec++;
+ if (nextitem < SAID_FIRST)
+ said_tokens[said_tokens_nr++] = nextitem << 8 | *spec++;
+ else
+ said_tokens[said_tokens_nr++] = SAID_LONG(nextitem);
+
+ } while ((nextitem != SAID_TERM) && (said_tokens_nr < MAX_SAID_TOKENS));
+
+ if (nextitem == SAID_TERM)
+ yyparse();
+ else {
+ sciprintf("Error: SAID spec is too long\n");
+ return 1;
+ }
+
+ if (said_parse_error) {
+ sciprintf("Error while parsing SAID spec: %s\n", said_parse_error);
+ free(said_parse_error);
+ return 1;
+ }
+
+ if (said_tree_pos == 0) {
+ sciprintf("Error: Out of tree space while parsing SAID spec\n");
+ return 1;
+ }
+
+ if (said_blessed != 1) {
+ sciprintf("Error: Found %d top branches\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+/**********************/
+/**** Augmentation ****/
+/**********************/
+
+
+/** primitive functions **/
+
+#define AUG_READ_BRANCH(a, br, p) \
+ if (tree[p].type != PARSE_TREE_NODE_BRANCH) \
+ return 0; \
+ a = tree[p].content.branches[br];
+
+#define AUG_READ_VALUE(a, p) \
+ if (tree[p].type != PARSE_TREE_NODE_LEAF) \
+ return 0; \
+ a = tree[p].content.value;
+
+#define AUG_ASSERT(i) \
+ if (!i) return 0;
+
+static int
+aug_get_next_sibling(parse_tree_node_t *tree, int pos, int *first, int *second)
+ /* Returns the next sibling relative to the specified position in 'tree',
+ ** sets *first and *second to its augment node values, returns the new position
+ ** or 0 if there was no next sibling
+ */
+{
+ int seek, valpos;
+
+ AUG_READ_BRANCH(pos, 1, pos);
+ AUG_ASSERT(pos);
+ AUG_READ_BRANCH(seek, 0, pos);
+ AUG_ASSERT(seek);
+
+ /* Now retreive first value */
+ AUG_READ_BRANCH(valpos, 0, seek);
+ AUG_ASSERT(valpos);
+ AUG_READ_VALUE(*first, valpos);
+
+ /* Get second value */
+ AUG_READ_BRANCH(seek, 1, seek);
+ AUG_ASSERT(seek);
+ AUG_READ_BRANCH(valpos, 0, seek);
+ AUG_ASSERT(valpos);
+ AUG_READ_VALUE(*second, valpos);
+
+ return pos;
+}
+
+
+static int
+aug_get_wgroup(parse_tree_node_t *tree, int pos)
+ /* Returns 0 if pos in tree is not the root of a 3-element list, otherwise
+ ** it returns the last element (which, in practice, is the word group
+ */
+{
+ int val;
+
+ AUG_READ_BRANCH(pos, 0, pos);
+ AUG_ASSERT(pos);
+ AUG_READ_BRANCH(pos, 1, pos);
+ AUG_ASSERT(pos);
+ AUG_READ_BRANCH(pos, 1, pos);
+ AUG_ASSERT(pos);
+ AUG_READ_VALUE(val, pos);
+
+ return val;
+}
+
+
+static int
+aug_get_base_node(parse_tree_node_t *tree)
+{
+ int startpos = 0;
+ AUG_READ_BRANCH(startpos, 1, startpos);
+ return startpos;
+}
+
+
+/** semi-primitive functions **/
+
+
+static int
+aug_get_first_child(parse_tree_node_t *tree, int pos, int *first, int *second)
+ /* like aug_get_next_sibling, except that it recurses into the tree and
+ ** finds the first child (usually *not* Ayanami Rei) of the current branch
+ ** rather than its next sibling.
+ */
+{
+ AUG_READ_BRANCH(pos, 0, pos);
+ AUG_ASSERT(pos);
+ AUG_READ_BRANCH(pos, 1, pos);
+ AUG_ASSERT(pos);
+
+ return aug_get_next_sibling(tree, pos, first, second);
+}
+
+static void
+aug_find_words_recursively(parse_tree_node_t *tree, int startpos,
+ int *base_words, int *base_words_nr,
+ int *ref_words, int *ref_words_nr,
+ int maxwords, int refbranch)
+ /* Finds and lists all base (141) and reference (144) words */
+{
+ int major, minor;
+ int word;
+ int pos = aug_get_first_child(tree, startpos, &major, &minor);
+
+ /* if (major == WORD_TYPE_REF)
+ refbranch = 1;*/
+
+ while (pos) {
+ if ((word = aug_get_wgroup(tree, pos))) { /* found a word */
+
+ if (!refbranch && major == WORD_TYPE_BASE) {
+ if ((*base_words_nr) == maxwords) {
+ sciprintf("Out of regular words\n");
+ return; /* return gracefully */
+ }
+
+ base_words[*base_words_nr] = word; /* register word */
+ ++(*base_words_nr);
+
+ }
+ if (major == WORD_TYPE_REF || refbranch) {
+ if ((*ref_words_nr) == maxwords) {
+ sciprintf("Out of reference words\n");
+ return; /* return gracefully */
+ }
+
+ ref_words[*ref_words_nr] = word; /* register word */
+ ++(*ref_words_nr);
+
+ }
+ if (major != WORD_TYPE_SYNTACTIC_SUGAR && major != WORD_TYPE_BASE && major != WORD_TYPE_REF)
+ sciprintf("aug_find_words_recursively(): Unknown word type %03x\n", major);
+
+ } else /* Did NOT find a word group: Attempt to recurse */
+ aug_find_words_recursively(tree, pos, base_words, base_words_nr,
+ ref_words, ref_words_nr, maxwords, refbranch || major == WORD_TYPE_REF);
+
+ pos = aug_get_next_sibling(tree, pos, &major, &minor);
+ }
+}
+
+
+static void
+aug_find_words(parse_tree_node_t *tree, int startpos,
+ int *base_words, int *base_words_nr,
+ int *ref_words, int *ref_words_nr,
+ int maxwords)
+ /* initializing wrapper for aug_find_words_recursively() */
+{
+ *base_words_nr = 0;
+ *ref_words_nr = 0;
+
+ aug_find_words_recursively(tree, startpos, base_words, base_words_nr, ref_words, ref_words_nr, maxwords, 0);
+}
+
+
+static inline int
+aug_contains_word(int *list, int length, int word)
+{
+ int i;
+ if (word == ANYWORD)
+ return (length);
+
+ for (i = 0; i < length; i++)
+ if (list[i] == word)
+ return 1;
+
+ return 0;
+}
+
+
+static int
+augment_sentence_expression(parse_tree_node_t *saidt, int augment_pos,
+ parse_tree_node_t *parset, int parse_branch,
+ int major, int minor,
+ int *base_words, int base_words_nr,
+ int *ref_words, int ref_words_nr);
+
+static int
+augment_match_expression_p(parse_tree_node_t *saidt, int augment_pos,
+ parse_tree_node_t *parset, int parse_basepos,
+ int major, int minor,
+ int *base_words, int base_words_nr,
+ int *ref_words, int ref_words_nr)
+{
+ int cmajor, cminor, cpos;
+ cpos = aug_get_first_child(saidt, augment_pos, &cmajor, &cminor);
+ if (!cpos) {
+ sciprintf("augment_match_expression_p(): Empty condition\n");
+ return 1;
+ }
+
+ scidprintf("Attempting to match (%03x %03x (%03x %03x\n", major, minor, cmajor, cminor);
+
+ if ((major == WORD_TYPE_BASE) && (minor == AUGMENT_SENTENCE_MINOR_RECURSE))
+ return augment_match_expression_p(saidt, cpos,
+ parset, parse_basepos,
+ cmajor, cminor,
+ base_words, base_words_nr,
+ ref_words, ref_words_nr);
+
+
+ switch (major) {
+
+ case WORD_TYPE_BASE:
+ while (cpos) {
+ if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_WORD) {
+ int word = aug_get_wgroup(saidt, cpos);
+ scidprintf("Looking for word %03x\n", word);
+
+ if (aug_contains_word(base_words, base_words_nr, word))
+ return 1;
+ } else if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_PHRASE) {
+ if (augment_sentence_expression(saidt, cpos,
+ parset, parse_basepos,
+ cmajor, cminor,
+ base_words, base_words_nr,
+ ref_words, ref_words_nr))
+ return 1;
+ } else if (cminor == AUGMENT_SENTENCE_MINOR_PARENTHESES) {
+ int gc_major, gc_minor;
+ int gchild = aug_get_first_child(saidt, cpos, &gc_major, &gc_minor);
+
+ while (gchild) {
+ if (augment_match_expression_p(saidt, cpos,
+ parset, parse_basepos,
+ major, minor,
+ base_words, base_words_nr,
+ ref_words, ref_words_nr))
+ return 1;
+ gchild = aug_get_next_sibling(saidt, gchild, &gc_major, &gc_minor);
+ }
+ } else
+ sciprintf("augment_match_expression_p(): Unknown type 141 minor number %3x\n", cminor);
+
+ cpos = aug_get_next_sibling(saidt, cpos, &cmajor, &cminor);
+
+ }
+ break;
+
+ case WORD_TYPE_REF:
+ while (cpos) {
+ if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_WORD) {
+ int word = aug_get_wgroup(saidt, cpos);
+ scidprintf("Looking for refword %03x\n", word);
+
+ if (aug_contains_word(ref_words, ref_words_nr, word))
+ return 1;
+ } else if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_PHRASE) {
+ if (augment_match_expression_p(saidt, cpos,
+ parset, parse_basepos,
+ cmajor, cminor,
+ base_words, base_words_nr,
+ ref_words, ref_words_nr))
+ return 1;
+ } else if (cminor == AUGMENT_SENTENCE_MINOR_PARENTHESES) {
+ int gc_major, gc_minor;
+ int gchild = aug_get_first_child(saidt, cpos, &gc_major, &gc_minor);
+
+ while (gchild) {
+ if (augment_match_expression_p(saidt, cpos,
+ parset, parse_basepos,
+ major, minor,
+ base_words, base_words_nr,
+ ref_words, ref_words_nr))
+ return 1;
+ gchild = aug_get_next_sibling(saidt, gchild, &gc_major, &gc_minor);
+ }
+ } else
+ sciprintf("augment_match_expression_p(): Unknown type 144 minor number %3x\n", cminor);
+
+ cpos = aug_get_next_sibling(saidt, cpos, &cmajor, &cminor);
+
+ }
+ break;
+
+ case AUGMENT_SENTENCE_PART_BRACKETS:
+ if (augment_match_expression_p(saidt, cpos,
+ parset, parse_basepos,
+ cmajor, cminor,
+ base_words, base_words_nr,
+ ref_words, ref_words_nr))
+ return 1;
+
+ scidprintf("Didn't match subexpression; checking sub-bracked predicate %03x\n", cmajor);
+
+ switch (cmajor) {
+ case WORD_TYPE_BASE:
+ if (!base_words_nr)
+ return 1;
+ break;
+
+ case WORD_TYPE_REF:
+ if (!ref_words_nr)
+ return 1;
+ break;
+
+ default:
+ sciprintf("augment_match_expression_p(): (subp1) Unkonwn sub-bracket predicate %03x\n", cmajor);
+ }
+
+ break;
+
+ default:
+ sciprintf("augment_match_expression_p(): Unknown predicate %03x\n", major);
+
+ }
+
+ scidprintf("Generic failure\n");
+ return 0;
+}
+
+static int
+augment_sentence_expression(parse_tree_node_t *saidt, int augment_pos,
+ parse_tree_node_t *parset, int parse_branch,
+ int major, int minor,
+ int *base_words, int base_words_nr,
+ int *ref_words, int ref_words_nr)
+{
+ int check_major, check_minor;
+ int check_pos = aug_get_first_child(saidt, augment_pos, &check_major, &check_minor);
+ do {
+ if (!(augment_match_expression_p(saidt, check_pos, parset, parse_branch,
+ check_major, check_minor, base_words, base_words_nr,
+ ref_words, ref_words_nr)))
+ return 0;
+ } while ((check_pos = aug_get_next_sibling(saidt, check_pos, &check_major, &check_minor)));
+ return 1;
+}
+
+
+
+static int
+augment_sentence_part(parse_tree_node_t *saidt, int augment_pos,
+ parse_tree_node_t *parset, int parse_basepos,
+ int major, int minor)
+{
+ int pmajor, pminor;
+ int parse_branch = parse_basepos;
+ int optional = 0;
+ int foundwords = 0;
+
+ scidprintf("Augmenting (%03x %03x\n", major, minor);
+
+ if (major == AUGMENT_SENTENCE_PART_BRACKETS) { /* '[/ foo]' is true if '/foo' or if there
+ ** exists no x for which '/x' is true
+ */
+ if ((augment_pos = aug_get_first_child(saidt, augment_pos, &major, &minor))) {
+ scidprintf("Optional part: Now augmenting (%03x %03x\n", major, minor);
+ optional = 1;
+ } else {
+ scidprintf("Matched empty optional expression\n");
+ return 1;
+ }
+ }
+
+ if ((major < 0x141)
+ || (major > 0x143)) {
+ scidprintf("augment_sentence_part(): Unexpected sentence part major number %03x\n", major);
+ return 0;
+ }
+
+ while ((parse_branch = aug_get_next_sibling(parset, parse_branch, &pmajor, &pminor)))
+ if (pmajor == major) { /* found matching sentence part */
+ int success;
+ int base_words_nr;
+ int ref_words_nr;
+ int base_words[AUGMENT_MAX_WORDS];
+ int ref_words[AUGMENT_MAX_WORDS];
+#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION
+ int i;
+#endif
+
+ scidprintf("Found match with pminor = %03x\n", pminor);
+ aug_find_words(parset, parse_branch, base_words, &base_words_nr,
+ ref_words, &ref_words_nr, AUGMENT_MAX_WORDS);
+ foundwords |= (ref_words_nr | base_words_nr);
+#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION
+ sciprintf("%d base words:", base_words_nr);
+ for (i = 0; i < base_words_nr; i++)
+ sciprintf(" %03x", base_words[i]);
+ sciprintf("\n%d reference words:", ref_words_nr);
+ for (i = 0; i < ref_words_nr; i++)
+ sciprintf(" %03x", ref_words[i]);
+ sciprintf("\n");
+#endif
+
+ success = augment_sentence_expression(saidt, augment_pos,
+ parset, parse_basepos, major, minor,
+ base_words, base_words_nr,
+ ref_words, ref_words_nr);
+
+ if (success) {
+ scidprintf("SUCCESS on augmenting (%03x %03x\n", major, minor);
+ return 1;
+ }
+ }
+
+ if (optional && (foundwords == 0)) {
+ scidprintf("Found no words and optional branch => SUCCESS on augmenting (%03x %03x\n", major, minor);
+ return 1;
+ }
+ scidprintf("FAILURE on augmenting (%03x %03x\n", major, minor);
+ return 0;
+}
+
+static int
+augment_parse_nodes(parse_tree_node_t *parset, parse_tree_node_t *saidt)
+{
+ int augment_basepos = 0;
+ int parse_basepos;
+ int major, minor;
+ int dontclaim = 0;
+
+ parse_basepos = aug_get_base_node(parset);
+ if (!parse_basepos) {
+ sciprintf("augment_parse_nodes(): Parse tree is corrupt\n");
+ return 0;
+ }
+
+ augment_basepos = aug_get_base_node(saidt);
+ if (!augment_basepos) {
+ sciprintf("augment_parse_nodes(): Said tree is corrupt\n");
+ return 0;
+ }
+ while ((augment_basepos = aug_get_next_sibling(saidt, augment_basepos, &major, &minor))) {
+
+ if ((major == 0x14b)
+ && (minor == SAID_LONG(SAID_GT)))
+ dontclaim = 1; /* special case */
+ else /* normal sentence part */
+ if (!(augment_sentence_part(saidt, augment_basepos, parset, parse_basepos, major, minor))) {
+ scidprintf("Returning failure\n");
+ return 0; /* fail */
+ }
+ }
+
+ scidprintf("Returning success with dontclaim=%d\n", dontclaim);
+
+ if (dontclaim)
+ return SAID_PARTIAL_MATCH;
+ else return 1; /* full match */
+}
+
+
+/*******************/
+/**** Main code ****/
+/*******************/
+
+int
+said(state_t *s, byte *spec, int verbose)
+{
+ int retval;
+
+ parse_tree_node_t *parse_tree_ptr = s->parser_nodes;
+
+ if (s->parser_valid) {
+
+ if (said_parse_spec(s, spec)) {
+ sciprintf("Offending spec was: ");
+ vocab_decypher_said_block(s, spec);
+ return SAID_NO_MATCH;
+ }
+
+ if (verbose)
+ vocab_dump_parse_tree("Said-tree", said_tree); /* Nothing better to do yet */
+ retval = augment_parse_nodes(parse_tree_ptr, &(said_tree[0]));
+
+ if (!retval)
+ return SAID_NO_MATCH;
+ else if (retval != SAID_PARTIAL_MATCH)
+ return SAID_FULL_MATCH;
+ else return SAID_PARTIAL_MATCH;
+ }
+
+ return SAID_NO_MATCH;
+}
+
+
+
+#ifdef SAID_DEBUG_PROGRAM
+int
+main (int argc, char *argv)
+{
+ byte block[] = {0x01, 0x00, 0xf8, 0xf5, 0x02, 0x01, 0xf6, 0xf2, 0x02, 0x01, 0xf2, 0x01, 0x03, 0xff};
+ state_t s;
+ con_passthrough = 1;
+
+ s.parser_valid = 1;
+ said(&s, block);
+}
+#endif
diff --git a/engines/sci/engine/savegame.c b/engines/sci/engine/savegame.c
new file mode 100644
index 0000000000..0537fcacd5
--- /dev/null
+++ b/engines/sci/engine/savegame.c
@@ -0,0 +1,4894 @@
+/***************************************************************************
+ savegame.cfsml 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) [jameson@linuxgames.com]
+
+***************************************************************************/
+/* Savegame handling for state_t structs. Makes heavy use of cfsml magic. */
+/* DON'T EDIT savegame.c ! Only modify savegame.cfsml, if something needs
+** to be changed. Refer to freesci/docs/misc/cfsml.spec if you don't understand
+** savegame.cfsml. If this doesn't solve your problem, contact the maintainer.
+*/
+
+#include <sci_memory.h>
+#include <gfx_operations.h>
+#include <sfx_engine.h>
+#include <engine.h>
+#include <assert.h>
+#include "heap.h"
+
+#ifdef _MSC_VER
+#include <direct.h>
+#endif
+
+#ifdef _WIN32
+#pragma warning( disable : 4101 )
+#endif
+
+#define HUNK_TYPE_GFX_SNAPSHOT_STRING "g\n"
+
+/* Missing:
+** - SFXdriver
+** - File input/output state (this is likely not to happen)
+*/
+
+static state_t *_global_save_state;
+/* Needed for some graphical stuff. */
+#define FILE_VERSION _global_save_state->savegame_version
+
+
+void
+write_reg_t(FILE *fh, reg_t *foo)
+{
+ fprintf(fh, PREG, PRINT_REG(*foo));
+}
+
+int
+read_reg_t(FILE *fh, reg_t *foo, char *lastval, int *line, int *hiteof)
+{
+ int segment, offset;
+
+ if (sscanf(lastval, PREG, &segment, &offset)<2)
+ {
+ sciprintf("Error parsing reg_t on line %d\n", *line);
+ return 1;
+ }
+
+ *foo = make_reg(segment, offset);
+ return 0;
+}
+
+void
+write_sci_version(FILE *fh, sci_version_t *foo)
+{
+ fprintf(fh, "%d.%03d.%03d", SCI_VERSION_MAJOR(*foo), SCI_VERSION_MINOR(*foo),
+ SCI_VERSION_PATCHLEVEL(*foo));
+}
+
+int
+read_sci_version(FILE *fh, sci_version_t *foo, char *lastval, int *line, int *hiteof)
+{
+ return version_parse(lastval, foo);
+}
+
+void
+write_PTN(FILE *fh, parse_tree_node_t *foo)
+{
+ if (foo->type == PARSE_TREE_NODE_LEAF)
+ fprintf(fh, "L%d", foo->content.value);
+ else
+ fprintf(fh, "B(%d,%d)", foo->content.branches[0], foo->content.branches[1]);
+}
+
+int
+read_PTN(FILE *fh, parse_tree_node_t *foo, char *lastval, int *line, int *hiteof)
+{
+ if (lastval[0] == 'L') {
+ char *c = lastval + 1;
+ char *strend;
+
+ while (*c && isspace(*c))
+ ++c;
+
+ if (!*c)
+ return 1;
+
+ foo->content.value = strtol(c, &strend, 0);
+
+ return (strend == c); /* Error if nothing could be read */
+
+ return 0;
+ } else if (lastval[0] == 'B') {
+ char *c = lastval + 1;
+ char *strend;
+
+ while (*c && isspace(*c)) ++c;
+ if (*c++ != '(') return 1;
+ while (*c && isspace(*c)) ++c;
+
+ foo->content.branches[0] = strtol(c, &strend, 0);
+ if (strend == c)
+ return 1;
+ c = strend;
+
+ while (*c && isspace(*c)) ++c;
+ if (*c++ != ',')
+ return 1;
+
+ while (*c && isspace(*c)) ++c;
+
+ foo->content.branches[1] = strtol(c, &strend, 0);
+ if (strend == c)
+ return 1;
+ c = strend;
+
+ while (*c && isspace(*c)) ++c;
+ if (*c++ != ')') return 1;
+
+ return 0;
+ } else return 1; /* failure to parse anything */
+}
+
+
+void
+write_menubar_tp(FILE *fh, menubar_t **foo);
+int
+read_menubar_tp(FILE *fh, menubar_t **foo, char *lastval, int *line, int *hiteof);
+
+void
+write_mem_obj_tp(FILE *fh, mem_obj_t **foo);
+int
+read_mem_obj_tp(FILE *fh, mem_obj_t **foo, char *lastval, int *line, int *hiteof);
+
+void
+write_int_hash_map_tp(FILE *fh, int_hash_map_t **foo);
+int
+read_int_hash_map_tp(FILE *fh, int_hash_map_t **foo, char *lastval, int *line, int *hiteof);
+
+void
+write_songlib_t(FILE *fh, songlib_t *foo);
+int
+read_songlib_t(FILE *fh, songlib_t *foo, char *lastval, int *line, int *hiteof);
+
+void
+write_int_hash_map_node_tp(FILE *fh, int_hash_map_node_t **foo);
+int
+read_int_hash_map_node_tp(FILE *fh, int_hash_map_node_t **foo, char *lastval, int *line, int *hiteof);
+
+int
+read_song_tp(FILE *fh, song_t **foo, char *lastval, int *line, int *hiteof);
+
+typedef mem_obj_t *mem_obj_ptr;
+
+
+/* Auto-generated CFSML declaration and function block */
+
+#define CFSML_SUCCESS 0
+#define CFSML_FAILURE 1
+
+
+#include <stdarg.h> /* We need va_lists */
+#include <sci_memory.h>
+
+#ifdef CFSML_DEBUG_MALLOC
+/*
+#define free(p) dbg_sci_free(p)
+#define malloc(s) dbg_sci_malloc(s)
+#define calloc(n, s) dbg_sci_calloc(n, s)
+#define realloc(p, s) dbg_sci_realloc(p, s)
+*/
+#define free dbg_sci_free
+#define malloc dbg_sci_malloc
+#define calloc dbg_sci_calloc
+#define realloc dbg_sci_realloc
+#endif
+
+static void
+_cfsml_error(char *fmt, ...)
+{
+ va_list argp;
+
+ fprintf(stderr, "Error: ");
+ va_start(argp, fmt);
+ vfprintf(stderr, fmt, argp);
+ va_end(argp);
+
+}
+
+
+static struct _cfsml_pointer_refstruct {
+ struct _cfsml_pointer_refstruct *next;
+ void *ptr;
+} *_cfsml_pointer_references = NULL;
+
+static struct _cfsml_pointer_refstruct **_cfsml_pointer_references_current = &_cfsml_pointer_references;
+
+static char *_cfsml_last_value_retreived = NULL;
+static char *_cfsml_last_identifier_retreived = NULL;
+
+static void
+_cfsml_free_pointer_references_recursively(struct _cfsml_pointer_refstruct *refs, int free_pointers)
+{
+ if (!refs)
+ return;
+ #ifdef CFSML_DEBUG_MALLOC
+ SCI_MEMTEST;
+ #endif
+
+ _cfsml_free_pointer_references_recursively(refs->next, free_pointers);
+ #ifdef CFSML_DEBUG_MALLOC
+ SCI_MEMTEST;
+
+ fprintf(stderr,"Freeing ptrref %p [%p] %s\n", refs->ptr, refs, free_pointers?
+ "ALL": "cleanup only");
+ #endif
+
+ if (free_pointers)
+ free(refs->ptr);
+
+ #ifdef CFSML_DEBUG_MALLOC
+ SCI_MEMTEST;
+ #endif
+ free(refs);
+ #ifdef CFSML_DEBUG_MALLOC
+ SCI_MEMTEST;
+ #endif
+}
+
+static void
+_cfsml_free_pointer_references(struct _cfsml_pointer_refstruct **meta_ref, int free_pointers)
+{
+ _cfsml_free_pointer_references_recursively(*meta_ref, free_pointers);
+ *meta_ref = NULL;
+ _cfsml_pointer_references_current = meta_ref;
+}
+
+static struct _cfsml_pointer_refstruct **
+_cfsml_get_current_refpointer()
+{
+ return _cfsml_pointer_references_current;
+}
+
+static void _cfsml_register_pointer(void *ptr)
+{
+ struct _cfsml_pointer_refstruct *newref = (struct _cfsml_pointer_refstruct*)sci_malloc(sizeof (struct _cfsml_pointer_refstruct));
+ #ifdef CFSML_DEBUG_MALLOC
+ SCI_MEMTEST;
+ fprintf(stderr,"Registering ptrref %p [%p]\n", ptr, newref);
+ #endif
+ newref->next = *_cfsml_pointer_references_current;
+ newref->ptr = ptr;
+ *_cfsml_pointer_references_current = newref;
+}
+
+
+static char *
+_cfsml_mangle_string(char *s)
+{
+ char *source = s;
+ char c;
+ char *target = (char *) sci_malloc(1 + strlen(s) * 2); /* We will probably need less than that */
+ char *writer = target;
+
+ while ((c = *source++)) {
+
+ if (c < 32) { /* Special character? */
+ *writer++ = '\\'; /* Escape... */
+ c += ('a' - 1);
+ } else if (c == '\\' || c == '"')
+ *writer++ = '\\'; /* Escape, but do not change */
+ *writer++ = c;
+
+ }
+ *writer = 0; /* Terminate string */
+
+ return (char *) sci_realloc(target, strlen(target) + 1);
+}
+
+
+static char *
+_cfsml_unmangle_string(char *s)
+{
+ char *target = (char *) sci_malloc(1 + strlen(s));
+ char *writer = target;
+ char *source = s;
+ char c;
+
+ while ((c = *source++) && (c > 31)) {
+ if (c == '\\') { /* Escaped character? */
+ c = *source++;
+ if ((c != '\\') && (c != '"')) /* Un-escape 0-31 only */
+ c -= ('a' - 1);
+ }
+ *writer++ = c;
+ }
+ *writer = 0; /* Terminate string */
+
+ return (char *) sci_realloc(target, strlen(target) + 1);
+}
+
+
+static char *
+_cfsml_get_identifier(FILE *fd, int *line, int *hiteof, int *assignment)
+{
+ int c;
+ int mem = 32;
+ int pos = 0;
+ int done = 0;
+ char *retval = (char *) sci_malloc(mem);
+
+ if (_cfsml_last_identifier_retreived) {
+ free(_cfsml_last_identifier_retreived);
+ _cfsml_last_identifier_retreived = NULL;
+ }
+
+ while (isspace(c = fgetc(fd)) && (c != EOF));
+ if (c == EOF) {
+ _cfsml_error("Unexpected end of file at line %d\n", *line);
+ free(retval);
+ *hiteof = 1;
+ return NULL;
+ }
+
+ ungetc(c, fd);
+
+ while (((c = fgetc(fd)) != EOF) && ((pos == 0) || (c != '\n')) && (c != '=')) {
+
+ if (pos == mem - 1) /* Need more memory? */
+ retval = (char *) sci_realloc(retval, mem *= 2);
+
+ if (!isspace(c)) {
+ if (done) {
+ _cfsml_error("Single word identifier expected at line %d\n", *line);
+ free(retval);
+ return NULL;
+ }
+ retval[pos++] = c;
+ } else
+ if (pos != 0)
+ done = 1; /* Finished the variable name */
+ else if (c == '\n')
+ ++(*line);
+ }
+
+ if (c == EOF) {
+ _cfsml_error("Unexpected end of file at line %d\n", *line);
+ free(retval);
+ *hiteof = 1;
+ return NULL;
+ }
+
+ if (c == '\n') {
+ ++(*line);
+ if (assignment)
+ *assignment = 0;
+ } else
+ if (assignment)
+ *assignment = 1;
+
+ if (pos == 0) {
+ _cfsml_error("Missing identifier in assignment at line %d\n", *line);
+ free(retval);
+ return NULL;
+ }
+
+ if (pos == mem - 1) /* Need more memory? */
+ retval = (char *) sci_realloc(retval, mem += 1);
+
+ retval[pos] = 0; /* Terminate string */
+
+ return _cfsml_last_identifier_retreived = retval;
+}
+
+
+static char *
+_cfsml_get_value(FILE *fd, int *line, int *hiteof)
+{
+ int c;
+ int mem = 64;
+ int pos = 0;
+ char *retval = (char *) sci_malloc(mem);
+
+ if (_cfsml_last_value_retreived) {
+ free(_cfsml_last_value_retreived);
+ _cfsml_last_value_retreived = NULL;
+ }
+
+ while (((c = fgetc(fd)) != EOF) && (c != '\n')) {
+
+ if (pos == mem - 1) /* Need more memory? */
+ retval = (char *) sci_realloc(retval, mem *= 2);
+
+ if (pos || (!isspace(c)))
+ retval[pos++] = c;
+
+ }
+
+ while ((pos > 0) && (isspace(retval[pos - 1])))
+ --pos; /* Strip trailing whitespace */
+
+ if (c == EOF)
+ *hiteof = 1;
+
+ if (pos == 0) {
+ _cfsml_error("Missing value in assignment at line %d\n", *line);
+ free(retval);
+ return NULL;
+ }
+
+ if (c == '\n')
+ ++(*line);
+
+ if (pos == mem - 1) /* Need more memory? */
+ retval = (char *) sci_realloc(retval, mem += 1);
+
+ retval[pos] = 0; /* Terminate string */
+ return (_cfsml_last_value_retreived = (char *) sci_realloc(retval, strlen(retval) + 1));
+ /* Re-allocate; this value might be used for quite some while (if we are
+ ** restoring a string)
+ */
+}
+static void
+_cfsml_write_synonym_t(FILE *fh, synonym_t* save_struc);
+static int
+_cfsml_read_synonym_t(FILE *fh, synonym_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_sfx_state_t(FILE *fh, sfx_state_t* save_struc);
+static int
+_cfsml_read_sfx_state_t(FILE *fh, sfx_state_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_clone_entry_t(FILE *fh, clone_entry_t* save_struc);
+static int
+_cfsml_read_clone_entry_t(FILE *fh, clone_entry_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_object_t(FILE *fh, object_t* save_struc);
+static int
+_cfsml_read_object_t(FILE *fh, object_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_string(FILE *fh, char ** save_struc);
+static int
+_cfsml_read_string(FILE *fh, char ** save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_menubar_t(FILE *fh, menubar_t* save_struc);
+static int
+_cfsml_read_menubar_t(FILE *fh, menubar_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_size_t(FILE *fh, size_t* save_struc);
+static int
+_cfsml_read_size_t(FILE *fh, size_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_list_entry_t(FILE *fh, list_entry_t* save_struc);
+static int
+_cfsml_read_list_entry_t(FILE *fh, list_entry_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_int_hash_map_t(FILE *fh, int_hash_map_t* save_struc);
+static int
+_cfsml_read_int_hash_map_t(FILE *fh, int_hash_map_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_gint16(FILE *fh, gint16* save_struc);
+static int
+_cfsml_read_gint16(FILE *fh, gint16* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_song_t(FILE *fh, song_t* save_struc);
+static int
+_cfsml_read_song_t(FILE *fh, song_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_menu_item_t(FILE *fh, menu_item_t* save_struc);
+static int
+_cfsml_read_menu_item_t(FILE *fh, menu_item_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_node_entry_t(FILE *fh, node_entry_t* save_struc);
+static int
+_cfsml_read_node_entry_t(FILE *fh, node_entry_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_seg_id_t(FILE *fh, seg_id_t* save_struc);
+static int
+_cfsml_read_seg_id_t(FILE *fh, seg_id_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_dynmem_t(FILE *fh, dynmem_t* save_struc);
+static int
+_cfsml_read_dynmem_t(FILE *fh, dynmem_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_local_variables_t(FILE *fh, local_variables_t* save_struc);
+static int
+_cfsml_read_local_variables_t(FILE *fh, local_variables_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_state_t(FILE *fh, state_t* save_struc);
+static int
+_cfsml_read_state_t(FILE *fh, state_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_node_table_t(FILE *fh, node_table_t* save_struc);
+static int
+_cfsml_read_node_table_t(FILE *fh, node_table_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_sys_strings_t(FILE *fh, sys_strings_t* save_struc);
+static int
+_cfsml_read_sys_strings_t(FILE *fh, sys_strings_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_byte(FILE *fh, byte* save_struc);
+static int
+_cfsml_read_byte(FILE *fh, byte* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_node_t(FILE *fh, node_t* save_struc);
+static int
+_cfsml_read_node_t(FILE *fh, node_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_list_table_t(FILE *fh, list_table_t* save_struc);
+static int
+_cfsml_read_list_table_t(FILE *fh, list_table_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_class_t(FILE *fh, class_t* save_struc);
+static int
+_cfsml_read_class_t(FILE *fh, class_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_song_handle_t(FILE *fh, song_handle_t* save_struc);
+static int
+_cfsml_read_song_handle_t(FILE *fh, song_handle_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_int(FILE *fh, int* save_struc);
+static int
+_cfsml_read_int(FILE *fh, int* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_menu_t(FILE *fh, menu_t* save_struc);
+static int
+_cfsml_read_menu_t(FILE *fh, menu_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_long(FILE *fh, long* save_struc);
+static int
+_cfsml_read_long(FILE *fh, long* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_clone_table_t(FILE *fh, clone_table_t* save_struc);
+static int
+_cfsml_read_clone_table_t(FILE *fh, clone_table_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_clone_t(FILE *fh, clone_t* save_struc);
+static int
+_cfsml_read_clone_t(FILE *fh, clone_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_list_t(FILE *fh, list_t* save_struc);
+static int
+_cfsml_read_list_t(FILE *fh, list_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_sys_string_t(FILE *fh, sys_string_t* save_struc);
+static int
+_cfsml_read_sys_string_t(FILE *fh, sys_string_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_script_t(FILE *fh, script_t* save_struc);
+static int
+_cfsml_read_script_t(FILE *fh, script_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_seg_manager_t(FILE *fh, seg_manager_t* save_struc);
+static int
+_cfsml_read_seg_manager_t(FILE *fh, seg_manager_t* save_struc, char *lastval, int *line, int *hiteof);
+
+static void
+_cfsml_write_synonym_t(FILE *fh, synonym_t* save_struc)
+{
+ int min, max, i;
+
+ fprintf(fh, "{\n");
+ fprintf(fh, "replaceant = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->replaceant));
+ fprintf(fh, "\n");
+ fprintf(fh, "replacement = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->replacement));
+ fprintf(fh, "\n");
+ fprintf(fh, "}");
+}
+
+static int
+_cfsml_read_synonym_t(FILE *fh, synonym_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+int min, max, i;
+ int assignment, closed, done;
+
+ if (strcmp(lastval, "{")) {
+ _cfsml_error("Reading record synonym_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval);
+ return CFSML_FAILURE;
+ };
+ closed = 0;
+ do {
+ char *value;
+ token = _cfsml_get_identifier(fh, line, hiteof, &assignment);
+
+ if (!token) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!assignment) {
+ if (!strcmp(token, "}"))
+ closed = 1;
+ else {
+ _cfsml_error("Expected assignment or closing braces in line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else {
+ value = "";
+ while (!value || !strcmp(value, ""))
+ value = _cfsml_get_value(fh, line, hiteof);
+ if (!value) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!strcmp(token, "replaceant")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->replaceant), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for replaceant at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "replacement")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->replacement), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for replacement at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ {
+ _cfsml_error("synonym_t: Assignment to invalid identifier '%s' in line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+ }
+ } while (!closed); /* Until closing braces are hit */
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_sfx_state_t(FILE *fh, sfx_state_t* save_struc)
+{
+ int min, max, i;
+
+ fprintf(fh, "{\n");
+ fprintf(fh, "songlib = ");
+ write_songlib_t(fh, (songlib_t*) &(save_struc->songlib));
+ fprintf(fh, "\n");
+ fprintf(fh, "}");
+}
+
+static int
+_cfsml_read_sfx_state_t(FILE *fh, sfx_state_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+int min, max, i;
+ int assignment, closed, done;
+
+ if (strcmp(lastval, "{")) {
+ _cfsml_error("Reading record sfx_state_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval);
+ return CFSML_FAILURE;
+ };
+ closed = 0;
+ do {
+ char *value;
+ token = _cfsml_get_identifier(fh, line, hiteof, &assignment);
+
+ if (!token) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!assignment) {
+ if (!strcmp(token, "}"))
+ closed = 1;
+ else {
+ _cfsml_error("Expected assignment or closing braces in line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else {
+ value = "";
+ while (!value || !strcmp(value, ""))
+ value = _cfsml_get_value(fh, line, hiteof);
+ if (!value) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!strcmp(token, "songlib")) {
+ if (read_songlib_t(fh, (songlib_t*) &(save_struc->songlib), value, line, hiteof)) {
+ _cfsml_error("Token expected by read_songlib_t() for songlib at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ {
+ _cfsml_error("sfx_state_t: Assignment to invalid identifier '%s' in line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+ }
+ } while (!closed); /* Until closing braces are hit */
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_clone_entry_t(FILE *fh, clone_entry_t* save_struc)
+{
+ int min, max, i;
+
+ fprintf(fh, "{\n");
+ fprintf(fh, "next_free = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->next_free));
+ fprintf(fh, "\n");
+ fprintf(fh, "entry = ");
+ _cfsml_write_clone_t(fh, (clone_t*) &(save_struc->entry));
+ fprintf(fh, "\n");
+ fprintf(fh, "}");
+}
+
+static int
+_cfsml_read_clone_entry_t(FILE *fh, clone_entry_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+int min, max, i;
+ int assignment, closed, done;
+
+ if (strcmp(lastval, "{")) {
+ _cfsml_error("Reading record clone_entry_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval);
+ return CFSML_FAILURE;
+ };
+ closed = 0;
+ do {
+ char *value;
+ token = _cfsml_get_identifier(fh, line, hiteof, &assignment);
+
+ if (!token) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!assignment) {
+ if (!strcmp(token, "}"))
+ closed = 1;
+ else {
+ _cfsml_error("Expected assignment or closing braces in line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else {
+ value = "";
+ while (!value || !strcmp(value, ""))
+ value = _cfsml_get_value(fh, line, hiteof);
+ if (!value) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!strcmp(token, "next_free")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->next_free), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for next_free at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "entry")) {
+ if (_cfsml_read_clone_t(fh, (clone_t*) &(save_struc->entry), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_clone_t() for entry at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ {
+ _cfsml_error("clone_entry_t: Assignment to invalid identifier '%s' in line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+ }
+ } while (!closed); /* Until closing braces are hit */
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_object_t(FILE *fh, object_t* save_struc)
+{
+ int min, max, i;
+
+ fprintf(fh, "{\n");
+ fprintf(fh, "flags = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->flags));
+ fprintf(fh, "\n");
+ fprintf(fh, "pos = ");
+ write_reg_t(fh, (reg_t*) &(save_struc->pos));
+ fprintf(fh, "\n");
+ fprintf(fh, "variables_nr = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->variables_nr));
+ fprintf(fh, "\n");
+ fprintf(fh, "variable_names_nr = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->variable_names_nr));
+ fprintf(fh, "\n");
+ fprintf(fh, "methods_nr = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->methods_nr));
+ fprintf(fh, "\n");
+ fprintf(fh, "variables = ");
+ min = max = save_struc->variables_nr;
+ if (!save_struc->variables)
+ min = max = 0; /* Don't write if it points to NULL */
+ fprintf(fh, "[%d][\n", max);
+ for (i = 0; i < min; i++) {
+ write_reg_t(fh, &(save_struc->variables[i]));
+ fprintf(fh, "\n");
+ }
+ fprintf(fh, "]");
+ fprintf(fh, "\n");
+ fprintf(fh, "}");
+}
+
+static int
+_cfsml_read_object_t(FILE *fh, object_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+int min, max, i;
+ int assignment, closed, done;
+
+ if (strcmp(lastval, "{")) {
+ _cfsml_error("Reading record object_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval);
+ return CFSML_FAILURE;
+ };
+ closed = 0;
+ do {
+ char *value;
+ token = _cfsml_get_identifier(fh, line, hiteof, &assignment);
+
+ if (!token) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!assignment) {
+ if (!strcmp(token, "}"))
+ closed = 1;
+ else {
+ _cfsml_error("Expected assignment or closing braces in line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else {
+ value = "";
+ while (!value || !strcmp(value, ""))
+ value = _cfsml_get_value(fh, line, hiteof);
+ if (!value) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!strcmp(token, "flags")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->flags), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for flags at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "pos")) {
+ if (read_reg_t(fh, (reg_t*) &(save_struc->pos), value, line, hiteof)) {
+ _cfsml_error("Token expected by read_reg_t() for pos at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "variables_nr")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->variables_nr), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for variables_nr at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "variable_names_nr")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->variable_names_nr), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for variable_names_nr at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "methods_nr")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->methods_nr), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for methods_nr at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "variables")) {
+ if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) {
+ _cfsml_error("Opening brackets expected at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ /* Prepare to restore dynamic array */
+ max = strtol(value + 1, NULL, 0);
+ if (max < 0) {
+ _cfsml_error("Invalid number of elements to allocate for dynamic array '%s' at line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+
+ if (max) {
+ save_struc->variables = (reg_t *) sci_malloc(max * sizeof(reg_t));
+#ifdef SATISFY_PURIFY
+ memset(save_struc->variables, 0, max * sizeof(reg_t));
+#endif
+ _cfsml_register_pointer(save_struc->variables);
+ }
+ else
+ save_struc->variables = NULL;
+ done = i = 0;
+ do {
+ if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) {
+ _cfsml_error("Token expected at line %d\n", *line);
+ return 1;
+ }
+ if (strcmp(value, "]")) {
+ if (i == max) {
+ _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line);
+ return CFSML_FAILURE;
+ }
+ if (read_reg_t(fh, &(save_struc->variables[i++]), value, line, hiteof)) {
+ _cfsml_error("Token expected by read_reg_t() for variables[i++] at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else done = 1;
+ } while (!done);
+ save_struc->variables_nr = max ; /* Set array size accordingly */
+ } else
+ {
+ _cfsml_error("object_t: Assignment to invalid identifier '%s' in line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+ }
+ } while (!closed); /* Until closing braces are hit */
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_string(FILE *fh, char ** save_struc)
+{
+ if (!(*save_struc))
+ fprintf(fh, "\\null\\");
+ else {
+ char *token = _cfsml_mangle_string((char *) *save_struc);
+ fprintf(fh, "\"%s\"", token);
+ free(token);
+ }
+}
+
+static int
+_cfsml_read_string(FILE *fh, char ** save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+
+ if (strcmp(lastval, "\\null\\")) { /* null pointer? */
+ if (*lastval == '"') { /* Quoted string? */
+ int seeker = strlen(lastval);
+
+ while (lastval[seeker] != '"')
+ --seeker;
+
+ if (!seeker) { /* No matching double-quotes? */
+ _cfsml_error("Unbalanced quotes at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+
+ lastval[seeker] = 0; /* Terminate string at closing quotes... */
+ lastval++; /* ...and skip the opening quotes locally */
+ }
+ *save_struc = _cfsml_unmangle_string(lastval);
+ _cfsml_register_pointer(*save_struc);
+ return CFSML_SUCCESS;
+ } else {
+ *save_struc = NULL;
+ return CFSML_SUCCESS;
+ }
+}
+
+static void
+_cfsml_write_menubar_t(FILE *fh, menubar_t* save_struc)
+{
+ int min, max, i;
+
+ fprintf(fh, "{\n");
+ fprintf(fh, "menus = ");
+ min = max = save_struc->menus_nr;
+ if (!save_struc->menus)
+ min = max = 0; /* Don't write if it points to NULL */
+ fprintf(fh, "[%d][\n", max);
+ for (i = 0; i < min; i++) {
+ _cfsml_write_menu_t(fh, &(save_struc->menus[i]));
+ fprintf(fh, "\n");
+ }
+ fprintf(fh, "]");
+ fprintf(fh, "\n");
+ fprintf(fh, "}");
+}
+
+static int
+_cfsml_read_menubar_t(FILE *fh, menubar_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+int min, max, i;
+ int assignment, closed, done;
+
+ if (strcmp(lastval, "{")) {
+ _cfsml_error("Reading record menubar_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval);
+ return CFSML_FAILURE;
+ };
+ closed = 0;
+ do {
+ char *value;
+ token = _cfsml_get_identifier(fh, line, hiteof, &assignment);
+
+ if (!token) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!assignment) {
+ if (!strcmp(token, "}"))
+ closed = 1;
+ else {
+ _cfsml_error("Expected assignment or closing braces in line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else {
+ value = "";
+ while (!value || !strcmp(value, ""))
+ value = _cfsml_get_value(fh, line, hiteof);
+ if (!value) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!strcmp(token, "menus")) {
+ if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) {
+ _cfsml_error("Opening brackets expected at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ /* Prepare to restore dynamic array */
+ max = strtol(value + 1, NULL, 0);
+ if (max < 0) {
+ _cfsml_error("Invalid number of elements to allocate for dynamic array '%s' at line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+
+ if (max) {
+ save_struc->menus = (menu_t *) sci_malloc(max * sizeof(menu_t));
+#ifdef SATISFY_PURIFY
+ memset(save_struc->menus, 0, max * sizeof(menu_t));
+#endif
+ _cfsml_register_pointer(save_struc->menus);
+ }
+ else
+ save_struc->menus = NULL;
+ done = i = 0;
+ do {
+ if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) {
+ _cfsml_error("Token expected at line %d\n", *line);
+ return 1;
+ }
+ if (strcmp(value, "]")) {
+ if (i == max) {
+ _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line);
+ return CFSML_FAILURE;
+ }
+ if (_cfsml_read_menu_t(fh, &(save_struc->menus[i++]), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_menu_t() for menus[i++] at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else done = 1;
+ } while (!done);
+ save_struc->menus_nr = max ; /* Set array size accordingly */
+ } else
+ {
+ _cfsml_error("menubar_t: Assignment to invalid identifier '%s' in line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+ }
+ } while (!closed); /* Until closing braces are hit */
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_size_t(FILE *fh, size_t* save_struc)
+{
+ fprintf(fh, "%li", (long) *save_struc);
+}
+
+static int
+_cfsml_read_size_t(FILE *fh, size_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+
+ *save_struc = strtol(lastval, &token, 0);
+ if ( (*save_struc == 0) && (token == lastval) ) {
+ _cfsml_error("strtol failed at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (*token != 0) {
+ _cfsml_error("Non-integer encountered while parsing int value at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_list_entry_t(FILE *fh, list_entry_t* save_struc)
+{
+ int min, max, i;
+
+ fprintf(fh, "{\n");
+ fprintf(fh, "next_free = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->next_free));
+ fprintf(fh, "\n");
+ fprintf(fh, "entry = ");
+ _cfsml_write_list_t(fh, (list_t*) &(save_struc->entry));
+ fprintf(fh, "\n");
+ fprintf(fh, "}");
+}
+
+static int
+_cfsml_read_list_entry_t(FILE *fh, list_entry_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+int min, max, i;
+ int assignment, closed, done;
+
+ if (strcmp(lastval, "{")) {
+ _cfsml_error("Reading record list_entry_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval);
+ return CFSML_FAILURE;
+ };
+ closed = 0;
+ do {
+ char *value;
+ token = _cfsml_get_identifier(fh, line, hiteof, &assignment);
+
+ if (!token) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!assignment) {
+ if (!strcmp(token, "}"))
+ closed = 1;
+ else {
+ _cfsml_error("Expected assignment or closing braces in line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else {
+ value = "";
+ while (!value || !strcmp(value, ""))
+ value = _cfsml_get_value(fh, line, hiteof);
+ if (!value) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!strcmp(token, "next_free")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->next_free), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for next_free at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "entry")) {
+ if (_cfsml_read_list_t(fh, (list_t*) &(save_struc->entry), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_list_t() for entry at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ {
+ _cfsml_error("list_entry_t: Assignment to invalid identifier '%s' in line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+ }
+ } while (!closed); /* Until closing braces are hit */
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_int_hash_map_t(FILE *fh, int_hash_map_t* save_struc)
+{
+ int min, max, i;
+
+ fprintf(fh, "{\n");
+ fprintf(fh, "base_value = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->base_value));
+ fprintf(fh, "\n");
+ fprintf(fh, "nodes = ");
+ min = max = DCS_INT_HASH_MAX+1;
+ fprintf(fh, "[%d][\n", max);
+ for (i = 0; i < min; i++) {
+ write_int_hash_map_node_tp(fh, &(save_struc->nodes[i]));
+ fprintf(fh, "\n");
+ }
+ fprintf(fh, "]");
+ fprintf(fh, "\n");
+ fprintf(fh, "}");
+}
+
+static int
+_cfsml_read_int_hash_map_t(FILE *fh, int_hash_map_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+int min, max, i;
+ int assignment, closed, done;
+
+ if (strcmp(lastval, "{")) {
+ _cfsml_error("Reading record int_hash_map_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval);
+ return CFSML_FAILURE;
+ };
+ closed = 0;
+ do {
+ char *value;
+ token = _cfsml_get_identifier(fh, line, hiteof, &assignment);
+
+ if (!token) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!assignment) {
+ if (!strcmp(token, "}"))
+ closed = 1;
+ else {
+ _cfsml_error("Expected assignment or closing braces in line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else {
+ value = "";
+ while (!value || !strcmp(value, ""))
+ value = _cfsml_get_value(fh, line, hiteof);
+ if (!value) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!strcmp(token, "base_value")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->base_value), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for base_value at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "nodes")) {
+ if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) {
+ _cfsml_error("Opening brackets expected at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ /* Prepare to restore static array */
+ max = DCS_INT_HASH_MAX+1;
+ done = i = 0;
+ do {
+ if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) {
+ _cfsml_error("Token expected at line %d\n", *line);
+ return 1;
+ }
+ if (strcmp(value, "]")) {
+ if (i == max) {
+ _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line);
+ return CFSML_FAILURE;
+ }
+ if (read_int_hash_map_node_tp(fh, &(save_struc->nodes[i++]), value, line, hiteof)) {
+ _cfsml_error("Token expected by read_int_hash_map_node_tp() for nodes[i++] at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else done = 1;
+ } while (!done);
+ } else
+ {
+ _cfsml_error("int_hash_map_t: Assignment to invalid identifier '%s' in line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+ }
+ } while (!closed); /* Until closing braces are hit */
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_gint16(FILE *fh, gint16* save_struc)
+{
+ fprintf(fh, "%li", (long) *save_struc);
+}
+
+static int
+_cfsml_read_gint16(FILE *fh, gint16* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+
+ *save_struc = strtol(lastval, &token, 0);
+ if ( (*save_struc == 0) && (token == lastval) ) {
+ _cfsml_error("strtol failed at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (*token != 0) {
+ _cfsml_error("Non-integer encountered while parsing int value at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_song_t(FILE *fh, song_t* save_struc)
+{
+ int min, max, i;
+
+ fprintf(fh, "{\n");
+ fprintf(fh, "handle = ");
+ _cfsml_write_song_handle_t(fh, (song_handle_t*) &(save_struc->handle));
+ fprintf(fh, "\n");
+ fprintf(fh, "resource_num = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->resource_num));
+ fprintf(fh, "\n");
+ fprintf(fh, "priority = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->priority));
+ fprintf(fh, "\n");
+ fprintf(fh, "status = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->status));
+ fprintf(fh, "\n");
+ fprintf(fh, "restore_behavior = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->restore_behavior));
+ fprintf(fh, "\n");
+ fprintf(fh, "restore_time = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->restore_time));
+ fprintf(fh, "\n");
+ fprintf(fh, "loops = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->loops));
+ fprintf(fh, "\n");
+ fprintf(fh, "hold = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->hold));
+ fprintf(fh, "\n");
+ fprintf(fh, "}");
+}
+
+static int
+_cfsml_read_song_t(FILE *fh, song_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+int min, max, i;
+ int assignment, closed, done;
+
+ if (strcmp(lastval, "{")) {
+ _cfsml_error("Reading record song_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval);
+ return CFSML_FAILURE;
+ };
+ closed = 0;
+ do {
+ char *value;
+ token = _cfsml_get_identifier(fh, line, hiteof, &assignment);
+
+ if (!token) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!assignment) {
+ if (!strcmp(token, "}"))
+ closed = 1;
+ else {
+ _cfsml_error("Expected assignment or closing braces in line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else {
+ value = "";
+ while (!value || !strcmp(value, ""))
+ value = _cfsml_get_value(fh, line, hiteof);
+ if (!value) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!strcmp(token, "handle")) {
+ if (_cfsml_read_song_handle_t(fh, (song_handle_t*) &(save_struc->handle), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_song_handle_t() for handle at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "resource_num")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->resource_num), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for resource_num at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "priority")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->priority), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for priority at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "status")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->status), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for status at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "restore_behavior")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->restore_behavior), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for restore_behavior at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "restore_time")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->restore_time), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for restore_time at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "loops")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->loops), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for loops at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "hold")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->hold), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for hold at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ {
+ _cfsml_error("song_t: Assignment to invalid identifier '%s' in line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+ }
+ } while (!closed); /* Until closing braces are hit */
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_menu_item_t(FILE *fh, menu_item_t* save_struc)
+{
+ int min, max, i;
+
+ fprintf(fh, "{\n");
+ fprintf(fh, "type = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->type));
+ fprintf(fh, "\n");
+ fprintf(fh, "keytext = ");
+ _cfsml_write_string(fh, (char **) &(save_struc->keytext));
+ fprintf(fh, "\n");
+ fprintf(fh, "keytext_size = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->keytext_size));
+ fprintf(fh, "\n");
+ fprintf(fh, "flags = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->flags));
+ fprintf(fh, "\n");
+ fprintf(fh, "said = ");
+ min = max = MENU_SAID_SPEC_SIZE;
+ fprintf(fh, "[%d][\n", max);
+ for (i = 0; i < min; i++) {
+ _cfsml_write_byte(fh, &(save_struc->said[i]));
+ fprintf(fh, "\n");
+ }
+ fprintf(fh, "]");
+ fprintf(fh, "\n");
+ fprintf(fh, "said_pos = ");
+ write_reg_t(fh, (reg_t*) &(save_struc->said_pos));
+ fprintf(fh, "\n");
+ fprintf(fh, "text = ");
+ _cfsml_write_string(fh, (char **) &(save_struc->text));
+ fprintf(fh, "\n");
+ fprintf(fh, "text_pos = ");
+ write_reg_t(fh, (reg_t*) &(save_struc->text_pos));
+ fprintf(fh, "\n");
+ fprintf(fh, "modifiers = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->modifiers));
+ fprintf(fh, "\n");
+ fprintf(fh, "key = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->key));
+ fprintf(fh, "\n");
+ fprintf(fh, "enabled = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->enabled));
+ fprintf(fh, "\n");
+ fprintf(fh, "tag = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->tag));
+ fprintf(fh, "\n");
+ fprintf(fh, "}");
+}
+
+static int
+_cfsml_read_menu_item_t(FILE *fh, menu_item_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+int min, max, i;
+ int assignment, closed, done;
+
+ if (strcmp(lastval, "{")) {
+ _cfsml_error("Reading record menu_item_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval);
+ return CFSML_FAILURE;
+ };
+ closed = 0;
+ do {
+ char *value;
+ token = _cfsml_get_identifier(fh, line, hiteof, &assignment);
+
+ if (!token) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!assignment) {
+ if (!strcmp(token, "}"))
+ closed = 1;
+ else {
+ _cfsml_error("Expected assignment or closing braces in line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else {
+ value = "";
+ while (!value || !strcmp(value, ""))
+ value = _cfsml_get_value(fh, line, hiteof);
+ if (!value) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!strcmp(token, "type")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->type), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for type at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "keytext")) {
+ if (_cfsml_read_string(fh, (char **) &(save_struc->keytext), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_string() for keytext at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "keytext_size")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->keytext_size), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for keytext_size at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "flags")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->flags), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for flags at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "said")) {
+ if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) {
+ _cfsml_error("Opening brackets expected at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ /* Prepare to restore static array */
+ max = MENU_SAID_SPEC_SIZE;
+ done = i = 0;
+ do {
+ if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) {
+ _cfsml_error("Token expected at line %d\n", *line);
+ return 1;
+ }
+ if (strcmp(value, "]")) {
+ if (i == max) {
+ _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line);
+ return CFSML_FAILURE;
+ }
+ if (_cfsml_read_byte(fh, &(save_struc->said[i++]), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_byte() for said[i++] at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else done = 1;
+ } while (!done);
+ } else
+ if (!strcmp(token, "said_pos")) {
+ if (read_reg_t(fh, (reg_t*) &(save_struc->said_pos), value, line, hiteof)) {
+ _cfsml_error("Token expected by read_reg_t() for said_pos at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "text")) {
+ if (_cfsml_read_string(fh, (char **) &(save_struc->text), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_string() for text at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "text_pos")) {
+ if (read_reg_t(fh, (reg_t*) &(save_struc->text_pos), value, line, hiteof)) {
+ _cfsml_error("Token expected by read_reg_t() for text_pos at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "modifiers")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->modifiers), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for modifiers at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "key")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->key), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for key at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "enabled")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->enabled), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for enabled at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "tag")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->tag), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for tag at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ {
+ _cfsml_error("menu_item_t: Assignment to invalid identifier '%s' in line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+ }
+ } while (!closed); /* Until closing braces are hit */
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_node_entry_t(FILE *fh, node_entry_t* save_struc)
+{
+ int min, max, i;
+
+ fprintf(fh, "{\n");
+ fprintf(fh, "next_free = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->next_free));
+ fprintf(fh, "\n");
+ fprintf(fh, "entry = ");
+ _cfsml_write_node_t(fh, (node_t*) &(save_struc->entry));
+ fprintf(fh, "\n");
+ fprintf(fh, "}");
+}
+
+static int
+_cfsml_read_node_entry_t(FILE *fh, node_entry_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+int min, max, i;
+ int assignment, closed, done;
+
+ if (strcmp(lastval, "{")) {
+ _cfsml_error("Reading record node_entry_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval);
+ return CFSML_FAILURE;
+ };
+ closed = 0;
+ do {
+ char *value;
+ token = _cfsml_get_identifier(fh, line, hiteof, &assignment);
+
+ if (!token) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!assignment) {
+ if (!strcmp(token, "}"))
+ closed = 1;
+ else {
+ _cfsml_error("Expected assignment or closing braces in line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else {
+ value = "";
+ while (!value || !strcmp(value, ""))
+ value = _cfsml_get_value(fh, line, hiteof);
+ if (!value) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!strcmp(token, "next_free")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->next_free), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for next_free at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "entry")) {
+ if (_cfsml_read_node_t(fh, (node_t*) &(save_struc->entry), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_node_t() for entry at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ {
+ _cfsml_error("node_entry_t: Assignment to invalid identifier '%s' in line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+ }
+ } while (!closed); /* Until closing braces are hit */
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_seg_id_t(FILE *fh, seg_id_t* save_struc)
+{
+ fprintf(fh, "%li", (long) *save_struc);
+}
+
+static int
+_cfsml_read_seg_id_t(FILE *fh, seg_id_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+
+ *save_struc = strtol(lastval, &token, 0);
+ if ( (*save_struc == 0) && (token == lastval) ) {
+ _cfsml_error("strtol failed at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (*token != 0) {
+ _cfsml_error("Non-integer encountered while parsing int value at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_dynmem_t(FILE *fh, dynmem_t* save_struc)
+{
+ int min, max, i;
+
+ fprintf(fh, "{\n");
+ fprintf(fh, "size = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->size));
+ fprintf(fh, "\n");
+ fprintf(fh, "description = ");
+ _cfsml_write_string(fh, (char **) &(save_struc->description));
+ fprintf(fh, "\n");
+ fprintf(fh, "buf = ");
+ min = max = save_struc->size;
+ if (!save_struc->buf)
+ min = max = 0; /* Don't write if it points to NULL */
+ fprintf(fh, "[%d][\n", max);
+ for (i = 0; i < min; i++) {
+ _cfsml_write_byte(fh, &(save_struc->buf[i]));
+ fprintf(fh, "\n");
+ }
+ fprintf(fh, "]");
+ fprintf(fh, "\n");
+ fprintf(fh, "}");
+}
+
+static int
+_cfsml_read_dynmem_t(FILE *fh, dynmem_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+int min, max, i;
+ int assignment, closed, done;
+
+ if (strcmp(lastval, "{")) {
+ _cfsml_error("Reading record dynmem_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval);
+ return CFSML_FAILURE;
+ };
+ closed = 0;
+ do {
+ char *value;
+ token = _cfsml_get_identifier(fh, line, hiteof, &assignment);
+
+ if (!token) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!assignment) {
+ if (!strcmp(token, "}"))
+ closed = 1;
+ else {
+ _cfsml_error("Expected assignment or closing braces in line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else {
+ value = "";
+ while (!value || !strcmp(value, ""))
+ value = _cfsml_get_value(fh, line, hiteof);
+ if (!value) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!strcmp(token, "size")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->size), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for size at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "description")) {
+ if (_cfsml_read_string(fh, (char **) &(save_struc->description), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_string() for description at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "buf")) {
+ if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) {
+ _cfsml_error("Opening brackets expected at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ /* Prepare to restore dynamic array */
+ max = strtol(value + 1, NULL, 0);
+ if (max < 0) {
+ _cfsml_error("Invalid number of elements to allocate for dynamic array '%s' at line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+
+ if (max) {
+ save_struc->buf = (byte *) sci_malloc(max * sizeof(byte));
+#ifdef SATISFY_PURIFY
+ memset(save_struc->buf, 0, max * sizeof(byte));
+#endif
+ _cfsml_register_pointer(save_struc->buf);
+ }
+ else
+ save_struc->buf = NULL;
+ done = i = 0;
+ do {
+ if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) {
+ _cfsml_error("Token expected at line %d\n", *line);
+ return 1;
+ }
+ if (strcmp(value, "]")) {
+ if (i == max) {
+ _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line);
+ return CFSML_FAILURE;
+ }
+ if (_cfsml_read_byte(fh, &(save_struc->buf[i++]), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_byte() for buf[i++] at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else done = 1;
+ } while (!done);
+ save_struc->size = max ; /* Set array size accordingly */
+ } else
+ {
+ _cfsml_error("dynmem_t: Assignment to invalid identifier '%s' in line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+ }
+ } while (!closed); /* Until closing braces are hit */
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_local_variables_t(FILE *fh, local_variables_t* save_struc)
+{
+ int min, max, i;
+
+ fprintf(fh, "{\n");
+ fprintf(fh, "script_id = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->script_id));
+ fprintf(fh, "\n");
+ fprintf(fh, "nr = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->nr));
+ fprintf(fh, "\n");
+ fprintf(fh, "locals = ");
+ min = max = save_struc->nr;
+ if (!save_struc->locals)
+ min = max = 0; /* Don't write if it points to NULL */
+ fprintf(fh, "[%d][\n", max);
+ for (i = 0; i < min; i++) {
+ write_reg_t(fh, &(save_struc->locals[i]));
+ fprintf(fh, "\n");
+ }
+ fprintf(fh, "]");
+ fprintf(fh, "\n");
+ fprintf(fh, "}");
+}
+
+static int
+_cfsml_read_local_variables_t(FILE *fh, local_variables_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+int min, max, i;
+ int assignment, closed, done;
+
+ if (strcmp(lastval, "{")) {
+ _cfsml_error("Reading record local_variables_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval);
+ return CFSML_FAILURE;
+ };
+ closed = 0;
+ do {
+ char *value;
+ token = _cfsml_get_identifier(fh, line, hiteof, &assignment);
+
+ if (!token) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!assignment) {
+ if (!strcmp(token, "}"))
+ closed = 1;
+ else {
+ _cfsml_error("Expected assignment or closing braces in line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else {
+ value = "";
+ while (!value || !strcmp(value, ""))
+ value = _cfsml_get_value(fh, line, hiteof);
+ if (!value) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!strcmp(token, "script_id")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->script_id), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for script_id at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "nr")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->nr), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for nr at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "locals")) {
+ if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) {
+ _cfsml_error("Opening brackets expected at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ /* Prepare to restore dynamic array */
+ max = strtol(value + 1, NULL, 0);
+ if (max < 0) {
+ _cfsml_error("Invalid number of elements to allocate for dynamic array '%s' at line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+
+ if (max) {
+ save_struc->locals = (reg_t *) sci_malloc(max * sizeof(reg_t));
+#ifdef SATISFY_PURIFY
+ memset(save_struc->locals, 0, max * sizeof(reg_t));
+#endif
+ _cfsml_register_pointer(save_struc->locals);
+ }
+ else
+ save_struc->locals = NULL;
+ done = i = 0;
+ do {
+ if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) {
+ _cfsml_error("Token expected at line %d\n", *line);
+ return 1;
+ }
+ if (strcmp(value, "]")) {
+ if (i == max) {
+ _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line);
+ return CFSML_FAILURE;
+ }
+ if (read_reg_t(fh, &(save_struc->locals[i++]), value, line, hiteof)) {
+ _cfsml_error("Token expected by read_reg_t() for locals[i++] at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else done = 1;
+ } while (!done);
+ save_struc->nr = max ; /* Set array size accordingly */
+ } else
+ {
+ _cfsml_error("local_variables_t: Assignment to invalid identifier '%s' in line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+ }
+ } while (!closed); /* Until closing braces are hit */
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_state_t(FILE *fh, state_t* save_struc)
+{
+ int min, max, i;
+
+ fprintf(fh, "{\n");
+ fprintf(fh, "savegame_version = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->savegame_version));
+ fprintf(fh, "\n");
+ fprintf(fh, "game_version = ");
+ _cfsml_write_string(fh, (char **) &(save_struc->game_version));
+ fprintf(fh, "\n");
+ fprintf(fh, "version = ");
+ write_sci_version(fh, (sci_version_t*) &(save_struc->version));
+ fprintf(fh, "\n");
+ fprintf(fh, "menubar = ");
+ write_menubar_tp(fh, (menubar_t **) &(save_struc->menubar));
+ fprintf(fh, "\n");
+ fprintf(fh, "status_bar_foreground = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->status_bar_foreground));
+ fprintf(fh, "\n");
+ fprintf(fh, "status_bar_background = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->status_bar_background));
+ fprintf(fh, "\n");
+ fprintf(fh, "seg_manager = ");
+ _cfsml_write_seg_manager_t(fh, (seg_manager_t*) &(save_struc->seg_manager));
+ fprintf(fh, "\n");
+ fprintf(fh, "classtable_size = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->classtable_size));
+ fprintf(fh, "\n");
+ fprintf(fh, "classtable = ");
+ min = max = save_struc->classtable_size;
+ if (!save_struc->classtable)
+ min = max = 0; /* Don't write if it points to NULL */
+ fprintf(fh, "[%d][\n", max);
+ for (i = 0; i < min; i++) {
+ _cfsml_write_class_t(fh, &(save_struc->classtable[i]));
+ fprintf(fh, "\n");
+ }
+ fprintf(fh, "]");
+ fprintf(fh, "\n");
+ fprintf(fh, "sound = ");
+ _cfsml_write_sfx_state_t(fh, (sfx_state_t*) &(save_struc->sound));
+ fprintf(fh, "\n");
+ fprintf(fh, "}");
+}
+
+static int
+_cfsml_read_state_t(FILE *fh, state_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+int min, max, i;
+ int assignment, closed, done;
+
+ if (strcmp(lastval, "{")) {
+ _cfsml_error("Reading record state_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval);
+ return CFSML_FAILURE;
+ };
+ closed = 0;
+ do {
+ char *value;
+ token = _cfsml_get_identifier(fh, line, hiteof, &assignment);
+
+ if (!token) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!assignment) {
+ if (!strcmp(token, "}"))
+ closed = 1;
+ else {
+ _cfsml_error("Expected assignment or closing braces in line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else {
+ value = "";
+ while (!value || !strcmp(value, ""))
+ value = _cfsml_get_value(fh, line, hiteof);
+ if (!value) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!strcmp(token, "savegame_version")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->savegame_version), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for savegame_version at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "game_version")) {
+ if (_cfsml_read_string(fh, (char **) &(save_struc->game_version), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_string() for game_version at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "version")) {
+ if (read_sci_version(fh, (sci_version_t*) &(save_struc->version), value, line, hiteof)) {
+ _cfsml_error("Token expected by read_sci_version() for version at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "menubar")) {
+ if (read_menubar_tp(fh, (menubar_t **) &(save_struc->menubar), value, line, hiteof)) {
+ _cfsml_error("Token expected by read_menubar_tp() for menubar at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "status_bar_foreground")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->status_bar_foreground), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for status_bar_foreground at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "status_bar_background")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->status_bar_background), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for status_bar_background at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "seg_manager")) {
+ if (_cfsml_read_seg_manager_t(fh, (seg_manager_t*) &(save_struc->seg_manager), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_seg_manager_t() for seg_manager at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "classtable_size")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->classtable_size), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for classtable_size at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "classtable")) {
+ if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) {
+ _cfsml_error("Opening brackets expected at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ /* Prepare to restore dynamic array */
+ max = strtol(value + 1, NULL, 0);
+ if (max < 0) {
+ _cfsml_error("Invalid number of elements to allocate for dynamic array '%s' at line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+
+ if (max) {
+ save_struc->classtable = (class_t *) sci_malloc(max * sizeof(class_t));
+#ifdef SATISFY_PURIFY
+ memset(save_struc->classtable, 0, max * sizeof(class_t));
+#endif
+ _cfsml_register_pointer(save_struc->classtable);
+ }
+ else
+ save_struc->classtable = NULL;
+ done = i = 0;
+ do {
+ if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) {
+ _cfsml_error("Token expected at line %d\n", *line);
+ return 1;
+ }
+ if (strcmp(value, "]")) {
+ if (i == max) {
+ _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line);
+ return CFSML_FAILURE;
+ }
+ if (_cfsml_read_class_t(fh, &(save_struc->classtable[i++]), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_class_t() for classtable[i++] at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else done = 1;
+ } while (!done);
+ save_struc->classtable_size = max ; /* Set array size accordingly */
+ } else
+ if (!strcmp(token, "sound")) {
+ if (_cfsml_read_sfx_state_t(fh, (sfx_state_t*) &(save_struc->sound), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_sfx_state_t() for sound at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ {
+ _cfsml_error("state_t: Assignment to invalid identifier '%s' in line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+ }
+ } while (!closed); /* Until closing braces are hit */
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_node_table_t(FILE *fh, node_table_t* save_struc)
+{
+ int min, max, i;
+
+ fprintf(fh, "{\n");
+ fprintf(fh, "entries_nr = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->entries_nr));
+ fprintf(fh, "\n");
+ fprintf(fh, "first_free = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->first_free));
+ fprintf(fh, "\n");
+ fprintf(fh, "entries_used = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->entries_used));
+ fprintf(fh, "\n");
+ fprintf(fh, "max_entry = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->max_entry));
+ fprintf(fh, "\n");
+ fprintf(fh, "table = ");
+ min = max = save_struc->entries_nr;
+ if (!save_struc->table)
+ min = max = 0; /* Don't write if it points to NULL */
+ fprintf(fh, "[%d][\n", max);
+ for (i = 0; i < min; i++) {
+ _cfsml_write_node_entry_t(fh, &(save_struc->table[i]));
+ fprintf(fh, "\n");
+ }
+ fprintf(fh, "]");
+ fprintf(fh, "\n");
+ fprintf(fh, "}");
+}
+
+static int
+_cfsml_read_node_table_t(FILE *fh, node_table_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+int min, max, i;
+ int assignment, closed, done;
+
+ if (strcmp(lastval, "{")) {
+ _cfsml_error("Reading record node_table_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval);
+ return CFSML_FAILURE;
+ };
+ closed = 0;
+ do {
+ char *value;
+ token = _cfsml_get_identifier(fh, line, hiteof, &assignment);
+
+ if (!token) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!assignment) {
+ if (!strcmp(token, "}"))
+ closed = 1;
+ else {
+ _cfsml_error("Expected assignment or closing braces in line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else {
+ value = "";
+ while (!value || !strcmp(value, ""))
+ value = _cfsml_get_value(fh, line, hiteof);
+ if (!value) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!strcmp(token, "entries_nr")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->entries_nr), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for entries_nr at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "first_free")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->first_free), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for first_free at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "entries_used")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->entries_used), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for entries_used at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "max_entry")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->max_entry), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for max_entry at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "table")) {
+ if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) {
+ _cfsml_error("Opening brackets expected at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ /* Prepare to restore dynamic array */
+ max = strtol(value + 1, NULL, 0);
+ if (max < 0) {
+ _cfsml_error("Invalid number of elements to allocate for dynamic array '%s' at line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+
+ if (max) {
+ save_struc->table = (node_entry_t *) sci_malloc(max * sizeof(node_entry_t));
+#ifdef SATISFY_PURIFY
+ memset(save_struc->table, 0, max * sizeof(node_entry_t));
+#endif
+ _cfsml_register_pointer(save_struc->table);
+ }
+ else
+ save_struc->table = NULL;
+ done = i = 0;
+ do {
+ if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) {
+ _cfsml_error("Token expected at line %d\n", *line);
+ return 1;
+ }
+ if (strcmp(value, "]")) {
+ if (i == max) {
+ _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line);
+ return CFSML_FAILURE;
+ }
+ if (_cfsml_read_node_entry_t(fh, &(save_struc->table[i++]), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_node_entry_t() for table[i++] at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else done = 1;
+ } while (!done);
+ save_struc->entries_nr = max ; /* Set array size accordingly */
+ } else
+ {
+ _cfsml_error("node_table_t: Assignment to invalid identifier '%s' in line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+ }
+ } while (!closed); /* Until closing braces are hit */
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_sys_strings_t(FILE *fh, sys_strings_t* save_struc)
+{
+ int min, max, i;
+
+ fprintf(fh, "{\n");
+ fprintf(fh, "strings = ");
+ min = max = SYS_STRINGS_MAX;
+ fprintf(fh, "[%d][\n", max);
+ for (i = 0; i < min; i++) {
+ _cfsml_write_sys_string_t(fh, &(save_struc->strings[i]));
+ fprintf(fh, "\n");
+ }
+ fprintf(fh, "]");
+ fprintf(fh, "\n");
+ fprintf(fh, "}");
+}
+
+static int
+_cfsml_read_sys_strings_t(FILE *fh, sys_strings_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+int min, max, i;
+ int assignment, closed, done;
+
+ if (strcmp(lastval, "{")) {
+ _cfsml_error("Reading record sys_strings_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval);
+ return CFSML_FAILURE;
+ };
+ closed = 0;
+ do {
+ char *value;
+ token = _cfsml_get_identifier(fh, line, hiteof, &assignment);
+
+ if (!token) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!assignment) {
+ if (!strcmp(token, "}"))
+ closed = 1;
+ else {
+ _cfsml_error("Expected assignment or closing braces in line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else {
+ value = "";
+ while (!value || !strcmp(value, ""))
+ value = _cfsml_get_value(fh, line, hiteof);
+ if (!value) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!strcmp(token, "strings")) {
+ if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) {
+ _cfsml_error("Opening brackets expected at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ /* Prepare to restore static array */
+ max = SYS_STRINGS_MAX;
+ done = i = 0;
+ do {
+ if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) {
+ _cfsml_error("Token expected at line %d\n", *line);
+ return 1;
+ }
+ if (strcmp(value, "]")) {
+ if (i == max) {
+ _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line);
+ return CFSML_FAILURE;
+ }
+ if (_cfsml_read_sys_string_t(fh, &(save_struc->strings[i++]), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_sys_string_t() for strings[i++] at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else done = 1;
+ } while (!done);
+ } else
+ {
+ _cfsml_error("sys_strings_t: Assignment to invalid identifier '%s' in line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+ }
+ } while (!closed); /* Until closing braces are hit */
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_byte(FILE *fh, byte* save_struc)
+{
+ fprintf(fh, "%li", (long) *save_struc);
+}
+
+static int
+_cfsml_read_byte(FILE *fh, byte* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+
+ *save_struc = strtol(lastval, &token, 0);
+ if ( (*save_struc == 0) && (token == lastval) ) {
+ _cfsml_error("strtol failed at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (*token != 0) {
+ _cfsml_error("Non-integer encountered while parsing int value at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_node_t(FILE *fh, node_t* save_struc)
+{
+ int min, max, i;
+
+ fprintf(fh, "{\n");
+ fprintf(fh, "pred = ");
+ write_reg_t(fh, (reg_t*) &(save_struc->pred));
+ fprintf(fh, "\n");
+ fprintf(fh, "succ = ");
+ write_reg_t(fh, (reg_t*) &(save_struc->succ));
+ fprintf(fh, "\n");
+ fprintf(fh, "key = ");
+ write_reg_t(fh, (reg_t*) &(save_struc->key));
+ fprintf(fh, "\n");
+ fprintf(fh, "value = ");
+ write_reg_t(fh, (reg_t*) &(save_struc->value));
+ fprintf(fh, "\n");
+ fprintf(fh, "}");
+}
+
+static int
+_cfsml_read_node_t(FILE *fh, node_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+int min, max, i;
+ int assignment, closed, done;
+
+ if (strcmp(lastval, "{")) {
+ _cfsml_error("Reading record node_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval);
+ return CFSML_FAILURE;
+ };
+ closed = 0;
+ do {
+ char *value;
+ token = _cfsml_get_identifier(fh, line, hiteof, &assignment);
+
+ if (!token) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!assignment) {
+ if (!strcmp(token, "}"))
+ closed = 1;
+ else {
+ _cfsml_error("Expected assignment or closing braces in line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else {
+ value = "";
+ while (!value || !strcmp(value, ""))
+ value = _cfsml_get_value(fh, line, hiteof);
+ if (!value) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!strcmp(token, "pred")) {
+ if (read_reg_t(fh, (reg_t*) &(save_struc->pred), value, line, hiteof)) {
+ _cfsml_error("Token expected by read_reg_t() for pred at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "succ")) {
+ if (read_reg_t(fh, (reg_t*) &(save_struc->succ), value, line, hiteof)) {
+ _cfsml_error("Token expected by read_reg_t() for succ at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "key")) {
+ if (read_reg_t(fh, (reg_t*) &(save_struc->key), value, line, hiteof)) {
+ _cfsml_error("Token expected by read_reg_t() for key at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "value")) {
+ if (read_reg_t(fh, (reg_t*) &(save_struc->value), value, line, hiteof)) {
+ _cfsml_error("Token expected by read_reg_t() for value at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ {
+ _cfsml_error("node_t: Assignment to invalid identifier '%s' in line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+ }
+ } while (!closed); /* Until closing braces are hit */
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_list_table_t(FILE *fh, list_table_t* save_struc)
+{
+ int min, max, i;
+
+ fprintf(fh, "{\n");
+ fprintf(fh, "entries_nr = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->entries_nr));
+ fprintf(fh, "\n");
+ fprintf(fh, "first_free = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->first_free));
+ fprintf(fh, "\n");
+ fprintf(fh, "entries_used = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->entries_used));
+ fprintf(fh, "\n");
+ fprintf(fh, "max_entry = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->max_entry));
+ fprintf(fh, "\n");
+ fprintf(fh, "table = ");
+ min = max = save_struc->entries_nr;
+ if (!save_struc->table)
+ min = max = 0; /* Don't write if it points to NULL */
+ fprintf(fh, "[%d][\n", max);
+ for (i = 0; i < min; i++) {
+ _cfsml_write_list_entry_t(fh, &(save_struc->table[i]));
+ fprintf(fh, "\n");
+ }
+ fprintf(fh, "]");
+ fprintf(fh, "\n");
+ fprintf(fh, "}");
+}
+
+static int
+_cfsml_read_list_table_t(FILE *fh, list_table_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+int min, max, i;
+ int assignment, closed, done;
+
+ if (strcmp(lastval, "{")) {
+ _cfsml_error("Reading record list_table_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval);
+ return CFSML_FAILURE;
+ };
+ closed = 0;
+ do {
+ char *value;
+ token = _cfsml_get_identifier(fh, line, hiteof, &assignment);
+
+ if (!token) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!assignment) {
+ if (!strcmp(token, "}"))
+ closed = 1;
+ else {
+ _cfsml_error("Expected assignment or closing braces in line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else {
+ value = "";
+ while (!value || !strcmp(value, ""))
+ value = _cfsml_get_value(fh, line, hiteof);
+ if (!value) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!strcmp(token, "entries_nr")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->entries_nr), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for entries_nr at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "first_free")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->first_free), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for first_free at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "entries_used")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->entries_used), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for entries_used at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "max_entry")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->max_entry), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for max_entry at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "table")) {
+ if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) {
+ _cfsml_error("Opening brackets expected at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ /* Prepare to restore dynamic array */
+ max = strtol(value + 1, NULL, 0);
+ if (max < 0) {
+ _cfsml_error("Invalid number of elements to allocate for dynamic array '%s' at line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+
+ if (max) {
+ save_struc->table = (list_entry_t *) sci_malloc(max * sizeof(list_entry_t));
+#ifdef SATISFY_PURIFY
+ memset(save_struc->table, 0, max * sizeof(list_entry_t));
+#endif
+ _cfsml_register_pointer(save_struc->table);
+ }
+ else
+ save_struc->table = NULL;
+ done = i = 0;
+ do {
+ if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) {
+ _cfsml_error("Token expected at line %d\n", *line);
+ return 1;
+ }
+ if (strcmp(value, "]")) {
+ if (i == max) {
+ _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line);
+ return CFSML_FAILURE;
+ }
+ if (_cfsml_read_list_entry_t(fh, &(save_struc->table[i++]), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_list_entry_t() for table[i++] at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else done = 1;
+ } while (!done);
+ save_struc->entries_nr = max ; /* Set array size accordingly */
+ } else
+ {
+ _cfsml_error("list_table_t: Assignment to invalid identifier '%s' in line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+ }
+ } while (!closed); /* Until closing braces are hit */
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_class_t(FILE *fh, class_t* save_struc)
+{
+ int min, max, i;
+
+ fprintf(fh, "{\n");
+ fprintf(fh, "script = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->script));
+ fprintf(fh, "\n");
+ fprintf(fh, "reg = ");
+ write_reg_t(fh, (reg_t*) &(save_struc->reg));
+ fprintf(fh, "\n");
+ fprintf(fh, "}");
+}
+
+static int
+_cfsml_read_class_t(FILE *fh, class_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+int min, max, i;
+ int assignment, closed, done;
+
+ if (strcmp(lastval, "{")) {
+ _cfsml_error("Reading record class_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval);
+ return CFSML_FAILURE;
+ };
+ closed = 0;
+ do {
+ char *value;
+ token = _cfsml_get_identifier(fh, line, hiteof, &assignment);
+
+ if (!token) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!assignment) {
+ if (!strcmp(token, "}"))
+ closed = 1;
+ else {
+ _cfsml_error("Expected assignment or closing braces in line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else {
+ value = "";
+ while (!value || !strcmp(value, ""))
+ value = _cfsml_get_value(fh, line, hiteof);
+ if (!value) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!strcmp(token, "script")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->script), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for script at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "reg")) {
+ if (read_reg_t(fh, (reg_t*) &(save_struc->reg), value, line, hiteof)) {
+ _cfsml_error("Token expected by read_reg_t() for reg at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ {
+ _cfsml_error("class_t: Assignment to invalid identifier '%s' in line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+ }
+ } while (!closed); /* Until closing braces are hit */
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_song_handle_t(FILE *fh, song_handle_t* save_struc)
+{
+ fprintf(fh, "%li", (long) *save_struc);
+}
+
+static int
+_cfsml_read_song_handle_t(FILE *fh, song_handle_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+
+ *save_struc = strtol(lastval, &token, 0);
+ if ( (*save_struc == 0) && (token == lastval) ) {
+ _cfsml_error("strtol failed at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (*token != 0) {
+ _cfsml_error("Non-integer encountered while parsing int value at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_int(FILE *fh, int* save_struc)
+{
+ fprintf(fh, "%li", (long) *save_struc);
+}
+
+static int
+_cfsml_read_int(FILE *fh, int* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+
+ *save_struc = strtol(lastval, &token, 0);
+ if ( (*save_struc == 0) && (token == lastval) ) {
+ _cfsml_error("strtol failed at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (*token != 0) {
+ _cfsml_error("Non-integer encountered while parsing int value at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_menu_t(FILE *fh, menu_t* save_struc)
+{
+ int min, max, i;
+
+ fprintf(fh, "{\n");
+ fprintf(fh, "title = ");
+ _cfsml_write_string(fh, (char **) &(save_struc->title));
+ fprintf(fh, "\n");
+ fprintf(fh, "title_width = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->title_width));
+ fprintf(fh, "\n");
+ fprintf(fh, "width = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->width));
+ fprintf(fh, "\n");
+ fprintf(fh, "items = ");
+ min = max = save_struc->items_nr;
+ if (!save_struc->items)
+ min = max = 0; /* Don't write if it points to NULL */
+ fprintf(fh, "[%d][\n", max);
+ for (i = 0; i < min; i++) {
+ _cfsml_write_menu_item_t(fh, &(save_struc->items[i]));
+ fprintf(fh, "\n");
+ }
+ fprintf(fh, "]");
+ fprintf(fh, "\n");
+ fprintf(fh, "}");
+}
+
+static int
+_cfsml_read_menu_t(FILE *fh, menu_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+int min, max, i;
+ int assignment, closed, done;
+
+ if (strcmp(lastval, "{")) {
+ _cfsml_error("Reading record menu_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval);
+ return CFSML_FAILURE;
+ };
+ closed = 0;
+ do {
+ char *value;
+ token = _cfsml_get_identifier(fh, line, hiteof, &assignment);
+
+ if (!token) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!assignment) {
+ if (!strcmp(token, "}"))
+ closed = 1;
+ else {
+ _cfsml_error("Expected assignment or closing braces in line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else {
+ value = "";
+ while (!value || !strcmp(value, ""))
+ value = _cfsml_get_value(fh, line, hiteof);
+ if (!value) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!strcmp(token, "title")) {
+ if (_cfsml_read_string(fh, (char **) &(save_struc->title), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_string() for title at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "title_width")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->title_width), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for title_width at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "width")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->width), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for width at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "items")) {
+ if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) {
+ _cfsml_error("Opening brackets expected at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ /* Prepare to restore dynamic array */
+ max = strtol(value + 1, NULL, 0);
+ if (max < 0) {
+ _cfsml_error("Invalid number of elements to allocate for dynamic array '%s' at line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+
+ if (max) {
+ save_struc->items = (menu_item_t *) sci_malloc(max * sizeof(menu_item_t));
+#ifdef SATISFY_PURIFY
+ memset(save_struc->items, 0, max * sizeof(menu_item_t));
+#endif
+ _cfsml_register_pointer(save_struc->items);
+ }
+ else
+ save_struc->items = NULL;
+ done = i = 0;
+ do {
+ if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) {
+ _cfsml_error("Token expected at line %d\n", *line);
+ return 1;
+ }
+ if (strcmp(value, "]")) {
+ if (i == max) {
+ _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line);
+ return CFSML_FAILURE;
+ }
+ if (_cfsml_read_menu_item_t(fh, &(save_struc->items[i++]), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_menu_item_t() for items[i++] at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else done = 1;
+ } while (!done);
+ save_struc->items_nr = max ; /* Set array size accordingly */
+ } else
+ {
+ _cfsml_error("menu_t: Assignment to invalid identifier '%s' in line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+ }
+ } while (!closed); /* Until closing braces are hit */
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_long(FILE *fh, long* save_struc)
+{
+ fprintf(fh, "%li", (long) *save_struc);
+}
+
+static int
+_cfsml_read_long(FILE *fh, long* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+
+ *save_struc = strtol(lastval, &token, 0);
+ if ( (*save_struc == 0) && (token == lastval) ) {
+ _cfsml_error("strtol failed at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (*token != 0) {
+ _cfsml_error("Non-integer encountered while parsing int value at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_clone_table_t(FILE *fh, clone_table_t* save_struc)
+{
+ int min, max, i;
+
+ fprintf(fh, "{\n");
+ fprintf(fh, "entries_nr = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->entries_nr));
+ fprintf(fh, "\n");
+ fprintf(fh, "first_free = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->first_free));
+ fprintf(fh, "\n");
+ fprintf(fh, "entries_used = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->entries_used));
+ fprintf(fh, "\n");
+ fprintf(fh, "max_entry = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->max_entry));
+ fprintf(fh, "\n");
+ fprintf(fh, "table = ");
+ min = max = save_struc->entries_nr;
+ if (!save_struc->table)
+ min = max = 0; /* Don't write if it points to NULL */
+ fprintf(fh, "[%d][\n", max);
+ for (i = 0; i < min; i++) {
+ _cfsml_write_clone_entry_t(fh, &(save_struc->table[i]));
+ fprintf(fh, "\n");
+ }
+ fprintf(fh, "]");
+ fprintf(fh, "\n");
+ fprintf(fh, "}");
+}
+
+static int
+_cfsml_read_clone_table_t(FILE *fh, clone_table_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+int min, max, i;
+ int assignment, closed, done;
+
+ if (strcmp(lastval, "{")) {
+ _cfsml_error("Reading record clone_table_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval);
+ return CFSML_FAILURE;
+ };
+ closed = 0;
+ do {
+ char *value;
+ token = _cfsml_get_identifier(fh, line, hiteof, &assignment);
+
+ if (!token) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!assignment) {
+ if (!strcmp(token, "}"))
+ closed = 1;
+ else {
+ _cfsml_error("Expected assignment or closing braces in line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else {
+ value = "";
+ while (!value || !strcmp(value, ""))
+ value = _cfsml_get_value(fh, line, hiteof);
+ if (!value) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!strcmp(token, "entries_nr")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->entries_nr), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for entries_nr at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "first_free")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->first_free), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for first_free at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "entries_used")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->entries_used), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for entries_used at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "max_entry")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->max_entry), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for max_entry at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "table")) {
+ if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) {
+ _cfsml_error("Opening brackets expected at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ /* Prepare to restore dynamic array */
+ max = strtol(value + 1, NULL, 0);
+ if (max < 0) {
+ _cfsml_error("Invalid number of elements to allocate for dynamic array '%s' at line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+
+ if (max) {
+ save_struc->table = (clone_entry_t *) sci_malloc(max * sizeof(clone_entry_t));
+#ifdef SATISFY_PURIFY
+ memset(save_struc->table, 0, max * sizeof(clone_entry_t));
+#endif
+ _cfsml_register_pointer(save_struc->table);
+ }
+ else
+ save_struc->table = NULL;
+ done = i = 0;
+ do {
+ if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) {
+ _cfsml_error("Token expected at line %d\n", *line);
+ return 1;
+ }
+ if (strcmp(value, "]")) {
+ if (i == max) {
+ _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line);
+ return CFSML_FAILURE;
+ }
+ if (_cfsml_read_clone_entry_t(fh, &(save_struc->table[i++]), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_clone_entry_t() for table[i++] at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else done = 1;
+ } while (!done);
+ save_struc->entries_nr = max ; /* Set array size accordingly */
+ } else
+ {
+ _cfsml_error("clone_table_t: Assignment to invalid identifier '%s' in line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+ }
+ } while (!closed); /* Until closing braces are hit */
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_clone_t(FILE *fh, clone_t* save_struc)
+{
+ int min, max, i;
+
+ fprintf(fh, "{\n");
+ fprintf(fh, "flags = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->flags));
+ fprintf(fh, "\n");
+ fprintf(fh, "pos = ");
+ write_reg_t(fh, (reg_t*) &(save_struc->pos));
+ fprintf(fh, "\n");
+ fprintf(fh, "variables_nr = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->variables_nr));
+ fprintf(fh, "\n");
+ fprintf(fh, "variable_names_nr = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->variable_names_nr));
+ fprintf(fh, "\n");
+ fprintf(fh, "methods_nr = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->methods_nr));
+ fprintf(fh, "\n");
+ fprintf(fh, "variables = ");
+ min = max = save_struc->variables_nr;
+ if (!save_struc->variables)
+ min = max = 0; /* Don't write if it points to NULL */
+ fprintf(fh, "[%d][\n", max);
+ for (i = 0; i < min; i++) {
+ write_reg_t(fh, &(save_struc->variables[i]));
+ fprintf(fh, "\n");
+ }
+ fprintf(fh, "]");
+ fprintf(fh, "\n");
+ fprintf(fh, "}");
+}
+
+static int
+_cfsml_read_clone_t(FILE *fh, clone_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+int min, max, i;
+ int assignment, closed, done;
+
+ if (strcmp(lastval, "{")) {
+ _cfsml_error("Reading record clone_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval);
+ return CFSML_FAILURE;
+ };
+ closed = 0;
+ do {
+ char *value;
+ token = _cfsml_get_identifier(fh, line, hiteof, &assignment);
+
+ if (!token) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!assignment) {
+ if (!strcmp(token, "}"))
+ closed = 1;
+ else {
+ _cfsml_error("Expected assignment or closing braces in line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else {
+ value = "";
+ while (!value || !strcmp(value, ""))
+ value = _cfsml_get_value(fh, line, hiteof);
+ if (!value) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!strcmp(token, "flags")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->flags), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for flags at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "pos")) {
+ if (read_reg_t(fh, (reg_t*) &(save_struc->pos), value, line, hiteof)) {
+ _cfsml_error("Token expected by read_reg_t() for pos at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "variables_nr")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->variables_nr), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for variables_nr at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "variable_names_nr")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->variable_names_nr), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for variable_names_nr at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "methods_nr")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->methods_nr), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for methods_nr at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "variables")) {
+ if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) {
+ _cfsml_error("Opening brackets expected at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ /* Prepare to restore dynamic array */
+ max = strtol(value + 1, NULL, 0);
+ if (max < 0) {
+ _cfsml_error("Invalid number of elements to allocate for dynamic array '%s' at line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+
+ if (max) {
+ save_struc->variables = (reg_t *) sci_malloc(max * sizeof(reg_t));
+#ifdef SATISFY_PURIFY
+ memset(save_struc->variables, 0, max * sizeof(reg_t));
+#endif
+ _cfsml_register_pointer(save_struc->variables);
+ }
+ else
+ save_struc->variables = NULL;
+ done = i = 0;
+ do {
+ if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) {
+ _cfsml_error("Token expected at line %d\n", *line);
+ return 1;
+ }
+ if (strcmp(value, "]")) {
+ if (i == max) {
+ _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line);
+ return CFSML_FAILURE;
+ }
+ if (read_reg_t(fh, &(save_struc->variables[i++]), value, line, hiteof)) {
+ _cfsml_error("Token expected by read_reg_t() for variables[i++] at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else done = 1;
+ } while (!done);
+ save_struc->variables_nr = max ; /* Set array size accordingly */
+ } else
+ {
+ _cfsml_error("clone_t: Assignment to invalid identifier '%s' in line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+ }
+ } while (!closed); /* Until closing braces are hit */
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_list_t(FILE *fh, list_t* save_struc)
+{
+ int min, max, i;
+
+ fprintf(fh, "{\n");
+ fprintf(fh, "first = ");
+ write_reg_t(fh, (reg_t*) &(save_struc->first));
+ fprintf(fh, "\n");
+ fprintf(fh, "last = ");
+ write_reg_t(fh, (reg_t*) &(save_struc->last));
+ fprintf(fh, "\n");
+ fprintf(fh, "}");
+}
+
+static int
+_cfsml_read_list_t(FILE *fh, list_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+int min, max, i;
+ int assignment, closed, done;
+
+ if (strcmp(lastval, "{")) {
+ _cfsml_error("Reading record list_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval);
+ return CFSML_FAILURE;
+ };
+ closed = 0;
+ do {
+ char *value;
+ token = _cfsml_get_identifier(fh, line, hiteof, &assignment);
+
+ if (!token) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!assignment) {
+ if (!strcmp(token, "}"))
+ closed = 1;
+ else {
+ _cfsml_error("Expected assignment or closing braces in line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else {
+ value = "";
+ while (!value || !strcmp(value, ""))
+ value = _cfsml_get_value(fh, line, hiteof);
+ if (!value) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!strcmp(token, "first")) {
+ if (read_reg_t(fh, (reg_t*) &(save_struc->first), value, line, hiteof)) {
+ _cfsml_error("Token expected by read_reg_t() for first at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "last")) {
+ if (read_reg_t(fh, (reg_t*) &(save_struc->last), value, line, hiteof)) {
+ _cfsml_error("Token expected by read_reg_t() for last at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ {
+ _cfsml_error("list_t: Assignment to invalid identifier '%s' in line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+ }
+ } while (!closed); /* Until closing braces are hit */
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_sys_string_t(FILE *fh, sys_string_t* save_struc)
+{
+ int min, max, i;
+
+ fprintf(fh, "{\n");
+ fprintf(fh, "name = ");
+ _cfsml_write_string(fh, (char **) &(save_struc->name));
+ fprintf(fh, "\n");
+ fprintf(fh, "max_size = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->max_size));
+ fprintf(fh, "\n");
+ fprintf(fh, "value = ");
+ _cfsml_write_string(fh, (char **) &(save_struc->value));
+ fprintf(fh, "\n");
+ fprintf(fh, "}");
+}
+
+static int
+_cfsml_read_sys_string_t(FILE *fh, sys_string_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+int min, max, i;
+ int assignment, closed, done;
+
+ if (strcmp(lastval, "{")) {
+ _cfsml_error("Reading record sys_string_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval);
+ return CFSML_FAILURE;
+ };
+ closed = 0;
+ do {
+ char *value;
+ token = _cfsml_get_identifier(fh, line, hiteof, &assignment);
+
+ if (!token) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!assignment) {
+ if (!strcmp(token, "}"))
+ closed = 1;
+ else {
+ _cfsml_error("Expected assignment or closing braces in line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else {
+ value = "";
+ while (!value || !strcmp(value, ""))
+ value = _cfsml_get_value(fh, line, hiteof);
+ if (!value) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!strcmp(token, "name")) {
+ if (_cfsml_read_string(fh, (char **) &(save_struc->name), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_string() for name at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "max_size")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->max_size), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for max_size at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "value")) {
+ if (_cfsml_read_string(fh, (char **) &(save_struc->value), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_string() for value at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ {
+ _cfsml_error("sys_string_t: Assignment to invalid identifier '%s' in line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+ }
+ } while (!closed); /* Until closing braces are hit */
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_script_t(FILE *fh, script_t* save_struc)
+{
+ int min, max, i;
+
+ fprintf(fh, "{\n");
+ fprintf(fh, "nr = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->nr));
+ fprintf(fh, "\n");
+ fprintf(fh, "buf_size = ");
+ _cfsml_write_size_t(fh, (size_t*) &(save_struc->buf_size));
+ fprintf(fh, "\n");
+ fprintf(fh, "script_size = ");
+ _cfsml_write_size_t(fh, (size_t*) &(save_struc->script_size));
+ fprintf(fh, "\n");
+ fprintf(fh, "heap_size = ");
+ _cfsml_write_size_t(fh, (size_t*) &(save_struc->heap_size));
+ fprintf(fh, "\n");
+ fprintf(fh, "obj_indices = ");
+ write_int_hash_map_tp(fh, (int_hash_map_t **) &(save_struc->obj_indices));
+ fprintf(fh, "\n");
+ fprintf(fh, "exports_nr = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->exports_nr));
+ fprintf(fh, "\n");
+ fprintf(fh, "synonyms_nr = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->synonyms_nr));
+ fprintf(fh, "\n");
+ fprintf(fh, "lockers = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->lockers));
+ fprintf(fh, "\n");
+ fprintf(fh, "objects_allocated = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->objects_allocated));
+ fprintf(fh, "\n");
+ fprintf(fh, "objects_nr = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->objects_nr));
+ fprintf(fh, "\n");
+ fprintf(fh, "objects = ");
+ min = max = save_struc->objects_allocated;
+ if (!save_struc->objects)
+ min = max = 0; /* Don't write if it points to NULL */
+ fprintf(fh, "[%d][\n", max);
+ for (i = 0; i < min; i++) {
+ _cfsml_write_object_t(fh, &(save_struc->objects[i]));
+ fprintf(fh, "\n");
+ }
+ fprintf(fh, "]");
+ fprintf(fh, "\n");
+ fprintf(fh, "locals_offset = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->locals_offset));
+ fprintf(fh, "\n");
+ fprintf(fh, "locals_segment = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->locals_segment));
+ fprintf(fh, "\n");
+ fprintf(fh, "marked_as_deleted = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->marked_as_deleted));
+ fprintf(fh, "\n");
+ fprintf(fh, "}");
+}
+
+static int
+_cfsml_read_script_t(FILE *fh, script_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+int min, max, i;
+ int assignment, closed, done;
+
+ if (strcmp(lastval, "{")) {
+ _cfsml_error("Reading record script_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval);
+ return CFSML_FAILURE;
+ };
+ closed = 0;
+ do {
+ char *value;
+ token = _cfsml_get_identifier(fh, line, hiteof, &assignment);
+
+ if (!token) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!assignment) {
+ if (!strcmp(token, "}"))
+ closed = 1;
+ else {
+ _cfsml_error("Expected assignment or closing braces in line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else {
+ value = "";
+ while (!value || !strcmp(value, ""))
+ value = _cfsml_get_value(fh, line, hiteof);
+ if (!value) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!strcmp(token, "nr")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->nr), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for nr at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "buf_size")) {
+ if (_cfsml_read_size_t(fh, (size_t*) &(save_struc->buf_size), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_size_t() for buf_size at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "script_size")) {
+ if (_cfsml_read_size_t(fh, (size_t*) &(save_struc->script_size), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_size_t() for script_size at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "heap_size")) {
+ if (_cfsml_read_size_t(fh, (size_t*) &(save_struc->heap_size), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_size_t() for heap_size at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "obj_indices")) {
+ if (read_int_hash_map_tp(fh, (int_hash_map_t **) &(save_struc->obj_indices), value, line, hiteof)) {
+ _cfsml_error("Token expected by read_int_hash_map_tp() for obj_indices at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "exports_nr")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->exports_nr), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for exports_nr at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "synonyms_nr")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->synonyms_nr), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for synonyms_nr at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "lockers")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->lockers), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for lockers at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "objects_allocated")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->objects_allocated), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for objects_allocated at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "objects_nr")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->objects_nr), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for objects_nr at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "objects")) {
+ if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) {
+ _cfsml_error("Opening brackets expected at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ /* Prepare to restore dynamic array */
+ max = strtol(value + 1, NULL, 0);
+ if (max < 0) {
+ _cfsml_error("Invalid number of elements to allocate for dynamic array '%s' at line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+
+ if (max) {
+ save_struc->objects = (object_t *) sci_malloc(max * sizeof(object_t));
+#ifdef SATISFY_PURIFY
+ memset(save_struc->objects, 0, max * sizeof(object_t));
+#endif
+ _cfsml_register_pointer(save_struc->objects);
+ }
+ else
+ save_struc->objects = NULL;
+ done = i = 0;
+ do {
+ if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) {
+ _cfsml_error("Token expected at line %d\n", *line);
+ return 1;
+ }
+ if (strcmp(value, "]")) {
+ if (i == max) {
+ _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line);
+ return CFSML_FAILURE;
+ }
+ if (_cfsml_read_object_t(fh, &(save_struc->objects[i++]), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_object_t() for objects[i++] at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else done = 1;
+ } while (!done);
+ save_struc->objects_allocated = max ; /* Set array size accordingly */
+ } else
+ if (!strcmp(token, "locals_offset")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->locals_offset), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for locals_offset at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "locals_segment")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->locals_segment), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for locals_segment at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "marked_as_deleted")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->marked_as_deleted), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for marked_as_deleted at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ {
+ _cfsml_error("script_t: Assignment to invalid identifier '%s' in line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+ }
+ } while (!closed); /* Until closing braces are hit */
+ return CFSML_SUCCESS;
+}
+
+static void
+_cfsml_write_seg_manager_t(FILE *fh, seg_manager_t* save_struc)
+{
+ int min, max, i;
+
+ fprintf(fh, "{\n");
+ fprintf(fh, "id_seg_map = ");
+ write_int_hash_map_tp(fh, (int_hash_map_t **) &(save_struc->id_seg_map));
+ fprintf(fh, "\n");
+ fprintf(fh, "heap = ");
+ min = max = save_struc->heap_size;
+ if (!save_struc->heap)
+ min = max = 0; /* Don't write if it points to NULL */
+ fprintf(fh, "[%d][\n", max);
+ for (i = 0; i < min; i++) {
+ write_mem_obj_tp(fh, &(save_struc->heap[i]));
+ fprintf(fh, "\n");
+ }
+ fprintf(fh, "]");
+ fprintf(fh, "\n");
+ fprintf(fh, "heap_size = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->heap_size));
+ fprintf(fh, "\n");
+ fprintf(fh, "reserved_id = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->reserved_id));
+ fprintf(fh, "\n");
+ fprintf(fh, "exports_wide = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->exports_wide));
+ fprintf(fh, "\n");
+ fprintf(fh, "sci1_1 = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->sci1_1));
+ fprintf(fh, "\n");
+ fprintf(fh, "gc_mark_bits = ");
+ _cfsml_write_int(fh, (int*) &(save_struc->gc_mark_bits));
+ fprintf(fh, "\n");
+ fprintf(fh, "mem_allocated = ");
+ _cfsml_write_size_t(fh, (size_t*) &(save_struc->mem_allocated));
+ fprintf(fh, "\n");
+ fprintf(fh, "clones_seg_id = ");
+ _cfsml_write_seg_id_t(fh, (seg_id_t*) &(save_struc->clones_seg_id));
+ fprintf(fh, "\n");
+ fprintf(fh, "lists_seg_id = ");
+ _cfsml_write_seg_id_t(fh, (seg_id_t*) &(save_struc->lists_seg_id));
+ fprintf(fh, "\n");
+ fprintf(fh, "nodes_seg_id = ");
+ _cfsml_write_seg_id_t(fh, (seg_id_t*) &(save_struc->nodes_seg_id));
+ fprintf(fh, "\n");
+ fprintf(fh, "}");
+}
+
+static int
+_cfsml_read_seg_manager_t(FILE *fh, seg_manager_t* save_struc, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+int min, max, i;
+ int assignment, closed, done;
+
+ if (strcmp(lastval, "{")) {
+ _cfsml_error("Reading record seg_manager_t; expected opening braces in line %d, got \"%s\"\n",*line, lastval);
+ return CFSML_FAILURE;
+ };
+ closed = 0;
+ do {
+ char *value;
+ token = _cfsml_get_identifier(fh, line, hiteof, &assignment);
+
+ if (!token) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!assignment) {
+ if (!strcmp(token, "}"))
+ closed = 1;
+ else {
+ _cfsml_error("Expected assignment or closing braces in line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else {
+ value = "";
+ while (!value || !strcmp(value, ""))
+ value = _cfsml_get_value(fh, line, hiteof);
+ if (!value) {
+ _cfsml_error("Expected token at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ if (!strcmp(token, "id_seg_map")) {
+ if (read_int_hash_map_tp(fh, (int_hash_map_t **) &(save_struc->id_seg_map), value, line, hiteof)) {
+ _cfsml_error("Token expected by read_int_hash_map_tp() for id_seg_map at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "heap")) {
+ if ((value[0] != '[') || (value[strlen(value) - 1] != '[')) {
+ _cfsml_error("Opening brackets expected at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ /* Prepare to restore dynamic array */
+ max = strtol(value + 1, NULL, 0);
+ if (max < 0) {
+ _cfsml_error("Invalid number of elements to allocate for dynamic array '%s' at line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+
+ if (max) {
+ save_struc->heap = (mem_obj_ptr *) sci_malloc(max * sizeof(mem_obj_ptr));
+#ifdef SATISFY_PURIFY
+ memset(save_struc->heap, 0, max * sizeof(mem_obj_ptr));
+#endif
+ _cfsml_register_pointer(save_struc->heap);
+ }
+ else
+ save_struc->heap = NULL;
+ done = i = 0;
+ do {
+ if (!(value = _cfsml_get_identifier(fh, line, hiteof, NULL))) {
+ _cfsml_error("Token expected at line %d\n", *line);
+ return 1;
+ }
+ if (strcmp(value, "]")) {
+ if (i == max) {
+ _cfsml_error("More elements than space available (%d) in '%s' at line %d\n", max, token, *line);
+ return CFSML_FAILURE;
+ }
+ if (read_mem_obj_tp(fh, &(save_struc->heap[i++]), value, line, hiteof)) {
+ _cfsml_error("Token expected by read_mem_obj_tp() for heap[i++] at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else done = 1;
+ } while (!done);
+ save_struc->heap_size = max ; /* Set array size accordingly */
+ } else
+ if (!strcmp(token, "heap_size")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->heap_size), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for heap_size at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "reserved_id")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->reserved_id), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for reserved_id at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "exports_wide")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->exports_wide), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for exports_wide at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "sci1_1")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->sci1_1), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for sci1_1 at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "gc_mark_bits")) {
+ if (_cfsml_read_int(fh, (int*) &(save_struc->gc_mark_bits), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_int() for gc_mark_bits at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "mem_allocated")) {
+ if (_cfsml_read_size_t(fh, (size_t*) &(save_struc->mem_allocated), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_size_t() for mem_allocated at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "clones_seg_id")) {
+ if (_cfsml_read_seg_id_t(fh, (seg_id_t*) &(save_struc->clones_seg_id), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_seg_id_t() for clones_seg_id at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "lists_seg_id")) {
+ if (_cfsml_read_seg_id_t(fh, (seg_id_t*) &(save_struc->lists_seg_id), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_seg_id_t() for lists_seg_id at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ if (!strcmp(token, "nodes_seg_id")) {
+ if (_cfsml_read_seg_id_t(fh, (seg_id_t*) &(save_struc->nodes_seg_id), value, line, hiteof)) {
+ _cfsml_error("Token expected by _cfsml_read_seg_id_t() for nodes_seg_id at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ } else
+ {
+ _cfsml_error("seg_manager_t: Assignment to invalid identifier '%s' in line %d\n", token, *line);
+ return CFSML_FAILURE;
+ }
+ }
+ } while (!closed); /* Until closing braces are hit */
+ return CFSML_SUCCESS;
+}
+
+
+/* Auto-generated CFSML declaration and function block ends here */
+/* Auto-generation performed by cfsml.pl 0.8.2 */
+
+void
+write_songlib_t(FILE *fh, songlib_t *songlib)
+{
+ song_t *seeker = *(songlib->lib);
+ int songcount = song_lib_count(*songlib);
+
+ fprintf(fh, "{\n");
+ fprintf(fh, "songcount = %d\n", songcount);
+ fprintf(fh, "list = \n");
+ fprintf(fh, "[\n");
+ while (seeker)
+ {
+ seeker->restore_time = seeker->it->get_timepos(seeker->it);
+/* Auto-generated CFSML data writer code */
+ _cfsml_write_song_t(fh, seeker);
+ fprintf(fh, "\n");
+/* End of auto-generated CFSML data writer code */
+ seeker = seeker->next;
+ }
+ fprintf(fh, "]\n");
+ fprintf(fh, "}\n");
+}
+
+int
+read_songlib_t(FILE *fh, songlib_t *songlib, char *lastval, int *line, int *hiteof)
+{
+ int songcount;
+ int i;
+ song_t *newsong;
+ int oldstatus;
+
+ fscanf(fh, "{\n");
+ fscanf(fh, "songcount = %d\n", &songcount);
+ fscanf(fh, "list = \n");
+ fscanf(fh, "[\n");
+ *line += 4;
+ song_lib_init(songlib);
+ for (i = 0; i < songcount; i++)
+ {
+/* Auto-generated CFSML data reader code */
+ {
+ int _cfsml_eof = 0, _cfsml_error;
+ int dummy;
+ char *_cfsml_inp = lastval;
+ _cfsml_error = read_song_tp(fh, &newsong, _cfsml_inp, &(*line), &_cfsml_eof);
+ *hiteof = _cfsml_error;
+ if (_cfsml_last_value_retreived) {
+ free(_cfsml_last_value_retreived);
+ _cfsml_last_value_retreived = NULL;
+ }
+ if (_cfsml_last_identifier_retreived) {
+ free(_cfsml_last_identifier_retreived);
+ _cfsml_last_identifier_retreived = NULL;
+ }
+ }
+/* End of auto-generated CFSML data reader code */
+ song_lib_add(*songlib, newsong);
+ }
+ fscanf(fh, "]\n");
+ fscanf(fh, "}\n");;
+ *line += 2;
+ return 0;
+}
+
+struct {
+ int type;
+ char *name;
+} mem_obj_string_names[] = {
+ {MEM_OBJ_INVALID, "INVALID"},
+ {MEM_OBJ_SCRIPT, "SCRIPT"},
+ {MEM_OBJ_CLONES, "CLONES"},
+ {MEM_OBJ_LOCALS, "LOCALS"},
+ {MEM_OBJ_STACK, "STACK"},
+ {MEM_OBJ_SYS_STRINGS,"SYS_STRINGS"},
+ {MEM_OBJ_LISTS,"LISTS"},
+ {MEM_OBJ_NODES,"NODES"},
+ {MEM_OBJ_HUNK,"HUNK"},
+ {MEM_OBJ_DYNMEM,"DYNMEM"}};
+
+int
+mem_obj_string_to_enum(char *str)
+{
+ int i;
+
+ for (i = 0; i <= MEM_OBJ_MAX; i++)
+ { if (!strcasecmp(mem_obj_string_names[i].name, str))
+ return i;
+ }
+
+ return -1;
+}
+
+static int bucket_length;
+
+void
+write_int_hash_map_tp(FILE *fh, int_hash_map_t **foo)
+{
+/* Auto-generated CFSML data writer code */
+ _cfsml_write_int_hash_map_t(fh, *foo);
+ fprintf(fh, "\n");
+/* End of auto-generated CFSML data writer code */
+}
+
+void
+write_song_tp(FILE *fh, song_t **foo)
+{
+/* Auto-generated CFSML data writer code */
+ _cfsml_write_song_t(fh, *foo);
+ fprintf(fh, "\n");
+/* End of auto-generated CFSML data writer code */
+}
+
+song_iterator_t *
+build_iterator(state_t *s, int song_nr, int type, songit_id_t id);
+
+int
+read_song_tp(FILE *fh, song_t **foo, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+ int assignment;
+ *foo = (song_t*) malloc(sizeof(song_t));
+ token = _cfsml_get_identifier(fh, line, hiteof, &assignment);
+/* Auto-generated CFSML data reader code */
+ {
+ int _cfsml_eof = 0, _cfsml_error;
+ int dummy;
+ char *_cfsml_inp = token;
+ _cfsml_error = _cfsml_read_song_t(fh, (*foo), _cfsml_inp, &(*line), &_cfsml_eof);
+ *hiteof = _cfsml_error;
+ if (_cfsml_last_value_retreived) {
+ free(_cfsml_last_value_retreived);
+ _cfsml_last_value_retreived = NULL;
+ }
+ if (_cfsml_last_identifier_retreived) {
+ free(_cfsml_last_identifier_retreived);
+ _cfsml_last_identifier_retreived = NULL;
+ }
+ }
+/* End of auto-generated CFSML data reader code */
+ (*foo)->delay = 0;
+ (*foo)->it = NULL;
+ (*foo)->next_playing = (*foo)->next_stopping = (*foo)->next = NULL;
+ return 0;
+}
+int
+read_int_hash_map_tp(FILE *fh, int_hash_map_t **foo, char *lastval, int *line, int *hiteof)
+{
+ *foo = (int_hash_map_t*)malloc(sizeof(int_hash_map_t));
+/* Auto-generated CFSML data reader code */
+ {
+ int _cfsml_eof = 0, _cfsml_error;
+ int dummy;
+ char *_cfsml_inp = lastval;
+ _cfsml_error = _cfsml_read_int_hash_map_t(fh, (*foo), _cfsml_inp, &(*line), &_cfsml_eof);
+ *hiteof = _cfsml_error;
+ if (_cfsml_last_value_retreived) {
+ free(_cfsml_last_value_retreived);
+ _cfsml_last_value_retreived = NULL;
+ }
+ if (_cfsml_last_identifier_retreived) {
+ free(_cfsml_last_identifier_retreived);
+ _cfsml_last_identifier_retreived = NULL;
+ }
+ }
+/* End of auto-generated CFSML data reader code */
+ (*foo)->holes = NULL;
+ return 0;
+}
+
+void
+write_int_hash_map_node_tp(FILE *fh, int_hash_map_node_t **foo)
+{
+ if (!(*foo))
+ {
+ fputs("\\null", fh);
+ } else
+ {
+ fprintf(fh,"[\n%d=>%d\n", (*foo)->name, (*foo)->value);
+ if ((*foo)->next)
+ {
+/* Auto-generated CFSML data writer code */
+ write_int_hash_map_node_tp(fh, &((*foo)->next));
+ fprintf(fh, "\n");
+/* End of auto-generated CFSML data writer code */
+ } else fputc('L', fh);
+ fputs("]", fh);
+ }
+}
+
+int
+read_int_hash_map_node_tp(FILE *fh, int_hash_map_node_t **foo, char *lastval, int *line, int *hiteof)
+{
+ static char buffer[80];
+
+ if (lastval[0] == '\\') {
+ *foo = NULL; /* No hash map node */
+ } else {
+ *foo = (int_hash_map_node_t*)malloc(sizeof(int_hash_map_node_t));
+ if (lastval[0] != '[')
+ {
+ sciprintf("Expected opening bracket in hash_map_node_t on line %d\n", *line);
+ return 1;
+ }
+
+ do {
+ (*line)++;
+ fgets(buffer, 80, fh);
+ if (buffer[0] == 'L')
+ {
+ (*foo)->next = NULL;
+ buffer[0] = buffer[1];
+ } /* HACK: deliberately no else clause here */
+ if (buffer[0] == ']')
+ {
+ break;
+ }
+ else if (buffer[0] == '[')
+ {
+ if (read_int_hash_map_node_tp(fh, &((*foo)->next), buffer, line, hiteof))
+ return 1;
+ }
+ else if (sscanf(buffer, "%d=>%d", &((*foo)->name), &((*foo)->value))<2)
+ {
+ sciprintf("Error parsing hash_map_node_t on line %d\n", *line);
+ return 1;
+ }
+ } while (1);
+ }
+
+ return 0;
+}
+
+void
+write_menubar_tp(FILE *fh, menubar_t **foo)
+{
+ if (*foo) {
+
+/* Auto-generated CFSML data writer code */
+ _cfsml_write_menubar_t(fh, (*foo));
+ fprintf(fh, "\n");
+/* End of auto-generated CFSML data writer code */
+
+ } else { /* Nothing to write */
+ fputs("\\null\\", fh);
+ }
+}
+
+
+int
+read_menubar_tp(FILE *fh, menubar_t **foo, char *lastval, int *line, int *hiteof)
+{
+
+ if (lastval[0] == '\\') {
+ *foo = NULL; /* No menu bar */
+ } else {
+
+ *foo = (menubar_t *) sci_malloc(sizeof(menubar_t));
+/* Auto-generated CFSML data reader code */
+ {
+ int _cfsml_eof = 0, _cfsml_error;
+ int dummy;
+ char *_cfsml_inp = lastval;
+ _cfsml_error = _cfsml_read_menubar_t(fh, (*foo), _cfsml_inp, &(*line), &_cfsml_eof);
+ *hiteof = _cfsml_error;
+ if (_cfsml_last_value_retreived) {
+ free(_cfsml_last_value_retreived);
+ _cfsml_last_value_retreived = NULL;
+ }
+ if (_cfsml_last_identifier_retreived) {
+ free(_cfsml_last_identifier_retreived);
+ _cfsml_last_identifier_retreived = NULL;
+ }
+ }
+/* End of auto-generated CFSML data reader code */
+
+ }
+ return *hiteof;
+}
+
+void
+write_mem_obj_t(FILE *fh, mem_obj_t *foo)
+{
+ fprintf(fh, "%s\n", mem_obj_string_names[foo->type].name);
+/* Auto-generated CFSML data writer code */
+ _cfsml_write_int(fh, &foo->segmgr_id);
+ fprintf(fh, "\n");
+/* End of auto-generated CFSML data writer code */
+ switch (foo->type)
+ {
+ case MEM_OBJ_SCRIPT:
+/* Auto-generated CFSML data writer code */
+ _cfsml_write_script_t(fh, &foo->data.script);
+ fprintf(fh, "\n");
+/* End of auto-generated CFSML data writer code */
+ break;
+ case MEM_OBJ_CLONES:
+/* Auto-generated CFSML data writer code */
+ _cfsml_write_clone_table_t(fh, &foo->data.clones);
+ fprintf(fh, "\n");
+/* End of auto-generated CFSML data writer code */
+ break;
+ case MEM_OBJ_LOCALS:
+/* Auto-generated CFSML data writer code */
+ _cfsml_write_local_variables_t(fh, &foo->data.locals);
+ fprintf(fh, "\n");
+/* End of auto-generated CFSML data writer code */
+ break;
+ case MEM_OBJ_SYS_STRINGS:
+/* Auto-generated CFSML data writer code */
+ _cfsml_write_sys_strings_t(fh, &foo->data.sys_strings);
+ fprintf(fh, "\n");
+/* End of auto-generated CFSML data writer code */
+ break;
+ case MEM_OBJ_STACK:
+/* Auto-generated CFSML data writer code */
+ _cfsml_write_int(fh, &foo->data.stack.nr);
+ fprintf(fh, "\n");
+/* End of auto-generated CFSML data writer code */
+ break;
+ case MEM_OBJ_HUNK:
+ break;
+ case MEM_OBJ_LISTS:
+/* Auto-generated CFSML data writer code */
+ _cfsml_write_list_table_t(fh, &foo->data.lists);
+ fprintf(fh, "\n");
+/* End of auto-generated CFSML data writer code */
+ break;
+ case MEM_OBJ_NODES:
+/* Auto-generated CFSML data writer code */
+ _cfsml_write_node_table_t(fh, &foo->data.nodes);
+ fprintf(fh, "\n");
+/* End of auto-generated CFSML data writer code */
+ break;
+ case MEM_OBJ_DYNMEM:
+/* Auto-generated CFSML data writer code */
+ _cfsml_write_dynmem_t(fh, &foo->data.dynmem);
+ fprintf(fh, "\n");
+/* End of auto-generated CFSML data writer code */
+ break;
+ }
+}
+
+int
+read_mem_obj_t(FILE *fh, mem_obj_t *foo, char *lastval, int *line, int *hiteof)
+{
+ char buffer[80];
+ foo->type = mem_obj_string_to_enum(lastval);
+ if (foo->type < 0)
+ {
+ sciprintf("Unknown mem_obj_t type %s on line %d\n", lastval, *line);
+ return 1;
+ }
+
+/* Auto-generated CFSML data reader code */
+ {
+ int _cfsml_eof = 0, _cfsml_error;
+ int dummy;
+ char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy);
+
+ _cfsml_error = _cfsml_read_int(fh, &foo->segmgr_id, _cfsml_inp, &(*line), &_cfsml_eof);
+ *hiteof = _cfsml_error;
+ if (_cfsml_last_value_retreived) {
+ free(_cfsml_last_value_retreived);
+ _cfsml_last_value_retreived = NULL;
+ }
+ if (_cfsml_last_identifier_retreived) {
+ free(_cfsml_last_identifier_retreived);
+ _cfsml_last_identifier_retreived = NULL;
+ }
+ }
+/* End of auto-generated CFSML data reader code */
+ switch (foo->type)
+ {
+ case MEM_OBJ_SCRIPT:
+/* Auto-generated CFSML data reader code */
+ {
+ int _cfsml_eof = 0, _cfsml_error;
+ int dummy;
+ char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy);
+
+ _cfsml_error = _cfsml_read_script_t(fh, &foo->data.script, _cfsml_inp, &(*line), &_cfsml_eof);
+ *hiteof = _cfsml_error;
+ if (_cfsml_last_value_retreived) {
+ free(_cfsml_last_value_retreived);
+ _cfsml_last_value_retreived = NULL;
+ }
+ if (_cfsml_last_identifier_retreived) {
+ free(_cfsml_last_identifier_retreived);
+ _cfsml_last_identifier_retreived = NULL;
+ }
+ }
+/* End of auto-generated CFSML data reader code */
+ break;
+ case MEM_OBJ_CLONES:
+/* Auto-generated CFSML data reader code */
+ {
+ int _cfsml_eof = 0, _cfsml_error;
+ int dummy;
+ char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy);
+
+ _cfsml_error = _cfsml_read_clone_table_t(fh, &foo->data.clones, _cfsml_inp, &(*line), &_cfsml_eof);
+ *hiteof = _cfsml_error;
+ if (_cfsml_last_value_retreived) {
+ free(_cfsml_last_value_retreived);
+ _cfsml_last_value_retreived = NULL;
+ }
+ if (_cfsml_last_identifier_retreived) {
+ free(_cfsml_last_identifier_retreived);
+ _cfsml_last_identifier_retreived = NULL;
+ }
+ }
+/* End of auto-generated CFSML data reader code */
+ break;
+ case MEM_OBJ_LOCALS:
+/* Auto-generated CFSML data reader code */
+ {
+ int _cfsml_eof = 0, _cfsml_error;
+ int dummy;
+ char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy);
+
+ _cfsml_error = _cfsml_read_local_variables_t(fh, &foo->data.locals, _cfsml_inp, &(*line), &_cfsml_eof);
+ *hiteof = _cfsml_error;
+ if (_cfsml_last_value_retreived) {
+ free(_cfsml_last_value_retreived);
+ _cfsml_last_value_retreived = NULL;
+ }
+ if (_cfsml_last_identifier_retreived) {
+ free(_cfsml_last_identifier_retreived);
+ _cfsml_last_identifier_retreived = NULL;
+ }
+ }
+/* End of auto-generated CFSML data reader code */
+ break;
+ case MEM_OBJ_SYS_STRINGS:
+/* Auto-generated CFSML data reader code */
+ {
+ int _cfsml_eof = 0, _cfsml_error;
+ int dummy;
+ char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy);
+
+ _cfsml_error = _cfsml_read_sys_strings_t(fh, &foo->data.sys_strings, _cfsml_inp, &(*line), &_cfsml_eof);
+ *hiteof = _cfsml_error;
+ if (_cfsml_last_value_retreived) {
+ free(_cfsml_last_value_retreived);
+ _cfsml_last_value_retreived = NULL;
+ }
+ if (_cfsml_last_identifier_retreived) {
+ free(_cfsml_last_identifier_retreived);
+ _cfsml_last_identifier_retreived = NULL;
+ }
+ }
+/* End of auto-generated CFSML data reader code */
+ break;
+ case MEM_OBJ_LISTS:
+/* Auto-generated CFSML data reader code */
+ {
+ int _cfsml_eof = 0, _cfsml_error;
+ int dummy;
+ char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy);
+
+ _cfsml_error = _cfsml_read_list_table_t(fh, &foo->data.lists, _cfsml_inp, &(*line), &_cfsml_eof);
+ *hiteof = _cfsml_error;
+ if (_cfsml_last_value_retreived) {
+ free(_cfsml_last_value_retreived);
+ _cfsml_last_value_retreived = NULL;
+ }
+ if (_cfsml_last_identifier_retreived) {
+ free(_cfsml_last_identifier_retreived);
+ _cfsml_last_identifier_retreived = NULL;
+ }
+ }
+/* End of auto-generated CFSML data reader code */
+ break;
+ case MEM_OBJ_NODES:
+/* Auto-generated CFSML data reader code */
+ {
+ int _cfsml_eof = 0, _cfsml_error;
+ int dummy;
+ char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy);
+
+ _cfsml_error = _cfsml_read_node_table_t(fh, &foo->data.nodes, _cfsml_inp, &(*line), &_cfsml_eof);
+ *hiteof = _cfsml_error;
+ if (_cfsml_last_value_retreived) {
+ free(_cfsml_last_value_retreived);
+ _cfsml_last_value_retreived = NULL;
+ }
+ if (_cfsml_last_identifier_retreived) {
+ free(_cfsml_last_identifier_retreived);
+ _cfsml_last_identifier_retreived = NULL;
+ }
+ }
+/* End of auto-generated CFSML data reader code */
+ break;
+ case MEM_OBJ_STACK:
+/* Auto-generated CFSML data reader code */
+ {
+ int _cfsml_eof = 0, _cfsml_error;
+ int dummy;
+ char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy);
+
+ _cfsml_error = _cfsml_read_int(fh, &foo->data.stack.nr, _cfsml_inp, &(*line), &_cfsml_eof);
+ *hiteof = _cfsml_error;
+ if (_cfsml_last_value_retreived) {
+ free(_cfsml_last_value_retreived);
+ _cfsml_last_value_retreived = NULL;
+ }
+ if (_cfsml_last_identifier_retreived) {
+ free(_cfsml_last_identifier_retreived);
+ _cfsml_last_identifier_retreived = NULL;
+ }
+ }
+/* End of auto-generated CFSML data reader code */
+ foo->data.stack.entries = (reg_t *)sci_calloc(foo->data.stack.nr, sizeof(reg_t));
+ break;
+ case MEM_OBJ_HUNK:
+ init_hunk_table(&foo->data.hunks);
+ break;
+ case MEM_OBJ_DYNMEM:
+/* Auto-generated CFSML data reader code */
+ {
+ int _cfsml_eof = 0, _cfsml_error;
+ int dummy;
+ char *_cfsml_inp = _cfsml_get_identifier(fh, &(*line), &_cfsml_eof, &dummy);
+
+ _cfsml_error = _cfsml_read_dynmem_t(fh, &foo->data.dynmem, _cfsml_inp, &(*line), &_cfsml_eof);
+ *hiteof = _cfsml_error;
+ if (_cfsml_last_value_retreived) {
+ free(_cfsml_last_value_retreived);
+ _cfsml_last_value_retreived = NULL;
+ }
+ if (_cfsml_last_identifier_retreived) {
+ free(_cfsml_last_identifier_retreived);
+ _cfsml_last_identifier_retreived = NULL;
+ }
+ }
+/* End of auto-generated CFSML data reader code */
+ break;
+ }
+
+ return *hiteof;
+}
+
+void
+write_mem_obj_tp(FILE *fh, mem_obj_t **foo)
+{
+ if (*foo) {
+
+/* Auto-generated CFSML data writer code */
+ write_mem_obj_t(fh, (*foo));
+ fprintf(fh, "\n");
+/* End of auto-generated CFSML data writer code */
+
+ } else { /* Nothing to write */
+ fputs("\\null\\", fh);
+ }
+}
+
+int
+read_mem_obj_tp(FILE *fh, mem_obj_t **foo, char *lastval, int *line, int *hiteof)
+{
+
+ if (lastval[0] == '\\') {
+ *foo = NULL; /* No menu bar */
+ } else {
+ *foo = (mem_obj_t *) sci_malloc(sizeof(mem_obj_t));
+/* Auto-generated CFSML data reader code */
+ {
+ int _cfsml_eof = 0, _cfsml_error;
+ int dummy;
+ char *_cfsml_inp = lastval;
+ _cfsml_error = read_mem_obj_t(fh, (*foo), _cfsml_inp, &(*line), &_cfsml_eof);
+ *hiteof = _cfsml_error;
+ if (_cfsml_last_value_retreived) {
+ free(_cfsml_last_value_retreived);
+ _cfsml_last_value_retreived = NULL;
+ }
+ if (_cfsml_last_identifier_retreived) {
+ free(_cfsml_last_identifier_retreived);
+ _cfsml_last_identifier_retreived = NULL;
+ }
+ }
+/* End of auto-generated CFSML data reader code */
+ return *hiteof;
+ }
+ return 0;
+}
+
+
+
+/* This function is called to undo some strange stuff done in preparation
+** to writing a gamestate to disk
+*/
+void
+_gamestate_unfrob(state_t *s)
+{
+}
+
+
+int
+gamestate_save(state_t *s, char *dirname)
+{
+ FILE *fh;
+ sci_dir_t dir;
+ char *filename;
+ int fd;
+
+ _global_save_state = s;
+ s->savegame_version = FREESCI_CURRENT_SAVEGAME_VERSION;
+ s->dyn_views_list_serial = (s->dyn_views)? s->dyn_views->serial : -2;
+ s->drop_views_list_serial = (s->drop_views)? s->drop_views->serial : -2;
+ s->port_serial = (s->port)? s->port->serial : -2;
+
+ if (s->execution_stack_base) {
+ sciprintf("Cannot save from below kernel function\n");
+ return 1;
+ }
+
+ scimkdir (dirname, 0700);
+
+ if (chdir (dirname)) {
+ sciprintf("Could not enter directory '%s'\n", dirname);
+ return 1;
+ }
+
+ sci_init_dir(&dir);
+ filename = sci_find_first(&dir, "*");
+ while (filename) {
+ if (strcmp(filename, "..") && strcmp(filename, "."))
+ unlink(filename); /* Delete all files in directory */
+ filename = sci_find_next(&dir);
+ }
+ sci_finish_find(&dir);
+
+/*
+ if (s->sound_server) {
+ if ((s->sound_server->save)(s, dirname)) {
+ sciprintf("Saving failed for the sound subsystem\n");
+ chdir ("..");
+ return 1;
+ }
+ }
+*/
+ fh = fopen("state", "w" FO_TEXT);
+
+ /* Calculate the time spent with this game */
+ s->game_time = time(NULL) - s->game_start_time.tv_sec;
+
+SCI_MEMTEST;
+/* Auto-generated CFSML data writer code */
+ _cfsml_write_state_t(fh, s);
+ fprintf(fh, "\n");
+/* End of auto-generated CFSML data writer code */
+SCI_MEMTEST;
+
+ fclose(fh);
+
+ _gamestate_unfrob(s);
+
+
+ chdir ("..");
+ return 0;
+}
+
+static seg_id_t
+find_unique_seg_by_type(seg_manager_t *self, int type)
+{
+ int i;
+
+ for (i = 0; i < self->heap_size; i++)
+ if (self->heap[i] &&
+ self->heap[i]->type == type)
+ return i;
+ return -1;
+}
+
+static byte *
+find_unique_script_block(state_t *s, byte *buf, int type)
+{
+ int magic_pos_adder = s->version >= SCI_VERSION_FTU_NEW_SCRIPT_HEADER ? 0 : 2;
+
+ buf += magic_pos_adder;
+ do {
+ int seeker_type = getUInt16(buf);
+ int seeker_size;
+
+ if (seeker_type == 0) break;
+ if (seeker_type == type) return buf;
+
+ seeker_size = getUInt16(buf + 2);
+ buf += seeker_size;
+ } while(1);
+
+ return NULL;
+}
+
+static
+void reconstruct_stack(state_t *retval)
+{
+ seg_id_t stack_seg = find_unique_seg_by_type(&retval->seg_manager, MEM_OBJ_STACK);
+ dstack_t *stack = &(retval->seg_manager.heap[stack_seg]->data.stack);
+
+ retval->stack_segment = stack_seg;
+ retval->stack_base = stack->entries;
+ retval->stack_top = retval->stack_base + VM_STACK_SIZE;
+}
+
+static
+int clone_entry_used(clone_table_t *table, int n)
+{
+ int backup;
+ int seeker = table->first_free;
+ clone_entry_t *entries = table->table;
+
+ if (seeker == HEAPENTRY_INVALID) return 1;
+
+ do {
+ if (seeker == n) return 0;
+ backup = seeker;
+ seeker = entries[seeker].next_free;
+ } while (entries[backup].next_free != HEAPENTRY_INVALID);
+
+ return 1;
+}
+
+static
+void load_script(state_t *s, seg_id_t seg)
+{
+ resource_t *script, *heap;
+ script_t *scr = &(s->seg_manager.heap[seg]->data.script);
+
+ scr->buf = (byte *) malloc(scr->buf_size);
+
+ script = scir_find_resource(s->resmgr, sci_script, scr->nr, 0);
+ if (s->version >= SCI_VERSION(1,001,000))
+ heap = scir_find_resource(s->resmgr, sci_heap, scr->nr, 0);
+
+ switch (s->seg_manager.sci1_1)
+ {
+ case 0 :
+ sm_mcpy_in_out( &s->seg_manager, 0, script->data, script->size, seg, SEG_ID);
+ break;
+ case 1 :
+ sm_mcpy_in_out( &s->seg_manager, 0, script->data, script->size, seg, SEG_ID);
+ sm_mcpy_in_out( &s->seg_manager, scr->script_size, heap->data, heap->size, seg, SEG_ID);
+ break;
+ }
+}
+
+static
+void reconstruct_scripts(state_t *s, seg_manager_t *self)
+{
+ int i;
+ mem_obj_t *mobj;
+ object_t **objects;
+ int *objects_nr;
+ for (i = 0; i < self->heap_size; i++)
+ if (self->heap[i])
+ {
+ mobj = self->heap[i];
+ switch (mobj->type)
+ {
+ case MEM_OBJ_SCRIPT:
+ {
+ int j;
+ script_t *scr = &mobj->data.script;
+
+ load_script(s, i);
+ scr->locals_block = scr->locals_segment == 0 ? NULL :
+ &s->seg_manager.heap[scr->locals_segment]->data.locals;
+ scr->export_table = (guint16 *) find_unique_script_block(s, scr->buf, sci_obj_exports);
+ scr->synonyms = find_unique_script_block(s, scr->buf, sci_obj_synonyms);
+ scr->code = NULL;
+ scr->code_blocks_nr = 0;
+ scr->code_blocks_allocated = 0;
+
+ if (!self->sci1_1)
+ scr->export_table += 3;
+
+ for (j = 0; j < scr->objects_nr; j++)
+ {
+ byte *data = scr->buf + scr->objects[j].pos.offset;
+ scr->objects[j].base = scr->buf;
+ scr->objects[j].base_obj = data;
+ }
+
+ }
+ }
+ }
+
+ for (i = 0; i < self->heap_size; i++)
+ if (self->heap[i])
+ {
+ mobj = self->heap[i];
+ switch (mobj->type)
+ {
+ case MEM_OBJ_SCRIPT:
+ {
+ int j;
+ script_t *scr = &mobj->data.script;
+
+ for (j = 0; j < scr->objects_nr; j++)
+ {
+ byte *data = scr->buf + scr->objects[j].pos.offset;
+
+ if (self->sci1_1)
+ {
+ guint16 *funct_area = (guint16 *) (scr->buf + getUInt16( data + 6 ));
+ guint16 *prop_area = (guint16 *) (scr->buf + getUInt16( data + 4 ));
+
+ scr->objects[j].base_method = funct_area;
+ scr->objects[j].base_vars = prop_area;
+ } else
+ {
+ int funct_area = getUInt16( data + SCRIPT_FUNCTAREAPTR_OFFSET );
+ object_t *base_obj;
+
+ base_obj = obj_get(s, scr->objects[j].variables[SCRIPT_SPECIES_SELECTOR]);
+
+ if (!base_obj)
+ {
+ sciprintf("Object without a base class: Script %d, index %d (reg address "PREG"\n",
+ scr->nr, j, PRINT_REG(scr->objects[j].variables[SCRIPT_SPECIES_SELECTOR]));
+ continue;
+ }
+ scr->objects[j].variable_names_nr = base_obj->variables_nr;
+ scr->objects[j].base_obj = base_obj->base_obj;
+
+ scr->objects[j].base_method = (guint16 *) (data + funct_area);
+ scr->objects[j].base_vars = (guint16 *) (data + scr->objects[j].variable_names_nr * 2 + SCRIPT_SELECTOR_OFFSET);
+ }
+ }
+ }
+ }
+ }
+}
+
+void
+reconstruct_clones(state_t *s, seg_manager_t *self)
+{
+ int i;
+ mem_obj_t *mobj;
+
+ for (i = 0; i < self->heap_size; i++)
+ if (self->heap[i])
+ {
+ mobj = self->heap[i];
+ switch (mobj->type)
+ {
+ case MEM_OBJ_CLONES:
+ {
+ int j;
+ clone_entry_t *seeker = mobj->data.clones.table;
+
+ sciprintf("Free list: ");
+ for (j = mobj->data.clones.first_free;
+ j != HEAPENTRY_INVALID;
+ j = mobj->data.clones.table[j].next_free)
+ {
+ sciprintf("%d ", j);
+ }
+ sciprintf("\n");
+
+ sciprintf("Entries w/zero vars: ");
+ for (j = 0; j < mobj->data.clones.max_entry; j++)
+ {
+ if (mobj->data.clones.table[j].entry.variables == NULL)
+ sciprintf("%d ", j);
+ }
+ sciprintf("\n");
+
+ for (j = 0; j < mobj->data.clones.max_entry; j++)
+ {
+ object_t *base_obj;
+
+ if (!clone_entry_used(&mobj->data.clones, j)) {
+ seeker++;
+ continue;
+ }
+ base_obj = obj_get(s, seeker->entry.variables[SCRIPT_SPECIES_SELECTOR]);
+ if (!base_obj)
+ {
+ sciprintf("Clone entry without a base class: %d\n", j);
+ seeker->entry.base = seeker->entry.base_obj = NULL;
+ seeker->entry.base_vars = seeker->entry.base_method = NULL;
+ continue;
+ }
+ seeker->entry.base = base_obj->base;
+ seeker->entry.base_obj = base_obj->base_obj;
+ seeker->entry.base_vars = base_obj->base_vars;
+ seeker->entry.base_method = base_obj->base_method;
+
+ seeker++;
+ }
+
+ break;
+ }
+ }
+ }
+}
+
+int
+_reset_graphics_input(state_t *s);
+
+song_iterator_t *
+new_fast_forward_iterator(song_iterator_t *it, int delta);
+
+static
+void reconstruct_sounds(state_t *s)
+{
+ song_t *seeker;
+ int it_type = s->resmgr->sci_version >= SCI_VERSION_01 ?
+ SCI_SONG_ITERATOR_TYPE_SCI1
+ : SCI_SONG_ITERATOR_TYPE_SCI0;
+
+ if (s->sound.songlib.lib)
+ seeker = *(s->sound.songlib.lib);
+ else
+ {
+ song_lib_init(&s->sound.songlib);
+ seeker = NULL;
+ }
+ while (seeker)
+ {
+ song_iterator_t *base, *ff;
+ int oldstatus;
+ song_iterator_message_t msg;
+
+ base = ff = build_iterator(s, seeker->resource_num, it_type, seeker->handle);
+ if (seeker->restore_behavior == RESTORE_BEHAVIOR_CONTINUE)
+ ff = (song_iterator_t *) new_fast_forward_iterator(base, seeker->restore_time);
+ ff->init(ff);
+
+ msg = songit_make_message(seeker->handle, SIMSG_SET_LOOPS(seeker->loops));
+ songit_handle_message(&ff, msg);
+ msg = songit_make_message(seeker->handle, SIMSG_SET_HOLD(seeker->hold));
+ songit_handle_message(&ff, msg);
+
+
+ oldstatus = seeker->status;
+ seeker->status = SOUND_STATUS_STOPPED;
+ seeker->it = ff;
+ sfx_song_set_status(&s->sound, seeker->handle, oldstatus);
+ seeker = seeker->next;
+ }
+
+}
+
+state_t *
+gamestate_restore(state_t *s, char *dirname)
+{
+ FILE *fh;
+ int fd;
+ int i;
+ int read_eof = 0;
+ state_t *retval;
+ songlib_t temp;
+
+ if (chdir (dirname)) {
+ sciprintf("Game state '%s' does not exist\n", dirname);
+ return NULL;
+ }
+
+/*
+ if (s->sound_server) {
+ if ((s->sound_server->restore)(s, dirname)) {
+ sciprintf("Restoring failed for the sound subsystem\n");
+ return NULL;
+ }
+ }
+*/
+
+ retval = (state_t *) sci_malloc(sizeof(state_t));
+
+ memset(retval, 0, sizeof(state_t));
+
+ retval->savegame_version = -1;
+ _global_save_state = retval;
+ retval->gfx_state = s->gfx_state;
+
+ fh = fopen("state", "r" FO_TEXT);
+ if (!fh) {
+ free(retval);
+ return NULL;
+ }
+
+ /* Backwards compatibility settings */
+ retval->dyn_views = NULL;
+ retval->drop_views = NULL;
+ retval->port = NULL;
+ retval->save_dir_copy_buf = NULL;
+
+ retval->sound_mute = s->sound_mute;
+ retval->sound_volume = s->sound_volume;
+
+/* Auto-generated CFSML data reader code */
+ {
+ int _cfsml_line_ctr = 0;
+ struct _cfsml_pointer_refstruct **_cfsml_myptrrefptr = _cfsml_get_current_refpointer();
+ int _cfsml_eof = 0, _cfsml_error;
+ int dummy;
+ char *_cfsml_inp = _cfsml_get_identifier(fh, &(_cfsml_line_ctr), &_cfsml_eof, &dummy);
+
+ _cfsml_error = _cfsml_read_state_t(fh, retval, _cfsml_inp, &(_cfsml_line_ctr), &_cfsml_eof);
+ read_eof = _cfsml_error;
+ _cfsml_free_pointer_references(_cfsml_myptrrefptr, _cfsml_error);
+ if (_cfsml_last_value_retreived) {
+ free(_cfsml_last_value_retreived);
+ _cfsml_last_value_retreived = NULL;
+ }
+ if (_cfsml_last_identifier_retreived) {
+ free(_cfsml_last_identifier_retreived);
+ _cfsml_last_identifier_retreived = NULL;
+ }
+ }
+/* End of auto-generated CFSML data reader code */
+
+ fclose(fh);
+
+ if ((retval->savegame_version < FREESCI_MINIMUM_SAVEGAME_VERSION) ||
+ (retval->savegame_version > FREESCI_CURRENT_SAVEGAME_VERSION)) {
+
+ if (retval->savegame_version < FREESCI_MINIMUM_SAVEGAME_VERSION)
+ sciprintf("Old savegame version detected- can't load\n");
+ else
+ sciprintf("Savegame version is %d- maximum supported is %0d\n", retval->savegame_version, FREESCI_CURRENT_SAVEGAME_VERSION);
+
+ chdir("..");
+ free(retval);
+ return NULL;
+ }
+
+ sfx_exit(&s->sound);
+ _gamestate_unfrob(retval);
+
+ /* Set exec stack base to zero */
+ retval->execution_stack_base = 0;
+ retval->execution_stack_pos = 0;
+
+ /* Now copy all current state information */
+ /* Graphics and input state: */
+ retval->animation_delay = s->animation_delay;
+ retval->animation_granularity = s->animation_granularity;
+ retval->gfx_state = s->gfx_state;
+
+ retval->resmgr = s->resmgr;
+
+ temp = retval->sound.songlib;
+ sfx_init(&retval->sound, retval->resmgr, s->sfx_init_flags);
+ retval->sfx_init_flags = s->sfx_init_flags;
+ song_lib_free(retval->sound.songlib);
+ retval->sound.songlib = temp;
+
+ _reset_graphics_input(retval);
+ reconstruct_stack(retval);
+ reconstruct_scripts(retval, &retval->seg_manager);
+ reconstruct_clones(retval, &retval->seg_manager);
+ retval->game_obj = s->game_obj;
+ retval->script_000 = &retval->seg_manager.heap[script_get_segment(s, 0, SCRIPT_GET_DONT_LOAD)]->data.script;
+ retval->gc_countdown = GC_INTERVAL - 1;
+ retval->save_dir_copy = make_reg(s->sys_strings_segment, SYS_STRING_SAVEDIR);
+ retval->save_dir_edit_offset = 0;
+ retval->sys_strings_segment = find_unique_seg_by_type(&retval->seg_manager, MEM_OBJ_SYS_STRINGS);
+ retval->sys_strings = &(((mem_obj_t *)(GET_SEGMENT(retval->seg_manager, retval->sys_strings_segment, MEM_OBJ_SYS_STRINGS)))->data.sys_strings);
+ sys_strings_restore(retval->sys_strings,
+ s->sys_strings);
+
+ /* Time state: */
+ sci_get_current_time(&(retval->last_wait_time));
+ retval->game_start_time.tv_sec = time(NULL) - retval->game_time;
+ retval->game_start_time.tv_usec = 0;
+
+ /* File IO state: */
+ retval->file_handles_nr = 2;
+ retval->file_handles = (FILE **)sci_calloc(2, sizeof(FILE *));
+
+ /* static parser information: */
+ retval->parser_rules = s->parser_rules;
+ retval->parser_words_nr = s->parser_words_nr;
+ retval->parser_words = s->parser_words;
+ retval->parser_suffices_nr = s->parser_suffices_nr;
+ retval->parser_suffices = s->parser_suffices;
+ retval->parser_branches_nr = s->parser_branches_nr;
+ retval->parser_branches = s->parser_branches;
+
+ /* static VM/Kernel information: */
+ retval->selector_names_nr = s->selector_names_nr;
+ retval->selector_names = s->selector_names;
+ retval->kernel_names_nr = s->kernel_names_nr;
+ retval->kernel_names = s->kernel_names;
+ retval->kfunct_table = s->kfunct_table;
+ retval->kfunct_nr = s->kfunct_nr;
+ retval->opcodes = s->opcodes;
+
+ memcpy(&(retval->selector_map), &(s->selector_map), sizeof(selector_map_t));
+
+ retval->max_version = retval->version;
+ retval->min_version = retval->version;
+ retval->parser_base = make_reg(s->sys_strings_segment, SYS_STRING_PARSER_BASE);
+
+ /* Copy breakpoint information from current game instance */
+ retval->have_bp = s->have_bp;
+ retval->bp_list = s->bp_list;
+
+ retval->debug_mode = s->debug_mode;
+
+ retval->resource_dir = s->resource_dir;
+ retval->work_dir = s->work_dir;
+ retval->kernel_opt_flags = 0;
+ retval->have_mouse_flag = s->have_mouse_flag;
+
+ retval->successor = NULL;
+ retval->pic_priority_table = (int*)gfxop_get_pic_metainfo(retval->gfx_state);
+ retval->game_name = sci_strdup(obj_get_name(retval, retval->game_obj));
+
+ retval->sound.it = NULL;
+ retval->sound.flags = s->sound.flags;
+ retval->sound.song = NULL;
+ retval->sound.suspended = s->sound.suspended;
+ retval->sound.debug = s->sound.debug;
+ reconstruct_sounds(retval);
+
+ chdir ("..");
+
+ return retval;
+}
diff --git a/engines/sci/engine/savegame.cfsml b/engines/sci/engine/savegame.cfsml
new file mode 100644
index 0000000000..57abbfb862
--- /dev/null
+++ b/engines/sci/engine/savegame.cfsml
@@ -0,0 +1,1226 @@
+/***************************************************************************
+ savegame.cfsml 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) [jameson@linuxgames.com]
+
+***************************************************************************/
+/* Savegame handling for state_t structs. Makes heavy use of cfsml magic. */
+/* DON'T EDIT savegame.c ! Only modify savegame.cfsml, if something needs
+** to be changed. Refer to freesci/docs/misc/cfsml.spec if you don't understand
+** savegame.cfsml. If this doesn't solve your problem, contact the maintainer.
+*/
+
+#include <sci_memory.h>
+#include <gfx_operations.h>
+#include <sfx_engine.h>
+#include <engine.h>
+#include <assert.h>
+#include "heap.h"
+
+#ifdef _MSC_VER
+#include <direct.h>
+#endif
+
+#ifdef _WIN32
+#pragma warning( disable : 4101 )
+#endif
+
+#define HUNK_TYPE_GFX_SNAPSHOT_STRING "g\n"
+
+/* Missing:
+** - SFXdriver
+** - File input/output state (this is likely not to happen)
+*/
+
+static state_t *_global_save_state;
+/* Needed for some graphical stuff. */
+#define FILE_VERSION _global_save_state->savegame_version
+
+
+void
+write_reg_t(FILE *fh, reg_t *foo)
+{
+ fprintf(fh, PREG, PRINT_REG(*foo));
+}
+
+int
+read_reg_t(FILE *fh, reg_t *foo, char *lastval, int *line, int *hiteof)
+{
+ int segment, offset;
+
+ if (sscanf(lastval, PREG, &segment, &offset)<2)
+ {
+ sciprintf("Error parsing reg_t on line %d\n", *line);
+ return 1;
+ }
+
+ *foo = make_reg(segment, offset);
+ return 0;
+}
+
+void
+write_sci_version(FILE *fh, sci_version_t *foo)
+{
+ fprintf(fh, "%d.%03d.%03d", SCI_VERSION_MAJOR(*foo), SCI_VERSION_MINOR(*foo),
+ SCI_VERSION_PATCHLEVEL(*foo));
+}
+
+int
+read_sci_version(FILE *fh, sci_version_t *foo, char *lastval, int *line, int *hiteof)
+{
+ return version_parse(lastval, foo);
+}
+
+void
+write_PTN(FILE *fh, parse_tree_node_t *foo)
+{
+ if (foo->type == PARSE_TREE_NODE_LEAF)
+ fprintf(fh, "L%d", foo->content.value);
+ else
+ fprintf(fh, "B(%d,%d)", foo->content.branches[0], foo->content.branches[1]);
+}
+
+int
+read_PTN(FILE *fh, parse_tree_node_t *foo, char *lastval, int *line, int *hiteof)
+{
+ if (lastval[0] == 'L') {
+ char *c = lastval + 1;
+ char *strend;
+
+ while (*c && isspace(*c))
+ ++c;
+
+ if (!*c)
+ return 1;
+
+ foo->content.value = strtol(c, &strend, 0);
+
+ return (strend == c); /* Error if nothing could be read */
+
+ return 0;
+ } else if (lastval[0] == 'B') {
+ char *c = lastval + 1;
+ char *strend;
+
+ while (*c && isspace(*c)) ++c;
+ if (*c++ != '(') return 1;
+ while (*c && isspace(*c)) ++c;
+
+ foo->content.branches[0] = strtol(c, &strend, 0);
+ if (strend == c)
+ return 1;
+ c = strend;
+
+ while (*c && isspace(*c)) ++c;
+ if (*c++ != ',')
+ return 1;
+
+ while (*c && isspace(*c)) ++c;
+
+ foo->content.branches[1] = strtol(c, &strend, 0);
+ if (strend == c)
+ return 1;
+ c = strend;
+
+ while (*c && isspace(*c)) ++c;
+ if (*c++ != ')') return 1;
+
+ return 0;
+ } else return 1; /* failure to parse anything */
+}
+
+
+void
+write_menubar_tp(FILE *fh, menubar_t **foo);
+int
+read_menubar_tp(FILE *fh, menubar_t **foo, char *lastval, int *line, int *hiteof);
+
+void
+write_mem_obj_tp(FILE *fh, mem_obj_t **foo);
+int
+read_mem_obj_tp(FILE *fh, mem_obj_t **foo, char *lastval, int *line, int *hiteof);
+
+void
+write_int_hash_map_tp(FILE *fh, int_hash_map_t **foo);
+int
+read_int_hash_map_tp(FILE *fh, int_hash_map_t **foo, char *lastval, int *line, int *hiteof);
+
+void
+write_songlib_t(FILE *fh, songlib_t *foo);
+int
+read_songlib_t(FILE *fh, songlib_t *foo, char *lastval, int *line, int *hiteof);
+
+void
+write_int_hash_map_node_tp(FILE *fh, int_hash_map_node_t **foo);
+int
+read_int_hash_map_node_tp(FILE *fh, int_hash_map_node_t **foo, char *lastval, int *line, int *hiteof);
+
+int
+read_song_tp(FILE *fh, song_t **foo, char *lastval, int *line, int *hiteof);
+
+typedef mem_obj_t *mem_obj_ptr;
+
+%CFSML
+
+TYPE byte "byte" LIKE int;
+TYPE long "long" LIKE int;
+TYPE gint16 "gint16" LIKE int;
+TYPE seg_id_t "seg_id_t" LIKE int;
+TYPE sci_version_t "sci_version_t" USING write_sci_version read_sci_version;
+TYPE menubar_tp "menubar_t *" USING write_menubar_tp read_menubar_tp;
+TYPE mem_obj_t "mem_obj_t" USING write_mem_obj_t read_mem_obj_t;
+TYPE mem_obj_ptr "mem_obj_t *" USING write_mem_obj_tp read_mem_obj_tp;
+TYPE reg_t "reg_t" USING write_reg_t read_reg_t;
+TYPE size_t "size_t" LIKE int;
+TYPE int_hash_map_tp "int_hash_map_t *" USING write_int_hash_map_tp read_int_hash_map_tp;
+TYPE int_hash_map_node_tp "int_hash_map_node_t *" USING write_int_hash_map_node_tp read_int_hash_map_node_tp;
+TYPE songlib_t "songlib_t" USING write_songlib_t read_songlib_t;
+TYPE song_tp "song_t *" USING write_song_tp read_song_tp;
+TYPE song_iterator_t "song_iterator_t" USING write_song_iterator_t read_song_iterator_t;
+TYPE song_handle_t "song_handle_t" LIKE int;
+
+RECORD song_t "song_t" {
+ song_handle_t handle;
+ int resource_num;
+ int priority;
+ int status;
+ int restore_behavior;
+ int restore_time;
+ int loops;
+ int hold;
+}
+
+RECORD int_hash_map_t "int_hash_map_t" {
+ int base_value;
+ int_hash_map_node_tp nodes[STATIC DCS_INT_HASH_MAX+1];
+}
+
+RECORD menu_item_t "menu_item_t" {
+ int type;
+ string keytext;
+ int keytext_size;
+
+ int flags;
+ byte said[STATIC MENU_SAID_SPEC_SIZE];
+ reg_t said_pos;
+ string text;
+ reg_t text_pos;
+ int modifiers;
+ int key;
+ int enabled;
+ int tag;
+}
+
+RECORD menu_t "menu_t" {
+ string title;
+ int title_width;
+ int width;
+
+ menu_item_t items[DYNAMIC items_nr];
+}
+
+RECORD menubar_t "menubar_t" {
+ menu_t menus[DYNAMIC menus_nr];
+}
+
+RECORD synonym_t "synonym_t" {
+ int replaceant;
+ int replacement;
+}
+
+
+RECORD seg_manager_t "seg_manager_t" {
+ int_hash_map_tp id_seg_map;
+ mem_obj_ptr heap[DYNAMIC heap_size];
+ int heap_size;
+ int reserved_id;
+ int exports_wide;
+ int sci1_1;
+ int gc_mark_bits;
+ size_t mem_allocated;
+ seg_id_t clones_seg_id;
+ seg_id_t lists_seg_id;
+ seg_id_t nodes_seg_id;
+}
+
+RECORD class_t "class_t" {
+ int script;
+ reg_t reg;
+}
+
+RECORD sfx_state_t "sfx_state_t" {
+ songlib_t songlib;
+}
+
+RECORD state_t "state_t" {
+ int savegame_version;
+
+ string game_version;
+ sci_version_t version;
+ menubar_tp menubar;
+ int status_bar_foreground;
+ int status_bar_background;
+ seg_manager_t seg_manager;
+ int classtable_size;
+ class_t classtable[DYNAMIC classtable_size];
+ sfx_state_t sound;
+}
+
+RECORD local_variables_t "local_variables_t" {
+ int script_id;
+ int nr;
+ reg_t locals[DYNAMIC nr];
+}
+
+RECORD object_t "object_t" {
+ int flags;
+ reg_t pos;
+ int variables_nr;
+ int variable_names_nr;
+ int methods_nr;
+ reg_t variables[DYNAMIC variables_nr];
+}
+
+RECORD clone_t "clone_t" {
+ int flags;
+ reg_t pos;
+ int variables_nr;
+ int variable_names_nr;
+ int methods_nr;
+ reg_t variables[DYNAMIC variables_nr];
+}
+
+RECORD list_t "list_t" {
+ reg_t first;
+ reg_t last;
+}
+
+RECORD node_t "node_t" {
+ reg_t pred;
+ reg_t succ;
+ reg_t key;
+ reg_t value;
+}
+
+RECORD clone_entry_t "clone_entry_t" {
+ int next_free;
+ clone_t entry;
+}
+
+RECORD clone_table_t "clone_table_t" {
+ int entries_nr;
+ int first_free;
+ int entries_used;
+ int max_entry;
+ clone_entry_t table[DYNAMIC entries_nr];
+}
+
+RECORD list_entry_t "list_entry_t" {
+ int next_free;
+ list_t entry;
+}
+
+RECORD list_table_t "list_table_t" {
+ int entries_nr;
+ int first_free;
+ int entries_used;
+ int max_entry;
+ list_entry_t table[DYNAMIC entries_nr];
+}
+
+RECORD node_entry_t "node_entry_t" {
+ int next_free;
+ node_t entry;
+}
+
+RECORD node_table_t "node_table_t" {
+ int entries_nr;
+ int first_free;
+ int entries_used;
+ int max_entry;
+ node_entry_t table[DYNAMIC entries_nr];
+}
+
+RECORD script_t "script_t" {
+ int nr;
+
+ size_t buf_size;
+ size_t script_size;
+ size_t heap_size;
+
+ int_hash_map_tp obj_indices;
+ int exports_nr;
+ int synonyms_nr;
+ int lockers;
+ int objects_allocated;
+ int objects_nr;
+ object_t objects[DYNAMIC objects_allocated];
+
+ int locals_offset;
+ int locals_segment;
+
+ int marked_as_deleted;
+}
+
+RECORD sys_string_t "sys_string_t" {
+ string name;
+ int max_size;
+ string value;
+}
+
+RECORD sys_strings_t "sys_strings_t" {
+ sys_string_t strings[STATIC SYS_STRINGS_MAX];
+}
+
+RECORD dynmem_t "dynmem_t" {
+ int size;
+ string description;
+ byte buf[DYNAMIC size];
+}
+
+%END CFSML
+
+void
+write_songlib_t(FILE *fh, songlib_t *songlib)
+{
+ song_t *seeker = *(songlib->lib);
+ int songcount = song_lib_count(*songlib);
+
+ fprintf(fh, "{\n");
+ fprintf(fh, "songcount = %d\n", songcount);
+ fprintf(fh, "list = \n");
+ fprintf(fh, "[\n");
+ while (seeker)
+ {
+ seeker->restore_time = seeker->it->get_timepos(seeker->it);
+ %CFSMLWRITE song_t seeker INTO fh;
+ seeker = seeker->next;
+ }
+ fprintf(fh, "]\n");
+ fprintf(fh, "}\n");
+}
+
+int
+read_songlib_t(FILE *fh, songlib_t *songlib, char *lastval, int *line, int *hiteof)
+{
+ int songcount;
+ int i;
+ song_t *newsong;
+ int oldstatus;
+
+ fscanf(fh, "{\n");
+ fscanf(fh, "songcount = %d\n", &songcount);
+ fscanf(fh, "list = \n");
+ fscanf(fh, "[\n");
+ *line += 4;
+ song_lib_init(songlib);
+ for (i = 0; i < songcount; i++)
+ {
+ %CFSMLREAD song_tp &newsong FROM fh ERRVAR *hiteof FIRSTTOKEN lastval LINECOUNTER *line;
+ song_lib_add(*songlib, newsong);
+ }
+ fscanf(fh, "]\n");
+ fscanf(fh, "}\n");;
+ *line += 2;
+ return 0;
+}
+
+struct {
+ int type;
+ char *name;
+} mem_obj_string_names[] = {
+ {MEM_OBJ_INVALID, "INVALID"},
+ {MEM_OBJ_SCRIPT, "SCRIPT"},
+ {MEM_OBJ_CLONES, "CLONES"},
+ {MEM_OBJ_LOCALS, "LOCALS"},
+ {MEM_OBJ_STACK, "STACK"},
+ {MEM_OBJ_SYS_STRINGS,"SYS_STRINGS"},
+ {MEM_OBJ_LISTS,"LISTS"},
+ {MEM_OBJ_NODES,"NODES"},
+ {MEM_OBJ_HUNK,"HUNK"},
+ {MEM_OBJ_DYNMEM,"DYNMEM"}};
+
+int
+mem_obj_string_to_enum(char *str)
+{
+ int i;
+
+ for (i = 0; i <= MEM_OBJ_MAX; i++)
+ { if (!strcasecmp(mem_obj_string_names[i].name, str))
+ return i;
+ }
+
+ return -1;
+}
+
+static int bucket_length;
+
+void
+write_int_hash_map_tp(FILE *fh, int_hash_map_t **foo)
+{
+ %CFSMLWRITE int_hash_map_t *foo INTO fh;
+}
+
+void
+write_song_tp(FILE *fh, song_t **foo)
+{
+ %CFSMLWRITE song_t *foo INTO fh;
+}
+
+song_iterator_t *
+build_iterator(state_t *s, int song_nr, int type, songit_id_t id);
+
+int
+read_song_tp(FILE *fh, song_t **foo, char *lastval, int *line, int *hiteof)
+{
+ char *token;
+ int assignment;
+ *foo = (song_t*) malloc(sizeof(song_t));
+ token = _cfsml_get_identifier(fh, line, hiteof, &assignment);
+ %CFSMLREAD song_t (*foo) FROM fh ERRVAR *hiteof FIRSTTOKEN token LINECOUNTER *line;
+ (*foo)->delay = 0;
+ (*foo)->it = NULL;
+ (*foo)->next_playing = (*foo)->next_stopping = (*foo)->next = NULL;
+ return 0;
+}
+int
+read_int_hash_map_tp(FILE *fh, int_hash_map_t **foo, char *lastval, int *line, int *hiteof)
+{
+ *foo = (int_hash_map_t*)malloc(sizeof(int_hash_map_t));
+ %CFSMLREAD int_hash_map_t (*foo) FROM fh ERRVAR *hiteof FIRSTTOKEN lastval LINECOUNTER *line;
+ (*foo)->holes = NULL;
+ return 0;
+}
+
+void
+write_int_hash_map_node_tp(FILE *fh, int_hash_map_node_t **foo)
+{
+ if (!(*foo))
+ {
+ fputs("\\null", fh);
+ } else
+ {
+ fprintf(fh,"[\n%d=>%d\n", (*foo)->name, (*foo)->value);
+ if ((*foo)->next)
+ {
+ %CFSMLWRITE int_hash_map_node_tp &((*foo)->next) INTO fh;
+ } else fputc('L', fh);
+ fputs("]", fh);
+ }
+}
+
+int
+read_int_hash_map_node_tp(FILE *fh, int_hash_map_node_t **foo, char *lastval, int *line, int *hiteof)
+{
+ static char buffer[80];
+
+ if (lastval[0] == '\\') {
+ *foo = NULL; /* No hash map node */
+ } else {
+ *foo = (int_hash_map_node_t*)malloc(sizeof(int_hash_map_node_t));
+ if (lastval[0] != '[')
+ {
+ sciprintf("Expected opening bracket in hash_map_node_t on line %d\n", *line);
+ return 1;
+ }
+
+ do {
+ (*line)++;
+ fgets(buffer, 80, fh);
+ if (buffer[0] == 'L')
+ {
+ (*foo)->next = NULL;
+ buffer[0] = buffer[1];
+ } /* HACK: deliberately no else clause here */
+ if (buffer[0] == ']')
+ {
+ break;
+ }
+ else if (buffer[0] == '[')
+ {
+ if (read_int_hash_map_node_tp(fh, &((*foo)->next), buffer, line, hiteof))
+ return 1;
+ }
+ else if (sscanf(buffer, "%d=>%d", &((*foo)->name), &((*foo)->value))<2)
+ {
+ sciprintf("Error parsing hash_map_node_t on line %d\n", *line);
+ return 1;
+ }
+ } while (1);
+ }
+
+ return 0;
+}
+
+void
+write_menubar_tp(FILE *fh, menubar_t **foo)
+{
+ if (*foo) {
+
+ %CFSMLWRITE menubar_t (*foo) INTO fh;
+
+ } else { /* Nothing to write */
+ fputs("\\null\\", fh);
+ }
+}
+
+
+int
+read_menubar_tp(FILE *fh, menubar_t **foo, char *lastval, int *line, int *hiteof)
+{
+
+ if (lastval[0] == '\\') {
+ *foo = NULL; /* No menu bar */
+ } else {
+
+ *foo = (menubar_t *) sci_malloc(sizeof(menubar_t));
+ %CFSMLREAD menubar_t (*foo) FROM fh ERRVAR *hiteof FIRSTTOKEN lastval LINECOUNTER *line;
+
+ }
+ return *hiteof;
+}
+
+void
+write_mem_obj_t(FILE *fh, mem_obj_t *foo)
+{
+ fprintf(fh, "%s\n", mem_obj_string_names[foo->type].name);
+ %CFSMLWRITE int &foo->segmgr_id INTO fh;
+ switch (foo->type)
+ {
+ case MEM_OBJ_SCRIPT:
+ %CFSMLWRITE script_t &foo->data.script INTO fh;
+ break;
+ case MEM_OBJ_CLONES:
+ %CFSMLWRITE clone_table_t &foo->data.clones INTO fh;
+ break;
+ case MEM_OBJ_LOCALS:
+ %CFSMLWRITE local_variables_t &foo->data.locals INTO fh;
+ break;
+ case MEM_OBJ_SYS_STRINGS:
+ %CFSMLWRITE sys_strings_t &foo->data.sys_strings INTO fh;
+ break;
+ case MEM_OBJ_STACK:
+ %CFSMLWRITE int &foo->data.stack.nr INTO fh;
+ break;
+ case MEM_OBJ_HUNK:
+ break;
+ case MEM_OBJ_LISTS:
+ %CFSMLWRITE list_table_t &foo->data.lists INTO fh;
+ break;
+ case MEM_OBJ_NODES:
+ %CFSMLWRITE node_table_t &foo->data.nodes INTO fh;
+ break;
+ case MEM_OBJ_DYNMEM:
+ %CFSMLWRITE dynmem_t &foo->data.dynmem INTO fh;
+ break;
+ }
+}
+
+int
+read_mem_obj_t(FILE *fh, mem_obj_t *foo, char *lastval, int *line, int *hiteof)
+{
+ char buffer[80];
+ foo->type = mem_obj_string_to_enum(lastval);
+ if (foo->type < 0)
+ {
+ sciprintf("Unknown mem_obj_t type %s on line %d\n", lastval, *line);
+ return 1;
+ }
+
+ %CFSMLREAD int &foo->segmgr_id FROM fh ERRVAR *hiteof LINECOUNTER *line;
+ switch (foo->type)
+ {
+ case MEM_OBJ_SCRIPT:
+ %CFSMLREAD script_t &foo->data.script FROM fh ERRVAR *hiteof LINECOUNTER *line;
+ break;
+ case MEM_OBJ_CLONES:
+ %CFSMLREAD clone_table_t &foo->data.clones FROM fh ERRVAR *hiteof LINECOUNTER *line;
+ break;
+ case MEM_OBJ_LOCALS:
+ %CFSMLREAD local_variables_t &foo->data.locals FROM fh ERRVAR *hiteof LINECOUNTER *line;
+ break;
+ case MEM_OBJ_SYS_STRINGS:
+ %CFSMLREAD sys_strings_t &foo->data.sys_strings FROM fh ERRVAR *hiteof LINECOUNTER *line;
+ break;
+ case MEM_OBJ_LISTS:
+ %CFSMLREAD list_table_t &foo->data.lists FROM fh ERRVAR *hiteof LINECOUNTER *line;
+ break;
+ case MEM_OBJ_NODES:
+ %CFSMLREAD node_table_t &foo->data.nodes FROM fh ERRVAR *hiteof LINECOUNTER *line;
+ break;
+ case MEM_OBJ_STACK:
+ %CFSMLREAD int &foo->data.stack.nr FROM fh ERRVAR *hiteof LINECOUNTER *line;
+ foo->data.stack.entries = (reg_t *)sci_calloc(foo->data.stack.nr, sizeof(reg_t));
+ break;
+ case MEM_OBJ_HUNK:
+ init_hunk_table(&foo->data.hunks);
+ break;
+ case MEM_OBJ_DYNMEM:
+ %CFSMLREAD dynmem_t &foo->data.dynmem FROM fh ERRVAR *hiteof LINECOUNTER *line;
+ break;
+ }
+
+ return *hiteof;
+}
+
+void
+write_mem_obj_tp(FILE *fh, mem_obj_t **foo)
+{
+ if (*foo) {
+
+ %CFSMLWRITE mem_obj_t (*foo) INTO fh;
+
+ } else { /* Nothing to write */
+ fputs("\\null\\", fh);
+ }
+}
+
+int
+read_mem_obj_tp(FILE *fh, mem_obj_t **foo, char *lastval, int *line, int *hiteof)
+{
+
+ if (lastval[0] == '\\') {
+ *foo = NULL; /* No menu bar */
+ } else {
+ *foo = (mem_obj_t *) sci_malloc(sizeof(mem_obj_t));
+ %CFSMLREAD mem_obj_t (*foo) FROM fh ERRVAR *hiteof FIRSTTOKEN lastval LINECOUNTER *line;
+ return *hiteof;
+ }
+ return 0;
+}
+
+
+
+/* This function is called to undo some strange stuff done in preparation
+** to writing a gamestate to disk
+*/
+void
+_gamestate_unfrob(state_t *s)
+{
+}
+
+
+int
+gamestate_save(state_t *s, char *dirname)
+{
+ FILE *fh;
+ sci_dir_t dir;
+ char *filename;
+ int fd;
+
+ _global_save_state = s;
+ s->savegame_version = FREESCI_CURRENT_SAVEGAME_VERSION;
+ s->dyn_views_list_serial = (s->dyn_views)? s->dyn_views->serial : -2;
+ s->drop_views_list_serial = (s->drop_views)? s->drop_views->serial : -2;
+ s->port_serial = (s->port)? s->port->serial : -2;
+
+ if (s->execution_stack_base) {
+ sciprintf("Cannot save from below kernel function\n");
+ return 1;
+ }
+
+ scimkdir (dirname, 0700);
+
+ if (chdir (dirname)) {
+ sciprintf("Could not enter directory '%s'\n", dirname);
+ return 1;
+ }
+
+ sci_init_dir(&dir);
+ filename = sci_find_first(&dir, "*");
+ while (filename) {
+ if (strcmp(filename, "..") && strcmp(filename, "."))
+ unlink(filename); /* Delete all files in directory */
+ filename = sci_find_next(&dir);
+ }
+ sci_finish_find(&dir);
+
+/*
+ if (s->sound_server) {
+ if ((s->sound_server->save)(s, dirname)) {
+ sciprintf("Saving failed for the sound subsystem\n");
+ chdir ("..");
+ return 1;
+ }
+ }
+*/
+ fh = fopen("state", "w" FO_TEXT);
+
+ /* Calculate the time spent with this game */
+ s->game_time = time(NULL) - s->game_start_time.tv_sec;
+
+SCI_MEMTEST;
+ %CFSMLWRITE state_t s INTO fh;
+SCI_MEMTEST;
+
+ fclose(fh);
+
+ _gamestate_unfrob(s);
+
+
+ chdir ("..");
+ return 0;
+}
+
+static seg_id_t
+find_unique_seg_by_type(seg_manager_t *self, int type)
+{
+ int i;
+
+ for (i = 0; i < self->heap_size; i++)
+ if (self->heap[i] &&
+ self->heap[i]->type == type)
+ return i;
+ return -1;
+}
+
+static byte *
+find_unique_script_block(state_t *s, byte *buf, int type)
+{
+ int magic_pos_adder = s->version >= SCI_VERSION_FTU_NEW_SCRIPT_HEADER ? 0 : 2;
+
+ buf += magic_pos_adder;
+ do {
+ int seeker_type = getUInt16(buf);
+ int seeker_size;
+
+ if (seeker_type == 0) break;
+ if (seeker_type == type) return buf;
+
+ seeker_size = getUInt16(buf + 2);
+ buf += seeker_size;
+ } while(1);
+
+ return NULL;
+}
+
+static
+void reconstruct_stack(state_t *retval)
+{
+ seg_id_t stack_seg = find_unique_seg_by_type(&retval->seg_manager, MEM_OBJ_STACK);
+ dstack_t *stack = &(retval->seg_manager.heap[stack_seg]->data.stack);
+
+ retval->stack_segment = stack_seg;
+ retval->stack_base = stack->entries;
+ retval->stack_top = retval->stack_base + VM_STACK_SIZE;
+}
+
+static
+int clone_entry_used(clone_table_t *table, int n)
+{
+ int backup;
+ int seeker = table->first_free;
+ clone_entry_t *entries = table->table;
+
+ if (seeker == HEAPENTRY_INVALID) return 1;
+
+ do {
+ if (seeker == n) return 0;
+ backup = seeker;
+ seeker = entries[seeker].next_free;
+ } while (entries[backup].next_free != HEAPENTRY_INVALID);
+
+ return 1;
+}
+
+static
+void load_script(state_t *s, seg_id_t seg)
+{
+ resource_t *script, *heap;
+ script_t *scr = &(s->seg_manager.heap[seg]->data.script);
+
+ scr->buf = (byte *) malloc(scr->buf_size);
+
+ script = scir_find_resource(s->resmgr, sci_script, scr->nr, 0);
+ if (s->version >= SCI_VERSION(1,001,000))
+ heap = scir_find_resource(s->resmgr, sci_heap, scr->nr, 0);
+
+ switch (s->seg_manager.sci1_1)
+ {
+ case 0 :
+ sm_mcpy_in_out( &s->seg_manager, 0, script->data, script->size, seg, SEG_ID);
+ break;
+ case 1 :
+ sm_mcpy_in_out( &s->seg_manager, 0, script->data, script->size, seg, SEG_ID);
+ sm_mcpy_in_out( &s->seg_manager, scr->script_size, heap->data, heap->size, seg, SEG_ID);
+ break;
+ }
+}
+
+static
+void reconstruct_scripts(state_t *s, seg_manager_t *self)
+{
+ int i;
+ mem_obj_t *mobj;
+ object_t **objects;
+ int *objects_nr;
+ for (i = 0; i < self->heap_size; i++)
+ if (self->heap[i])
+ {
+ mobj = self->heap[i];
+ switch (mobj->type)
+ {
+ case MEM_OBJ_SCRIPT:
+ {
+ int j;
+ script_t *scr = &mobj->data.script;
+
+ load_script(s, i);
+ scr->locals_block = scr->locals_segment == 0 ? NULL :
+ &s->seg_manager.heap[scr->locals_segment]->data.locals;
+ scr->export_table = (guint16 *) find_unique_script_block(s, scr->buf, sci_obj_exports);
+ scr->synonyms = find_unique_script_block(s, scr->buf, sci_obj_synonyms);
+ scr->code = NULL;
+ scr->code_blocks_nr = 0;
+ scr->code_blocks_allocated = 0;
+
+ if (!self->sci1_1)
+ scr->export_table += 3;
+
+ for (j = 0; j < scr->objects_nr; j++)
+ {
+ byte *data = scr->buf + scr->objects[j].pos.offset;
+ scr->objects[j].base = scr->buf;
+ scr->objects[j].base_obj = data;
+ }
+
+ }
+ }
+ }
+
+ for (i = 0; i < self->heap_size; i++)
+ if (self->heap[i])
+ {
+ mobj = self->heap[i];
+ switch (mobj->type)
+ {
+ case MEM_OBJ_SCRIPT:
+ {
+ int j;
+ script_t *scr = &mobj->data.script;
+
+ for (j = 0; j < scr->objects_nr; j++)
+ {
+ byte *data = scr->buf + scr->objects[j].pos.offset;
+
+ if (self->sci1_1)
+ {
+ guint16 *funct_area = (guint16 *) (scr->buf + getUInt16( data + 6 ));
+ guint16 *prop_area = (guint16 *) (scr->buf + getUInt16( data + 4 ));
+
+ scr->objects[j].base_method = funct_area;
+ scr->objects[j].base_vars = prop_area;
+ } else
+ {
+ int funct_area = getUInt16( data + SCRIPT_FUNCTAREAPTR_OFFSET );
+ object_t *base_obj;
+
+ base_obj = obj_get(s, scr->objects[j].variables[SCRIPT_SPECIES_SELECTOR]);
+
+ if (!base_obj)
+ {
+ sciprintf("Object without a base class: Script %d, index %d (reg address "PREG"\n",
+ scr->nr, j, PRINT_REG(scr->objects[j].variables[SCRIPT_SPECIES_SELECTOR]));
+ continue;
+ }
+ scr->objects[j].variable_names_nr = base_obj->variables_nr;
+ scr->objects[j].base_obj = base_obj->base_obj;
+
+ scr->objects[j].base_method = (guint16 *) (data + funct_area);
+ scr->objects[j].base_vars = (guint16 *) (data + scr->objects[j].variable_names_nr * 2 + SCRIPT_SELECTOR_OFFSET);
+ }
+ }
+ }
+ }
+ }
+}
+
+void
+reconstruct_clones(state_t *s, seg_manager_t *self)
+{
+ int i;
+ mem_obj_t *mobj;
+
+ for (i = 0; i < self->heap_size; i++)
+ if (self->heap[i])
+ {
+ mobj = self->heap[i];
+ switch (mobj->type)
+ {
+ case MEM_OBJ_CLONES:
+ {
+ int j;
+ clone_entry_t *seeker = mobj->data.clones.table;
+
+ sciprintf("Free list: ");
+ for (j = mobj->data.clones.first_free;
+ j != HEAPENTRY_INVALID;
+ j = mobj->data.clones.table[j].next_free)
+ {
+ sciprintf("%d ", j);
+ }
+ sciprintf("\n");
+
+ sciprintf("Entries w/zero vars: ");
+ for (j = 0; j < mobj->data.clones.max_entry; j++)
+ {
+ if (mobj->data.clones.table[j].entry.variables == NULL)
+ sciprintf("%d ", j);
+ }
+ sciprintf("\n");
+
+ for (j = 0; j < mobj->data.clones.max_entry; j++)
+ {
+ object_t *base_obj;
+
+ if (!clone_entry_used(&mobj->data.clones, j)) {
+ seeker++;
+ continue;
+ }
+ base_obj = obj_get(s, seeker->entry.variables[SCRIPT_SPECIES_SELECTOR]);
+ if (!base_obj)
+ {
+ sciprintf("Clone entry without a base class: %d\n", j);
+ seeker->entry.base = seeker->entry.base_obj = NULL;
+ seeker->entry.base_vars = seeker->entry.base_method = NULL;
+ continue;
+ }
+ seeker->entry.base = base_obj->base;
+ seeker->entry.base_obj = base_obj->base_obj;
+ seeker->entry.base_vars = base_obj->base_vars;
+ seeker->entry.base_method = base_obj->base_method;
+
+ seeker++;
+ }
+
+ break;
+ }
+ }
+ }
+}
+
+int
+_reset_graphics_input(state_t *s);
+
+song_iterator_t *
+new_fast_forward_iterator(song_iterator_t *it, int delta);
+
+static
+void reconstruct_sounds(state_t *s)
+{
+ song_t *seeker;
+ int it_type = s->resmgr->sci_version >= SCI_VERSION_01 ?
+ SCI_SONG_ITERATOR_TYPE_SCI1
+ : SCI_SONG_ITERATOR_TYPE_SCI0;
+
+ if (s->sound.songlib.lib)
+ seeker = *(s->sound.songlib.lib);
+ else
+ {
+ song_lib_init(&s->sound.songlib);
+ seeker = NULL;
+ }
+ while (seeker)
+ {
+ song_iterator_t *base, *ff;
+ int oldstatus;
+ song_iterator_message_t msg;
+
+ base = ff = build_iterator(s, seeker->resource_num, it_type, seeker->handle);
+ if (seeker->restore_behavior == RESTORE_BEHAVIOR_CONTINUE)
+ ff = (song_iterator_t *) new_fast_forward_iterator(base, seeker->restore_time);
+ ff->init(ff);
+
+ msg = songit_make_message(seeker->handle, SIMSG_SET_LOOPS(seeker->loops));
+ songit_handle_message(&ff, msg);
+ msg = songit_make_message(seeker->handle, SIMSG_SET_HOLD(seeker->hold));
+ songit_handle_message(&ff, msg);
+
+
+ oldstatus = seeker->status;
+ seeker->status = SOUND_STATUS_STOPPED;
+ seeker->it = ff;
+ sfx_song_set_status(&s->sound, seeker->handle, oldstatus);
+ seeker = seeker->next;
+ }
+
+}
+
+state_t *
+gamestate_restore(state_t *s, char *dirname)
+{
+ FILE *fh;
+ int fd;
+ int i;
+ int read_eof = 0;
+ state_t *retval;
+ songlib_t temp;
+
+ if (chdir (dirname)) {
+ sciprintf("Game state '%s' does not exist\n", dirname);
+ return NULL;
+ }
+
+/*
+ if (s->sound_server) {
+ if ((s->sound_server->restore)(s, dirname)) {
+ sciprintf("Restoring failed for the sound subsystem\n");
+ return NULL;
+ }
+ }
+*/
+
+ retval = (state_t *) sci_malloc(sizeof(state_t));
+
+ memset(retval, 0, sizeof(state_t));
+
+ retval->savegame_version = -1;
+ _global_save_state = retval;
+ retval->gfx_state = s->gfx_state;
+
+ fh = fopen("state", "r" FO_TEXT);
+ if (!fh) {
+ free(retval);
+ return NULL;
+ }
+
+ /* Backwards compatibility settings */
+ retval->dyn_views = NULL;
+ retval->drop_views = NULL;
+ retval->port = NULL;
+ retval->save_dir_copy_buf = NULL;
+
+ retval->sound_mute = s->sound_mute;
+ retval->sound_volume = s->sound_volume;
+
+ %CFSMLREAD-ATOMIC state_t retval FROM fh ERRVAR read_eof;
+
+ fclose(fh);
+
+ if ((retval->savegame_version < FREESCI_MINIMUM_SAVEGAME_VERSION) ||
+ (retval->savegame_version > FREESCI_CURRENT_SAVEGAME_VERSION)) {
+
+ if (retval->savegame_version < FREESCI_MINIMUM_SAVEGAME_VERSION)
+ sciprintf("Old savegame version detected- can't load\n");
+ else
+ sciprintf("Savegame version is %d- maximum supported is %0d\n", retval->savegame_version, FREESCI_CURRENT_SAVEGAME_VERSION);
+
+ chdir("..");
+ free(retval);
+ return NULL;
+ }
+
+ sfx_exit(&s->sound);
+ _gamestate_unfrob(retval);
+
+ /* Set exec stack base to zero */
+ retval->execution_stack_base = 0;
+ retval->execution_stack_pos = 0;
+
+ /* Now copy all current state information */
+ /* Graphics and input state: */
+ retval->animation_delay = s->animation_delay;
+ retval->animation_granularity = s->animation_granularity;
+ retval->gfx_state = s->gfx_state;
+
+ retval->resmgr = s->resmgr;
+
+ temp = retval->sound.songlib;
+ sfx_init(&retval->sound, retval->resmgr, s->sfx_init_flags);
+ retval->sfx_init_flags = s->sfx_init_flags;
+ song_lib_free(retval->sound.songlib);
+ retval->sound.songlib = temp;
+
+ _reset_graphics_input(retval);
+ reconstruct_stack(retval);
+ reconstruct_scripts(retval, &retval->seg_manager);
+ reconstruct_clones(retval, &retval->seg_manager);
+ retval->game_obj = s->game_obj;
+ retval->script_000 = &retval->seg_manager.heap[script_get_segment(s, 0, SCRIPT_GET_DONT_LOAD)]->data.script;
+ retval->gc_countdown = GC_INTERVAL - 1;
+ retval->save_dir_copy = make_reg(s->sys_strings_segment, SYS_STRING_SAVEDIR);
+ retval->save_dir_edit_offset = 0;
+ retval->sys_strings_segment = find_unique_seg_by_type(&retval->seg_manager, MEM_OBJ_SYS_STRINGS);
+ retval->sys_strings = &(((mem_obj_t *)(GET_SEGMENT(retval->seg_manager, retval->sys_strings_segment, MEM_OBJ_SYS_STRINGS)))->data.sys_strings);
+ sys_strings_restore(retval->sys_strings,
+ s->sys_strings);
+
+ /* Time state: */
+ sci_get_current_time(&(retval->last_wait_time));
+ retval->game_start_time.tv_sec = time(NULL) - retval->game_time;
+ retval->game_start_time.tv_usec = 0;
+
+ /* File IO state: */
+ retval->file_handles_nr = 2;
+ retval->file_handles = (FILE **)sci_calloc(2, sizeof(FILE *));
+
+ /* static parser information: */
+ retval->parser_rules = s->parser_rules;
+ retval->parser_words_nr = s->parser_words_nr;
+ retval->parser_words = s->parser_words;
+ retval->parser_suffices_nr = s->parser_suffices_nr;
+ retval->parser_suffices = s->parser_suffices;
+ retval->parser_branches_nr = s->parser_branches_nr;
+ retval->parser_branches = s->parser_branches;
+
+ /* static VM/Kernel information: */
+ retval->selector_names_nr = s->selector_names_nr;
+ retval->selector_names = s->selector_names;
+ retval->kernel_names_nr = s->kernel_names_nr;
+ retval->kernel_names = s->kernel_names;
+ retval->kfunct_table = s->kfunct_table;
+ retval->kfunct_nr = s->kfunct_nr;
+ retval->opcodes = s->opcodes;
+
+ memcpy(&(retval->selector_map), &(s->selector_map), sizeof(selector_map_t));
+
+ retval->max_version = retval->version;
+ retval->min_version = retval->version;
+ retval->parser_base = make_reg(s->sys_strings_segment, SYS_STRING_PARSER_BASE);
+
+ /* Copy breakpoint information from current game instance */
+ retval->have_bp = s->have_bp;
+ retval->bp_list = s->bp_list;
+
+ retval->debug_mode = s->debug_mode;
+
+ retval->resource_dir = s->resource_dir;
+ retval->work_dir = s->work_dir;
+ retval->kernel_opt_flags = 0;
+ retval->have_mouse_flag = s->have_mouse_flag;
+
+ retval->successor = NULL;
+ retval->pic_priority_table = (int*)gfxop_get_pic_metainfo(retval->gfx_state);
+ retval->game_name = sci_strdup(obj_get_name(retval, retval->game_obj));
+
+ retval->sound.it = NULL;
+ retval->sound.flags = s->sound.flags;
+ retval->sound.song = NULL;
+ retval->sound.suspended = s->sound.suspended;
+ retval->sound.debug = s->sound.debug;
+ reconstruct_sounds(retval);
+
+ chdir ("..");
+
+ return retval;
+}
diff --git a/engines/sci/engine/sci_graphics.h b/engines/sci/engine/sci_graphics.h
new file mode 100644
index 0000000000..74a26b0490
--- /dev/null
+++ b/engines/sci/engine/sci_graphics.h
@@ -0,0 +1,44 @@
+/***************************************************************************
+ sci_graphics.h Copyright (C) 2000 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>
+
+***************************************************************************/
+
+/* SCI constants and definitions */
+
+#ifndef _SCI_GRAPHICS_H_
+#define _SCI_GRAPHICS_H_
+
+#define MAX_TEXT_WIDTH_MAGIC_VALUE 192
+/* This is the real width of a text with a specified width of 0 */
+
+#define SELECTOR_STATE_SELECTABLE 1
+#define SELECTOR_STATE_FRAMED 2
+#define SELECTOR_STATE_DISABLED 4
+#define SELECTOR_STATE_SELECTED 8
+/* Internal states */
+#define SELECTOR_STATE_DITHER_FRAMED 0x1000
+
+
+#endif /* !_SCI_GRAPHICS_H_ */
diff --git a/engines/sci/engine/scriptconsole.c b/engines/sci/engine/scriptconsole.c
new file mode 100644
index 0000000000..661fa253e9
--- /dev/null
+++ b/engines/sci/engine/scriptconsole.c
@@ -0,0 +1,1342 @@
+/***************************************************************************
+ 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]
+
+***************************************************************************/
+/* Second half of the console implementation: VM dependent stuff */
+/* Remember, it doesn't have to be fast. */
+
+#include <sci_memory.h>
+#include <engine.h>
+#ifdef SCI_CONSOLE
+
+state_t *con_gamestate = NULL;
+
+/***************************************************************************/
+/* console commands */
+
+static int c_version(struct _state *s); /* displays the package and version number */
+static int c_list(struct _state *s); /* lists various types of things */
+static int c_man(struct _state *s); /* 'manual page' */
+static int c_set(struct _state *s); /* sets an int variable */
+static int c_print(struct _state *s); /* prints a variable */
+static int c_size(struct _state *s); /* displays the size of a resource */
+static int c_dump(struct _state *s); /* gives a hex dump of a resource */
+static int c_objinfo(struct _state *s); /* shows some info about one class */
+static int c_objmethods(struct _state *s); /* Disassembles all methods of a class */
+static int c_hexgrep(struct _state *s); /* Searches a string in one resource or resource class */
+static int c_selectornames(struct _state *s); /* Displays all selector names */
+static int c_kernelnames(struct _state *s); /* Displays all kernel function names */
+static int c_dissectscript(struct _state *s); /* Splits a script into objects and explains them */
+
+typedef struct {
+ const char *name;
+ const char *description;
+} cmd_mm_entry_t; /* All later structures must "extend" this */
+
+typedef cmd_mm_entry_t cmd_page_t; /* Simple info page */
+
+typedef struct {
+ const char *name;
+ const char *description;
+ int (*command) (state_t *);
+ const char *param;
+} cmd_command_t;
+
+typedef struct
+{
+ const char *name;
+ const char *description;
+ union {
+ int *intp;
+ char **charpp;
+ reg_t *reg;
+ } var;
+} cmd_var_t;
+
+
+typedef void printfunc_t(cmd_mm_entry_t *data, int full);
+
+typedef struct {
+ const char *name;
+ void *data; /* cmd_mm_entry_t */
+ size_t size_per_entry;
+ printfunc_t *print;
+ int entries; /* Number of used entries */
+ int allocated; /* Number of allocated entries */
+} cmd_mm_struct_t;
+
+#define CMD_MM_ENTRIES 3 /* command console memory and manual page manager */
+#define CMD_MM_DEFAULT_ALLOC 4 /* Number of table entries to allocate per default */
+
+#define CMD_MM_CMD 0 /* Commands */
+#define CMD_MM_VAR 1 /* Variables */
+#define CMD_MM_DOC 2 /* Misc. documentation */
+
+static const char *cmd_mm_names[CMD_MM_ENTRIES] = {
+ "Commands",
+ "Variables",
+ "Documentation"
+};
+static size_t cmd_mm_sizes_per_entry[CMD_MM_ENTRIES] = {
+ sizeof(cmd_command_t),
+ sizeof(cmd_var_t),
+ sizeof(cmd_page_t)
+};
+
+
+static void _cmd_print_command(cmd_mm_entry_t *data, int full);
+static void _cmd_print_var(cmd_mm_entry_t *data, int full);
+static void _cmd_print_page(cmd_mm_entry_t *data, int full);
+
+static printfunc_t *cmd_mm_printers[CMD_MM_ENTRIES] = {
+ _cmd_print_command,
+ _cmd_print_var,
+ _cmd_print_page
+};
+
+static cmd_mm_struct_t cmd_mm[CMD_MM_ENTRIES];
+
+static int _cmd_initialized = 0;
+static int _lists_need_sorting = 0;
+
+unsigned int cmd_paramlength;
+cmd_param_t *cmd_params;
+
+
+/********** dmalloc functions **********/
+
+#ifdef WITH_DMALLOC
+
+int
+c_dm_stats (state_t * s)
+{
+ dmalloc_log_stats();
+ return 0;
+}
+
+int
+c_dm_log_unfreed (state_t * s)
+{
+ dmalloc_log_unfreed();
+ return 0;
+}
+
+int
+c_dm_verify (state_t * s)
+{
+ unsigned long pointer_var;
+ void *ptr;
+
+ pointer_var = strtoul (cmd_params[0].str, NULL, 0);
+ ptr = (void *) pointer_var;
+
+ dmalloc_verify (ptr);
+
+ return 0;
+}
+
+int
+c_dm_debug (state_t * s)
+{
+ if (cmd_paramlength) {
+ long newval = strtol (cmd_params[0].str, NULL, 0);
+
+ sciprintf ("Setting dmalloc_debug(%ld)\n", newval);
+ dmalloc_debug (newval);
+ }
+ else
+ sciprintf ("dmalloc_debug is at 0x%lx\n", dmalloc_debug_current());
+ return 0;
+}
+
+int
+c_dm_mark (state_t * s)
+{
+ unsigned long mark = dmalloc_mark();
+
+ dmalloc_message ("------------- MARK 0x%lx ---------------\n", mark);
+ sciprintf ("mark 0x%lx\n", mark);
+ return 0;
+}
+
+int
+c_dm_chmark (state_t * s)
+{
+ unsigned long mark = strtoul (cmd_params[0].str, NULL, 0);
+ sciprintf ("Checking mark 0x%lx\n", mark);
+ dmalloc_message ("--- Mark 0x%lx:\n", mark);
+ dmalloc_log_changed (mark, 1, 1, 1);
+ return 0;
+}
+
+int
+c_dm_print (state_t * s)
+{
+ int i;
+ for (i = 0; i < cmd_paramlength; i++)
+ dmalloc_message ("%s\n", cmd_params[i].str);
+ return 0;
+}
+
+void
+con_init_dmalloc()
+{
+ con_hook_command (c_dm_stats, "dm_stats", "",
+ "Prints memory usage stats\n to the dmalloc output file\n\n dm_stats");
+ con_hook_command (c_dm_log_unfreed, "dm_log_unfreed", "",
+ "Prints unfreed pointer\n information to the dmalloc\n output file\n\n"
+ "USAGE\n\n dm_log_unfreed");
+ con_hook_command (c_dm_verify, "dm_verify", "s",
+ "Verifies one pointer,\n prints output to dmalloc file\n\nUSAGE\n\n"
+ " dm_verify <ptr>\n dm_verify 0\n\n 'dm_verify 0' will verify\n ALL current pointers.\n");
+ con_hook_command (c_dm_debug, "dm_debug", "s*",
+ "Sets the dmalloc debug\n state or displays it\n\nUSAGE\n\n dm_debug <mode>\n dm_debug");
+ con_hook_command (c_dm_mark, "dm_mark", "",
+ "Gets a mark describing\n the current heap state\n\nUSAGE\n\n dm_mark\n\n"
+ " The mark is written to the\n dmalloc output file and\n to sci output.\n\nSEE ALSO\n\n cm_chmark");
+ con_hook_command (c_dm_chmark, "dm_chmark", "s",
+ "Checks changes in the\n heap state since a certain\n mark was retreived\n\n"
+ "USAGE\n\n c_dm_chmark <mark>\n\n Output is written to the\n dmalloc output file.\n\n Use dm_mark to retreive a\n"
+ " mark.\n\nSEE ALSO\n\n c_dm_mark");
+ con_hook_command (c_dm_print, "dm_print", "s*",
+ "Prints something to the\n dmalloc output.\n\nUSAGE\n\n dm_print <text>");
+}
+#else /* WITH_DMALLOC */
+
+void
+con_init_dmalloc (void)
+{
+}
+
+#endif /* WITH_DMALLOC */
+
+
+void
+_cmd_exit (void)
+{
+ int t;
+
+ for (t = 0; t < CMD_MM_ENTRIES; t++)
+ free(cmd_mm[t].data);
+}
+
+static cmd_mm_entry_t *
+cmd_mm_find(char *name, int type)
+{
+ int i;
+
+ for (i = 0; i < cmd_mm[type].entries; i++)
+ if (!strcmp(((cmd_mm_entry_t *)((byte *)cmd_mm[type].data + i * cmd_mm[type].size_per_entry))->name, name))
+ return ((cmd_mm_entry_t *)((byte *)cmd_mm[type].data + i * cmd_mm[type].size_per_entry));
+
+ return NULL;
+}
+
+static int
+_cmd_mm_comp (const void *a, const void *b)
+{
+ return strcmp (((cmd_mm_entry_t *) a)->name, ((cmd_mm_entry_t *) b)->name);
+}
+
+void
+con_sort_all (void)
+{
+ int i;
+
+ for (i = 0; i < CMD_MM_ENTRIES; i++)
+ if (cmd_mm[i].entries && _lists_need_sorting & (1 << i))
+ qsort (cmd_mm[i].data, cmd_mm[i].entries, cmd_mm[i].size_per_entry,
+ _cmd_mm_comp);
+
+ _lists_need_sorting = 0;
+}
+
+void
+con_init (void)
+{
+ if (!_cmd_initialized) {
+ int i;
+
+ _cmd_initialized = 1;
+ for (i = 0; i < CMD_MM_ENTRIES; i++) {
+ cmd_mm[i].name = cmd_mm_names[i];
+ cmd_mm[i].size_per_entry = cmd_mm_sizes_per_entry[i];
+ cmd_mm[i].entries = 0;
+ cmd_mm[i].allocated = CMD_MM_DEFAULT_ALLOC;
+ cmd_mm[i].data = sci_calloc(cmd_mm[i].allocated, cmd_mm[i].size_per_entry);
+ cmd_mm[i].print = cmd_mm_printers[i];
+ }
+
+ atexit (_cmd_exit);
+
+ /* Hook up some commands */
+ con_hook_command (&c_version, "version", "",
+ "Displays the version number");
+ con_hook_command (&c_list, "list", "s*",
+ "Lists various things (try 'list')");
+ con_hook_command (&c_man, "man", "s",
+ "Gives a short description of something");
+ con_hook_command (&c_print, "print", "s", "Prints an int variable");
+ con_hook_command (&c_set, "set", "si", "Sets an int variable");
+ con_hook_command (&c_size, "size", "si",
+ "Displays the size of a resource");
+ con_hook_command (&c_dump, "dump", "si", "HexDumps a resource");
+ con_hook_command (&c_hexgrep, "hexgrep", "shh*",
+ "Searches some resources for a\n"
+ " particular sequence of bytes, re-\n presented"
+ " as hexadecimal numbers.\n\n"
+ "EXAMPLES:\n hexgrep script e8 03 c8 00\n"
+ " hexgrep pic.042 fe");
+ con_hook_command (&c_dissectscript, "dissectscript", "i",
+ "Examines a script.");
+
+ con_hook_page("addresses",
+ "Passing address parameters\n\n"
+ " Address parameters may be passed in one of\n"
+ " three forms:\n"
+ " - ssss:oooo -- where 'ssss' denotes a\n"
+ " segment and 'oooo' an offset. Example:\n"
+ " \"a:c5\" would address something in seg-\n"
+ " ment 0xa at offset 0xc5.\n"
+ " - &scr:oooo -- where 'scr' is a script number\n"
+ " and oooo an offset within that script; will\n"
+ " fail if the script is not currently loaded\n"
+ " - $REG -- where 'REG' is one of 'PC', 'ACC',\n"
+ " 'PREV' or 'OBJ': References the address\n"
+ " indicated by the register of this name.\n"
+ " - $REG+n (or -n) -- Like $REG, but modifies\n"
+ " the offset part by a specific amount (which\n"
+ " is specified in hexadecimal).\n"
+ " - ?obj -- Looks up an object with the specified\n"
+ " name, uses its address. This will abort if\n"
+ " the object name is ambiguous; in that case,\n"
+ " a list of addresses and indices is provided.\n"
+ " ?obj.idx may be used to disambiguate 'obj'\n"
+ " by the index 'idx'.\n");
+
+ con_init_dmalloc();
+
+ con_hook_int (&con_passthrough, "con_passthrough",
+ "scicon->stdout passthrough");
+ }
+}
+
+static inline int
+clone_is_used(clone_table_t *t, int idx)
+{
+ return ENTRY_IS_VALID(t, idx);
+}
+
+int
+parse_reg_t(state_t *s, char *str, reg_t *dest)
+{ /* Returns 0 on success */
+ int rel_offsetting = 0;
+ char *offsetting = NULL;
+ /* Non-NULL: Parse end of string for relative offsets */
+ char *endptr;
+
+ if (!s) {
+ sciprintf("Addresses can only be parsed if a global state is present");
+ return 1; /* Requires a valid state */
+ }
+
+ if (*str == '$') { /* Register */
+ rel_offsetting = 1;
+
+ if (!strncasecmp(str+1, "PC", 2)) {
+ *dest = s->execution_stack[s->execution_stack_pos].addr.pc;
+ offsetting = str + 3;
+ } else if (!strncasecmp(str+1, "P", 1)) {
+ *dest = s->execution_stack[s->execution_stack_pos].addr.pc;
+ offsetting = str + 2;
+ } else if (!strncasecmp(str+1, "PREV", 4)) {
+ *dest = s->r_prev;
+ offsetting = str + 5;
+ } else if (!strncasecmp(str+1, "ACC", 3)) {
+ *dest = s->r_acc;
+ offsetting = str + 4;
+ } else if (!strncasecmp(str+1, "A", 1)) {
+ *dest = s->r_acc;
+ offsetting = str + 2;
+ } else if (!strncasecmp(str+1, "OBJ", 3)) {
+ *dest = s->execution_stack[s->execution_stack_pos].objp;
+ offsetting = str + 4;
+ } else if (!strncasecmp(str+1, "O", 1)) {
+ *dest = s->execution_stack[s->execution_stack_pos].objp;
+ offsetting = str + 2;
+ } else return 1; /* No matching register */
+
+ if (!*offsetting)
+ offsetting = NULL;
+ else if (*offsetting != '+' && *offsetting != '-')
+ return 1;
+ } else if (*str == '&') {
+ int script_nr;
+ /* Look up by script ID */
+ char *colon = strchr(str, ':');
+
+ if (!colon)
+ return 1;
+ *colon = 0;
+ offsetting = colon+1;
+
+ script_nr = strtol(str+1, &endptr, 10);
+
+ if (*endptr)
+ return 1;
+
+ dest->segment = sm_seg_get(&s->seg_manager, script_nr);
+
+ if (!dest->segment) {
+ return 1;
+ }
+ } else if (*str == '?') {
+ int index = -1;
+ int times_found = 0;
+ char *str_objname;
+ char *str_suffix;
+ char suffchar = 0; /* Supress spurious -Wall warning */
+ int i;
+ /* Parse obj by name */
+
+ str_objname = strchr(str, '+');
+ str_suffix = strchr(str, '-');
+ if (str_objname < str_suffix)
+ str_suffix = str_objname;
+ if (str_suffix) {
+ suffchar = (*str_suffix);
+ *str_suffix = 0;
+ }
+
+ str_objname = strchr(str, '.');
+
+ if (str_objname) {
+ *str_objname = 0;
+ index = strtol(str_objname+1, &endptr, 16);
+ if (*endptr)
+ return -1;
+ }
+
+ str_objname = str + 1;
+
+ /* Now all values are available; iterate over all objects. */
+ for (i = 0; i < s->seg_manager.heap_size; i++) {
+ mem_obj_t *mobj = s->seg_manager.heap[i];
+ int idx = 0;
+ int max_index = 0;
+
+ if (mobj) {
+ if (mobj->type == MEM_OBJ_SCRIPT)
+ max_index = mobj->data.script.objects_nr;
+ else if (mobj->type == MEM_OBJ_CLONES)
+ max_index = mobj->data.clones.max_entry;
+ }
+
+ while (idx < max_index) {
+ int valid = 1;
+ object_t *obj = NULL; /* Surpress spurious warning */
+ reg_t objpos;
+ objpos.segment = i;
+
+ if (mobj->type == MEM_OBJ_SCRIPT) {
+ obj = mobj->data.script.objects + idx;
+ objpos.offset = obj->pos.offset;
+ } else if (mobj->type == MEM_OBJ_CLONES) {
+ obj = &(mobj->data.clones.table[idx].entry);
+ objpos.offset = idx;
+ valid = clone_is_used(&mobj->data.clones, idx);
+ }
+
+ if (valid) {
+ char *objname = (char *) obj->base
+ + obj->variables[SCRIPT_NAME_SELECTOR].offset;
+ if (!strcmp(objname, str_objname)) {
+ /* Found a match! */
+ if (index < 0 ||
+ times_found == index)
+ *dest = objpos;
+ else if (times_found < 0 && index) {
+
+ if (index == 1) {
+ /* First time we realized
+ ** the ambiguity */
+ sciprintf("Ambiguous:\n");
+ sciprintf(" %3x: ["PREG"] %s\n", 0, PRINT_REG(*dest), str_objname);
+ }
+ sciprintf(" %3x: ["PREG"] %s\n", index, PRINT_REG(objpos), str_objname);
+ }
+ ++times_found;
+ }
+ }
+
+ ++idx;
+ }
+
+ }
+
+ if (!times_found)
+ return 1;
+
+ if (times_found > 1
+ && index < 0) {
+ sciprintf("Ambiguous: Aborting.\n");
+ return 1; /* Ambiguous */
+ }
+
+ if (times_found <= index)
+ return 1; /* Not found */
+
+ offsetting = str_suffix;
+ if (offsetting)
+ *str_suffix = suffchar;
+ rel_offsetting = 1;
+ } else {
+ char *colon = strchr(str, ':');
+
+ if (!colon) {
+ offsetting = str;
+ dest->segment = 0;
+ } else {
+ *colon = 0;
+ offsetting = colon+1;
+
+ dest->segment = strtol(str, &endptr, 16);
+ if (*endptr)
+ return 1;
+ }
+ }
+ if (offsetting) {
+ int val = strtol(offsetting, &endptr, 16);
+
+ if (rel_offsetting)
+ dest->offset += val;
+ else
+ dest->offset = val;
+
+ if (*endptr)
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+con_parse (state_t *s, const char *command)
+{
+ int quote = 0; /* quoting? */
+ int done = 0; /* are we done yet? */
+ int cdone = 0; /* Done with the current command? */
+ const char *paramt; /* parameter types */
+ char *cmd = (command && command[0]) ? (char *) sci_strdup (command) :
+ (char *) sci_strdup(" ");
+ char *_cmd = cmd;
+ int pos = 0;
+
+ if (!_cmd_initialized)
+ con_init();
+
+ while (!done) {
+ cmd_command_t *command_todo;
+ int onvar = 1; /* currently working on a variable? */
+ unsigned int parammem = 0;
+ unsigned int i;
+ cdone = 0;
+ pos = 0;
+
+ /* cmd_params = sci_realloc(cmd_params, parammem); */
+ cmd_paramlength = 0;
+
+ while (*cmd == ' ')
+ cmd++;
+
+ while (!cdone) {
+ switch (cmd[pos]) {
+ case 0:
+ done = 1;
+ case ';':
+ if (!quote)
+ cdone = 1;
+ case ' ':
+ if (!quote)
+ cmd[pos] = onvar = 0;
+ break;
+ case '\\': /* don't check next char for special meaning */
+ memmove (cmd + pos, cmd + pos + 1, strlen (cmd + pos) - 1);
+ break;
+ case '"':
+ quote ^= 1;
+ memmove (cmd + pos, cmd + pos + 1, strlen (cmd + pos));
+ pos--;
+ break;
+ default:
+ if (!onvar) {
+ onvar = 1;
+ if (cmd_paramlength == parammem)
+ cmd_params = (cmd_param_t*)sci_realloc(cmd_params,
+ sizeof (cmd_param_t)
+ * (parammem += 8));
+
+ cmd_params[cmd_paramlength].str = cmd + pos;
+
+ cmd_paramlength++;
+ }
+ break;
+ }
+ pos++;
+ }
+
+ if (quote)
+ sciprintf ("unbalanced quotes\n");
+ else if (strcmp (cmd, "") != 0) {
+
+ command_todo = (cmd_command_t *) cmd_mm_find(cmd, CMD_MM_CMD);
+
+ if (!command_todo)
+ sciprintf ("%s: not found\n", cmd);
+ else {
+ unsigned int minparams;
+ int need_state = 0;
+
+ paramt = command_todo->param;
+ if (command_todo->param[0] == '!') {
+ need_state = 1;
+ paramt++;
+ }
+
+ minparams = strlen (paramt);
+
+ if ((paramt[0] != 0) && (paramt[strlen (paramt) - 1] == '*'))
+ minparams -= 2;
+
+ if (cmd_paramlength < minparams)
+ sciprintf ("%s: needs more than %d parameters\n",
+ cmd, cmd_paramlength);
+
+ else if ((cmd_paramlength > strlen (paramt))
+ && ((strlen (paramt) == 0)
+ || paramt[strlen (paramt) - 1] != '*'))
+ sciprintf ("%s: too many parameters", cmd);
+ else {
+ int do_execute = !need_state || s; /* /me wants an
+ ** implication arrow */
+ char paramtype;
+ int paramtypepos = 0;
+ char *endptr;
+
+ for (i = 0; i < cmd_paramlength; i++) {
+ paramtype = paramt[paramtypepos];
+
+ if ((paramt[paramtypepos + 1])
+ && (paramt[paramtypepos + 1] != '*'))
+ paramtypepos++;
+ /* seek next param type unless end of string or '* ' */
+
+ switch (paramtype) {
+ /* Now turn the parameters into variables of the appropriate types,
+ ** unless they're strings, and store them in the global cmd_params[]
+ ** structure */
+
+ case 'a': {
+ char *oldname = cmd_params[i].str;
+ if (parse_reg_t(s, oldname,
+ &(cmd_params[i].reg))) {
+ sciprintf("%s: '%s' is not an address or object\n", cmd, oldname);
+ do_execute = 0;
+ }
+ break;
+ }
+
+ case 'i': {
+ char *orgstr = cmd_params[i].str;
+
+ cmd_params[i].val = strtol (orgstr, &endptr, 0);
+ if (*endptr != '\0') {
+ do_execute = 0;
+ sciprintf ("%s: '%s' is not an int\n", cmd, orgstr);
+ }
+ }
+ break;
+
+ case 'h': {
+ char *orgstr = cmd_params[i].str;
+
+ cmd_params[i].val = strtol (orgstr, &endptr, 16);
+
+ if (*endptr != '\0') {
+ do_execute = 0;
+ sciprintf ("%s: '%s' is not a hex number\n", cmd, orgstr);
+ }
+
+ cmd_params[i].val &= 0xff; /* Clip hex numbers to 0x00 ... 0xff */
+ }
+ break;
+
+ case 's':
+ break;
+
+ default:
+ fprintf(stderr, "Internal error: Heap corruption or prior assertion failed:\n"
+ "Unknown parameter type '%c' for funtion\n", paramtype);
+
+ }
+ }
+
+ if (do_execute) {
+ command_todo->command(s);
+ } else fprintf(stderr, "Skipping command...\n");
+ }
+ }
+ }
+ cmd += pos;
+ }
+
+ free (_cmd);
+ if (cmd_params)
+ free (cmd_params);
+ cmd_params = NULL;
+
+}
+
+/* (unused)
+static cmd_mm_entry_t *
+con_iterate_entry(int ID, int *counter)
+{
+ byte *retval;
+ con_init();
+
+ if (*counter >= cmd_mm[ID].entries)
+ return 0;
+ retval = cmd_mm[ID].data;
+ retval += (*counter) * cmd_mm[ID].size_per_entry;
+
+ (*counter)++;
+
+ return (cmd_mm_entry_t *) retval;
+}
+*/
+
+static cmd_mm_entry_t *
+con_alloc_page_entry(int ID)
+{
+ int entry;
+ con_init();
+
+ if (cmd_mm[ID].entries >= cmd_mm[ID].allocated) {
+ int nextsize = cmd_mm[ID].allocated;
+ if (nextsize >= 64)
+ nextsize += 16;
+ else
+ nextsize <<= 1;
+
+ cmd_mm[ID].data = sci_realloc(cmd_mm[ID].data,
+ nextsize * cmd_mm[ID].size_per_entry);
+ cmd_mm[ID].allocated = nextsize;
+ }
+
+ _lists_need_sorting |= (1 << ID);
+
+ entry = cmd_mm[ID].entries++;
+ return (cmd_mm_entry_t *) (((byte *)cmd_mm[ID].data)
+ + entry * cmd_mm[ID].size_per_entry);
+}
+
+int
+con_hook_page(const char *name, const char *body)
+{
+ cmd_page_t *page = (cmd_page_t *) con_alloc_page_entry(CMD_MM_DOC);
+
+ page->name = name;
+ page->description = body;
+
+ return 0;
+}
+
+int
+con_hook_command (int command (state_t *), const char *name, const char *param,
+ const char *description)
+{
+ cmd_command_t *cmd = NULL;
+ unsigned int i;
+
+
+ if (NULL == name) {
+ sciprintf("console.c: con_hook_command(): NULL passed for name\n");
+ return -1;
+ }
+
+ if (command == NULL)
+ return 1;
+
+ if (param == NULL)
+ param = "";
+
+ if (description == NULL)
+ description = "";
+
+ i = 0;
+ while (param[i] != 0) {
+ switch (param[i]) {
+ case '*':
+ if (param[i + 1] != 0)
+ return 1;
+ if (i == 0)
+ return 1;
+ case 'h':
+ case '!':
+ case 'i':
+ case 'a':
+ case 's':
+ case 'r':
+ break;
+ default:
+ return 1;
+ }
+ i++;
+ }
+ cmd = (cmd_command_t *) con_alloc_page_entry(CMD_MM_CMD);
+
+ cmd->command = command;
+ cmd->name = name;
+ cmd->param = param;
+ cmd->description = description;
+
+ return 0;
+}
+
+
+int
+con_hook_int (int *pointer, const char *name, const char *description)
+{
+ cmd_var_t *var;
+
+ if (pointer == NULL)
+ return 1;
+
+ if (description == NULL)
+ description = "";
+
+ var = (cmd_var_t *) con_alloc_page_entry(CMD_MM_VAR);
+
+ var->var.intp = pointer;
+ var->name = name;
+ var->description = description;
+
+ return 0;
+}
+
+
+/***************************************************************************
+ * Console commands and support functions
+ ***************************************************************************/
+
+
+static int
+get_resource_number (char *resid)
+/* Gets the resource number of a resource string, or returns -1 */
+{
+ int i, res = -1;
+
+ for (i = 0; i < sci_invalid_resource; i++)
+ if (strcmp (sci_resource_types[i], resid) == 0)
+ res = i;
+ return res;
+}
+
+static int
+c_version (state_t * s)
+{
+ if (NULL == s) {
+ sciprintf("console.c: c_version: NULL passed for parameter s\n");
+ return -1;
+ }
+
+ sciprintf ("FreeSCI, version " VERSION "\n");
+ sciprintf ("Resource file version: %s\n", sci_version_types[s->resmgr->sci_version]);
+ sciprintf ("Emulated interpreter version: %d.%03d.%03d\n",
+ SCI_VERSION_MAJOR(s->version),
+ SCI_VERSION_MINOR(s->version),
+ SCI_VERSION_PATCHLEVEL(s->version));
+
+ return 0;
+}
+
+static int
+c_list_words(state_t *s)
+{
+ word_t **words;
+ int words_nr;
+ int i;
+
+ words = vocab_get_words(s->resmgr, &words_nr);
+
+ if (!words) {
+ sciprintf("No vocabulary.\n");
+ return 1;
+ }
+
+ for (i = 0; i < words_nr; i++)
+ sciprintf("%4d: %03x [%03x] %s\n",
+ i,
+ words[i]->w_class,
+ words[i]->group,
+ words[i]->word);
+
+ vocab_free_words(words, words_nr);
+ return 0;
+}
+
+int
+c_list_suffices(state_t *s)
+{
+ suffix_t **suffices;
+ int suffices_nr;
+ int i;
+ char word_buf[256], alt_buf[256];
+
+ suffices = vocab_get_suffices(s->resmgr, &suffices_nr);
+
+ if (!suffices) {
+ sciprintf("No suffix vocabulary.\n");
+ return 1;
+ }
+
+ for (i = 0; i < suffices_nr; i++) {
+ suffix_t *suf = suffices[i];
+
+ strncpy(word_buf, suf->word_suffix,
+ suf->word_suffix_length);
+ word_buf[suf->word_suffix_length] = 0;
+ strncpy(alt_buf, suf->alt_suffix,
+ suf->alt_suffix_length);
+ alt_buf[suf->alt_suffix_length] = 0;
+
+ sciprintf("%4d: (%03x) -%12s => -%12s (%03x)\n",
+ i, suf->class_mask, word_buf,
+ alt_buf, suf->result_class);
+ }
+
+ vocab_free_suffices(s->resmgr, suffices, suffices_nr);
+ return 0;
+}
+
+static void
+_cmd_print_command(cmd_mm_entry_t *data, int full)
+{
+ const char *paramseeker = ((cmd_command_t *) data)->param;
+
+ if (full) {
+ sciprintf ("SYNOPSIS\n\n %s ", data->name, paramseeker);
+
+ while (*paramseeker) {
+ switch (*paramseeker) {
+ case '!': break;
+ case 'i':
+ sciprintf (" (int)");
+ break;
+ case 'a':
+ sciprintf (" (addr)");
+ break;
+ case 's':
+ sciprintf (" (string)");
+ break;
+ case 'h':
+ sciprintf (" (hexbyte)");
+ break;
+ case '*':
+ sciprintf ("*");
+ break;
+ default:
+ sciprintf (" (Unknown(%c))", *paramseeker);
+ }
+ paramseeker++;
+ }
+
+ sciprintf("\n\nDESCRIPTION\n\n %s",
+ data->description);
+ } else
+ sciprintf(" %s", data->name);
+}
+
+static void
+_cmd_print_var(cmd_mm_entry_t *data, int full)
+{
+ cmd_var_t *var = (cmd_var_t *) data;
+ if (full)
+ sciprintf ("VALUE\n\n");
+ sciprintf(" %s = %d\n", var->name, *(var->var.intp));
+
+ if (full)
+ sciprintf("\n\nDESCRIPTION\n\n %s",
+ data->description);
+}
+
+static void
+_cmd_print_page(cmd_mm_entry_t *data, int full)
+{
+ if (full)
+ sciprintf("\n\nDESCRIPTION\n\n %s\n",
+ data->description);
+ else sciprintf("%s\n", data->name);
+}
+
+static int
+c_list (state_t * s)
+{
+ if (_lists_need_sorting)
+ con_sort_all();
+
+ if (cmd_paramlength == 0)
+ {
+ sciprintf ("usage: list [type]\nwhere type is one of the following:\n"
+ "cmds - lists all commands\n"
+ "vars - lists all variables\n"
+ "docs - lists all misc. documentation\n"
+ "\n"
+ "restypes - lists all resource types\n"
+ "selectors - lists all selectors\n"
+ "syscalls - lists all kernel functions\n"
+ "words - lists all kernel words\n"
+ "suffixes - lists all suffix replacements\n"
+ "[resource] - lists all [resource]s");
+ }
+ else if (cmd_paramlength == 1) {
+ const char *mm_subsects[3] = {"cmds", "vars", "docs"};
+ int mm_found = -1;
+ int i;
+
+ for (i = 0; i < 3; i++)
+ if (mm_subsects[i] && !strcmp(mm_subsects[i], cmd_params[0].str))
+ mm_found = i;
+
+ if (mm_found >= 0)
+ for (i = 0; i < cmd_mm[mm_found].entries; i++)
+ cmd_mm[mm_found].print((cmd_mm_entry_t *)
+ (((byte *)cmd_mm[mm_found].data)
+ + i * cmd_mm[mm_found].size_per_entry), 0);
+ else {
+ if (!s) {
+ sciprintf("You need a state to do that!\n");
+ return 1;
+ }
+
+ if (!strcmp("selectors", cmd_params[0].str))
+ return c_selectornames(s);
+ else if (!strcmp("syscalls", cmd_params[0].str))
+ return c_kernelnames(s);
+ else if (!strcmp("suffixes", cmd_params[0].str)
+ || !strcmp("suffices", cmd_params[0].str)
+ || !strcmp("sufficos", cmd_params[0].str))
+ /* sufficos: Accusative Plural of 'suffix' */
+ return c_list_suffices(s);
+ else if (!strcmp("words", cmd_params[0].str))
+ return c_list_words(s);
+ else if (strcmp ("restypes", cmd_params[0].str) == 0) {
+ int i;
+ for (i = 0; i < sci_invalid_resource; i++)
+ sciprintf ("%s\n", sci_resource_types[i]);
+ }
+ else {
+ int res = get_resource_number (cmd_params[0].str);
+ if (res == -1)
+ sciprintf ("Unknown resource type: '%s'\n", cmd_params[0].str);
+ else {
+ int i;
+ for (i = 0; i < sci_max_resource_nr[s->resmgr->sci_version]; i++)
+ if (scir_test_resource (s->resmgr, res, i))
+ sciprintf ("%s.%03d\n", sci_resource_types[res], i);
+ }
+ }
+ }
+ }
+ else
+ sciprintf ("list can only be used with one argument");
+ return 0;
+}
+
+
+static int
+c_man (state_t * s)
+{
+ int section = 0;
+ unsigned int i;
+ char *name = cmd_params[0].str;
+ char *c = strchr(name, '.');
+ cmd_mm_entry_t *entry;
+
+ if (c) {
+ *c = 0;
+ section = atoi(c + 1);
+ }
+
+ if (section < 0 || section >= CMD_MM_ENTRIES) {
+ sciprintf("Invalid section %d\n",
+ section);
+ return 1;
+ }
+
+ sciprintf("section:%d\n", section);
+ if (section)
+ entry = cmd_mm_find(name, section - 1);
+ else
+ for (i = 0; i < CMD_MM_ENTRIES && !section; i++) {
+ if ((entry = cmd_mm_find(name, i)))
+ section = i+1;
+ }
+
+ if (!entry) {
+ sciprintf("No manual entry\n");
+ return 1;
+ }
+
+ sciprintf ("-- %s: %s.%d\n", cmd_mm[section - 1].name, name, section);
+ cmd_mm[section - 1].print(entry, 1);
+
+ return 0;
+}
+
+static int
+c_set (state_t * s)
+{
+ cmd_var_t *var = (cmd_var_t *) cmd_mm_find(cmd_params[0].str, CMD_MM_VAR);
+
+ if (var)
+ *(var->var.intp) = cmd_params[1].val;
+
+ return 0;
+}
+
+static int
+c_print (state_t * s)
+{
+ cmd_var_t *var = (cmd_var_t *) cmd_mm_find(cmd_params[0].str, CMD_MM_VAR);
+
+ if (var)
+ sciprintf ("%d", *(var->var.intp));
+ else
+ sciprintf ("Not defined.");
+
+ return 0;
+}
+
+
+static int
+c_size (state_t * s)
+{
+ int res = get_resource_number (cmd_params[0].str);
+ if (res == -1)
+ sciprintf ("Resource type '%s' is not valid\n", cmd_params[0].str);
+ else
+ {
+ resource_t *resource = scir_find_resource(s->resmgr, res, cmd_params[1].val, 0);
+ if (resource)
+ {
+ sciprintf ("Size: %d\n", resource->size);
+ }
+ else
+ sciprintf ("Resource %s.%03d not found\n", cmd_params[0].str,
+ cmd_params[1].val);
+ }
+ return 0;
+}
+
+
+static int
+c_dump (state_t * s)
+{
+ int res = get_resource_number (cmd_params[0].str);
+
+ if (res == -1)
+ sciprintf ("Resource type '%s' is not valid\n", cmd_params[0].str);
+ else
+ {
+ resource_t *resource = scir_find_resource(s->resmgr, res, cmd_params[1].val, 0);
+ if (resource)
+ sci_hexdump (resource->data, resource->size, 0);
+ else
+ sciprintf ("Resource %s.%03d not found\n", cmd_params[0].str,
+ cmd_params[1].val);
+ }
+ return 0;
+}
+
+
+static int
+c_hexgrep (state_t * s)
+{
+ int i, seeklen, resnr, restype, resmax;
+ unsigned char *seekstr = NULL;
+ resource_t *script = NULL;
+ char *dot = strchr (cmd_params[0].str, '.');
+
+ if (NULL == s)
+ {
+ fprintf(stderr, "console.c: c_hexgrep(): NULL passed for s\r\n");
+ return(-1);
+ }
+
+ seekstr = (unsigned char*)sci_malloc (seeklen = (cmd_paramlength - 1));
+
+ if (NULL == seekstr)
+ {
+ fprintf(stderr, "console.c: c_hexgrep(): malloc failed for seekstr\r\n");
+ return(-1);
+ }
+
+ for (i = 0; i < seeklen; i++)
+ seekstr[i] = (byte)cmd_params[i + 1].val;
+
+ if (dot)
+ {
+ *dot = 0;
+ resmax = resnr = atoi (dot + 1);
+ }
+ else
+ {
+ resnr = 0;
+ resmax = 999;
+ }
+
+ if ((restype = get_resource_number (cmd_params[0].str)) == -1)
+ {
+ sciprintf ("Unknown resource type \"%s\"\n", cmd_params[0].str);
+ free(seekstr);
+ return 1;
+ }
+
+ for (; resnr <= resmax; resnr++)
+ if ((script = scir_find_resource(s->resmgr, restype, resnr, 0)))
+ {
+ int seeker = 0, seekerold = 0;
+ int comppos = 0;
+ int output_script_name = 0;
+
+ while (seeker < script->size)
+ {
+
+ if (script->data[seeker] == seekstr[comppos])
+ {
+ if (comppos == 0)
+ seekerold = seeker;
+
+ comppos++;
+
+ if (comppos == seeklen)
+ {
+ comppos = 0;
+ seeker = seekerold + 1;
+
+ if (!output_script_name)
+ {
+ sciprintf ("\nIn %s.%03d:\n", sci_resource_types[restype], resnr);
+ output_script_name = 1;
+ }
+ sciprintf (" 0x%04x\n", seekerold);
+ }
+ }
+ else
+ comppos = 0;
+
+ seeker++;
+ }
+ }
+
+ free (seekstr);
+
+ return 0;
+}
+
+
+static int
+c_selectornames (state_t * s)
+{
+ int namectr;
+ char **snames = NULL;
+ int seeker = 0;
+
+ if (NULL == s)
+ {
+ sciprintf("console.c: c_selectornames(): NULL passed for parameter s\n");
+ return -1;
+ }
+
+ snames = vocabulary_get_snames (s->resmgr, &namectr, s ? s->version : 0);
+
+ if (!snames)
+ {
+ sciprintf ("No selector name table found!\n");
+ return 1;
+ }
+
+ sciprintf ("Selector names in numeric order:\n");
+ while (snames[seeker])
+ {
+ sciprintf ("%03x: %s\n", seeker, snames[seeker]);
+ seeker++;
+ }
+ vocabulary_free_snames (snames);
+ return 0;
+}
+
+static int
+c_kernelnames (state_t * s)
+{
+ int knamectr;
+ char **knames = vocabulary_get_knames (s->resmgr, &knamectr);
+ int seeker = 0;
+
+ if (NULL == s) {
+ sciprintf("console.c: c_kernelnames NULL passed for parameter s\n");
+ return -1;
+ }
+
+ if (!knames)
+ {
+ sciprintf ("No kernel name table found!\n");
+ return 1;
+ }
+
+ sciprintf ("Syscalls in numeric order:\n");
+ for (seeker = 0; seeker < knamectr; seeker++)
+ sciprintf ("%03x: %s\n", seeker, knames[seeker]);
+
+ vocabulary_free_knames (knames);
+ return 0;
+}
+
+static int
+c_dissectscript (state_t * s)
+{
+ if (NULL == s)
+ {
+ sciprintf("console.c: c_dissectscript(): NULL passed for parameter s\n");
+ return -1;
+ }
+
+ script_dissect (s->resmgr, cmd_params[0].val, s->selector_names, s->selector_names_nr);
+ return 0;
+}
+#endif /* SCI_CONSOLE */
+
+
diff --git a/engines/sci/engine/scriptdebug.c b/engines/sci/engine/scriptdebug.c
new file mode 100644
index 0000000000..1a4b53e342
--- /dev/null
+++ b/engines/sci/engine/scriptdebug.c
@@ -0,0 +1,3887 @@
+/***************************************************************************
+ scriptdebug.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) [jameson@linuxgames.com]
+
+***************************************************************************/
+/* Script debugger functionality. Absolutely not threadsafe. */
+
+#include "gc.h"
+#include <sciresource.h>
+#include <engine.h>
+#include <console.h>
+#include <kdebug.h>
+#include <vocabulary.h>
+#include "kernel_types.h"
+#include <sci_midi.h>
+#include <sci_widgets.h>
+#include <reg_t_hashmap.h>
+
+#ifdef _WIN32
+# include <win32/sci_win32.h>
+# include <windows.h>
+# include <io.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+/* Assume this is a sufficient precondition */
+# include <signal.h>
+#endif
+
+extern int debug_sleeptime_factor;
+int _debugstate_valid = 0; /* Set to 1 while script_debug is running */
+int _debug_step_running = 0; /* Set to >0 to allow multiple stepping */
+int _debug_commands_not_hooked = 1; /* Commands not hooked to the console yet? */
+int _debug_seeking = 0; /* Stepping forward until some special condition is met */
+int _debug_seek_level = 0; /* Used for seekers that want to check their exec stack depth */
+int _debug_seek_special = 0; /* Used for special seeks(1) */
+int _weak_validations = 1; /* Some validation errors are reduced to warnings if non-0 */
+reg_t _debug_seek_reg = NULL_REG_INITIALIZER; /* Used for special seeks(2) */
+
+#define _DEBUG_SEEK_NOTHING 0
+#define _DEBUG_SEEK_CALLK 1 /* Step forward until callk is found */
+#define _DEBUG_SEEK_LEVEL_RET 2 /* Step forward until returned from this level */
+#define _DEBUG_SEEK_SPECIAL_CALLK 3 /* Step forward until a /special/ callk is found */
+#define _DEBUG_SEEK_SO 5 /* Step forward until specified PC (after the send command) and stack depth */
+#define _DEBUG_SEEK_GLOBAL 6 /* Step forward until one specified global variable is modified */
+
+static reg_t *p_pc;
+static stack_ptr_t *p_sp;
+static stack_ptr_t *p_pp;
+static reg_t *p_objp;
+static int *p_restadjust;
+static seg_id_t *p_var_segs;
+static reg_t **p_vars;
+static reg_t **p_var_base;
+static int *p_var_max; /* May be NULL even in valid state! */
+
+static const int MIDI_cmdlen[16] = {0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 2, 2, 2, 1, 1, 2, 0};
+
+int _kdebug_cheap_event_hack = 0;
+int _kdebug_cheap_soundcue_hack = -1;
+
+char inputbuf[256] = "";
+
+#define LOOKUP_SPECIES(species) (\
+ (species >= 1000)? species : *(s->classtable[species].scriptposp) \
+ + s->classtable[species].class_offset)
+
+const char *
+_debug_get_input_default(void)
+{
+ char newinpbuf[256];
+
+ printf("> ");
+ fgets(newinpbuf, 254, stdin);
+
+ if (strlen(newinpbuf) != 0)
+ memcpy(inputbuf, newinpbuf, 256);
+
+ return inputbuf;
+}
+
+
+static inline int
+_parse_ticks(byte *data, int *offset_p, int size)
+{
+ int ticks = 0;
+ int tempticks;
+ int offset = 0;
+
+ do {
+ tempticks = data[offset++];
+ ticks += (tempticks == SCI_MIDI_TIME_EXPANSION_PREFIX)?
+ SCI_MIDI_TIME_EXPANSION_LENGTH : tempticks;
+ } while (tempticks == SCI_MIDI_TIME_EXPANSION_PREFIX
+ && offset < size);
+
+ if (offset_p)
+ *offset_p = offset;
+
+ return ticks;
+}
+
+
+static void
+midi_hexdump(byte *data, int size, int notational_offset)
+{ /* Specialised for SCI01 tracks (this affects the way cumulative cues are treated ) */
+ int offset = 0;
+ int prev = 0;
+
+ if (*data == 0xf0) /* SCI1 priority spec */
+ offset = 8;
+
+ while (offset < size) {
+ int old_offset = offset;
+ int offset_mod;
+ int time = _parse_ticks(data + offset, &offset_mod,
+ size);
+ int cmd;
+ int pleft;
+ int firstarg;
+ int i;
+ int blanks = 0;
+
+ offset += offset_mod;
+ fprintf(stderr, " [%04x] %d\t",
+ old_offset + notational_offset, time);
+
+ cmd = data[offset];
+ if (!(cmd & 0x80)) {
+ cmd = prev;
+ if (prev < 0x80) {
+ fprintf(stderr, "Track broken at %x after"
+ " offset mod of %d\n",
+ offset + notational_offset, offset_mod);
+ sci_hexdump(data, size, notational_offset);
+ return;
+ }
+ fprintf(stderr, "(rs %02x) ", cmd);
+ blanks += 8;
+ } else {
+ ++offset;
+ fprintf(stderr, "%02x ", cmd);
+ blanks += 3;
+ }
+ prev = cmd;
+
+ pleft = MIDI_cmdlen[cmd >> 4];
+ if (SCI_MIDI_CONTROLLER(cmd) && data[offset]
+ == SCI_MIDI_CUMULATIVE_CUE)
+ --pleft; /* This is SCI(0)1 specific */
+
+ for (i = 0; i < pleft; i++) {
+ if (i == 0)
+ firstarg = data[offset];
+ fprintf(stderr, "%02x ", data[offset++]);
+ blanks += 3;
+ }
+
+ while (blanks < 16) {
+ blanks += 4;
+ fprintf(stderr, " ");
+ }
+
+ while (blanks < 20) {
+ ++blanks;
+ fprintf(stderr, " ");
+ }
+
+ if (cmd == SCI_MIDI_EOT)
+ fprintf(stderr, ";; EOT");
+ else if (cmd == SCI_MIDI_SET_SIGNAL) {
+ if (firstarg == SCI_MIDI_SET_SIGNAL_LOOP)
+ fprintf(stderr, ";; LOOP point");
+ else
+ fprintf(stderr, ";; CUE (%d)", firstarg);
+ } else if (SCI_MIDI_CONTROLLER(cmd)) {
+ if (firstarg == SCI_MIDI_CUMULATIVE_CUE)
+ fprintf(stderr, ";; CUE (cumulative)");
+ else if (firstarg == SCI_MIDI_RESET_ON_SUSPEND)
+ fprintf(stderr, ";; RESET-ON-SUSPEND flag");
+ }
+ fprintf(stderr, "\n");
+
+ if (old_offset >= offset) {
+ fprintf(stderr, "-- Not moving forward anymore,"
+ " aborting (%x/%x)\n", offset, old_offset);
+ return;
+ }
+ }
+}
+
+#define SONGDATA(x) data[offset + (x)]
+#define CHECK_FOR_END_ABSOLUTE(off) if ((off) >= size) return;
+static void
+sci01_song_header_dump(byte *data, int size)
+{
+ int offset = 0;
+ int smallest_start = 10000;
+
+ sciprintf ("SCI01 song track mappings:\n");
+
+ if (*data == 0xf0) /* SCI1 priority spec */
+ offset = 8;
+
+ CHECK_FOR_END_ABSOLUTE(0);
+ while (SONGDATA(0) != 0xff) {
+ byte device_id = data[offset];
+ sciprintf("* Device %02x:\n", device_id);
+ offset++;
+ CHECK_FOR_END_ABSOLUTE(offset + 1);
+ while (SONGDATA(0) != 0xff) {
+ int track_offset;
+ int end;
+ byte header1, header2;
+
+ CHECK_FOR_END_ABSOLUTE(offset + 7);
+
+ offset += 2;
+
+ track_offset = getUInt16(data + offset);
+ header1 = data[track_offset];
+ header2 = data[track_offset+1];
+ track_offset += 2;
+
+ if (track_offset < smallest_start)
+ smallest_start = track_offset;
+ end = getUInt16(data + offset + 2);
+ sciprintf(" - %04x -- %04x",
+ track_offset, track_offset + end);
+
+ if (track_offset == 0xfe)
+ sciprintf(" (PCM data)\n");
+ else
+ sciprintf(" (channel %d, special %d,"
+ " %d playing notes, %d foo)\n",
+ header1 & 0xf,
+ header1 >> 4,
+ header2 & 0xf,
+ header2 >> 4);
+
+ offset += 4;
+ }
+ offset++;
+ }
+}
+#undef CHECK_FOR_END_ABSOLUTE
+#undef SONGDATA
+
+
+
+int c_sfx_01_header(state_t *s)
+{
+ resource_t *song = scir_find_resource(s->resmgr,
+ sci_sound,
+ cmd_params[0].val,
+ 0);
+
+ if (!song) {
+ sciprintf("Doesn't exist\n");
+ return 1;
+ }
+
+ sci01_song_header_dump(song->data, song->size);
+ return 0;
+}
+
+int c_sfx_01_track(state_t *s)
+{
+ resource_t *song = scir_find_resource(s->resmgr,
+ sci_sound,
+ cmd_params[0].val,
+ 0);
+
+ int offset = cmd_params[1].val;
+
+ if (!song) {
+ sciprintf("Doesn't exist\n");
+ return 1;
+ }
+
+ midi_hexdump(song->data + offset, song->size, offset);
+ return 0;
+}
+
+
+
+const char *(*_debug_get_input)(void) = _debug_get_input_default;
+
+int
+c_segtable(state_t *s)
+{
+ int i;
+
+ sciprintf(" ---- segment table ----\n");
+ for (i = 0; i < s->seg_manager.heap_size; i++) {
+ mem_obj_t *mobj = s->seg_manager.heap[i];
+ if (mobj && mobj->type) {
+ sciprintf(" [%04x] ", i);
+
+ switch (mobj->type) {
+
+ case MEM_OBJ_SCRIPT:
+ sciprintf("S script.%03d l:%d ",
+ mobj->data.script.nr,
+ mobj->data.script.lockers);
+ break;
+
+ case MEM_OBJ_CLONES:
+ sciprintf("C clones (%d allocd)",
+ mobj->data.clones.entries_used);
+ break;
+
+ case MEM_OBJ_LOCALS:
+ sciprintf("V locals %03d",
+ mobj->data.locals.script_id);
+ break;
+
+ case MEM_OBJ_STACK:
+ sciprintf("D data stack (%d)",
+ mobj->data.stack.nr);
+ break;
+
+ case MEM_OBJ_SYS_STRINGS:
+ sciprintf("Y system string table");
+ break;
+
+ case MEM_OBJ_LISTS:
+ sciprintf("L lists (%d)", mobj->data.lists.entries_used);
+ break;
+
+ case MEM_OBJ_NODES:
+ sciprintf("N nodes (%d)", mobj->data.nodes.entries_used);
+ break;
+
+ case MEM_OBJ_HUNK:
+ sciprintf("H hunk (%d)", mobj->data.hunks.entries_used);
+ break;
+
+ case MEM_OBJ_DYNMEM:
+ sciprintf("M dynmem: %d bytes", mobj->data.dynmem.size);
+ break;
+
+ default:
+ sciprintf("I Invalid (type = %x)", mobj->type);
+ break;
+ }
+
+ sciprintf(" seg_ID = %d \n", mobj->segmgr_id);
+ }
+ }
+ sciprintf("\n");
+ return 0;
+}
+
+
+static void
+print_obj_head(state_t *s, object_t *obj)
+{
+ sciprintf(PREG" %s : %3d vars, %3d methods\n",
+ PRINT_REG(obj->pos),
+ obj_get_name(s, obj->pos),
+ obj->variables_nr,
+ obj->methods_nr);
+}
+
+static void
+print_list(state_t *s, list_t *l)
+{
+ reg_t pos = l->first;
+ reg_t my_prev = NULL_REG_INITIALIZER;
+
+ sciprintf("\t<\n");
+
+ while (!IS_NULL_REG(pos)) {
+ node_t *node;
+ mem_obj_t *mobj = GET_SEGMENT(s->seg_manager, pos.segment, MEM_OBJ_NODES);
+
+ if (!mobj || !ENTRY_IS_VALID(&(mobj->data.nodes), pos.offset)) {
+ sciprintf(" WARNING: "PREG": Doesn't contain list node!\n",
+ PRINT_REG(pos));
+ return;
+ }
+
+ node = &(mobj->data.nodes.table[pos.offset].entry);
+
+ sciprintf("\t"PREG" : "PREG" -> "PREG"\n",
+ PRINT_REG(pos), PRINT_REG(node->key), PRINT_REG(node->value));
+
+ if (!REG_EQ(my_prev, node->pred))
+ sciprintf(" WARNING: current node gives "PREG" as predecessor!\n",
+ PRINT_REG(node->pred));
+
+ my_prev = pos;
+ pos = node->succ;
+ }
+
+ if (!REG_EQ(my_prev, l->last))
+ sciprintf(" WARNING: Last node was expected to be "PREG", was "PREG"!\n",
+ PRINT_REG(l->last), PRINT_REG(my_prev));
+ sciprintf("\t>\n");
+}
+
+
+static void
+_c_single_seg_info(state_t *s, mem_obj_t *mobj)
+{
+ switch (mobj->type) {
+
+ case MEM_OBJ_SCRIPT: {
+ int i;
+ script_t *scr = &(mobj->data.script);
+ sciprintf("script.%03d locked by %d, bufsize=%d (%x)\n",
+ scr->nr, scr->lockers, scr->buf_size, scr->buf_size);
+ if (scr->export_table)
+ sciprintf(" Exports: %4d at %d\n",
+ scr->exports_nr,
+ ((byte *) scr->export_table) - ((byte *)scr->buf));
+ else
+ sciprintf(" Exports: none\n");
+
+ sciprintf(" Synynms: %4d\n", scr->synonyms_nr);
+
+ if (scr->locals_block)
+ sciprintf(" Locals : %4d in segment 0x%x\n",
+ scr->locals_block->nr,
+ scr->locals_segment);
+ else
+ sciprintf(" Locals : none\n");
+
+ sciprintf(" Objects: %4d\n",
+ scr->objects_nr);
+ for (i = 0; i < scr->objects_nr; i++) {
+ sciprintf(" ");
+ print_obj_head(s, scr->objects + i);
+ }
+ }
+ break;
+
+ case MEM_OBJ_LOCALS: {
+ local_variables_t *locals = &(mobj->data.locals);
+ sciprintf("locals for script.%03d\n", locals->script_id);
+ sciprintf(" %d (0x%x) locals\n", locals->nr, locals->nr);
+ }
+ break;
+
+ case MEM_OBJ_STACK: {
+ dstack_t *stack = &(mobj->data.stack);
+ sciprintf("stack\n");
+ sciprintf(" %d (0x%x) entries\n", stack->nr, stack->nr);
+ }
+ break;
+
+ case MEM_OBJ_SYS_STRINGS: {
+ sys_strings_t *strings = &(mobj->data.sys_strings);
+ int i;
+
+ sciprintf("system string table\n");
+ for (i = 0; i < SYS_STRINGS_MAX; i++)
+ if (strings->strings[i].name)
+ sciprintf(" %s[%d]=\"%s\"\n",
+ strings->strings[i].name,
+ strings->strings[i].max_size,
+ strings->strings[i].value);
+ }
+ break;
+
+ case MEM_OBJ_CLONES: {
+ int i = 0;
+ clone_table_t *ct =
+ &(mobj->data.clones);
+
+ sciprintf("clones\n");
+
+ for (i = 0; i < ct->max_entry; i++)
+ if (ENTRY_IS_VALID(ct, i)) {
+ sciprintf(" [%04x] ", i);
+ print_obj_head(s, &(ct->table[i].entry));
+ }
+ }
+ break;
+
+ case MEM_OBJ_LISTS: {
+ int i = 0;
+ list_table_t *lt =
+ &(mobj->data.lists);
+
+ sciprintf("lists\n");
+ for (i = 0; i < lt->max_entry; i++)
+ if (ENTRY_IS_VALID(lt, i)) {
+ sciprintf(" [%04x]: ", i);
+ print_list(s, &(lt->table[i].entry));
+ }
+ }
+ break;
+
+ case MEM_OBJ_NODES: {
+ sciprintf("nodes (total %d)\n", mobj->data.nodes.entries_used);
+ break;
+ }
+
+ case MEM_OBJ_HUNK: {
+ int i;
+ hunk_table_t *ht =
+ &(mobj->data.hunks);
+
+ sciprintf("hunk (total %d)\n", mobj->data.hunks.entries_used);
+ for (i = 0; i < ht->max_entry; i++)
+ if (ENTRY_IS_VALID(ht, i)) {
+ sciprintf(" [%04x] %d bytes at %p, type=%s\n",
+ i, ht->table[i].entry.size, ht->table[i].entry.mem,
+ ht->table[i].entry.type);
+ }
+ }
+
+ case MEM_OBJ_DYNMEM: {
+ sciprintf("dynmem (%s): %d bytes\n",
+ mobj->data.dynmem.description?
+ mobj->data.dynmem.description:"no description",
+ mobj->data.dynmem.size);
+
+ sci_hexdump(mobj->data.dynmem.buf, mobj->data.dynmem.size, 0);
+ }
+ break;
+
+ default : sciprintf("Invalid type %d\n", mobj->type);
+ break;
+ }
+}
+
+static int
+show_node(state_t *s, reg_t addr)
+{
+
+ mem_obj_t *mobj = GET_SEGMENT(s->seg_manager, addr.segment, MEM_OBJ_LISTS);
+
+ if (mobj) {
+ list_table_t *lt = &(mobj->data.lists);
+ list_t *list;
+
+ if (!ENTRY_IS_VALID(lt, addr.offset)) {
+ sciprintf("Address does not contain a list\n");
+ return 1;
+ }
+
+ list = &(lt->table[addr.offset].entry);
+
+ sciprintf(PREG" : first x last = ("PREG", "PREG")\n",
+ PRINT_REG(addr), PRINT_REG(list->first),
+ PRINT_REG(list->last));
+ } else {
+ node_table_t *nt;
+ node_t *node;
+ mobj = GET_SEGMENT(s->seg_manager, addr.segment, MEM_OBJ_NODES);
+
+ if (!mobj) {
+ sciprintf("Segment #%04x is not a list or node segment\n", addr.segment);
+ return 1;
+ }
+
+ nt = &(mobj->data.nodes);
+
+ if (!ENTRY_IS_VALID(nt, addr.offset)) {
+ sciprintf("Address does not contain a node\n");
+ return 1;
+ }
+ node = &(nt->table[addr.offset].entry);
+
+ sciprintf(PREG" : prev x next = ("PREG", "PREG"); maps "PREG" -> "PREG"\n",
+ PRINT_REG(addr),
+ PRINT_REG(node->pred),
+ PRINT_REG(node->succ),
+ PRINT_REG(node->key),
+ PRINT_REG(node->value));
+ }
+ return 0;
+}
+
+int objinfo(state_t *s, reg_t pos);
+
+void
+song_lib_dump(songlib_t songlib, int line);
+
+static int
+c_songlib_print(state_t *s)
+{
+ song_lib_dump(s->sound.songlib, __LINE__);
+ return 0;
+}
+
+static int
+c_vr(state_t *s)
+{
+ reg_t reg = cmd_params[0].reg;
+ reg_t reg_end = cmd_paramlength > 1 ? cmd_params[1].reg : NULL_REG;
+ int type_mask = determine_reg_type(s, reg, 1);
+ int filter;
+ int found = 0;
+
+ sciprintf(PREG" is of type 0x%x%s: ", PRINT_REG(reg), type_mask & ~KSIG_INVALID,
+ type_mask & KSIG_INVALID ? " (invalid)" : "");
+
+ type_mask &= ~KSIG_INVALID;
+
+ if (reg.segment == 0
+ && reg.offset == 0) {
+ sciprintf("Null.\n");
+ return 0;
+ }
+
+ if (reg_end.segment != reg.segment)
+ {
+ sciprintf("Ending segment different from starting segment. Assuming no bound on dump.\n");
+ reg_end = NULL_REG;
+ }
+
+ for (filter = 1; filter < 0xf000; filter <<= 1) {
+ int type = type_mask & filter;
+
+ if (found && type) {
+ sciprintf("--- Alternatively, it could be a ");
+ }
+
+
+ switch (type) {
+ case 0: break;
+
+ case KSIG_LIST: {
+ list_t *l = LOOKUP_LIST(reg);
+
+ sciprintf("list\n");
+
+ if (l)
+ print_list(s, l);
+ else
+ sciprintf("Invalid list.\n");
+ }
+ break;
+
+ case KSIG_NODE:
+ sciprintf("list node\n");
+ show_node(s, reg);
+ break;
+
+ case KSIG_OBJECT:
+ sciprintf("object\n");
+ objinfo(s, reg);
+ break;
+
+ case KSIG_REF: {
+ int size;
+ unsigned char *block = sm_dereference(&s->seg_manager,
+ reg, &size);
+
+ sciprintf("raw data\n");
+
+ if (reg_end.segment != 0 && size < reg_end.offset - reg.offset)
+ {
+ sciprintf("Block end out of bounds (size %d). Resetting.\n", size);
+ reg_end = NULL_REG;
+ }
+
+ if (reg_end.segment != 0 && (size >= reg_end.offset - reg.offset))
+ size = reg_end.offset - reg.offset;
+
+ if (reg_end.segment != 0)
+ sciprintf("Block size less than or equal to %d\n", size);
+
+ sci_hexdump(block, size, 0);
+ }
+ break;
+
+ case KSIG_ARITHMETIC:
+ sciprintf("arithmetic value\n %d (%04x)\n", (gint16) reg.offset, reg.offset);
+ break;
+
+
+ default: sciprintf("unknown.\n", type);
+
+ }
+
+ if (type) {
+ sciprintf("\n");
+ found = 1;
+ }
+ }
+
+ return 0;
+}
+
+
+int
+c_segkill(state_t *s)
+{
+ int i = 0;
+ while (i < cmd_paramlength) {
+ int nr = cmd_params[i++].val;
+
+ sm_set_lockers(&(s->seg_manager), nr, 0, SEG_ID);
+ }
+ return 0;
+}
+
+static int
+c_mousepos(state_t *s)
+{
+ sci_event_t event;
+
+ sciprintf("Click somewhere in the game window...\n");
+
+ while (event = gfxop_get_event(s->gfx_state, SCI_EVT_MOUSE_RELEASE),
+ event.type != SCI_EVT_MOUSE_RELEASE)
+ {
+ };
+
+ sciprintf("Mouse pointer at (%d, %d)\n",
+ s->gfx_state->pointer_pos.x,
+ s->gfx_state->pointer_pos.y);
+ return 0;
+}
+
+int
+c_seginfo(state_t *s)
+{
+ int i = 0;
+
+ if (cmd_paramlength) {
+ while (i < cmd_paramlength) {
+ int nr = cmd_params[i++].val;
+ if (nr < 0
+ || nr >= s->seg_manager.heap_size
+ || !s->seg_manager.heap[nr]) {
+ sciprintf("Segment %04x does not exist\n", nr);
+ return 1;
+ }
+ sciprintf("[%04x] ", nr);
+ _c_single_seg_info(s, s->seg_manager.heap[nr]);
+ }
+ } else for (i = 0; i < s->seg_manager.heap_size; i++)
+ if (s->seg_manager.heap[i]) {
+ sciprintf("[%04x] ", i);
+ _c_single_seg_info(s, s->seg_manager.heap[i]);
+ sciprintf("\n");
+ }
+ return 0;
+}
+
+int
+c_debuginfo(state_t *s)
+{
+ exec_stack_t *eframe = NULL;
+
+ if (!_debugstate_valid) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ if (s->execution_stack && s->execution_stack_pos >= 0)
+ eframe = s->execution_stack + s->execution_stack_pos;
+
+ sciprintf("acc="PREG" prev="PREG" &rest=%x\n",
+ PRINT_REG(s->r_acc),
+ PRINT_REG(s->r_prev), *p_restadjust);
+ if (eframe)
+ sciprintf("pc="PREG" obj="PREG" fp="PSTK" sp="PSTK"\n",
+ PRINT_REG(*p_pc),
+ PRINT_REG(*p_objp),
+ PRINT_STK(*p_pp),
+ PRINT_STK(*p_sp));
+ else
+ sciprintf("<no execution stack: pc,obj,fp omitted>\n");
+ return 0;
+}
+
+
+int
+c_step(state_t *s)
+{
+ _debugstate_valid = 0;
+ if (cmd_paramlength && (cmd_params[0].val > 0))
+ _debug_step_running = cmd_params[0].val - 1;
+ return 0;
+}
+
+#ifdef __GNUC__
+#warning "Re-implement con:so"
+#endif
+#if 0
+int
+c_stepover(state_t *s)
+{
+ int opcode, opnumber;
+
+ if (!_debugstate_valid) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ _debugstate_valid = 0;
+ opcode = s->heap [*p_pc];
+ opnumber = opcode >> 1;
+ if (opnumber == 0x22 /* callb */ || opnumber == 0x23 /* calle */ ||
+ opnumber == 0x25 /* send */ || opnumber == 0x2a /* self */ ||
+ opnumber == 0x2b /* super */)
+ {
+ _debug_seeking = _DEBUG_SEEK_SO;
+ _debug_seek_level = s->execution_stack_pos;
+ /* Store in _debug_seek_special the offset of the next command after send */
+ switch (opcode)
+ {
+ case 0x46: /* calle W */
+ _debug_seek_special = *p_pc + 5; break;
+
+ case 0x44: /* callb W */
+ case 0x47: /* calle B */
+ case 0x56: /* super W */
+ _debug_seek_special = *p_pc + 4; break;
+
+ case 0x45: /* callb B */
+ case 0x57: /* super B */
+ case 0x4A: /* send W */
+ case 0x54: /* self W */
+ _debug_seek_special = *p_pc + 3; break;
+
+ default:
+ _debug_seek_special = *p_pc + 2;
+ }
+ }
+
+ return 0;
+}
+#endif
+
+int
+c_sim_parse(state_t *s)
+{
+ unsigned int i;
+ const char *operators = ",&/()[]#<>";
+
+ if (!_debugstate_valid) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ if (cmd_paramlength == 0) {
+ s->parser_valid = 0;
+ return 0;
+ }
+
+ for (i = 0; i < cmd_paramlength; i++) {
+ int flag = 0;
+ char *token = cmd_params[i].str;
+
+ if (strlen(token) == 1) {/* could be an operator */
+ int j = 0;
+ while (operators[j] && (operators[j] != token[0]))
+ j++;
+ if (operators[j]) {
+ s->parser_nodes[i].type = 1;
+ s->parser_nodes[i].content.value = j + 0xf0;
+ flag = 1; /* found an operator */
+ }
+ }
+
+ if (!flag) {
+ char *openb = strchr(token, '['); /* look for opening braces */
+ result_word_t *result;
+
+ if (openb)
+ *openb = 0; /* remove them and the rest */
+
+ result = vocab_lookup_word(token, strlen(token),
+ s->parser_words, s->parser_words_nr,
+ s->parser_suffices, s->parser_suffices_nr);
+
+ if (result) {
+ s->parser_nodes[i].type = 0;
+ s->parser_nodes[i].content.value = result->group;
+ free(result);
+ } else { /* group name was specified directly? */
+ int val = strtol(token, NULL, 0);
+ if (val) {
+ s->parser_nodes[i].type = 0;
+ s->parser_nodes[i].content.value = val;
+ } else { /* invalid and not matched */
+ sciprintf("Couldn't interpret '%s'\n", token);
+ s->parser_valid = 0;
+ return 1;
+ }
+ }
+ }
+
+ }
+
+ s->parser_nodes[cmd_paramlength].type = -1; /* terminate */
+
+ s->parser_valid = 2;
+ return 0;
+}
+
+
+int
+c_classtable(state_t *s)
+{
+ int i;
+
+ if (!_debugstate_valid) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ sciprintf("Available classes:\n");
+ for (i = 0; i < s->classtable_size; i++)
+ if (s->classtable[i].reg.segment)
+ sciprintf(" Class 0x%x at "PREG" (script 0x%x)\n",
+ i,
+ PRINT_REG(s->classtable[i].reg),
+ s->classtable[i].script);
+
+ return 0;
+}
+
+int
+c_viewinfo(state_t *s)
+{
+ int view = cmd_params[0].val;
+ int palette = cmd_params[1].val;
+ int loops, i;
+ gfxr_view_t *view_pixmaps = NULL;
+
+ if (!s) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+ sciprintf("Resource view.%d ", view);
+
+ loops = gfxop_lookup_view_get_loops(s->gfx_state, view);
+
+ if (loops < 0)
+ sciprintf("does not exist.\n");
+ else {
+ sciprintf("has %d loops:\n", loops);
+
+ for (i = 0; i < loops; i++) {
+ int j, cels;
+
+ sciprintf("Loop %d: %d cels.\n", i, cels = gfxop_lookup_view_get_cels(s->gfx_state, view, i));
+ for (j = 0; j < cels; j++) {
+ int width;
+ int height;
+ point_t mod;
+
+ if (con_can_handle_pixmaps()) {
+ view_pixmaps = gfxr_get_view(s->gfx_state->resstate,
+ view, &i, &j, palette);
+ con_insert_pixmap(gfx_clone_pixmap(view_pixmaps->loops[i].cels[j],
+ s->gfx_state->driver->mode));
+ }
+
+ gfxop_get_cel_parameters(s->gfx_state, view, i, j, &width, &height, &mod);
+
+ sciprintf(" cel %d: size %dx%d, adj+(%d,%d)\n",j, width, height, mod.x, mod.y);
+ }
+ }
+ }
+ return 0;
+}
+
+int
+c_list_sentence_fragments(state_t *s)
+{
+ int i;
+
+ if (!s) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ for (i = 0; i < s->parser_branches_nr; i++) {
+ int j = 0;
+
+ sciprintf("R%02d: [%x] ->", i, s->parser_branches[i].id);
+ while ((j < 10) && s->parser_branches[i].data[j]) {
+ int dat = s->parser_branches[i].data[j++];
+
+ switch (dat) {
+ case VOCAB_TREE_NODE_COMPARE_TYPE:
+ dat = s->parser_branches[i].data[j++];
+ sciprintf(" C(%x)", dat);
+ break;
+
+ case VOCAB_TREE_NODE_COMPARE_GROUP:
+ dat = s->parser_branches[i].data[j++];
+ sciprintf(" WG(%x)", dat);
+ break;
+
+ case VOCAB_TREE_NODE_FORCE_STORAGE:
+ dat = s->parser_branches[i].data[j++];
+ sciprintf(" FORCE(%x)", dat);
+ break;
+
+ default:
+ if (dat > VOCAB_TREE_NODE_LAST_WORD_STORAGE) {
+ int dat2 = s->parser_branches[i].data[j++];
+ sciprintf(" %x[%x]", dat, dat2);
+ } else
+ sciprintf(" ?%x?", dat);
+ }
+ }
+ sciprintf("\n");
+ }
+
+ sciprintf("%d rules.\n", s->parser_branches_nr);
+ return 0;
+}
+
+enum {
+ _parse_eoi,
+ _parse_token_pareno,
+ _parse_token_parenc,
+ _parse_token_nil,
+ _parse_token_number
+};
+
+int
+_parse_getinp(int *i, int *nr)
+{
+ char *token;
+
+ if ((unsigned)*i == cmd_paramlength)
+ return _parse_eoi;
+
+ token = cmd_params[(*i)++].str;
+
+ if (!strcmp(token,"("))
+ return _parse_token_pareno;
+
+ if (!strcmp(token,")"))
+ return _parse_token_parenc;
+
+ if (!strcmp(token,"nil"))
+ return _parse_token_nil;
+
+ *nr = strtol(token, NULL, 0);
+ return _parse_token_number;
+}
+
+int
+_parse_nodes(state_t *s, int *i, int *pos, int type, int nr)
+{
+ int nexttk, nextval, newpos, oldpos;
+
+ if (type == _parse_token_nil)
+ return 0;
+
+ if (type == _parse_token_number) {
+ s->parser_nodes[*pos += 1].type = PARSE_TREE_NODE_LEAF;
+ s->parser_nodes[*pos].content.value = nr;
+ return *pos;
+ }
+ if (type == _parse_eoi) {
+ sciprintf("Unbalanced parentheses\n");
+ return -1;
+ }
+ if (type == _parse_token_parenc) {
+ sciprintf("Syntax error at token %d\n", *i);
+ return -1;
+ }
+ s->parser_nodes[oldpos = ++(*pos)].type = PARSE_TREE_NODE_BRANCH;
+
+ nexttk = _parse_getinp(i, &nextval);
+ if ((newpos = s->parser_nodes[oldpos].content.branches[0] = _parse_nodes(s, i, pos, nexttk, nextval)) == -1)
+ return -1;
+
+ nexttk = _parse_getinp(i, &nextval);
+ if ((newpos = s->parser_nodes[oldpos].content.branches[1] = _parse_nodes(s, i, pos, nexttk, nextval)) == -1)
+ return -1;
+
+ if (_parse_getinp(i, &nextval) != _parse_token_parenc)
+ sciprintf("Expected ')' at token %d\n", *i);
+ return oldpos;
+}
+
+int
+c_set_parse_nodes(state_t *s)
+{
+ int i = 0;
+ int foo, bar;
+ int pos = -1;
+
+ if (!s) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ bar = _parse_getinp(&i, &foo);
+ if (_parse_nodes(s, &i, &pos, bar, foo) == -1)
+ return 1;
+
+ vocab_dump_parse_tree("debug-parse-tree", s->parser_nodes);
+ return 0;
+}
+
+/* from grammar.c: */
+int
+vocab_gnf_parse(parse_tree_node_t *nodes, result_word_t *words, int words_nr,
+ parse_tree_branch_t *branch0, parse_rule_list_t *tlist, int verbose);
+/* parses with a GNF rule set */
+
+int
+c_parse(state_t *s)
+{
+ result_word_t *words;
+ int words_nr;
+ char *error;
+ char *string;
+
+ if (!s) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ string = cmd_params[0].str;
+ sciprintf("Parsing '%s'\n", string);
+ words = vocab_tokenize_string(string, &words_nr,
+ s->parser_words, s->parser_words_nr,
+ s->parser_suffices, s->parser_suffices_nr,
+ &error);
+ if (words) {
+
+ int i, syntax_fail = 0;
+
+ vocab_synonymize_tokens(words, words_nr, s->synonyms, s->synonyms_nr);
+
+ sciprintf("Parsed to the following blocks:\n");
+
+ for (i = 0; i < words_nr; i++)
+ sciprintf(" Type[%04x] Group[%04x]\n", words[i].w_class, words[i].group);
+
+ if (vocab_gnf_parse(&(s->parser_nodes[0]), words, words_nr, s->parser_branches,
+ s->parser_rules, 1))
+ syntax_fail = 1; /* Building a tree failed */
+
+ free(words);
+
+ if (syntax_fail)
+ sciprintf("Building a tree failed.\n");
+ else
+ vocab_dump_parse_tree("debug-parse-tree", s->parser_nodes);
+
+ } else {
+
+ sciprintf("Unknown word: '%s'\n", error);
+ free(error);
+ }
+ return 0;
+}
+
+int
+c_save_game(state_t *s)
+{
+ int omit_check = cmd_params[0].str[0] == '_';
+ int i;
+
+ if (!s) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ if (!omit_check) {
+ int result = 0;
+ for (i = 0; i < s->file_handles_nr; i++)
+ if (s->file_handles[i])
+ result++;
+
+ if (result) {
+ sciprintf("Game state has %d open file handles.\n", result);
+ sciprintf("Save to '_%s' to ignore this check.\nGame was NOT saved.\n", cmd_params[0].str);
+ return 1;
+ }
+ }
+
+ if (gamestate_save(s, cmd_params[0].str)) {
+ sciprintf("Saving the game state to '%s' failed\n", cmd_params[0].str);
+ }
+
+ return 0;
+}
+
+int
+c_restore_game(state_t *s)
+{
+ state_t *newstate;
+
+ if (!s) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ newstate = gamestate_restore(s, cmd_params[0].str);
+
+ if (newstate) {
+
+ s->successor = newstate; /* Set successor */
+
+ script_abort_flag = SCRIPT_ABORT_WITH_REPLAY; /* Abort current game */
+ _debugstate_valid = 0;
+ s->execution_stack_pos = s->execution_stack_base;
+
+ return 0;
+ } else {
+ sciprintf("Restoring gamestate '%s' failed.\n", cmd_params[0].str);
+ return 1;
+ }
+}
+
+extern char *old_save_dir; /* Ouch */
+
+int
+c_restart_game(state_t *s)
+{
+ unsigned int i;
+ char *deref_save_dir = (char*)kernel_dereference_bulk_pointer(s, s->save_dir_copy, 1);
+
+ if (!s) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ old_save_dir = strdup(deref_save_dir);
+ for (i = 0; i < cmd_paramlength; i++) {
+ if ((strcmp(cmd_params[0].str, "-r") == 0)
+ || (strcmp(cmd_params[0].str, "--replay") == 0))
+ s->restarting_flags |= SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE;
+ else
+ if ((strcmp(cmd_params[0].str, "-p") == 0)
+ || (strcmp(cmd_params[0].str, "--play") == 0))
+ s->restarting_flags &= ~SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE;
+ else {
+ sciprintf("Invalid parameter '%s'\n", cmd_params[0].str);
+ return 1;
+ }
+ }
+
+ sciprintf("Restarting\n");
+
+ s->restarting_flags |= SCI_GAME_IS_RESTARTING_NOW;
+
+ script_abort_flag = 1;
+ _debugstate_valid = 0;
+ return 0;
+}
+
+
+int
+c_stack(state_t *s)
+{
+ int i;
+ exec_stack_t *xs;
+
+ if (!s) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ if (s->execution_stack_pos >= 0)
+ xs = s->execution_stack + s->execution_stack_pos;
+ else {
+ sciprintf("No exec stack!");
+ return 1;
+ }
+
+ for (i = cmd_params[0].val ; i > 0; i--) {
+ if ((xs->sp - xs->fp - i) == 0)
+ sciprintf("-- temp variables --\n");
+ if (xs->sp - i >= s->stack_base)
+ sciprintf(PSTK" = "PREG"\n",
+ PRINT_STK(xs->sp - i), PRINT_REG(xs->sp[-i]));
+ }
+
+ return 0;
+}
+
+const char *selector_name(state_t *s, int selector)
+{
+ if (selector >= 0 && selector < s->selector_names_nr)
+ return s->selector_names[selector];
+ else
+ return "--INVALID--";
+}
+
+int prop_ofs_to_id(state_t *s, int prop_ofs, reg_t objp)
+{
+ object_t *obj = obj_get(s, objp);
+ byte *selectoroffset;
+ int selectors;
+
+ if (!obj) {
+ sciprintf("Applied prop_ofs_to_id on non-object at "PREG"\n",
+ PRINT_REG(objp));
+ return -1;
+ }
+
+ selectors = obj->variables_nr;
+
+ if (s->version < SCI_VERSION(1,001,000))
+ selectoroffset = ((byte *) (obj->base_obj)) + SCRIPT_SELECTOR_OFFSET + selectors * 2;
+ else
+ {
+ if (!(obj->variables[SCRIPT_INFO_SELECTOR].offset & SCRIPT_INFO_CLASS))
+ {
+ obj = obj_get(s, obj->variables[SCRIPT_SUPERCLASS_SELECTOR]);
+ selectoroffset = (byte *) obj->base_vars;
+ } else selectoroffset = (byte *) obj->base_vars;
+ }
+
+ if (prop_ofs < 0 || (prop_ofs >> 1) >= selectors) {
+ sciprintf("Applied prop_ofs_to_id to invalid property offset %x (property #%d not"
+ " in [0..%d]) on object at "PREG"\n",
+ prop_ofs, prop_ofs >> 1, selectors - 1,
+ PRINT_REG(objp));
+ return -1;
+ }
+
+
+ return getUInt16(selectoroffset + prop_ofs);
+}
+
+reg_t
+disassemble(state_t *s, reg_t pos, int print_bw_tag, int print_bytecode)
+/* Disassembles one command from the heap, returns address of next command or 0 if a ret was
+** encountered.
+*/
+{
+ mem_obj_t *memobj = GET_SEGMENT(s->seg_manager, pos.segment, MEM_OBJ_SCRIPT);
+ script_t *script_entity = NULL;
+ byte *scr;
+ int scr_size;
+ reg_t retval = make_reg(pos.segment, pos.offset + 1);
+ word param_value;
+ int opsize;
+ int opcode;
+ int bytecount = 1;
+ int i = 0;
+
+ if (!memobj) {
+ sciprintf("Disassembly failed: Segment %04x non-existant or not a script\n",
+ pos.segment);
+ return retval;
+ } else script_entity = &(memobj->data.script);
+
+ scr = script_entity->buf;
+ scr_size = script_entity->buf_size;
+
+ if (pos.offset >= scr_size) {
+ sciprintf("Trying to disassemble beyond end of script\n");
+ return pos;
+ }
+
+ opsize = scr[pos.offset];
+ opcode = opsize >> 1;
+
+ if (!_debugstate_valid) {
+ sciprintf("Not in debug state\n");
+ return retval;
+ }
+
+ opsize &= 1; /* byte if true, word if false */
+
+
+ sciprintf(PREG": ", PRINT_REG(pos));
+
+ if (print_bytecode) {
+ while (formats[opcode][i]) {
+ switch (formats[opcode][i++]) {
+
+ case Script_SByte:
+ case Script_Byte: bytecount++;
+ break;
+
+ case Script_Word:
+ case Script_SWord: bytecount += 2;
+ break;
+
+ case Script_SVariable:
+ case Script_Variable:
+ case Script_Property:
+ case Script_Global:
+ case Script_Local:
+ case Script_Temp:
+ case Script_Param:
+ case Script_SRelative:
+ if (opsize)
+ bytecount ++;
+ else
+ bytecount += 2;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (pos.offset + bytecount > scr_size) {
+ sciprintf("Operation arguments extend beyond end of script\n");
+ return retval;
+ }
+
+ for (i = 0; i < bytecount; i++)
+ sciprintf("%02x ", scr[pos.offset + i]);
+
+ for (i = bytecount; i < 5; i++)
+ sciprintf(" ");
+
+ }
+
+ if (print_bw_tag)
+ sciprintf("[%c] ", opsize? 'B' : 'W');
+ sciprintf("%s", s->opcodes[opcode].name);
+
+ i = 0;
+ while (formats[opcode][i])
+
+ switch (formats[opcode][i++]) {
+
+ case Script_Invalid: sciprintf("-Invalid operation-"); break;
+
+ case Script_SByte:
+ case Script_Byte: sciprintf(" %02x", scr[retval.offset++]); break;
+
+ case Script_Word:
+ case Script_SWord:
+ sciprintf(" %04x", 0xffff & (scr[retval.offset]
+ | (scr[retval.offset+1] << 8)));
+ retval.offset += 2;
+ break;
+
+ case Script_SVariable:
+ case Script_Variable:
+ case Script_Property:
+ case Script_Global:
+ case Script_Local:
+ case Script_Temp:
+ case Script_Param:
+ if (opsize)
+ param_value = scr[retval.offset++];
+ else {
+ param_value = 0xffff & (scr[retval.offset]
+ | (scr[retval.offset+1] << 8));
+ retval.offset += 2;
+ }
+
+ if (opcode == op_callk)
+ sciprintf(" %s[%x]", (param_value < s->kfunct_nr) ?
+ ((param_value < s->kernel_names_nr) ? s->kernel_names[param_value] : "[Unknown(postulated)]")
+ : "<invalid>", param_value);
+ else sciprintf(opsize? " %02x" : " %04x", param_value);
+
+ break;
+
+ case Script_Offset:
+ if (opsize)
+ param_value = scr[retval.offset++];
+ else {
+ param_value = 0xffff & (scr[retval.offset]
+ | (scr[retval.offset+1] << 8));
+ retval.offset += 2;
+ }
+ sciprintf (opsize? " %02x [%04x]" : " %04x", param_value);
+ break;
+
+ case Script_SRelative:
+ if (opsize)
+ param_value = scr[retval.offset++];
+ else {
+ param_value = 0xffff & (scr[retval.offset]
+ | (scr[retval.offset+1] << 8));
+ retval.offset += 2;
+ }
+ sciprintf (opsize? " %02x [%04x]" : " %04x [%04x]", param_value, (0xffff) & (retval.offset + param_value));
+ break;
+
+ case Script_End: retval = NULL_REG;
+ break;
+
+ default:
+ sciprintf("Internal assertion failed in 'disassemble', %s, L%d\n", __FILE__, __LINE__);
+
+ }
+
+ if (REG_EQ(pos, *p_pc)) /* Extra information if debugging the current opcode */
+
+ if ((opcode == op_pTos)||(opcode == op_sTop)||
+ (opcode == op_pToa)||(opcode == op_aTop)||
+ (opcode == op_dpToa)||(opcode == op_ipToa)||
+ (opcode == op_dpTos)||(opcode == op_ipTos)) {
+ int prop_ofs = scr[pos.offset + 1];
+ int prop_id = prop_ofs_to_id(s, prop_ofs, *p_objp);
+
+ sciprintf(" (%s)", selector_name(s, prop_id));
+ }
+
+ sciprintf("\n");
+
+ if (REG_EQ(pos, *p_pc)) { /* Extra information if debugging the current opcode */
+
+ if (opcode == op_callk) {
+ int stackframe = (scr[pos.offset + 2] >> 1)
+ + (*p_restadjust);
+ int argc = ((*p_sp)[- stackframe - 1]).offset;
+ int i;
+
+ if (s->version >= SCI_VERSION_FTU_NEW_SCRIPT_HEADER)
+ argc += (*p_restadjust);
+
+
+
+ sciprintf(" Kernel params: (");
+
+ for (i = 0; i < argc; i++) {
+ sciprintf(PREG, PRINT_REG((*p_sp)[i - stackframe]));
+ if (i+1 < argc)
+ sciprintf(", ");
+ }
+ sciprintf(")\n");
+
+ }
+ else if ((opcode == op_send) || (opcode == op_self)) {
+ int restmod = *p_restadjust;
+ int stackframe = (scr[pos.offset + 1] >> 1) + restmod;
+ reg_t *sb = *p_sp;
+ word selector;
+ reg_t *val_ref;
+ reg_t fun_ref;
+
+ while (stackframe > 0) {
+ int argc = sb[- stackframe + 1].offset;
+ const char *name = NULL;
+ reg_t called_obj_addr;
+
+ if (opcode == op_send)
+ called_obj_addr = s->r_acc;
+ else if (opcode == op_self)
+ called_obj_addr = *p_objp;
+
+ selector = sb[- stackframe].offset;
+
+ name = obj_get_name(s, called_obj_addr);
+
+ if (!name)
+ name = "<invalid>";
+
+ sciprintf(" %s::%s[", name, (selector > s->selector_names_nr)
+ ? "<invalid>" : selector_name(s,selector));
+
+ switch (lookup_selector(s, called_obj_addr, selector,
+ &val_ref, &fun_ref)) {
+ case SELECTOR_METHOD:
+ sciprintf("FUNCT");
+ argc += restmod;
+ restmod = 0;
+ break;
+ case SELECTOR_VARIABLE:
+ sciprintf("VAR");
+ break;
+ case SELECTOR_NONE:
+ sciprintf("INVALID");
+ break;
+ }
+
+ sciprintf("](");
+
+ while (argc--) {
+
+ sciprintf(PREG, PRINT_REG(sb[- stackframe + 2]));
+ if (argc)
+ sciprintf(", ");
+ stackframe--;
+ }
+
+ sciprintf(")\n");
+
+ stackframe -= 2;
+ } /* while (stackframe > 0) */
+
+ } /* Send-like opcodes */
+
+ } /* (heappos == *p_pc) */
+
+
+ return retval;
+}
+
+int
+c_dumpnodes(state_t *s)
+{
+ int end = MIN(cmd_params[0].val, VOCAB_TREE_NODES);
+ int i;
+
+
+ if (!_debugstate_valid) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ for (i = 0; i < end; i++) {
+ sciprintf(" Node %03x: ", i);
+ if (s->parser_nodes[i].type == PARSE_TREE_NODE_LEAF)
+ sciprintf("Leaf: %04x\n", s->parser_nodes[i].content.value);
+ else
+ sciprintf("Branch: ->%04x, ->%04x\n", s->parser_nodes[i].content.branches[0],
+ s->parser_nodes[i].content.branches[1]);
+ }
+
+ return 0;
+}
+
+static const char *varnames[] = {"global", "local", "temp", "param"};
+static const char *varabbrev = "gltp";
+
+int
+c_vmvarlist(state_t *s)
+{
+ int i;
+
+ for (i=0;i<4;i++) {
+ sciprintf("%s vars at "PREG" ",
+ varnames[i],
+ PRINT_REG(make_reg(p_var_segs[i], p_vars[i] - p_var_base[i])));
+ if (p_var_max)
+ sciprintf(" total %d", p_var_max[i]);
+ sciprintf("\n");
+ }
+ return 0;
+}
+
+int
+c_vmvars(state_t *s)
+{
+ const char *vartype_pre = strchr(varabbrev, *cmd_params[0].str);
+ int vartype;
+ int idx = cmd_params[1].val;
+
+ if (!vartype_pre) {
+ sciprintf("Invalid variable type '%c'\n",
+ *cmd_params[0].str);
+ return 1;
+ }
+ vartype = vartype_pre - varabbrev;
+
+ if (idx < 0) {
+ sciprintf("Invalid: negative index\n");
+ return 1;
+ }
+ if ((p_var_max)
+ && (p_var_max[vartype] <= idx)) {
+ sciprintf("Max. index is %d (0x%x)\n",
+ p_var_max[vartype], p_var_max[vartype]);
+ return 1;
+ }
+
+ switch(cmd_paramlength) {
+ case 2:
+ sciprintf("%s var %d == "PREG"\n", varnames[vartype], idx,
+ PRINT_REG(p_vars[vartype][idx]));
+ break;
+
+ case 3:
+ p_vars[vartype][idx] = cmd_params[2].reg;
+ break;
+
+ default:
+ sciprintf("Too many arguments\n");
+ }
+ return 0;
+}
+
+#ifdef HAVE_SYSV_IPC
+static int _codebug_pid = 0;
+static int _codebug_stdin[2];
+static int _codebug_stdout[2];
+static int _codebug_commands[2]; /* sends commands to intermediate process */
+
+/* Codebugging uses two child processes:
+** The first one only performs output "coloring", its child process
+** is an actual secondary freesci process
+*/
+
+#define CODEBUG_BUFSIZE 512
+static const char *_codebug_colstr = "\033[31m\033[1m";
+static const char *_codebug_uncolstr = "\033[0m";
+
+static void
+codebug_send_command(const char *cmd)
+{
+ if (_codebug_pid)
+ write(_codebug_commands[1], cmd, strlen(cmd));
+}
+
+static void
+_print_colored(int from_fd)
+{
+ char buf[CODEBUG_BUFSIZE + 64];
+ char *buf_offset;
+ int br;
+ int length_increment;
+
+ strcpy(buf, _codebug_colstr);
+ length_increment = strlen(_codebug_colstr);
+ buf_offset = buf + length_increment;
+ length_increment += strlen(_codebug_uncolstr);
+
+ do {
+ br = read(from_fd, buf_offset, CODEBUG_BUFSIZE);
+ if (br > 0) {
+ strcpy(buf_offset + br, _codebug_uncolstr);
+ /* Atomic write with colors to nullify risk of
+ ** interference from fg process */
+ write(1, buf, br + length_increment);
+ }
+ } while (br == CODEBUG_BUFSIZE);
+}
+
+void
+_codebug_kill_children()
+{
+ if (_codebug_pid)
+ kill(_codebug_pid, SIGTERM);
+}
+
+void
+_codebug_sighandler(int i)
+{
+ if (i == SIGPIPE || i == SIGTERM)
+ kill(_codebug_pid, SIGTERM);
+
+ write(1, _codebug_uncolstr, strlen(_codebug_uncolstr));
+ fprintf(stderr, "Child process failed, aborting!\n");
+ fprintf(stderr, "(if you're using the UNIX sound server, you'll probably get spurious"
+ " warnings regarding the sound server now...)\n");
+ exit(1);
+}
+
+static void
+do_codebug(char *path, char *datapath)
+{
+ pipe(_codebug_stdin);
+ pipe(_codebug_stdout);
+ close(_codebug_commands[1]);
+
+ _codebug_pid = fork();
+ if (_codebug_pid) {
+ fd_set fs;
+ int max_n;
+
+ /* parent process */
+ close(_codebug_stdin[0]);
+ close(_codebug_stdout[1]);
+
+ max_n = _codebug_commands[0];
+ if (max_n < _codebug_stdout[0])
+ max_n = _codebug_stdout[0];
+
+ signal(SIGCHLD, _codebug_sighandler);
+ signal(SIGPIPE, _codebug_sighandler);
+ signal(SIGTERM, _codebug_sighandler);
+
+ while (1) { /* Until sigchild */
+ FD_ZERO(&fs);
+ FD_SET(_codebug_commands[0], &fs);
+ FD_SET(_codebug_stdout[0], &fs);
+
+ fflush(NULL);
+ select(max_n + 1, &fs, NULL, NULL, NULL);
+
+ if (FD_ISSET(_codebug_commands[0], &fs)) {
+ char buf[CODEBUG_BUFSIZE];
+ int br;
+ do {
+ br = read(_codebug_commands[0], buf, CODEBUG_BUFSIZE);
+ if (br > 0)
+ write(_codebug_stdin[1], buf, br);
+ } while (br == CODEBUG_BUFSIZE);
+ }
+
+ if (FD_ISSET(_codebug_stdout[0], &fs))
+ _print_colored(_codebug_stdout[0]);
+
+ }
+ } else {
+ /* child */
+ close(_codebug_stdin[1]);
+ close(_codebug_stdout[0]);
+
+ /* Re-associate stdin, stdout, stderr with i/o pipes */
+ dup2(_codebug_stdin[0], 0);
+ dup2(_codebug_stdout[1], 1);
+ dup2(_codebug_stdout[1], 2);
+
+ printf("Calling '%s' on data directory '%s'...\n", path, datapath);
+ execlp(path, path, "-D", "-gnull", "-Pnull", "-Onull", "-Mmt32gm",
+ "--disable-readline", "-d", datapath, NULL);
+ perror("execlp of FreeSCI");
+ exit(1);
+ }
+}
+
+static int
+c_codebug(state_t *s)
+{
+ char *path = cmd_params[0].str;
+
+ if (_codebug_pid) {
+ sciprintf("Already codebugging!\n");
+ return 1;
+ }
+
+ pipe(_codebug_commands);
+ _codebug_pid = fork();
+
+ if (!_codebug_pid)
+ do_codebug(path, s->resource_dir); /* Do codebugging in a child process */
+ else {
+ close(_codebug_commands[0]);
+ sleep(1); /* Yield to the scheduler, at least a bit */
+ atexit(_codebug_kill_children);
+ }
+ return 0;
+}
+#endif
+
+static int
+c_backtrace(state_t *s)
+{
+ int i;
+
+ if (!_debugstate_valid) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ sciprintf("Call stack (current base: 0x%x):\n", s->execution_stack_base);
+ for (i = 0; i <= s->execution_stack_pos; i++) {
+ exec_stack_t *call = &(s->execution_stack[i]);
+ const char *objname = obj_get_name(s, call->sendp);
+ int paramc, totalparamc;
+
+ switch (call->type) {
+
+ case EXEC_STACK_TYPE_CALL: {/* Normal function */
+ sciprintf(" %x:[%x] %s::%s(", i, call->origin,
+ objname, (call->selector == -1)? "<call[be]?>":
+ selector_name(s,call->selector));
+ }
+ break;
+
+ case EXEC_STACK_TYPE_KERNEL: /* Kernel function */
+ sciprintf(" %x:[%x] k%s(", i, call->origin, s->kernel_names[-(call->selector)-42]);
+ break;
+
+ case EXEC_STACK_TYPE_VARSELECTOR:
+ sciprintf(" %x:[%x] vs%s %s::%s (", i, call->origin, (call->argc)? "write" : "read",
+ objname, s->selector_names[call->selector]);
+ break;
+ } /* switch */
+
+ totalparamc = call->argc;
+
+ if (totalparamc > 16)
+ totalparamc = 16;
+
+ for (paramc = 1; paramc <= totalparamc; paramc++) {
+ sciprintf(PREG, PRINT_REG(call->variables_argp[paramc]));
+
+ if (paramc < call->argc)
+ sciprintf(", ");
+ }
+
+ if (call->argc > 16)
+ sciprintf("...");
+
+ sciprintf(")\n obj@"PREG,
+ PRINT_REG(call->objp));
+ if (call->type == EXEC_STACK_TYPE_CALL) {
+ sciprintf(" pc="PREG,
+ PRINT_REG(call->addr.pc));
+ if (call->sp == CALL_SP_CARRY)
+ sciprintf(" sp,fp:carry");
+ else {
+ sciprintf(" sp="PSTK,
+ PRINT_STK(call->sp));
+ sciprintf(" fp="PSTK,
+ PRINT_STK(call->fp));
+ }
+ } else
+ sciprintf(" pc:none");
+
+ sciprintf(" argp:"PSTK, PRINT_STK(call->variables_argp));
+ if (call->type == EXEC_STACK_TYPE_CALL)
+ sciprintf(" script: %d", s->seg_manager.heap[call->addr.pc.segment]->data.script.nr);
+ sciprintf("\n");
+ }
+ return 0;
+}
+
+static int
+c_redraw_screen(state_t *s)
+{
+ if (!_debugstate_valid) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ s->visual->draw(GFXW(s->visual), gfx_point(0,0));
+ gfxop_update_box(s->gfx_state, gfx_rect(0, 0, 320, 200));
+ gfxop_update(s->gfx_state);
+ gfxop_usleep(s->gfx_state, 10);
+
+ return 0;
+}
+
+
+static int
+c_clear_screen(state_t *s)
+{
+ if (!_debugstate_valid) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ gfxop_clear_box(s->gfx_state, gfx_rect(0, 0, 320, 200));
+ gfxop_update_box(s->gfx_state, gfx_rect(0, 0, 320, 200));
+ return 0;
+}
+
+static int
+c_visible_map(state_t *s)
+{
+ if (!s) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+WARNING(fixme!)
+#if 0
+ if (s->onscreen_console)
+ con_restore_screen(s, s->osc_backup);
+
+ if (cmd_params[0].val <= 3) s->pic_visible_map = cmd_params[0].val;
+ c_redraw_screen(s);
+
+ if (s->onscreen_console)
+ s->osc_backup = con_backup_screen(s);
+#endif
+ return 0;
+}
+
+
+static int
+c_gfx_current_port(state_t *s)
+{
+ if (!_debugstate_valid) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ if (!s->port)
+ sciprintf("none.\n");
+ else
+ sciprintf("%d\n", s->port->ID);
+ return 0;
+}
+
+static int
+c_gfx_print_port(state_t *s)
+{
+ gfxw_port_t *port;
+
+ if (!_debugstate_valid) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ port = s->port;
+
+ if (cmd_paramlength > 0) {
+ if (s->visual) {
+ port = gfxw_find_port(s->visual, cmd_params[0].val);
+ } else {
+ sciprintf("visual is uninitialized.\n");
+ return 1;
+ }
+ }
+
+ if (port)
+ port->print(GFXW(port), 0);
+ else
+ sciprintf("No such port.\n");
+
+ return 0;
+}
+
+static int
+c_gfx_priority(state_t *s)
+{
+ if (!_debugstate_valid) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ if (cmd_paramlength) {
+ int zone = cmd_params[0].val;
+ if (zone < 0)
+ zone = 0;
+ if (zone > 15) zone = 15;
+
+ sciprintf("Zone %x starts at y=%d\n", zone, PRIORITY_BAND_FIRST(zone));
+ } else {
+ sciprintf("Priority bands start at y=%d\nThey end at y=%d\n", s->priority_first, s->priority_last);
+ }
+
+ return 0;
+}
+
+static int
+c_gfx_print_visual(state_t *s)
+{
+ if (!_debugstate_valid) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ if (s->visual)
+ s->visual->print(GFXW(s->visual), 0);
+ else
+ sciprintf("visual is uninitialized.\n");
+
+ return 0;
+}
+
+static int
+c_gfx_print_dynviews(state_t *s)
+{
+ if (!_debugstate_valid) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ if (!s->dyn_views)
+ sciprintf("No dynview list active.\n");
+ else
+ s->dyn_views->print(GFXW(s->dyn_views), 0);
+
+ return 0;
+}
+
+static int
+c_gfx_print_dropviews(state_t *s)
+{
+ if (!_debugstate_valid) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ if (!s->drop_views)
+ sciprintf("No dropped dynview list active.\n");
+ else
+ s->drop_views->print(GFXW(s->drop_views), 0);
+
+ return 0;
+}
+
+static int
+c_gfx_drawpic(state_t *s)
+{
+ int flags = 1, default_palette = 0;
+
+ if (!_debugstate_valid) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ if (cmd_paramlength > 1) {
+ default_palette = cmd_params[1].val;
+
+ if (cmd_paramlength > 2)
+ flags = cmd_params[2].val;
+ }
+
+ gfxop_new_pic(s->gfx_state, cmd_params[0].val, flags, default_palette);
+ gfxop_clear_box(s->gfx_state, gfx_rect(0, 0, 320, 200));
+ gfxop_update(s->gfx_state);
+ gfxop_usleep(s->gfx_state, 0);
+ return 0;
+}
+
+#ifdef GFXW_DEBUG_WIDGETS
+extern gfxw_widget_t *debug_widgets[];
+extern int debug_widget_pos;
+
+static int
+c_gfx_print_widget(state_t *s)
+{
+ if (!_debugstate_valid) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ if (cmd_paramlength) {
+ unsigned int i;
+ for (i = 0; i < cmd_paramlength ; i++) {
+ int widget_nr = cmd_params[i].val;
+
+ sciprintf("===== Widget #%d:\n", widget_nr);
+ debug_widgets[widget_nr]->print(debug_widgets[widget_nr], 0);
+ }
+
+ } else if(debug_widget_pos>1)
+ sciprintf("Widgets 0-%d are active\n", debug_widget_pos-1);
+ else if (debug_widget_pos == 1)
+ sciprintf("Widget 0 is active\n");
+ else
+ sciprintf("No widgets are active\n");
+
+ return 0;
+}
+#endif
+
+static int
+c_gfx_show_map(state_t *s)
+{
+ int map = cmd_params[0].val;
+ if (!_debugstate_valid) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ gfxop_set_clip_zone(s->gfx_state, gfx_rect_fullscreen);
+
+ switch (map) {
+ case 0:
+ s->visual->add_dirty_abs(GFXWC(s->visual), gfx_rect(0, 0, 320, 200), 0);
+ s->visual->draw(GFXW(s->visual), gfx_point(0, 0));
+ break;
+
+ case 1:
+ gfx_xlate_pixmap(s->gfx_state->pic->priority_map, s->gfx_state->driver->mode, GFX_XLATE_FILTER_NONE);
+ gfxop_draw_pixmap(s->gfx_state, s->gfx_state->pic->priority_map, gfx_rect(0, 0, 320, 200), gfx_point(0, 0));
+ break;
+
+ case 2:
+ gfx_xlate_pixmap(s->gfx_state->control_map, s->gfx_state->driver->mode, GFX_XLATE_FILTER_NONE);
+ gfxop_draw_pixmap(s->gfx_state, s->gfx_state->control_map, gfx_rect(0, 0, 320, 200), gfx_point(0, 0));
+ break;
+
+ default:
+ sciprintf("Map %d is not available.\n", map);
+ return 1;
+ }
+
+ gfxop_update(s->gfx_state);
+ return 0;
+}
+
+
+static int
+c_gfx_draw_cel(state_t *s)
+{
+ int view = cmd_params[0].val;
+ int loop = cmd_params[1].val;
+ int cel = cmd_params[2].val;
+ int palette = cmd_params[3].val;
+ if (!s) {
+ sciprintf("Not in debug state!\n");
+ return 1;
+ }
+
+ gfxop_set_clip_zone(s->gfx_state, gfx_rect_fullscreen);
+ gfxop_draw_cel(s->gfx_state, view, loop, cel, gfx_point(160, 100),
+ s->ega_colors[0], palette);
+ gfxop_update(s->gfx_state);
+
+ return 0;
+}
+
+static int
+c_gfx_fill_screen(state_t *s)
+{
+ int col = cmd_params[0].val;
+
+ if (!s) {
+ sciprintf("Not in debug state!\n");
+ return 1;
+ }
+
+ if (col < 0 || col > 15)
+ col = 0;
+
+ gfxop_set_clip_zone(s->gfx_state, gfx_rect_fullscreen);
+ gfxop_fill_box(s->gfx_state, gfx_rect_fullscreen, s->ega_colors[col]);
+ gfxop_update(s->gfx_state);
+
+ return 0;
+}
+
+static int
+c_gfx_draw_rect(state_t *s)
+{
+ int col = cmd_params[4].val;
+
+ if (!s) {
+ sciprintf("Not in debug state!\n");
+ return 1;
+ }
+
+ if (col < 0 || col > 15)
+ col = 0;
+
+ gfxop_set_clip_zone(s->gfx_state, gfx_rect_fullscreen);
+ gfxop_fill_box(s->gfx_state, gfx_rect(cmd_params[0].val, cmd_params[1].val, cmd_params[2].val, cmd_params[3].val), s->ega_colors[col]);
+ gfxop_update(s->gfx_state);
+
+ return 0;
+}
+
+static int
+c_gfx_propagate_rect(state_t *s)
+{
+ int map = cmd_params[4].val;
+ rect_t rect;
+
+ if (!s) {
+ sciprintf("Not in debug state!\n");
+ return 1;
+ }
+
+ if (map < 0 || map > 1)
+ map = 0;
+
+ gfxop_set_clip_zone(s->gfx_state, gfx_rect_fullscreen);
+
+ rect = gfx_rect(cmd_params[0].val,
+ cmd_params[1].val,
+ cmd_params[2].val,
+ cmd_params[3].val);
+
+ if (map == 1)
+ gfxop_clear_box(s->gfx_state, rect);
+ else
+ gfxop_update_box(s->gfx_state, rect);
+ gfxop_update(s->gfx_state);
+ gfxop_usleep(s->gfx_state, 10);
+
+ return 0;
+}
+
+#define GETRECT(ll, rr, tt, bb) \
+ll = GET_SELECTOR(pos, ll); \
+rr = GET_SELECTOR(pos, rr); \
+tt = GET_SELECTOR(pos, tt); \
+bb = GET_SELECTOR(pos, bb);
+
+static int
+c_gfx_draw_viewobj(state_t *s)
+{
+#ifdef __GNUC__
+#warning "Re-implement con:gfx_draw_viewobj"
+#endif
+#if 0
+ heap_ptr pos = (heap_ptr) (cmd_params[0].val);
+ int is_view;
+ int x, y, priority;
+ int nsLeft, nsRight, nsBottom, nsTop;
+ int brLeft, brRight, brBottom, brTop;
+
+ if (!s) {
+ sciprintf("Not in debug state!\n");
+ return 1;
+ }
+
+ if ((pos < 4) || (pos > 0xfff0)) {
+ sciprintf("Invalid address.\n");
+ return 1;
+ }
+
+ if ((getInt16(s->heap + pos + SCRIPT_OBJECT_MAGIC_OFFSET)) != SCRIPT_OBJECT_MAGIC_NUMBER) {
+ sciprintf("Not an object.\n");
+ return 0;
+ }
+
+
+ is_view =
+ (lookup_selector(s, pos, s->selector_map.x, NULL) == SELECTOR_VARIABLE)
+ &&
+ (lookup_selector(s, pos, s->selector_map.brLeft, NULL) == SELECTOR_VARIABLE)
+ &&
+ (lookup_selector(s, pos, s->selector_map.signal, NULL) == SELECTOR_VARIABLE)
+ &&
+ (lookup_selector(s, pos, s->selector_map.nsTop, NULL) == SELECTOR_VARIABLE);
+
+ if (!is_view) {
+ sciprintf("Not a dynamic View object.\n");
+ return 0;
+ }
+
+ x = GET_SELECTOR(pos, x);
+ y = GET_SELECTOR(pos, y);
+ priority = GET_SELECTOR(pos, priority);
+ GETRECT(brLeft, brRight, brBottom, brTop);
+ GETRECT(nsLeft, nsRight, nsBottom, nsTop);
+ gfxop_set_clip_zone(s->gfx_state, gfx_rect_fullscreen);
+
+ brTop += 10;
+ brBottom += 10;
+ nsTop += 10;
+ nsBottom += 10;
+
+ gfxop_fill_box(s->gfx_state, gfx_rect(nsLeft, nsTop,
+ nsRight - nsLeft + 1,
+ nsBottom - nsTop + 1),
+ s->ega_colors[2]);
+
+ gfxop_fill_box(s->gfx_state, gfx_rect(brLeft, brTop,
+ brRight - brLeft + 1,
+ brBottom - brTop + 1),
+ s->ega_colors[1]);
+
+
+ gfxop_fill_box(s->gfx_state, gfx_rect(x-1, y-1,
+ 3, 3),
+ s->ega_colors[0]);
+
+ gfxop_fill_box(s->gfx_state, gfx_rect(x-1, y,
+ 3, 1),
+ s->ega_colors[priority]);
+
+ gfxop_fill_box(s->gfx_state, gfx_rect(x, y-1,
+ 1, 3),
+ s->ega_colors[priority]);
+
+ gfxop_update(s->gfx_state);
+
+ return 0;
+#endif
+}
+
+
+static int
+c_gfx_flush_resources(state_t *s)
+{
+ if (!_debugstate_valid) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ gfxop_set_pointer_cursor(s->gfx_state, GFXOP_NO_POINTER);
+ sciprintf("Flushing resources...\n");
+ s->visual->widfree(GFXW(s->visual));
+ gfxr_free_all_resources(s->gfx_state->driver, s->gfx_state->resstate);
+ s->visual = NULL;
+ return 0;
+}
+
+static int
+c_gfx_update_zone(state_t *s)
+{
+ if (!_debugstate_valid) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ return s->gfx_state->driver->update(s->gfx_state->driver,
+ gfx_rect(cmd_params[0].val,
+ cmd_params[1].val,
+ cmd_params[2].val,
+ cmd_params[3].val),
+ gfx_point(cmd_params[0].val,
+ cmd_params[1].val),
+ GFX_BUFFER_FRONT
+ );
+
+}
+
+static int
+c_disasm_addr(state_t *s)
+{
+ reg_t vpc = cmd_params[0].reg;
+ int op_count = 1;
+ int do_bwc = 0;
+ int do_bytes = 0;
+ int i;
+ int invalid = 0;
+ int size;
+ sm_dereference(&s->seg_manager, vpc, &size);
+ size += vpc.offset; /* total segment size */
+
+ for (i = 1; i < cmd_paramlength; i++) {
+ if (!strcasecmp(cmd_params[i].str, "bwt"))
+ do_bwc = 1;
+ else if (!strcasecmp(cmd_params[i].str, "bc"))
+ do_bytes = 1;
+ else if (toupper(cmd_params[i].str[0]) == 'C')
+ op_count = atoi(cmd_params[i].str + 1);
+ else {
+ invalid = 1;
+ sciprintf("Invalid option '%s'\n", cmd_params[i].str);
+ }
+ }
+
+ if (invalid || op_count < 0)
+ return invalid;
+
+ do {
+ vpc = disassemble(s, vpc, do_bwc, do_bytes);
+ } while ((vpc.offset > 0) && (vpc.offset + 6 < size) && (--op_count));
+ return 0;
+}
+
+
+static int
+c_disasm(state_t *s)
+{
+ object_t *obj = obj_get(s, cmd_params[0].reg);
+ int selector_id = script_find_selector(s, cmd_params[1].str);
+ reg_t addr;
+
+ if (!obj) {
+ sciprintf("Not an object.");
+ return 1;
+ }
+
+ if (selector_id < 0) {
+ sciprintf("Not a valid selector name.");
+ return 1;
+ }
+
+ if (lookup_selector(s, cmd_params[0].reg, selector_id, NULL, &addr) != SELECTOR_METHOD) {
+ sciprintf("Not a method.");
+ return 1;
+ }
+
+ do {
+ addr = disassemble(s, addr, 0, 0);
+ } while (addr.offset > 0);
+
+ return 0;
+}
+
+static int
+c_sg(state_t *s)
+{
+ _debug_seeking = _DEBUG_SEEK_GLOBAL;
+ _debug_seek_special = cmd_params[0].val;
+ _debugstate_valid = 0;
+
+ return 0;
+}
+
+
+static int
+c_snk(state_t *s)
+{
+ int callk_index;
+ char *endptr;
+
+ if (!_debugstate_valid) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ if (cmd_paramlength > 0) {
+ /* Try to convert the parameter to a number. If the conversion stops
+ before end of string, assume that the parameter is a function name
+ and scan the function table to find out the index. */
+ callk_index = strtoul (cmd_params [0].str, &endptr, 0);
+ if (*endptr != '\0')
+ {
+ int i;
+
+ callk_index = -1;
+ for (i = 0; i < s->kernel_names_nr; i++)
+ if (!strcmp (cmd_params [0].str, s->kernel_names [i]))
+ {
+ callk_index = i;
+ break;
+ }
+
+ if (callk_index == -1)
+ {
+ sciprintf ("Unknown kernel function '%s'\n", cmd_params [0].str);
+ return 1;
+ }
+ }
+
+ _debug_seeking = _DEBUG_SEEK_SPECIAL_CALLK;
+ _debug_seek_special = callk_index;
+ _debugstate_valid = 0;
+ } else {
+ _debug_seeking = _DEBUG_SEEK_CALLK;
+ _debugstate_valid = 0;
+ }
+ return 0;
+}
+
+
+static int
+c_sret(state_t *s)
+{
+ _debug_seeking = _DEBUG_SEEK_LEVEL_RET;
+ _debug_seek_level = s->execution_stack_pos;
+ _debugstate_valid = 0;
+ return 0;
+}
+
+static int
+c_go(state_t *s)
+{
+ _debug_seeking = 0;
+ _debugstate_valid = 0;
+ script_debug_flag = 0;
+ return 0;
+}
+
+
+static int
+c_set_acc(state_t *s)
+{
+ s->r_acc = cmd_params[0].reg;
+ return 0;
+}
+
+static int
+c_send(state_t *s)
+{
+ reg_t object = cmd_params[0].reg;
+ char *selector_name = cmd_params[1].str;
+ stack_ptr_t stackframe = s->execution_stack->sp;
+ int i, selector_id, selector_type;
+ exec_stack_t *xstack;
+ object_t *o;
+ reg_t *vptr;
+ reg_t fptr;
+
+ selector_id = vocabulary_lookup_sname(s->selector_names, selector_name);
+
+ if (selector_id < 0)
+ {
+ sciprintf("Unknown selector: \"%s\"\n", selector_name);
+ return 1;
+ }
+
+ o = obj_get(s, object);
+ if (o == NULL)
+ {
+ sciprintf("Address \""PREG"\" is not an object\n", PRINT_REG(object));
+ return 1;
+ }
+
+ selector_type = lookup_selector(s, object, selector_id, &vptr, &fptr);
+
+ if (selector_type == SELECTOR_NONE)
+ {
+ sciprintf("Object does not support selector: \"%s\"\n", selector_name);
+ return 1;
+ }
+
+ stackframe[0] = make_reg(0, selector_id);
+ stackframe[1] = make_reg(0, cmd_paramlength - 2);
+
+ for (i=2; i<cmd_paramlength; i++)
+ stackframe[i] = cmd_params[i].reg;
+
+ xstack = add_exec_stack_entry(s, fptr, s->execution_stack->sp + cmd_paramlength, object,
+ cmd_paramlength - 2, s->execution_stack->sp - 1, 0, object,
+ s->execution_stack_pos, SCI_XS_CALLEE_LOCALS);
+ xstack->selector = selector_id;
+ xstack->type = selector_type == SELECTOR_VARIABLE ? EXEC_STACK_TYPE_VARSELECTOR :
+ EXEC_STACK_TYPE_CALL;
+
+ /* Now commit the actual function: */
+ xstack = send_selector(s, object, object,
+ stackframe, cmd_paramlength - 2, stackframe);
+
+ xstack->sp += cmd_paramlength;
+ xstack->fp += cmd_paramlength;
+
+ s->execution_stack_pos_changed = 1;
+
+ return 0;
+}
+
+static int
+c_resource_id(state_t *s)
+{
+ int id = cmd_params[0].val;
+
+ sciprintf("%s.%d (0x%x)\n", sci_resource_types[id >> 11], id &0x7ff, id & 0x7ff);
+ return 0;
+}
+
+static int
+c_listclones(state_t *s)
+{
+ /*
+ int i, j = 0;
+ sciprintf("Listing all logged clones:\n");
+
+ for (i = 0; i < SCRIPT_MAX_CLONES; i++)
+ if (s->clone_list[i]) {
+ sciprintf(" Clone at %04x\n", s->clone_list[i]);
+ j++;
+ }
+
+ sciprintf("Total of %d clones.\n", j);
+ */
+ sciprintf("This function is temporarily disabled.\n");
+ return 0;
+}
+
+
+typedef struct {
+ const char *name;
+ const char option;
+ unsigned int flag;
+} generic_config_flag_t;
+
+
+static void
+handle_config_update(const generic_config_flag_t *flags_list, int flags_nr,
+ const char *subsystem,
+ int *active_options_p,
+ char *changestring /* or NULL to display*/)
+{
+ if (!changestring) {
+ int j;
+
+ sciprintf("Logging in %s:\n", subsystem);
+ if (!(*active_options_p))
+ sciprintf(" (nothing)\n");
+
+ for (j = 0; j < flags_nr; j++)
+ if (*active_options_p
+ & flags_list[j].flag) {
+ sciprintf(" - %s (%c)\n",
+ flags_list[j].name,
+ flags_list[j].option);
+ }
+
+ } else {
+ int mode;
+ int j = 0;
+ int flags = 0;
+
+ if (changestring[0] == '-')
+ mode = 0;
+ else if (changestring[0] == '+')
+ mode = 1;
+ else {
+ sciprintf("Mode spec must start with '+' or '-' in '%s'\n", changestring);
+ return;
+ }
+
+ while (changestring[++j]) {
+ int k;
+ int flag = 0;
+
+ if (changestring[j] == '*')
+ flags = ~0; /* Everything */
+ else
+ for (k = 0; !flag && k < flags_nr; k++)
+ if (flags_list[k].option
+ == changestring[j])
+ flag = flags_list[k].flag;
+
+ if (!flag) {
+ sciprintf("Invalid/unknown mode flag '%c'\n",
+ changestring[j]);
+ return;
+ }
+ flags |= flag;
+ }
+
+ if (mode) /* + */
+ *active_options_p |= flags;
+ else /* - */
+ *active_options_p &= ~flags;
+ }
+
+}
+
+static int
+c_handle_config_update(const generic_config_flag_t *flags, int flags_nr,
+ const char *subsystem, int *active_options_p)
+{
+ int i;
+
+ if (!_debugstate_valid) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ if (cmd_paramlength == 0)
+ handle_config_update(flags, flags_nr,
+ subsystem, active_options_p,
+ 0);
+
+ for (i = 0; i < cmd_paramlength; i++)
+ handle_config_update(flags, flags_nr,
+ subsystem, active_options_p,
+ cmd_params[i].str);
+ return 0;
+}
+
+const generic_config_flag_t SCIk_Debug_Names[SCIk_DEBUG_MODES] = {
+ {"Stubs", 'u', (1 << SCIkSTUB_NR)},
+ {"Lists and nodes", 'l', (1 << 1)},
+ {"Graphics", 'g', (1 << 2)},
+ {"Character handling", 'c', (1 << 3)},
+ {"Memory management", 'm', (1 << 4)},
+ {"Function parameter checks", 'f', (1 << SCIkFUNCCHK_NR)},
+ {"Bresenham algorithms", 'b', (1 << 6)},
+ {"Audio subsystem", 'a', (1 << SCIkSOUNDCHK_NR)},
+ {"System graphics driver", 'd', (1 << SCIkGFXDRIVER_NR)},
+ {"Base setter results", 's', (1 << SCIkBASESETTER_NR)},
+ {"Parser", 'p', (1 << SCIkPARSER_NR)},
+ {"Menu handling", 'M', (1 << 11)},
+ {"Said specs", 'S', (1 << 12)},
+ {"File I/O", 'F', (1 << 13)},
+ {"Time", 't', (1 << 14)},
+ {"Room numbers", 'r', (1 << 15)},
+ {"FreeSCI 0.3.3 kernel emulation", 'e', (1 << 16)},
+ {"Pathfinding", 'P', (1 << SCIkAVOIDPATH_NR)}
+} ;
+
+
+void
+set_debug_mode (struct _state *s, int mode, const char *areas)
+{
+ char *param = (char*)sci_malloc(strlen(areas) + 2);
+
+ param[0] = (mode)? '+' : '-';
+ strcpy(param + 1, areas);
+
+ handle_config_update(SCIk_Debug_Names, SCIk_DEBUG_MODES,
+ "VM and kernel",
+ (int *) &(s->debug_mode),
+ param);
+
+ sci_free(param);
+}
+
+int
+c_debuglog(state_t *s)
+{
+ return c_handle_config_update(SCIk_Debug_Names, SCIk_DEBUG_MODES,
+ "VM and kernel",
+ (int *) &(s->debug_mode));
+}
+
+#define SFX_DEBUG_MODES 2
+#define FROBNICATE_HANDLE(reg) ((reg).segment << 16 | (reg).offset)
+
+static int
+c_sfx_debuglog(state_t *s)
+{
+ const generic_config_flag_t sfx_debug_modes[SFX_DEBUG_MODES] = {
+ {"Song activation/deactivation", 's', SFX_DEBUG_SONGS},
+ {"Song cue polling and delivery", 'c', SFX_DEBUG_CUES}
+ };
+
+ return c_handle_config_update(sfx_debug_modes, SFX_DEBUG_MODES,
+ "sound subsystem",
+ (int *) &(s->sound.debug));
+}
+
+static int
+c_sfx_remove(state_t *s)
+{
+ reg_t id = cmd_params[0].reg;
+ int handle = FROBNICATE_HANDLE(id);
+
+ if (id.segment) {
+ sfx_song_set_status(&s->sound,
+ handle, SOUND_STATUS_STOPPED);
+ sfx_remove_song(&s->sound, handle);
+ PUT_SEL32V(id, signal, -1);
+ PUT_SEL32V(id, nodePtr, 0);
+ PUT_SEL32V(id, handle, 0);
+ }
+
+ return 0;
+}
+
+#define GFX_DEBUG_MODES 4
+
+int
+c_gfx_debuglog(state_t *s)
+{
+ gfx_driver_t *drv = s->gfx_state->driver;
+ const generic_config_flag_t gfx_debug_modes[GFX_DEBUG_MODES] = {
+ { "Mouse Pointer", 'p', GFX_DEBUG_POINTER},
+ { "Updates", 'u', GFX_DEBUG_UPDATES},
+ { "Pixmap operations", 'x', GFX_DEBUG_PIXMAPS},
+ { "Basic operations", 'b', GFX_DEBUG_BASIC},
+ };
+
+ return c_handle_config_update(gfx_debug_modes, GFX_DEBUG_MODES,
+ "graphics subsystem",
+ (int *) &(drv->debug_flags));
+
+}
+
+int
+c_dump_words(state_t *s)
+{
+ int i;
+
+ if (!s) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ if (!s->parser_words) {
+ sciprintf("No words.\n");
+ return 0;
+ }
+
+ for (i = 0; i < s->parser_words_nr; i++) {
+ word_t *word = s->parser_words[i];
+ sciprintf("%s: C %03x G %03x\n", word->word, word->w_class, word->group);
+ }
+ sciprintf("%d words\n", s->parser_words_nr);
+
+ return 0;
+}
+
+int
+c_simkey(state_t *s)
+{
+ _kdebug_cheap_event_hack = cmd_params[0].val;
+ return 0;
+}
+
+static int
+c_is_sample(state_t *s)
+{
+ resource_t *song = scir_find_resource(s->resmgr,
+ sci_sound,
+ cmd_params[0].val,
+ 0);
+ song_iterator_t *songit;
+ sfx_pcm_feed_t *data;
+
+ if (!song) {
+ sciprintf("Not a sound resource.\n");
+ return 1;
+ }
+
+ songit = songit_new(song->data, song->size, SCI_SONG_ITERATOR_TYPE_SCI0,
+ 0xcaffe /* What do I care about the ID? */);
+
+ if (!songit) {
+ sciprintf("Error-- Could not convert to song iterator\n");
+ return 1;
+ }
+
+ if ((data = songit->get_pcm_feed(songit))) {
+ sciprintf("\nIs sample (encoding %dHz/%s/%04x).\n",
+ data->conf.rate, (data->conf.stereo)?
+ ((data->conf.stereo == SFX_PCM_STEREO_LR)?
+ "stereo-LR" : "stereo-RL") : "mono",
+ data->conf.format);
+ data->destroy(data);
+ } else
+ sciprintf("Valid song, but not a sample.\n");
+
+ songit_free(songit);
+
+ return 0;
+}
+
+int
+c_simsoundcue(state_t *s)
+{
+ _kdebug_cheap_soundcue_hack = cmd_params[0].val;
+ return 0;
+}
+
+
+#define ASSERT_PARAMS(number) \
+ if (cmd_paramlength <= number) {\
+ sciprintf("Operation '%s' needs %d parameters\n", op, number); \
+ return 1;\
+ }
+
+
+
+#define GETRECT(ll, rr, tt, bb) \
+ll = GET_SELECTOR(pos, ll); \
+rr = GET_SELECTOR(pos, rr); \
+tt = GET_SELECTOR(pos, tt); \
+bb = GET_SELECTOR(pos, bb);
+
+#if 0
+#ifdef __GNUC__
+#warning "Re-implement viewobjinfo"
+#endif
+static void
+viewobjinfo(state_t *s, heap_ptr pos)
+{
+
+ char *signals[16] = {
+ "stop_update",
+ "updated",
+ "no_update",
+ "hidden",
+ "fixed_priority",
+ "always_update",
+ "force_update",
+ "remove",
+ "frozen",
+ "is_extra",
+ "hit_obstacle",
+ "doesnt_turn",
+ "no_cycler",
+ "ignore_horizon",
+ "ignore_actor",
+ "dispose!"
+ };
+
+ int x, y, z, priority;
+ int cel, loop, view, signal;
+ int nsLeft, nsRight, nsBottom, nsTop;
+ int lsLeft, lsRight, lsBottom, lsTop;
+ int brLeft, brRight, brBottom, brTop;
+ int i;
+ int have_rects = 0;
+ abs_rect_t nsrect, nsrect_clipped, brrect;
+
+ if (lookup_selector(s, pos, s->selector_map.nsBottom, NULL) == SELECTOR_VARIABLE) {
+ GETRECT(nsLeft, nsRight, nsBottom, nsTop);
+ GETRECT(lsLeft, lsRight, lsBottom, lsTop);
+ GETRECT(brLeft, brRight, brBottom, brTop);
+ have_rects = 1;
+ }
+
+ GETRECT(view, loop, signal, cel);
+
+ sciprintf("\n-- View information:\ncel %d/%d/%d at ", view, loop, cel);
+
+ x = GET_SELECTOR(pos, x);
+ y = GET_SELECTOR(pos, y);
+ priority = GET_SELECTOR(pos, priority);
+ if (s->selector_map.z > 0) {
+ z = GET_SELECTOR(pos, z);
+ sciprintf("(%d,%d,%d)\n", x, y, z);
+ } else sciprintf("(%d,%d)\n", x, y);
+
+ if (priority == -1)
+ sciprintf("No priority.\n\n");
+ else
+ sciprintf("Priority = %d (band starts at %d)\n\n",
+ priority, PRIORITY_BAND_FIRST(priority));
+
+ if (have_rects) {
+ sciprintf("nsRect: [%d..%d]x[%d..%d]\n", nsLeft, nsRight, nsTop, nsBottom);
+ sciprintf("lsRect: [%d..%d]x[%d..%d]\n", lsLeft, lsRight, lsTop, lsBottom);
+ sciprintf("brRect: [%d..%d]x[%d..%d]\n", brLeft, brRight, brTop, brBottom);
+ }
+
+ nsrect = get_nsrect(s, pos, 0);
+ nsrect_clipped = get_nsrect(s, pos, 1);
+ brrect = set_base(s, pos);
+ sciprintf("new nsRect: [%d..%d]x[%d..%d]\n", nsrect.x, nsrect.xend,
+ nsrect.y, nsrect.yend);
+ sciprintf("new clipped nsRect: [%d..%d]x[%d..%d]\n", nsrect_clipped.x,
+ nsrect_clipped.xend, nsrect_clipped.y, nsrect_clipped.yend);
+ sciprintf("new brRect: [%d..%d]x[%d..%d]\n", brrect.x, brrect.xend,
+ brrect.y, brrect.yend);
+ sciprintf("\n signals = %04x:\n", signal);
+
+ for (i = 0; i < 16; i++)
+ if (signal & (1 << i))
+ sciprintf(" %04x: %s\n", 1 << i, signals[i]);
+}
+#endif
+#undef GETRECT
+
+
+int
+objinfo(state_t *s, reg_t pos)
+{
+ object_t *obj = obj_get(s, pos);
+ object_t *var_container = obj;
+ int i;
+
+ sciprintf("["PREG"]: ", PRINT_REG(pos));
+ if (!obj) {
+ sciprintf("Not an object.");
+ return 1;
+ }
+
+ print_obj_head(s, obj);
+
+ if (!(obj->variables[SCRIPT_INFO_SELECTOR].offset & SCRIPT_INFO_CLASS))
+ var_container = obj_get(s, obj->variables[SCRIPT_SUPERCLASS_SELECTOR]);
+ sciprintf(" -- member variables:\n");
+ for (i = 0; i < obj->variables_nr; i++) {
+
+ sciprintf(" ");
+ if (i < var_container->variable_names_nr)
+ sciprintf("[%03x] %s = ", VM_OBJECT_GET_VARSELECTOR(var_container, i),
+ selector_name(s, VM_OBJECT_GET_VARSELECTOR(var_container, i)));
+ else
+ sciprintf("p#%x = ", i);
+
+ sciprintf(PREG"\n", PRINT_REG(obj->variables[i]));
+ }
+ sciprintf(" -- methods:\n");
+ for (i = 0; i < obj->methods_nr; i++) {
+ reg_t fptr = VM_OBJECT_READ_FUNCTION(obj, i);
+ sciprintf(" [%03x] %s = "PREG"\n",
+ VM_OBJECT_GET_FUNCSELECTOR(obj, i),
+ selector_name(s, VM_OBJECT_GET_FUNCSELECTOR(obj, i)),
+ PRINT_REG(fptr));
+ }
+ if (s->seg_manager.heap[pos.segment]->type==MEM_OBJ_SCRIPT)
+ sciprintf("\nOwner script:\t%d\n", s->seg_manager.heap[pos.segment]->data.script.nr);
+ return 0;
+}
+
+int
+c_vo(state_t *s)
+{
+ return objinfo(s, cmd_params[0].reg);
+}
+
+int
+c_obj(state_t *s)
+{
+ return objinfo(s, *p_objp);
+}
+
+int
+c_accobj(state_t *s)
+{
+ return objinfo(s, s->r_acc);
+}
+
+int
+c_shownode(state_t *s)
+{
+ reg_t addr = cmd_params[0].reg;
+
+ return show_node(s, addr);
+}
+
+/*** Breakpoint commands ***/
+
+static breakpoint_t *
+bp_alloc(state_t *s)
+{
+ breakpoint_t *bp;
+
+ if (s->bp_list)
+ {
+ bp = s->bp_list;
+ while (bp->next)
+ bp = bp->next;
+ bp->next = (breakpoint_t *) sci_malloc (sizeof (breakpoint_t));
+ bp = bp->next;
+ }
+ else {
+ s->bp_list = (breakpoint_t *) sci_malloc (sizeof (breakpoint_t));
+ bp = s->bp_list;
+ }
+
+ bp->next = NULL;
+
+ return bp;
+}
+
+int
+c_bpx(state_t *s)
+{
+ breakpoint_t *bp;
+
+ /* Note: We can set a breakpoint on a method that has not been loaded yet.
+ Thus, we can't check whether the command argument is a valid method name.
+ A breakpoint set on an invalid method name will just never trigger. */
+
+ bp=bp_alloc (s);
+
+ bp->type = BREAK_SELECTOR;
+ bp->data.name = (char*)sci_malloc (strlen (cmd_params [0].str)+1);
+ strcpy (bp->data.name, cmd_params [0].str);
+ s->have_bp |= BREAK_SELECTOR;
+
+ return 0;
+}
+
+int
+c_bpe(state_t *s)
+{
+ breakpoint_t *bp;
+
+ bp=bp_alloc (s);
+
+ bp->type = BREAK_EXPORT;
+ bp->data.address = (cmd_params [0].val << 16 | cmd_params [1].val);
+ s->have_bp |= BREAK_EXPORT;
+
+ return 0;
+}
+
+int
+c_bplist(state_t *s)
+{
+ breakpoint_t *bp;
+ int i = 0;
+ long bpdata;
+
+ bp = s->bp_list;
+ while (bp)
+ {
+ sciprintf (" #%i: ", i);
+ switch (bp->type)
+ {
+ case BREAK_SELECTOR:
+ sciprintf ("Execute %s\n", bp->data.name);
+ break;
+ case BREAK_EXPORT:
+ bpdata = bp->data.address;
+ sciprintf ("Execute script %d, export %d\n", bpdata >> 16, bpdata & 0xFFFF);
+ break;
+ }
+
+ bp = bp->next;
+ i++;
+ }
+
+ return 0;
+}
+
+int c_bpdel(state_t *s)
+{
+ breakpoint_t *bp, *bp_next, *bp_prev;
+ int i = 0, found = 0;
+ int type;
+
+ /* Find breakpoint with given index */
+ bp_prev = NULL;
+ bp = s->bp_list;
+ while (bp && i < cmd_params [0].val)
+ {
+ bp_prev = bp;
+ bp = bp->next;
+ i++;
+ }
+ if (!bp)
+ {
+ sciprintf ("Invalid breakpoint index %i\n", cmd_params [0].val);
+ return 1;
+ }
+
+ /* Delete it */
+ bp_next = bp->next;
+ type = bp->type;
+ if (type == BREAK_SELECTOR) sci_free (bp->data.name);
+ free (bp);
+ if (bp_prev)
+ bp_prev->next = bp_next;
+ else
+ s->bp_list = bp_next;
+
+ /* Check if there are more breakpoints of the same type. If not, clear
+ the respective bit in s->have_bp. */
+ for (bp = s->bp_list; bp; bp=bp->next)
+ if (bp->type == type)
+ {
+ found = 1;
+ break;
+ }
+
+ if (!found) s->have_bp &= ~type;
+
+ return 0;
+}
+
+int
+c_gnf(state_t *s)
+{
+ if (!s) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ vocab_gnf_dump(s->parser_branches, s->parser_branches_nr);
+ return 0;
+}
+
+int
+c_se(state_t *s)
+{
+ stop_on_event=1;
+ _debugstate_valid = script_debug_flag = script_error_flag = 0;
+ return 0;
+}
+
+int
+c_type(state_t *s)
+{
+ int t = determine_reg_type(s, cmd_params[0].reg, 1);
+ int invalid = t & KSIG_INVALID;
+
+ switch (t & ~KSIG_INVALID) {
+
+ case 0:
+ sciprintf("Invalid");
+ break;
+
+ case KSIG_LIST:
+ sciprintf("List");
+ break;
+
+ case KSIG_OBJECT:
+ sciprintf("Object");
+ break;
+
+ case KSIG_REF:
+ sciprintf("Reference");
+ break;
+
+ case KSIG_ARITHMETIC:
+ sciprintf("Arithmetic");
+ break;
+
+ default:
+ sciprintf("Erroneous unknown type %02x(%d decimal)\n", t, t);
+
+ }
+
+ sciprintf("%s\n", invalid ? " (invalid)" : "");
+}
+
+int
+c_statusbar(state_t *s)
+{
+ if (!s) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ s->titlebar_port->color=s->ega_colors[cmd_params[0].val];
+ s->titlebar_port->bgcolor=s->ega_colors[cmd_params[1].val];
+
+ s->status_bar_foreground=cmd_params[0].val;
+ s->status_bar_background=cmd_params[1].val;
+
+ sciw_set_status_bar(s, s->titlebar_port, s->status_bar_text,
+ s->status_bar_foreground,
+ s->status_bar_background);
+ gfxop_update(s->gfx_state);
+ return 0;
+}
+
+int
+c_sci_version(state_t *s)
+{
+ if (!s) {
+ sciprintf("Not in debug state\n");
+ return 1;
+ }
+
+ sciprintf("Emulating SCI version %d.%03d.%03d\n",
+ SCI_VERSION_MAJOR(s->version),
+ SCI_VERSION_MINOR(s->version),
+ SCI_VERSION_PATCHLEVEL(s->version));
+
+ return 0;
+}
+
+
+int
+c_sleep(state_t *s)
+{
+ sleep(cmd_params[0].val);
+ return 0;
+}
+
+static void
+_print_address(void * _, reg_t addr)
+{
+ if (addr.segment)
+ sciprintf(" "PREG"\n", PRINT_REG(addr));
+}
+
+#define GET_SEG_INTERFACE(seg_id) \
+ seg_interface_t * seg_interface = get_seg_interface(&(s->seg_manager), seg_id); \
+ if (!seg_interface) { \
+ sciprintf("Unknown segment : %x\n", seg_id); \
+ return 1; \
+ }
+
+static int
+c_gc_show_reachable(state_t *s)
+{
+ reg_t addr = cmd_params[0].reg;
+
+ GET_SEG_INTERFACE(addr.segment);
+
+ sciprintf("Reachable from "PREG":\n", PRINT_REG(addr));
+ seg_interface->list_all_outgoing_references(seg_interface, s, addr, NULL, _print_address);
+
+ seg_interface->deallocate_self(seg_interface);
+ return 0;
+}
+
+static int
+c_gc_show_freeable(state_t *s)
+{
+ reg_t addr = cmd_params[0].reg;
+
+ GET_SEG_INTERFACE(addr.segment);
+
+ sciprintf("Freeable in segment %04x:\n", addr.segment);
+ seg_interface->list_all_deallocatable(seg_interface, NULL, _print_address);
+
+ seg_interface->deallocate_self(seg_interface);
+ return 0;
+}
+
+static int
+c_gc_normalise(state_t *s)
+{
+ reg_t addr = cmd_params[0].reg;
+
+ GET_SEG_INTERFACE(addr.segment);
+
+ addr = seg_interface->find_canonic_address(seg_interface, addr);
+ sciprintf(" "PREG"\n", PRINT_REG(addr));
+
+ seg_interface->deallocate_self(seg_interface);
+ return 0;
+
+}
+
+static int
+c_gc(state_t *s)
+{
+ run_gc(s);
+ return 0;
+}
+
+static void
+print_all_of_them(void *_1, reg_t reg, int _2)
+{
+ sciprintf(" - "PREG"\n", PRINT_REG(reg));
+}
+
+static int
+c_gc_list_reachable(state_t *s)
+{
+ reg_t_hash_map_ptr use_map = find_all_used_references(s);
+
+ sciprintf("Reachable references (normalised):\n");
+ apply_to_reg_t_hash_map(use_map, NULL, print_all_of_them);
+
+ free_reg_t_hash_map (use_map);
+}
+
+
+void
+script_debug(state_t *s, reg_t *pc, stack_ptr_t *sp, stack_ptr_t *pp, reg_t *objp,
+ int *restadjust,
+ seg_id_t *segids, reg_t **variables,
+ reg_t **variables_base, int *variables_nr,
+ int bp)
+{
+ int have_windowed = s->gfx_state->driver->capabilities & GFX_CAPABILITY_WINDOWED;
+ static int last_step;
+ /* Do we support a separate console? */
+
+#ifndef WANT_CONSOLE
+ int missing_tty = !isatty(0) || !isatty(1);
+
+ if (!have_windowed || missing_tty) {
+ script_debug_flag = sci_debug_flags = 0;
+
+ fprintf(stderr, "On-screen console disabled and ");
+ if (!have_windowed)
+ fprintf(stderr, "driver claims to be running fullscreen.\n");
+ else
+ fprintf(stderr, "no terminal found.\n");
+
+ if (last_step == script_step_counter)
+ fprintf(stderr, "This error seems to be unrecoverable.\n");
+ if (script_error_flag || script_step_counter == last_step) {
+ fprintf(stderr, "Aborting...\n");
+ exit(1);
+ } else
+ fprintf(stderr, "Continuing...\n");
+ last_step = script_step_counter;
+ return;
+ }
+#endif
+
+
+ if (sci_debug_flags & _DEBUG_FLAG_LOGGING) {
+ int old_debugstate = _debugstate_valid;
+
+ p_var_segs = segids;
+ p_vars = variables;
+ p_var_max = variables_nr;
+ p_var_base = variables_base;
+ p_pc = pc;
+ p_sp = sp;
+ p_pp = pp;
+ p_objp = objp;
+ p_restadjust = restadjust;
+ sciprintf("%d: acc="PREG" ", script_step_counter, PRINT_REG(s->r_acc));
+ _debugstate_valid = 1;
+ disassemble(s, *pc, 0, 1);
+ if (_debug_seeking == _DEBUG_SEEK_GLOBAL)
+ sciprintf("Global %d (0x%x) = "PREG"\n", _debug_seek_special,
+ _debug_seek_special, PRINT_REG(s->script_000->locals_block->locals[_debug_seek_special]));
+
+ _debugstate_valid = old_debugstate;
+
+ if (!script_debug_flag)
+ return;
+ }
+
+ if (_debug_seeking && !bp) { /* Are we looking for something special? */
+ mem_obj_t *memobj = GET_SEGMENT(s->seg_manager, pc->segment, MEM_OBJ_SCRIPT);
+
+ if (memobj) {
+ script_t *scr = &(memobj->data.script);
+ byte *code_buf = scr->buf;
+ int code_buf_size = scr->buf_size;
+ int opcode = pc->offset >= code_buf_size? 0 : code_buf[pc->offset];
+ int op = opcode >> 1;
+ int paramb1 = pc->offset + 1 >= code_buf_size? 0 : code_buf[pc->offset + 1];
+ int paramf1 = (opcode & 1)? paramb1 :
+ (pc->offset + 2 >= code_buf_size? 0 : getInt16(code_buf + pc->offset + 1));
+
+ switch (_debug_seeking) {
+
+ case _DEBUG_SEEK_SPECIAL_CALLK:
+ if (paramb1 != _debug_seek_special)
+ return;
+
+ case _DEBUG_SEEK_CALLK: {
+ if (op != op_callk) return;
+ break;
+ }
+
+ case _DEBUG_SEEK_LEVEL_RET: {
+ if ((op != op_ret) || (_debug_seek_level < s->execution_stack_pos)) return;
+ break;
+ }
+
+ case _DEBUG_SEEK_SO:
+ if (!REG_EQ(*pc,_debug_seek_reg) ||
+ s->execution_stack_pos != _debug_seek_level)
+ return;
+ break;
+
+ case _DEBUG_SEEK_GLOBAL:
+
+ if (op < op_sag)
+ return;
+ if ((op & 0x3) > 1)
+ return; /* param or temp */
+ if ((op & 0x3) && s->execution_stack[s->execution_stack_pos].local_segment > 0)
+ return; /* locals and not running in script.000 */
+ if (paramf1 != _debug_seek_special)
+ return; /* CORRECT global? */
+
+ break;
+
+ } /* switch(_debug_seeking) */
+
+ _debug_seeking = _DEBUG_SEEK_NOTHING; /* OK, found whatever we
+ ** were looking for */
+ }
+ } /* if (_debug_seeking) */
+
+
+ _debugstate_valid = (_debug_step_running == 0);
+
+ if (_debugstate_valid) {
+ p_pc = pc;
+ p_sp = sp;
+ p_pp = pp;
+ p_objp = objp;
+ p_restadjust = restadjust;
+ p_var_segs = segids;
+ p_vars = variables;
+ p_var_max = variables_nr;
+ p_var_base = variables_base;
+
+ c_debuginfo(s);
+ sciprintf("Step #%d\n", script_step_counter);
+ disassemble(s, *pc, 0, 1);
+
+ if (_debug_commands_not_hooked) {
+
+ _debug_commands_not_hooked = 0;
+
+ con_hook_command(c_sfx_remove, "sfx_remove", "!a",
+ "Kills a playing sound.");
+ con_hook_command(c_debuginfo, "registers", "", "Displays all current register values");
+ con_hook_command(c_vmvars, "vmvars", "!sia*", "Displays or changes variables in the VM\n\nFirst parameter is either g(lobal), l(ocal), t(emp) or p(aram).\nSecond parameter is the var number\nThird parameter (if specified) is the value to set the variable to");
+ con_hook_command(c_sci_version, "sci_version", "", "Prints the SCI version currently being emulated");
+ con_hook_command(c_vmvarlist, "vmvarlist", "!", "Displays the addresses of variables in the VM");
+ con_hook_command(c_step, "s", "i*", "Executes one or several operations\n\nEXAMPLES\n\n"
+ " s 4\n\n Execute 4 commands\n\n s\n\n Execute next command");
+#ifdef __GNUC__
+#warning "Re-enable con:so hook"
+#endif
+#if 0
+ con_hook_command(c_stepover, "so", "", "Executes one operation skipping over sends");
+#endif
+ con_hook_command(c_disasm_addr, "disasm-addr", "!as*", "Disassembles one or more commands\n\n"
+ "USAGE\n\n disasm-addr [startaddr] <options>\n\n"
+ " Valid options are:\n"
+ " bwt : Print byte/word tag\n"
+ " c<x> : Disassemble <x> bytes\n"
+ " bc : Print bytecode\n\n");
+ con_hook_command(c_disasm, "disasm", "!as", "Disassembles a method by name\n\nUSAGE\n\n disasm <obj> <method>\n\n");
+ con_hook_command(c_obj, "obj", "!", "Displays information about the\n currently active object/class.\n"
+ "\n\nSEE ALSO\n\n vo.1, accobj.1");
+ con_hook_command(c_accobj, "accobj", "!", "Displays information about an\n object or class at the\n"
+ "address indexed by acc.\n\nSEE ALSO\n\n obj.1, vo.1");
+ con_hook_command(c_classtable, "classtable", "", "Lists all available classes");
+ con_hook_command(c_stack, "stack", "i", "Dumps the specified number of stack elements");
+ con_hook_command(c_backtrace, "bt", "", "Dumps the send/self/super/call/calle/callb stack");
+ con_hook_command(c_snk, "snk", "s*", "Steps forward until it hits the next\n callk operation.\n"
+ " If invoked with a parameter, it will\n look for that specific callk.\n");
+ con_hook_command(c_se, "se", "", "Steps forward until an SCI event is received.\n");
+ con_hook_command(c_listclones, "clonetable", "", "Lists all registered clones");
+ con_hook_command(c_set_acc, "set_acc", "!a", "Sets the accumulator");
+ con_hook_command(c_send, "send", "!asa*", "Sends a message to an object\nExample: send ?fooScript cue");
+ con_hook_command(c_sret, "sret", "", "Steps forward until ret is called\n on the current execution"
+ " stack\n level.");
+ con_hook_command(c_resource_id, "resource_id", "i", "Identifies a resource number by\n"
+ " splitting it up in resource type\n and resource number.");
+ con_hook_command(c_clear_screen, "clear_screen", "", "Clears the screen, shows the\n background pic and picviews");
+ con_hook_command(c_redraw_screen, "redraw_screen", "", "Redraws the screen");
+ con_hook_command(c_debuglog, "debuglog", "!s*", "Sets the debug log modes.\n Possible parameters:\n"
+ " +x (sets debugging for x)\n -x (unsets debugging for x)\n\nPossible values"
+ " for x:\n u: Unimpl'd/stubbed stuff\n l: Lists and nodes\n g: Graphics\n"
+ " c: Character handling\n m: Memory management\n f: Function call checks\n"
+ " b: Bresenham details\n a: Audio\n d: System gfx management\n s: Base setter\n"
+ " p: Parser\n M: The menu system\n S: Said specs\n F: File I/O\n t: GetTime\n"
+ " e: 0.3.3 kernel emulation\n r: Room numbers\n P: Pathfinding\n"
+ " *: Everything\n\n"
+ " If invoked withour parameters,\n it will list all activated\n debug options.\n\n"
+ "SEE ALSO\n"
+ " gfx_debuglog.1, sfx_debuglog.1\n");
+ con_hook_command(c_visible_map, "set_vismap", "i", "Sets the visible map.\n Default is 0 (visual).\n"
+ " Other useful values are:\n 1: Priority\n 2: Control\n 3: Auxiliary\n");
+ con_hook_command(c_simkey, "simkey", "i", "Simulates a keypress with the\n specified scancode.\n");
+ con_hook_command(c_statusbar, "statusbar", "ii", "Sets the colors of the status bar. Also controllable from the script.\n");
+ con_hook_command(c_bpx, "bpx", "s", "Sets a breakpoint on the execution of\n the specified method.\n\n EXAMPLE:\n"
+ " bpx ego::doit\n\n May also be used to set a breakpoint\n that applies whenever an object\n"
+ " of a specific type is touched:\n bpx foo::\n");
+ con_hook_command(c_bpe, "bpe", "ii", "Sets a breakpoint on the execution of specified"
+ " exported function.\n");
+ con_hook_command(c_bplist, "bplist", "", "Lists all breakpoints.\n");
+ con_hook_command(c_bpdel, "bpdel", "i", "Deletes a breakpoint with specified index.");
+ con_hook_command(c_go, "go", "", "Executes the script.\n");
+ con_hook_command(c_dumpnodes, "dumpnodes", "i", "shows the specified number of nodes\n"
+ " from the parse node tree");
+ con_hook_command(c_save_game, "save_game", "s", "Saves the current game state to\n the hard disk");
+ con_hook_command(c_restore_game, "restore_game", "s", "Restores a saved game from the\n hard disk");
+ con_hook_command(c_restart_game, "restart", "s*", "Restarts the game.\n\nUSAGE\n\n restart [-r] [-p]"
+ " [--play] [--replay]\n\n There are two ways to restart an SCI\n game:\n"
+ " play (-p) calls the game object's play()\n method\n replay (-r) calls the replay() method");
+ con_hook_command(c_mousepos, "mousepos", "",
+ "Reveal the location of a mouse click.\n\n");
+ con_hook_command(c_viewinfo, "viewinfo", "ii", "Displays the number of loops\n and cels of each loop"
+ " for the\n specified view resource and palette.");
+ con_hook_command(c_list_sentence_fragments, "list_sentence_fragments", "", "Lists all sentence fragments (which\n"
+ " are used to build Parse trees).");
+ con_hook_command(c_parse, "parse", "s", "Parses a sequence of words and prints\n the resulting parse tree.\n"
+ " The word sequence must be provided as a\n single string.");
+ con_hook_command(c_gnf, "gnf", "", "Displays the Parse grammar\n in strict GNF");
+ con_hook_command(c_set_parse_nodes, "set_parse_nodes", "s*", "Sets the contents of all parse nodes.\n"
+ " Input token must be separated by\n blanks.");
+ con_hook_command(c_sfx_debuglog, "sfx_debuglog", "s*",
+ "Sets or prints the sound subsystem debug\n"
+ "settings\n\n"
+ "USAGE\n\n"
+ " sfx_debuglog {[+|-][p|u|x|b]+}*\n\n"
+ " sfx_debuglog\n\n"
+ " Prints current settings\n\n"
+ " sfx_debuglog +X\n\n"
+ " Activates all debug features listed in X\n\n"
+ " sfx_debuglog -X\n\n"
+ " Deactivates the debug features listed in X\n\n"
+ " Debug features:\n"
+ " s: Active song changes\n"
+ " c: Song cues\n"
+ "SEE ALSO\n"
+ " debuglog.1, gfx_debuglog.1\n");
+ con_hook_command(c_gfx_debuglog, "gfx_debuglog", "s*",
+ "Sets or prints the gfx driver's debug\n"
+ "settings\n\n"
+ "USAGE\n\n"
+ " gfx_debuglog {[+|-][p|u|x|b]+}*\n\n"
+ " gfx_debuglog\n\n"
+ " Prints current settings\n\n"
+ " gfx_debuglog +X\n\n"
+ " Activates all debug features listed in X\n\n"
+ " gfx_debuglog -X\n\n"
+ " Deactivates the debug features listed in X\n\n"
+ " Debug features:\n"
+ " p: Pointer\n"
+ " u: Updates\n"
+ " x: Pixmaps\n"
+ " b: Basic features\n\n"
+ "SEE ALSO\n"
+ " debuglog.1, sfx_debuglog.1\n");
+
+#ifdef SCI_SIMPLE_SAID_CODE
+ con_hook_command(c_sim_parse, "simparse", "s*", "Simulates a parsed entity.\n\nUSAGE\n Call this"
+ " function with a list of\n Said operators, words, and word group"
+ "\n numbers to match Said() specs\n that look identical.\n"
+ "\n Note that opening braces and\n everything behind them are\n"
+ "\n removed from all non-operator\n parameter tokens.\n"
+ "\n simparse without parameters\n removes the entity.\n");
+#endif /* SCI_SIMPLE_SAID_CODE */
+#ifdef GFXW_DEBUG_WIDGETS
+ con_hook_command(c_gfx_print_widget, "gfx_print_widget", "i*", "If called with no parameters, it\n shows which widgets are active.\n"
+ " With parameters, it lists the\n widget corresponding to the\n numerical index specified (for\n each parameter).");
+#endif
+ con_hook_command(c_gfx_flush_resources, "gfx_free_widgets", "", "Frees all dynamically allocated\n widgets (for memory profiling).\n");
+ con_hook_command(c_gfx_current_port, "gfx_current_port", "", "Determines the current port number");
+ con_hook_command(c_gfx_print_port, "gfx_print_port", "i*", "Displays all information about the\n specified port,"
+ " or the current port\n if no port was specified.");
+ con_hook_command(c_gfx_print_visual, "gfx_print_visual", "", "Displays all information about the\n current widget state");
+ con_hook_command(c_gfx_print_dynviews, "gfx_print_dynviews", "", "Shows the dynview list");
+ con_hook_command(c_gfx_print_dropviews, "gfx_print_dropviews", "", "Shows the list of dropped\n dynviews");
+ con_hook_command(c_gfx_drawpic, "gfx_drawpic", "ii*", "Draws a pic resource\n\nUSAGE\n gfx_drawpic <nr> [<pal> [<fl>]]\n"
+ " where <nr> is the number of the pic resource\n to draw\n <pal> is the optional default\n palette for the pic (0 is"
+ "\n assumed if not specified)\n <fl> are any pic draw flags (default\n is 1)");
+ con_hook_command(c_dump_words, "dumpwords", "", "Lists all parser words");
+ con_hook_command(c_gfx_show_map, "gfx_show_map", "i", "Shows one of the screen maps\n Semantics of the int parameter:\n"
+ " 0: visual map (back buffer)\n 1: priority map (back buf.)\n 2: control map (static buf.)");
+ con_hook_command(c_gfx_fill_screen, "gfx_fill_screen", "i", "Fills the screen with one\n of the EGA colors\n");
+ con_hook_command(c_gfx_draw_rect, "gfx_draw_rect", "iiiii", "Draws a rectangle to the screen\n with one of the EGA colors\n\nUSAGE\n\n"
+ " gfx_draw_rect <x> <y> <xl> <yl> <color>");
+ con_hook_command(c_gfx_propagate_rect,
+ "gfx_propagate_rect",
+ "iiiii",
+ "Propagates a lower gfx buffer to a\n"
+ " higher gfx buffer.\n\nUSAGE\n\n"
+ " gfx_propagate_rect <x> <y> <xl> <yl> <buf>\n");
+ con_hook_command(c_gfx_update_zone, "gfx_update_zone", "iiii", "Propagates a rectangular area from\n the back buffer to the front buffer"
+ "\n\nUSAGE\n\n"
+ " gfx_update_zone <x> <y> <xl> <yl>");
+#ifdef __GNUC__
+#warning "Re-enable con:draw_viewobj"
+#endif
+#if 0
+ con_hook_command(c_gfx_draw_viewobj, "draw_viewobj", "i", "Draws the nsRect and brRect of a\n dynview object.\n\n nsRect is green, brRect\n"
+ " is blue.\n");
+#endif
+ con_hook_command(c_gfx_draw_cel, "gfx_draw_cel", "iiii", "Draws a single view\n cel to the center of the\n screen\n\n"
+ "USAGE\n gfx_draw_cel <view> <loop> <cel> <palette>\n");
+ con_hook_command(c_gfx_priority, "gfx_priority", "i*", "Prints information about priority\n bands\nUSAGE\n\n gfx_priority\n\n"
+ " will print the min and max values\n for the priority bands\n\n gfx_priority <val>\n\n Print start of the priority\n"
+ " band for the specified\n priority\n");
+ con_hook_command(c_segtable, "segtable", "!",
+ "Gives a short listing of all segments\n\n"
+ "SEE ALSO\n\n"
+ " seginfo.1");
+ con_hook_command(c_segkill, "segkill", "!i*",
+ "Deletes the specified segment\n\n"
+ "USAGE\n\n"
+ " segkill <nr>\n");
+ con_hook_command(c_seginfo, "seginfo", "!i*",
+ "Explains the specified segment\n\n"
+ "USAGE\n\n"
+ " seginfo\n"
+ " seginfo <nr>\n"
+ " Either explains all active segments\n"
+ " (no parameter) or the specified one.\n\n"
+ "SEE ALSO\n\n"
+ " segtable.1");
+ con_hook_command(c_vo, "vo", "!a",
+ "Examines an object\n\n"
+ "SEE ALSO\n\n"
+ " addresses.3, type.1");
+ con_hook_command(c_vr, "vr", "!aa*",
+ "Examines an arbitrary reference\n\n");
+ con_hook_command(c_sg, "sg", "!i",
+ "Steps until the global variable with the\n"
+ "specified index is modified.\n\nSEE ALSO\n\n"
+ " s.1, snk.1, so.1, bpx.1");
+ con_hook_command(c_songlib_print, "songlib_print", "",
+ "");
+#ifdef HAVE_SYSV_IPC
+ con_hook_command(c_codebug, "codebug", "!s",
+ "Starts codebugging mode\n\nUSAGE\n\n"
+ " codebug path/to/old/freesci\n\n"
+ " A different version of FreeSCI may be\n"
+ " spawned as a child process; all commands\n"
+ " send to this debugger will also be sent to\n"
+ " to the child process.\n\nSEE ALSO\n\n codebugging.3");
+#endif
+ con_hook_command(c_type, "type", "!a",
+ "Determines the type of a value\n\n"
+ "SEE ALSO\n\n addresses.3, vo.1");
+ con_hook_command(c_shownode, "shownode", "!a",
+ "Prints information about a list node\n"
+ " or list base.\n\n");
+ con_hook_command(c_is_sample, "is-sample", "i",
+ "Tests whether a given sound resource\n"
+ " is a PCM sample, and displays infor-\n"
+ " mation on it if it is.\n\n");
+ con_hook_command(c_sfx_01_header, "sfx-01-header", "i",
+ "Dumps the header of an SCI01 song\n\n"
+ "SEE ALSO\n\n"
+ " sfx-01-track.1\n\n");
+ con_hook_command(c_sfx_01_track, "sfx-01-track", "ii",
+ "Dumps a track from an SCI01 song\n\n"
+ "USAGE\n\n"
+ " sfx-01-track <song> <offset>\n\n"
+ "SEE ALSO\n\n"
+ " sfx-01-header.1\n\n");
+ con_hook_command(c_sleep, "sleep", "i", "Suspends everything for the\n"
+ " specified number of seconds");
+ con_hook_command(c_gc_show_reachable, "gc-list-reachable", "!a",
+ "Prints all addresses directly reachable from\n"
+ " the memory object specified as parameter.\n\n"
+ "SEE ALSO\n\n"
+ " gc-list-freeable.1, gc-normalise.1, gc.1,\n"
+ " gc-all-reachable.1");
+ con_hook_command(c_gc_show_freeable, "gc-list-freeable", "!a",
+ "Prints all addresses freeable in the segment\n"
+ " associated with the address (offset is ignored).\n\n"
+ "SEE ALSO\n\n"
+ " gc-list-freeable.1, gc-normalise.1, gc.1,\n"
+ " gc-all-reachable.1");
+ con_hook_command(c_gc_normalise, "gc-normalise", "!a",
+ "Prints the \"normal\" address of a given address,\n"
+ " i.e. the address we would free in order to free\n"
+ " the object associated with the original address.\n\n"
+ "SEE ALSO\n\n"
+ " gc-list-freeable.1, gc-list-reachable.1, gc.1,\n"
+ " gc-all-reachable.1");
+ con_hook_command(c_gc, "gc", "",
+ "Performs garbage collection.\n\n"
+ "SEE ALSO\n\n"
+ " gc-list-freeable.1, gc-list-reachable.1,\n"
+ " gc-all-reachable.1, gc-normalise.1");
+ con_hook_command(c_gc_list_reachable, "gc-all-reachable", "",
+ "Lists all reachable objects, normalised.\n\n"
+ "SEE ALSO\n\n"
+ " gc-list-freeable.1, gc-list-reachable.1,\n"
+ " gc.1, gc-normalise.1");
+
+
+ con_hook_int(&script_debug_flag, "script_debug_flag", "Set != 0 to enable debugger\n");
+ con_hook_int(&script_checkloads_flag, "script_checkloads_flag", "Set != 0 to display information\n"
+ " when scripts are loaded or unloaded");
+ con_hook_int(&script_abort_flag, "script_abort_flag", "Set != 0 to abort execution\n");
+ con_hook_int(&script_step_counter, "script_step_counter", "# of executed SCI operations\n");
+ con_hook_int(&sci_debug_flags, "debug_flags", "Debug flags:\n 0x0001: Log each command executed\n"
+ " 0x0002: Break on warnings\n \0x0004: Print VM warnings\n");
+ con_hook_int(&_weak_validations, "weak_validations", "Set != 0 to turn some validation errors\n"
+ " into warnings\n");
+
+ con_hook_int(&script_gc_interval, "gc-interval", "Number of kernel calls in between gcs");
+ con_hook_int(&debug_sleeptime_factor, "sleep-factor", "Factor to multiply with wait times\n Set to 0 to speed up games");
+
+ con_hook_page("codebugging",
+ "Co-debugging allows to run two (sufficiently\n"
+ " recent) versions of FreeSCI concurrently,\n"
+ " with one acting as a client of the other.\n"
+ " Co-debugging can be started by calling\n"
+ " 'codebug' (see codebug.1); note that the\n"
+ " argument passed to it must be a version of\n"
+ " FreeSCI that performs fflush(NULL) before\n"
+ " each read; only late 0.3.3-devel and later\n"
+ " have this property.\n\n"
+ " In co-debug mode, all commands are sent to\n"
+ " both programs, UNLESS one of the following\n"
+ " prefixes is used:\n\n"
+ " '.' : Only sends to the foreground version\n"
+ " ':' : Only sends to tbe background version\n\n"
+ " For example, when running 0.3.3 from within\n"
+ " 0.6.0, \".version\" would determine the version\n"
+ " as 0.6.0, and \"0.3.3\" would be returned for\n"
+ " \":version\". Both versions would be print\n"
+ " if only \"version\" was invoked, each result\n"
+ " coming from a different process.\n\n"
+ "COLORS\n\n"
+ " Whenever possible, the background process will\n"
+ " have its output marked by a non-default color\n"
+ " (usually red).\n\n"
+ "TROUBLESHOOTING\n\n"
+ " If the background version appears to be silent,\n"
+ " make sure it is calling fflush(NULL) before\n"
+ " reading input.\n\n"
+ "SEE ALSO\n\n"
+ " codebug.1");
+
+ } /* If commands were not hooked up */
+
+ }
+
+ if (_debug_step_running)
+ _debug_step_running--;
+
+ while (_debugstate_valid) {
+ int skipfirst = 0;
+ const char *commandstring;
+#ifdef WANT_CONSOLE
+ char *input;
+#endif
+
+ /* Suspend music playing */
+ sfx_suspend(&s->sound, 1);
+
+#ifdef WANT_CONSOLE
+# ifndef FORCE_CONSOLE
+ if (!have_windowed) {
+# endif
+ con_gfx_show(s->gfx_state);
+ input = con_gfx_read(s->gfx_state);
+ con_gfx_hide(s->gfx_state);
+ commandstring = input;
+ sciprintf("> %s\n", commandstring);
+# ifndef FORCE_CONSOLE
+ } else
+# endif
+#endif
+#ifndef FORCE_CONSOLE
+ commandstring = _debug_get_input();
+
+ /* Check if a specific destination has been given */
+ if (commandstring
+ && (commandstring[0] == '.'
+ || commandstring[0] == ':'))
+ skipfirst = 1;
+
+#endif
+#ifdef HAVE_SYSV_IPC
+ if (commandstring
+ && commandstring[0] != '.') {
+ codebug_send_command(commandstring + skipfirst);
+ codebug_send_command("\n");
+ }
+#endif
+ if (commandstring
+ && commandstring[0] != ':')
+ con_parse(s, commandstring + skipfirst);
+ sciprintf("\n");
+
+ /* Resume music playing */
+ sfx_suspend(&s->sound, 0);
+ }
+}
+
diff --git a/engines/sci/engine/seg_manager.c b/engines/sci/engine/seg_manager.c
new file mode 100644
index 0000000000..8bc6187a22
--- /dev/null
+++ b/engines/sci/engine/seg_manager.c
@@ -0,0 +1,2063 @@
+/***************************************************************************
+ seg_manager.c Copyright (C) 2002 Xiaojun Chen, 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 <seg_manager.h>
+#include <sciresource.h>
+#include <versions.h>
+#include <engine.h>
+
+
+/*#define GC_DEBUG*/ /* Debug garbage collection */
+/*#define GC_DEBUG_VERBOSE*/ /* Debug garbage verbosely */
+
+#define SM_MEMORY_POISON /* Poison memory upon deallocation */
+
+mem_obj_t* mem_obj_allocate(seg_manager_t *self, seg_id_t segid, int hash_id, mem_obj_enum type);
+
+#undef DEBUG_SEG_MANAGER /* Define to turn on debugging */
+#define GET_SEGID() if (flag == SCRIPT_ID) \
+ id = sm_seg_get (self, id); \
+ VERIFY ( sm_check (self, id), "invalid seg id" );
+#define VERIFY_MEM( mem_ptr, ret ) if (! (mem_ptr) ) {\
+ sciprintf( "%s, *d, no enough memory", __FILE__, __LINE__ ); \
+ return ret; \
+ }
+
+#define INVALID_SCRIPT_ID -1
+
+void dbg_print( const char* msg, void *i ) {
+#ifdef DEBUG_SEG_MANAGER
+ char buf[1000];
+ sprintf( buf, "%s = [0x%x], dec:[%d]", msg, i, i);
+ perror( buf );
+#endif
+}
+
+/*--------------------------*/
+/*-- forward declarations --*/
+/*--------------------------*/
+
+void
+sm_script_initialise_locals_zero(seg_manager_t *self, seg_id_t seg, int count);
+
+void
+sm_script_initialise_locals(seg_manager_t *self, reg_t location);
+
+static int
+_sm_deallocate (seg_manager_t* self, int seg, int recursive);
+
+static hunk_t *
+sm_alloc_hunk(seg_manager_t *self, reg_t *);
+
+static void
+sm_free_hunk(seg_manager_t *self, reg_t addr);
+
+static int
+sm_check(seg_manager_t* self, int seg);
+/* Check segment validity
+** Parameters: (int) seg: The segment to validate
+** Returns : (int) 0 if 'seg' is an invalid segment
+** 1 if 'seg' is a valid segment
+*/
+
+/*******************************/
+/** End of Memory Management **/
+/*******************************/
+
+static inline int
+find_free_id(seg_manager_t *self, int *id)
+{
+ char was_added = 0;
+ int retval;
+
+ while (!was_added) {
+ retval = int_hash_map_check_value(self->id_seg_map, self->reserved_id,
+ 1, &was_added);
+ *id = self->reserved_id--;
+ if (self->reserved_id < -1000000)
+ self->reserved_id = -10;
+ /* Make sure we don't underflow */
+ }
+
+ return retval;
+}
+
+static mem_obj_t *
+alloc_nonscript_segment(seg_manager_t *self, mem_obj_enum type, seg_id_t *segid)
+{ /* Allocates a non-script segment */
+ int id;
+ *segid = find_free_id(self, &id);
+ return mem_obj_allocate(self, *segid, id, type);
+}
+
+
+void sm_init(seg_manager_t* self, int sci1_1) {
+ int i;
+
+ self->mem_allocated = 0; /* Initialise memory count */
+
+ self->id_seg_map = new_int_hash_map();
+ self->reserved_id = INVALID_SCRIPT_ID;
+ int_hash_map_check_value (self->id_seg_map, self->reserved_id, 1, NULL); /* reserve 0 for seg_id */
+ self->reserved_id--; /* reserved_id runs in the reversed direction to make sure no one will use it. */
+
+ self->heap_size = DEFAULT_SCRIPTS;
+ self->heap = (mem_obj_t**) sci_calloc(self->heap_size, sizeof(mem_obj_t *));
+
+ self->clones_seg_id = 0;
+ self->lists_seg_id = 0;
+ self->nodes_seg_id = 0;
+ self->hunks_seg_id = 0;
+
+ self->exports_wide = 0;
+ self->sci1_1 = sci1_1;
+
+ /* initialize the heap pointers*/
+ for (i = 0; i < self->heap_size; i++) {
+ self->heap[i] = NULL;
+ }
+
+ /* gc initialisation */
+ self->gc_mark_bits = 0;
+}
+
+/* destroy the object, free the memorys if allocated before */
+void sm_destroy (seg_manager_t* self) {
+ int i;
+ /* free memory*/
+ for (i = 0; i < self->heap_size; i++) {
+ if (self->heap[i])
+ _sm_deallocate(self, i, 0);
+ }
+
+ free_int_hash_map(self->id_seg_map);
+
+ sci_free (self->heap);
+ self->heap = NULL;
+}
+
+/* allocate a memory for script from heap
+** Parameters: (state_t *) s: The state to operate on
+** (int) script_nr: The script number to load
+** Returns : 0 - allocation failure
+** 1 - allocated successfully
+** seg_id - allocated segment id
+*/
+mem_obj_t* sm_allocate_script (seg_manager_t* self, struct _state *s, int script_nr, int* seg_id) {
+ int seg;
+ char was_added;
+ mem_obj_t* mem;
+
+ seg = int_hash_map_check_value (self->id_seg_map, script_nr, 1, &was_added);
+ if (!was_added) {
+ *seg_id = seg;
+ return self->heap[*seg_id];
+ }
+
+ /* allocate the mem_obj_t */
+ mem = mem_obj_allocate(self, seg, script_nr, MEM_OBJ_SCRIPT);
+ if (!mem) {
+ sciprintf("%s, %d, Not enough memory, ", __FILE__, __LINE__ );
+ return NULL;
+ }
+
+ *seg_id = seg;
+ return mem;
+}
+
+static void sm_set_script_size(mem_obj_t *mem, struct _state *s, int script_nr)
+{
+ resource_t *script = scir_find_resource(s->resmgr, sci_script, script_nr, 0);
+ resource_t *heap = scir_find_resource(s->resmgr, sci_heap, script_nr, 0);
+
+ mem->data.script.script_size = script->size;
+ mem->data.script.heap_size = 0; /* Set later */
+
+ if (!script || (s->version >= SCI_VERSION(1,001,000) && !heap))
+ {
+ sciprintf("%s: failed to load %s\n", __FUNCTION__,
+ !script ? "script" : "heap");
+ return;
+ }
+ if (s->version < SCI_VERSION_FTU_NEW_SCRIPT_HEADER) {
+ mem->data.script.buf_size = script->size + getUInt16(script->data)*2;
+ /* locals_size = getUInt16(script->data)*2; */
+ }
+ else if (s->version < SCI_VERSION(1,001,000)) {
+ mem->data.script.buf_size = script->size;
+ }
+ else
+ {
+ mem->data.script.buf_size = script->size + heap->size;
+ mem->data.script.heap_size = heap->size;
+
+ /* Ensure that the start of the heap resource can be word-aligned. */
+ if (script->size & 2)
+ {
+ mem->data.script.buf_size++;
+ mem->data.script.script_size++;
+ }
+
+ if (mem->data.script.buf_size > 65535)
+ {
+ sciprintf("Script and heap sizes combined exceed 64K.\n"
+ "This means a fundamental design bug was made in FreeSCI\n"
+ "regarding SCI1.1 games.\nPlease report this so it can be"
+ "fixed in the next major version!\n");
+ return;
+ }
+ }
+}
+
+int sm_initialise_script(mem_obj_t *mem, struct _state *s, int script_nr)
+{
+ /* allocate the script.buf */
+ script_t *scr;
+
+ sm_set_script_size(mem, s, script_nr);
+ mem->data.script.buf = (byte*) sci_malloc (mem->data.script.buf_size);
+
+ dbg_print( "mem->data.script.buf ", mem->data.script.buf );
+ if (!mem->data.script.buf) {
+ sm_free_script ( mem );
+ sciprintf("seg_manager.c: Not enough memory space for script size" );
+ mem->data.script.buf_size = 0;
+ return 0;
+ }
+
+ /* Initialize objects */
+ scr = &(mem->data.script);
+ scr->objects = NULL;
+ scr->objects_allocated = 0;
+ scr->objects_nr = 0; /* No objects recorded yet */
+
+ scr->locals_offset = 0;
+ scr->locals_block = NULL;
+
+ scr->code = NULL;
+ scr->code_blocks_nr = 0;
+ scr->code_blocks_allocated = 0;
+
+ scr->nr = script_nr;
+ scr->marked_as_deleted = 0;
+ scr->relocated = 0;
+
+ scr->obj_indices = new_int_hash_map();
+
+ if (s->version >= SCI_VERSION(1,001,000))
+ scr->heap_start = scr->buf + scr->script_size;
+ else
+ scr->heap_start = scr->buf;
+
+ return 1;
+}
+
+int
+_sm_deallocate (seg_manager_t* self, int seg, int recursive)
+{
+ mem_obj_t *mobj;
+ VERIFY ( sm_check (self, seg), "invalid seg id" );
+
+ mobj = self->heap[seg];
+ int_hash_map_remove_value( self->id_seg_map, mobj->segmgr_id );
+
+ switch (mobj->type) {
+
+ case MEM_OBJ_SCRIPT:
+ sm_free_script ( mobj );
+
+ mobj->data.script.buf = NULL;
+ if (recursive && mobj->data.script.locals_segment)
+ _sm_deallocate(self, mobj->data.script.locals_segment,
+ recursive);
+ break;
+
+ case MEM_OBJ_LOCALS:
+ sci_free(mobj->data.locals.locals);
+ mobj->data.locals.locals = NULL;
+ break;
+
+ case MEM_OBJ_DYNMEM:
+ if (mobj->data.dynmem.buf)
+ sci_free(mobj->data.dynmem.buf);
+ mobj->data.dynmem.buf = NULL;
+ break;
+ case MEM_OBJ_SYS_STRINGS:
+ sys_string_free_all(&(mobj->data.sys_strings));
+ break;
+ case MEM_OBJ_STACK:
+ sci_free(mobj->data.stack.entries);
+ mobj->data.stack.entries = NULL;
+ break;
+ case MEM_OBJ_LISTS:
+ sci_free(mobj->data.lists.table);
+ mobj->data.lists.table = NULL;
+ mobj->data.lists.entries_nr = mobj->data.lists.max_entry = 0;
+ break;
+ case MEM_OBJ_NODES:
+ sci_free(mobj->data.nodes.table);
+ mobj->data.nodes.table = NULL;
+ mobj->data.nodes.entries_nr = mobj->data.nodes.max_entry = 0;
+ break;
+ case MEM_OBJ_CLONES:
+ sci_free(mobj->data.clones.table);
+ mobj->data.clones.table = NULL;
+ mobj->data.clones.entries_nr = mobj->data.clones.max_entry = 0;
+ break;
+ case MEM_OBJ_HUNK:
+ sci_free(mobj->data.hunks.table);
+ mobj->data.hunks.table = NULL;
+ mobj->data.hunks.entries_nr = mobj->data.hunks.max_entry = 0;
+ break;
+ case MEM_OBJ_RESERVED:
+ sci_free(mobj->data.reserved);
+ break;
+ default:
+ fprintf(stderr, "Deallocating segment type %d not supported!\n",
+ mobj->type);
+ BREAKPOINT();
+ }
+
+ free(mobj);
+ self->heap[seg] = NULL;
+
+ return 1;
+}
+
+int sm_script_marked_deleted(seg_manager_t* self, int script_nr)
+{
+ script_t *scr;
+ int seg = sm_seg_get( self, script_nr );
+ VERIFY ( sm_check (self, seg), "invalid seg id" );
+
+ scr = &(self->heap[seg]->data.script);
+ return scr->marked_as_deleted;
+}
+
+void sm_mark_script_deleted(seg_manager_t* self, int script_nr)
+{
+ script_t *scr;
+ int seg = sm_seg_get( self, script_nr );
+ VERIFY ( sm_check (self, seg), "invalid seg id" );
+
+ scr = &(self->heap[seg]->data.script);
+ scr->marked_as_deleted = 1;
+}
+
+void sm_unmark_script_deleted(seg_manager_t* self, int script_nr)
+{
+ script_t *scr;
+ int seg = sm_seg_get( self, script_nr );
+ VERIFY ( sm_check (self, seg), "invalid seg id" );
+
+ scr = &(self->heap[seg]->data.script);
+ scr->marked_as_deleted = 0;
+}
+
+int
+sm_script_is_marked_as_deleted(seg_manager_t* self, seg_id_t seg)
+{
+ script_t *scr;
+
+ if (!sm_check (self, seg))
+ return 0;
+
+ if (self->heap[seg]->type != MEM_OBJ_SCRIPT)
+ return 0;
+
+ scr = &(self->heap[seg]->data.script);
+ return scr->marked_as_deleted;
+}
+
+
+int sm_deallocate_script (seg_manager_t* self, int script_nr) {
+ int seg = sm_seg_get( self, script_nr );
+
+ _sm_deallocate(self, seg, 1);
+ return 1;
+}
+
+mem_obj_t*
+mem_obj_allocate(seg_manager_t *self, seg_id_t segid, int hash_id, mem_obj_enum type)
+{
+ mem_obj_t* mem = ( mem_obj_t* ) sci_calloc( sizeof (mem_obj_t), 1 );
+ if( !mem ) {
+ sciprintf( "seg_manager.c: invalid mem_obj " );
+ return NULL;
+ }
+
+ if (segid >= self->heap_size) {
+ void *temp;
+ int oldhs = self->heap_size;
+
+ if (segid >= self->heap_size * 2) {
+ sciprintf( "seg_manager.c: hash_map error or others??" );
+ return NULL;
+ }
+ self->heap_size *= 2;
+ temp = sci_realloc ((void*)self->heap, self->heap_size * sizeof( mem_obj_t* ) );
+ if (!temp) {
+ sciprintf("seg_manager.c: Not enough memory space for script size" );
+ return NULL;
+ }
+ self->heap = (mem_obj_t**) temp;
+
+ /* Clear pointers */
+ memset(self->heap + oldhs, 0, sizeof(mem_obj_t *) * (self->heap_size - oldhs));
+ }
+
+ mem->segmgr_id = hash_id;
+ mem->type = type;
+
+ /* hook it to the heap */
+ self->heap[segid] = mem;
+ return mem;
+}
+
+/* No longer in use? */
+/* void sm_object_init (object_t* object) { */
+/* if( !object ) return; */
+/* object->variables_nr = 0; */
+/* object->variables = NULL; */
+/* }; */
+
+void
+sm_free_script ( mem_obj_t* mem )
+{
+ if( !mem ) return;
+ if( mem->data.script.buf ) {
+ sci_free( mem->data.script.buf );
+ mem->data.script.buf = NULL;
+ mem->data.script.buf_size = 0;
+ }
+ if( mem->data.script.objects ) {
+ int i;
+ for( i = 0; i < mem->data.script.objects_nr; i++ ) {
+ object_t* object = &mem->data.script.objects[i];
+ if( object->variables ) {
+ free( object->variables );
+ object->variables = NULL;
+ object->variables_nr = 0;
+ }
+ }
+ free( mem->data.script.objects );
+ mem->data.script.objects = NULL;
+ mem->data.script.objects_nr = 0;
+ }
+
+ free_int_hash_map(mem->data.script.obj_indices);
+ if (NULL != mem->data.script.code) {
+ sci_free(mem->data.script.code);
+ }
+}
+
+/* memory operations */
+static void
+sm_mset (seg_manager_t* self, int offset, int c, size_t n, int id, int flag) {
+ mem_obj_t* mem_obj;
+ GET_SEGID();
+ mem_obj = self->heap[id];
+ switch (mem_obj->type) {
+ case MEM_OBJ_SCRIPT:
+ if (mem_obj->data.script.buf) {
+ memset( mem_obj->data.script.buf + offset, c, n );
+ }
+ break;
+ case MEM_OBJ_CLONES:
+ sciprintf( "memset for clones haven't been implemented\n" );
+ break;
+ default:
+ sciprintf( "unknown mem obj type\n" );
+ break;
+ }
+}
+
+static void
+sm_mcpy_in_in (seg_manager_t* self, int dst, const int src, size_t n, int id, int flag) {
+ mem_obj_t* mem_obj;
+ GET_SEGID();
+ mem_obj = self->heap[id];
+ switch (mem_obj->type) {
+ case MEM_OBJ_SCRIPT:
+ if (mem_obj->data.script.buf) {
+ memcpy ( mem_obj->data.script.buf + dst, mem_obj->data.script.buf + src, n );
+ }
+ break;
+ case MEM_OBJ_CLONES:
+ sciprintf( "memcpy for clones haven't been implemented\n" );
+ break;
+ default:
+ sciprintf( "unknown mem obj type\n" );
+ break;
+ }
+}
+
+void
+sm_mcpy_in_out (seg_manager_t* self, int dst, const void* src, size_t n, int id, int flag) {
+ mem_obj_t* mem_obj;
+ GET_SEGID();
+ mem_obj = self->heap[id];
+ switch (mem_obj->type) {
+ case MEM_OBJ_SCRIPT:
+ if (mem_obj->data.script.buf) {
+ memcpy ( mem_obj->data.script.buf + dst, src, n );
+ }
+ break;
+ case MEM_OBJ_CLONES:
+ sciprintf( "memcpy for clones hasn't been implemented yet\n" );
+ break;
+ default:
+ sciprintf( "unknown mem obj type\n" );
+ break;
+ }
+}
+
+static void
+sm_mcpy_out_in (seg_manager_t* self, void* dst, const int src, size_t n, int id, int flag) {
+ mem_obj_t* mem_obj;
+ GET_SEGID();
+ mem_obj = self->heap[id];
+ switch (mem_obj->type) {
+ case MEM_OBJ_SCRIPT:
+ if (mem_obj->data.script.buf) {
+ memcpy ( dst, mem_obj->data.script.buf + src, n );
+ }
+ break;
+ case MEM_OBJ_CLONES:
+ sciprintf( "memcpy for clones hasn't been implemented yet\n" );
+ break;
+ default:
+ sciprintf( "unknown mem obj type\n" );
+ break;
+ }
+}
+
+gint16
+sm_get_heap (seg_manager_t* self, reg_t reg)
+{
+ mem_obj_t* mem_obj;
+ mem_obj_enum mem_type;
+
+ VERIFY( sm_check (self, reg.segment), "Invalid seg id" );
+ mem_obj = self->heap[reg.segment];
+ mem_type = mem_obj->type;
+
+ switch( mem_type ) {
+ case MEM_OBJ_SCRIPT:
+ VERIFY( reg.offset + 1 < mem_obj->data.script.buf_size, "invalid offset\n" );
+ return (unsigned char)mem_obj->data.script.buf[reg.offset] |
+ ( ((unsigned char)mem_obj->data.script.buf[reg.offset+1]) << 8 );
+ case MEM_OBJ_CLONES:
+ sciprintf( "memcpy for clones hasn't been implemented yet\n" );
+ break;
+ default:
+ sciprintf( "unknown mem obj type\n" );
+ break;
+ }
+ return 0; /* never get here */
+}
+
+void sm_put_heap (seg_manager_t* self, reg_t reg, gint16 value ) {
+ mem_obj_t* mem_obj;
+ mem_obj_enum mem_type;
+
+ VERIFY( sm_check (self, reg.segment), "Invalid seg id" );
+ mem_obj = self->heap[reg.segment];
+ mem_type = mem_obj->type;
+
+ switch( mem_type ) {
+ case MEM_OBJ_SCRIPT:
+ VERIFY( reg.offset + 1 < mem_obj->data.script.buf_size, "invalid offset" );
+ mem_obj->data.script.buf[reg.offset] = value & 0xff;
+ mem_obj->data.script.buf[reg.offset + 1] = value >> 8;
+ break;
+ case MEM_OBJ_CLONES:
+ sciprintf( "memcpy for clones haven't been implemented\n" );
+ break;
+ default:
+ sciprintf( "unknown mem obj type\n" );
+ break;
+ }
+}
+
+/* return the seg if script_id is valid and in the map, else -1 */
+int sm_seg_get (seg_manager_t* self, int script_id)
+{
+ return int_hash_map_check_value (self->id_seg_map, script_id, 0, NULL);
+}
+
+/* validate the seg
+** return:
+** 0 - invalid seg
+** 1 - valid seg
+*/
+static int
+sm_check (seg_manager_t* self, int seg) {
+ if ( seg < 0 || seg >= self->heap_size ) {
+ return 0;
+ }
+ if (!self->heap[seg]) {
+ sciprintf("seg_manager.c: seg %x is removed from memory, but not removed from hash_map\n", seg );
+ return 0;
+ }
+ return 1;
+}
+
+int sm_script_is_loaded (seg_manager_t* self, int id, id_flag flag) {
+ if (flag == SCRIPT_ID)
+ id = sm_seg_get (self, id);
+ return sm_check (self, id);
+}
+
+void sm_increment_lockers (seg_manager_t* self, int id, id_flag flag) {
+ if (flag == SCRIPT_ID)
+ id = sm_seg_get (self, id);
+ VERIFY ( sm_check (self, id), "invalid seg id" );
+ self->heap[id]->data.script.lockers++;
+}
+
+void sm_decrement_lockers (seg_manager_t* self, int id, id_flag flag) {
+ if (flag == SCRIPT_ID)
+ id = sm_seg_get (self, id);
+ VERIFY ( sm_check (self, id), "invalid seg id" );
+
+ if (self->heap[id]->data.script.lockers > 0)
+ self->heap[id]->data.script.lockers--;
+}
+
+int sm_get_lockers (seg_manager_t* self, int id, id_flag flag) {
+ if (flag == SCRIPT_ID)
+ id = sm_seg_get (self, id);
+ VERIFY ( sm_check (self, id), "invalid seg id" );
+ return self->heap[id]->data.script.lockers;
+}
+
+void sm_set_lockers (seg_manager_t* self, int lockers, int id, id_flag flag) {
+ if (flag == SCRIPT_ID)
+ id = sm_seg_get (self, id);
+ VERIFY ( sm_check (self, id), "invalid seg id" );
+ self->heap[id]->data.script.lockers = lockers;
+}
+
+void
+sm_set_export_table_offset (struct _seg_manager_t* self, int offset, int id, id_flag flag)
+{
+ script_t *scr = &(self->heap[id]->data.script);
+
+ GET_SEGID();
+ if (offset) {
+ scr->export_table = (guint16 *)(scr->buf + offset + 2);
+ scr->exports_nr = getUInt16((byte *)(scr->export_table - 1));
+ } else {
+ scr->export_table = NULL;
+ scr->exports_nr = 0;
+ }
+}
+
+int
+sm_hash_segment_data(struct _seg_manager_t* self, int id)
+{
+ int i, len, hash_code = 0x55555555;
+ char *buf;
+
+ if (self->heap[id]->type == MEM_OBJ_LISTS) return 0;
+ if (self->heap[id]->type == MEM_OBJ_NODES) return 0;
+ if (self->heap[id]->type == MEM_OBJ_CLONES) return 0;
+ buf = (char*)sm_dereference(self, make_reg(id, 0), &len);
+
+ for (i = 0; i < len; i++)
+ hash_code = (hash_code * 19) + *(buf + i);
+
+ return hash_code;
+}
+
+void
+sm_set_export_width(struct _seg_manager_t* self, int flag)
+{
+ self->exports_wide = flag;
+}
+
+static guint16 *
+sm_get_export_table_offset (struct _seg_manager_t* self, int id, int flag, int *max)
+{
+ GET_SEGID();
+ if (max)
+ *max = self->heap[id]->data.script.exports_nr;
+ return self->heap[id]->data.script.export_table;
+}
+
+void
+sm_set_synonyms_offset (struct _seg_manager_t* self, int offset, int id, id_flag flag) {
+ GET_SEGID();
+ self->heap[id]->data.script.synonyms =
+ self->heap[id]->data.script.buf + offset;
+}
+
+byte *
+sm_get_synonyms(seg_manager_t *self, int id, id_flag flag)
+{
+ GET_SEGID();
+ return self->heap[id]->data.script.synonyms;
+}
+
+void
+sm_set_synonyms_nr (struct _seg_manager_t* self, int nr, int id, id_flag flag) {
+ GET_SEGID();
+ self->heap[id]->data.script.synonyms_nr = nr;
+}
+
+int
+sm_get_synonyms_nr (struct _seg_manager_t* self, int id, id_flag flag)
+{
+ GET_SEGID();
+ return self->heap[id]->data.script.synonyms_nr;
+}
+
+static int
+sm_get_heappos (struct _seg_manager_t* self, int id, int flag)
+{
+ GET_SEGID();
+ return 0;
+}
+
+static void
+sm_set_variables (struct _seg_manager_t* self, reg_t reg, int obj_index, reg_t variable_reg, int variable_index ) {
+ script_t* script;
+ VERIFY ( sm_check (self, reg.segment), "invalid seg id" );
+ VERIFY ( self->heap[reg.segment], "invalid mem" );
+
+ script = &(self->heap[reg.segment]->data.script);
+
+ VERIFY( obj_index < script->objects_nr, "Invalid obj_index" );
+
+ VERIFY( variable_index >= 0
+ && variable_index < script->objects[obj_index].variables_nr,
+ "Attempt to write to invalid variable number" );
+
+ script->objects[obj_index].variables[variable_index] = variable_reg;
+}
+
+
+static inline int
+_relocate_block(seg_manager_t *self, reg_t *block, int block_location, int block_items, seg_id_t segment, int location)
+{
+ int rel = location - block_location;
+ int index;
+
+ if (rel < 0)
+ return 0;
+
+ index = rel >> 1;
+
+ if (index >= block_items)
+ return 0;
+
+ if (rel & 1) {
+ sciprintf("Error: Attempt to relocate odd variable #%d.5e (relative to %04x)\n",
+ index, block_location);
+ return 0;
+ }
+ block[index].segment = segment; /* Perform relocation */
+ if (self->sci1_1)
+ block[index].offset += self->heap[segment]->data.script.script_size;
+
+ return 1;
+}
+
+static inline int
+_relocate_local(seg_manager_t *self, script_t *scr, seg_id_t segment, int location)
+{
+ if (scr->locals_block)
+ return _relocate_block(self,
+ scr->locals_block->locals, scr->locals_offset,
+ scr->locals_block->nr,
+ segment, location);
+ else
+ return 0; /* No hands, no cookies */
+}
+
+static inline int
+_relocate_object(seg_manager_t *self, object_t *obj, seg_id_t segment, int location)
+{
+ return _relocate_block(self,
+ obj->variables, obj->pos.offset, obj->variables_nr,
+ segment, location);
+}
+
+void
+sm_script_add_code_block(seg_manager_t *self, reg_t location)
+{
+ mem_obj_t *mobj = self->heap[location.segment];
+ script_t *scr;
+ int index;
+
+ VERIFY( !(location.segment >= self->heap_size || mobj->type != MEM_OBJ_SCRIPT),
+ "Attempt to add a code block to non-script\n" );
+
+ scr = &(mobj->data.script);
+
+ if (++scr->code_blocks_nr > scr->code_blocks_allocated)
+ {
+ scr->code_blocks_allocated += DEFAULT_OBJECTS_INCREMENT;
+ scr->code = (code_block_t*)sci_realloc(scr->code, scr->code_blocks_allocated *
+ sizeof(code_block_t));
+ }
+
+ index = scr->code_blocks_nr - 1;
+ scr->code[index].pos = location;
+ scr->code[index].size = getUInt16(scr->buf + location.offset - 2);
+}
+
+void
+sm_script_relocate(seg_manager_t *self, reg_t block)
+{
+ mem_obj_t *mobj = self->heap[block.segment];
+ script_t *scr;
+ int count;
+ int i;
+
+ VERIFY( !(block.segment >= self->heap_size || mobj->type != MEM_OBJ_SCRIPT),
+ "Attempt relocate non-script\n" );
+
+ scr = &(mobj->data.script);
+
+ VERIFY( block.offset < scr->buf_size
+ && getUInt16(scr->buf + block.offset)*2 + block.offset < scr->buf_size,
+ "Relocation block outside of script\n" );
+
+ count = getUInt16(scr->buf + block.offset);
+
+ for (i = 0; i <= count; i++) {
+ int pos = getUInt16(scr->buf + block.offset + 2 + (i*2));
+ if (!pos) continue; /* FIXME: A hack pending investigation */
+
+ if (!_relocate_local(self, scr, block.segment, pos)) {
+ int k, done = 0;
+
+ for (k = 0; !done && k < scr->objects_nr; k++) {
+ if (_relocate_object(self, scr->objects + k, block.segment, pos))
+ done = 1;
+ }
+
+ for (k = 0; !done && k < scr->code_blocks_nr; k++) {
+ if (pos >= scr->code[k].pos.offset &&
+ pos < scr->code[k].pos.offset+scr->code[k].size)
+ done = 1;
+ }
+
+ if (!done) {
+ sciprintf("While processing relocation block "PREG":\n",
+ PRINT_REG(block));
+ sciprintf("Relocation failed for index %04x (%d/%d)\n", pos, i+1, count);
+ if (scr->locals_block)
+ sciprintf("- locals: %d at %04x\n",
+ scr->locals_block->nr,
+ scr->locals_offset);
+ else
+ sciprintf("- No locals\n");
+ for (k = 0; k < scr->objects_nr; k++)
+ sciprintf("- obj#%d at %04x w/ %d vars\n",
+ k,
+ scr->objects[k].pos.offset,
+ scr->objects[k].variables_nr);
+// SQ3 script 71 has broken relocation entries.
+// Since this is mainstream, we can't break out as we used to do.
+ sciprintf("Trying to continue anyway...\n");
+// BREAKPOINT();
+ }
+ }
+ }
+}
+
+void
+sm_heap_relocate(seg_manager_t *self, state_t *s, reg_t block)
+{
+ mem_obj_t *mobj = self->heap[block.segment];
+ script_t *scr;
+ int count;
+ int i;
+
+ VERIFY( !(block.segment >= self->heap_size || mobj->type != MEM_OBJ_SCRIPT),
+ "Attempt relocate non-script\n" );
+
+ scr = &(mobj->data.script);
+
+ VERIFY( block.offset < scr->heap_size
+ && getUInt16(scr->heap_start + block.offset)*2 + block.offset < scr->buf_size,
+ "Relocation block outside of script\n" );
+
+ if (scr->relocated) return;
+ scr->relocated = 1;
+ count = getUInt16(scr->heap_start + block.offset);
+
+ for (i = 0; i < count; i++) {
+ int pos = getUInt16(scr->heap_start + block.offset + 2 + (i*2)) + scr->script_size;
+
+ if (!_relocate_local(self, scr, block.segment, pos)) {
+ int k, done = 0;
+
+ for (k = 0; !done && k < scr->objects_nr; k++) {
+ if (_relocate_object(self, scr->objects + k, block.segment, pos))
+ done = 1;
+ }
+
+ if (!done) {
+ sciprintf("While processing relocation block "PREG":\n",
+ PRINT_REG(block));
+ sciprintf("Relocation failed for index %04x (%d/%d)\n", pos, i+1, count);
+ if (scr->locals_block)
+ sciprintf("- locals: %d at %04x\n",
+ scr->locals_block->nr,
+ scr->locals_offset);
+ else
+ sciprintf("- No locals\n");
+ for (k = 0; k < scr->objects_nr; k++)
+ sciprintf("- obj#%d at %04x w/ %d vars\n",
+ k,
+ scr->objects[k].pos.offset,
+ scr->objects[k].variables_nr);
+ sciprintf("Triggering breakpoint...\n");
+ BREAKPOINT();
+ }
+ }
+ }
+}
+
+#define INST_LOOKUP_CLASS(id) ((id == 0xffff)? NULL_REG : get_class_address(s, id, SCRIPT_GET_LOCK, NULL_REG))
+
+reg_t
+get_class_address(state_t *s, int classnr, int lock, reg_t caller);
+
+static object_t *
+sm_script_obj_init0(seg_manager_t *self, state_t *s, reg_t obj_pos)
+{
+ mem_obj_t *mobj = self->heap[obj_pos.segment];
+ script_t *scr;
+ object_t *obj;
+ int id;
+ int base = obj_pos.offset - SCRIPT_OBJECT_MAGIC_OFFSET;
+ reg_t temp;
+
+ VERIFY( !(obj_pos.segment >= self->heap_size || mobj->type != MEM_OBJ_SCRIPT),
+ "Attempt to initialize object in non-script\n" );
+
+ scr = &(mobj->data.script);
+
+ VERIFY( base < scr->buf_size,
+ "Attempt to initialize object beyond end of script\n" );
+
+ if (!scr->objects) {
+ scr->objects_allocated = DEFAULT_OBJECTS;
+ scr->objects = (object_t*)sci_malloc(sizeof(object_t) * scr->objects_allocated);
+ }
+ if (scr->objects_nr == scr->objects_allocated) {
+ scr->objects_allocated += DEFAULT_OBJECTS_INCREMENT;
+ scr->objects = (object_t*)sci_realloc(scr->objects,
+ sizeof(object_t)
+ * scr->objects_allocated);
+
+ }
+
+ temp = make_reg(obj_pos.segment, base);
+ id = int_hash_map_check_value(scr->obj_indices, base, 1, NULL);
+ scr->objects_nr++;
+
+ obj = scr->objects + id;
+
+ VERIFY( base + SCRIPT_FUNCTAREAPTR_OFFSET < scr->buf_size,
+ "Function area pointer stored beyond end of script\n" );
+
+ {
+ byte *data = (byte *) (scr->buf + base);
+ int funct_area = getUInt16( data + SCRIPT_FUNCTAREAPTR_OFFSET );
+ int variables_nr;
+ int functions_nr;
+ int is_class;
+ int i;
+
+ obj->flags = 0;
+ obj->pos = temp;
+
+ VERIFY ( base + funct_area < scr->buf_size,
+ "Function area pointer references beyond end of script" );
+
+ variables_nr = getUInt16( data + SCRIPT_SELECTORCTR_OFFSET );
+ functions_nr = getUInt16( data + funct_area - 2 );
+ is_class = getUInt16( data + SCRIPT_INFO_OFFSET ) & SCRIPT_INFO_CLASS;
+
+ VERIFY ( base + funct_area + functions_nr * 2
+ /* add again for classes, since those also store selectors */
+ + (is_class? functions_nr * 2 : 0) < scr->buf_size,
+ "Function area extends beyond end of script" );
+
+ obj->variables_nr = variables_nr;
+ obj->variables = (reg_t*)sci_malloc(sizeof(reg_t) * variables_nr);
+
+ obj->methods_nr = functions_nr;
+ obj->base = scr->buf;
+ obj->base_obj = data;
+ obj->base_method = (guint16 *) (data + funct_area);
+ obj->base_vars = NULL;
+
+ for (i = 0; i < variables_nr; i++)
+ obj->variables[i] = make_reg(0, getUInt16(data + (i*2)));
+ }
+
+ return obj;
+}
+
+static object_t *
+sm_script_obj_init11(seg_manager_t *self, state_t *s, reg_t obj_pos)
+{
+ mem_obj_t *mobj = self->heap[obj_pos.segment];
+ script_t *scr;
+ object_t *obj;
+ int id;
+ int base = obj_pos.offset;
+
+ VERIFY( !(obj_pos.segment >= self->heap_size || mobj->type != MEM_OBJ_SCRIPT),
+ "Attempt to initialize object in non-script\n" );
+
+ scr = &(mobj->data.script);
+
+ VERIFY( base < scr->buf_size,
+ "Attempt to initialize object beyond end of script\n" );
+
+ if (!scr->objects) {
+ scr->objects_allocated = DEFAULT_OBJECTS;
+ scr->objects = (object_t*)sci_malloc(sizeof(object_t) * scr->objects_allocated);
+ }
+ if (scr->objects_nr == scr->objects_allocated) {
+ scr->objects_allocated += DEFAULT_OBJECTS_INCREMENT;
+ scr->objects = (object_t*)sci_realloc(scr->objects,
+ sizeof(object_t)
+ * scr->objects_allocated);
+
+ }
+
+ id = int_hash_map_check_value(scr->obj_indices, obj_pos.offset, 1, NULL);
+ scr->objects_nr++;
+
+ obj = scr->objects + id;
+
+ VERIFY( base + SCRIPT_FUNCTAREAPTR_OFFSET < scr->buf_size,
+ "Function area pointer stored beyond end of script\n" );
+
+ {
+ byte *data = (byte *) (scr->buf + base);
+ guint16 *funct_area = (guint16 *) (scr->buf + getUInt16( data + 6 ));
+ guint16 *prop_area = (guint16 *) (scr->buf + getUInt16( data + 4 ));
+ int variables_nr;
+ int functions_nr;
+ int is_class;
+ int i;
+
+ obj->flags = 0;
+ obj->pos = obj_pos;
+
+ VERIFY ( (byte *) funct_area < scr->buf + scr->buf_size,
+ "Function area pointer references beyond end of script" );
+
+ variables_nr = getUInt16(data + 2);
+ functions_nr = *funct_area;
+ is_class = getUInt16( data + 14 ) & SCRIPT_INFO_CLASS;
+
+ obj->base_method = funct_area;
+ obj->base_vars = prop_area;
+
+ VERIFY ( ((byte *) funct_area + functions_nr) < scr->buf + scr->buf_size,
+ "Function area extends beyond end of script" );
+
+ obj->variables_nr = variables_nr;
+ obj->variable_names_nr = variables_nr;
+ obj->variables = (reg_t*)sci_malloc(sizeof(reg_t) * variables_nr);
+
+ obj->methods_nr = functions_nr;
+ obj->base = scr->buf;
+ obj->base_obj = data;
+
+ for (i = 0; i < variables_nr; i++)
+ obj->variables[i] = make_reg(0, getUInt16(data + (i*2)));
+ }
+
+ return obj;
+}
+
+object_t *
+sm_script_obj_init(seg_manager_t *self, state_t *s, reg_t obj_pos)
+{
+ if (!self->sci1_1)
+ return sm_script_obj_init0(self, s, obj_pos);
+ else
+ return sm_script_obj_init11(self, s, obj_pos);
+}
+
+static local_variables_t *
+_sm_alloc_locals_segment(seg_manager_t *self, script_t *scr, int count)
+{
+ if (!count) { /* No locals */
+ scr->locals_segment = 0;
+ scr->locals_block = NULL;
+ return NULL;
+ } else {
+ mem_obj_t *mobj;
+ local_variables_t *locals;
+
+ if (scr->locals_segment) {
+ mobj = self->heap[scr->locals_segment];
+ VERIFY(mobj != NULL, "Re-used locals segment was NULL'd out");
+ VERIFY(mobj->type == MEM_OBJ_LOCALS, "Re-used locals segment did not consist of local variables");
+ VERIFY(mobj->data.locals.script_id == scr->nr, "Re-used locals segment belonged to other script");
+ } else
+ mobj = alloc_nonscript_segment(self, MEM_OBJ_LOCALS,
+ &scr->locals_segment);
+
+ locals = scr->locals_block = &(mobj->data.locals);
+ locals->script_id = scr->nr;
+ locals->locals = (reg_t*)sci_calloc(count, sizeof(reg_t));
+ locals->nr = count;
+
+ return locals;
+ }
+}
+
+void
+sm_script_initialise_locals_zero(seg_manager_t *self, seg_id_t seg, int count)
+{
+ mem_obj_t *mobj = self->heap[seg];
+ script_t *scr;
+
+ VERIFY( !(seg >= self->heap_size || mobj->type != MEM_OBJ_SCRIPT),
+ "Attempt to initialize locals in non-script\n" );
+
+ scr = &(mobj->data.script);
+
+ scr->locals_offset = -count * 2; /* Make sure it's invalid */
+
+ _sm_alloc_locals_segment(self, scr, count);
+}
+
+void
+sm_script_initialise_locals(seg_manager_t *self, reg_t location)
+{
+ mem_obj_t *mobj = self->heap[location.segment];
+ int count;
+ script_t *scr;
+ local_variables_t *locals;
+
+ VERIFY( !(location.segment >= self->heap_size || mobj->type != MEM_OBJ_SCRIPT),
+ "Attempt to initialize locals in non-script\n" );
+
+ scr = &(mobj->data.script);
+
+ VERIFY( location.offset + 1 < scr->buf_size,
+ "Locals beyond end of script\n" );
+
+ if (self->sci1_1)
+ count = getUInt16(scr->buf + location.offset - 2);
+ else
+ count = (getUInt16(scr->buf + location.offset - 2) - 4) >> 1;
+ /* half block size */
+
+ scr->locals_offset = location.offset;
+
+ if (!(location.offset + count * 2 + 1 < scr->buf_size)) {
+ sciprintf("Locals extend beyond end of script: offset %04x, count %x vs size %x\n",
+ location.offset, count, scr->buf_size);
+ count = (scr->buf_size - location.offset) >> 1;
+ }
+
+ locals = _sm_alloc_locals_segment(self, scr, count);
+ if (locals) {
+ int i;
+ byte *base = (byte *) (scr->buf + location.offset);
+
+ for (i = 0; i < count; i++)
+ locals->locals[i].offset = getUInt16(base + i*2);
+ }
+}
+
+void
+sm_script_relocate_exports_sci11(seg_manager_t *self, int seg)
+{
+ mem_obj_t *mobj = self->heap[seg];
+ script_t *scr;
+ int i;
+ int location;
+
+ VERIFY( !(seg >= self->heap_size || mobj->type != MEM_OBJ_SCRIPT),
+ "Attempt to relocate exports in non-script\n" );
+
+ scr = &(mobj->data.script);
+ for (i = 0; i < scr->exports_nr; i++)
+ {
+ /* We are forced to use an ugly heuristic here to distinguish function
+ exports from object/class exports. The former kind points into the
+ script resource, the latter into the heap resource. */
+ location = getUInt16((byte *)(scr->export_table + i));
+ if (getUInt16(scr->heap_start + location) == SCRIPT_OBJECT_MAGIC_NUMBER)
+ {
+ putInt16((byte *)(scr->export_table + i), location+scr->heap_start-scr->buf);
+ } else
+ {
+ /* Otherwise it's probably a function export,
+ and we don't need to do anything. */
+ }
+ }
+
+}
+
+void
+sm_script_initialise_objects_sci11(seg_manager_t *self, state_t *s, int seg)
+{
+ mem_obj_t *mobj = self->heap[seg];
+ script_t *scr;
+ byte *seeker;
+
+ VERIFY( !(seg >= self->heap_size || mobj->type != MEM_OBJ_SCRIPT),
+ "Attempt to relocate exports in non-script\n" );
+
+ scr = &(mobj->data.script);
+ seeker = scr->heap_start + 4 + getUInt16(scr->heap_start + 2) * 2;
+
+ while (getUInt16(seeker) == SCRIPT_OBJECT_MAGIC_NUMBER)
+ {
+
+ if (getUInt16(seeker + 14) & SCRIPT_INFO_CLASS)
+ {
+ int classpos = seeker-scr->buf;
+ int species = getUInt16(seeker + 10);
+
+ if (species < 0 || species >= s->classtable_size) {
+ sciprintf("Invalid species %d(0x%x) not in interval "
+ "[0,%d) while instantiating script %d\n",
+ species, species, s->classtable_size,
+ scr->nr);
+ script_debug_flag = script_error_flag = 1;
+ return;
+ }
+
+ s->classtable[species].script = scr->nr;
+ s->classtable[species].reg.segment = seg;
+ s->classtable[species].reg.offset = classpos;
+ }
+ seeker += getUInt16(seeker + 2) * 2;
+ }
+
+ seeker = scr->heap_start + 4 + getUInt16(scr->heap_start + 2) * 2;
+ while (getUInt16(seeker) == SCRIPT_OBJECT_MAGIC_NUMBER)
+ {
+ reg_t reg;
+ object_t *obj;
+
+ reg.segment = seg;
+ reg.offset = seeker-scr->buf;
+ obj = sm_script_obj_init(&s->seg_manager, s, reg);
+
+#if 0
+ if (obj->variables[5].offset != 0xffff)
+ {
+ obj->variables[5] =
+ INST_LOOKUP_CLASS(obj->variables[5].offset);
+
+
+ base_obj = obj_get(s, obj->variables[5]);
+ obj->variable_names_nr = base_obj->variables_nr;
+ obj->base_obj = base_obj->base_obj;
+ }
+#endif
+
+ /* Copy base from species class, as we need its selector IDs */
+ obj->variables[6] =
+ INST_LOOKUP_CLASS(obj->variables[6].offset);
+
+ seeker += getUInt16(seeker + 2) * 2;
+ }
+
+}
+void
+sm_script_free_unused_objects(seg_manager_t *self, seg_id_t seg)
+{
+ mem_obj_t *mobj = self->heap[seg];
+ script_t *scr;
+
+ VERIFY( !(seg >= self->heap_size || mobj->type != MEM_OBJ_SCRIPT),
+ "Attempt to free unused objects in non-script\n" );
+
+
+ scr = &(mobj->data.script);
+ if (scr->objects_allocated > scr->objects_nr) {
+ if (scr->objects_nr)
+ scr->objects = (object_t*)sci_realloc(scr->objects, sizeof(object_t)
+ * scr->objects_nr);
+ else {
+ if (scr->objects_allocated)
+ sci_free(scr->objects);
+ scr->objects = NULL;
+ }
+ scr->objects_allocated = scr->objects_nr;
+ }
+}
+
+static inline char *dynprintf(char *msg, ...)
+{
+ va_list argp;
+ char *buf = (char*)sci_malloc(strlen(msg) + 100);
+
+ va_start(argp, msg);
+ vsprintf(buf, msg, argp);
+ va_end(argp);
+
+ return buf;
+}
+
+
+dstack_t *
+sm_allocate_stack(seg_manager_t *self, int size, seg_id_t *segid)
+{
+ mem_obj_t *memobj = alloc_nonscript_segment(self, MEM_OBJ_STACK, segid);
+ dstack_t *retval = &(memobj->data.stack);
+
+ retval->entries = (reg_t*)sci_calloc(size, sizeof(reg_t));
+ retval->nr = size;
+
+ return retval;
+}
+
+sys_strings_t *
+sm_allocate_sys_strings(seg_manager_t *self, seg_id_t *segid)
+{
+ mem_obj_t *memobj = alloc_nonscript_segment(self, MEM_OBJ_SYS_STRINGS, segid);
+ sys_strings_t *retval = &(memobj->data.sys_strings);
+
+ memset(retval, 0, sizeof(sys_string_t)*SYS_STRINGS_MAX);
+
+ return retval;
+}
+
+seg_id_t
+sm_allocate_reserved_segment(seg_manager_t *self, char *src_name)
+{
+ seg_id_t segid;
+ mem_obj_t *memobj = alloc_nonscript_segment(self, MEM_OBJ_RESERVED, &segid);
+ char *name = sci_strdup(src_name);
+
+ memobj->data.reserved = name;
+
+ return segid;
+}
+
+guint16
+sm_validate_export_func(struct _seg_manager_t* self, int pubfunct, int seg ) {
+ script_t* script;
+ guint16 offset;
+ VERIFY ( sm_check (self, seg), "invalid seg id" );
+ VERIFY (self->heap[seg]->type == MEM_OBJ_SCRIPT, "Can only validate exports on scripts");
+
+ script = &self->heap[seg]->data.script;
+ if( script->exports_nr <= pubfunct ) {
+ sciprintf( "pubfunct is invalid" );
+ return 0;
+ }
+
+ if (self->exports_wide) pubfunct *= 2;
+ offset = getUInt16( (byte*)(script->export_table + pubfunct) );
+ VERIFY ( offset < script->buf_size, "invalid export function pointer" );
+
+ return offset;
+}
+
+
+void
+sm_free_hunk_entry(seg_manager_t *self, reg_t addr)
+{
+ sm_free_hunk(self, addr);
+}
+
+
+hunk_t *
+sm_alloc_hunk_entry(seg_manager_t *self, const char *hunk_type, int size, reg_t *reg)
+{
+ hunk_t *h = sm_alloc_hunk(self, reg);
+
+ if (!h)
+ return NULL;
+
+ h->mem = sci_malloc(size);
+ h->size = size;
+ h->type = hunk_type;
+
+ return h;
+}
+
+static void
+_clone_cleanup(clone_t *clone)
+{
+ if (clone->variables)
+ sci_free(clone->variables); /* Free the dynamically allocated memory part */
+}
+
+static void
+_hunk_cleanup(hunk_t *hunk)
+{
+ if (hunk->mem)
+ free (hunk->mem);
+}
+
+DEFINE_HEAPENTRY(list, 8, 4)
+DEFINE_HEAPENTRY(node, 32, 16)
+DEFINE_HEAPENTRY_WITH_CLEANUP(clone, 16, 4, _clone_cleanup)
+DEFINE_HEAPENTRY_WITH_CLEANUP(hunk, 4, 4, _hunk_cleanup)
+
+#define DEFINE_ALLOC_DEALLOC(STATIC, TYPE, SEGTYPE, PLURAL) \
+STATIC TYPE##_t * \
+sm_alloc_##TYPE(seg_manager_t *self, reg_t *addr) \
+{ \
+ mem_obj_t *mobj; \
+ TYPE##_table_t *table; \
+ int offset; \
+ \
+ if (!self->TYPE##s_seg_id) { \
+ mobj = alloc_nonscript_segment(self, SEGTYPE, &(self->TYPE##s_seg_id)); \
+ init_##TYPE##_table(&(mobj->data.PLURAL)); \
+ } else \
+ mobj = self->heap[self->TYPE##s_seg_id]; \
+ \
+ table = &(mobj->data.PLURAL); \
+ offset = alloc_##TYPE##_entry(table); \
+ \
+ *addr = make_reg(self->TYPE##s_seg_id, offset); \
+ return &(mobj->data.PLURAL.table[offset].entry); \
+} \
+ \
+STATIC void \
+sm_free_##TYPE(seg_manager_t *self, reg_t addr) \
+{ \
+ mem_obj_t *mobj = GET_SEGMENT(*self, addr.segment, SEGTYPE); \
+ \
+ if (!mobj) { \
+ sciprintf("Attempt to free " #TYPE " from address "PREG \
+ ": Invalid segment type\n", \
+ PRINT_REG(addr)); \
+ return; \
+ } \
+ \
+ free_##TYPE##_entry(&(mobj->data.PLURAL), addr.offset); \
+}
+
+DEFINE_ALLOC_DEALLOC(, clone, MEM_OBJ_CLONES, clones)
+DEFINE_ALLOC_DEALLOC(, list, MEM_OBJ_LISTS, lists)
+DEFINE_ALLOC_DEALLOC(, node, MEM_OBJ_NODES, nodes)
+DEFINE_ALLOC_DEALLOC(static, hunk, MEM_OBJ_HUNK, hunks)
+
+
+
+byte *
+sm_dereference(seg_manager_t *self, reg_t pointer, int *size)
+{
+ mem_obj_t *mobj;
+ byte *base = NULL;
+ int count;
+
+ if (!pointer.segment
+ || (pointer.segment >= self->heap_size)
+ || !self->heap[pointer.segment]) {
+ sciprintf("Error: Attempt to dereference invalid pointer "PREG"!\n",
+ PRINT_REG(pointer));
+ return NULL; /* Invalid */
+ }
+
+
+ mobj = self->heap[pointer.segment];
+
+ switch (mobj->type) {
+
+ case MEM_OBJ_SCRIPT:
+ if (pointer.offset > mobj->data.script.buf_size) {
+ sciprintf("Error: Attempt to dereference invalid pointer "PREG
+ " into script segment (script size=%d)\n",
+ PRINT_REG(pointer), mobj->data.script.buf_size);
+ return NULL;
+ }
+ if (size)
+ *size = mobj->data.script.buf_size - pointer.offset;
+ return (byte *) (mobj->data.script.buf + pointer.offset);
+ break;
+
+ case MEM_OBJ_LOCALS:
+ count = mobj->data.locals.nr * sizeof(reg_t);
+ base = (byte *) mobj->data.locals.locals;
+ break;
+
+ case MEM_OBJ_STACK:
+ count = mobj->data.stack.nr * sizeof(reg_t);
+ base = (byte *) mobj->data.stack.entries;
+ break;
+
+ case MEM_OBJ_DYNMEM:
+ count = mobj->data.dynmem.size;
+ base = (byte *) mobj->data.dynmem.buf;
+ break;
+
+ case MEM_OBJ_SYS_STRINGS:
+ if (size)
+ *size = mobj->data.sys_strings.strings[pointer.offset].max_size;
+ if (pointer.offset < SYS_STRINGS_MAX
+ && mobj->data.sys_strings.strings[pointer.offset].name)
+ return (byte *) (mobj->data.sys_strings.strings[pointer.offset].value);
+ else {
+ sciprintf("Error: Attempt to dereference invalid pointer "PREG"!\n",
+ PRINT_REG(pointer));
+ return NULL;
+ }
+
+ case MEM_OBJ_RESERVED:
+ sciprintf("Error: Trying to dereference pointer "PREG" to reserved segment `%s'!\n",
+ mobj->data.reserved);
+ return NULL;
+ break;
+
+ default:
+ sciprintf("Error: Trying to dereference pointer "PREG" to inappropriate"
+ " segment!\n",
+ PRINT_REG(pointer));
+ return NULL;
+ }
+
+ if (size)
+ *size = count;
+
+ return
+ base + pointer.offset;
+}
+
+
+unsigned char *
+sm_alloc_dynmem(seg_manager_t *self, int size, const char *descr, reg_t *addr)
+{
+ seg_id_t seg;
+ mem_obj_t *mobj = alloc_nonscript_segment(self, MEM_OBJ_DYNMEM, &seg);
+ *addr = make_reg(seg, 0);
+
+ mobj->data.dynmem.size = size;
+
+ if (size == 0)
+ mobj->data.dynmem.buf = NULL;
+ else
+ mobj->data.dynmem.buf = (byte*) sci_malloc(size);
+
+ mobj->data.dynmem.description = descr;
+
+ return (unsigned char *) (mobj->data.dynmem.buf);
+}
+
+const char *
+sm_get_description(seg_manager_t *self, reg_t addr)
+{
+ mem_obj_t *mobj = self->heap[addr.segment];
+
+ if (addr.segment >= self->heap_size)
+ return "";
+
+ switch (mobj->type)
+ {
+ case MEM_OBJ_DYNMEM:
+ return mobj->data.dynmem.description;
+ default:
+ return "";
+ }
+}
+
+int
+sm_free_dynmem(seg_manager_t *self, reg_t addr)
+{
+
+ if (addr.segment <= 0
+ || addr.segment >= self->heap_size
+ || !self->heap[addr.segment]
+ || self->heap[addr.segment]->type != MEM_OBJ_DYNMEM)
+ return 1; /* error */
+
+ _sm_deallocate(self, addr.segment, 1);
+ return 0; /* OK */
+}
+
+
+/************************************************************/
+/* ------------------- Segment interface ------------------ */
+/************************************************************/
+
+
+static void
+free_at_address_stub (seg_interface_t *self, reg_t sub_addr)
+{
+// sciprintf(" Request to free "PREG"\n", PRINT_REG(sub_addr));
+ /* STUB */
+}
+
+
+
+static reg_t
+find_canonic_address_base (seg_interface_t *self, reg_t addr)
+{
+ addr.offset = 0;
+ return addr;
+}
+
+static reg_t
+find_canonic_address_id (seg_interface_t *self, reg_t addr)
+{
+ return addr;
+}
+
+static void
+free_at_address_nop (seg_interface_t *self, reg_t sub_addr)
+{
+}
+
+static void
+list_all_deallocatable_nop (seg_interface_t *self, void *param, void (*note) (void*param, reg_t addr))
+{
+}
+
+static void
+list_all_deallocatable_base (seg_interface_t *self, void *param, void (*note) (void*param, reg_t addr))
+{
+ (*note) (param, make_reg (self->seg_id, 0));
+}
+
+static void
+list_all_outgoing_references_nop (seg_interface_t *self, state_t *s, reg_t addr, void *param, void (*note) (void*param, reg_t addr))
+{
+}
+
+static void
+deallocate_self (seg_interface_t *self)
+{
+ sci_free (self);
+}
+
+
+static void
+free_at_address_script (seg_interface_t *self, reg_t addr)
+{
+ script_t *script;
+ VERIFY(self->mobj->type == MEM_OBJ_SCRIPT, "Trying to free a non-script!");
+ script = &(self->mobj->data.script);
+/*
+ sciprintf("[GC] Freeing script "PREG"\n", PRINT_REG(addr));
+ if (script->locals_segment)
+ sciprintf("[GC] Freeing locals %04x:0000\n", script->locals_segment);
+*/
+
+ if (script->marked_as_deleted)
+ sm_deallocate_script(self->segmgr, script->nr);
+}
+
+static void
+list_all_outgoing_references_script (seg_interface_t *self, state_t *s, reg_t addr, void *param, void (*note) (void*param, reg_t addr))
+{
+ script_t *script = &(self->mobj->data.script);
+
+ if (addr.offset <= script->buf_size
+ && addr.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET
+ && RAW_IS_OBJECT(script->buf + addr.offset)) {
+ int idx = RAW_GET_CLASS_INDEX(script, addr);
+ if (idx >= 0 && idx < script->objects_nr) {
+ object_t *obj = script->objects + idx;
+ int i;
+
+ /* Note all local variables, if we have a local variable environment */
+ if (script->locals_segment)
+ (*note) (param, make_reg(script->locals_segment, 0));
+
+ for (i = 0; i < obj->variables_nr; i++)
+ (*note) (param, obj->variables[i]);
+ } else {
+ fprintf(stderr, "Request for outgoing script-object reference at "PREG" yielded invalid index %d\n", PRINT_REG(addr), idx);
+ }
+ } else {
+/* fprintf(stderr, "Unexpected request for outgoing script-object references at "PREG"\n", PRINT_REG(addr));*/
+ /* Happens e.g. when we're looking into strings */
+ }
+}
+
+/*-------------------- script --------------------*/
+static seg_interface_t seg_interface_script = {
+ /* segmgr = */ NULL,
+ /* mobj = */ NULL,
+ /* seg_id = */ 0,
+ /* type_id = */ MEM_OBJ_SCRIPT,
+ /* type = */ "script",
+ /* find_canonic_address = */ find_canonic_address_base,
+ /* free_at_address = */ free_at_address_script,
+ /* list_all_deallocatable = */ list_all_deallocatable_base,
+ /* list_all_outgoing_references = */ list_all_outgoing_references_script,
+ /* deallocate_self = */ deallocate_self
+};
+
+
+#define LIST_ALL_DEALLOCATABLE(kind, kind_field) \
+ mem_obj_t *mobj = self->mobj; \
+ kind##_table_t * table = &(mobj->data.kind_field); \
+ int i; \
+ \
+ for (i = 0; i < table->max_entry; i++) \
+ if (ENTRY_IS_VALID(table, i)) \
+ (*note) (param, make_reg(self->seg_id, i));
+
+static void
+list_all_deallocatable_clones (seg_interface_t *self, void *param, void (*note) (void*param, reg_t addr))
+{
+ LIST_ALL_DEALLOCATABLE(clone, clones);
+}
+
+static void
+list_all_outgoing_references_clones (seg_interface_t *self, state_t *s, reg_t addr, void *param, void (*note) (void*param, reg_t addr))
+{
+ mem_obj_t *mobj = self->mobj;
+ clone_table_t *clone_table = &(mobj->data.clones);
+ clone_t *clone;
+ int i;
+
+ assert (addr.segment == self->seg_id);
+
+ if (!(ENTRY_IS_VALID(clone_table, addr.offset))) {
+ fprintf(stderr, "Unexpected request for outgoing references from clone at "PREG"\n", PRINT_REG(addr));
+// BREAKPOINT();
+ return;
+ }
+
+ clone = &(clone_table->table[addr.offset].entry);
+
+ /* Emit all member variables (including references to the 'super' delegate) */
+ for (i = 0; i < clone->variables_nr; i++)
+ (*note) (param, clone->variables[i]);
+
+ /* Note that this also includes the 'base' object, which is part of the script and therefore also
+ ** emits the locals. */
+ (*note) (param, clone->pos);
+// sciprintf("[GC] Reporting clone-pos "PREG"\n", PRINT_REG(clone->pos));
+}
+
+
+void
+free_at_address_clones(seg_interface_t *self, reg_t addr)
+{
+ object_t *victim_obj;
+
+ assert (addr.segment == self->seg_id);
+
+ victim_obj = &(self->mobj->data.clones.table[addr.offset].entry);
+
+#ifdef GC_DEBUG
+ if (!(victim_obj->flags & OBJECT_FLAG_FREED))
+ sciprintf("[GC] Warning: Clone "PREG" not reachable and not freed (freeing now)\n",
+ PRINT_REG(addr));
+# ifdef GC_DEBUG_VERBOSE
+ else
+ sciprintf("[GC-DEBUG] Clone "PREG": Freeing\n", PRINT_REG(addr));
+# endif
+#endif
+/*
+ sciprintf("[GC] Clone "PREG": Freeing\n", PRINT_REG(addr));
+ sciprintf("[GC] Clone had pos "PREG"\n", PRINT_REG(victim_obj->pos));
+*/
+ sci_free(victim_obj->variables);
+ victim_obj->variables = NULL;
+ sm_free_clone(self->segmgr, addr);
+}
+
+/*-------------------- clones --------------------*/
+static seg_interface_t seg_interface_clones = {
+ /* segmgr = */ NULL,
+ /* mobj = */ NULL,
+ /* seg_id = */ 0,
+ /* type_id = */ MEM_OBJ_CLONES,
+ /* type = */ "clones",
+ /* find_canonic_address = */ find_canonic_address_id,
+ /* free_at_address = */ free_at_address_clones,
+ /* list_all_deallocatable = */ list_all_deallocatable_clones,
+ /* list_all_outgoing_references = */ list_all_outgoing_references_clones,
+ /* deallocate_self = */ deallocate_self
+};
+
+
+static reg_t
+find_canonic_address_locals (seg_interface_t *self, reg_t addr)
+{
+ local_variables_t *locals = &(self->mobj->data.locals);
+ /* Reference the owning script */
+ seg_id_t owner_seg = sm_seg_get(self->segmgr, locals->script_id);
+
+ assert (owner_seg >= 0);
+
+ return make_reg(owner_seg, 0);
+}
+
+static void
+list_all_outgoing_references_locals (seg_interface_t *self, state_t *s, reg_t addr, void *param, void (*note) (void*param, reg_t addr))
+{
+ local_variables_t *locals = &(self->mobj->data.locals);
+ int i;
+
+ assert (addr.segment == self->seg_id);
+
+ for (i = 0; i < locals->nr; i++)
+ (*note) (param, locals->locals[i]);
+}
+
+/*-------------------- locals --------------------*/
+static seg_interface_t seg_interface_locals = {
+ /* segmgr = */ NULL,
+ /* mobj = */ NULL,
+ /* seg_id = */ 0,
+ /* type_id = */ MEM_OBJ_LOCALS,
+ /* type = */ "locals",
+ /* find_canonic_address = */ find_canonic_address_locals,
+ /* free_at_address = */ free_at_address_stub,
+ /* list_all_deallocatable = */ list_all_deallocatable_nop,
+ /* list_all_outgoing_references = */ list_all_outgoing_references_locals,
+ /* deallocate_self = */ deallocate_self
+};
+
+
+static void
+list_all_outgoing_references_stack (seg_interface_t *self, state_t *s, reg_t addr, void *param, void (*note) (void*param, reg_t addr))
+{
+ int i;
+fprintf(stderr, "Emitting %d stack entries\n", self->mobj->data.stack.nr);
+ for (i = 0; i < self->mobj->data.stack.nr; i++)
+ (*note) (param, self->mobj->data.stack.entries[i]);
+fprintf(stderr, "DONE");
+}
+
+/*-------------------- stack --------------------*/
+static seg_interface_t seg_interface_stack = {
+ /* segmgr = */ NULL,
+ /* mobj = */ NULL,
+ /* seg_id = */ 0,
+ /* type_id = */ MEM_OBJ_STACK,
+ /* type = */ "stack",
+ /* find_canonic_address = */ find_canonic_address_base,
+ /* free_at_address = */ free_at_address_nop,
+ /* list_all_deallocatable = */ list_all_deallocatable_nop,
+ /* list_all_outgoing_references = */ list_all_outgoing_references_stack,
+ /* deallocate_self = */ deallocate_self
+};
+
+/*-------------------- system strings --------------------*/
+static seg_interface_t seg_interface_sys_strings = {
+ /* segmgr = */ NULL,
+ /* mobj = */ NULL,
+ /* seg_id = */ 0,
+ /* type_id = */ MEM_OBJ_SYS_STRINGS,
+ /* type = */ "system strings",
+ /* find_canonic_address = */ find_canonic_address_id,
+ /* free_at_address = */ free_at_address_nop,
+ /* list_all_deallocatable = */ list_all_deallocatable_nop,
+ /* list_all_outgoing_references = */ list_all_outgoing_references_nop,
+ /* deallocate_self = */ deallocate_self
+};
+
+static void
+list_all_deallocatable_list (seg_interface_t *self, void *param, void (*note) (void*param, reg_t addr))
+{
+ LIST_ALL_DEALLOCATABLE(list, lists);
+}
+
+static void
+list_all_outgoing_references_list (seg_interface_t *self, state_t *s, reg_t addr, void *param, void (*note) (void*param, reg_t addr))
+{
+ list_table_t *table = &(self->mobj->data.lists);
+ list_t *list = &(table->table[addr.offset].entry);
+
+ if (!ENTRY_IS_VALID(table, addr.offset)) {
+ fprintf(stderr, "Invalid list referenced for outgoing references: "PREG"\n", PRINT_REG(addr));
+ return;
+ }
+
+ note (param, list->first);
+ note (param, list->last);
+ /* We could probably get away with just one of them, but
+ ** let's be conservative here. */
+}
+
+static void
+free_at_address_lists (seg_interface_t *self, reg_t sub_addr)
+{
+ sm_free_list (self->segmgr, sub_addr);
+}
+
+/*-------------------- lists --------------------*/
+static seg_interface_t seg_interface_lists = {
+ /* segmgr = */ NULL,
+ /* mobj = */ NULL,
+ /* seg_id = */ 0,
+ /* type_id = */ MEM_OBJ_LISTS,
+ /* type = */ "lists",
+ /* find_canonic_address = */ find_canonic_address_id,
+ /* free_at_address = */ free_at_address_lists,
+ /* list_all_deallocatable = */ list_all_deallocatable_list,
+ /* list_all_outgoing_references = */ list_all_outgoing_references_list,
+ /* deallocate_self = */ deallocate_self
+};
+
+static void
+list_all_deallocatable_nodes (seg_interface_t *self, void *param, void (*note) (void*param, reg_t addr))
+{
+ LIST_ALL_DEALLOCATABLE(node, nodes);
+}
+
+static void
+list_all_outgoing_references_nodes (seg_interface_t *self, state_t *s, reg_t addr, void *param, void (*note) (void*param, reg_t addr))
+{
+ node_table_t *table = &(self->mobj->data.nodes);
+ node_t *node = &(table->table[addr.offset].entry);
+
+ if (!ENTRY_IS_VALID(table, addr.offset)) {
+ fprintf(stderr, "Invalid node referenced for outgoing references: "PREG"\n", PRINT_REG(addr));
+ return;
+ }
+
+ /* We need all four here. Can't just stick with 'pred' OR 'succ' because node operations allow us
+ ** to walk around from any given node */
+ note (param, node->pred);
+ note (param, node->succ);
+ note (param, node->key);
+ note (param, node->value);
+}
+
+static void
+free_at_address_nodes (seg_interface_t *self, reg_t sub_addr)
+{
+ sm_free_node (self->segmgr, sub_addr);
+}
+
+/*-------------------- nodes --------------------*/
+static seg_interface_t seg_interface_nodes = {
+ /* segmgr = */ NULL,
+ /* mobj = */ NULL,
+ /* seg_id = */ 0,
+ /* type_id = */ MEM_OBJ_NODES,
+ /* type = */ "nodes",
+ /* find_canonic_address = */ find_canonic_address_id,
+ /* free_at_address = */ free_at_address_nodes,
+ /* list_all_deallocatable = */ list_all_deallocatable_nodes,
+ /* list_all_outgoing_references = */ list_all_outgoing_references_nodes,
+ /* deallocate_self = */ deallocate_self
+};
+
+static void
+list_all_deallocatable_hunk (seg_interface_t *self, void *param, void (*note) (void*param, reg_t addr))
+{
+ LIST_ALL_DEALLOCATABLE(hunk, hunks);
+}
+
+/*-------------------- hunk --------------------*/
+static seg_interface_t seg_interface_hunk = {
+ /* segmgr = */ NULL,
+ /* mobj = */ NULL,
+ /* seg_id = */ 0,
+ /* type_id = */ MEM_OBJ_HUNK,
+ /* type = */ "hunk",
+ /* find_canonic_address = */ find_canonic_address_id,
+ /* free_at_address = */ free_at_address_stub,
+ /* list_all_deallocatable = */ list_all_deallocatable_hunk,
+ /* list_all_outgoing_references = */ list_all_outgoing_references_nop,
+ /* deallocate_self = */ deallocate_self
+};
+
+
+
+/*-------------------- dynamic memory --------------------*/
+static seg_interface_t seg_interface_dynmem = {
+ /* segmgr = */ NULL,
+ /* mobj = */ NULL,
+ /* seg_id = */ 0,
+ /* type_id = */ MEM_OBJ_DYNMEM,
+ /* type = */ "dynamic memory",
+ /* find_canonic_address = */ find_canonic_address_base,
+ /* free_at_address = */ free_at_address_stub,
+ /* list_all_deallocatable = */ list_all_deallocatable_base,
+ /* list_all_outgoing_references = */ list_all_outgoing_references_nop,
+ /* deallocate_self = */ deallocate_self
+};
+
+/*-------------------- reserved --------------------*/
+static seg_interface_t seg_interface_reserved = {
+ /* segmgr = */ NULL,
+ /* mobj = */ NULL,
+ /* seg_id = */ 0,
+ /* type_id = */ MEM_OBJ_RESERVED,
+ /* type = */ "reserved",
+ /* find_canonic_address = */ find_canonic_address_id,
+ /* free_at_address = */ free_at_address_nop,
+ /* list_all_deallocatable = */ list_all_deallocatable_nop,
+ /* list_all_outgoing_references = */ list_all_outgoing_references_nop,
+ /* deallocate_self = */ deallocate_self
+};
+
+
+static seg_interface_t* seg_interfaces[MEM_OBJ_MAX] = {
+ &seg_interface_script,
+ &seg_interface_clones,
+ &seg_interface_locals,
+ &seg_interface_stack,
+ &seg_interface_sys_strings,
+ &seg_interface_lists,
+ &seg_interface_nodes,
+ &seg_interface_hunk,
+ &seg_interface_dynmem,
+ &seg_interface_reserved
+};
+
+
+seg_interface_t *
+get_seg_interface(seg_manager_t *self, seg_id_t segid)
+{
+ mem_obj_t *mobj;
+ seg_interface_t *retval;
+
+ if (!sm_check(self, segid))
+ return NULL; /* Invalid segment */
+
+ mobj = self->heap[segid];
+ retval = (seg_interface_t*)sci_malloc(sizeof(seg_interface_t));
+ memcpy(retval, seg_interfaces[mobj->type - 1], sizeof (seg_interface_t));
+
+ if (mobj->type != retval->type_id) {
+ fprintf(stderr, "Improper segment interface for %d", mobj->type );
+ exit(1);
+ }
+
+ retval->segmgr = self;
+ retval->mobj = mobj;
+ retval->seg_id = segid;
+
+ return retval;
+}
diff --git a/engines/sci/engine/simplesaid.c b/engines/sci/engine/simplesaid.c
new file mode 100644
index 0000000000..e74a3aec90
--- /dev/null
+++ b/engines/sci/engine/simplesaid.c
@@ -0,0 +1,312 @@
+/***************************************************************************
+ simplesaid.c Copyright (C) 2000 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>
+
+***************************************************************************/
+
+
+#ifdef SCI_SIMPLE_SAID_CODE
+
+#include <engine.h>
+#include <kdebug.h>
+
+static int current_pword;
+static int said_pos;
+static int refstart_pos, parse_pos;
+static parse_tree_node_t *words;
+static int dontclaim; /* Whether the Parse() event should be claimed */
+static int reached_end;
+
+static state_t *state;
+static heap_ptr addr;
+
+#define WORDP_MODE_STAY 0
+#define WORDP_MODE_NEXT 1
+#define WORDP_MODE_SEEK 2
+
+/* hack */
+#define s state
+
+static inline int /* Returns whether the specified word matches */
+word_p(int mode, int word)
+{
+ int oldpp = parse_pos;
+ do {
+ if (mode)
+ ++parse_pos;
+
+ current_pword = words[parse_pos << 1].content.value;
+ /* SDEBUG("Word at %d = %03x\n", parse_pos, current_pword); */
+ if (current_pword == word) {
+ /* ++matches; */
+ SCIkdebug(SCIkSAID, "wordp(%d, %03x) from %d -> 1\n", mode, word, oldpp);
+
+ reached_end |= (words[(parse_pos + 1) << 1].type == -1);
+
+ return 1;
+ }
+
+ } while ((mode == WORDP_MODE_SEEK) && (words[parse_pos <<1].type != -1));
+
+ if (words[parse_pos << 1].type == -1)
+ --parse_pos;
+
+ SCIkdebug(SCIkSAID, "wordp(%d, %03x) from %d -> 0\n", mode, word, oldpp);
+
+ return 0;
+}
+
+
+
+static inline int /* End of input? */
+end_p()
+{
+ int val = (words[(parse_pos + 1) << 1 ].type == -1) || reached_end;
+
+ SCIkdebug(SCIkSAID, "endp(pp=%d) = %d\n", parse_pos, val);
+
+ return val;
+}
+
+
+
+static inline int /* Returns whether the current_word references that specified word */
+reference_p(int referenced_word_index, int word)
+{
+ int val = 0;
+ int seeker = (refstart_pos << 1) + 2;
+
+ while (words[seeker].type != -1) {
+
+ if (words[seeker].content.value == word)
+ if (((words[seeker + 1].content.branches[0] > -1)
+ && (words[seeker + 1].content.branches[0] == referenced_word_index))
+ || ((words[seeker + 1].content.branches[1] > -1)
+ && (words[seeker + 1].content.branches[1] == referenced_word_index))) {
+ val = 1;
+ reached_end |= (words[seeker + 2].type == -1);
+ break;
+ }
+
+ seeker += 2;
+ }
+
+ SCIkdebug(SCIkSAID, "%03x > pos[%d] = %d (start at %d)\n", word, referenced_word_index, val, refstart_pos);
+
+ return val;
+}
+
+
+static inline int /* Checks whether the current word has trailing references */
+follows_p(void)
+{
+ /* int val = (words[(parse_pos << 1) + 1].content.branches[1] > (parse_pos << 1));
+
+ SDEBUG("follows-p(pp=%d) = %d\n", parse_pos, val);*/
+
+ dontclaim = 1;
+ return 1; /* appears to work best */
+}
+
+
+static inline int
+next_parse_token(int *token_is_op)
+{
+ int item = state->heap[addr++];
+
+ if (item < 0xf0) {
+ item = item << 8 | state->heap[addr++];
+ *token_is_op = 0;
+ } else
+ *token_is_op = 1;
+
+ return item;
+}
+
+#define STATE_INITIAL 0
+#define STATE_OR 1
+#define STATE_SEEK 2
+#define STATE_REF 3
+
+static int
+simplesaid(int minor_state, int terminator)
+{
+ int major_state = STATE_INITIAL;
+ int refword = parse_pos; /* The word references apply to */
+ int aspiring_refword = -1; /* in "a < b < c", c refers to b, and aspiring_refword will be b. */
+ int truth = 1;
+ int token_is_op;
+ SCIkdebug(SCIkSAID, "simplesaid(%02x)\n", terminator);
+
+ while (42) {
+
+ int token = next_parse_token(&token_is_op);
+ SCIkdebug(SCIkSAID, "Got token %03x on truth %d\n", token, truth);
+
+ if (token_is_op && (token == terminator)) {
+ if (terminator == SAID_TERM)
+ truth = truth && end_p();
+ SCIkdebug(SCIkSAID, "Terminator; returning %d\n", truth);
+ return truth;
+ }
+
+ if (token_is_op) /* operator? */
+ switch (token) {
+
+ case SAID_COMMA:
+ minor_state = STATE_OR;
+ break;
+
+ case SAID_SLASH:
+ if (!truth) {
+ while (((token = next_parse_token(&token_is_op)) != terminator)
+ && (token != SAID_TERM)); /* Proceed to end of block */
+ if (token != terminator)
+ SCIkwarn(SCIkERROR, "Syntax error: Unexpected end of spec");
+ return 0;
+ }
+
+ major_state = STATE_SEEK;
+ minor_state = STATE_INITIAL;
+ break;
+
+ case SAID_PARENC:
+ SCIkwarn(SCIkERROR, "')' without matching '('\n");
+ return 0;
+
+ case SAID_PARENO:
+ switch (minor_state) {
+
+ case STATE_OR:
+ truth |= simplesaid(minor_state, SAID_PARENC);
+ break;
+
+ case STATE_INITIAL:
+ truth = truth && simplesaid(minor_state, SAID_PARENC);
+ break;
+
+ default:
+ SCIkwarn(SCIkERROR, "'(' in minor state %d\n", minor_state);
+ }
+ break;
+
+ case SAID_BRACKC:
+ SCIkwarn(SCIkERROR, "']' without matching '['\n");
+ return 0;
+
+ case SAID_BRACKO: {
+ int parse_pos_old = parse_pos;
+ int recurse = simplesaid(minor_state, SAID_BRACKC);
+ if (!recurse)
+ parse_pos = parse_pos_old;
+ break;
+ }
+
+ case SAID_LT:
+ if (aspiring_refword > -1) /* "a < b < c" */
+ refword = aspiring_refword; /* refword = 'b' (in the case above) */
+ major_state = STATE_REF;
+ break;
+
+ case SAID_GT:
+ return truth && follows_p();
+
+ case SAID_TERM:
+ SCIkwarn(SCIkERROR, "Unexpected end of spec\n");
+ return 0;
+
+ default:
+ SCIkwarn(SCIkERROR, "Syntax error: Unexpected token 0x%02x\n", token);
+ return 0;
+ } else {
+ int tempresult;
+
+ /* ++word_tokens_nr; /* Normal word token */
+
+ switch(major_state) {
+
+ case STATE_INITIAL:
+ tempresult = word_p(((minor_state == STATE_OR)? WORDP_MODE_STAY : WORDP_MODE_NEXT), token);
+ refword = parse_pos;
+ break;
+
+ case STATE_SEEK:
+ tempresult = word_p(WORDP_MODE_SEEK, token);
+ major_state = STATE_INITIAL;
+ refword = parse_pos;
+ break;
+
+ case STATE_REF:
+ tempresult = reference_p(refword, token);
+ aspiring_refword = parse_pos;
+ break;
+
+ default:
+ SCIkwarn(SCIkERROR, "Invalid major state!\n");
+ return 0;
+ }
+
+ SCIkdebug(SCIkSAID, "state=(%d,%d), truth = %d * %d\n", major_state, minor_state, truth, tempresult);
+
+ if (minor_state == STATE_OR)
+ truth |= tempresult;
+ else
+ truth = truth && tempresult;
+
+ minor_state = STATE_INITIAL;
+ }
+ }
+}
+
+#undef s
+
+int
+vocab_simple_said_test(state_t *s, heap_ptr address)
+{
+ int matched;
+ current_pword = reached_end = 0;
+ dontclaim = 0;
+ said_pos = 0;
+ if (s->parser_lastmatch_word == SAID_NO_MATCH)
+ SCIkdebug(SCIkSAID, "lastmatch_word: none\n");
+ else
+ SCIkdebug(SCIkSAID, "lastmatch_word=%d\n", s->parser_lastmatch_word);
+ parse_pos = (s->parser_lastmatch_word == SAID_NO_MATCH)? -1 : s->parser_lastmatch_word;
+ refstart_pos = parse_pos;
+ state = s;
+ addr = address;
+ words = s->parser_nodes;
+ matched = simplesaid(STATE_INITIAL, SAID_TERM);
+ SCIkdebug(SCIkSAID, "Result: (matched,dontclaim)=(%d,%d)\n", matched, dontclaim);
+
+ if (!matched)
+ return SAID_NO_MATCH;
+
+ if (dontclaim) /* partial match */
+ return parse_pos; /* Return lastmatch word */
+
+ return SAID_FULL_MATCH;
+}
+
+#endif /* SCI_SIMPLE_SAID_CODE */
diff --git a/engines/sci/engine/sys_strings.c b/engines/sci/engine/sys_strings.c
new file mode 100644
index 0000000000..945d79343c
--- /dev/null
+++ b/engines/sci/engine/sys_strings.c
@@ -0,0 +1,119 @@
+/***************************************************************************
+ sys_strings.c Copyright (C) 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 (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+
+#include <sys_strings.h>
+#include <stdio.h>
+#include <sci_memory.h>
+
+void
+sys_string_acquire(sys_strings_t *strings, int index, const char *name, int max_len)
+{
+ sys_string_t *str = strings->strings + index;
+
+ if (index < 0 || index >= SYS_STRINGS_MAX) {
+ fprintf(stderr, "[SYSSTR] Error: Attempt to acquire string #%d\n",
+ index);
+ BREAKPOINT();
+ }
+
+ if (str->name
+ && (strcmp(name, str->name)
+ || (str->max_size != max_len))) {
+ fprintf(stderr, "[SYSSTR] Error: Attempt to re-acquire existing string #%d;"
+ "was '%s', tried to claim as '%s'\n",
+ index, str->name, name);
+ BREAKPOINT();
+ }
+
+ str->name = strdup(name);
+ str->max_size = max_len;
+ str->value = (char*)sci_malloc(max_len + 1);
+ str->value[0] = 0; /* Set to empty string */
+}
+
+int
+sys_string_set(sys_strings_t *strings, int index, const char *value)
+{
+ sys_string_t *str = strings->strings + index;
+
+ if (index < 0 || index >= SYS_STRINGS_MAX || !str->name) {
+ fprintf(stderr, "[SYSSTR] Error: Attempt to write to invalid/unused string #%d\n",
+ index);
+ BREAKPOINT();
+ return 1;
+ }
+
+ strncpy(str->value, value, str->max_size);
+ str->value[str->max_size] = 0; /* Make sure to terminate */
+ return 0;
+}
+
+void
+sys_string_free(sys_strings_t *strings, int index)
+{
+ sys_string_t *str = strings->strings + index;
+
+ free(str->name);
+ str->name = NULL;
+
+ free(str->value);
+ str->value = NULL;
+
+ str->max_size = 0;
+}
+
+void
+sys_string_free_all(sys_strings_t *strings)
+{
+ int i;
+
+ for (i=0;i<SYS_STRINGS_MAX;i++)
+ {
+ if (strings->strings[i].name)
+ sys_string_free(strings, i);
+ }
+
+}
+
+void
+sys_strings_restore(sys_strings_t *new_strings, sys_strings_t *old_strings)
+{
+ int i;
+
+ /* First, pad memory */
+ for (i = 0; i < SYS_STRINGS_MAX; i++) {
+ sys_string_t *s = new_strings->strings + i;
+ char *data = s->value;
+ if (data) {
+ s->value = (char *)sci_malloc(s->max_size + 1);
+ strcpy(s->value, data);
+ sci_free(data);
+ }
+ }
+
+ sys_string_set(new_strings, SYS_STRING_SAVEDIR, old_strings->strings[SYS_STRING_SAVEDIR].value);
+}
diff --git a/engines/sci/engine/vm.c b/engines/sci/engine/vm.c
new file mode 100644
index 0000000000..d628e80e7a
--- /dev/null
+++ b/engines/sci/engine/vm.c
@@ -0,0 +1,2410 @@
+/***************************************************************************
+ vm.c Copyright (C) 1999 -- 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]
+
+***************************************************************************/
+
+
+#include <sciresource.h>
+#include <engine.h>
+#include <versions.h>
+#include <kdebug.h>
+#include "kernel_types.h"
+#include <seg_manager.h>
+#include "gc.h"
+#include <sfx_player.h>
+
+#ifdef HAVE_SETJMP_H
+#include <setjmp.h>
+#endif
+
+reg_t NULL_REG = NULL_REG_INITIALIZER;
+
+/*#define VM_DEBUG_SEND*/
+#undef STRICT_SEND /* Disallows variable sends with more than one parameter */
+#undef STRICT_READ /* Disallows reading from out-of-bounds parameters and locals */
+
+
+int script_abort_flag = 0; /* Set to 1 to abort execution */
+int script_error_flag = 0; /* Set to 1 if an error occured, reset each round by the VM */
+int script_checkloads_flag = 0; /* Print info when scripts get (un)loaded */
+int script_step_counter = 0; /* Counts the number of steps executed */
+int script_gc_interval = GC_INTERVAL; /* Number of steps in between gcs */
+
+extern int _debug_step_running; /* scriptdebug.c */
+extern int _debug_seeking; /* scriptdebug.c */
+extern int _weak_validations; /* scriptdebug.c */
+
+
+calls_struct_t *send_calls = NULL;
+int send_calls_allocated = 0;
+int bp_flag = 0;
+static reg_t _dummy_register = NULL_REG_INITIALIZER;
+
+
+static int jump_initialized = 0;
+#ifdef HAVE_SETJMP_H
+static jmp_buf vm_error_address;
+#endif
+
+/*-- validation functionality --*/
+
+#ifndef DISABLE_VALIDATIONS
+
+static inline reg_t *
+validate_property(object_t *obj, int index)
+{
+ if (!obj)
+ {
+ if (sci_debug_flags & 4)
+ sciprintf("[VM] Sending to disposed object!\n");
+ _dummy_register = NULL_REG;
+ return &_dummy_register;
+ }
+
+ if (index < 0 || index >= obj->variables_nr) {
+ if (sci_debug_flags & 4)
+ sciprintf("[VM] Invalid property #%d (out of [0..%d]) requested!\n", index,
+ obj->variables_nr);
+
+ _dummy_register = NULL_REG;
+ return &_dummy_register;
+ }
+
+ return obj->variables + index;
+}
+
+static inline stack_ptr_t
+validate_stack_addr(state_t *s, stack_ptr_t sp)
+{
+ if (sp >= s->stack_base && sp < s->stack_top)
+ return sp;
+
+ script_debug_flag = script_error_flag = 1;
+ if (sci_debug_flags & 4)
+ sciprintf("[VM] Stack index %d out of valid range [%d..%d]\n",
+ sp - s->stack_base, 0, s->stack_top - s->stack_base -1);
+ return 0;
+}
+
+
+static inline int
+validate_arithmetic(reg_t reg)
+{
+ if (reg.segment) {
+ if (!_weak_validations)
+ script_debug_flag = script_error_flag = 1;
+ if (sci_debug_flags & 4)
+ sciprintf("[VM] Attempt to read arithmetic value from non-zero segment [%04x]\n", reg.segment);
+ return 0;
+ }
+
+ return reg.offset;
+}
+
+static inline int
+signed_validate_arithmetic(reg_t reg)
+{
+ if (reg.segment) {
+ if (!_weak_validations)
+ script_debug_flag = script_error_flag = 1;
+ if (sci_debug_flags & 4)
+ sciprintf("[VM] Attempt to read arithmetic value from non-zero segment [%04x]\n", reg.segment);
+ return 0;
+ }
+
+ if (reg.offset&0x8000)
+ return (signed) (reg.offset)-65536;
+ else
+ return reg.offset;
+}
+
+static inline int
+validate_variable(reg_t *r, reg_t *stack_base, int type, int max, int index, int line)
+{
+ const char *names[4] = {"global", "local", "temp", "param"};
+
+ if (index < 0 || index >= max) {
+ sciprintf("[VM] Attempt to use invalid %s variable %04x ", names[type], index);
+ if (max == 0)
+ sciprintf("(variable type invalid)");
+ else
+ sciprintf("(out of range [%d..%d])", 0, max-1);
+ sciprintf(" in %s, line %d\n", __FILE__, line);
+ if (!_weak_validations)
+ script_debug_flag = script_error_flag = 1;
+
+#ifdef STRICT_READ
+ return 1;
+#else /* !STRICT_READ */
+ if (type == VAR_PARAM || type == VAR_TEMP) {
+ int total_offset = r - stack_base;
+ if (total_offset < 0 || total_offset >= VM_STACK_SIZE) {
+ sciprintf("[VM] Access would be outside even of the stack (%d); access denied\n",
+ total_offset);
+ return 1;
+ } else {
+ sciprintf("[VM] Access within stack boundaries; access granted.\n");
+ return 0;
+ }
+ };
+#endif
+ }
+
+ return 0;
+}
+
+static inline reg_t
+validate_read_var(reg_t *r, reg_t *stack_base, int type, int max, int index, int line, reg_t default_value)
+{
+ if (!validate_variable(r, stack_base, type, max, index, line))
+ return r[index];
+ else
+ return default_value;
+}
+
+static inline void
+validate_write_var(reg_t *r, reg_t *stack_base, int type, int max, int index, int line, reg_t value)
+{
+ if (!validate_variable(r, stack_base, type, max, index, line))
+ r[index] = value;
+}
+
+# define ASSERT_ARITHMETIC(v) validate_arithmetic(v)
+
+#else
+/*-- Non-validating alternatives -- */
+
+# define validate_stack_addr(s, sp) sp
+# define validate_arithmetic(r) ((r).offset)
+# define signed_validate_arithmetic(r) ((int) ((r).offset)&0x8000 ? (signed) ((r).offset)-65536 : ((r).offset))
+# define validate_variable(r, sb, t, m, i, l)
+# define validate_read_var(r, sb, t, m, i, l) ((r)[i])
+# define validate_write_var(r, sb, t, m, i, l, v) ((r)[i] = (v))
+# define validate_property(o, p) (&((o)->variables[p]))
+# define ASSERT_ARITHMETIC(v) (v).offset
+
+#endif
+
+#define READ_VAR(type, index, def) validate_read_var(variables[type], s->stack_base, type, variables_max[type], index, __LINE__, def)
+#define WRITE_VAR(type, index, value) validate_write_var(variables[type], s->stack_base, type, variables_max[type], index, __LINE__, value)
+#define WRITE_VAR16(type, index, value) WRITE_VAR(type, index, make_reg(0, value));
+
+#define ACC_ARITHMETIC_L(op) make_reg(0, (op validate_arithmetic(s->r_acc)))
+#define ACC_AUX_LOAD() aux_acc = signed_validate_arithmetic(s->r_acc)
+#define ACC_AUX_STORE() s->r_acc = make_reg(0, aux_acc)
+
+#define OBJ_PROPERTY(o, p) (*validate_property(o, p))
+
+/*==--------------------------==*/
+
+int
+script_error(state_t *s, const char *file, int line, const char *reason)
+{
+ sciprintf("Script error in file %s, line %d: %s\n", file, line, reason);
+ script_debug_flag = script_error_flag = 1;
+ return 0;
+}
+#define CORE_ERROR(area, msg) script_error(s, "[" area "] " __FILE__, __LINE__, msg)
+
+reg_t
+get_class_address(state_t *s, int classnr, int lock, reg_t caller)
+{
+ class_t *class = s->classtable + classnr;
+
+ if (NULL == s) {
+ sciprintf("vm.c: get_class_address(): NULL passed for \"s\"\n");
+ return NULL_REG;
+ }
+
+ if (classnr < 0
+ || s->classtable_size <= classnr
+ || class->script < 0) {
+ sciprintf("[VM] Attempt to dereference class %x, which doesn't exist (max %x)\n",
+ classnr, s->classtable_size);
+ script_error_flag = script_debug_flag = 1;
+ return NULL_REG;
+ } else {
+ if (!class->reg.segment) {
+ script_get_segment(s, class->script, lock);
+
+ if (!class->reg.segment) {
+ sciprintf("[VM] Trying to instantiate class %x by instantiating script 0x%x (%03d) failed;"
+ " Entering debugger.\n", classnr, class->script);
+ script_error_flag = script_debug_flag = 1;
+ return NULL_REG;
+ }
+ } else
+ if (caller.segment != class->reg.segment)
+ sm_increment_lockers(&s->seg_manager, class->reg.segment, SEG_ID);
+
+ return class->reg;
+ }
+}
+
+/* Operating on the stack */
+/* 16 bit: */
+#define PUSH(v) PUSH32(make_reg(0, v))
+#define POP() (validate_arithmetic(POP32()))
+/* 32 bit: */
+#define PUSH32(a) (*(validate_stack_addr(s, (xs->sp)++)) = (a))
+#define POP32() (*(validate_stack_addr(s, --(xs->sp))))
+
+/* Getting instruction parameters */
+#define GET_OP_BYTE() ((guint8) code_buf[(xs->addr.pc.offset)++])
+#define GET_OP_WORD() (getUInt16(code_buf + ((xs->addr.pc.offset) += 2) - 2))
+#define GET_OP_FLEX() ((opcode & 1)? GET_OP_BYTE() : GET_OP_WORD())
+#define GET_OP_SIGNED_BYTE() ((gint8)(code_buf[(xs->addr.pc.offset)++]))
+#define GET_OP_SIGNED_WORD() ((getInt16(code_buf + ((xs->addr.pc.offset) += 2) - 2)))
+#define GET_OP_SIGNED_FLEX() ((opcode & 1)? GET_OP_SIGNED_BYTE() : GET_OP_SIGNED_WORD())
+
+#define SEG_GET_HEAP( s, reg ) sm_get_heap( &s->seg_manager, reg )
+#define OBJ_SPECIES(s, reg) SEG_GET_HEAP(s, make_reg(reg.segment, reg.offset + SCRIPT_SPECIES_OFFSET))
+/* Returns an object's species */
+
+#define OBJ_SUPERCLASS(s, reg) SEG_GET_HEAP(s, make_reg(reg.segment, reg.offset + SCRIPT_SUPERCLASS_OFFSET))
+/* Returns an object's superclass */
+
+inline exec_stack_t *
+execute_method(state_t *s, word script, word pubfunct, stack_ptr_t sp,
+ reg_t calling_obj, word argc, stack_ptr_t argp)
+{
+ int seg;
+ guint16 temp;
+
+ if (!sm_script_is_loaded (&s->seg_manager, script, SCRIPT_ID)) /* Script not present yet? */
+ script_instantiate(s, script);
+ else
+ sm_unmark_script_deleted(&s->seg_manager, script);
+
+ seg = sm_seg_get( &s->seg_manager, script );
+
+ temp = sm_validate_export_func( &s->seg_manager, pubfunct, seg );
+ VERIFY( temp, "Invalid pubfunct in export table" );
+ if( !temp ) {
+ sciprintf("Request for invalid exported function 0x%x of script 0x%x\n", pubfunct, script);
+ script_error_flag = script_debug_flag = 1;
+ return NULL;
+ }
+
+ /* Check if a breakpoint is set on this method */
+ if (s->have_bp & BREAK_EXPORT)
+ {
+ breakpoint_t *bp;
+ guint32 bpaddress;
+
+ bpaddress = (script << 16 | pubfunct);
+
+ bp = s->bp_list;
+ while (bp)
+ {
+ if (bp->type == BREAK_EXPORT && bp->data.address == bpaddress)
+ {
+ sciprintf ("Break on script %d, export %d\n", script, pubfunct);
+ script_debug_flag = 1;
+ bp_flag = 1;
+ break;
+ }
+ bp = bp->next;
+ }
+ }
+
+ return add_exec_stack_entry(s, make_reg( seg, temp ),
+ sp, calling_obj, argc, argp, -1, calling_obj,
+ s->execution_stack_pos, seg);
+}
+
+
+static void
+_exec_varselectors(state_t *s)
+{ /* Executes all varselector read/write ops on the TOS */
+ /* Now check the TOS to execute all varselector entries */
+ if (s->execution_stack_pos >= 0)
+ while (s->execution_stack[s->execution_stack_pos].type == EXEC_STACK_TYPE_VARSELECTOR) {
+ /* varselector access? */
+ if (s->execution_stack[s->execution_stack_pos].argc) { /* write? */
+ reg_t temp = s->execution_stack[s->execution_stack_pos].variables_argp[1];
+ *(s->execution_stack[s->execution_stack_pos].addr.varp) = temp;
+
+ } else /* No, read */
+ s->r_acc = *(s->execution_stack[s->execution_stack_pos].addr.varp);
+
+ --(s->execution_stack_pos);
+ }
+}
+
+exec_stack_t *
+send_selector(state_t *s, reg_t send_obj, reg_t work_obj,
+ stack_ptr_t sp, int framesize, stack_ptr_t argp)
+ /* send_obj and work_obj are equal for anything but 'super' */
+ /* Returns a pointer to the TOS exec_stack element */
+{
+#ifdef VM_DEBUG_SEND
+ int i;
+#endif
+ reg_t *varp;
+ reg_t funcp;
+ int selector;
+ int argc;
+ int origin = s->execution_stack_pos; /* Origin: Used for debugging */
+ exec_stack_t *retval = s->execution_stack + s->execution_stack_pos;
+ int print_send_action = 0;
+ /* We return a pointer to the new active exec_stack_t */
+
+ /* The selector calls we catch are stored below: */
+ int send_calls_nr = -1;
+
+ if (NULL == s) {
+ sciprintf("vm.c: exec_stack_t(): NULL passed for \"s\"\n");
+ return NULL;
+ }
+
+ while (framesize > 0) {
+
+ selector = validate_arithmetic(*argp++);
+ argc = validate_arithmetic(*argp);
+
+ if (argc > 0x800){ /* More arguments than the stack could possibly accomodate for */
+ CORE_ERROR("SEND", "More than 0x800 arguments to function call\n");
+ return NULL;
+ }
+
+ /* Check if a breakpoint is set on this method */
+ if (s->have_bp & BREAK_SELECTOR) {
+ breakpoint_t *bp;
+ char method_name [256];
+
+ sprintf (method_name, "%s::%s",
+ obj_get_name(s, send_obj),
+ s->selector_names [selector]);
+
+ bp = s->bp_list;
+ while (bp) {
+ int cmplen = strlen(bp->data.name);
+ if (bp->data.name[cmplen - 1] != ':')
+ cmplen = 256;
+
+ if (bp->type == BREAK_SELECTOR && !strncmp (bp->data.name, method_name, cmplen)) {
+ sciprintf ("Break on %s (in ["PREG"])\n", method_name,
+ PRINT_REG(send_obj));
+ script_debug_flag = print_send_action = 1;
+ bp_flag = 1;
+ break;
+ }
+ bp = bp->next;
+ }
+ }
+
+#ifdef VM_DEBUG_SEND
+ sciprintf("Send to "PREG", selector %04x (%s):",
+ PRINT_REG(send_obj), selector, s->selector_names[selector]);
+#endif /* VM_DEBUG_SEND */
+
+ if (++send_calls_nr == (send_calls_allocated - 1))
+ send_calls = (calls_struct_t*)sci_realloc(send_calls, sizeof(calls_struct_t)
+ * (send_calls_allocated *= 2));
+
+
+ switch (lookup_selector(s, send_obj, selector, &varp, &funcp)) {
+
+ case SELECTOR_NONE:
+ sciprintf("Send to invalid selector 0x%x of object at "PREG"\n",
+ 0xffff & selector, PRINT_REG(send_obj));
+ script_error_flag = script_debug_flag = 1;
+ --send_calls_nr;
+ break;
+
+ case SELECTOR_VARIABLE:
+
+#ifdef VM_DEBUG_SEND
+ sciprintf("Varselector: ");
+ if (argc)
+ sciprintf("Write "PREG"\n", PRINT_REG(argp[1]));
+ else
+ sciprintf("Read\n");
+#endif /* VM_DEBUG_SEND */
+
+ switch (argc) {
+ case 0: /* Read selector */
+ if (print_send_action) {
+ sciprintf("[read selector]\n");
+ print_send_action = 0;
+ }
+ /* fallthrough */
+ case 1:
+#ifndef STRICT_SEND
+ default:
+#endif
+ { /* Argument is supplied -> Selector should be set */
+
+ if (print_send_action) {
+ reg_t val = *varp;
+ reg_t new = argp[1];
+
+ sciprintf("[write to selector: change "PREG" to "PREG"]\n",
+ PRINT_REG(val), PRINT_REG(new));
+ print_send_action = 0;
+ }
+ send_calls[send_calls_nr].address.var = varp; /* register the call */
+ send_calls[send_calls_nr].argp = argp;
+ send_calls[send_calls_nr].argc = argc;
+ send_calls[send_calls_nr].selector = selector;
+ send_calls[send_calls_nr].type = EXEC_STACK_TYPE_VARSELECTOR; /* Register as a varselector */
+
+ } break;
+#ifdef STRICT_SEND
+ default:
+ --send_calls_nr;
+ sciprintf("Send error: Variable selector %04x in "PREG" called with %04x params\n",
+ selector, PRINT_REG(send_obj), argc);
+ script_debug_flag = 1; /* Enter debug mode */
+ _debug_seeking = _debug_step_running = 0;
+
+#endif
+ }
+ break;
+
+ case SELECTOR_METHOD:
+
+#ifdef VM_DEBUG_SEND
+ sciprintf("Funcselector(");
+ for (i = 0; i < argc; i++) {
+ sciprintf(PREG, PRINT_REG(argp[i+1]));
+ if (i + 1 < argc)
+ sciprintf(", ");
+ }
+ sciprintf(") at "PREG"\n", PRINT_REG(funcp));
+#endif /* VM_DEBUG_SEND */
+ if (print_send_action) {
+ sciprintf("[invoke selector]\n");
+ print_send_action = 0;
+ }
+
+ send_calls[send_calls_nr].address.func = funcp; /* register call */
+ send_calls[send_calls_nr].argp = argp;
+ send_calls[send_calls_nr].argc = argc;
+ send_calls[send_calls_nr].selector = selector;
+ send_calls[send_calls_nr].type = EXEC_STACK_TYPE_CALL;
+ send_calls[send_calls_nr].sp = sp;
+ sp = CALL_SP_CARRY; /* Destroy sp, as it will be carried over */
+
+ break;
+ } /* switch(lookup_selector()) */
+
+
+ framesize -= (2 + argc);
+ argp += argc + 1;
+ }
+
+ /* Iterate over all registered calls in the reverse order. This way, the first call is
+ ** placed on the TOS; as soon as it returns, it will cause the second call to be executed.
+ */
+ for (; send_calls_nr >= 0; send_calls_nr--)
+ if (send_calls[send_calls_nr].type == EXEC_STACK_TYPE_VARSELECTOR) /* Write/read variable? */
+ retval = add_exec_stack_varselector(s, work_obj, send_calls[send_calls_nr].argc,
+ send_calls[send_calls_nr].argp,
+ send_calls[send_calls_nr].selector,
+ send_calls[send_calls_nr].address.var, origin);
+
+ else
+ retval =
+ add_exec_stack_entry(s, send_calls[send_calls_nr].address.func,
+ send_calls[send_calls_nr].sp, work_obj,
+ send_calls[send_calls_nr].argc,
+ send_calls[send_calls_nr].argp,
+ send_calls[send_calls_nr].selector,
+ send_obj, origin,
+ SCI_XS_CALLEE_LOCALS);
+
+ _exec_varselectors(s);
+
+ retval = s->execution_stack + s->execution_stack_pos;
+ return retval;
+}
+
+
+exec_stack_t *
+add_exec_stack_varselector(state_t *s, reg_t objp, int argc, stack_ptr_t argp,
+ selector_t selector, reg_t *address, int origin)
+{
+ exec_stack_t *xstack = add_exec_stack_entry(s, NULL_REG, address, objp, argc, argp,
+ selector, objp, origin, SCI_XS_CALLEE_LOCALS);
+ /* Store selector address in sp */
+
+ xstack->addr.varp = address;
+ xstack->type = EXEC_STACK_TYPE_VARSELECTOR;
+
+ return xstack;
+}
+
+
+exec_stack_t *
+add_exec_stack_entry(state_t *s, reg_t pc, stack_ptr_t sp, reg_t objp, int argc,
+ stack_ptr_t argp, selector_t selector, reg_t sendp, int origin,
+ seg_id_t locals_segment)
+/* Returns new TOS element for the execution stack*/
+/* locals_segment may be -1 if derived from the called object */
+{
+ exec_stack_t *xstack = NULL;
+
+ if (!s->execution_stack)
+ s->execution_stack =
+ (exec_stack_t*)sci_malloc(sizeof(exec_stack_t) * (s->execution_stack_size = 16));
+
+ if (++(s->execution_stack_pos) == s->execution_stack_size) /* Out of stack space? */
+ s->execution_stack = (exec_stack_t*)sci_realloc(s->execution_stack,
+ sizeof(exec_stack_t) * (s->execution_stack_size += 8));
+
+ /* sciprintf("Exec stack: [%d/%d], origin %d, at %p\n", s->execution_stack_pos,
+ s->execution_stack_size, origin, s->execution_stack); */
+
+ xstack = s->execution_stack + s->execution_stack_pos;
+
+ xstack->objp = objp;
+ if (locals_segment != SCI_XS_CALLEE_LOCALS)
+ xstack->local_segment = locals_segment;
+ else
+ xstack->local_segment = pc.segment;
+
+ xstack->sendp = sendp;
+ xstack->addr.pc = pc;
+ xstack->fp = xstack->sp = sp;
+ xstack->argc = argc;
+
+ xstack->variables_argp = argp; /* Parameters */
+
+ *argp = make_reg(0, argc); /* SCI code relies on the zeroeth argument to equal argc */
+
+ /* Additional debug information */
+ xstack->selector = selector;
+ xstack->origin = origin;
+
+ xstack->type = EXEC_STACK_TYPE_CALL; /* Normal call */
+
+ return xstack;
+}
+
+
+#ifdef DISABLE_VALIDATONS
+# define kernel_matches_signature(a, b, c, d) 1
+#endif
+
+
+void
+vm_handle_fatal_error(state_t *s, int line, const char *file)
+{
+ fprintf(stderr, "Fatal VM error in %s, L%d; aborting...\n", file, line);
+#ifdef HAVE_SETJMP_H
+ if (jump_initialized)
+ longjmp(vm_error_address, 0);
+#endif
+ fprintf(stderr, "Could not recover, exitting...\n");
+ exit(1);
+}
+
+static inline script_t *
+script_locate_by_segment(state_t *s, seg_id_t seg)
+{
+ mem_obj_t *memobj = GET_SEGMENT(s->seg_manager, seg, MEM_OBJ_SCRIPT);
+ if (memobj)
+ return &(memobj->data.script);
+
+ return NULL;
+}
+
+
+static reg_t
+pointer_add(state_t *s, reg_t base, int offset)
+{
+ mem_obj_t *mobj = GET_SEGMENT_ANY(s->seg_manager, base.segment);
+
+ if (!mobj) {
+ script_debug_flag = script_error_flag = 1;
+ sciprintf("[VM] Error: Attempt to add %d to invalid pointer "PREG"!", offset, PRINT_REG(base));
+ return NULL_REG;
+ }
+
+ switch (mobj->type) {
+
+ case MEM_OBJ_LOCALS:
+ base.offset += 2*offset;
+ return base;
+
+ case MEM_OBJ_SCRIPT:
+ case MEM_OBJ_STACK:
+ case MEM_OBJ_DYNMEM:
+ base.offset += offset;
+ return base;
+ break;
+
+ default:
+ sciprintf("[VM] Error: Attempt to add %d to pointer "PREG": Pointer arithmetics of this type unsupported!", offset, PRINT_REG(base));
+ return NULL_REG;
+
+ }
+}
+
+static inline void
+gc_countdown(state_t *s)
+{
+ if (s->gc_countdown-- <= 0) {
+ s->gc_countdown = script_gc_interval;
+ run_gc(s);
+ }
+}
+
+static byte _fake_return_buffer[2] = {op_ret << 1, op_ret << 1};
+
+void
+run_vm(state_t *s, int restoring)
+{
+ reg_t *variables[4]; /* global, local, temp, param, as immediate pointers */
+ reg_t *variables_base[4]; /* Used for referencing VM ops */
+ seg_id_t variables_seg[4]; /* Same as above, contains segment IDs */
+#ifndef DISABLE_VALIDATIONS
+ int variables_max[4]; /* Max. values for all variables */
+ int code_buf_size = 0 /* (Avoid spurious warning) */;
+#endif
+ int temp;
+ gint16 aux_acc; /* Auxiliary 16 bit accumulator */
+ reg_t r_temp; /* Temporary register */
+ stack_ptr_t s_temp; /* Temporary stack pointer */
+ gint16 opparams[4]; /* opcode parameters */
+
+ int restadjust = s->r_amp_rest; /* &rest adjusts the parameter count
+ ** by this value */
+ /* Current execution data: */
+ exec_stack_t *xs = s->execution_stack + s->execution_stack_pos;
+ exec_stack_t *xs_new = NULL /* (Avoid spurious warning) */; /* Used during some operations */
+ object_t *obj = obj_get(s, xs->objp);
+ script_t *local_script = script_locate_by_segment(s, xs->local_segment);
+ int old_execution_stack_base = s->execution_stack_base; /* Used to detect the
+ ** stack bottom, for "physical"
+ ** returns */
+ byte *code_buf = NULL /* (Avoid spurious warning) */;
+
+ if (!local_script) {
+ script_error(s, __FILE__, __LINE__, "Program Counter gone astray");
+ return;
+ }
+
+ if (NULL == s) {
+ sciprintf("vm.c: run_vm(): NULL passed for \"s\"\n");
+ return;
+ }
+
+#ifdef HAVE_SETJMP_H
+ setjmp(vm_error_address);
+ jump_initialized = 1;
+#endif
+
+ if (!restoring)
+ s->execution_stack_base = s->execution_stack_pos;
+
+#ifndef DISABLE_VALIDATIONS
+ /* Initialize maximum variable count */
+ if (s->script_000->locals_block)
+ variables_max[VAR_GLOBAL] = s->script_000->locals_block->nr;
+ else
+ variables_max[VAR_GLOBAL] = 0;
+#endif
+
+ variables_seg[VAR_GLOBAL] = s->script_000->locals_segment;
+ variables_seg[VAR_TEMP] = variables_seg[VAR_PARAM] = s->stack_segment;
+ variables_base[VAR_TEMP] = variables_base[VAR_PARAM] = s->stack_base;
+
+ /* SCI code reads the zeroeth argument to determine argc */
+ if (s->script_000->locals_block)
+ variables_base[VAR_GLOBAL] = variables[VAR_GLOBAL]
+ = s->script_000->locals_block->locals;
+ else
+ variables_base[VAR_GLOBAL] = variables[VAR_GLOBAL] = NULL;
+
+
+
+ s->execution_stack_pos_changed = 1; /* Force initialization */
+
+ while (1) {
+ byte opcode;
+ int old_pc_offset;
+ stack_ptr_t old_sp = xs->sp;
+ byte opnumber;
+ int var_type; /* See description below */
+ int var_number;
+
+ old_pc_offset = xs->addr.pc.offset;
+
+ if (s->execution_stack_pos_changed) {
+ script_t *scr;
+ xs = s->execution_stack + s->execution_stack_pos;
+ s->execution_stack_pos_changed = 0;
+
+ scr = script_locate_by_segment(s, xs->addr.pc.segment);
+ if (!scr) {
+ /* No script? Implicit return via fake instruction buffer */
+ SCIkdebug(SCIkWARNING, "Running on non-existant script in segment %x!\n", xs->addr.pc.segment);
+ code_buf = _fake_return_buffer;
+#ifndef DISABLE_VALIDATIONS
+ code_buf_size = 2;
+#endif
+ xs->addr.pc.offset = 1;
+
+ scr = NULL;
+ obj = NULL;
+ } else {
+ obj = obj_get(s, xs->objp);
+ code_buf = scr->buf;
+#ifndef DISABLE_VALIDATIONS
+ code_buf_size = scr->buf_size;
+#endif
+ /* if (!obj) {
+ SCIkdebug(SCIkWARNING, "Running with non-existant self= "PREG"\n", PRINT_REG(xs->objp));
+ }*/
+
+ local_script = script_locate_by_segment(s, xs->local_segment);
+ if (!local_script) {
+ SCIkdebug(SCIkWARNING, "Could not find local script from segment %x!\n", xs->local_segment);
+ local_script = NULL;
+ variables_base[VAR_LOCAL] = variables[VAR_LOCAL] = NULL;
+#ifndef DISABLE_VALIDATIONS
+ variables_max[VAR_LOCAL] = 0;
+#endif
+ } else {
+
+ variables_seg[VAR_LOCAL] = local_script->locals_segment;
+ if (local_script->locals_block)
+ variables_base[VAR_LOCAL] = variables[VAR_LOCAL]
+ = local_script->locals_block->locals;
+ else
+ variables_base[VAR_LOCAL] = variables[VAR_LOCAL]
+ = NULL;
+#ifndef DISABLE_VALIDATIONS
+ if (local_script->locals_block)
+ variables_max[VAR_LOCAL] = local_script->locals_block->nr;
+ else
+ variables_max[VAR_LOCAL] = 0;
+ variables_max[VAR_TEMP] = xs->sp - xs->fp;
+ variables_max[VAR_PARAM] = xs->argc + 1;
+#endif
+ }
+ variables[VAR_TEMP] = xs->fp;
+ variables[VAR_PARAM] = xs->variables_argp;
+ }
+
+ }
+
+ script_error_flag = 0; /* Set error condition to false */
+
+ if (script_abort_flag)
+ return; /* Emergency */
+
+ /* Debug if this has been requested: */
+ if (script_debug_flag || sci_debug_flags) {
+ script_debug(s, &(xs->addr.pc), &(xs->sp), &(xs->fp),
+ &(xs->objp), &restadjust,
+ variables_seg, variables, variables_base,
+#ifdef DISABLE_VALIDATIONS
+ NULL,
+#else
+ variables_max,
+#endif
+ bp_flag);
+ bp_flag = 0;
+ }
+
+#ifndef DISABLE_VALIDATIONS
+ if (xs->sp < xs->fp)
+ script_error(s, "[VM] "__FILE__, __LINE__, "Stack underflow");
+
+ variables_max[VAR_TEMP] = xs->sp - xs->fp;
+
+ if (xs->addr.pc.offset >= code_buf_size)
+ script_error(s, "[VM] "__FILE__, __LINE__, "Program Counter gone astray");
+#endif
+
+ opcode = GET_OP_BYTE(); /* Get opcode */
+
+ opnumber = opcode >> 1;
+
+ for (temp = 0; formats[opnumber][temp]; temp++) /* formats comes from script.c */
+ switch(formats[opnumber][temp]) {
+
+ case Script_Byte: opparams[temp] = GET_OP_BYTE(); break;
+ case Script_SByte: opparams[temp] = GET_OP_SIGNED_BYTE(); break;
+
+ case Script_Word: opparams[temp] = GET_OP_WORD(); break;
+ case Script_SWord: opparams[temp] = GET_OP_SIGNED_WORD(); break;
+
+ case Script_Variable:
+ case Script_Property:
+
+ case Script_Local:
+ case Script_Temp:
+ case Script_Global:
+ case Script_Param:
+ opparams[temp] = GET_OP_FLEX(); break;
+
+ case Script_SVariable:
+ case Script_SRelative:
+ opparams[temp] = GET_OP_SIGNED_FLEX(); break;
+
+ case Script_Offset:
+ opparams[temp] = GET_OP_FLEX(); break;
+
+ case Script_None:
+ case Script_End:
+ break;
+
+ case Script_Invalid:
+ default:
+ sciprintf("opcode %02x: Invalid!", opcode);
+ script_debug_flag = script_error_flag = 1;
+
+ }
+
+
+ switch (opnumber) {
+
+ case 0x00: /* bnot */
+ s->r_acc = ACC_ARITHMETIC_L (0xffff ^ /*acc*/);
+ break;
+
+ case 0x01: /* add */
+ r_temp = POP32();
+ if (r_temp.segment || s->r_acc.segment) {
+ reg_t r_ptr;
+ int offset;
+ /* Pointer arithmetics! */
+ if (s->r_acc.segment) {
+ if (r_temp.segment) {
+ sciprintf("Error: Attempt to add two pointers, stack="PREG" and acc="PREG"!\n",
+ PRINT_REG(r_temp), PRINT_REG(s->r_acc));
+ script_debug_flag = script_error_flag = 1;
+ offset = 0;
+ } else {
+ r_ptr = s->r_acc;
+ offset = r_temp.offset;
+ }
+ } else {
+ r_ptr = r_temp;
+ offset = s->r_acc.offset;
+ }
+
+ s->r_acc = pointer_add(s, r_ptr, offset);
+
+ } else
+ s->r_acc = make_reg(0, r_temp.offset + s->r_acc.offset);
+ break;
+
+ case 0x02: /* sub */
+ r_temp = POP32();
+ if (r_temp.segment || s->r_acc.segment) {
+ reg_t r_ptr;
+ int offset;
+ /* Pointer arithmetics! */
+ if (s->r_acc.segment) {
+ if (r_temp.segment) {
+ sciprintf("Error: Attempt to subtract two pointers, stack="PREG" and acc="PREG"!\n",
+ PRINT_REG(r_temp), PRINT_REG(s->r_acc));
+ script_debug_flag = script_error_flag = 1;
+ offset = 0;
+ } else {
+ r_ptr = s->r_acc;
+ offset = r_temp.offset;
+ }
+ } else {
+ r_ptr = r_temp;
+ offset = s->r_acc.offset;
+ }
+
+ s->r_acc = pointer_add(s, r_ptr, -offset);
+
+ } else
+ s->r_acc = make_reg(0, r_temp.offset - s->r_acc.offset);
+ break;
+
+ case 0x03: /* mul */
+ s->r_acc = ACC_ARITHMETIC_L (((gint16)POP()) * (gint16)/*acc*/);
+ break;
+
+ case 0x04: /* div */
+ ACC_AUX_LOAD();
+ aux_acc = aux_acc != 0 ? ((gint16)POP()) / aux_acc : 0;
+ ACC_AUX_STORE();
+ break;
+
+ case 0x05: /* mod */
+ ACC_AUX_LOAD();
+ aux_acc = aux_acc != 0 ? ((gint16)POP()) % aux_acc : 0;
+ ACC_AUX_STORE();
+ break;
+
+ case 0x06: /* shr */
+ s->r_acc = ACC_ARITHMETIC_L(((guint16) POP()) >> /*acc*/);
+ break;
+
+ case 0x07: /* shl */
+ s->r_acc = ACC_ARITHMETIC_L(((guint16) POP()) << /*acc*/);
+ break;
+
+ case 0x08: /* xor */
+ s->r_acc = ACC_ARITHMETIC_L(POP() ^ /*acc*/);
+ break;
+
+ case 0x09: /* and */
+ s->r_acc = ACC_ARITHMETIC_L(POP() & /*acc*/);
+ break;
+
+ case 0x0a: /* or */
+ s->r_acc = ACC_ARITHMETIC_L(POP() | /*acc*/);
+ break;
+
+ case 0x0b: /* neg */
+ s->r_acc = ACC_ARITHMETIC_L(-/*acc*/);
+ break;
+
+ case 0x0c: /* not */
+ s->r_acc = make_reg(0, !(s->r_acc.offset || s->r_acc.segment));
+ /* Must allow pointers to be negated, as this is used for
+ ** checking whether objects exist */
+ break;
+
+ case 0x0d: /* eq? */
+ s->r_prev = s->r_acc;
+ r_temp = POP32();
+ s->r_acc = make_reg(0, REG_EQ(r_temp, s->r_acc));
+ /* Explicitly allow pointers to be compared */
+ break;
+
+ case 0x0e: /* ne? */
+ s->r_prev = s->r_acc;
+ r_temp = POP32();
+ s->r_acc = make_reg(0, !REG_EQ(r_temp, s->r_acc));
+ /* Explicitly allow pointers to be compared */
+ break;
+
+ case 0x0f: /* gt? */
+ s->r_prev = s->r_acc;
+ s->r_acc = ACC_ARITHMETIC_L((gint16)POP() > (gint16)/*acc*/);
+ break;
+
+ case 0x10: /* ge? */
+ s->r_prev = s->r_acc;
+ s->r_acc = ACC_ARITHMETIC_L((gint16)POP() >= (gint16)/*acc*/);
+ break;
+
+ case 0x11: /* lt? */
+ s->r_prev = s->r_acc;
+ s->r_acc = ACC_ARITHMETIC_L((gint16)POP() < (gint16)/*acc*/);
+ break;
+
+ case 0x12: /* le? */
+ s->r_prev = s->r_acc;
+ s->r_acc = ACC_ARITHMETIC_L((gint16)POP() <= (gint16)/*acc*/);
+ break;
+
+ case 0x13: /* ugt? */
+ s->r_prev = s->r_acc;
+ r_temp = POP32();
+ s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset > s->r_acc.offset);
+ break;
+
+ case 0x14: /* uge? */
+ s->r_prev = s->r_acc;
+ r_temp = POP32();
+ s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset >= s->r_acc.offset);
+ break;
+
+ case 0x15: /* ult? */
+ s->r_prev = s->r_acc;
+ r_temp = POP32();
+ s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset < s->r_acc.offset);
+ break;
+
+ case 0x16: /* ule? */
+ s->r_prev = s->r_acc;
+ r_temp = POP32();
+ s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset <= s->r_acc.offset);
+ break;
+
+ case 0x17: /* bt */
+ if (s->r_acc.offset || s->r_acc.segment)
+ xs->addr.pc.offset += opparams[0];
+ break;
+
+ case 0x18: /* bnt */
+ if (!(s->r_acc.offset || s->r_acc.segment))
+ xs->addr.pc.offset += opparams[0];
+ break;
+
+ case 0x19: /* jmp */
+ xs->addr.pc.offset += opparams[0];
+ break;
+
+ case 0x1a: /* ldi */
+ s->r_acc = make_reg(0, opparams[0]);
+ break;
+
+ case 0x1b: /* push */
+ PUSH32(s->r_acc);
+ break;
+
+ case 0x1c: /* pushi */
+ PUSH(opparams[0]);
+ break;
+
+ case 0x1d: /* toss */
+ xs->sp--;
+ break;
+
+ case 0x1e: /* dup */
+ r_temp = xs->sp[-1];
+ PUSH32(r_temp);
+ break;
+
+ case 0x1f: { /* link */
+ int i;
+ for (i = 0; i < opparams[0]; i++)
+ xs->sp[i] = NULL_REG;
+ xs->sp += opparams[0];
+ break;
+ }
+
+ case 0x20: { /* call */
+ int argc = (opparams[1] >> 1) /* Given as offset, but we need count */
+ + 1 + restadjust;
+ stack_ptr_t call_base = xs->sp - argc;
+
+ xs->sp[1].offset += restadjust;
+ xs_new = add_exec_stack_entry(s, make_reg(xs->addr.pc.segment,
+ xs->addr.pc.offset
+ + opparams[0]),
+ xs->sp, xs->objp,
+ (validate_arithmetic(*call_base))
+ + restadjust,
+ call_base, NULL_SELECTOR, xs->objp,
+ s->execution_stack_pos, xs->local_segment);
+ restadjust = 0; /* Used up the &rest adjustment */
+ xs->sp = call_base;
+
+ s->execution_stack_pos_changed = 1;
+ break;
+ }
+
+ case 0x21: /* callk */
+ gc_countdown(s);
+
+ xs->sp -= (opparams[1] >> 1)+1;
+ if (s->version >= SCI_VERSION_FTU_NEW_SCRIPT_HEADER) {
+ xs->sp -= restadjust;
+ s->r_amp_rest = 0; /* We just used up the restadjust, remember? */
+ }
+
+ if (opparams[0] >= s->kfunct_nr) {
+
+ sciprintf("Invalid kernel function 0x%x requested\n", opparams[0]);
+ script_debug_flag = script_error_flag = 1;
+
+ } else {
+ int argc = ASSERT_ARITHMETIC(xs->sp[0]);
+
+ if (s->version >= SCI_VERSION_FTU_NEW_SCRIPT_HEADER)
+ argc += restadjust;
+
+ if (s->kfunct_table[opparams[0]].signature
+ && !kernel_matches_signature(s,
+ s->kfunct_table[opparams[0]]
+ .signature,
+ argc, xs->sp + 1)) {
+ sciprintf("[VM] Invalid arguments to kernel call %x\n",
+ opparams[0]);
+ script_debug_flag = script_error_flag = 1;
+ } else {
+ s->r_acc = s->kfunct_table[opparams[0]]
+ .fun(s, opparams[0], argc, xs->sp + 1);
+ }
+ /* Call kernel function */
+
+ /* Calculate xs again: The kernel function might
+ ** have spawned a new VM */
+
+ xs_new = s->execution_stack + s->execution_stack_pos;
+ s->execution_stack_pos_changed = 1;
+
+ if (s->version>=SCI_VERSION_FTU_NEW_SCRIPT_HEADER)
+ restadjust = s->r_amp_rest;
+
+ }
+ break;
+
+ case 0x22: /* callb */
+ temp = ((opparams[1] >> 1) + restadjust + 1);
+ s_temp = xs->sp;
+ xs->sp -= temp;
+
+
+ xs->sp[0].offset += restadjust;
+ xs_new = execute_method(s, 0, opparams[0], s_temp, xs->objp,
+ xs->sp[0].offset, xs->sp);
+ restadjust = 0; /* Used up the &rest adjustment */
+ if (xs_new) /* in case of error, keep old stack */
+ s->execution_stack_pos_changed = 1;
+ break;
+
+ case 0x23: /* calle */
+ temp = ((opparams[2] >> 1) + restadjust + 1);
+ s_temp = xs->sp;
+ xs->sp -= temp;
+
+ xs->sp[0].offset += restadjust;
+ xs_new = execute_method(s, opparams[0], opparams[1], s_temp, xs->objp,
+ xs->sp[0].offset, xs->sp);
+ restadjust = 0; /* Used up the &rest adjustment */
+
+ if (xs_new) /* in case of error, keep old stack */
+ s->execution_stack_pos_changed = 1;
+ break;
+
+ case 0x24: /* ret */
+ do {
+ stack_ptr_t old_sp = xs->sp;
+ stack_ptr_t old_fp = xs->fp;
+ exec_stack_t *old_xs = s->execution_stack + s->execution_stack_pos;
+
+ if (s->execution_stack_pos == s->execution_stack_base) { /* Have we reached the base? */
+ s->execution_stack_base = old_execution_stack_base; /* Restore stack base */
+
+ --(s->execution_stack_pos);
+
+ s->execution_stack_pos_changed = 1;
+ s->r_amp_rest = restadjust; /* Update &rest */
+ return; /* "Hard" return */
+ }
+
+ if (old_xs->type == EXEC_STACK_TYPE_VARSELECTOR) {
+ /* varselector access? */
+ if (old_xs->argc) /* write? */
+ *(old_xs->addr.varp) = old_xs->variables_argp[1];
+ else /* No, read */
+ s->r_acc = *(old_xs->addr.varp);
+ }
+
+ /* Not reached the base, so let's do a soft return */
+ --(s->execution_stack_pos);
+ xs = old_xs - 1;
+ s->execution_stack_pos_changed = 1;
+ xs = s->execution_stack + s->execution_stack_pos;
+
+ if (xs->sp == CALL_SP_CARRY /* Used in sends to 'carry' the stack pointer */
+ || xs->type != EXEC_STACK_TYPE_CALL) {
+ xs->sp = old_sp;
+ xs->fp = old_fp;
+ }
+
+ } while (xs->type == EXEC_STACK_TYPE_VARSELECTOR);
+ /* Iterate over all varselector accesses */
+ s->execution_stack_pos_changed = 1;
+ xs_new = xs;
+
+ break;
+
+ case 0x25: /* send */
+ s_temp = xs->sp;
+ xs->sp -= ((opparams[0] >> 1) + restadjust); /* Adjust stack */
+
+ xs->sp[1].offset += restadjust;
+ xs_new = send_selector(s, s->r_acc, s->r_acc, s_temp,
+ (int)(opparams[0]>>1) + (word)restadjust,
+ xs->sp);
+
+ if (xs_new && xs_new != xs)
+ s->execution_stack_pos_changed = 1;
+
+ restadjust = 0;
+
+ break;
+
+ case 0x28: /* class */
+ s->r_acc = get_class_address(s, (unsigned) opparams[0], SCRIPT_GET_LOCK, xs->addr.pc);
+ break;
+
+ case 0x2a: /* self */
+ s_temp = xs->sp;
+ xs->sp -= ((opparams[0] >> 1) + restadjust); /* Adjust stack */
+
+ xs->sp[1].offset += restadjust;
+ xs_new = send_selector(s, xs->objp, xs->objp, s_temp,
+ (int)(opparams[0]>>1) + (word)restadjust,
+ xs->sp);
+
+ if (xs_new && xs_new != xs)
+ s->execution_stack_pos_changed = 1;
+
+ restadjust = 0;
+ break;
+
+ case 0x2b: /* super */
+ r_temp = get_class_address(s, opparams[0], SCRIPT_GET_LOAD, xs->addr.pc);
+
+ if (!r_temp.segment)
+ CORE_ERROR("VM", "Invalid superclass in object");
+ else {
+ s_temp = xs->sp;
+ xs->sp -= ((opparams[1] >> 1) + restadjust); /* Adjust stack */
+
+ xs->sp[1].offset += restadjust;
+ xs_new = send_selector(s, r_temp, xs->objp, s_temp,
+ (int)(opparams[1]>>1) + (word)restadjust,
+ xs->sp);
+
+ if (xs_new && xs_new != xs)
+ s->execution_stack_pos_changed = 1;
+
+ restadjust = 0;
+ }
+
+ break;
+
+ case 0x2c: /* &rest */
+ temp = (guint16) opparams[0]; /* First argument */
+ restadjust = xs->argc - temp + 1; /* +1 because temp counts the paramcount while argc doesn't */
+ if (restadjust < 0)
+ restadjust = 0;
+
+ for (; temp <= xs->argc; temp++)
+ PUSH32(xs->variables_argp[temp]);
+
+ break;
+
+ case 0x2d: /* lea */
+ temp = (guint16) opparams[0] >> 1;
+ var_number = temp & 0x03; /* Get variable type */
+
+ /* Get variable block offset */
+ r_temp.segment = variables_seg[var_number];
+ r_temp.offset = variables[var_number] - variables_base[var_number];
+
+ if (temp & 0x08) /* Add accumulator offset if requested */
+ r_temp.offset += signed_validate_arithmetic(s->r_acc);
+
+ r_temp.offset += opparams[1]; /* Add index */
+ r_temp.offset *= sizeof(reg_t);
+ /* That's the immediate address now */
+ s->r_acc = r_temp;
+ break;
+
+
+ case 0x2e: /* selfID */
+ s->r_acc = xs->objp;
+ break;
+
+ case 0x30: /* pprev */
+ PUSH32(s->r_prev);
+ break;
+
+ case 0x31: /* pToa */
+ s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1));
+ break;
+
+ case 0x32: /* aTop */
+ OBJ_PROPERTY(obj, (opparams[0] >> 1)) = s->r_acc;
+ break;
+
+ case 0x33: /* pTos */
+ PUSH32(OBJ_PROPERTY(obj, opparams[0] >> 1));
+ break;
+
+ case 0x34: /* sTop */
+ OBJ_PROPERTY(obj, (opparams[0] >> 1)) = POP32();
+ break;
+
+ case 0x35: /* ipToa */
+ s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1));
+ s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1)) =
+ ACC_ARITHMETIC_L( 1 + /*acc*/);
+ break;
+
+ case 0x36: /* dpToa */
+ s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1));
+ s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1)) =
+ ACC_ARITHMETIC_L(-1 + /*acc*/);
+ break;
+
+ case 0x37: /* ipTos */
+ ASSERT_ARITHMETIC(OBJ_PROPERTY(obj, (opparams[0] >> 1)));
+ temp = ++OBJ_PROPERTY(obj, (opparams[0] >> 1)).offset;
+ PUSH(temp);
+ break;
+
+ case 0x38: /* dpTos */
+ ASSERT_ARITHMETIC(OBJ_PROPERTY(obj, (opparams[0] >> 1)));
+ temp = --OBJ_PROPERTY(obj, (opparams[0] >> 1)).offset;
+ PUSH(temp);
+ break;
+
+
+ case 0x39: /* lofsa */
+ s->r_acc.segment = xs->addr.pc.segment;
+
+ if (s->version >= SCI_VERSION(1,001,000))
+ s->r_acc.offset = opparams[0]+local_script->script_size;
+ else
+ if (s->version >= SCI_VERSION_FTU_LOFS_ABSOLUTE)
+ s->r_acc.offset = opparams[0];
+ else
+ s->r_acc.offset = xs->addr.pc.offset + opparams[0];
+#ifndef DISABLE_VALIDATIONS
+ if (s->r_acc.offset >= code_buf_size) {
+ sciprintf("VM: lofsa operation overflowed: "PREG" beyond end"
+ " of script (at %04x)\n", PRINT_REG(s->r_acc),
+ code_buf_size);
+ script_error_flag = script_debug_flag = 1;
+ }
+#endif
+ break;
+
+
+ case 0x3a: /* lofss */
+ r_temp.segment = xs->addr.pc.segment;
+
+ if (s->version >= SCI_VERSION_FTU_LOFS_ABSOLUTE)
+ r_temp.offset = opparams[0]; else
+ r_temp.offset = xs->addr.pc.offset + opparams[0];
+#ifndef DISABLE_VALIDATIONS
+ if (r_temp.offset >= code_buf_size) {
+ sciprintf("VM: lofss operation overflowed: "PREG" beyond end"
+ " of script (at %04x)\n", PRINT_REG(r_temp),
+ code_buf_size);
+ script_error_flag = script_debug_flag = 1;
+ }
+#endif
+ PUSH32(r_temp);
+ break;
+
+ case 0x3b: /* push0 */
+ PUSH(0);
+ break;
+
+ case 0x3c: /* push1 */
+ PUSH(1);
+ break;
+
+ case 0x3d: /* push2 */
+ PUSH(2);
+ break;
+
+ case 0x3e: /* pushSelf */
+ PUSH32(xs->objp);
+ break;
+
+ case 0x40: /* lag */
+ case 0x41: /* lal */
+ case 0x42: /* lat */
+ case 0x43: /* lap */
+ var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */
+ var_number = opparams[0];
+ s->r_acc = READ_VAR(var_type, var_number, s->r_acc);
+ break;
+
+ case 0x44: /* lsg */
+ case 0x45: /* lsl */
+ case 0x46: /* lst */
+ case 0x47: /* lsp */
+ var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */
+ var_number = opparams[0];
+ PUSH32(READ_VAR(var_type, var_number, s->r_acc));
+ break;
+
+ case 0x48: /* lagi */
+ case 0x49: /* lali */
+ case 0x4a: /* lati */
+ case 0x4b: /* lapi */
+ var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */
+ var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
+ s->r_acc = READ_VAR(var_type, var_number, s->r_acc);
+ break;
+
+ case 0x4c: /* lsgi */
+ case 0x4d: /* lsli */
+ case 0x4e: /* lsti */
+ case 0x4f: /* lspi */
+ var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */
+ var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
+ PUSH32(READ_VAR(var_type, var_number, s->r_acc));
+ break;
+
+ case 0x50: /* sag */
+ case 0x51: /* sal */
+ case 0x52: /* sat */
+ case 0x53: /* sap */
+ var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */
+ var_number = opparams[0];
+ WRITE_VAR(var_type, var_number, s->r_acc);
+ break;
+
+ case 0x54: /* ssg */
+ case 0x55: /* ssl */
+ case 0x56: /* sst */
+ case 0x57: /* ssp */
+ var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */
+ var_number = opparams[0];
+ WRITE_VAR(var_type, var_number, POP32());
+ break;
+
+ case 0x58: /* sagi */
+ case 0x59: /* sali */
+ case 0x5a: /* sati */
+ case 0x5b: /* sapi */
+ /* Special semantics because it wouldn't really make a whole lot
+ ** of sense otherwise, with acc being used for two things
+ ** simultaneously... */
+ var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */
+ var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
+ WRITE_VAR(var_type, var_number, s->r_acc = POP32());
+ break;
+
+ case 0x5c: /* ssgi */
+ case 0x5d: /* ssli */
+ case 0x5e: /* ssti */
+ case 0x5f: /* sspi */
+ var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */
+ var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
+ WRITE_VAR(var_type, var_number, POP32());
+ break;
+
+ case 0x60: /* +ag */
+ case 0x61: /* +al */
+ case 0x62: /* +at */
+ case 0x63: /* +ap */
+ var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */
+ var_number = opparams[0];
+ s->r_acc = make_reg(0,
+ 1 + validate_arithmetic(READ_VAR(var_type,
+ var_number,
+ s->r_acc)));
+ WRITE_VAR(var_type, var_number, s->r_acc);
+ break;
+
+ case 0x64: /* +sg */
+ case 0x65: /* +sl */
+ case 0x66: /* +st */
+ case 0x67: /* +sp */
+ var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */
+ var_number = opparams[0];
+ r_temp = make_reg(0,
+ 1 + validate_arithmetic(READ_VAR(var_type,
+ var_number,
+ s->r_acc)));
+ PUSH32(r_temp);
+ WRITE_VAR(var_type, var_number, r_temp);
+ break;
+
+ case 0x68: /* +agi */
+ case 0x69: /* +ali */
+ case 0x6a: /* +ati */
+ case 0x6b: /* +api */
+ var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */
+ var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
+ s->r_acc = make_reg(0,
+ 1 + validate_arithmetic(READ_VAR(var_type,
+ var_number,
+ s->r_acc)));
+ WRITE_VAR(var_type, var_number, s->r_acc);
+ break;
+
+ case 0x6c: /* +sgi */
+ case 0x6d: /* +sli */
+ case 0x6e: /* +sti */
+ case 0x6f: /* +spi */
+ var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */
+ var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
+ r_temp = make_reg(0,
+ 1 + validate_arithmetic(READ_VAR(var_type,
+ var_number,
+ s->r_acc)));
+ PUSH32(r_temp);
+ WRITE_VAR(var_type, var_number, r_temp);
+ break;
+
+ case 0x70: /* -ag */
+ case 0x71: /* -al */
+ case 0x72: /* -at */
+ case 0x73: /* -ap */
+ var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */
+ var_number = opparams[0];
+ s->r_acc = make_reg(0,
+ -1 + validate_arithmetic(READ_VAR(var_type,
+ var_number, s->r_acc)));
+ WRITE_VAR(var_type, var_number, s->r_acc);
+ break;
+
+ case 0x74: /* -sg */
+ case 0x75: /* -sl */
+ case 0x76: /* -st */
+ case 0x77: /* -sp */
+ var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */
+ var_number = opparams[0];
+ r_temp = make_reg(0,
+ -1 + validate_arithmetic(READ_VAR(var_type,
+ var_number, s->r_acc)));
+ PUSH32(r_temp);
+ WRITE_VAR(var_type, var_number, r_temp);
+ break;
+
+ case 0x78: /* -agi */
+ case 0x79: /* -ali */
+ case 0x7a: /* -ati */
+ case 0x7b: /* -api */
+ var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */
+ var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
+ s->r_acc = make_reg(0,
+ -1 + validate_arithmetic(READ_VAR(var_type,
+ var_number,
+ s->r_acc)));
+ WRITE_VAR(var_type, var_number, s->r_acc);
+ break;
+
+ case 0x7c: /* -sgi */
+ case 0x7d: /* -sli */
+ case 0x7e: /* -sti */
+ case 0x7f: /* -spi */
+ var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */
+ var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
+ r_temp = make_reg(0,
+ -1 + validate_arithmetic(READ_VAR(var_type,
+ var_number,
+ s->r_acc)));
+ PUSH32(r_temp);
+ WRITE_VAR(var_type, var_number, r_temp);
+ break;
+
+ default:
+ script_error(s, __FILE__, __LINE__, "Illegal opcode");
+
+ } /* switch(opcode >> 1) */
+
+
+ if (s->execution_stack_pos_changed) /* Force initialization */
+ xs = xs_new;
+
+#ifndef DISABLE_VALIDATIONS
+ if (xs != s->execution_stack + s->execution_stack_pos) {
+ sciprintf("Error: xs is stale (%d vs %d); last command was %02x\n",
+ xs-s->execution_stack, s->execution_stack_pos, opnumber);
+ }
+#endif
+
+ if (script_error_flag) {
+ _debug_step_running = 0; /* Stop multiple execution */
+ _debug_seeking = 0; /* Stop special seeks */
+ xs->addr.pc.offset = old_pc_offset;
+ xs->sp = old_sp;
+ } else
+ ++script_step_counter;
+ }
+}
+
+
+
+static inline int
+_obj_locate_varselector(state_t *s, object_t *obj, selector_t slc)
+{ /* Determines if obj explicitly defines slc as a varselector */
+ /* Returns -1 if not found */
+
+ if (s->version < SCI_VERSION(1,001,000))
+ {
+ int varnum = obj->variable_names_nr;
+ int selector_name_offset = varnum * 2 + SCRIPT_SELECTOR_OFFSET;
+ int i;
+ byte *buf = obj->base_obj + selector_name_offset;
+
+ obj->base_vars = (guint16 *) buf;
+
+ for (i = 0; i < varnum; i++)
+ if (getUInt16(buf + (i << 1)) == slc) /* Found it? */
+ return i; /* report success */
+
+ return -1; /* Failed */
+ }
+ else
+ {
+ byte *buf = (byte *) obj->base_vars;
+ int i;
+ int varnum = obj->variables[1].offset;
+
+ if (!(obj->variables[SCRIPT_INFO_SELECTOR].offset & SCRIPT_INFO_CLASS))
+ buf = ((byte *) obj_get(s, obj->variables[SCRIPT_SUPERCLASS_SELECTOR])->base_vars);
+
+ for (i = 0; i < varnum; i++)
+ if (getUInt16(buf + (i << 1)) == slc) /* Found it? */
+ return i; /* report success */
+
+ return -1; /* Failed */
+ }
+}
+
+
+static inline int
+_class_locate_funcselector(state_t *s, object_t *obj, selector_t slc)
+{ /* Determines if obj is a class and explicitly defines slc as a funcselector */
+ /* Does NOT say anything about obj's superclasses, i.e. failure may be
+ ** returned even if one of the superclasses defines the funcselector. */
+ int funcnum = obj->methods_nr;
+ int i;
+
+ for (i = 0; i < funcnum; i++)
+ if (VM_OBJECT_GET_FUNCSELECTOR(obj, i) == slc) /* Found it? */
+ return i; /* report success */
+
+ return -1; /* Failed */
+}
+
+
+static inline int
+_lookup_selector_function(state_t *s, int seg_id, object_t *obj, selector_t selector_id, reg_t *fptr)
+{
+ int index;
+
+ /* "recursive" lookup */
+
+ while (obj) {
+ index = _class_locate_funcselector(s, obj, selector_id);
+
+ if (index >= 0) {
+ if (fptr)
+ {
+ if (s->version < SCI_VERSION(1,001,000))
+ *fptr = make_reg(obj->pos.segment,
+ getUInt16((byte *)
+ (obj->base_method + index
+ + obj->methods_nr + 1)));
+ else
+ *fptr = make_reg(obj->pos.segment,
+ getUInt16((byte *)
+ (obj->base_method + index * 2 + 2)));
+ }
+
+ return SELECTOR_METHOD;
+ } else {
+ seg_id = obj->variables[SCRIPT_SUPERCLASS_SELECTOR].segment;
+ obj = obj_get(s, obj->variables[SCRIPT_SUPERCLASS_SELECTOR]);
+ }
+ }
+
+ return SELECTOR_NONE;
+}
+
+int
+lookup_selector(state_t *s, reg_t obj_location, selector_t selector_id, reg_t **vptr, reg_t *fptr)
+{
+ object_t *obj = obj_get(s, obj_location);
+ object_t *species;
+ int index;
+
+ /* Early SCI versions used the LSB in the selector ID as a read/write
+ ** toggle, meaning that we must remove it for selector lookup. */
+ if (s->version < SCI_VERSION_FTU_NEW_SCRIPT_HEADER)
+ selector_id &= ~1;
+
+ if (!obj) {
+ CORE_ERROR("SLC-LU", "Attempt to send to non-object or invalid script");
+ sciprintf("Address was "PREG"\n", PRINT_REG(obj_location));
+ return SELECTOR_NONE;
+ }
+
+ if (IS_CLASS(obj))
+ species = obj;
+ else
+ species = obj_get(s, obj->variables[SCRIPT_SPECIES_SELECTOR]);
+
+
+ if (!obj) {
+ CORE_ERROR("SLC-LU", "Error while looking up Species class");
+ sciprintf("Original address was "PREG"\n", PRINT_REG(obj_location));
+ sciprintf("Species address was "PREG"\n", PRINT_REG(obj->variables[SCRIPT_SPECIES_SELECTOR]));
+ return SELECTOR_NONE;
+ }
+
+ index = _obj_locate_varselector(s, obj, selector_id);
+
+ if (index >= 0) {
+ /* Found it as a variable */
+ if (vptr)
+ *vptr = obj->variables + index;
+ return SELECTOR_VARIABLE;
+ } return
+ _lookup_selector_function(s, obj_location.segment, obj, selector_id, fptr);
+
+}
+
+
+/* Detects SCI versions by their different script header */
+void script_detect_versions(state_t *s)
+{
+ int c;
+ resource_t *script = {0};
+
+
+ if (scir_find_resource(s->resmgr, sci_heap, 0, 0))
+ {
+ version_require_later_than(s, SCI_VERSION(1,001,000));
+ return;
+ }
+
+ for (c = 0; c < 1000; c++) {
+ if ((script = scir_find_resource(s->resmgr, sci_script, c, 0))) {
+
+ int id = getInt16(script->data);
+
+ if (id > 15) {
+ version_require_earlier_than(s, SCI_VERSION_FTU_NEW_SCRIPT_HEADER);
+ return;
+ }
+ }
+ }
+}
+
+
+seg_id_t
+script_get_segment(state_t *s, int script_nr, int load)
+{
+ seg_id_t segment;
+
+ if ((load & SCRIPT_GET_LOAD) == SCRIPT_GET_LOAD)
+ script_instantiate(s, script_nr);
+
+ segment = sm_seg_get(&s->seg_manager, script_nr);
+
+ if (segment > 0) {
+ if ((load & SCRIPT_GET_LOCK) == SCRIPT_GET_LOCK)
+ sm_increment_lockers(&s->seg_manager, segment, SEG_ID);
+
+ return segment;
+ } else
+ return 0;
+}
+
+reg_t
+script_lookup_export(state_t *s, int script_nr, int export_index)
+{
+ seg_id_t seg = script_get_segment(s, script_nr, SCRIPT_GET_DONT_LOAD);
+ mem_obj_t *memobj;
+ script_t *script = NULL;
+
+#ifndef DISABLE_VALIDATIONS
+ if (!seg) {
+ CORE_ERROR("EXPORTS", "Script invalid or not loaded");
+ sciprintf("Script was script.03d (0x%x)\n",
+ script_nr, script_nr);
+ return NULL_REG;
+ }
+#endif
+
+ memobj = GET_SEGMENT(s->seg_manager, seg, MEM_OBJ_SCRIPT);
+
+ if (memobj)
+ script = &(memobj->data.script);
+
+#ifndef DISABLE_VALIDATIONS
+ if (script
+ && export_index < script->exports_nr
+ && export_index >= 0)
+#endif
+ return make_reg(seg, getUInt16((byte *)(script->export_table + export_index)));
+#ifndef DISABLE_VALIDATIONS
+ else {
+ CORE_ERROR("EXPORTS", "Export invalid or script missing ");
+ if (!script)
+ sciprintf("(script.%03d missing)\n", script_nr);
+ else
+ sciprintf("(script.%03d: Sought export %d/%d)\n",
+ script_nr, export_index, script->exports_nr);
+ return NULL_REG;
+ }
+#endif
+}
+
+#define INST_LOOKUP_CLASS(id) ((id == 0xffff)? NULL_REG : get_class_address(s, id, SCRIPT_GET_LOCK, reg))
+
+int sm_script_marked_deleted(seg_manager_t* self, int script_nr);
+int sm_initialise_script(mem_obj_t *mem, struct _state *s, int script_nr);
+
+int
+script_instantiate_common(state_t *s, int script_nr, resource_t **script, resource_t **heap, int *was_new)
+{
+ int seg;
+ int seg_id;
+ int marked_for_deletion;
+ mem_obj_t *mem;
+ reg_t reg;
+
+ *was_new = 1;
+
+ *script = scir_find_resource(s->resmgr, sci_script, script_nr, 0);
+ if (s->version >= SCI_VERSION(1,001,000))
+ *heap = scir_find_resource(s->resmgr, sci_heap, script_nr, 0);
+
+ if (!*script || (s->version >= SCI_VERSION(1,001,000) && !heap)) {
+ sciprintf("Script 0x%x requested but not found\n", script_nr);
+ /* script_debug_flag = script_error_flag = 1; */
+ if (s->version >= SCI_VERSION(1,001,000))
+ {
+ if (*heap)
+ sciprintf("Inconsistency: heap resource WAS found\n");
+ else if (*script)
+ sciprintf("Inconsistency: script resource WAS found\n");
+ }
+ return 0;
+ }
+
+ if (NULL == s) {
+ sciprintf("vm.c: script_instantiate(): NULL passed for \"s\"\n");
+ return 0;
+ }
+
+ seg = sm_seg_get ( &s->seg_manager, script_nr );
+ if (sm_script_is_loaded (&s->seg_manager, script_nr, SCRIPT_ID)) {
+ marked_for_deletion = sm_script_marked_deleted(&s->seg_manager, script_nr);
+
+ if (!marked_for_deletion) {
+ sm_increment_lockers( &s->seg_manager, seg, SEG_ID );
+ return seg;
+ }
+ else
+ {
+ seg_id = seg;
+ mem = s->seg_manager.heap[seg];
+ sm_free_script(mem);
+ }
+ }
+ else if (!(mem = sm_allocate_script( &s->seg_manager, s, script_nr, &seg_id ))) { /* ALL YOUR SCRIPT BASE ARE BELONG TO US */
+ sciprintf("Not enough heap space for script size 0x%x of script 0x%x,"
+ " should this happen?`\n",
+ (*script)->size, script_nr);
+ script_debug_flag = script_error_flag = 1;
+ return 0;
+ }
+
+ sm_initialise_script(mem, s, script_nr);
+
+ reg.segment = seg_id;
+ reg.offset = 0;
+
+ /* Set heap position (beyond the size word) */
+ sm_set_lockers( &s->seg_manager, 1, reg.segment, SEG_ID );
+ sm_set_export_table_offset( &s->seg_manager, 0, reg.segment, SEG_ID );
+ sm_set_synonyms_offset( &s->seg_manager, 0, reg.segment, SEG_ID );
+ sm_set_synonyms_nr( &s->seg_manager, 0, reg.segment, SEG_ID );
+
+ *was_new = 0;
+
+ return seg_id;
+}
+
+int
+script_instantiate_sci0(state_t *s, int script_nr)
+{
+ int objtype;
+ unsigned int objlength;
+ reg_t reg, reg_tmp;
+ int seg_id;
+ int relocation = -1;
+ int magic_pos_adder; /* Usually 0; 2 for older SCI versions */
+ resource_t *script;
+ int was_new;
+
+ seg_id = script_instantiate_common(s, script_nr, &script, NULL, &was_new);
+
+ if (was_new) return seg_id;
+
+ reg.segment = seg_id;
+ reg.offset = 0;
+
+ if (s->version < SCI_VERSION_FTU_NEW_SCRIPT_HEADER) {
+ /*
+ int locals_size = getUInt16(script->data)*2;
+ int locals = (locals_size)? script->size : 0;
+ */
+ int locals_nr = getUInt16(script->data);
+
+ /* Old script block */
+ /* There won't be a localvar block in this case */
+ /* Instead, the script starts with a 16 bit int specifying the
+ ** number of locals we need; these are then allocated and zeroed. */
+
+ sm_mcpy_in_out( &s->seg_manager, 0, script->data, script->size, reg.segment, SEG_ID);
+ magic_pos_adder = 2; /* Step over the funny prefix */
+
+ if (locals_nr)
+ sm_script_initialise_locals_zero( &s->seg_manager,
+ reg.segment, locals_nr);
+
+ } else {
+ sm_mcpy_in_out( &s->seg_manager, 0, script->data, script->size, reg.segment, SEG_ID);
+ magic_pos_adder = 0;
+ }
+
+ /* Now do a first pass through the script objects to find the
+ ** export table and local variable block
+ */
+
+ objlength = 0;
+ reg_tmp = reg;
+ reg.offset = magic_pos_adder;
+
+ do {
+ reg_t data_base;
+ reg_t addr;
+ reg.offset += objlength; /* Step over the last checked object */
+ objtype = SEG_GET_HEAP(s, reg);
+ if( !objtype ) break;
+
+ objlength = SEG_GET_HEAP(s, make_reg(reg.segment, reg.offset + 2));
+
+ data_base = reg;
+ data_base.offset += 4;
+
+ addr = data_base;
+
+ switch( objtype ) {
+ case sci_obj_exports: {
+ sm_set_export_table_offset( &s->seg_manager, data_base.offset,
+ reg.segment, SEG_ID );
+ }
+ break;
+
+ case sci_obj_synonyms:
+ sm_set_synonyms_offset( &s->seg_manager, addr.offset, reg.segment, SEG_ID ); /* +4 is to step over the header */
+ sm_set_synonyms_nr( &s->seg_manager, (objlength) / 4, reg.segment, SEG_ID );
+ break;
+
+ case sci_obj_localvars:
+ sm_script_initialise_locals( &s->seg_manager, data_base);
+ break;
+
+ case sci_obj_class: {
+ int classpos = addr.offset - SCRIPT_OBJECT_MAGIC_OFFSET;
+ int species;
+ reg_tmp.offset = addr.offset - SCRIPT_OBJECT_MAGIC_OFFSET;
+ species = OBJ_SPECIES(s, reg_tmp);
+ if (species < 0 || species >= s->classtable_size) {
+ sciprintf("Invalid species %d(0x%x) not in interval "
+ "[0,%d) while instantiating script %d\n",
+ species, species, s->classtable_size,
+ script_nr);
+ script_debug_flag = script_error_flag = 1;
+ return 1;
+ }
+
+ s->classtable[species].script = script_nr;
+ s->classtable[species].reg = addr;
+ s->classtable[species].reg.offset = classpos;
+ /* Set technical class position-- into the block allocated for it */
+
+ }
+ break;
+
+ default:
+ break;
+ }
+ } while (objtype != 0);
+ /* And now a second pass to adjust objects and class pointers, and the general pointers */
+
+ objlength = 0;
+ reg.offset = magic_pos_adder; /* Reset counter */
+
+ do {
+ reg_t addr;
+ reg.offset += objlength; /* Step over the last checked object */
+ objtype = SEG_GET_HEAP(s, reg);
+ if( !objtype ) break;
+ objlength = SEG_GET_HEAP(s, make_reg(reg.segment, reg.offset + 2));
+ reg.offset += 4; /* Step over header */
+
+ addr = reg;
+
+ switch (objtype) {
+ case sci_obj_code:
+ sm_script_add_code_block(&s->seg_manager, addr);
+ break;
+ case sci_obj_object:
+ case sci_obj_class:
+ { /* object or class? */
+ object_t *obj = sm_script_obj_init(&s->seg_manager, s, addr);
+ object_t *base_obj;
+
+ /* Instantiate the superclass, if neccessary */
+ obj->variables[SCRIPT_SPECIES_SELECTOR] =
+ INST_LOOKUP_CLASS(obj->variables[SCRIPT_SPECIES_SELECTOR].offset);
+
+ base_obj = obj_get(s, obj->variables[SCRIPT_SPECIES_SELECTOR]);
+ obj->variable_names_nr = base_obj->variables_nr;
+ obj->base_obj = base_obj->base_obj;
+ /* Copy base from species class, as we need its selector IDs */
+
+ obj->variables[SCRIPT_SUPERCLASS_SELECTOR] =
+ INST_LOOKUP_CLASS(obj->variables[SCRIPT_SUPERCLASS_SELECTOR].offset);
+
+ } /* if object or class */
+ break;
+ case sci_obj_pointers: /* A relocation table */
+ relocation = addr.offset;
+ break;
+
+ default:
+ break;
+ }
+
+ reg.offset -= 4; /* Step back on header */
+
+ } while ((objtype != 0) && (((unsigned)reg.offset) < script->size - 2));
+
+ if (relocation >= 0)
+ sm_script_relocate(&s->seg_manager, make_reg(reg.segment, relocation));
+
+ sm_script_free_unused_objects(&s->seg_manager, reg.segment);
+
+ return reg.segment; /* instantiation successful */
+}
+
+void
+sm_script_relocate_exports_sci11(seg_manager_t *self, int seg);
+void
+sm_script_initialise_objects_sci11(seg_manager_t *self, state_t *s, int seg);
+void
+sm_heap_relocate(seg_manager_t *self, state_t *s, reg_t block);
+
+int
+script_instantiate_sci11(state_t *s, int script_nr)
+{
+ resource_t *script, *heap;
+ int seg_id;
+ int heap_start;
+ reg_t reg;
+ int was_new;
+
+ seg_id = script_instantiate_common(s, script_nr, &script, &heap, &was_new);
+
+ if (was_new) return seg_id;
+
+ heap_start = script->size;
+ if (script->size & 2)
+ heap_start ++;
+
+ sm_mcpy_in_out( &s->seg_manager, 0, script->data, script->size, seg_id, SEG_ID);
+ sm_mcpy_in_out( &s->seg_manager, heap_start, heap->data, heap->size, seg_id, SEG_ID);
+
+ if (getUInt16(script->data+6) > 0)
+ sm_set_export_table_offset(&s->seg_manager, 6,
+ seg_id, SEG_ID);
+
+ reg.segment = seg_id;
+ reg.offset = heap_start+4;
+ sm_script_initialise_locals(&s->seg_manager, reg);
+
+ sm_script_relocate_exports_sci11(&s->seg_manager, seg_id);
+ sm_script_initialise_objects_sci11(&s->seg_manager, s, seg_id);
+
+ reg.offset = getUInt16(heap->data);
+ sm_heap_relocate(&s->seg_manager, s, reg);
+
+ return seg_id;
+}
+
+int
+script_instantiate(state_t *s, int script_nr)
+{
+ if (s->version >= SCI_VERSION(1,001,000))
+ return script_instantiate_sci11(s, script_nr);
+ else
+ return script_instantiate_sci0(s, script_nr);
+}
+
+void sm_mark_script_deleted(seg_manager_t* self, int script_nr);
+
+void
+script_uninstantiate_sci0(state_t *s, int script_nr, seg_id_t seg)
+{
+ reg_t reg = make_reg( seg, (s->version < SCI_VERSION_FTU_NEW_SCRIPT_HEADER)? 2 : 0 );
+ int objtype, objlength;
+
+ /* Make a pass over the object in order uninstantiate all superclasses */
+ objlength = 0;
+
+ do {
+ reg.offset += objlength; /* Step over the last checked object */
+
+ objtype = SEG_GET_HEAP(s, reg);
+ if( !objtype ) break;
+ objlength = SEG_GET_HEAP(s, make_reg(reg.segment, reg.offset + 2)); /* use SEG_UGET_HEAP ?? */
+
+ reg.offset += 4; /* Step over header */
+
+ if ((objtype == sci_obj_object) || (objtype == sci_obj_class)) { /* object or class? */
+ int superclass;
+
+ reg.offset -= SCRIPT_OBJECT_MAGIC_OFFSET;
+
+ superclass = OBJ_SUPERCLASS(s, reg); /* Get superclass... */
+
+ if (superclass >= 0) {
+ int superclass_script = s->classtable[superclass].script;
+
+ if (superclass_script == script_nr) {
+ if( sm_get_lockers( &s->seg_manager, reg.segment, SEG_ID) )
+ sm_decrement_lockers( &s->seg_manager, reg.segment, SEG_ID); /* Decrease lockers if this is us ourselves */
+ } else
+ script_uninstantiate(s, superclass_script);
+ /* Recurse to assure that the superclass lockers number gets decreased */
+ }
+
+ reg.offset += SCRIPT_OBJECT_MAGIC_OFFSET;
+ } /* if object or class */
+
+ reg.offset -= 4; /* Step back on header */
+
+ } while (objtype != 0);
+}
+
+void
+script_uninstantiate(state_t *s, int script_nr)
+{
+ reg_t reg = make_reg( 0, (s->version < SCI_VERSION_FTU_NEW_SCRIPT_HEADER)? 2 : 0 );
+ int i;
+
+ reg.segment = sm_seg_get( &s->seg_manager, script_nr);
+
+ if (!sm_script_is_loaded (&s->seg_manager, script_nr, SCRIPT_ID) || reg.segment <= 0 ) { /* Is it already loaded? */
+ /* sciprintf("Warning: unloading script 0x%x requested although not loaded\n", script_nr); */
+ /* This is perfectly valid SCI behaviour */
+ return;
+ }
+
+ sm_decrement_lockers( &s->seg_manager, reg.segment, SEG_ID); /* One less locker */
+
+ if( sm_get_lockers( &s->seg_manager, reg.segment, SEG_ID) > 0 )
+ return;
+
+ /* Free all classtable references to this script */
+ for (i = 0; i < s->classtable_size; i++)
+ if (s->classtable[i].reg.segment == reg.segment)
+ s->classtable[i].reg = NULL_REG;
+
+ if (s->version < SCI_VERSION(1,001,000))
+ script_uninstantiate_sci0(s, script_nr, reg.segment);
+ else
+ sciprintf("FIXME: Add proper script uninstantiation for SCI 1.1\n");
+
+ if( sm_get_lockers( &s->seg_manager, reg.segment, SEG_ID) )
+ return; /* if xxx.lockers > 0 */
+
+ /* Otherwise unload it completely */
+ /* Explanation: I'm starting to believe that this work is done by SCI itself. */
+ sm_mark_script_deleted( &s->seg_manager, script_nr );
+
+ if (script_checkloads_flag)
+ sciprintf("Unloaded script 0x%x.\n", script_nr);
+
+ return;
+}
+
+
+static void
+_init_stack_base_with_selector(state_t *s, selector_t selector)
+{
+ s->stack_base[0] = make_reg(0, (word) selector);
+ s->stack_base[1] = NULL_REG;
+}
+
+static state_t *
+_game_run(state_t *s, int restoring)
+{
+ state_t *successor = NULL;
+ int game_is_finished = 0;
+ do {
+ s->execution_stack_pos_changed = 0;
+ run_vm(s, (successor || restoring)? 1 : 0);
+ if (s->restarting_flags & SCI_GAME_IS_RESTARTING_NOW) { /* Restart was requested? */
+
+ sci_free(s->execution_stack);
+ s->execution_stack = NULL;
+ s->execution_stack_pos = -1;
+ s->execution_stack_pos_changed = 0;
+
+ game_exit(s);
+ script_free_engine(s);
+ script_init_engine(s, s->version);
+ game_init(s);
+ sfx_reset_player();
+ _init_stack_base_with_selector(s, s->selector_map.play);
+ /* Call the play selector */
+
+ send_selector(s, s->game_obj, s->game_obj,
+ s->stack_base, 2, s->stack_base);
+
+ script_abort_flag = 0;
+ s->restarting_flags = SCI_GAME_WAS_RESTARTED |
+ SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE;
+
+ }
+ else {
+ successor = s->successor;
+ if (successor) {
+ game_exit(s);
+ script_free_vm_memory(s);
+ sci_free(s);
+ s = successor;
+
+ if (!send_calls_allocated)
+ send_calls = (calls_struct_t*)sci_calloc(sizeof(calls_struct_t),
+ send_calls_allocated = 16);
+
+ if (script_abort_flag == SCRIPT_ABORT_WITH_REPLAY) {
+ sciprintf("Restarting with replay()\n");
+ s->execution_stack_pos = -1; /* Resatart with replay */
+
+ _init_stack_base_with_selector(s, s->selector_map.replay);
+ /* Call the replay selector */
+
+ send_selector(s, s->game_obj, s->game_obj,
+ s->stack_base, 2,
+ s->stack_base);
+ }
+
+ script_abort_flag = 0;
+
+ } else
+ game_is_finished = 1;
+ }
+ } while (!game_is_finished);
+
+ return s;
+}
+
+int objinfo(state_t *s, reg_t pos);
+
+int
+game_run(state_t **_s)
+{
+ state_t *s = *_s;
+
+ sciprintf(" Calling %s::play()\n", s->game_name);
+ _init_stack_base_with_selector(s, s->selector_map.play); /* Call the play selector */
+
+
+ /* Now: Register the first element on the execution stack- */
+ if (!send_selector(s, s->game_obj, s->game_obj,
+ s->stack_base, 2,
+ s->stack_base) || script_error_flag) {
+ objinfo(s, s->game_obj);
+ sciprintf("Failed to run the game! Aborting...\n");
+ return 1;
+ }
+ /* and ENGAGE! */
+ *_s = s = _game_run(s, 0);
+
+ sciprintf(" Game::play() finished.\n");
+ return 0;
+}
+
+int
+game_restore(state_t **_s, char *game_name)
+{
+ state_t *s;
+ int debug_state = _debugstate_valid;
+
+ sciprintf("Restoring savegame '%s'...\n", game_name);
+ s = gamestate_restore(*_s, game_name);
+
+ if (!s) {
+ sciprintf("Restoring gamestate '%s' failed.\n", game_name);
+ return 1;
+ }
+ _debugstate_valid = debug_state;
+ script_abort_flag = 0;
+ s->restarting_flags = 0;
+
+ s->execution_stack_pos = -1; /* Resatart with replay */
+
+ _init_stack_base_with_selector(s, s->selector_map.replay);
+ /* Call the replay selector */
+
+ send_selector(s, s->game_obj, s->game_obj,
+ s->stack_base, 2,
+ s->stack_base);
+
+ *_s = s = _game_run(s, 1);
+
+ sciprintf(" Game::play() finished.\n");
+ return 0;
+}
+
+
+object_t *
+obj_get(state_t *s, reg_t offset)
+{
+ mem_obj_t *memobj = GET_OBJECT_SEGMENT(s->seg_manager, offset.segment);
+ object_t *obj = NULL;
+ int idx;
+
+ if (memobj != NULL) {
+ if (memobj->type == MEM_OBJ_CLONES
+ && ENTRY_IS_VALID(&memobj->data.clones, offset.offset))
+ obj = &(memobj->data.clones.table[offset.offset].entry);
+ else if (memobj->type == MEM_OBJ_SCRIPT) {
+ if (offset.offset <= memobj->data.script.buf_size
+ && offset.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET
+ && RAW_IS_OBJECT(memobj->data.script.buf + offset.offset)) {
+ idx = RAW_GET_CLASS_INDEX(&(memobj->data.script), offset);
+ if (idx >= 0 && idx < memobj->data.script.objects_nr)
+ obj = memobj->data.script.objects + idx;
+ }
+ }
+ }
+
+ return obj;
+}
+
+const char *
+obj_get_name(struct _state *s, reg_t pos)
+{
+ object_t *obj = obj_get(s, pos);
+
+ if (!obj)
+ return "<no such object>";
+
+ return
+ (const char*)(obj->base + obj->variables[SCRIPT_NAME_SELECTOR].offset);
+}
+
+
+void
+quit_vm()
+{
+ script_abort_flag = 1; /* Terminate VM */
+ _debugstate_valid = 0;
+ _debug_seeking = 0;
+ _debug_step_running = 0;
+}
+
+