SCI: Stage 1 of the game detection overhaul. The end goal is to autodetect

as much as possible. All SCI_VERSION_* information was removed from
detection.cpp (much of it was incorrect anyway).

svn-id: r43449
This commit is contained in:
Walter van Niftrik 2009-08-16 19:18:19 +00:00
parent 582eb13fa2
commit 00f4794c0a
22 changed files with 592 additions and 853 deletions

File diff suppressed because it is too large Load diff

View file

@ -179,7 +179,7 @@ static void _free_graphics_input(EngineState *s) {
} }
int game_init_sound(EngineState *s, int sound_flags) { int game_init_sound(EngineState *s, int sound_flags) {
if (s->resmgr->sciVersion() >= SCI_VERSION_01) if (s->resmgr->sciVersion() > SCI_VERSION_0_LATE)
sound_flags |= SFX_STATE_FLAG_MULTIPLAY; sound_flags |= SFX_STATE_FLAG_MULTIPLAY;
s->sfx_init_flags = sound_flags; s->sfx_init_flags = sound_flags;

View file

@ -382,6 +382,8 @@ Kernel::~Kernel() {
} }
void Kernel::detectSciFeatures() { void Kernel::detectSciFeatures() {
// FIXME Much of this is unreliable
Resource *r = _resmgr->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SNAMES), 0); Resource *r = _resmgr->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SNAMES), 0);
Common::StringList staticSelectorTable; Common::StringList staticSelectorTable;
@ -396,9 +398,15 @@ void Kernel::detectSciFeatures() {
features = 0; features = 0;
// Initialize features based on SCI version // Initialize features based on SCI version
if (_resmgr->sciVersion() == SCI_VERSION_0) { switch (_resmgr->sciVersion()) {
case SCI_VERSION_0_EARLY:
features |= kFeatureOldScriptHeader; features |= kFeatureOldScriptHeader;
/* Fallthrough */
case SCI_VERSION_0_LATE:
features |= kFeatureOldGfxFunctions; features |= kFeatureOldGfxFunctions;
break;
default:
break;
} }
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
@ -413,18 +421,12 @@ void Kernel::detectSciFeatures() {
tmp = staticSelectorTable[i]; tmp = staticSelectorTable[i];
} }
if (tmp == "setTarget") // "motionInited" can also be used
features &= ~kFeatureOldScriptHeader;
if (tmp == "motionCue") if (tmp == "motionCue")
features &= ~kFeatureOldGfxFunctions; features &= ~kFeatureOldGfxFunctions;
if (tmp == "egoMoveSpeed" && _resmgr->sciVersion() < SCI_VERSION_1_1) if (tmp == "egoMoveSpeed" && _resmgr->sciVersion() < SCI_VERSION_1_1)
features |= kFeatureLofsAbsolute; features |= kFeatureLofsAbsolute;
if (tmp == "sightAngle" && _resmgr->sciVersion() == SCI_VERSION_0)
features |= kFeatureSci0Sci1Table;
if (tmp == "setVol") if (tmp == "setVol")
features |= kFeatureSci1Sound; features |= kFeatureSci1Sound;
@ -437,12 +439,6 @@ void Kernel::detectSciFeatures() {
printf("Kernel auto-detected features:\n"); printf("Kernel auto-detected features:\n");
printf("Script block headers: ");
if (features & kFeatureOldScriptHeader)
printf("old\n");
else
printf("new\n");
printf("Graphics functions: "); printf("Graphics functions: ");
if (features & kFeatureOldGfxFunctions) if (features & kFeatureOldGfxFunctions)
printf("old\n"); printf("old\n");
@ -462,9 +458,6 @@ void Kernel::detectSciFeatures() {
printf("SCI01\n"); printf("SCI01\n");
else else
printf("SCI0\n"); printf("SCI0\n");
if (features & kFeatureSci0Sci1Table)
printf("Found SCI0 game using a SCI1 kernel table\n");
} }
void Kernel::loadSelectorNames() { void Kernel::loadSelectorNames() {
@ -642,15 +635,6 @@ void Kernel::mapFunctions() {
int mapped = 0; int mapped = 0;
int ignored = 0; int ignored = 0;
uint functions_nr = getKernelNamesSize(); uint functions_nr = getKernelNamesSize();
uint max_functions_nr = (_resmgr->sciVersion() == SCI_VERSION_0) ? 0x72 : 0x7b;
if (functions_nr < max_functions_nr) {
warning("SCI version believed to have %d kernel"
" functions, but only %d reported-- filling up remaining %d",
max_functions_nr, functions_nr, max_functions_nr - functions_nr);
functions_nr = max_functions_nr;
}
_kernelFuncs.resize(functions_nr); _kernelFuncs.resize(functions_nr);
@ -833,15 +817,9 @@ reg_t *kernel_dereference_reg_pointer(EngineState *s, reg_t pointer, int entries
} }
void Kernel::setDefaultKernelNames() { void Kernel::setDefaultKernelNames() {
bool isSci0 = (_resmgr->sciVersion() == SCI_VERSION_0); bool isSci0 = (_resmgr->sciVersion() <= SCI_VERSION_0_LATE);
int offset = 0; int offset = 0;
// Check if we have a SCI01 game which uses a SCI1 kernel table (e.g. the KQ1 demo
// and full version). We do this by checking if the sightAngle selector exists, as no
// SCI0 game seems to have it
if (features & kFeatureSci0Sci1Table)
isSci0 = false;
_kernelNames.resize(SCI_KNAMES_DEFAULT_ENTRIES_NR + (isSci0 ? 4 : 0)); _kernelNames.resize(SCI_KNAMES_DEFAULT_ENTRIES_NR + (isSci0 ? 4 : 0));
for (int i = 0; i < SCI_KNAMES_DEFAULT_ENTRIES_NR; i++) { for (int i = 0; i < SCI_KNAMES_DEFAULT_ENTRIES_NR; i++) {
// In SCI0, Platform was DoAvoider // In SCI0, Platform was DoAvoider
@ -897,23 +875,7 @@ static void vocab_get_knames11(ResourceManager *resmgr, Common::StringList &name
bool Kernel::loadKernelNames() { bool Kernel::loadKernelNames() {
_kernelNames.clear(); _kernelNames.clear();
switch (_resmgr->sciVersion()) {
case SCI_VERSION_0:
case SCI_VERSION_01:
case SCI_VERSION_1:
case SCI_VERSION_1_1:
setDefaultKernelNames(); setDefaultKernelNames();
break;
#ifdef ENABLE_SCI32
case SCI_VERSION_32:
vocab_get_knames11(_resmgr, _kernelNames);
#endif
break;
default:
break;
}
return true; return true;
} }

View file

@ -60,8 +60,7 @@ enum AutoDetectedFeatures {
kFeatureOldGfxFunctions = 1 << 1, kFeatureOldGfxFunctions = 1 << 1,
kFeatureLofsAbsolute = 1 << 2, kFeatureLofsAbsolute = 1 << 2,
kFeatureSci01Sound = 1 << 3, kFeatureSci01Sound = 1 << 3,
kFeatureSci1Sound = 1 << 4, kFeatureSci1Sound = 1 << 4
kFeatureSci0Sci1Table = 1 << 5
}; };
class Kernel { class Kernel {

View file

@ -40,7 +40,7 @@ reg_t kGetEvent(EngineState *s, int funct_nr, int argc, reg_t *argv) {
reg_t obj = argv[1]; reg_t obj = argv[1];
sci_event_t e; sci_event_t e;
int oldx, oldy; int oldx, oldy;
int modifier_mask = s->_version <= SCI_VERSION_0 ? SCI_EVM_ALL : SCI_EVM_NO_FOOLOCK; int modifier_mask = s->_version <= SCI_VERSION_01 ? SCI_EVM_ALL : SCI_EVM_NO_FOOLOCK;
// If there's a simkey pending, and the game wants a keyboard event, use the // If there's a simkey pending, and the game wants a keyboard event, use the
// simkey instead of a normal event // simkey instead of a normal event

View file

@ -304,9 +304,9 @@ static gfx_color_t graph_map_color(EngineState *s, int color, int priority, int
reg_t kSetCursor(EngineState *s, int funct_nr, int argc, reg_t *argv) { reg_t kSetCursor(EngineState *s, int funct_nr, int argc, reg_t *argv) {
switch (argc) { switch (argc) {
case 1 : case 1 :
if (s->_version < SCI_VERSION_1) { if (s->_version < SCI_VERSION_1_LATE) {
GFX_ASSERT(gfxop_set_pointer_cursor(s->gfx_state, argv[0].toSint16())); GFX_ASSERT(gfxop_set_pointer_cursor(s->gfx_state, argv[0].toSint16()));
} else if (s->_version == SCI_VERSION_1) { } else if (s->_version < SCI_VERSION_1_1) {
if (argv[0].toSint16() <= 1) { if (argv[0].toSint16() <= 1) {
// Newer (SCI1.1) semantics: show/hide cursor // Newer (SCI1.1) semantics: show/hide cursor
CursorMan.showMouse(argv[0].toSint16() != 0); CursorMan.showMouse(argv[0].toSint16() != 0);
@ -314,16 +314,16 @@ reg_t kSetCursor(EngineState *s, int funct_nr, int argc, reg_t *argv) {
// Pre-SCI1.1: set cursor according to the first parameter // Pre-SCI1.1: set cursor according to the first parameter
GFX_ASSERT(gfxop_set_pointer_cursor(s->gfx_state, argv[0].toSint16())); GFX_ASSERT(gfxop_set_pointer_cursor(s->gfx_state, argv[0].toSint16()));
} }
} else if (s->_version >= SCI_VERSION_1_1) { } else {
// SCI1.1: Show/hide cursor // SCI1.1: Show/hide cursor
CursorMan.showMouse(argv[0].toSint16() != 0); CursorMan.showMouse(argv[0].toSint16() != 0);
} }
break; break;
case 2 : case 2 :
if (s->_version < SCI_VERSION_1) { if (s->_version < SCI_VERSION_1_LATE) {
GFX_ASSERT(gfxop_set_pointer_cursor(s->gfx_state, GFX_ASSERT(gfxop_set_pointer_cursor(s->gfx_state,
argv[1].toSint16() == 0 ? GFXOP_NO_POINTER : argv[0].toSint16())); argv[1].toSint16() == 0 ? GFXOP_NO_POINTER : argv[0].toSint16()));
} else if (s->_version == SCI_VERSION_1) { } else if (s->_version < SCI_VERSION_1_1) {
// Pre-SCI1.1: set cursor according to the first parameter, and toggle its // Pre-SCI1.1: set cursor according to the first parameter, and toggle its
// visibility based on the second parameter // visibility based on the second parameter
// Some late SCI1 games actually use the SCI1.1 version of this call (EcoQuest 1 // Some late SCI1 games actually use the SCI1.1 version of this call (EcoQuest 1
@ -342,7 +342,7 @@ reg_t kSetCursor(EngineState *s, int funct_nr, int argc, reg_t *argv) {
GFX_ASSERT(gfxop_set_pointer_position(s->gfx_state, GFX_ASSERT(gfxop_set_pointer_position(s->gfx_state,
Common::Point(argv[0].toUint16(), argv[1].toUint16()))); Common::Point(argv[0].toUint16(), argv[1].toUint16())));
} }
} else if (s->_version >= SCI_VERSION_1_1) { } else {
// SCI1.1 and newer: set pointer position // SCI1.1 and newer: set pointer position
GFX_ASSERT(gfxop_set_pointer_position(s->gfx_state, GFX_ASSERT(gfxop_set_pointer_position(s->gfx_state,
Common::Point(argv[0].toUint16(), argv[1].toUint16()))); Common::Point(argv[0].toUint16(), argv[1].toUint16())));

View file

@ -274,7 +274,7 @@ static void bresenham_autodetect(EngineState *s) {
} }
buf = s->seg_manager->getScript(fptr.segment)->buf + fptr.offset; buf = s->seg_manager->getScript(fptr.segment)->buf + fptr.offset;
handle_movecnt = (s->_version <= SCI_VERSION_0 || checksum_bytes(buf, 8) == 0x216) ? INCREMENT_MOVECNT : IGNORE_MOVECNT; handle_movecnt = (s->_version <= SCI_VERSION_01 || checksum_bytes(buf, 8) == 0x216) ? INCREMENT_MOVECNT : IGNORE_MOVECNT;
printf("b-moveCnt action based on checksum: %s\n", handle_movecnt == IGNORE_MOVECNT ? "ignore" : "increment"); printf("b-moveCnt action based on checksum: %s\n", handle_movecnt == IGNORE_MOVECNT ? "ignore" : "increment");
} else { } else {
warning("bresenham_autodetect failed"); warning("bresenham_autodetect failed");
@ -293,7 +293,7 @@ reg_t kDoBresen(EngineState *s, int funct_nr, int argc, reg_t *argv) {
int completed = 0; int completed = 0;
int max_movcnt = GET_SEL32V(client, moveSpeed); int max_movcnt = GET_SEL32V(client, moveSpeed);
if (s->_version > SCI_VERSION_0) if (s->_version > SCI_VERSION_01)
signal &= ~_K_VIEW_SIG_FLAG_HIT_OBSTACLE; signal &= ~_K_VIEW_SIG_FLAG_HIT_OBSTACLE;
if (handle_movecnt == UNINITIALIZED) if (handle_movecnt == UNINITIALIZED)
@ -380,7 +380,7 @@ reg_t kDoBresen(EngineState *s, int funct_nr, int argc, reg_t *argv) {
completed = 1; completed = 1;
} }
if (s->_version > SCI_VERSION_0) if (s->_version > SCI_VERSION_01)
if (completed) if (completed)
invoke_selector(INV_SEL(mover, moveDone, kStopOnInvalidSelector), 0); invoke_selector(INV_SEL(mover, moveDone, kStopOnInvalidSelector), 0);

View file

@ -157,9 +157,9 @@ void process_sound_events(EngineState *s) { /* Get all sound events, apply their
SongHandle handle; SongHandle handle;
int cue; int cue;
if (s->_version >= SCI_VERSION_01) if (s->_version > SCI_VERSION_01)
return; return;
/* SCI01 and later explicitly poll for everything */ /* SCI1 and later explicitly poll for everything */
while ((result = s->_sound.sfx_poll(&handle, &cue))) { while ((result = s->_sound.sfx_poll(&handle, &cue))) {
reg_t obj = DEFROBNICATE_HANDLE(handle); reg_t obj = DEFROBNICATE_HANDLE(handle);

View file

@ -693,7 +693,13 @@ int _reset_graphics_input(EngineState *s);
static void reconstruct_sounds(EngineState *s) { static void reconstruct_sounds(EngineState *s) {
Song *seeker; Song *seeker;
SongIteratorType it_type = s->resmgr->sciVersion() >= SCI_VERSION_01 ? SCI_SONG_ITERATOR_TYPE_SCI1 : SCI_SONG_ITERATOR_TYPE_SCI0; SongIteratorType it_type;
if (((SciEngine *)g_engine)->getKernel()->usesSci01SoundFunctions()
|| ((SciEngine *)g_engine)->getKernel()->usesSci1SoundFunctions())
it_type = SCI_SONG_ITERATOR_TYPE_SCI1;
else
it_type = SCI_SONG_ITERATOR_TYPE_SCI0;
seeker = s->_sound._songlib._lib; seeker = s->_sound._songlib._lib;

View file

@ -88,18 +88,11 @@ opcode_format g_opcode_formats[128][4] = {
}; };
#undef END #undef END
void script_adjust_opcode_formats(int res_version) { void script_adjust_opcode_formats(SciVersion version) {
switch (res_version) { // TODO: Check that this is correct
case SCI_VERSION_0: if ((version >= SCI_VERSION_1_1) || ((SciEngine*)g_engine)->getKernel()->hasLofsAbsolute()) {
break;
case SCI_VERSION_01:
case SCI_VERSION_1:
case SCI_VERSION_1_1:
g_opcode_formats[op_lofsa][0] = Script_Offset; g_opcode_formats[op_lofsa][0] = Script_Offset;
g_opcode_formats[op_lofss][0] = Script_Offset; g_opcode_formats[op_lofss][0] = Script_Offset;
break;
default:
error("script_adjust_opcode_formats(): Unknown script version %d\n", res_version);
} }
} }

View file

@ -202,7 +202,7 @@ enum sci_opcodes { /* FIXME */
extern opcode_format g_opcode_formats[128][4]; extern opcode_format g_opcode_formats[128][4];
void script_adjust_opcode_formats(int res_version); void script_adjust_opcode_formats(SciVersion version);
void script_free_breakpoints(EngineState *s); void script_free_breakpoints(EngineState *s);

View file

@ -129,7 +129,7 @@ public:
SpeedThrottler(SciVersion version) { SpeedThrottler(SciVersion version) {
if (version >= SCI_VERSION_1_1) if (version >= SCI_VERSION_1_1)
_maxInstructions = 3300; _maxInstructions = 3300;
else if (version >= SCI_VERSION_1) else if (version >= SCI_VERSION_1_EARLY)
_maxInstructions = 2200; _maxInstructions = 2200;
else else
_maxInstructions = 1100; _maxInstructions = 1100;

View file

@ -260,7 +260,6 @@ Common::String readSciVersionFromExe(Common::SeekableReadStream *exeStream, Comm
// String-encoded result, copied from buffer // String-encoded result, copied from buffer
char currentString[10]; char currentString[10];
Common::String resultString;
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
unsigned char ch = *buf++; unsigned char ch = *buf++;
@ -275,17 +274,10 @@ Common::String readSciVersionFromExe(Common::SeekableReadStream *exeStream, Comm
// Terminate string // Terminate string
currentString[9] = 0; currentString[9] = 0;
// Return the current string if it's parseable // Return the current string
SciVersion version;
if (getSciVersionFromString(currentString, &version, platform)) {
delete[] buffer;
return currentString; return currentString;
} }
// Save the found string and continue searching
resultString = currentString;
}
if (accept) if (accept)
currentString[state++] = ch; currentString[state++] = ch;
else else
@ -293,57 +285,7 @@ Common::String readSciVersionFromExe(Common::SeekableReadStream *exeStream, Comm
} }
delete[] buffer; delete[] buffer;
return resultString; return "unknown";
}
bool getSciVersionFromString(Common::String versionString, SciVersion *version, Common::Platform platform) {
*version = SCI_VERSION_AUTODETECT;
if (platform == Common::kPlatformAmiga) {
if (versionString.hasPrefix("1.002.")) {
*version = SCI_VERSION_0;
} else if (versionString.hasPrefix("1.003.")) {
*version = SCI_VERSION_01;
} else if (versionString.hasPrefix("1.004.")) {
*version = SCI_VERSION_01;
} else if (versionString.hasPrefix("1.005.")) {
*version = SCI_VERSION_1;
} else if (versionString == "x.yyy.zzz") {
// How to map it?
} else {
return false;
}
} else if (versionString.hasPrefix("0.000.")) {
*version = SCI_VERSION_0;
} else if (versionString.hasPrefix("S.old.")) {
*version = SCI_VERSION_01;
} else if (versionString.hasPrefix("1.000.")) {
*version = SCI_VERSION_1;
} else if (versionString.hasPrefix("1.001.")) {
*version = SCI_VERSION_1_1;
} else if (versionString.hasPrefix("2.000.")
|| versionString.hasPrefix("2.100.")
|| versionString.hasPrefix("3.000.")) {
*version = SCI_VERSION_32;
} else if (versionString.hasPrefix("1.ECO.")
|| versionString.hasPrefix("1.SQ1.")
|| versionString.hasPrefix("1.SQ4.")
|| versionString.hasPrefix("1.LS5.")
|| versionString.hasPrefix("1.pq3.")
|| versionString.hasPrefix("FAIRY.")
|| versionString.hasPrefix("T.A00.")) {
*version = SCI_VERSION_1;
} else if (versionString.hasPrefix("L.rry.")
|| versionString.hasPrefix("l.cfs.")) {
*version = SCI_VERSION_1_1;
} else if (versionString == "x.yyy.zzz") {
// How to map it?
} else {
// Unknown or not a version number
return false;
}
return true;
} }
} // End of namespace Sci } // End of namespace Sci

View file

@ -33,7 +33,6 @@ namespace Sci {
Common::Platform getGameExePlatform(Common::SeekableReadStream *exeStream); Common::Platform getGameExePlatform(Common::SeekableReadStream *exeStream);
Common::String readSciVersionFromExe(Common::SeekableReadStream *exeStream, Common::Platform platform); Common::String readSciVersionFromExe(Common::SeekableReadStream *exeStream, Common::Platform platform);
bool getSciVersionFromString(Common::String versionString, SciVersion *version, Common::Platform platform);
} // End of namespace Sci } // End of namespace Sci

View file

@ -57,11 +57,6 @@ GfxResManager::GfxResManager(gfx_options_t *options, GfxDriver *driver, Resource
_portBounds = Common::Rect(0, 10, 320, 200); // default value, with a titlebar of 10px _portBounds = Common::Rect(0, 10, 320, 200); // default value, with a titlebar of 10px
_version = resManager->sciVersion(); _version = resManager->sciVersion();
// Workaround for QFG1 VGA (has SCI 1.1 view data with SCI 1 compression)
if (_version == SCI_VERSION_1 && !strcmp(((SciEngine*)g_engine)->getGameID(), "qfg1")) {
_version = SCI_VERSION_1_1;
}
if (!_resManager->isVGA()) { if (!_resManager->isVGA()) {
_staticPalette = gfx_sci0_pic_colors->getref(); _staticPalette = gfx_sci0_pic_colors->getref();
} else if (_version == SCI_VERSION_1_1) { } else if (_version == SCI_VERSION_1_1) {
@ -537,7 +532,8 @@ gfxr_view_t *GfxResManager::getView(int nr, int *loop, int *cel, int palette) {
int resid = GFXR_RES_ID(GFX_RESOURCE_TYPE_VIEW, nr); int resid = GFXR_RES_ID(GFX_RESOURCE_TYPE_VIEW, nr);
if (!_resManager->isVGA()) { if (!_resManager->isVGA()) {
int pal = (_version == SCI_VERSION_0) ? -1 : palette; // TODO: Check which versions this applies to
int pal = (_version < SCI_VERSION_1_EARLY) ? -1 : palette;
view = getEGAView(resid, viewRes->data, viewRes->size, pal); view = getEGAView(resid, viewRes->data, viewRes->size, pal);
} else { } else {
if (_version < SCI_VERSION_1_1) if (_version < SCI_VERSION_1_1)
@ -679,7 +675,7 @@ gfx_pixmap_t *GfxResManager::getCursor(int num) {
} }
gfx_pixmap_t *cursor = gfxr_draw_cursor(GFXR_RES_ID(GFX_RESOURCE_TYPE_CURSOR, num), gfx_pixmap_t *cursor = gfxr_draw_cursor(GFXR_RES_ID(GFX_RESOURCE_TYPE_CURSOR, num),
cursorRes->data, cursorRes->size, _version != SCI_VERSION_0); cursorRes->data, cursorRes->size, _version > SCI_VERSION_01);
if (!cursor) if (!cursor)
return NULL; return NULL;

View file

@ -231,15 +231,6 @@ public:
int calculatePic(gfxr_pic_t *scaled_pic, gfxr_pic_t *unscaled_pic, int calculatePic(gfxr_pic_t *scaled_pic, gfxr_pic_t *unscaled_pic,
int flags, int default_palette, int nr); int flags, int default_palette, int nr);
/**
* Determines whether support for pointers with more than two colors
* is required.
*
* @return false if no support for multi-colored pointers is required,
* true otherwise
*/
bool multicoloredPointers() { return _version > SCI_VERSION_1; }
/** /**
* Frees all resources currently allocated. * Frees all resources currently allocated.

View file

@ -319,63 +319,6 @@ int sci0_get_compression_method(Common::ReadStream &stream) {
return compressionMethod; return compressionMethod;
} }
SciVersion ResourceManager::guessSciVersion() {
Common::File file;
char filename[MAXPATHLEN];
int compression;
Resource *res;
int i;
for (i = 0; i < 1000; i++) {
res = testResource(ResourceId(kResourceTypeView, i));
if (!res)
continue;
if (res->source->source_type == kSourceDirectory)
continue;
strcpy(filename, res->source->location_name.c_str());
if (!file.open(filename))
continue;
file.seek(res->file_offset, SEEK_SET);
compression = sci0_get_compression_method(file);
file.close();
if (compression == 3) {
return SCI_VERSION_01;
}
}
// Try the same thing with pics
for (i = 0; i < 1000; i++) {
res = testResource(ResourceId(kResourceTypePic, i));
if (!res)
continue;
if (res->source->source_type == kSourceDirectory)
continue;
strcpy(filename, res->source->location_name.c_str());
if (!file.open(filename))
continue;
file.seek(res->file_offset, SEEK_SET);
compression = sci0_get_compression_method(file);
file.close();
if (compression == 3) {
return SCI_VERSION_01;
}
}
return SCI_VERSION_AUTODETECT;
}
int ResourceManager::addAppropriateSources() { int ResourceManager::addAppropriateSources() {
ResourceSource *map; ResourceSource *map;
@ -484,91 +427,23 @@ ResourceManager::ResourceManager(int maxMemory) {
addInternalSources(); addInternalSources();
scanNewSources(); scanNewSources();
switch (_mapVersion) { _sciVersion = detectSciVersion();
case kResVersionSci0Sci1Early:
if (testResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SCI0_MAIN_VOCAB))) {
_sciVersion = guessSciVersion() ? SCI_VERSION_01 : SCI_VERSION_0;
} else if (testResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SCI1_MAIN_VOCAB))) {
_sciVersion = guessSciVersion();
if (_sciVersion != SCI_VERSION_01) {
_sciVersion = testResource(ResourceId(kResourceTypeVocab, 912)) ? SCI_VERSION_0 : SCI_VERSION_01;
}
} else {
_sciVersion = guessSciVersion() ? SCI_VERSION_01 : SCI_VERSION_0;
}
break;
case kResVersionSci1Middle:
_sciVersion = SCI_VERSION_01;
break;
case kResVersionSci1Late:
_sciVersion = SCI_VERSION_1;
break;
case kResVersionSci11:
_sciVersion = SCI_VERSION_1_1;
break;
case kResVersionSci32:
_sciVersion = SCI_VERSION_32;
break;
default:
_sciVersion = SCI_VERSION_AUTODETECT;
}
_isVGA = false; if (_sciVersion != SCI_VERSION_AUTODETECT)
debug("Resmgr: Detected %s", versionNames[_sciVersion]);
// Determine if the game is using EGA graphics or not
if (_sciVersion == SCI_VERSION_0) {
_isVGA = false; // There is no SCI0 VGA game
} else if (_sciVersion >= SCI_VERSION_1_1) {
_isVGA = true; // There is no SCI11 EGA game
} else {
// SCI01 or SCI1: EGA games have the second byte of their views set
// to 0, VGA ones to non-zero
int i = 0;
while (true) {
Resource *res = findResource(ResourceId(kResourceTypeView, i), 0);
if (res) {
_isVGA = (res->data[1] != 0);
break;
}
i++;
}
}
// Workaround for QFG1 VGA (has SCI 1.1 view data with SCI 1 compression)
if (_sciVersion == SCI_VERSION_1 && !strcmp(((SciEngine*)g_engine)->getGameID(), "qfg1")) {
debug("Resmgr: Detected QFG1 VGA");
_isVGA = true;
}
// temporary version printout - should be reworked later
switch (_sciVersion) {
case SCI_VERSION_0:
debug("Resmgr: Detected SCI0");
break;
case SCI_VERSION_01:
debug("Resmgr: Detected SCI01");
break;
case SCI_VERSION_1:
debug("Resmgr: Detected SCI1");
break;
case SCI_VERSION_1_1:
debug("Resmgr: Detected SCI1.1");
break;
#ifdef ENABLE_SCI32
case SCI_VERSION_32:
debug("Resmgr: Couldn't determine SCI version");
break;
#endif
default:
debug("Resmgr: Couldn't determine SCI version");
break;
}
if (_isVGA)
debug("Resmgr: Detected VGA graphic resources");
else else
debug("Resmgr: Detected non-VGA/EGA graphic resources"); debug("Resmgr: Couldn't determine SCI version");
switch (_viewType) {
case kViewEga:
debug("Resmgr: Detected EGA graphic resources");
break;
case kViewVga:
debug("Resmgr: Detected VGA graphic resources");
break;
case kViewVga11:
debug("Resmgr: Detected SCI1.1 VGA graphic resources");
}
} }
ResourceManager::~ResourceManager() { ResourceManager::~ResourceManager() {
@ -1408,10 +1283,10 @@ int ResourceManager::readResourceInfo(Resource *res, Common::File *file,
compression = kCompNone; compression = kCompNone;
break; break;
case 1: case 1:
compression = (_sciVersion == SCI_VERSION_0) ? kCompLZW : kCompHuffman; compression = (_sciVersion <= SCI_VERSION_01) ? kCompLZW : kCompHuffman;
break; break;
case 2: case 2:
compression = (_sciVersion == SCI_VERSION_0) ? kCompHuffman : kCompLZW1; compression = (_sciVersion <= SCI_VERSION_01) ? kCompHuffman : kCompLZW1;
break; break;
case 3: case 3:
compression = kCompLZW1View; compression = kCompLZW1View;
@ -1483,4 +1358,239 @@ int ResourceManager::decompress(Resource *res, Common::File *file) {
return error; return error;
} }
ResourceCompression ResourceManager::getViewCompression() {
int viewsTested = 0;
// Test 10 views to see if any are compressed
for (int i = 0; i < 1000; i++) {
Common::File *file;
Resource *res = testResource(ResourceId(kResourceTypeView, i));
if (!res)
continue;
if (res->source->source_type != kSourceVolume)
continue;
file = getVolumeFile(res->source->location_name.c_str());
if (!file)
continue;
file->seek(res->file_offset, SEEK_SET);
uint32 szPacked;
ResourceCompression compression;
if (readResourceInfo(res, file, szPacked, compression))
continue;
if (compression != kCompNone)
return compression;
if (++viewsTested == 10)
break;
}
return kCompNone;
}
ResourceManager::ViewType ResourceManager::detectViewType() {
for (int i = 0; i < 1000; i++) {
Resource *res = findResource(ResourceId(kResourceTypeView, i), 0);
if (res) {
//FIXME: Amiga
switch(res->data[1]) {
case 0:
return kViewEga;
default:
return kViewVga;
}
}
}
warning("Resmgr: Couldn't find any views");
return kViewVga;
}
SciVersion ResourceManager::detectSciVersion() {
// We use the view compression to set a preliminary _sciVersion for the sake of getResourceInfo
// Pretend we have a SCI0 game
_sciVersion = SCI_VERSION_0_EARLY;
bool oldDecompressors = true;
ResourceCompression viewCompression = getViewCompression();
if (viewCompression != kCompLZW) {
// If it's a different compression type from kCompLZW, the game is probably
// SCI_VERSION_1_EGA or later. If the views are uncompressed, it is
// likely not an early disk game.
_sciVersion = SCI_VERSION_1_EGA;
oldDecompressors = false;
}
// Set view type
if (viewCompression == kCompDCL) {
// SCI1.1 VGA views
_viewType = kViewVga11;
} else {
// Otherwise we detect it from a view
_viewType = detectViewType();
}
switch (_mapVersion) {
case kResVersionSci0Sci1Early:
if (_viewType == kViewVga) {
// VGA
return SCI_VERSION_1_EARLY;
}
// EGA
if (hasOldScriptHeader())
return SCI_VERSION_0_EARLY;
if (oldDecompressors) {
// It's either SCI_VERSION_0_LATE or SCI_VERSION_01
// We first check for SCI1 vocab.999
if (testResource(ResourceId(kResourceTypeVocab, 999))) {
if (hasSci0Voc999()) {
return SCI_VERSION_0_LATE;
} else {
return SCI_VERSION_01;
}
}
// If vocab.999 is missing, we try vocab.900
if (testResource(ResourceId(kResourceTypeVocab, 900))) {
if (hasSci1Voc900()) {
return SCI_VERSION_01;
} else {
return SCI_VERSION_0_LATE;
}
}
warning("Failed to accurately determine SCI version");
// No parser, we assume SCI_VERSION_01.
return SCI_VERSION_01;
}
// New decompressors. It's either SCI_VERSION_1_EGA or SCI_VERSION_1_EARLY.
if (hasSci1Voc900())
return SCI_VERSION_1_EGA;
// SCI_VERSION_1_EARLY EGA versions seem to be lacking a valid vocab.900.
// If this turns out to be unreliable, we could do some pic resource checks instead.
return SCI_VERSION_1_EARLY;
case kResVersionSci1Middle:
return SCI_VERSION_1_LATE;
case kResVersionSci1Late:
if (_viewType == kViewVga11) {
// SCI1.1 resources, assume SCI1.1
return SCI_VERSION_1_1;
}
return SCI_VERSION_1_LATE;
case kResVersionSci11:
return SCI_VERSION_1_1;
case kResVersionSci32:
return SCI_VERSION_32;
default:
return SCI_VERSION_AUTODETECT;
}
}
// Functions below are based on PD code by Brian Provinciano (SCI Studio)
bool ResourceManager::hasOldScriptHeader() {
Resource *res = findResource(ResourceId(kResourceTypeScript, 0), 0);
if (!res) {
warning("Resmgr: Failed to find script.000");
return false;
}
uint offset = 2;
const int objTypes = 17;
while (offset < res->size) {
uint16 objType = READ_LE_UINT16(res->data + offset);
if (!objType) {
offset += 2;
// We should be at the end of the resource now
return offset == res->size;
}
if (objType >= objTypes) {
// Invalid objType
return false;
}
int skip = READ_LE_UINT16(res->data + offset + 2);
if (skip < 2) {
// Invalid size
return false;
}
offset += skip;
}
return false; }
bool ResourceManager::hasSci0Voc999() {
Resource *res = findResource(ResourceId(kResourceTypeVocab, 999), 0);
if (!res) {
// No vocab present, possibly a demo version
return false;
}
if (res->size < 2)
return false;
uint16 count = READ_LE_UINT16(res->data);
// Make sure there's enough room for the pointers
if (res->size < (uint)count * 2)
return false;
// Iterate over all pointers
for (uint i = 0; i < count; i++) {
// Offset to string
uint16 offset = READ_LE_UINT16(res->data + 2 + count * 2);
// Look for end of string
do {
if (offset >= res->size) {
// Out of bounds
return false;
}
} while (res->data[offset++]);
}
return true;
}
bool ResourceManager::hasSci1Voc900() {
Resource *res = findResource(ResourceId(kResourceTypeVocab, 900), 0);
if (!res )
return false;
if (res->size < 0x1fe)
return false;
uint16 offset = 0x1fe;
while (offset < res->size) {
offset++;
do {
if (offset >= res->size) {
// Out of bounds;
return false;
}
} while (res->data[offset++]);
offset += 3;
}
return offset == res->size;
}
} // End of namespace Sci } // End of namespace Sci

View file

@ -47,12 +47,15 @@ namespace Sci {
/** SCI versions */ /** SCI versions */
enum SciVersion { enum SciVersion {
SCI_VERSION_AUTODETECT = 0, SCI_VERSION_AUTODETECT,
SCI_VERSION_0 = 1, SCI_VERSION_0_EARLY, // Early KQ4, 1988 xmas card
SCI_VERSION_01 = 2, SCI_VERSION_0_LATE, // KQ4, LSL2, LSL3, SQ3 etc
SCI_VERSION_1 = 3, SCI_VERSION_01, // KQ1 and multilingual games (S.old.*)
SCI_VERSION_1_1 = 4, SCI_VERSION_1_EGA, // EGA with parser, QFG2
SCI_VERSION_32 = 5 SCI_VERSION_1_EARLY, // KQ5, LSL1. (EGA/VGA)
SCI_VERSION_1_LATE, // ECO1, LSL5. (EGA/VGA)
SCI_VERSION_1_1, // KQ6, ECO2
SCI_VERSION_32 // GK
}; };
/** Resource status types */ /** Resource status types */
@ -225,7 +228,14 @@ public:
kResVersionSci32 kResVersionSci32
}; };
bool isVGA() const { return _isVGA; } // TODO: Amiga
enum ViewType {
kViewEga,
kViewVga,
kViewVga11
};
bool isVGA() const { return (_viewType == kViewVga) || (_viewType == kViewVga11); }
/** /**
* Returns the SCI version as detected by the resource manager * Returns the SCI version as detected by the resource manager
@ -284,7 +294,7 @@ public:
void setAudioLanguage(int language); void setAudioLanguage(int language);
protected: protected:
bool _isVGA; // Used to determine if the game has EGA or VGA graphics ViewType _viewType; // Used to determine if the game has EGA or VGA graphics
int _maxMemory; //!< Config option: Maximum total byte number allocated int _maxMemory; //!< Config option: Maximum total byte number allocated
Common::List<ResourceSource *> _sources; Common::List<ResourceSource *> _sources;
int _memoryLocked; //!< Amount of resource bytes in locked memory int _memoryLocked; //!< Amount of resource bytes in locked memory
@ -403,7 +413,12 @@ protected:
void addToLRU(Resource *res); void addToLRU(Resource *res);
void removeFromLRU(Resource *res); void removeFromLRU(Resource *res);
SciVersion guessSciVersion(); ResourceCompression getViewCompression();
ViewType detectViewType();
bool hasOldScriptHeader();
bool hasSci0Voc999();
bool hasSci1Voc900();
SciVersion detectSciVersion();
}; };
} // End of namespace Sci } // End of namespace Sci

View file

@ -43,11 +43,15 @@ namespace Sci {
class GfxDriver; class GfxDriver;
const char *versionNames[6] = { // FIXME: error-prone
"Autodetected", const char *versionNames[9] = {
"SCI0", "Autodetect",
"SCI0 Early",
"SCI0 Late",
"SCI01", "SCI01",
"SCI1", "SCI1 EGA",
"SCI1 Early",
"SCI1 Late",
"SCI1.1", "SCI1.1",
"SCI32" "SCI32"
}; };
@ -128,51 +132,25 @@ Common::Error SciEngine::run() {
// FIXME/TODO: Move some of the stuff below to init() // FIXME/TODO: Move some of the stuff below to init()
SciVersion version = getVersion();
const uint32 flags = getFlags(); const uint32 flags = getFlags();
_resmgr = new ResourceManager(256 * 1024); _resmgr = new ResourceManager(256 * 1024);
_version = _resmgr->sciVersion();
if (!_resmgr) { if (!_resmgr) {
printf("No resources found, aborting...\n"); printf("No resources found, aborting...\n");
return Common::kNoGameDataFoundError; return Common::kNoGameDataFoundError;
} }
// When version is set to autodetect, use version as determined by resource manager
if (version == SCI_VERSION_AUTODETECT)
version = _resmgr->sciVersion();
_kernel = new Kernel(_resmgr); _kernel = new Kernel(_resmgr);
_vocabulary = new Vocabulary(_resmgr); _vocabulary = new Vocabulary(_resmgr);
script_adjust_opcode_formats(_resmgr->sciVersion()); script_adjust_opcode_formats(_version);
#if 0 _gamestate = new EngineState(_resmgr, _version, flags);
printf("Mapping instruments to General Midi\n");
map_MIDI_instruments(_resmgr);
#endif
_gamestate = new EngineState(_resmgr, version, flags);
// Verify that we haven't got an invalid game detection entry
if (version < SCI_VERSION_1) {
// SCI0/SCI01
} else if (version == SCI_VERSION_1) {
if (flags & GF_SCI0_OLDGETTIME) {
error("This game entry is erroneous. It's marked as SCI1, but it has SCI0 flags set");
}
} else if (version == SCI_VERSION_1_1 || version == SCI_VERSION_32) {
if (flags & GF_SCI0_OLDGETTIME) {
error("This game entry is erroneous. It's marked as SCI1.1/SCI32, but it has SCI0 flags set");
}
} else {
error ("Unknown SCI version in game entry");
}
if (script_init_engine(_gamestate)) if (script_init_engine(_gamestate))
return Common::kUnknownError; return Common::kUnknownError;
if (game_init(_gamestate)) { /* Initialize */ if (game_init(_gamestate)) { /* Initialize */
warning("Game initialization failed: Aborting..."); warning("Game initialization failed: Aborting...");
// TODO: Add an "init failed" error? // TODO: Add an "init failed" error?
@ -226,7 +204,7 @@ Common::Error SciEngine::run() {
return Common::kUnknownError; return Common::kUnknownError;
} }
printf("Emulating SCI version %s\n", versionNames[version]); printf("Emulating SCI version %s\n", versionNames[_version]);
game_run(&_gamestate); // Run the game game_run(&_gamestate); // Run the game
@ -265,7 +243,7 @@ const char* SciEngine::getGameID() const {
} }
SciVersion SciEngine::getVersion() const { SciVersion SciEngine::getVersion() const {
return _gameDescription->version; return _version;
} }
Common::Language SciEngine::getLanguage() const { Common::Language SciEngine::getLanguage() const {

View file

@ -66,10 +66,9 @@ enum kDebugLevels {
struct SciGameDescription { struct SciGameDescription {
ADGameDescription desc; ADGameDescription desc;
uint32 flags; uint32 flags;
SciVersion version;
}; };
extern const char *versionNames[6]; extern const char *versionNames[];
enum SciGameFlags { enum SciGameFlags {
// SCI0 flags // SCI0 flags
@ -113,6 +112,7 @@ public:
private: private:
const SciGameDescription *_gameDescription; const SciGameDescription *_gameDescription;
SciVersion _version;
ResourceManager *_resmgr; ResourceManager *_resmgr;
EngineState *_gamestate; EngineState *_gamestate;
Kernel *_kernel; Kernel *_kernel;

View file

@ -641,7 +641,7 @@ int MidiPlayer_Adlib::open(ResourceManager *resmgr) {
static_cast<MidiDriver_Adlib *>(_driver)->loadResource(res); static_cast<MidiDriver_Adlib *>(_driver)->loadResource(res);
return static_cast<MidiDriver_Adlib *>(_driver)->open(resmgr->sciVersion() == SCI_VERSION_0); return static_cast<MidiDriver_Adlib *>(_driver)->open(resmgr->sciVersion() <= SCI_VERSION_0_LATE);
} }
} // End of namespace Sci } // End of namespace Sci

View file

@ -91,7 +91,7 @@ Vocabulary::Vocabulary(ResourceManager *resmgr) : _resmgr(resmgr) {
debug(2, "Initializing vocabulary"); debug(2, "Initializing vocabulary");
if (_resmgr->sciVersion() <= SCI_VERSION_01 && loadParserWords()) { if (_resmgr->sciVersion() <= SCI_VERSION_1_EGA && loadParserWords()) {
loadSuffixes(); loadSuffixes();
if (loadBranches()) if (loadBranches())
// Now build a GNF grammar out of this // Now build a GNF grammar out of this