aboutsummaryrefslogtreecommitdiff
path: root/engines/tinsel
diff options
context:
space:
mode:
Diffstat (limited to 'engines/tinsel')
-rw-r--r--engines/tinsel/detection.cpp158
-rw-r--r--engines/tinsel/drives.cpp54
-rw-r--r--engines/tinsel/drives.h12
-rw-r--r--engines/tinsel/handle.cpp4
-rw-r--r--engines/tinsel/sound.cpp2
-rw-r--r--engines/tinsel/sound.h3
-rw-r--r--engines/tinsel/strres.cpp3
-rw-r--r--engines/tinsel/tinsel.cpp3
8 files changed, 234 insertions, 5 deletions
diff --git a/engines/tinsel/detection.cpp b/engines/tinsel/detection.cpp
index 4028ea2245..e02eec44a3 100644
--- a/engines/tinsel/detection.cpp
+++ b/engines/tinsel/detection.cpp
@@ -27,6 +27,7 @@
#include "engines/advancedDetector.h"
#include "common/file.h"
+#include "common/md5.h"
#include "common/savefile.h"
#include "tinsel/cursor.h"
@@ -475,6 +476,7 @@ public:
}
virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
+ const ADGameDescription *fallbackDetect(const Common::FSList &fslist) const;
virtual bool hasFeature(MetaEngineFeature f) const;
virtual SaveStateList listSaves(const char *target) const;
@@ -542,6 +544,162 @@ bool TinselMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGa
return gd != 0;
}
+struct SizeMD5 {
+ int size;
+ char md5[32+1];
+};
+typedef Common::HashMap<Common::String, SizeMD5, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> SizeMD5Map;
+typedef Common::HashMap<Common::String, Common::FSNode, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap;
+typedef Common::Array<const ADGameDescription*> ADGameDescList;
+
+/**
+ * Fallback detection scans the list of Discworld 2 targets to see if it can detect an installation
+ * where the files haven't been renamed (i.e. don't have the '1' just before the extension)
+ */
+const ADGameDescription *TinselMetaEngine::fallbackDetect(const Common::FSList &fslist) const {
+ Common::String extra;
+ FileMap allFiles;
+ SizeMD5Map filesSizeMD5;
+
+ const ADGameFileDescription *fileDesc;
+ const Tinsel::TinselGameDescription *g;
+
+ if (fslist.empty())
+ return NULL;
+
+ // First we compose a hashmap of all files in fslist.
+ // Includes nifty stuff like removing trailing dots and ignoring case.
+ for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
+ if (file->isDirectory()) {
+ if (!scumm_stricmp(file->getName().c_str(), "dw2")) {
+ // Probably Discworld 2 subfolder on CD, so add it's contents as well
+ Common::FSList files;
+ if (file->getChildren(files, Common::FSNode::kListAll)) {
+ Common::FSList::const_iterator file2;
+ for (file2 = files.begin(); file2 != files.end(); ++file2) {
+ if (file2->isDirectory())
+ continue;
+
+ Common::String fname = file2->getName();
+ allFiles[fname] = *file2;
+ }
+ }
+ }
+ continue;
+ }
+
+ Common::String tstr = file->getName();
+
+ allFiles[tstr] = *file; // Record the presence of this file
+ }
+
+ // Check which files are included in some dw2 ADGameDescription *and* present
+ // in fslist without a '1' suffix character. Compute MD5s and file sizes for these files.
+ for (g = &Tinsel::gameDescriptions[0]; g->desc.gameid != 0; ++g) {
+ if (strcmp(g->desc.gameid, "dw2") != 0)
+ continue;
+
+ for (fileDesc = g->desc.filesDescriptions; fileDesc->fileName; fileDesc++) {
+ // Get the next filename, stripping off any '1' suffix character
+ char tempFilename[50];
+ strcpy(tempFilename, fileDesc->fileName);
+ char *pOne = strchr(tempFilename, '1');
+ if (pOne) strcpy(pOne, pOne + 1);
+
+ Common::String fname(tempFilename);
+ if (allFiles.contains(fname) && !filesSizeMD5.contains(fname)) {
+ SizeMD5 tmp;
+ if (!md5_file_string(allFiles[fname], tmp.md5, detectionParams.md5Bytes))
+ tmp.md5[0] = 0;
+
+ Common::File testFile;
+ if (testFile.open(allFiles[fname]))
+ tmp.size = (int32)testFile.size();
+ else
+ tmp.size = -1;
+
+ filesSizeMD5[fname] = tmp;
+ }
+ }
+ }
+
+ ADGameDescList matched;
+ int maxFilesMatched = 0;
+ bool gotAnyMatchesWithAllFiles = false;
+
+ // MD5 based matching
+ uint i;
+ for (i = 0, g = &Tinsel::gameDescriptions[0]; g->desc.gameid != 0; ++g) {
+ if (strcmp(g->desc.gameid, "dw2") != 0)
+ continue;
+
+ bool fileMissing = false;
+
+ if ((detectionParams.flags & kADFlagUseExtraAsHint) && !extra.empty() && g->desc.extra != extra)
+ continue;
+
+ bool allFilesPresent = true;
+
+ // Try to match all files for this game
+ for (fileDesc = g->desc.filesDescriptions; fileDesc->fileName; fileDesc++) {
+ // Get the next filename, stripping off any '1' suffix character
+ char tempFilename[50];
+ strcpy(tempFilename, fileDesc->fileName);
+ char *pOne = strchr(tempFilename, '1');
+ if (pOne) strcpy(pOne, pOne + 1);
+
+ Common::String tstr(tempFilename);
+
+ if (!filesSizeMD5.contains(tstr)) {
+ fileMissing = true;
+ allFilesPresent = false;
+ break;
+ }
+
+ if (fileDesc->md5 != NULL && 0 != strcmp(fileDesc->md5, filesSizeMD5[tstr].md5)) {
+ fileMissing = true;
+ break;
+ }
+
+ if (fileDesc->fileSize != -1 && fileDesc->fileSize != filesSizeMD5[tstr].size) {
+ fileMissing = true;
+ break;
+ }
+ }
+
+ if (allFilesPresent)
+ gotAnyMatchesWithAllFiles = true;
+
+ if (!fileMissing) {
+ // Count the number of matching files. Then, only keep those
+ // entries which match a maximal amount of files.
+ int curFilesMatched = 0;
+ for (fileDesc = g->desc.filesDescriptions; fileDesc->fileName; fileDesc++)
+ curFilesMatched++;
+
+ if (curFilesMatched > maxFilesMatched) {
+ maxFilesMatched = curFilesMatched;
+
+ for (uint j = 0; j < matched.size();) {
+ if (matched[j]->flags & ADGF_KEEPMATCH)
+ ++j;
+ else
+ matched.remove_at(j);
+ }
+ matched.push_back((const ADGameDescription *)g);
+ } else if (curFilesMatched == maxFilesMatched) {
+ matched.push_back((const ADGameDescription *)g);
+ }
+ }
+ }
+
+ // We didn't find a match
+ if (matched.empty())
+ return NULL;
+
+ return *matched.begin();
+}
+
int TinselMetaEngine::getMaximumSaveSlot() const { return 99; }
void TinselMetaEngine::removeSaveState(const char *target, int slot) const {
diff --git a/engines/tinsel/drives.cpp b/engines/tinsel/drives.cpp
index a674211f66..6803ababa2 100644
--- a/engines/tinsel/drives.cpp
+++ b/engines/tinsel/drives.cpp
@@ -24,6 +24,7 @@
* CD/drive handling functions
*/
+#include "gui/message.h"
#include "tinsel/drives.h"
#include "tinsel/scene.h"
#include "tinsel/tinsel.h"
@@ -32,7 +33,7 @@
namespace Tinsel {
-static char currentCD = '1';
+char currentCD = '1';
static uint32 cdFlags[] = { fCd1, fCd2, fCd3, fCd4, fCd5, fCd6, fCd7, fCd8 };
static bool bChangingCD = false;
@@ -93,9 +94,29 @@ int GetCD(int flags) {
return cd;
}
+static uint32 lastTime = 0;
+extern LANGUAGE sampleLanguage;
+
void DoCdChange(void) {
- if (bChangingCD) {
+ if (bChangingCD && (g_system->getMillis() > (lastTime + 1000))) {
+ lastTime = g_system->getMillis();
_vm->_sound->closeSampleStream();
+
+ // Use the filesize of the sample file to determine, for Discworld 2, which CD it is
+ if (TinselV2) {
+ TinselFile f;
+ if (!f.open(_vm->getSampleFile(sampleLanguage)))
+ // No CD present
+ return;
+
+ char sampleCdNumber = (f.size() >= (200 * 1024 * 1024)) ? '1' : '2';
+
+ f.close();
+
+ if (currentCD != sampleCdNumber)
+ return;
+ }
+
_vm->_sound->openSampleFiles();
ChangeLanguage(TextLanguage());
bChangingCD = false;
@@ -126,4 +147,33 @@ bool GotoCD(void) {
return true;
}
+bool TinselFile::_warningShown = false;
+
+bool TinselFile::open(const Common::String &filename) {
+ if (Common::File::open(filename)) {
+ // If it's the sample file, strip off the CD number from the filename
+
+
+ return true;
+ }
+
+ if (!TinselV2)
+ return false;
+
+ // Check if the file being requested is the *1.* or *2.* files
+ const char *fname = filename.c_str();
+ const char *p = strchr(fname, '1');
+ if (!p)
+ p = strchr(fname, '2');
+ if (!p || (*(p + 1) != '.'))
+ return false;
+
+ // Form a filename without the CD number character
+ char newFilename[50];
+ strncpy(newFilename, fname, p - fname);
+ strcpy(newFilename + (p - fname), p + 1);
+
+ return Common::File::open(newFilename);
+}
+
} // end of namespace Tinsel
diff --git a/engines/tinsel/drives.h b/engines/tinsel/drives.h
index 4441466f82..339f4384d0 100644
--- a/engines/tinsel/drives.h
+++ b/engines/tinsel/drives.h
@@ -27,6 +27,7 @@
#ifndef TINSEL_DRIVES_H
#define TINSEL_DRIVES_H
+#include "common/file.h"
#include "tinsel/dw.h"
#include "tinsel/coroutine.h"
@@ -44,6 +45,8 @@ namespace Tinsel {
#define fAllCds (fCd1|fCd2|fCd3|fCd4|fCd5|fCd6|fCd7|fCd8)
+extern char currentCD;
+
void DoCdChange(void);
void CdCD(CORO_PARAM);
@@ -58,6 +61,15 @@ void SetNextCD(int cdNumber);
bool GotoCD(void);
+class TinselFile: public Common::File {
+private:
+ static bool _warningShown;
+public:
+ virtual bool open(const Common::String &filename);
+ char getCdNumber();
+};
+
+
} // end of namespace Tinsel
#endif /* TINSEL_DRIVES_H */
diff --git a/engines/tinsel/handle.cpp b/engines/tinsel/handle.cpp
index f3ef9b3e4b..cfe91f7216 100644
--- a/engines/tinsel/handle.cpp
+++ b/engines/tinsel/handle.cpp
@@ -420,6 +420,10 @@ byte *LockMem(SCNHANDLE offset) {
if (pH->pNode->pBaseAddr == NULL)
error("Out of memory");
+ if (TinselV2) {
+ SetCD(pH->flags2 & fAllCds);
+ CdCD(nullContext);
+ }
LoadFile(pH, true);
// make sure address is valid
diff --git a/engines/tinsel/sound.cpp b/engines/tinsel/sound.cpp
index 5c4f74e0d2..54178a898a 100644
--- a/engines/tinsel/sound.cpp
+++ b/engines/tinsel/sound.cpp
@@ -399,7 +399,7 @@ void SoundManager::openSampleFiles(void) {
if (_vm->getFeatures() & GF_FLOPPY || _vm->getFeatures() & GF_DEMO)
return;
- Common::File f;
+ TinselFile f;
if (_sampleIndex)
// already allocated
diff --git a/engines/tinsel/sound.h b/engines/tinsel/sound.h
index 8b969a5f48..15919f8ec5 100644
--- a/engines/tinsel/sound.h
+++ b/engines/tinsel/sound.h
@@ -34,6 +34,7 @@
#include "tinsel/dw.h"
#include "tinsel/tinsel.h"
+#include "tinsel/drives.h"
namespace Tinsel {
@@ -87,7 +88,7 @@ protected:
long _sampleIndexLen;
/** file stream for sample file */
- Common::File _sampleStream;
+ TinselFile _sampleStream;
bool offscreenChecks(int x, int &y);
int8 getPan(int x);
diff --git a/engines/tinsel/strres.cpp b/engines/tinsel/strres.cpp
index bbf02301db..b8a92572eb 100644
--- a/engines/tinsel/strres.cpp
+++ b/engines/tinsel/strres.cpp
@@ -25,6 +25,7 @@
*/
#include "tinsel/dw.h"
+#include "tinsel/drives.h"
#include "tinsel/sound.h"
#include "tinsel/strres.h"
#include "common/file.h"
@@ -80,7 +81,7 @@ LANGUAGE textLanguage, sampleLanguage = TXT_ENGLISH;
* @param newLang The new language
*/
void ChangeLanguage(LANGUAGE newLang) {
- Common::File f;
+ TinselFile f;
uint32 textLen = 0; // length of buffer
textLanguage = newLang;
diff --git a/engines/tinsel/tinsel.cpp b/engines/tinsel/tinsel.cpp
index 8fe0446932..a3760272b1 100644
--- a/engines/tinsel/tinsel.cpp
+++ b/engines/tinsel/tinsel.cpp
@@ -862,6 +862,9 @@ TinselEngine::TinselEngine(OSystem *syst, const TinselGameDescription *gameDesc)
_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
+ // Add DW2 subfolder to search path in case user is running directly from the CDs
+ Common::File::addDefaultDirectory(_gameDataDir.getChild("dw2"));
+
const GameSettings *g;
const char *gameid = ConfMan.get("gameid").c_str();