Moved game version checking to ScummVM's detector and removed the FreeSCI version checking routines

svn-id: r38458
This commit is contained in:
Filippos Karapetis 2009-02-18 09:09:37 +00:00
parent f51990a3de
commit 056a13f710
6 changed files with 328 additions and 599 deletions

File diff suppressed because it is too large Load Diff

View File

@ -147,20 +147,4 @@ version_parse(const char *vn, sci_version_t *result);
** (sci_version_t) *result: The resulting version number on success
*/
int
version_detect_from_executable(sci_version_t *result);
/* Try to detect version from Sierra executable in cwd
** Returns : (int) 0 on success, 1 on failure
** (sci_version_t) *result: The version number detected on success
*/
const char *
version_guess_from_hashcode(sci_version_t *result, int *res_version, guint32 *code);
/* Try to detect version from Sierra resource file(s) in cwd
** Returns : (const char *) NULL on failure, the name of the associated game otherwise
** (sci_version_t) *result: The version number detected on success
** (int) *res_version: The resource version number detected on success
** (guint32) *code: The resource hash code
*/
#endif /* !_SCI_VERSIONS_H_ */

View File

@ -128,86 +128,6 @@ init_gamestate(state_t *gamestate, sci_version_t version) {
return 0;
}
static void
detect_versions(sci_version_t *version, int *res_version) {
sci_version_t exe_version;
sci_version_t hash_version;
int hash_res_version;
guint32 code;
int got_exe_version;
const char *game_name;
sciprintf("Detecting interpreter and resource versions...\n");
got_exe_version = !version_detect_from_executable(&exe_version);
if (got_exe_version) {
sciprintf("Interpreter version: %d.%03d.%03d (by executable scan)\n",
SCI_VERSION_MAJOR(exe_version),
SCI_VERSION_MINOR(exe_version),
SCI_VERSION_PATCHLEVEL(exe_version));
if (SCI_VERSION_MAJOR(exe_version) >= 1) {
sciprintf("FIXME: Implement version mapping (results of executable scan ignored)\n");
got_exe_version = 0;
}
}
game_name = version_guess_from_hashcode(&hash_version, &hash_res_version, &code);
if (game_name) {
sciprintf("Interpreter version: %d.%03d.%03d (by hash code %08X)\n",
SCI_VERSION_MAJOR(hash_version),
SCI_VERSION_MINOR(hash_version),
SCI_VERSION_PATCHLEVEL(hash_version), code);
if (got_exe_version && exe_version != hash_version)
sciprintf("UNEXPECTED INCONSISTENCY: Hash code %08X indicates interpreter version\n"
" %d.%03d.%03d, but analysis of the executable yields %d.%03d.%03d (for game\n"
" '%s'). Please report this!\n",
code,
SCI_VERSION_MAJOR(hash_version),
SCI_VERSION_MINOR(hash_version),
SCI_VERSION_PATCHLEVEL(hash_version),
SCI_VERSION_MAJOR(exe_version),
SCI_VERSION_MINOR(exe_version),
SCI_VERSION_PATCHLEVEL(exe_version), game_name);
if (hash_res_version != SCI_VERSION_AUTODETECT)
sciprintf("Resource version: %d (by hash code)\n", hash_res_version);
sciprintf("Game identified as '%s'\n", game_name);
} else {
sciprintf("Could not identify game by hash code: %08X\n", code);
if (got_exe_version)
sciprintf("Please report the preceding two lines and the name of the game you were trying\n"
"to run to the FreeSCI development team to help other users!\n",
code);
}
if (game_name)
*version = hash_version;
else if (got_exe_version)
*version = exe_version;
else
*version = 0;
if (game_name)
*res_version = hash_res_version;
else
*res_version = SCI_VERSION_AUTODETECT;
if (*version)
sciprintf("Using interpreter version %d.%03d.%03d\n",
SCI_VERSION_MAJOR(*version),
SCI_VERSION_MINOR(*version),
SCI_VERSION_PATCHLEVEL(*version));
if (*res_version != SCI_VERSION_AUTODETECT)
sciprintf("Using resource version %d\n", *res_version);
}
SciEngine::SciEngine(OSystem *syst, const SciGameDescription *desc)
: Engine(syst) {
// Put your engine in a sane state, but do nothing big yet;
@ -221,6 +141,7 @@ SciEngine::SciEngine(OSystem *syst, const SciGameDescription *desc)
// Set up the engine specific debug levels
//Common::addSpecialDebugLevel(SCI_DEBUG_RESOURCES, "resources", "Debug the resources loading");
_version = desc->version;
printf("SciEngine::SciEngine\n");
}
@ -269,12 +190,12 @@ Common::Error SciEngine::go() {
script_debug_flag = 0;
sci_version_t version;
int res_version;
int res_version = SCI_VERSION_AUTODETECT;
// FIXME. An evil hack until File class will be used properly
chdir(ConfMan.get("path").c_str());
detect_versions(&version, &res_version);
version = getVersion();
char resource_dir[MAXPATHLEN+1] = "";
getcwd(resource_dir, MAXPATHLEN); /* Store resource directory */

View File

@ -49,6 +49,7 @@ struct GameFlags {
struct SciGameDescription {
ADGameDescription desc;
GameFlags flags;
int version;
};
//class Console;
@ -60,8 +61,9 @@ public:
virtual Common::Error init(void);
virtual Common::Error go(void);
int getVersion() { return _version; }
private:
int _version;
//Console *_console;
};

View File

@ -1,125 +0,0 @@
/* 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$
*
*/
/* Game identification */
#ifndef _SCI_GAMES_H_
#define _SCI_GAMES_H_
#ifndef NEED_SCI_VERSIONS
# error "You shouldn't be including this header file."
#endif
#include "sci/include/versions.h"
typedef struct _sci_game {
int id; /* currently CRC of resource.001 */
int res_version;
sci_version_t version;
const char *name;
} sci_game_t;
/* Interpreter versions for Amiga and Atari ST ports are tentative */
sci_game_t sci_games[] = {
{ 0x5D451535, SCI_VERSION_AUTODETECT, SCI_VERSION(1, 000, 510), "Leisure Suit Larry 1 v1.0-mac"}, /* x.yyy.zzz */ /* Launcher says v2.0, game crashes on DoAvoider */
{ 0x6C176EE0, SCI_VERSION_AUTODETECT, SCI_VERSION(1, 000, 577), "Leisure Suit Larry 1 v2.1"},
{ 0x1C36E076, SCI_VERSION_AUTODETECT, SCI_VERSION(1, 000, 510), "Leisure Suit Larry 1 v1.000-es"}, /* 1.SQ4.057 */ /* Crashes on function 0x7b */
{ 0xFEAB629D, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 343), "Leisure Suit Larry 2 v1.000.011-3.5"},
{ 0x13DD3CD2, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 343), "Leisure Suit Larry 2 v1.000.011-5.25" },
{ 0x1D0F3B31, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 572), "Leisure Suit Larry 2 v1.001.006-st"}, /* 1.000.159 */
{ 0x40BEC726, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 409), "Leisure Suit Larry 2 v1.002.000-3.5"},
{ 0x0C848403, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 409), "Leisure Suit Larry 2 v1.002.000-5.25" },
{ 0x7E9CF339, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 572), "Leisure Suit Larry 2 v1.003-ami"}, /* x.yyy.zzz */
{ 0x06D737B5, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 572), "Leisure Suit Larry 3 v1.003-3.5" },
{ 0xE0A1C352, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 572), "Leisure Suit Larry 3 v1.003-5.25" },
{ 0xC48FE83A, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 572), "Leisure Suit Larry 3 v1.021-3.5" },
{ 0x484587DD, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 572), "Leisure Suit Larry 3 v1.021-5.25"},
/* { 0x????????, SCI_VERSION_AUTODETECT, SCI_VERSION(0,000,572), "Leisure Suit Larry 3 v1.021-st"},*/ /* 1.002.026 */
{ 0x6348030A, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 572), "Leisure Suit Larry 3 v1.039-ami"}, /* 1.002.032 */
{ 0x94EA377B, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 685), "CB1" },
{ 0xFD9EE7BD, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 685), "Camelot" },
{ 0x2829987F, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 685), "Camelot" },
{ 0x980CEAD3, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 629), "Demo Quest" },
{ 0x3DB972CA, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 572), "Hoyle 2" },
{ 0xC0B37651, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 685), "Iceman" },
{ 0xDABA6B8A, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 999), "KQ1 v1.000.051-3.5" }, /* S.old.010 */
{ 0x270E37F3, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 274), "KQ4" },
{ 0x685F1205, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 502), "KQ4" },
{ 0xC14E3A2A, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 395), "PQ2" },
{ 0x4BD66036, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 490), "PQ2" },
{ 0x7132D6D8, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 629), "QfG1" },
{ 0xF8F4913F, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 685), "SQ3" },
{ 0x34FBC324, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 999), "SQ3/DE" }, /* S.old.114 */
{ 0xE4A3234D, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 506), "Fun Seekers Guide v1.02"},
{ 0x85AFE241, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 519), "Hoyle 1 v1.000.104"},
{ 0xE0E070C3, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 572), "Hoyle 2 v1.000.011"},
{ 0xD0B8794E, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 668), "Iceman v1.023"},
{ 0x94EA377B, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 631), "The Colonel's Bequest v1.000.046"},
{ 0x28543FDF, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 453), "Astro Chicken"},
{ 0x31F46F7D, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 453), "Space Quest III v1.0V int"},
{ 0xAA2C94B9, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 685), "Mixed-Up Mother Goose v1.011 Int.#8.2.90"},
{ 0x3B15678B, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 631), "The Colonel's Bequest v1.000.046-3.5"},
{ 0x0E042F46, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 530), "Hoyle 1 v1.000.113-3.5"},
{ 0x1EACB959, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 566), "HQ v1.000-5.25"},
{ 0x2BEAF5E7, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 566), "HQ v1.001-5.25"},
{ 0x63626D3E, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 668), "Iceman v1.023-5.25"},
{ 0xDA5E7B7D, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 409), "KQ4 v1.003.006-3.5"},
{ 0x376A5472, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 502), "KQ4 v1.006.003-5.25"},
{ 0x364B40B2, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 395), "PQ2 v1.001.000-5.25"},
{ 0x664B4123, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 409), "PQ2 v1.001.006-3.5"},
{ 0x379F4582, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 453), "SQ3 v1.0V-5.25"},
{ 0x04B0B081, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 294), "xmascard v1.04"},
{ 0x4447B28D, SCI_VERSION_AUTODETECT, SCI_VERSION(1, 000, 72), "Trial v1.105"},
{ 0xB1C2CCAE, SCI_VERSION_AUTODETECT, SCI_VERSION(1, 000, 200), "SQ4 v1.052"}, /* 1.000.753 */
{ 0xAA6AF6A9, SCI_VERSION_AUTODETECT, SCI_VERSION(1, 000, 60), "KQ5 v0.000.062"},
{ 0x092C2C0D, 3, SCI_VERSION(1, 000, 172), "jones v1.000.060"}, /* x.yyy.zzz */
{ 0xC415A485, SCI_VERSION_AUTODETECT, SCI_VERSION(1, 000, 172), "jones v1.000.060-cd"}, /* x.yyy.zzz */
{ 0x89C595E3, SCI_VERSION_AUTODETECT, SCI_VERSION(1, 000, 510), "SQ1 v2.000"}, /* T.A00.081 */
{ 0x09D4FC54, SCI_VERSION_AUTODETECT, SCI_VERSION(1, 000, 510), "LSL5 v1.000"}, /* T.A00.169 */
{ 0xF3ED1D81, SCI_VERSION_AUTODETECT, SCI_VERSION(1, 000, 510), "PQ3 v1.00"}, /* T.A00.178 */
{ 0x501B5E6B, SCI_VERSION_AUTODETECT, SCI_VERSION(1, 000, 510), "Brain v1.000"}, /* 1.000.044 */
{ 0xB1B7279B, SCI_VERSION_AUTODETECT, SCI_VERSION(1, 000, 510), "Longbow v1.000"}, /* 1.000.168 */
{ 0x82595EBE, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 453), "SQ3 v1.0V-ami"}, /* x.yyy.zzz */
{ 0xF6080B61, SCI_VERSION_AUTODETECT, SCI_VERSION(0, 000, 530), "Hoyle 1 v1.000.139-ami"}, /* x.yyy.zzz */
{ 0x8AFEA2D0, 2, SCI_VERSION(1, 000, 000), "KQ1 v1.000.054-ami"}, /* 1.003.007 */
/* Undetermined Amiga versions: */
/* { 0x8AE5F854, ?, SCI_VERSION(), "ARTHUR" }, */
/* { 0x9FB7015B, ?, SCI_VERSION(), "CB1" }, */
/* { 0x560CEDD5, ?, SCI_VERSION(), "iceMan" }, */
{ 0, 0, 0, NULL } /* terminator */
};
#endif /* _SCI_GAMES_H_ */

View File

@ -27,182 +27,8 @@
#include "sci/include/versions.h"
#include "sci/include/engine.h"
#include "sci/include/resource.h"
#include "sci/scicore/games.h"
#include "sci/scicore/exe.h"
/* Maxmimum number of bytes to hash from start of file */
#define VERSION_DETECT_HASH_SIZE 1000000
#define VERSION_DETECT_BUF_SIZE 4096
static int
scan_file(char *filename, sci_version_t *version) {
char buf[VERSION_DETECT_BUF_SIZE];
char result_string[10]; /* string-encoded result, copied from buf */
int characters_left;
int state = 0;
/* 'state' encodes how far we have matched the version pattern
** "n.nnn.nnn"
**
** n.nnn.nnn
** 0123456789
**
** Since we cannot be certain that the pattern does not begin with an
** alphanumeric character, some states are ambiguous.
** The pattern is expected to be terminated with a non-alphanumeric
** character.
*/
exe_file_t *f = exe_open(filename);
if (!f)
return 1;
do {
int i;
int accept;
characters_left = exe_read(f, buf, VERSION_DETECT_BUF_SIZE);
for (i = 0; i < characters_left; i++) {
const char ch = buf[i];
accept = 0; /* By default, we don't like this character */
if (isalnum((unsigned char) ch)) {
accept = (state != 1
&& state != 5
&& state != 9);
} else if (ch == '.') {
accept = (state == 1
|| state == 5);
} else if (state == 9) {
result_string[9] = 0; /* terminate string */
if (!version_parse(result_string, version)) {
exe_close(f);
return 0; /* success! */
}
/* Continue searching. */
}
if (accept)
result_string[state++] = ch;
else
state = 0;
}
} while (characters_left == VERSION_DETECT_BUF_SIZE);
exe_close(f);
return 1; /* failure */
}
static guint32
read_uint32(byte *data) {
return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
}
static guint16
read_uint16(byte *data) {
return (data[0] << 8) | data[1];
}
static int
is_mac_exe(char *filename) {
FILE *file;
byte buf[4];
guint32 val;
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;
}
void
version_require_earlier_than(state_t *s, sci_version_t version) {
if (s->version_lock_flag)
@ -258,102 +84,4 @@ version_parse(const char *vn, sci_version_t *result) {
return 0;
}
int
version_detect_from_executable(sci_version_t *result) {
sci_dir_t dir;
char *filename;
int mac = 0;
/* For Mac versions we need to search the resource fork */
mac = !chdir(".rsrc");
sci_init_dir(&dir);
filename = sci_find_first(&dir, "*");
while (filename) {
if (mac ? is_mac_exe(filename) : is_exe(filename))
if (!scan_file(filename, result)) {
sci_finish_find(&dir);
if (mac)
chdir("..");
return 0;
}
filename = sci_find_next(&dir);
}
if (mac)
chdir("..");
return 1;
}
#define HASHCODE_MAGIC_RESOURCE_000 0x55555555
#define HASHCODE_MAGIC_RESOURCE_001 0x00000001
const char * /* Original version by Solomon Peachy */
version_guess_from_hashcode(sci_version_t *result, int *res_version, guint32 *code) {
int i;
int fd = -1;
int left = VERSION_DETECT_HASH_SIZE;
guint32 hash_code;
guint8 buf[VERSION_DETECT_BUF_SIZE];
if (IS_VALID_FD(fd = sci_open("resource.001", O_RDONLY | O_BINARY))) {
hash_code = HASHCODE_MAGIC_RESOURCE_001;
} else if (IS_VALID_FD(fd = sci_open("resource.000", O_RDONLY | O_BINARY))) {
hash_code = HASHCODE_MAGIC_RESOURCE_000;
} else {
sciprintf("Warning: Could not find RESOURCE.000 or RESOURCE.001, cannot determine hash code\n");
*code = 0;
/* complete and utter failure */
return NULL;
}
while (left > 0) {
int len = read(fd, buf, left < VERSION_DETECT_BUF_SIZE ? left : VERSION_DETECT_BUF_SIZE);
if (len == -1) {
sciprintf("Warning: read error while computing hash code for resource file\n");
*code = 0;
return NULL;
}
if (len == 0)
/* EOF */
break;
for (i = 0; i < len; i++)
hash_code = (hash_code * 19) + *(buf + i);
/* This is the string hashing algorithm used by Objective Caml 3.08; the general idea
** of multiplying the previous hash code with a prime number between 5 and 23 appears
** to be generally considered to be a "good" approach to exhausting the entire 32 bit
** number space in a somewhat equal distribution. For large chunks of data, such as
** SCI resource files, this should both perform well and yield a good distribution,
** or at least that's what standard library designers have been assuming for quite a
** while. */
left -= len;
}
close(fd);
*code = hash_code;
for (i = 0 ; sci_games[i].name ; i++) {
if ((unsigned int)sci_games[i].id == hash_code) {
*result = sci_games[i].version;
*res_version = sci_games[i].res_version;
return sci_games[i].name;
}
}
return NULL; /* Failed to find matching game */
}
#undef VERSION_DETECT_BUF_SIZE