diff options
Diffstat (limited to 'engines/sci')
-rw-r--r-- | engines/sci/detection.cpp | 31 | ||||
-rw-r--r-- | engines/sci/include/versions.h | 6 | ||||
-rw-r--r-- | engines/sci/module.mk | 3 | ||||
-rw-r--r-- | engines/sci/scicore/exe.cpp | 80 | ||||
-rw-r--r-- | engines/sci/scicore/exe.h | 58 | ||||
-rw-r--r-- | engines/sci/scicore/exe_dec.h | 64 | ||||
-rw-r--r-- | engines/sci/scicore/exe_lzexe.cpp | 328 | ||||
-rw-r--r-- | engines/sci/scicore/exe_raw.cpp | 66 | ||||
-rw-r--r-- | engines/sci/scicore/versions.cpp | 194 |
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 |