aboutsummaryrefslogtreecommitdiff
path: root/engines/sci
diff options
context:
space:
mode:
authorFilippos Karapetis2009-02-18 22:20:28 +0000
committerFilippos Karapetis2009-02-18 22:20:28 +0000
commit7e5dba8940166dc737e90d7570749813e1c2f802 (patch)
tree0beb50609be569cc29af347b0f3e92876124d2b1 /engines/sci
parent7d73e8ccc4d014a582c72f9877a95d8005fc7c20 (diff)
downloadscummvm-rg350-7e5dba8940166dc737e90d7570749813e1c2f802.tar.gz
scummvm-rg350-7e5dba8940166dc737e90d7570749813e1c2f802.tar.bz2
scummvm-rg350-7e5dba8940166dc737e90d7570749813e1c2f802.zip
Readded the code which reads the version from the original executable to the fallback detector (still very hackish...). This is probably the only known way currently to determine the version used by each game variant and add appropriate game flags
svn-id: r38509
Diffstat (limited to 'engines/sci')
-rw-r--r--engines/sci/detection.cpp31
-rw-r--r--engines/sci/include/versions.h6
-rw-r--r--engines/sci/module.mk3
-rw-r--r--engines/sci/scicore/exe.cpp80
-rw-r--r--engines/sci/scicore/exe.h58
-rw-r--r--engines/sci/scicore/exe_dec.h64
-rw-r--r--engines/sci/scicore/exe_lzexe.cpp328
-rw-r--r--engines/sci/scicore/exe_raw.cpp66
-rw-r--r--engines/sci/scicore/versions.cpp194
9 files changed, 824 insertions, 6 deletions
diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp
index a5bd425cce..861826ffc0 100644
--- a/engines/sci/detection.cpp
+++ b/engines/sci/detection.cpp
@@ -1044,7 +1044,7 @@ static const struct SciGameDescription SciGameDescriptions[] = {
{},
SCI_VERSION(0, 000, 000) // FIXME: add version here
},
-
+#if 0
// Space Quest 1 VGA Remake - English Amiga (from www.back2roots.org)
{{"sq1sci", "VGA Remake", {
{"resource.map", 0, "106484b372af1d4cbf866472cc2813dc", 6396},
@@ -1085,7 +1085,7 @@ static const struct SciGameDescription SciGameDescriptions[] = {
{},
SCI_VERSION(1, 000, 510)
},
-
+#endif
// Space Quest 3 - English Amiga (from www.back2roots.org)
{{"sq3", "", {
{"resource.map", 0, "bad41385acde6d677a8d55a7b20437e3", 5868},
@@ -1357,11 +1357,30 @@ public:
const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fslist) const {
- printf("If this is *NOT* a fan-modified version (in particular, not a fan-made\n");
- printf("translation), please, report the data above, including the following\n");
- printf("version number, from the game's executable: ");
+ int exeVersion = 0;
+
+ // First grab all filenames
+ for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
+ if (file->isDirectory()) continue;
+ Common::String filename = file->getName();
+ filename.toLowercase();
+
+ // FIXME: This is all quite hackish
+ if (filename.contains("scidhuv")) {
+ exeVersion = version_detect_from_executable((char *)file->getPath().c_str());
+ break;
+ }
+ }
+
+ printf("If this is *NOT* a fan-modified version (in particular, not a fan-made\n");
+ printf("translation), please, report the data above, including the following\n");
+ printf("version number, from the game's executable:\n");
- // TODO
+ printf("Interpreter version: %d.%03d.%03d (by executable scan)\n",
+ SCI_VERSION_MAJOR(exeVersion),
+ SCI_VERSION_MINOR(exeVersion),
+ SCI_VERSION_PATCHLEVEL(exeVersion));
+
return 0;
}
diff --git a/engines/sci/include/versions.h b/engines/sci/include/versions.h
index c18840598a..15c7e332e9 100644
--- a/engines/sci/include/versions.h
+++ b/engines/sci/include/versions.h
@@ -147,4 +147,10 @@ version_parse(const char *vn, sci_version_t *result);
** (sci_version_t) *result: The resulting version number on success
*/
+int
+version_detect_from_executable(char *filename);
+/* Try to detect version from Sierra executable in cwd
+** Returns : (int) The version number detected, or 0 if we weren't successful
+*/
+
#endif /* !_SCI_VERSIONS_H_ */
diff --git a/engines/sci/module.mk b/engines/sci/module.mk
index fbb4508e7c..9cb5241b3d 100644
--- a/engines/sci/module.mk
+++ b/engines/sci/module.mk
@@ -55,6 +55,9 @@ MODULE_OBJS = \
scicore/decompress01.o \
scicore/decompress1.o \
scicore/decompress11.o \
+ scicore/exe.o \
+ scicore/exe_lzexe.o \
+ scicore/exe_raw.o \
scicore/resource.o \
scicore/resource_map.o \
scicore/resource_patch.o \
diff --git a/engines/sci/scicore/exe.cpp b/engines/sci/scicore/exe.cpp
new file mode 100644
index 0000000000..518818ff33
--- /dev/null
+++ b/engines/sci/scicore/exe.cpp
@@ -0,0 +1,80 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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
+ * of the License, 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "sci/include/sci_memory.h"
+
+#include "sci/scicore/exe.h"
+#include "sci/scicore/exe_dec.h"
+
+extern exe_decompressor_t exe_decompressor_lzexe;
+extern exe_decompressor_t exe_decompressor_raw;
+
+exe_decompressor_t *exe_decompressors[] = {
+ &exe_decompressor_lzexe,
+ &exe_decompressor_raw,
+ NULL
+};
+
+struct _exe_file {
+ struct _exe_decompressor *decompressor;
+ struct _exe_handle *handle;
+};
+
+exe_file_t *
+exe_open(const char *filename) {
+ int i = 0;
+ exe_decompressor_t *dec;
+
+ while ((dec = exe_decompressors[i])) {
+ exe_handle_t *handle = dec->open(filename);
+
+ if (handle) {
+ exe_file_t *file = (exe_file_t*)sci_malloc(sizeof(exe_file_t));
+
+ sciprintf("Scanning '%s' with decompressor '%s'\n",
+ filename, dec->name);
+
+ file->handle = handle;
+ file->decompressor = dec;
+ return file;
+ }
+
+ i++;
+ }
+
+ return NULL;
+}
+
+int
+exe_read(exe_file_t *file, void *buf, int count) {
+ return file->decompressor->read(file->handle, buf, count);
+}
+
+void
+exe_close(exe_file_t *file) {
+ file->decompressor->close(file->handle);
+
+ free(file);
+}
diff --git a/engines/sci/scicore/exe.h b/engines/sci/scicore/exe.h
new file mode 100644
index 0000000000..db40d3689c
--- /dev/null
+++ b/engines/sci/scicore/exe.h
@@ -0,0 +1,58 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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
+ * of the License, 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef _SCI_EXE_H_
+#define _SCI_EXE_H_
+
+typedef struct _exe_file exe_file_t;
+
+exe_file_t *
+exe_open(const char *filename);
+/* Opens an executable file
+** Parameters: (const char *) filename: Filename of executable to open
+** Returns : (exe_file_t *) File handle, or NULL on error
+** This function will try to find a decompressor that can handle this type
+** of executable
+*/
+
+int
+exe_read(exe_file_t *file, void *buf, int count);
+/* Reads from an executable file
+** Parameters: (exe_file_t *) file: File handle
+** (void *) buf: Buffer to store decompressed data
+** (int) count: Size of decompressed data requested, in bytes
+** Returns : (int) Number of bytes of decompressed data that was stored in
+** buf. If this value is less than count an error has
+** occured, or end-of-file was reached.
+*/
+
+void
+exe_close(exe_file_t *handle);
+/* Closes an executable file
+** Parameters: (exe_file_t *) file: File handle
+** Returns : (void)
+*/
+
+#endif /* !_SCI_EXE_H_ */
diff --git a/engines/sci/scicore/exe_dec.h b/engines/sci/scicore/exe_dec.h
new file mode 100644
index 0000000000..45c1f58382
--- /dev/null
+++ b/engines/sci/scicore/exe_dec.h
@@ -0,0 +1,64 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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
+ * of the License, 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef _SCI_EXE_DEC_H_
+#define _SCI_EXE_DEC_H_
+
+typedef struct _exe_handle exe_handle_t;
+
+typedef struct _exe_decompressor {
+ const char *name; /* Decompressor name. Unique identifier, should consist
+ ** of lower-case (where applicable) alphanumerics
+ */
+
+ exe_handle_t * (*open)(const char *filename);
+ /* Opens an executable file
+ ** Parameters: (const char *) filename: Filename of executable to open.
+ ** Returns : (exe_handle_t *) Decompressor file handle, or NULL on
+ ** error.
+ ** This function will verify that the file can be handled by the
+ ** decompressor. If this is not the case the function will fail.
+ */
+
+ int (*read)(exe_handle_t *handle, void *buf, int count);
+ /* Reads from executable file
+ ** Parameters: (exe_handle_t *) handle: Decompressor file handle.
+ ** (void *) buf: Buffer to store decompressed data.
+ ** (int) count: Size of decompressed data requested, in
+ ** bytes.
+ ** Returns : (int) Number of bytes of decompressed data that was
+ ** stored in buf. If this value is less than count
+ ** an error has occured, or end-of-file was
+ ** reached.
+ */
+
+ void (*close)(exe_handle_t *handle);
+ /* Closes a decompressor file handle.
+ ** Parameters: (exe_handle_t *) handle: Decompressor file handle.
+ ** Returns : (void)
+ */
+} exe_decompressor_t;
+
+#endif /* !_SCI_EXE_DEC_H_ */
diff --git a/engines/sci/scicore/exe_lzexe.cpp b/engines/sci/scicore/exe_lzexe.cpp
new file mode 100644
index 0000000000..a8876b3bc5
--- /dev/null
+++ b/engines/sci/scicore/exe_lzexe.cpp
@@ -0,0 +1,328 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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
+ * of the License, 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/* Based on public domain code by Mitugu Kurizono. */
+
+#include "sci/include/sci_memory.h"
+#include "sci/scicore/exe_dec.h"
+
+/* Macro to interpret two sequential bytes as an unsigned integer. */
+#define UINT16(A) ((*((A) + 1) << 8) + *(A))
+
+/* The amount of most recent data (in bytes) that we need to keep in the
+** buffer. lzexe compression is based on copying chunks of previous data to
+** form new data.
+*/
+#define LZEXE_WINDOW 8192
+
+/* Buffer size. */
+#define LZEXE_BUFFER_SIZE (LZEXE_WINDOW + 4096)
+
+/* Maximum amount of data (in bytes) that can be in the buffer at the start
+** of the decompression loop. The maximum amount of data that can be added
+** to the buffer during a single step of the loop is 256 bytes.
+*/
+#define LZEXE_BUFFER_MAX (LZEXE_BUFFER_SIZE - 256)
+
+struct _exe_handle {
+ FILE *f;
+
+ /* Output buffer. */
+ guint8 buffer[LZEXE_BUFFER_SIZE];
+ guint8 *bufptr;
+
+ /* Bit buffer. Bits [0..count) still contain unprocessed data. */
+ int buf;
+ int count;
+
+ /* End of data flag. */
+ int eod;
+};
+
+static int
+lzexe_read_uint16(FILE *f, int *value) {
+ int data;
+
+ if ((*value = fgetc(f)) == EOF)
+ return 0;
+
+ if ((data = fgetc(f)) == EOF)
+ return 0;
+
+ *value |= data << 8;
+ return 1;
+}
+
+static int
+lzexe_read_uint8(FILE *f, int *value) {
+ if ((*value = fgetc(f)) == EOF)
+ return 0;
+
+ return 1;
+}
+
+static int
+lzexe_init(exe_handle_t *handle, FILE *f) {
+ handle->f = f;
+ handle->bufptr = handle->buffer;
+ handle->eod = 0;
+
+ if (!lzexe_read_uint16(handle->f, &handle->buf))
+ return 0;
+
+ handle->count = 16;
+ return 1;
+}
+
+static int
+lzexe_get_bit(exe_handle_t *handle, int *bit) {
+ *bit = handle->buf & 1;
+
+ if (--handle->count == 0) {
+ if (!lzexe_read_uint16(handle->f, &handle->buf))
+ return 0;
+ handle->count = 16;
+ } else
+ handle->buf >>= 1;
+
+ return 1;
+}
+
+static int
+lzexe_decompress(exe_handle_t *handle) {
+ while (!handle->eod
+ && handle->bufptr - handle->buffer <= LZEXE_BUFFER_MAX) {
+ int bit;
+ int len, span;
+
+ if (!lzexe_get_bit(handle, &bit))
+ return 0;
+
+ if (bit) {
+ /* 1: copy byte verbatim. */
+
+ int data;
+
+ if (!lzexe_read_uint8(handle->f, &data))
+ return 0;
+
+ *handle->bufptr++ = data;
+
+ continue;
+ }
+
+ if (!lzexe_get_bit(handle, &bit))
+ return 0;
+
+ if (!bit) {
+ /* 00: copy small block. */
+
+ /* Next two bits indicate block length - 2. */
+ if (!lzexe_get_bit(handle, &bit))
+ return 0;
+
+ len = bit << 1;
+
+ if (!lzexe_get_bit(handle, &bit))
+ return 0;
+
+ len |= bit;
+ len += 2;
+
+ /* Read span byte. This forms the low byte of a
+ ** negative two's compliment value.
+ */
+ if (!lzexe_read_uint8(handle->f, &span))
+ return 0;
+
+ /* Convert to negative integer. */
+ span -= 256;
+ } else {
+ /* 01: copy large block. */
+ int data;
+
+ /* Read low byte of span value. */
+ if (!lzexe_read_uint8(handle->f, &span))
+ return 0;
+
+ /* Read next byte. Bits [7..3] contain bits [12..8]
+ ** of span value. Bits [2..0] contain block length -
+ ** 2.
+ */
+ if (!lzexe_read_uint8(handle->f, &data))
+ return 0;
+ span |= (data & 0xf8) << 5;
+ /* Convert to negative integer. */
+ span -= 8192;
+
+ len = (data & 7) + 2;
+
+ if (len == 2) {
+ /* Next byte is block length value - 1. */
+ if (!lzexe_read_uint8(handle->f, &len))
+ return 0;
+
+ if (len == 0) {
+ /* End of data reached. */
+ handle->eod = 1;
+ break;
+ }
+
+ if (len == 1)
+ /* Segment change marker. */
+ continue;
+
+ len++;
+ }
+ }
+
+ assert(handle->bufptr + span >= handle->buffer);
+
+ /* Copy block. */
+ while (len-- > 0) {
+ *handle->bufptr = *(handle->bufptr + span);
+ handle->bufptr++;
+ }
+ }
+
+ return 1;
+}
+
+static exe_handle_t *
+lzexe_open(const char *filename) {
+ exe_handle_t *handle;
+ guint8 head[0x20];
+ guint8 size[2];
+ off_t fpos;
+
+ FILE *f = sci_fopen(filename, "rb");
+
+ if (!f)
+ return NULL;
+
+ /* Read exe header plus possible lzexe signature. */
+ if (fread(head, 1, 0x20, f) != 0x20)
+ return NULL;
+
+ /* Verify "MZ" signature, header size == 2 paragraphs and number of
+ ** overlays == 0.
+ */
+ if (UINT16(head) != 0x5a4d || UINT16(head + 8) != 2
+ || UINT16(head + 0x1a) != 0)
+ return NULL;
+
+ /* Verify that first relocation item offset is 0x1c. */
+ if (UINT16(head + 0x18) != 0x1c)
+ return NULL;
+
+ /* Look for lzexe signature. */
+ if (memcmp(head + 0x1c, "LZ09", 4)
+ && memcmp(head + 0x1c, "LZ91", 4)) {
+ return NULL;
+ }
+
+ /* Calculate code segment offset in exe file. */
+ fpos = (UINT16(head + 0x16) + UINT16(head + 8)) << 4;
+ /* Seek to offset 8 of info table at start of code segment. */
+ if (fseek(f, fpos + 8, SEEK_SET) == -1)
+ return NULL;
+
+ /* Read size of compressed data in paragraphs. */
+ if (fread(size, 1, 2, f) != 2)
+ return NULL;
+
+ /* Move file pointer to start of compressed data. */
+ fpos -= UINT16(size) << 4;
+ if (fseek(f, fpos, SEEK_SET) == -1)
+ return NULL;
+
+ handle = (exe_handle_t*)sci_malloc(sizeof(exe_handle_t));
+
+ if (!lzexe_init(handle, f)) {
+ free(handle);
+ return NULL;
+ }
+
+ return handle;
+}
+
+static int
+lzexe_read(exe_handle_t *handle, void *buf, int count) {
+ int done = 0;
+
+ while (done != count) {
+ int size, copy, i;
+ int left = count - done;
+
+ if (!lzexe_decompress(handle))
+ return done;
+
+ /* Total amount of bytes in buffer. */
+ size = handle->bufptr - handle->buffer;
+
+ /* If we're not at end of data we need to maintain the
+ ** window.
+ */
+ if (!handle->eod)
+ copy = size - LZEXE_WINDOW;
+ else {
+ if (size == 0)
+ /* No data left. */
+ return done;
+
+ copy = size;
+ }
+
+ /* Do not copy more than requested. */
+ if (copy > left)
+ copy = left;
+
+ memcpy((char *) buf + done, handle->buffer, copy);
+
+ /* Move remaining data to start of buffer. */
+ for (i = copy; i < size; i++)
+ handle->buffer[i - copy] = handle->buffer[i];
+
+ handle->bufptr -= copy;
+ done += copy;
+ }
+
+ return done;
+}
+
+static void
+lzexe_close(exe_handle_t *handle) {
+ fclose(handle->f);
+
+ free(handle);
+}
+
+exe_decompressor_t
+exe_decompressor_lzexe = {
+ "lzexe",
+ lzexe_open,
+ lzexe_read,
+ lzexe_close
+};
diff --git a/engines/sci/scicore/exe_raw.cpp b/engines/sci/scicore/exe_raw.cpp
new file mode 100644
index 0000000000..17d46f524f
--- /dev/null
+++ b/engines/sci/scicore/exe_raw.cpp
@@ -0,0 +1,66 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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
+ * of the License, 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "sci/include/sci_memory.h"
+
+struct _exe_handle {
+ FILE *f;
+};
+
+#include "sci/scicore/exe_dec.h"
+
+static exe_handle_t *
+raw_open(const char *filename) {
+ FILE *f = sci_fopen(filename, "rb");
+ exe_handle_t *handle;
+
+ if (!f)
+ return NULL;
+
+ handle = (exe_handle_t*)sci_malloc(sizeof(exe_handle_t));
+ handle->f = f;
+
+ return handle;
+}
+
+static int
+raw_read(exe_handle_t *handle, void *buf, int count) {
+ return fread(buf, 1, count, handle->f);
+}
+
+static void
+raw_close(exe_handle_t *handle) {
+ fclose(handle->f);
+
+ free(handle);
+}
+
+exe_decompressor_t
+exe_decompressor_raw = {
+ "raw",
+ raw_open,
+ raw_read,
+ raw_close
+};
diff --git a/engines/sci/scicore/versions.cpp b/engines/sci/scicore/versions.cpp
index 37952ed217..83ad3ddaa5 100644
--- a/engines/sci/scicore/versions.cpp
+++ b/engines/sci/scicore/versions.cpp
@@ -24,9 +24,14 @@
*/
#define NEED_SCI_VERSIONS
+
+#include "common/system.h"
+#include "common/config-manager.h"
+
#include "sci/include/versions.h"
#include "sci/include/engine.h"
#include "sci/include/resource.h"
+#include "sci/scicore/exe.h" // for reading version from the executable
void
version_require_earlier_than(state_t *s, sci_version_t version) {
@@ -83,4 +88,193 @@ version_parse(const char *vn, sci_version_t *result) {
return 0;
}
+// Exe scanning functions
+
+/* Maxmimum number of bytes to hash from start of file */
+#define VERSION_DETECT_HASH_SIZE 1000000
+
+#define VERSION_DETECT_BUF_SIZE 4096
+
+static int
+scan_file(char *filename, sci_version_t *version) {
+ char buf[VERSION_DETECT_BUF_SIZE];
+ char result_string[10]; /* string-encoded result, copied from buf */
+ int characters_left;
+ int state = 0;
+ /* 'state' encodes how far we have matched the version pattern
+ ** "n.nnn.nnn"
+ **
+ ** n.nnn.nnn
+ ** 0123456789
+ **
+ ** Since we cannot be certain that the pattern does not begin with an
+ ** alphanumeric character, some states are ambiguous.
+ ** The pattern is expected to be terminated with a non-alphanumeric
+ ** character.
+ */
+
+ exe_file_t *f = exe_open(filename);
+
+ if (!f)
+ return 1;
+
+ do {
+ int i;
+ int accept;
+
+ characters_left = exe_read(f, buf, VERSION_DETECT_BUF_SIZE);
+
+ for (i = 0; i < characters_left; i++) {
+ const char ch = buf[i];
+ accept = 0; /* By default, we don't like this character */
+
+ if (isalnum((unsigned char) ch)) {
+ accept = (state != 1
+ && state != 5
+ && state != 9);
+ } else if (ch == '.') {
+ accept = (state == 1
+ || state == 5);
+ } else if (state == 9) {
+ result_string[9] = 0; /* terminate string */
+
+ if (!version_parse(result_string, version)) {
+ exe_close(f);
+ return 0; /* success! */
+ }
+
+ /* Continue searching. */
+ }
+
+ if (accept)
+ result_string[state++] = ch;
+ else
+ state = 0;
+
+ }
+
+ } while (characters_left == VERSION_DETECT_BUF_SIZE);
+
+ exe_close(f);
+ return 1; /* failure */
+}
+
+static guint32
+read_uint32(byte *data) {
+ return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
+}
+
+static guint16
+read_uint16(byte *data) {
+ return (data[0] << 8) | data[1];
+}
+
+static int
+is_mac_exe(char *filename) {
+ FILE *file;
+ byte buf[4];
+ guint32 val;
+ unsigned int i;
+
+ /* Mac executables have no extension */
+ if (strchr(filename, '.'))
+ return 0;
+
+ file = fopen(filename, "rb");
+ if (!file)
+ return 0;
+
+ if (fseek(file, 4, SEEK_SET) == -1) {
+ fclose(file);
+ return 0;
+ }
+
+ /* Read resource map offset */
+ if (fread(buf, 1, 4, file) < 4) {
+ fclose(file);
+ return 0;
+ }
+
+ val = read_uint32(buf);
+
+ if (fseek(file, val + 28, SEEK_SET) == -1) {
+ fclose(file);
+ return 0;
+ }
+
+ /* Read number of types in map */
+ if (fread(buf, 1, 2, file) < 2) {
+ fclose(file);
+ return 0;
+ }
+
+ val = read_uint16(buf) + 1;
+
+ for (i = 0; i < val; i++) {
+ if (fread(buf, 1, 4, file) < 4) {
+ fclose(file);
+ return 0;
+ }
+
+ /* Look for executable code */
+ if (!memcmp(buf, "CODE", 4)) {
+ fclose(file);
+ return 1;
+ }
+
+ /* Skip to next list entry */
+ if (fseek(file, 4, SEEK_CUR) == -1) {
+ fclose(file);
+ return 0;
+ }
+ }
+
+ fclose(file);
+ return 0;
+}
+
+static int
+is_exe(char *filename) {
+ FILE *file;
+ char buf[4];
+ unsigned char header[] = {0x00, 0x00, 0x03, 0xf3};
+
+ /* PC and Atari ST executable extensions */
+ if (strstr(filename, ".exe") || strstr(filename, ".EXE")
+ || strstr(filename, ".prg") || strstr(filename, ".PRG"))
+ return 1;
+
+ /* Check for Amiga executable */
+ if (strchr(filename, '.'))
+ return 0;
+
+ file = fopen(filename, "rb");
+ if (!file)
+ return 0;
+
+ if (fread(buf, 1, 4, file) < 4) {
+ fclose(file);
+ return 0;
+ }
+
+ fclose(file);
+
+ /* Check header bytes */
+ return memcmp(buf, header, 4) == 0;
+}
+
+int
+version_detect_from_executable(char *filename) {
+ int mac = 0;
+ int result;
+
+ if (mac ? is_mac_exe(filename) : is_exe(filename)) {
+ if (scan_file(filename, &result)) {
+ return result;
+ }
+ }
+
+ return 0;
+}
+
#undef VERSION_DETECT_BUF_SIZE