diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp index 959f18739ba..ee9fd5fb184 100644 --- a/engines/sci/detection.cpp +++ b/engines/sci/detection.cpp @@ -3123,9 +3123,8 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl // therefore add the directory here, so that the game files can be opened later on // TODO/FIXME: This should be removed, as it will cause problems with game detection: // if we got a game A, and then try to detect another game B, adding a default - // directory here means that game A's files will be opened first. We either need to - // remove the directory added here, or rewrite all the functions which access game - // files + // directory here means that game A's files will be opened first. We need to rewrite + // all the functions that access game files to use FSNodes instead Common::File::addDefaultDirectory(file->getParent().getPath()); foundResMap = true; } @@ -3167,20 +3166,22 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl s_fallbackDesc.desc.flags = ADGF_NO_FLAGS; // Determine the game id - ResourceManager *resMgr = new ResourceManager(); + ResourceManager *resMgr = new ResourceManager(fslist); SciVersion version = resMgr->sciVersion(); - Kernel *kernel = new Kernel(resMgr); - SegManager *segManager = new SegManager(resMgr, version, kernel->hasOldScriptHeader()); - if (!script_instantiate(resMgr, segManager, version, kernel->hasOldScriptHeader(), 0)) { + Kernel *kernel = new Kernel(resMgr, true); + bool hasOldScriptHeader = kernel->hasOldScriptHeader(); + delete kernel; + + SegManager *segManager = new SegManager(resMgr, version, hasOldScriptHeader); + if (!script_instantiate(resMgr, segManager, version, hasOldScriptHeader, 0)) { warning("fallbackDetect(): Could not instantiate script 0"); return 0; } reg_t game_obj = script_lookup_export(segManager, 0, 0); - Common::String gameName = obj_get_name(segManager,version, game_obj); - debug(2, " \"%s\" at %04x:%04x", gameName.c_str(), PRINT_REG(game_obj)); + Common::String gameName = obj_get_name(segManager, version, game_obj); + debug(2, "Detected ID: \"%s\" at %04x:%04x", gameName.c_str(), PRINT_REG(game_obj)); gameName.toLowercase(); s_fallbackDesc.desc.gameid = strdup(convertSierraGameId(gameName).c_str()); - delete kernel; delete segManager; delete resMgr; diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp index 09d342b7fe4..223e7fc1e97 100644 --- a/engines/sci/engine/kernel.cpp +++ b/engines/sci/engine/kernel.cpp @@ -363,11 +363,15 @@ static const char *argtype_description[] = { "Arithmetic" }; -Kernel::Kernel(ResourceManager *resmgr) : _resmgr(resmgr) { +Kernel::Kernel(ResourceManager *resmgr, bool minimalLoad) : _resmgr(resmgr) { memset(&_selectorMap, 0, sizeof(_selectorMap)); // FIXME: Remove this once/if we C++ify selector_map_t loadSelectorNames(); detectSciFeatures(); + + if (minimalLoad) // If we're only asked to detect game features, stop here + return; + mapSelectors(); // Map a few special selectors for later use loadOpcodes(); loadKernelNames(); diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index 8be51549f68..997cdaea77e 100644 --- a/engines/sci/engine/kernel.h +++ b/engines/sci/engine/kernel.h @@ -65,7 +65,12 @@ enum AutoDetectedFeatures { class Kernel { public: - Kernel(ResourceManager *resmgr); + /** + * Initializes the SCI kernel + * @param minimalLoad If true, only the selector names are loaded, to detect game features. + * It's set to true by the advanced game detector to speed it up + */ + Kernel(ResourceManager *resmgr, bool minimalLoad = false); ~Kernel(); uint getOpcodesSize() const { return _opcodes.size(); } diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp index 2ca198954a8..9b9c9ee26c6 100644 --- a/engines/sci/resource.cpp +++ b/engines/sci/resource.cpp @@ -112,6 +112,20 @@ ResourceSource *ResourceManager::addExternalMap(const char *file_name) { newsrc->source_type = kSourceExtMap; newsrc->location_name = file_name; + newsrc->resourceFile = 0; + newsrc->scanned = false; + newsrc->associated_map = NULL; + + _sources.push_back(newsrc); + return newsrc; +} + +ResourceSource *ResourceManager::addExternalMap(const Common::FSNode *mapFile) { + ResourceSource *newsrc = new ResourceSource(); + + newsrc->source_type = kSourceExtMap; + newsrc->location_name = mapFile->getName(); + newsrc->resourceFile = mapFile; newsrc->scanned = false; newsrc->associated_map = NULL; @@ -125,6 +139,21 @@ ResourceSource *ResourceManager::addSource(ResourceSource *map, ResSourceType ty newsrc->source_type = type; newsrc->scanned = false; newsrc->location_name = filename; + newsrc->resourceFile = 0; + newsrc->volume_number = number; + newsrc->associated_map = map; + + _sources.push_back(newsrc); + return newsrc; +} + +ResourceSource *ResourceManager::addSource(ResourceSource *map, ResSourceType type, const Common::FSNode *resFile, int number) { + ResourceSource *newsrc = new ResourceSource(); + + newsrc->source_type = type; + newsrc->scanned = false; + newsrc->location_name = resFile->getName(); + newsrc->resourceFile = resFile; newsrc->volume_number = number; newsrc->associated_map = map; @@ -342,6 +371,48 @@ int ResourceManager::addAppropriateSources() { return 1; } +int ResourceManager::addAppropriateSources(const Common::FSList &fslist) { + ResourceSource *map = 0; + + // First, find resource.map + for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) { + if (file->isDirectory()) + continue; + + Common::String filename = file->getName(); + filename.toLowercase(); + + if (filename.contains("resource.map") || filename.contains("resmap.000")) { + map = addExternalMap(file); + break; + } + } + + if (!map) + return 0; + + // Now find all the resource.0?? files + for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) { + if (file->isDirectory()) + continue; + + Common::String filename = file->getName(); + filename.toLowercase(); + + if (filename.contains("resource.0") || filename.contains("ressci.0")) { + const char *dot = strrchr(filename.c_str(), '.'); + int number = atoi(dot + 1); + + addSource(map, kSourceVolume, file, number); + } + } + + // This function is only called by the advanced detector, and we don't really need + // to add a patch directory or message.map here + + return 1; +} + int ResourceManager::addInternalSources() { Common::List *resources = listResources(kResourceTypeMap); Common::List::iterator itr = resources->begin(); @@ -397,14 +468,22 @@ void ResourceManager::freeResourceSources() { } ResourceManager::ResourceManager() { + addAppropriateSources(); + init(); +} + +ResourceManager::ResourceManager(const Common::FSList &fslist) { + addAppropriateSources(fslist); + init(); +} + +void ResourceManager::init() { _memoryLocked = 0; _memoryLRU = 0; _LRU.clear(); _resMap.clear(); _audioMapSCI1 = NULL; - addAppropriateSources(); - // FIXME: put this in an Init() function, so that we can error out if detection fails completely _mapVersion = detectMapVersion(); @@ -601,7 +680,8 @@ const char *ResourceManager::versionDescription(ResVersion version) const { } ResourceManager::ResVersion ResourceManager::detectMapVersion() { - Common::File file; + Common::SeekableReadStream *fileStream = 0; + Common::File *file = 0; byte buff[6]; ResourceSource *rsrc= 0; @@ -609,23 +689,30 @@ ResourceManager::ResVersion ResourceManager::detectMapVersion() { rsrc = *it; if (rsrc->source_type == kSourceExtMap) { - file.open(rsrc->location_name); + if (rsrc->resourceFile) { + fileStream = rsrc->resourceFile->createReadStream(); + } else { + file = new Common::File(); + file->open(rsrc->location_name); + if (file->isOpen()) + fileStream = file; + } break; } } - if (file.isOpen() == false) { + if (!fileStream) { error("Failed to open resource map file"); return kResVersionUnknown; } // detection // SCI0 and SCI01 maps have last 6 bytes set to FF - file.seek(-4, SEEK_END); - uint32 uEnd = file.readUint32LE(); + fileStream->seek(-4, SEEK_END); + uint32 uEnd = fileStream->readUint32LE(); if (uEnd == 0xFFFFFFFF) { // check if 0 or 01 - try to read resources in SCI0 format and see if exists - file.seek(0, SEEK_SET); - while (file.read(buff, 6) == 6 && !(buff[0] == 0xFF && buff[1] == 0xFF && buff[2] == 0xFF)) { + fileStream->seek(0, SEEK_SET); + while (fileStream->read(buff, 6) == 6 && !(buff[0] == 0xFF && buff[1] == 0xFF && buff[2] == 0xFF)) { if (getVolume(rsrc, (buff[5] & 0xFC) >> 2) == NULL) return kResVersionSci1Middle; } @@ -639,14 +726,15 @@ ResourceManager::ResVersion ResourceManager::detectMapVersion() { uint16 lastDirectoryOffset = 0; uint16 directorySize = 0; ResVersion mapDetected = kResVersionUnknown; - file.seek(0, SEEK_SET); - while (!file.eos()) { - directoryType = file.readByte(); - directoryOffset = file.readUint16LE(); + fileStream->seek(0, SEEK_SET); + + while (!fileStream->eos()) { + directoryType = fileStream->readByte(); + directoryOffset = fileStream->readUint16LE(); if ((directoryType < 0x80) || ((directoryType > 0xA0) && (directoryType != 0xFF))) break; // Offset is above file size? -> definitely not SCI1/SCI1.1 - if (directoryOffset > file.size()) + if (directoryOffset > fileStream->size()) break; if (lastDirectoryOffset) { directorySize = directoryOffset - lastDirectoryOffset; @@ -655,11 +743,14 @@ ResourceManager::ResVersion ResourceManager::detectMapVersion() { if ((directorySize % 5 == 0) && (directorySize % 6)) mapDetected = kResVersionSci11; } - if (directoryType==0xFF) { + if (directoryType == 0xFF) { // FFh entry needs to point to EOF - if (directoryOffset != file.size()) + if (directoryOffset != fileStream->size()) break; - if (mapDetected) + + delete fileStream; + + if (mapDetected) return mapDetected; return kResVersionSci1Late; } @@ -675,29 +766,41 @@ ResourceManager::ResVersion ResourceManager::detectMapVersion() { // "lastDirectoryOffset". This is probably not the correct fix, since before r43000 // the loop above could not prematurely terminate and thus this would always check the // last directory entry instead of the last checked directory entry. - file.seek(lastDirectoryOffset - 7, SEEK_SET); - if (file.readByte() == 0xFF && file.readUint16LE() == file.size()) + fileStream->seek(lastDirectoryOffset - 7, SEEK_SET); + if (fileStream->readByte() == 0xFF && fileStream->readUint16LE() == fileStream->size()) return kResVersionSci32; // TODO : check if there is a difference between these maps #endif + delete fileStream; + return kResVersionUnknown; } ResourceManager::ResVersion ResourceManager::detectVolVersion() { - Common::File file; + Common::SeekableReadStream *fileStream = 0; + Common::File *file = 0; ResourceSource *rsrc; + for (Common::List::iterator it = _sources.begin(); it != _sources.end(); ++it) { rsrc = *it; if (rsrc->source_type == kSourceVolume) { - file.open(rsrc->location_name); + if (rsrc->resourceFile) { + fileStream = rsrc->resourceFile->createReadStream(); + } else { + file = new Common::File(); + file->open(rsrc->location_name); + if (file->isOpen()) + fileStream = file; + } break; } } - if (file.isOpen() == false) { + if (!fileStream) { error("Failed to open volume file"); return kResVersionUnknown; } + // SCI0 volume format: {wResId wPacked+4 wUnpacked wCompression} = 8 bytes // SCI1 volume format: {bResType wResNumber wPacked+4 wUnpacked wCompression} = 9 bytes // SCI1.1 volume format: {bResType wResNumber wPacked wUnpacked wCompression} = 9 bytes @@ -710,15 +813,17 @@ ResourceManager::ResVersion ResourceManager::detectVolVersion() { bool failed = false; // Check for SCI0, SCI1, SCI1.1 and SCI32 v2 (Gabriel Knight 1 CD) formats - while (!file.eos() && file.pos() < 0x100000) { + while (!fileStream->eos() && fileStream->pos() < 0x100000) { if (curVersion > kResVersionSci0Sci1Early) - file.readByte(); - resId = file.readUint16LE(); - dwPacked = (curVersion < kResVersionSci32) ? file.readUint16LE() : file.readUint32LE(); - dwUnpacked = (curVersion < kResVersionSci32) ? file.readUint16LE() : file.readUint32LE(); - wCompression = (curVersion < kResVersionSci32) ? file.readUint16LE() : file.readUint32LE(); - if (file.eos()) + fileStream->readByte(); + resId = fileStream->readUint16LE(); + dwPacked = (curVersion < kResVersionSci32) ? fileStream->readUint16LE() : fileStream->readUint32LE(); + dwUnpacked = (curVersion < kResVersionSci32) ? fileStream->readUint16LE() : fileStream->readUint32LE(); + wCompression = (curVersion < kResVersionSci32) ? fileStream->readUint16LE() : fileStream->readUint32LE(); + if (fileStream->eos()) { + delete fileStream; return curVersion; + } int chk = (curVersion == kResVersionSci0Sci1Early) ? 4 : 20; int offs = curVersion < kResVersionSci11 ? 4 : 0; @@ -740,18 +845,20 @@ ResourceManager::ResVersion ResourceManager::detectVolVersion() { break; } - file.seek(0, SEEK_SET); + fileStream->seek(0, SEEK_SET); continue; } if (curVersion < kResVersionSci11) - file.seek(dwPacked - 4, SEEK_CUR); + fileStream->seek(dwPacked - 4, SEEK_CUR); else if (curVersion == kResVersionSci11) - file.seek((9 + dwPacked) % 2 ? dwPacked + 1 : dwPacked, SEEK_CUR); + fileStream->seek((9 + dwPacked) % 2 ? dwPacked + 1 : dwPacked, SEEK_CUR); else if (curVersion == kResVersionSci32) - file.seek(dwPacked, SEEK_CUR);//(9 + wPacked) % 2 ? wPacked + 1 : wPacked, SEEK_CUR); + fileStream->seek(dwPacked, SEEK_CUR);//(9 + wPacked) % 2 ? wPacked + 1 : wPacked, SEEK_CUR); } + delete fileStream; + if (!failed) return curVersion; diff --git a/engines/sci/resource.h b/engines/sci/resource.h index 8ab740f463f..4250225ffe7 100644 --- a/engines/sci/resource.h +++ b/engines/sci/resource.h @@ -28,6 +28,7 @@ #include "common/str.h" #include "common/file.h" +#include "common/fs.h" #include "common/archive.h" #include "sound/audiostream.h" @@ -136,6 +137,7 @@ struct ResourceSource { ResSourceType source_type; bool scanned; Common::String location_name; // FIXME: Replace by FSNode ? + const Common::FSNode *resourceFile; int volume_number; ResourceSource *associated_map; }; @@ -247,6 +249,7 @@ public: * Creates a new SCI resource manager. */ ResourceManager(); + ResourceManager(const Common::FSList &fslist); ~ResourceManager(); /** @@ -305,6 +308,11 @@ protected: ResVersion _mapVersion; //!< RESOURCE.MAP version SciVersion _sciVersion; //!< Detected SCI version */ + /** + * Initializes the resource manager + */ + void init(); + /** * Add a path to the resource manager's list of sources. * @return a pointer to the added source structure, or NULL if an error occurred. @@ -322,12 +330,19 @@ protected: */ ResourceSource *addSource(ResourceSource *map, ResSourceType type, const char *filename, int number); + + ResourceSource *addSource(ResourceSource *map, ResSourceType type, + const Common::FSNode *resFile, int number); + /** * Add an external (i.e., separate file) map resource to the resource manager's list of sources. * @param file_name The name of the volume to add * @return A pointer to the added source structure, or NULL if an error occurred. */ ResourceSource *addExternalMap(const char *file_name); + + ResourceSource *addExternalMap(const Common::FSNode *mapFile); + /** * Add an internal (i.e., resource) map to the resource manager's list of sources. * @param name The name of the resource to add @@ -344,6 +359,7 @@ protected: */ void scanNewSources(); int addAppropriateSources(); + int addAppropriateSources(const Common::FSList &fslist); int addInternalSources(); void freeResourceSources();