From f028f53d3e9b077c7a821b244a717124f631ac10 Mon Sep 17 00:00:00 2001 From: Greg Frieger Date: Sat, 7 Mar 2009 00:59:38 +0000 Subject: Map/volume loader and version detection functions cleaned and reworked. Autodetection still misdetects some SCI1/SCI1.1 games though. svn-id: r39169 --- engines/sci/scicore/resource.cpp | 267 +++++++++++++++++++++++------------ engines/sci/scicore/resource.h | 17 ++- engines/sci/scicore/resource_map.cpp | 128 ++--------------- 3 files changed, 191 insertions(+), 221 deletions(-) (limited to 'engines/sci/scicore') diff --git a/engines/sci/scicore/resource.cpp b/engines/sci/scicore/resource.cpp index 647a4abd05..e483af813c 100644 --- a/engines/sci/scicore/resource.cpp +++ b/engines/sci/scicore/resource.cpp @@ -260,8 +260,6 @@ int sci_test_view_type(ResourceManager *mgr) { Resource *res; int i; - mgr->_sciVersion = SCI_VERSION_AUTODETECT; - for (i = 0; i < 1000; i++) { res = mgr->testResource(kResourceTypeView, i); @@ -281,8 +279,7 @@ int sci_test_view_type(ResourceManager *mgr) { file.close(); if (compression == 3) { - mgr->_sciVersion = SCI_VERSION_01_VGA; - return mgr->_sciVersion; + return SCI_VERSION_01_VGA; } } @@ -306,16 +303,14 @@ int sci_test_view_type(ResourceManager *mgr) { file.close(); if (compression == 3) { - mgr->_sciVersion = SCI_VERSION_01_VGA; - return mgr->_sciVersion; + return SCI_VERSION_01_VGA; } } - return mgr->_sciVersion; + return SCI_VERSION_AUTODETECT; } int ResourceManager::addAppropriateSources() { - //char path_separator; ResourceSource *map; if (!Common::File::exists("RESOURCE.MAP")) @@ -336,20 +331,13 @@ int ResourceManager::addAppropriateSources() { return 1; } -int ResourceManager::scanNewSources(int *detected_version, ResourceSource *source) { +int ResourceManager::scanNewSources(ResourceSource *source) { if (!source) return SCI_ERROR_NO_RESOURCE_FILES_FOUND; - int preset_version = _sciVersion; int resource_error = 0; - int dummy = _sciVersion; - - if (detected_version == NULL) - detected_version = &dummy; - - *detected_version = _sciVersion; if (source->next) - scanNewSources(detected_version, source->next); + scanNewSources(source->next); if (!source->scanned) { source->scanned = true; @@ -358,46 +346,20 @@ int ResourceManager::scanNewSources(int *detected_version, ResourceSource *sourc readResourcePatches(source); break; case kSourceExtMap: - if (preset_version <= SCI_VERSION_01_VGA_ODD /* || preset_version == SCI_VERSION_AUTODETECT -- subsumed by the above line */) { - resource_error = readResourceMapSCI0(source, detected_version); -#if 0 - if (resource_error >= SCI_ERROR_CRITICAL) { - sciprintf("Resmgr: Error while loading resource map: %s\n", sci_error_types[resource_error]); - if (resource_error == SCI_ERROR_RESMAP_NOT_FOUND) - sciprintf("Running SCI games without a resource map is not supported ATM\n"); - sci_free(mgr); - return NULL; - } - if (resource_error == SCI_ERROR_RESMAP_NOT_FOUND) { - // FIXME: Try reading w/o resource.map - resource_error = SCI_ERROR_NO_RESOURCE_FILES_FOUND; - } - - if (resource_error == SCI_ERROR_NO_RESOURCE_FILES_FOUND) { - // Initialize empty resource manager - mgr->_resourcesNr = 0; - mgr->_resources = 0; // FIXME: Was = (Resource*)sci_malloc(1); - resource_error = 0; - } -#endif + if (_mapVersion < SCI_VERSION_1) + resource_error = readResourceMapSCI0(source); + else + resource_error = readResourceMapSCI1(source, getVolume(source, 0)); + if (resource_error == SCI_ERROR_RESMAP_NOT_FOUND) { + // FIXME: Try reading w/o resource.map + resource_error = SCI_ERROR_NO_RESOURCE_FILES_FOUND; } - if ((preset_version == SCI_VERSION_1_EARLY) || (preset_version == SCI_VERSION_1_LATE) || (preset_version == SCI_VERSION_1_1) || - ((*detected_version == SCI_VERSION_AUTODETECT) && (preset_version == SCI_VERSION_AUTODETECT))) { - resource_error = readResourceMapSCI1(source, getVolume(source, 0), detected_version); - if (resource_error == SCI_ERROR_RESMAP_NOT_FOUND) { - // FIXME: Try reading w/o resource.map - resource_error = SCI_ERROR_NO_RESOURCE_FILES_FOUND; - } - - if (resource_error == SCI_ERROR_NO_RESOURCE_FILES_FOUND) { - // Initialize empty resource manager - _resMap.clear(); - resource_error = 0; - } + if (resource_error == SCI_ERROR_NO_RESOURCE_FILES_FOUND) { + // Initialize empty resource manager + _resMap.clear(); + resource_error = 0; } - - _sciVersion = *detected_version; break; default: break; @@ -414,10 +376,7 @@ void ResourceManager::freeResourceSources(ResourceSource *rss) { } ResourceManager::ResourceManager(int version, int maxMemory) { - int resmap_version = version; - _maxMemory = maxMemory; - _memoryLocked = 0; _memoryLRU = 0; _LRU.clear(); @@ -427,45 +386,28 @@ ResourceManager::ResourceManager(int version, int maxMemory) { _sciVersion = version; addAppropriateSources(); - scanNewSources(&resmap_version, _sources); + _mapVersion = detectMapVersion(); + debug("Detected resource map:%d %s", _mapVersion, sci_version_types[_mapVersion]); + _volVersion = detectVolVersion(); + debug("Detected volume :%d %s", _volVersion, sci_version_types[_volVersion]); + scanNewSources(_sources); if (version == SCI_VERSION_AUTODETECT) - switch (resmap_version) { + switch (_mapVersion) { case SCI_VERSION_0: if (testResource(kResourceTypeVocab, VOCAB_RESOURCE_SCI0_MAIN_VOCAB)) { - version = sci_test_view_type(this); - if (version == SCI_VERSION_01_VGA) { - debug("Resmgr: Detected KQ5 or similar\n"); - } else { - debug("Resmgr: Detected SCI0\n"); - version = SCI_VERSION_0; - } + version = sci_test_view_type(this) ? SCI_VERSION_01_VGA : SCI_VERSION_0; } else if (testResource(kResourceTypeVocab, VOCAB_RESOURCE_SCI1_MAIN_VOCAB)) { version = sci_test_view_type(this); - if (version == SCI_VERSION_01_VGA) { - debug("Resmgr: Detected KQ5 or similar\n"); - } else { - if (testResource(kResourceTypeVocab, 912)) { - debug("Resmgr: Running KQ1 or similar, using SCI0 resource encoding\n"); - version = SCI_VERSION_0; - } else { - version = SCI_VERSION_01; - debug("Resmgr: Detected SCI01\n"); - } + if (version != SCI_VERSION_01_VGA) { + version = testResource(kResourceTypeVocab, 912) ? SCI_VERSION_0 : SCI_VERSION_01; } } else { - version = sci_test_view_type(this); - if (version == SCI_VERSION_01_VGA) { - debug("Resmgr: Detected KQ5 or similar\n"); - } else { - debug("Resmgr: Warning: Could not find vocabulary; assuming SCI0 w/o parser\n"); - version = SCI_VERSION_0; - } + version = sci_test_view_type(this) ? SCI_VERSION_01_VGA : SCI_VERSION_0; } break; case SCI_VERSION_01_VGA_ODD: - version = resmap_version; - debug("Resmgr: Detected Jones/CD or similar\n"); + version = _mapVersion; break; case SCI_VERSION_1: { Resource *res = testResource(kResourceTypeScript, 0); @@ -473,23 +415,40 @@ ResourceManager::ResourceManager(int version, int maxMemory) { _sciVersion = version = SCI_VERSION_1_EARLY; loadResource(res); - if (res->status == SCI_STATUS_NOMALLOC) { - _sciVersion = version = SCI_VERSION_1_LATE; - debug("Resmgr: Detected SCI1 Late"); - } else - debug("Resmgr: Detected SCI1 Early"); + if (res->status == SCI_STATUS_NOMALLOC) + version = SCI_VERSION_1_LATE; break; } case SCI_VERSION_1_1: // No need to handle SCI 1.1 here - it was done in resource_map.cpp version = SCI_VERSION_1_1; - debug("Resmgr: Detected SCI1.1"); break; default: - debug("Resmgr: Warning: While autodetecting: Couldn't determine SCI version"); + version = SCI_VERSION_AUTODETECT; } _sciVersion = version; + // 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_01_VGA: + debug("Resmgr: Detected SCI01VGA - KQ5 or similar"); break; + case SCI_VERSION_01_VGA_ODD: + debug("Resmgr: Detected SCI01VGA - Jones/CD or similar"); break; + case SCI_VERSION_1_EARLY: + debug("Resmgr: Detected SCI1 Early"); break; + case SCI_VERSION_1_LATE: + debug("Resmgr: Detected SCI1 Late"); break; + case SCI_VERSION_1_1: + debug("Resmgr: Detected SCI1.1"); break; + case SCI_VERSION_32: + debug("Resmgr: Couldn't determine SCI version"); break; + default: + debug("Resmgr: Couldn't determine SCI version"); break; + } } ResourceManager::~ResourceManager() { @@ -635,4 +594,128 @@ void ResourceManager::unlockResource(Resource *res, int resnum, ResourceType res freeOldResources(0); } +int ResourceManager::detectMapVersion() { + Common::File file; + byte buff[6]; + ResourceSource *rsrc = _sources; + // looking for extarnal map among sources + while (rsrc) { + if (rsrc->source_type == kSourceExtMap) { + file.open(rsrc->location_name); + break; + } + rsrc = rsrc->next; + } + if (file.isOpen() == false) { + warning("Failed to open resource map file"); + return SCI_VERSION_AUTODETECT; + } + // detection + // SCI0 and SCI01 maps have last 6 bytes set to FF + file.seek(-4, SEEK_END); + uint32 uEnd = file.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)) { + if (getVolume(rsrc, (buff[5] & 0xFC) >> 2) == NULL) + return SCI_VERSION_01_VGA_ODD; + } + return SCI_VERSION_0; + } + // SCI1E/L and some SCI1.1 maps have last directory entry set to 0xFF + // and offset set to filesize + // SCI1 have 6-bytes entries, while SCI1.1 have 5-byte entries + file.seek(1, SEEK_SET); + uint16 off1, off = file.readUint16LE(); + uint16 nEntries = off / 3; + file.seek(1, SEEK_CUR); + file.seek(off - 3, SEEK_SET); + if (file.readByte() == 0xFF && file.readUint16LE() == file.size()) { + file.seek(3, SEEK_SET); + for (int i = 0; i < nEntries; i++) { + file.seek(1, SEEK_CUR); + off1 = file.readUint16LE(); + if ((off1 - off) % 5 && (off1 - off) % 6 == 0) + return SCI_VERSION_1; + if ((off1 - off) % 5 == 0 && (off1 - off) % 6) + return SCI_VERSION_1_1; + off = off1; + } + return SCI_VERSION_1; + } + // late SCI1.1 and SCI32 maps have last directory entry set to 0xFF + // offset set to filesize and 4 more bytes + file.seek(off - 7, SEEK_SET); + if (file.readByte() == 0xFF && file.readUint16LE() == file.size()) + return SCI_VERSION_1_1; // TODO : check if there is a difference between these maps + + return SCI_VERSION_AUTODETECT; +} + +int ResourceManager::detectVolVersion() { + Common::File file; + ResourceSource *rsrc = _sources; + // looking for a volume among sources + while (rsrc) { + if (rsrc->source_type == kSourceVolume) { + file.open(rsrc->location_name); + break; + } + rsrc = rsrc->next; + } + if (file.isOpen() == false) { + warning("Failed to open volume file"); + return SCI_VERSION_AUTODETECT; + } + // SCI0 volume format: {wResId wPacked+4 wUnpacked bCompression bUnknown} = 8 bytes + // SCI1 volume format: {bResType wResNumber wPacked+4 wUnpacked bCompression bUnknown} = 9 bytes + // Try to parse volume with SCI0 scheme to see if it make sense + // Checking 1MB of data should be enough to determine the version + uint16 resId, wPacked, wUnpacked; + byte bCompression; + bool bFailed = false; + while(!file.eos() && !bFailed && file.pos() < 0x100000) { + resId = file.readUint16LE(); + wPacked = file.readUint16LE(); + wUnpacked = file.readUint16LE(); + bCompression = file.readByte(); + if(file.eos()) + break; + if ((bCompression > 4) || (bCompression == 0 && wPacked != wUnpacked + 4) + || (wUnpacked < wPacked - 4)) { + bFailed = true; + break; + } + file.seek(wPacked - 3, SEEK_CUR); + } + if (!bFailed) + return SCI_VERSION_0; + // Check for SCI1/SCI1.1 format + bFailed = false; + uint32 pos; + file.seek(0, SEEK_SET); + while(!file.eos() && !bFailed && file.pos() < 0x100000) { + pos = file.pos(); + file.seek(1, SEEK_CUR); + resId = file.readUint16LE(); + wPacked = file.readUint16LE(); + wUnpacked = file.readUint16LE(); + bCompression = file.readByte(); + if(file.eos()) + break; + if ((bCompression > 20) || (bCompression == 0 && wPacked != wUnpacked + 4) + || (wUnpacked < wPacked - 4)) { + bFailed = true; + break; + } + file.seek(wPacked - 3, SEEK_CUR); + } + if (!bFailed) + return SCI_VERSION_1; + + // TODO: check for more differences between SCI1/SCI1.1/SCI32 resource format + return SCI_VERSION_1_1; +} + } // End of namespace Sci diff --git a/engines/sci/scicore/resource.h b/engines/sci/scicore/resource.h index 1e327e003d..415a89cac8 100644 --- a/engines/sci/scicore/resource.h +++ b/engines/sci/scicore/resource.h @@ -45,9 +45,6 @@ namespace Sci { #define SCI_STATUS_ENQUEUED 2 /* In the LRU queue */ #define SCI_STATUS_LOCKED 3 /* Allocated and in use */ -#define SCI_RES_FILE_NR_PATCH -1 /* Resource was read from a patch file rather than from a resource */ - - /*** INITIALIZATION RESULT TYPES ***/ #define SCI_ERROR_IO_ERROR 1 #define SCI_ERROR_EMPTY_OBJECT 2 @@ -180,6 +177,8 @@ public: class ResourceManager { public: int _sciVersion; /* SCI resource version to use */ + int _mapVersion; // RESOURCE.MAP version + int _volVersion; // RESOURCE.0xx version /** * Creates a new FreeSCI resource manager. @@ -222,7 +221,7 @@ public: * used during startup. May be NULL. * @return One of SCI_ERROR_*. */ - int scanNewSources(int *detected_version, ResourceSource *source); + int scanNewSources(ResourceSource *source); //! Looks up a resource's data /** @param type: The resource type to look for @@ -270,20 +269,20 @@ protected: void freeOldResources(int last_invulnerable); /**--- Resource map decoding functions ---*/ + int detectMapVersion(); + int detectVolVersion(); /* Reads the SCI0 resource.map file from a local directory ** Returns : (int) 0 on success, an SCI_ERROR_* code otherwise */ - int readResourceMapSCI0(ResourceSource *map, int *sci_version); + int readResourceMapSCI0(ResourceSource *map); /* Reads the SCI1 resource.map file from a local directory ** Returns : (int) 0 on success, an SCI_ERROR_* code otherwise */ - int readResourceMapSCI1(ResourceSource *map, ResourceSource *vol, int *sci_version); + int readResourceMapSCI1(ResourceSource *map, ResourceSource *vol); - int isSCI10or11(int *types); - int detectOddSCI01(Common::File &file); - int resReadEntry(ResourceSource *map, byte *buf, Resource *res, int sci_version); + int resReadEntry(ResourceSource *map, byte *buf, Resource *res); ResourceType resTypeSCI1(int ofs, int *types, ResourceType lastrt); int parseHeaderSCI1(Common::ReadStream &stream, int *types, ResourceType *lastrt); diff --git a/engines/sci/scicore/resource_map.cpp b/engines/sci/scicore/resource_map.cpp index 7956ce1c41..bce10dd0db 100644 --- a/engines/sci/scicore/resource_map.cpp +++ b/engines/sci/scicore/resource_map.cpp @@ -88,44 +88,13 @@ namespace Sci { | (((bytes)[3]) << 9) \ | (((bytes)[2]) << 1)) -int ResourceManager::detectOddSCI01(Common::File &file) { - byte buf[6]; - int files_ok = 1; - int fsize, resource_nr, read_ok; - char filename[14]; - - fsize = file.size(); - if (fsize < 0) { - perror("Error occured while trying to get filesize of resource.map"); - return SCI_ERROR_RESMAP_NOT_FOUND; - } - - resource_nr = fsize / SCI0_RESMAP_ENTRIES_SIZE; - - while (resource_nr-- > 1) { - read_ok = file.read(&buf, SCI0_RESMAP_ENTRIES_SIZE); - - if (read_ok) { - sprintf(filename, "resource.%03i", SCI0_RESFILE_GET_FILE(buf + 2)); - if (!Common::File::exists(filename)) { - files_ok = 0; - break; - } - } - } - - file.seek(0, SEEK_SET); - - return files_ok; -} - -int ResourceManager::resReadEntry(ResourceSource *map, byte *buf, Resource *res, int sci_version) { - res->id = READ_LE_UINT16(buf);//buf[0] | (buf[1] << 8); +int ResourceManager::resReadEntry(ResourceSource *map, byte *buf, Resource *res) { + res->id = READ_LE_UINT16(buf); res->type = (ResourceType)SCI0_RESID_GET_TYPE(buf); res->number = SCI0_RESID_GET_NUMBER(buf); res->status = SCI_STATUS_NOMALLOC; - if (sci_version == SCI_VERSION_01_VGA_ODD) { + if (_mapVersion == SCI_VERSION_01_VGA_ODD) { res->source = getVolume(map, SCI01V_RESFILE_GET_FILE(buf + 2)); res->file_offset = SCI01V_RESFILE_GET_OFFSET(buf + 2); @@ -195,9 +164,7 @@ int ResourceManager::parseHeaderSCI1(Common::ReadStream &stream, int *types, Res return size; } - - -int ResourceManager::readResourceMapSCI0(ResourceSource *map, int *sci_version) { +int ResourceManager::readResourceMapSCI0(ResourceSource *map) { int fsize; Common::File file; Resource *res, res1; @@ -208,43 +175,7 @@ int ResourceManager::readResourceMapSCI0(ResourceSource *map, int *sci_version) if (!file.open(map->location_name)) return SCI_ERROR_RESMAP_NOT_FOUND; - file.read(&buf, 4); - - /* Theory: An SCI1 map file begins with an index that allows us to seek quickly - to a particular resource type. The entries are three bytes long; one byte - resource type, two bytes start position and so on. - The below code therefore tests for three things: - - Is the first resource type 'view'? - Do those entries start at an offset that is an exact multiple of the - index entry size? - Is the second resource type 'pic'? - - This requires that a given game has both views and pics, - a safe assumption usually, except in message.map and room-specific - (audio) map files, neither of which SCI0 has. - - */ - - if ((buf[0] == 0x80) && (buf[1] % 3 == 0) && (buf[3] == 0x81)) { - return SCI_ERROR_INVALID_RESMAP_ENTRY; - } - file.seek(0, SEEK_SET); - - switch (detectOddSCI01(file)) { - case 0 : // Odd SCI01 - if (*sci_version == SCI_VERSION_AUTODETECT) - *sci_version = SCI_VERSION_01_VGA_ODD; - break; - case 1 : // SCI0 or normal SCI01 - if (*sci_version == SCI_VERSION_AUTODETECT) - *sci_version = SCI_VERSION_0; - break; - default : // Neither, or error occurred - return SCI_ERROR_RESMAP_NOT_FOUND; - } - fsize = file.size(); if (fsize < 0) { perror("Error occured while trying to get filesize of resource.map"); @@ -264,7 +195,7 @@ int ResourceManager::readResourceMapSCI0(ResourceSource *map, int *sci_version) if(buf[5] == 0xff) break; - if (resReadEntry(map, buf, &res1, *sci_version)) + if (resReadEntry(map, buf, &res1)) return SCI_ERROR_RESMAP_NOT_FOUND; uint32 resId = RESOURCE_HASH(res1.type, res1.number); // adding a new resource @@ -291,40 +222,7 @@ int ResourceManager::readResourceMapSCI0(ResourceSource *map, int *sci_version) return 0; } -#define TEST fprintf(stderr, "OK in line %d\n", __LINE__); - -int ResourceManager::isSCI10or11(int *types) { - int this_restype = 0; - int next_restype = 1; - - while (next_restype <= kResourceTypeHeap) { - int could_be_10 = 0; - int could_be_11 = 0; - - while (types[this_restype] == 0) { - this_restype++; - next_restype++; - } - - while (types[next_restype] == 0) - next_restype++; - - could_be_10 = ((types[next_restype] - types[this_restype]) % SCI1_RESMAP_ENTRIES_SIZE) == 0; - could_be_11 = ((types[next_restype] - types[this_restype]) % SCI11_RESMAP_ENTRIES_SIZE) == 0; - - if (could_be_10 && !could_be_11) - return SCI_VERSION_1; - if (could_be_11 && !could_be_10) - return SCI_VERSION_1_1; - - this_restype++; - next_restype++; - } - - return SCI_VERSION_AUTODETECT; -} - -int ResourceManager::readResourceMapSCI1(ResourceSource *map, ResourceSource *vol, int *sci_version) { +int ResourceManager::readResourceMapSCI1(ResourceSource *map, ResourceSource *vol) { int fsize; Common::File file; int resource_nr; @@ -334,7 +232,6 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map, ResourceSource *vo byte buf[SCI1_RESMAP_ENTRIES_SIZE]; ResourceType lastrt; int entrysize; - int entry_size_selector; if (!file.open(map->location_name)) return SCI_ERROR_RESMAP_NOT_FOUND; @@ -345,16 +242,7 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map, ResourceSource *vo return SCI_ERROR_INVALID_RESMAP_ENTRY; } - entry_size_selector = isSCI10or11(types); - if (*sci_version == SCI_VERSION_AUTODETECT) - *sci_version = entry_size_selector; - - if (*sci_version == SCI_VERSION_AUTODETECT) { // That didn't help - sciprintf("Unable to detect resource map version\n"); - return SCI_ERROR_NO_RESOURCE_FILES_FOUND; - } - - entrysize = entry_size_selector == SCI_VERSION_1_1 ? SCI11_RESMAP_ENTRIES_SIZE : SCI1_RESMAP_ENTRIES_SIZE; + entrysize = _mapVersion == SCI_VERSION_1_1 ? SCI11_RESMAP_ENTRIES_SIZE : SCI1_RESMAP_ENTRIES_SIZE; fsize = file.size(); if (fsize < 0) { @@ -388,7 +276,7 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map, ResourceSource *vo res->number = number; res->id = res->number | (res->type << 16); // only 1st source would be used when loading resource - if (entry_size_selector < SCI_VERSION_1_1) { + if (_mapVersion < SCI_VERSION_1_1) { res->source = getVolume(map, SCI1_RESFILE_GET_FILE(buf)); res->file_offset = SCI1_RESFILE_GET_OFFSET(buf); } else { -- cgit v1.2.3