Started rewriting the SCI engine to use FSNode instead of file names. This is the proper solution for removing the hack in the fallback detector, but it still needs work. Also, reduced the things needed to be initialized a bit, so that the detection is a bit faster

svn-id: r43510
This commit is contained in:
Filippos Karapetis 2009-08-18 14:10:31 +00:00
parent 766cdac9f3
commit db0cd620f6
5 changed files with 178 additions and 45 deletions

View File

@ -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;

View File

@ -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();

View File

@ -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(); }

View File

@ -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;

View File

@ -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();