diff options
| -rw-r--r-- | engines/sci/detection.cpp | 21 | ||||
| -rw-r--r-- | engines/sci/engine/kernel.cpp | 6 | ||||
| -rw-r--r-- | engines/sci/engine/kernel.h | 7 | ||||
| -rw-r--r-- | engines/sci/resource.cpp | 173 | ||||
| -rw-r--r-- | engines/sci/resource.h | 16 | 
5 files changed, 178 insertions, 45 deletions
diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp index 959f18739b..ee9fd5fb18 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 09d342b7fe..223e7fc1e9 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 8be51549f6..997cdaea77 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 2ca198954a..9b9c9ee26c 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<ResourceId> *resources = listResources(kResourceTypeMap);  	Common::List<ResourceId>::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<ResourceSource *>::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 8ab740f463..4250225ffe 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();  	/** @@ -306,6 +309,11 @@ protected:  	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();  | 
