Map/volume loader and version detection functions cleaned and reworked.

Autodetection still misdetects some SCI1/SCI1.1 games though.

svn-id: r39169
This commit is contained in:
Greg Frieger 2009-03-07 00:59:38 +00:00
parent d262073193
commit f028f53d3e
3 changed files with 191 additions and 221 deletions

View File

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

View File

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

View File

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