aboutsummaryrefslogtreecommitdiff
path: root/engines/tinsel
diff options
context:
space:
mode:
Diffstat (limited to 'engines/tinsel')
-rw-r--r--engines/tinsel/detection.cpp8
-rw-r--r--engines/tinsel/detection_tables.h144
-rw-r--r--engines/tinsel/dialogs.cpp49
-rw-r--r--engines/tinsel/dialogs.h2
-rw-r--r--engines/tinsel/drives.cpp2
-rw-r--r--engines/tinsel/handle.cpp12
-rw-r--r--engines/tinsel/music.cpp229
-rw-r--r--engines/tinsel/music.h6
-rw-r--r--engines/tinsel/pcode.cpp43
-rw-r--r--engines/tinsel/saveload.cpp50
-rw-r--r--engines/tinsel/scene.h6
-rw-r--r--engines/tinsel/sound.cpp6
-rw-r--r--engines/tinsel/tinlib.cpp25
-rw-r--r--engines/tinsel/tinsel.h28
14 files changed, 385 insertions, 225 deletions
diff --git a/engines/tinsel/detection.cpp b/engines/tinsel/detection.cpp
index 0f662e22bd..2e4be33e53 100644
--- a/engines/tinsel/detection.cpp
+++ b/engines/tinsel/detection.cpp
@@ -63,6 +63,14 @@ uint16 TinselEngine::getVersion() const {
return _gameDescription->version;
}
+bool TinselEngine::getIsADGFDemo() const {
+ return (bool)(_gameDescription->desc.flags & ADGF_DEMO);
+}
+
+bool TinselEngine::isCD() const {
+ return (bool)(_gameDescription->desc.flags & ADGF_CD);
+}
+
} // End of namespace Tinsel
static const PlainGameDescriptor tinselGames[] = {
diff --git a/engines/tinsel/detection_tables.h b/engines/tinsel/detection_tables.h
index be44b1c462..631c2dce14 100644
--- a/engines/tinsel/detection_tables.h
+++ b/engines/tinsel/detection_tables.h
@@ -47,7 +47,7 @@ static const TinselGameDescription gameDescriptions[] = {
},
GID_DW1,
0,
- GF_DEMO,
+ 0,
TINSEL_V0,
},
@@ -61,12 +61,12 @@ static const TinselGameDescription gameDescriptions[] = {
},
Common::EN_ANY,
Common::kPlatformPC,
- ADGF_DEMO,
+ ADGF_DEMO | ADGF_CD,
GUIO0()
},
GID_DW1,
0,
- GF_CD,
+ 0,
TINSEL_V1,
},
#if 0
@@ -81,12 +81,12 @@ static const TinselGameDescription gameDescriptions[] = {
},
Common::EN_ANY,
Common::kPlatformMacintosh,
- ADGF_DEMO,
+ ADGF_DEMO | ADGF_CD,
GUIO0()
},
GID_DW1,
0,
- GF_CD | GF_SCNFILES | GF_BIG_ENDIAN,
+ GF_SCNFILES,
TINSEL_V1,
},
#endif
@@ -110,7 +110,7 @@ static const TinselGameDescription gameDescriptions[] = {
},
GID_DW1,
0,
- GF_FLOPPY | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
+ GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
@@ -133,7 +133,7 @@ static const TinselGameDescription gameDescriptions[] = {
},
GID_DW1,
0,
- GF_FLOPPY | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
+ GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
@@ -156,7 +156,7 @@ static const TinselGameDescription gameDescriptions[] = {
},
GID_DW1,
0,
- GF_FLOPPY | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
+ GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
@@ -179,7 +179,7 @@ static const TinselGameDescription gameDescriptions[] = {
},
GID_DW1,
0,
- GF_FLOPPY | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
+ GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
@@ -195,7 +195,7 @@ static const TinselGameDescription gameDescriptions[] = {
},
GID_DW1,
0,
- GF_FLOPPY | GF_ENHANCED_AUDIO_SUPPORT,
+ GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
@@ -214,7 +214,42 @@ static const TinselGameDescription gameDescriptions[] = {
},
GID_DW1,
0,
- GF_CD | GF_ENHANCED_AUDIO_SUPPORT,
+ GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ { // Polish fan translation CD V1 version, with *.gra files (same as the floppy one, with english.smp)
+ {
+ "dw",
+ "CD",
+ {
+ {"dw.gra", 0, "ef05bbd2a754bd11a2e87bcd84ab5ccf", 781864},
+ {"english.smp", 0, NULL, -1},
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_CD,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ { // Polish fan translaction floppy V1 version, with *.gra files
+ {
+ "dw",
+ "Floppy",
+ AD_ENTRY1s("dw.gra", "ef05bbd2a754bd11a2e87bcd84ab5ccf", 781864),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ GID_DW1,
+ 0,
+ GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
@@ -236,7 +271,7 @@ static const TinselGameDescription gameDescriptions[] = {
},
GID_DW1,
0,
- GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
+ GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
@@ -261,7 +296,7 @@ static const TinselGameDescription gameDescriptions[] = {
},
GID_DW1,
0,
- GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
+ GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
@@ -280,12 +315,12 @@ static const TinselGameDescription gameDescriptions[] = {
},
Common::DE_DEU,
Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
+ ADGF_DROPLANGUAGE,
GUIO0()
},
GID_DW1,
0,
- GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
+ GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
{
@@ -308,7 +343,7 @@ static const TinselGameDescription gameDescriptions[] = {
},
GID_DW1,
0,
- GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
+ GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
{
@@ -331,7 +366,7 @@ static const TinselGameDescription gameDescriptions[] = {
},
GID_DW1,
0,
- GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
+ GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
@@ -351,7 +386,7 @@ static const TinselGameDescription gameDescriptions[] = {
},
GID_DW1,
0,
- GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
+ GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
@@ -371,7 +406,7 @@ static const TinselGameDescription gameDescriptions[] = {
},
GID_DW1,
0,
- GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
+ GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
@@ -390,14 +425,14 @@ static const TinselGameDescription gameDescriptions[] = {
},
GID_DW1,
0,
- GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
+ GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
{ // multilanguage PSX demo
{
"dw",
- "CD demo",
+ "CD Demo",
{
{"french.txt", 0, "e7020d35f58d0d187052ac406d86cc87", 273914},
{"german.txt", 0, "52f0a01e0ff0d340b02a36fd5109d705", 263942},
@@ -408,12 +443,12 @@ static const TinselGameDescription gameDescriptions[] = {
},
Common::EN_ANY,
Common::kPlatformPSX,
- ADGF_DEMO,
+ ADGF_CD | ADGF_DEMO,
GUIO0()
},
GID_DW1,
0,
- GF_CD | GF_SCNFILES,
+ GF_SCNFILES,
TINSEL_V1,
},
@@ -434,7 +469,7 @@ static const TinselGameDescription gameDescriptions[] = {
},
GID_DW1,
0,
- GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
+ GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
#endif
@@ -456,7 +491,7 @@ static const TinselGameDescription gameDescriptions[] = {
},
GID_DW1,
0,
- GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT | GF_BIG_ENDIAN,
+ GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
@@ -475,7 +510,7 @@ static const TinselGameDescription gameDescriptions[] = {
},
GID_DW1,
0,
- GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT | GF_ALT_MIDI,
+ GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT | GF_ALT_MIDI,
TINSEL_V1,
},
@@ -496,11 +531,32 @@ static const TinselGameDescription gameDescriptions[] = {
},
GID_DW1,
0,
- GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
+ GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ { // Polish fan translaction Discworld 1
+ {
+ "dw",
+ "CD",
+ {
+ {"dw.scn", 0, "fa169d2c98660215ebd84b49c1899eef", 776396},
+ {"english.txt", 0, "c1a53eb7ec812689dab70e2bb22cf2ab", 224151},
+ {"english.smp", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::PL_POL,
+ Common::kPlatformPC,
+ ADGF_CD,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
- { // English DW2 demo
+ { // English Discworld 2 demo
{
"dw2",
"Demo",
@@ -511,12 +567,12 @@ static const TinselGameDescription gameDescriptions[] = {
},
Common::EN_ANY,
Common::kPlatformPC,
- ADGF_DEMO,
+ ADGF_DEMO | ADGF_CD,
GUIO1(GUIO_NOASPECT)
},
GID_DW2,
0,
- GF_CD | GF_SCNFILES | GF_DEMO,
+ GF_SCNFILES,
TINSEL_V2,
},
@@ -531,12 +587,12 @@ static const TinselGameDescription gameDescriptions[] = {
},
Common::EN_GRB,
Common::kPlatformPC,
- ADGF_NO_FLAGS,
+ ADGF_CD,
GUIO1(GUIO_NOASPECT)
},
GID_DW2,
0,
- GF_CD | GF_SCNFILES,
+ GF_SCNFILES,
TINSEL_V2,
},
@@ -551,12 +607,12 @@ static const TinselGameDescription gameDescriptions[] = {
},
Common::EN_USA,
Common::kPlatformPC,
- ADGF_NO_FLAGS,
+ ADGF_CD,
GUIO1(GUIO_NOASPECT)
},
GID_DW2,
0,
- GF_CD | GF_SCNFILES,
+ GF_SCNFILES,
TINSEL_V2,
},
@@ -571,12 +627,12 @@ static const TinselGameDescription gameDescriptions[] = {
},
Common::FR_FRA,
Common::kPlatformPC,
- ADGF_NO_FLAGS,
+ ADGF_CD,
GUIO1(GUIO_NOASPECT)
},
GID_DW2,
0,
- GF_CD | GF_SCNFILES,
+ GF_SCNFILES,
TINSEL_V2,
},
@@ -591,12 +647,12 @@ static const TinselGameDescription gameDescriptions[] = {
},
Common::DE_DEU,
Common::kPlatformPC,
- ADGF_NO_FLAGS,
+ ADGF_CD,
GUIO1(GUIO_NOASPECT)
},
GID_DW2,
0,
- GF_CD | GF_SCNFILES,
+ GF_SCNFILES,
TINSEL_V2,
},
@@ -612,12 +668,12 @@ static const TinselGameDescription gameDescriptions[] = {
},
Common::IT_ITA,
Common::kPlatformPC,
- ADGF_NO_FLAGS,
+ ADGF_CD,
GUIO1(GUIO_NOASPECT)
},
GID_DW2,
0,
- GF_CD | GF_SCNFILES,
+ GF_SCNFILES,
TINSEL_V2,
},
{
@@ -632,12 +688,12 @@ static const TinselGameDescription gameDescriptions[] = {
},
Common::ES_ESP,
Common::kPlatformPC,
- ADGF_NO_FLAGS,
+ ADGF_CD,
GUIO1(GUIO_NOASPECT)
},
GID_DW2,
0,
- GF_CD | GF_SCNFILES,
+ GF_SCNFILES,
TINSEL_V2,
},
@@ -653,12 +709,12 @@ static const TinselGameDescription gameDescriptions[] = {
},
Common::RU_RUS,
Common::kPlatformPC,
- ADGF_NO_FLAGS,
+ ADGF_CD,
GUIO1(GUIO_NOASPECT)
},
GID_DW2,
0,
- GF_CD | GF_SCNFILES,
+ GF_SCNFILES,
TINSEL_V2,
},
diff --git a/engines/tinsel/dialogs.cpp b/engines/tinsel/dialogs.cpp
index fbe9e8d1f6..56ee2ea752 100644
--- a/engines/tinsel/dialogs.cpp
+++ b/engines/tinsel/dialogs.cpp
@@ -753,6 +753,11 @@ static CONFBOX t1RestartBox[] = {
#endif
};
+static CONFBOX t1RestartBoxPSX[] = {
+ { AAGBUT, INITGAME, TM_NONE, NULL, USE_POINTER, 122, 48, 23, 19, NULL, IX1_TICK1 },
+ { AAGBUT, CLOSEWIN, TM_NONE, NULL, USE_POINTER, 82, 48, 23, 19, NULL, IX1_CROSS1 }
+};
+
static CONFBOX t2RestartBox[] = {
{ AAGBUT, INITGAME, TM_NONE, NULL, 0, 140, 78, BW, BH, NULL, IX2_TICK1 },
{ AAGBUT, CLOSEWIN, TM_NONE, NULL, 0, 60, 78, BW, BH, NULL, IX2_CROSS1 }
@@ -763,10 +768,10 @@ static CONFINIT t1ciRestart = { 6, 2, 72, 53, false, t1RestartBox, ARRAYSIZE(t1R
#else
static CONFINIT t1ciRestart = { 4, 2, 98, 53, false, t1RestartBox, ARRAYSIZE(t1RestartBox), SIX_RESTART_HEADING };
#endif
+static CONFINIT t1ciRestartPSX = { 8, 2, 46, 53, false, t1RestartBoxPSX, ARRAYSIZE(t1RestartBoxPSX), SIX_RESTART_HEADING };
static CONFINIT t2ciRestart = { 4, 2, 196, 53, false, t2RestartBox, sizeof(t2RestartBox)/sizeof(CONFBOX), SS_RESTART_HEADING };
-#define ciRestart (TinselV2 ? t2ciRestart : t1ciRestart)
-#define restartBox (TinselV2 ? t2RestartBox : t1RestartBox)
+#define ciRestart (TinselV2 ? t2ciRestart : (TinselV1PSX ? t1ciRestartPSX : t1ciRestart))
/*-------------------------------------------------------------*\
| This is the sound control 'menu'. In Discworld 2, it also |
@@ -1038,18 +1043,20 @@ static bool RePosition();
static bool LanguageChange() {
LANGUAGE nLang = _vm->_config->_language;
- if (_vm->getFeatures() & GF_USE_3FLAGS) {
- // VERY quick dodgy bodge
- if (cd.selBox == 0)
- nLang = TXT_FRENCH; // = 1
- else if (cd.selBox == 1)
- nLang = TXT_GERMAN; // = 2
- else
- nLang = TXT_SPANISH; // = 4
- } else if (_vm->getFeatures() & GF_USE_4FLAGS) {
- nLang = (LANGUAGE)(cd.selBox + 1);
- } else if (_vm->getFeatures() & GF_USE_5FLAGS) {
- nLang = (LANGUAGE)cd.selBox;
+ if ((_vm->getFeatures() & GF_USE_3FLAGS) || (_vm->getFeatures() & GF_USE_4FLAGS) || (_vm->getFeatures() & GF_USE_5FLAGS)) {
+ // Languages: TXT_ENGLISH, TXT_FRENCH, TXT_GERMAN, TXT_ITALIAN, TXT_SPANISH
+ // 5 flag versions include English
+ int selected = (_vm->getFeatures() & GF_USE_5FLAGS) ? cd.selBox : cd.selBox + 1;
+ // Make sure that a language flag has been selected. If the user has
+ // changed the language speed slider and hasn't clicked on a flag, it
+ // won't be selected.
+ if (selected >= 0 && selected <= 4) {
+ nLang = (LANGUAGE)selected;
+
+ // 3 flag versions don't include Italian
+ if (selected >= 3 && (_vm->getFeatures() & GF_USE_3FLAGS))
+ nLang = TXT_SPANISH;
+ }
}
if (nLang != _vm->_config->_language) {
@@ -1256,6 +1263,20 @@ static INV_OBJECT *GetInvObject(int id) {
}
/**
+ * Returns true if the given id represents a valid inventory object
+ */
+bool GetIsInvObject(int id) {
+ INV_OBJECT *pObject = g_invObjects;
+
+ for (int i = 0; i < g_numObjects; i++, pObject++) {
+ if (pObject->id == id)
+ return true;
+ }
+
+ return false;
+}
+
+/**
* Convert item ID number to index.
*/
static int GetObjectIndex(int id) {
diff --git a/engines/tinsel/dialogs.h b/engines/tinsel/dialogs.h
index 8c48eb8b76..ab53ba771c 100644
--- a/engines/tinsel/dialogs.h
+++ b/engines/tinsel/dialogs.h
@@ -152,6 +152,8 @@ void InvSetLimit(int invno, int n);
void InvSetSize(int invno, int MinWidth, int MinHeight,
int StartWidth, int StartHeight, int MaxWidth, int MaxHeight);
+bool GetIsInvObject(int id);
+
int WhichInventoryOpen();
bool IsTopWindow();
diff --git a/engines/tinsel/drives.cpp b/engines/tinsel/drives.cpp
index 5c4b939e4e..3ecef83753 100644
--- a/engines/tinsel/drives.cpp
+++ b/engines/tinsel/drives.cpp
@@ -149,7 +149,7 @@ bool GotoCD() {
bool TinselFile::_warningShown = false;
-TinselFile::TinselFile() : ReadStreamEndian((_vm->getFeatures() & GF_BIG_ENDIAN) != 0) {
+TinselFile::TinselFile() : ReadStreamEndian(TinselV1Mac) {
_stream = NULL;
}
diff --git a/engines/tinsel/handle.cpp b/engines/tinsel/handle.cpp
index c3089db990..14d588dcec 100644
--- a/engines/tinsel/handle.cpp
+++ b/engines/tinsel/handle.cpp
@@ -99,14 +99,16 @@ void SetupHandleTable() {
MEMHANDLE *pH;
TinselFile f;
- if (f.open(TinselV1PSX? PSX_INDEX_FILENAME : INDEX_FILENAME)) {
+ const char *indexFileName = TinselV1PSX ? PSX_INDEX_FILENAME : INDEX_FILENAME;
+
+ if (f.open(indexFileName)) {
// get size of index file
len = f.size();
if (len > 0) {
if ((len % RECORD_SIZE) != 0) {
// index file is corrupt
- error(FILE_IS_CORRUPT, TinselV1PSX? PSX_INDEX_FILENAME : INDEX_FILENAME);
+ error(FILE_IS_CORRUPT, indexFileName);
}
// calc number of handles
@@ -132,16 +134,16 @@ void SetupHandleTable() {
if (f.eos() || f.err()) {
// index file is corrupt
- error(FILE_IS_CORRUPT, (TinselV1PSX? PSX_INDEX_FILENAME : INDEX_FILENAME));
+ error(FILE_IS_CORRUPT, indexFileName);
}
// close the file
f.close();
} else { // index file is corrupt
- error(FILE_IS_CORRUPT, (TinselV1PSX? PSX_INDEX_FILENAME : INDEX_FILENAME));
+ error(FILE_IS_CORRUPT, indexFileName);
}
} else { // cannot find the index file
- error(CANNOT_FIND_FILE, (TinselV1PSX? PSX_INDEX_FILENAME : INDEX_FILENAME));
+ error(CANNOT_FIND_FILE, indexFileName);
}
// allocate memory nodes and load all permanent graphics
diff --git a/engines/tinsel/music.cpp b/engines/tinsel/music.cpp
index 781a378f13..b3bfbcc5dc 100644
--- a/engines/tinsel/music.cpp
+++ b/engines/tinsel/music.cpp
@@ -131,21 +131,13 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) {
g_currentMidi = dwFileOffset;
g_currentLoop = bLoop;
- // Tinsel V1 PSX uses a different music format, so i
- // disable it here.
- // TODO: Maybe this should be moved to a better place...
- if (TinselV1PSX) return false;
+ bool mute = false;
+ if (ConfMan.hasKey("mute"))
+ mute = ConfMan.getBool("mute");
- if (_vm->_config->_musicVolume != 0) {
- bool mute = false;
- if (ConfMan.hasKey("mute"))
- mute = ConfMan.getBool("mute");
-
- SetMidiVolume(mute ? 0 : _vm->_config->_musicVolume);
- }
+ SetMidiVolume(mute ? 0 : _vm->_config->_musicVolume);
// the index and length of the last tune loaded
- static uint32 dwLastMidiIndex = 0; // FIXME: Avoid non-const global vars
uint32 dwSeqLen = 0; // length of the sequence
// Support for external music from the music enhancement project
@@ -186,61 +178,53 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) {
if (dwFileOffset == 0)
return true;
- if (dwFileOffset != dwLastMidiIndex) {
- Common::File midiStream;
-
- // open MIDI sequence file in binary mode
- if (!midiStream.open(MIDI_FILE))
- error(CANNOT_FIND_FILE, MIDI_FILE);
-
- // update index of last tune loaded
- dwLastMidiIndex = dwFileOffset;
-
- // move to correct position in the file
- midiStream.seek(dwFileOffset, SEEK_SET);
-
- // read the length of the sequence
- dwSeqLen = midiStream.readUint32LE();
-
- // make sure buffer is large enough for this sequence
- assert(dwSeqLen > 0 && dwSeqLen <= g_midiBuffer.size);
-
- // stop any currently playing tune
- _vm->_midiMusic->stop();
-
- // read the sequence
- if (midiStream.read(g_midiBuffer.pDat, dwSeqLen) != dwSeqLen)
- error(FILE_IS_CORRUPT, MIDI_FILE);
-
- midiStream.close();
-
- // WORKAROUND for bug #2820054 "DW1: No intro music at first start on Wii",
- // which actually affects all ports, since it's specific to the GRA version.
- //
- // The GRA version does not seem to set the channel volume at all for the first
- // intro track, thus we need to do that here. We only initialize the channels
- // used in that sequence. And we are using 127 as default channel volume.
- //
- // Only in the GRA version dwFileOffset can be "38888", just to be sure, we
- // check for the SCN files feature flag not being set though.
- if (_vm->getGameID() == GID_DW1 && dwFileOffset == 38888 && !(_vm->getFeatures() & GF_SCNFILES)) {
- _vm->_midiMusic->send(0x7F07B0 | 3);
- _vm->_midiMusic->send(0x7F07B0 | 5);
- _vm->_midiMusic->send(0x7F07B0 | 8);
- _vm->_midiMusic->send(0x7F07B0 | 10);
- _vm->_midiMusic->send(0x7F07B0 | 13);
- }
+ Common::File midiStream;
- _vm->_midiMusic->playXMIDI(g_midiBuffer.pDat, dwSeqLen, bLoop);
+ // open MIDI sequence file in binary mode
+ if (!midiStream.open(MIDI_FILE))
+ error(CANNOT_FIND_FILE, MIDI_FILE);
- // Store the length
- //dwLastSeqLen = dwSeqLen;
- } else {
- // dwFileOffset == dwLastMidiIndex
- _vm->_midiMusic->stop();
- _vm->_midiMusic->playXMIDI(g_midiBuffer.pDat, dwSeqLen, bLoop);
+ // move to correct position in the file
+ midiStream.seek(dwFileOffset, SEEK_SET);
+
+ // read the length of the sequence
+ dwSeqLen = midiStream.readUint32LE();
+
+ // make sure buffer is large enough for this sequence
+ assert(dwSeqLen > 0 && dwSeqLen <= g_midiBuffer.size);
+
+ // stop any currently playing tune
+ _vm->_midiMusic->stop();
+
+ // read the sequence. This needs to be read again before playSEQ() is
+ // called even if the music is restarting, as playSEQ() reads the file
+ // name off the buffer itself. However, that function adds SMF headers
+ // to the buffer, thus if it's read again, the SMF headers will be read
+ // and the filename will always be 'MThd'.
+ if (midiStream.read(g_midiBuffer.pDat, dwSeqLen) != dwSeqLen)
+ error(FILE_IS_CORRUPT, MIDI_FILE);
+
+ midiStream.close();
+
+ // WORKAROUND for bug #2820054 "DW1: No intro music at first start on Wii",
+ // which actually affects all ports, since it's specific to the GRA version.
+ //
+ // The GRA version does not seem to set the channel volume at all for the first
+ // intro track, thus we need to do that here. We only initialize the channels
+ // used in that sequence. And we are using 127 as default channel volume.
+ //
+ // Only in the GRA version dwFileOffset can be "38888", just to be sure, we
+ // check for the SCN files feature flag not being set though.
+ if (_vm->getGameID() == GID_DW1 && dwFileOffset == 38888 && !(_vm->getFeatures() & GF_SCNFILES)) {
+ _vm->_midiMusic->send(0x7F07B0 | 3);
+ _vm->_midiMusic->send(0x7F07B0 | 5);
+ _vm->_midiMusic->send(0x7F07B0 | 8);
+ _vm->_midiMusic->send(0x7F07B0 | 10);
+ _vm->_midiMusic->send(0x7F07B0 | 13);
}
+ _vm->_midiMusic->playMIDI(dwSeqLen, bLoop);
+
return true;
}
@@ -284,27 +268,7 @@ int GetMidiVolume() {
*/
void SetMidiVolume(int vol) {
assert(vol >= 0 && vol <= Audio::Mixer::kMaxChannelVolume);
-
- static int priorVolMusic = 0; // FIXME: Avoid non-const global vars
-
- if (vol == 0 && priorVolMusic == 0) {
- // Nothing to do
- } else if (vol == 0 && priorVolMusic != 0) {
- // Stop current midi sequence
- StopMidi();
- _vm->_midiMusic->setVolume(vol);
- } else if (vol != 0 && priorVolMusic == 0) {
- // Perhaps restart last midi sequence
- if (g_currentLoop)
- PlayMidiSequence(g_currentMidi, true);
-
- _vm->_midiMusic->setVolume(vol);
- } else if (vol != 0 && priorVolMusic != 0) {
- // Alter current volume
- _vm->_midiMusic->setVolume(vol);
- }
-
- priorVolMusic = vol;
+ _vm->_midiMusic->setVolume(vol);
}
/**
@@ -314,8 +278,7 @@ void OpenMidiFiles() {
Common::File midiStream;
// Demo version has no midi file
- // Also, Discworld PSX uses still unsupported psx SEQ format for music...
- if ((_vm->getFeatures() & GF_DEMO) || (TinselVersion == TINSEL_V2) || TinselV1PSX)
+ if (TinselV0 || TinselV2)
return;
if (g_midiBuffer.pDat)
@@ -412,7 +375,7 @@ void MidiMusicPlayer::send(uint32 b) {
}
}
-void MidiMusicPlayer::playXMIDI(byte *midiData, uint32 size, bool loop) {
+void MidiMusicPlayer::playMIDI(uint32 size, bool loop) {
Common::StackLock lock(_mutex);
if (_isPlaying)
@@ -420,6 +383,13 @@ void MidiMusicPlayer::playXMIDI(byte *midiData, uint32 size, bool loop) {
stop();
+ if (TinselV1PSX)
+ playSEQ(size, loop);
+ else
+ playXMIDI(size, loop);
+}
+
+void MidiMusicPlayer::playXMIDI(uint32 size, bool loop) {
// It seems like not all music (the main menu music, for instance) set
// all the instruments explicitly. That means the music will sound
// different, depending on which music played before it. This appears
@@ -433,7 +403,78 @@ void MidiMusicPlayer::playXMIDI(byte *midiData, uint32 size, bool loop) {
// Load XMID resource data
MidiParser *parser = MidiParser::createParser_XMIDI();
- if (parser->loadMusic(midiData, size)) {
+ if (parser->loadMusic(g_midiBuffer.pDat, size)) {
+ parser->setTrack(0);
+ parser->setMidiDriver(this);
+ parser->setTimerRate(getBaseTempo());
+ parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
+ parser->property(MidiParser::mpSendSustainOffOnNotesOff, 1);
+
+ _parser = parser;
+
+ _isLooping = loop;
+ _isPlaying = true;
+ } else {
+ delete parser;
+ }
+}
+
+void MidiMusicPlayer::playSEQ(uint32 size, bool loop) {
+ // MIDI.DAT holds the file names in DW1 PSX
+ Common::String baseName((char *)g_midiBuffer.pDat, size);
+ Common::String seqName = baseName + ".SEQ";
+
+ // TODO: Load the instrument bank (<baseName>.VB and <baseName>.VH)
+
+ Common::File seqFile;
+ if (!seqFile.open(seqName))
+ error("Failed to open SEQ file '%s'", seqName.c_str());
+
+ if (seqFile.readUint32LE() != MKTAG('S', 'E', 'Q', 'p'))
+ error("Failed to find SEQp tag");
+
+ // Make sure we don't have a SEP file (with multiple SEQ's inside)
+ if (seqFile.readUint32BE() != 1)
+ error("Can only play SEQ files, not SEP");
+
+ uint16 ppqn = seqFile.readUint16BE();
+ uint32 tempo = seqFile.readUint16BE() << 8;
+ tempo |= seqFile.readByte();
+ /* uint16 beat = */ seqFile.readUint16BE();
+
+ // SEQ is directly based on SMF and we'll use that to our advantage here
+ // and convert to SMF and then use the SMF MidiParser.
+
+ // Calculate the SMF size we'll need
+ uint32 dataSize = seqFile.size() - 15;
+ uint32 actualSize = dataSize + 7 + 22;
+
+ // Resize the buffer if necessary
+ if (g_midiBuffer.size < actualSize) {
+ g_midiBuffer.pDat = (byte *)realloc(g_midiBuffer.pDat, actualSize);
+ assert(g_midiBuffer.pDat);
+ }
+
+ // Now construct the header
+ WRITE_BE_UINT32(g_midiBuffer.pDat, MKTAG('M', 'T', 'h', 'd'));
+ WRITE_BE_UINT32(g_midiBuffer.pDat + 4, 6); // header size
+ WRITE_BE_UINT16(g_midiBuffer.pDat + 8, 0); // type 0
+ WRITE_BE_UINT16(g_midiBuffer.pDat + 10, 1); // one track
+ WRITE_BE_UINT16(g_midiBuffer.pDat + 12, ppqn);
+ WRITE_BE_UINT32(g_midiBuffer.pDat + 14, MKTAG('M', 'T', 'r', 'k'));
+ WRITE_BE_UINT32(g_midiBuffer.pDat + 18, dataSize + 7); // SEQ data size + tempo change event size
+
+ // Add in a fake tempo change event
+ WRITE_BE_UINT32(g_midiBuffer.pDat + 22, 0x00FF5103); // no delta, meta event, tempo change, param size = 3
+ WRITE_BE_UINT16(g_midiBuffer.pDat + 26, tempo >> 8);
+ g_midiBuffer.pDat[28] = tempo & 0xFF;
+
+ // Now copy in the rest of the events
+ seqFile.read(g_midiBuffer.pDat + 29, dataSize);
+ seqFile.close();
+
+ MidiParser *parser = MidiParser::createParser_SMF();
+ if (parser->loadMusic(g_midiBuffer.pDat, actualSize)) {
parser->setTrack(0);
parser->setMidiDriver(this);
parser->setTimerRate(getBaseTempo());
@@ -870,14 +911,12 @@ void RestoreMidiFacts(SCNHANDLE Midi, bool Loop) {
g_currentMidi = Midi;
g_currentLoop = Loop;
- if (_vm->_config->_musicVolume != 0 && Loop) {
- bool mute = false;
- if (ConfMan.hasKey("mute"))
- mute = ConfMan.getBool("mute");
+ bool mute = false;
+ if (ConfMan.hasKey("mute"))
+ mute = ConfMan.getBool("mute");
- PlayMidiSequence(g_currentMidi, true);
- SetMidiVolume(mute ? 0 : _vm->_config->_musicVolume);
- }
+ PlayMidiSequence(g_currentMidi, true);
+ SetMidiVolume(mute ? 0 : _vm->_config->_musicVolume);
}
#if 0
diff --git a/engines/tinsel/music.h b/engines/tinsel/music.h
index d43fed268d..121bf3d79b 100644
--- a/engines/tinsel/music.h
+++ b/engines/tinsel/music.h
@@ -64,7 +64,7 @@ public:
virtual void setVolume(int volume);
- void playXMIDI(byte *midiData, uint32 size, bool loop);
+ void playMIDI(uint32 size, bool loop);
// void stop();
void pause();
@@ -76,6 +76,10 @@ public:
// The original sets the "sequence timing" to 109 Hz, whatever that
// means. The default is 120.
uint32 getBaseTempo() { return _driver ? (109 * _driver->getBaseTempo()) / 120 : 0; }
+
+private:
+ void playXMIDI(uint32 size, bool loop);
+ void playSEQ(uint32 size, bool loop);
};
class PCMMusicPlayer : public Audio::AudioStream {
diff --git a/engines/tinsel/pcode.cpp b/engines/tinsel/pcode.cpp
index 60f04b47fd..6ea18c8268 100644
--- a/engines/tinsel/pcode.cpp
+++ b/engines/tinsel/pcode.cpp
@@ -122,6 +122,8 @@ static uint32 g_hMasterScript;
struct WorkaroundEntry {
TinselEngineVersion version; ///< Engine version this workaround applies to
bool scnFlag; ///< Only applicable for Tinsel 1 (DW 1)
+ bool isDemo; ///< Flags whether it's for a demo
+ Common::Platform platform; ///< Platform filter
SCNHANDLE hCode; ///< Script to apply fragment to
int ip; ///< Script offset to run this fragment before
int numBytes; ///< Number of bytes in the script
@@ -129,6 +131,7 @@ struct WorkaroundEntry {
};
#define FRAGMENT_WORD(x) (byte)(x & 0xFF), (byte)(x >> 8)
+#define FRAGMENT_DWORD(x) (byte)(x & 0xFF), (byte)(x >> 8), (byte)(x >> 16), (byte)(x >> 24)
static const byte fragment1[] = {OP_ZERO, OP_GSTORE | OPSIZE16, 206, 0};
static const byte fragment2[] = {OP_LIBCALL | OPSIZE8, 110};
@@ -149,6 +152,10 @@ static const byte fragment12[] = {OP_JMPTRUE | OPSIZE16, FRAGMENT_WORD(1491),
OP_IMM | OPSIZE16, FRAGMENT_WORD(322), OP_LIBCALL | OPSIZE8, 46, // Give back the whistle
OP_JUMP | OPSIZE16, FRAGMENT_WORD(1568)};
static const byte fragment13[] = {OP_ZERO, OP_GSTORE | OPSIZE16, FRAGMENT_WORD(306)};
+static const byte fragment14[] = {OP_LIBCALL | OPSIZE8, 58,
+ OP_IMM, FRAGMENT_DWORD((42 << 23)), OP_ONE, OP_ZERO, OP_LIBCALL | OPSIZE8, 44,
+ OP_LIBCALL | OPSIZE8, 97, OP_JUMP | OPSIZE16, FRAGMENT_WORD(2220)
+};
#undef FRAGMENT_WORD
@@ -157,7 +164,7 @@ const WorkaroundEntry workaroundList[] = {
// book back to the present. In the GRA version, it was global 373,
// and was reset when he is returned to the past, but was forgotten
// in the SCN version, so this ensures the flag is properly reset.
- {TINSEL_V1, true, 427942095, 1, sizeof(fragment1), fragment1},
+ {TINSEL_V1, true, false, Common::kPlatformUnknown, 427942095, 1, sizeof(fragment1), fragment1},
// DW1-GRA: Rincewind exiting the Inn is blocked by the luggage.
// Whilst you can then move into walkable areas, saving and
@@ -165,26 +172,26 @@ const WorkaroundEntry workaroundList[] = {
// fragment turns off NPC blocking for the Outside Inn rooms so that
// the luggage won't block Past Outside Inn.
// See bug report #2525010.
- {TINSEL_V1, false, 444622076, 0, sizeof(fragment2), fragment2},
+ {TINSEL_V1, false, false, Common::kPlatformUnknown, 444622076, 0, sizeof(fragment2), fragment2},
// Present Outside Inn
- {TINSEL_V1, false, 352600876, 0, sizeof(fragment2), fragment2},
+ {TINSEL_V1, false, false, Common::kPlatformUnknown, 352600876, 0, sizeof(fragment2), fragment2},
// DW1-GRA: Talking to palace guards in Act 2 gives !!!HIGH
// STRING||| - this happens if you initiate dialog with one of the
// guards, but not the other. So these fragments provide the correct
// talk parameters where needed.
// See bug report #2831159.
- {TINSEL_V1, false, 310506872, 463, sizeof(fragment4), fragment4},
- {TINSEL_V1, false, 310506872, 485, sizeof(fragment5), fragment5},
- {TINSEL_V1, false, 310506872, 513, sizeof(fragment6), fragment6},
- {TINSEL_V1, false, 310506872, 613, sizeof(fragment7), fragment7},
- {TINSEL_V1, false, 310506872, 641, sizeof(fragment8), fragment8},
+ {TINSEL_V1, false, false, Common::kPlatformUnknown, 310506872, 463, sizeof(fragment4), fragment4},
+ {TINSEL_V1, false, false, Common::kPlatformUnknown, 310506872, 485, sizeof(fragment5), fragment5},
+ {TINSEL_V1, false, false, Common::kPlatformUnknown, 310506872, 513, sizeof(fragment6), fragment6},
+ {TINSEL_V1, false, false, Common::kPlatformUnknown, 310506872, 613, sizeof(fragment7), fragment7},
+ {TINSEL_V1, false, false, Common::kPlatformUnknown, 310506872, 641, sizeof(fragment8), fragment8},
// DW1-SCN: The script for the lovable street-Starfish does a
// 'StopSample' after flicking the coin to ensure it's sound is
// stopped, but which also accidentally can stop any active
// conversation with the Amazon.
- {TINSEL_V1, true, 394640351, 121, sizeof(fragment9), fragment9},
+ {TINSEL_V1, true, false, Common::kPlatformUnknown, 394640351, 121, sizeof(fragment9), fragment9},
// DW2: In the garden, global #490 is set when the bees begin their
// 'out of hive' animation, and reset when done. But if the game is
@@ -197,25 +204,29 @@ const WorkaroundEntry workaroundList[] = {
// * Stealing the mallets from the wizards (bug #2820788).
// This fix ensures that the global is reset when the Garden scene
// is loaded (both entering and restoring a game).
- {TINSEL_V2, true, 2888147476U, 0, sizeof(fragment3), fragment3},
+ {TINSEL_V2, true, false, Common::kPlatformUnknown, 2888147476U, 0, sizeof(fragment3), fragment3},
// DW1-GRA: Corrects text being drawn partially off-screen during
// the blackboard description of the Librarian.
- {TINSEL_V1, false, 293831402, 133, sizeof(fragment10), fragment10},
+ {TINSEL_V1, false, false, Common::kPlatformUnknown, 293831402, 133, sizeof(fragment10), fragment10},
// DW1-GRA/SCN: Corrects the dead-end of being able to give the
// whistle back to the pirate before giving him the parrot.
// See bug report #2934211.
- {TINSEL_V1, true, 352601285, 1569, sizeof(fragment11), fragment11},
- {TINSEL_V1, false, 352602304, 1488, sizeof(fragment12), fragment12},
+ {TINSEL_V1, true, false, Common::kPlatformUnknown, 352601285, 1569, sizeof(fragment11), fragment11},
+ {TINSEL_V1, false, false, Common::kPlatformUnknown, 352602304, 1488, sizeof(fragment12), fragment12},
// DW2: Corrects a bug with global 306 not being cleared if you leave
// the marketplace scene whilst D'Blah is talking (even if it's not
// actually audible); returning to the scene and clicking on him multiple
// times would cause the game to crash
- {TINSEL_V2, true, 1109294728, 0, sizeof(fragment13), fragment13},
+ {TINSEL_V2, true, false, Common::kPlatformUnknown, 1109294728, 0, sizeof(fragment13), fragment13},
+
+ // DW1 PSX DEMO: Alters a script in the PSX DW1 demo to show the Idle animation scene rather than
+ // quitting the game when no user input happens for a while
+ {TINSEL_V1, true, true, Common::kPlatformPSX, 0, 2186, sizeof(fragment14), fragment14},
- {TINSEL_V0, false, 0, 0, 0, NULL}
+ {TINSEL_V0, false, false, Common::kPlatformUnknown, 0, 0, 0, NULL}
};
//----------------- LOCAL GLOBAL DATA --------------------
@@ -582,6 +593,8 @@ void Interpret(CORO_PARAM, INT_CONTEXT *ic) {
if ((wkEntry->version == TinselVersion) &&
(wkEntry->hCode == ic->hCode) &&
(wkEntry->ip == ip) &&
+ (wkEntry->isDemo == _vm->getIsADGFDemo()) &&
+ ((wkEntry->platform == Common::kPlatformUnknown) || (wkEntry->platform == _vm->getPlatform())) &&
(!TinselV1 || (wkEntry->scnFlag == ((_vm->getFeatures() & GF_SCNFILES) != 0)))) {
// Point to start of workaround fragment
ip = 0;
diff --git a/engines/tinsel/saveload.cpp b/engines/tinsel/saveload.cpp
index 0a552c8c2b..518e27f02b 100644
--- a/engines/tinsel/saveload.cpp
+++ b/engines/tinsel/saveload.cpp
@@ -55,8 +55,7 @@ namespace Tinsel {
* only saves/loads those which are valid for the version of the savegame
* which is being loaded/saved currently.
*/
-#define CURRENT_VER 1
-// TODO: Not yet used
+#define CURRENT_VER 2
/**
* An auxillary macro, used to specify savegame versions. We use this instead
@@ -97,12 +96,13 @@ struct SaveGameHeader {
TimeDate dateTime;
bool scnFlag;
byte language;
+ uint16 numInterpreters; // Savegame version 2 or later only
};
enum {
DW1_SAVEGAME_ID = 0x44575399, // = 'DWSc' = "DiscWorld 1 ScummVM"
DW2_SAVEGAME_ID = 0x44573253, // = 'DW2S' = "DiscWorld 2 ScummVM"
- SAVEGAME_HEADER_SIZE = 4 + 4 + 4 + SG_DESC_LEN + 7 + 1 + 1
+ SAVEGAME_HEADER_SIZE = 4 + 4 + 4 + SG_DESC_LEN + 7 + 1 + 1 + 2
};
#define SAVEGAME_ID (TinselV2 ? (uint32)DW2_SAVEGAME_ID : (uint32)DW1_SAVEGAME_ID)
@@ -186,6 +186,15 @@ static bool syncSaveGameHeader(Common::Serializer &s, SaveGameHeader &hdr) {
}
}
+ // Handle the number of interpreter contexts that will be saved in the savegame
+ if (tmp >= 2) {
+ tmp -= 2;
+ hdr.numInterpreters = NUM_INTERPRET;
+ s.syncAsUint16LE(hdr.numInterpreters);
+ } else {
+ hdr.numInterpreters = (TinselV2 ? 70 : 64) - 20;
+ }
+
// Skip over any extra bytes
s.skip(tmp);
return true;
@@ -262,7 +271,7 @@ static void syncSoundReel(Common::Serializer &s, SOUNDREELS &sr) {
s.syncAsSint32LE(sr.actorCol);
}
-static void syncSavedData(Common::Serializer &s, SAVED_DATA &sd) {
+static void syncSavedData(Common::Serializer &s, SAVED_DATA &sd, int numInterp) {
s.syncAsUint32LE(sd.SavedSceneHandle);
s.syncAsUint32LE(sd.SavedBgroundHandle);
for (int i = 0; i < MAX_MOVERS; ++i)
@@ -273,7 +282,7 @@ static void syncSavedData(Common::Serializer &s, SAVED_DATA &sd) {
s.syncAsSint32LE(sd.NumSavedActors);
s.syncAsSint32LE(sd.SavedLoffset);
s.syncAsSint32LE(sd.SavedToffset);
- for (int i = 0; i < NUM_INTERPRET; ++i)
+ for (int i = 0; i < numInterp; ++i)
sd.SavedICInfo[i].syncWithSerializer(s);
for (int i = 0; i < MAX_POLY; ++i)
s.syncAsUint32LE(sd.SavedDeadPolys[i]);
@@ -422,7 +431,7 @@ char *ListEntry(int i, letype which) {
return NULL;
}
-static void DoSync(Common::Serializer &s) {
+static bool DoSync(Common::Serializer &s, int numInterp) {
int sg = 0;
if (TinselV2) {
@@ -434,7 +443,7 @@ static void DoSync(Common::Serializer &s) {
if (TinselV2 && s.isLoading())
HoldItem(INV_NOICON);
- syncSavedData(s, *g_srsd);
+ syncSavedData(s, *g_srsd, numInterp);
syncGlobInfo(s); // Glitter globals
syncInvInfo(s); // Inventory data
@@ -443,6 +452,10 @@ static void DoSync(Common::Serializer &s) {
sg = WhichItemHeld();
s.syncAsSint32LE(sg);
if (s.isLoading()) {
+ if (sg != -1 && !GetIsInvObject(sg))
+ // Not a valid inventory object, so return false
+ return false;
+
if (TinselV2)
g_thingHeld = sg;
else
@@ -459,7 +472,7 @@ static void DoSync(Common::Serializer &s) {
if (*g_SaveSceneSsCount != 0) {
SAVED_DATA *sdPtr = g_SaveSceneSsData;
for (int i = 0; i < *g_SaveSceneSsCount; ++i, ++sdPtr)
- syncSavedData(s, *sdPtr);
+ syncSavedData(s, *sdPtr, numInterp);
// Flag that there is a saved scene to return to. Note that in this context 'saved scene'
// is a stored scene to return to from another scene, such as from the Summoning Book close-up
@@ -469,6 +482,8 @@ static void DoSync(Common::Serializer &s) {
if (!TinselV2)
syncAllActorsAlive(s);
+
+ return true;
}
/**
@@ -487,8 +502,23 @@ static bool DoRestore() {
delete f; // Invalid header, or savegame too new -> skip it
return false;
}
+
+ // Load in the data. For older savegame versions, we potentially need to load the data twice, once
+ // for pre 1.5 savegames, and if that fails, a second time for 1.5 savegames
+ int numInterpreters = hdr.numInterpreters;
+ int32 currentPos = f->pos();
+ for (int tryNumber = 0; tryNumber < ((hdr.ver >= 2) ? 1 : 2); ++tryNumber) {
+ // If it's the second loop iteration, try with the 1.5 savegame number of interpreter contexts
+ if (tryNumber == 1) {
+ f->seek(currentPos);
+ numInterpreters = 80;
+ }
- DoSync(s);
+ // Load the savegame data
+ if (DoSync(s, numInterpreters))
+ // Data load was successful (or likely), so break out of loop
+ break;
+ }
uint32 id = f->readSint32LE();
if (id != (uint32)0xFEEDFACE)
@@ -575,7 +605,7 @@ static void DoSave() {
return;
}
- DoSync(s);
+ DoSync(s, hdr.numInterpreters);
// Write out the special Id for Discworld savegames
f->writeUint32LE(0xFEEDFACE);
diff --git a/engines/tinsel/scene.h b/engines/tinsel/scene.h
index baaff27a3e..06e5c096d9 100644
--- a/engines/tinsel/scene.h
+++ b/engines/tinsel/scene.h
@@ -75,9 +75,9 @@ enum REEL {
typedef enum { TRANS_DEF, TRANS_CUT, TRANS_FADE } TRANSITS;
// amount to shift scene handles by
-#define SCNHANDLE_SHIFT ((TinselV2 && !IsDemo) ? 25 : 23)
-#define OFFSETMASK ((TinselV2 && !IsDemo) ? 0x01ffffffL : 0x007fffffL)
-#define HANDLEMASK ((TinselV2 && !IsDemo) ? 0xFE000000L : 0xFF800000L)
+#define SCNHANDLE_SHIFT ((TinselV2 && !TinselV2Demo) ? 25 : 23)
+#define OFFSETMASK ((TinselV2 && !TinselV2Demo) ? 0x01ffffffL : 0x007fffffL)
+#define HANDLEMASK ((TinselV2 && !TinselV2Demo) ? 0xFE000000L : 0xFF800000L)
void DoHailScene(SCNHANDLE scene);
diff --git a/engines/tinsel/sound.cpp b/engines/tinsel/sound.cpp
index f575b03270..e052302cfd 100644
--- a/engines/tinsel/sound.cpp
+++ b/engines/tinsel/sound.cpp
@@ -75,7 +75,7 @@ SoundManager::~SoundManager() {
// playSample for DiscWorld 1
bool SoundManager::playSample(int id, Audio::Mixer::SoundType type, Audio::SoundHandle *handle) {
// Floppy version has no sample file
- if (_vm->getFeatures() & GF_FLOPPY)
+ if (!_vm->isCD())
return false;
// no sample driver?
@@ -182,7 +182,7 @@ bool SoundManager::playSample(int id, int sub, bool bLooped, int x, int y, int p
Audio::Mixer::SoundType type, Audio::SoundHandle *handle) {
// Floppy version has no sample file
- if (_vm->getFeatures() & GF_FLOPPY)
+ if (!_vm->isCD())
return false;
// no sample driver?
@@ -471,7 +471,7 @@ void SoundManager::setSFXVolumes(uint8 volume) {
*/
void SoundManager::openSampleFiles() {
// Floppy and demo versions have no sample files, except for the Discworld 2 demo
- if (_vm->getFeatures() & GF_FLOPPY || (IsDemo && !TinselV2))
+ if (!_vm->isCD() || TinselV0)
return;
TinselFile f;
diff --git a/engines/tinsel/tinlib.cpp b/engines/tinsel/tinlib.cpp
index dfa44c505a..058f8eb6fd 100644
--- a/engines/tinsel/tinlib.cpp
+++ b/engines/tinsel/tinlib.cpp
@@ -1625,10 +1625,6 @@ static void Play(CORO_PARAM, SCNHANDLE hFilm, int x, int y, bool bComplete, int
* Play a midi file.
*/
static void PlayMidi(CORO_PARAM, SCNHANDLE hMidi, int loop, bool complete) {
- // FIXME: This is a workaround for the FIXME below
- if (GetMidiVolume() == 0 || TinselV1PSX)
- return;
-
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
@@ -1637,18 +1633,13 @@ static void PlayMidi(CORO_PARAM, SCNHANDLE hMidi, int loop, bool complete) {
PlayMidiSequence(hMidi, loop == MIDI_LOOP);
- // FIXME: The following check messes up the script arguments when
- // entering the secret door in the bookshelf in the library,
- // leading to a crash, when the music volume is set to 0 (MidiPlaying()
- // always false then).
- //
- // Why exactly this happens is unclear. An analysis of the involved
- // script(s) might reveal more.
- //
- // Note: This check&sleep was added in DW v2. It was most likely added
- // to ensure that the MIDI song started playing before the next opcode
+ // This check&sleep was added in DW v2. It was most likely added to
+ // ensure that the MIDI song started playing before the next opcode
// is executed.
- if (!MidiPlaying())
+ // In DW1, it messes up the script arguments when entering the secret
+ // door in the bookshelf in the library, leading to a crash, when the
+ // music volume is set to 0.
+ if (!MidiPlaying() && TinselV2)
CORO_SLEEP(1);
if (complete) {
@@ -3412,7 +3403,7 @@ static void TalkOrSay(CORO_PARAM, SPEECH_TYPE speechType, SCNHANDLE hText, int x
// Kick off the sample now (perhaps with a delay)
if (g_bNoPause)
g_bNoPause = false;
- else if (!IsDemo)
+ else if (!TinselV2Demo)
CORO_SLEEP(SysVar(SV_SPEECHDELAY));
//SamplePlay(VOICE, hText, _ctx->sub, false, -1, -1, PRIORITY_TALK);
@@ -4244,7 +4235,7 @@ int CallLibraryRoutine(CORO_PARAM, int operand, int32 *pp, const INT_CONTEXT *pi
int libCode;
if (TinselV0) libCode = DW1DEMO_CODES[operand];
else if (!TinselV2) libCode = DW1_CODES[operand];
- else if (_vm->getFeatures() & GF_DEMO) libCode = DW2DEMO_CODES[operand];
+ else if (TinselV2Demo) libCode = DW2DEMO_CODES[operand];
else libCode = DW2_CODES[operand];
debug(7, "CallLibraryRoutine op %d (escOn %d, myEscape %d)", operand, pic->escOn, pic->myEscape);
diff --git a/engines/tinsel/tinsel.h b/engines/tinsel/tinsel.h
index 59344c44f4..123249125e 100644
--- a/engines/tinsel/tinsel.h
+++ b/engines/tinsel/tinsel.h
@@ -53,7 +53,6 @@ class Config;
class MidiDriver;
class MidiMusicPlayer;
class PCMMusicPlayer;
-class Scheduler;
class SoundManager;
typedef Common::List<Common::Rect> RectList;
@@ -64,21 +63,16 @@ enum TinselGameID {
};
enum TinselGameFeatures {
- GF_DEMO = 1 << 0,
- GF_CD = 1 << 1,
- GF_FLOPPY = 1 << 2,
- GF_SCNFILES = 1 << 3,
- GF_ENHANCED_AUDIO_SUPPORT = 1 << 4,
- GF_ALT_MIDI = 1 << 5, // Alternate sequence in midi.dat file
+ GF_SCNFILES = 1 << 0,
+ GF_ENHANCED_AUDIO_SUPPORT = 1 << 1,
+ GF_ALT_MIDI = 1 << 2, // Alternate sequence in midi.dat file
// The GF_USE_?FLAGS values specify how many country flags are displayed
// in the subtitles options dialog.
// None of these defined -> 1 language, in ENGLISH.TXT
- GF_USE_3FLAGS = 1 << 6, // French, German, Spanish
- GF_USE_4FLAGS = 1 << 7, // French, German, Spanish, Italian
- GF_USE_5FLAGS = 1 << 8, // All 5 flags
-
- GF_BIG_ENDIAN = 1 << 9
+ GF_USE_3FLAGS = 1 << 3, // French, German, Spanish
+ GF_USE_4FLAGS = 1 << 4, // French, German, Spanish, Italian
+ GF_USE_5FLAGS = 1 << 5 // All 5 flags
};
/**
@@ -135,13 +129,12 @@ typedef bool (*KEYFPTR)(const Common::KeyState &);
#define TinselV0 (TinselVersion == TINSEL_V0)
#define TinselV1 (TinselVersion == TINSEL_V1)
#define TinselV2 (TinselVersion == TINSEL_V2)
+#define TinselV2Demo (TinselVersion == TINSEL_V2 && _vm->getIsADGFDemo())
#define TinselV1PSX (TinselVersion == TINSEL_V1 && _vm->getPlatform() == Common::kPlatformPSX)
#define TinselV1Mac (TinselVersion == TINSEL_V1 && _vm->getPlatform() == Common::kPlatformMacintosh)
-#define IsDemo (_vm->getFeatures() & GF_DEMO)
-
-#define READ_16(v) ((_vm->getFeatures() & GF_BIG_ENDIAN) ? READ_BE_UINT16(v) : READ_LE_UINT16(v))
-#define READ_32(v) ((_vm->getFeatures() & GF_BIG_ENDIAN) ? READ_BE_UINT32(v) : READ_LE_UINT32(v))
+#define READ_16(v) (TinselV1Mac ? READ_BE_UINT16(v) : READ_LE_UINT16(v))
+#define READ_32(v) (TinselV1Mac ? READ_BE_UINT32(v) : READ_LE_UINT32(v))
// Global reference to the TinselEngine object
extern TinselEngine *_vm;
@@ -154,7 +147,6 @@ class TinselEngine : public Engine {
Common::Point _mousePos;
uint8 _dosPlayerDir;
Console *_console;
- Scheduler *_scheduler;
static const char *const _sampleIndices[][3];
static const char *const _sampleFiles[][3];
@@ -188,6 +180,8 @@ public:
uint16 getVersion() const;
uint32 getFlags() const;
Common::Platform getPlatform() const;
+ bool getIsADGFDemo() const;
+ bool isCD() const;
const char *getSampleIndex(LANGUAGE lang);
const char *getSampleFile(LANGUAGE lang);