aboutsummaryrefslogtreecommitdiff
path: root/engines/scumm
diff options
context:
space:
mode:
Diffstat (limited to 'engines/scumm')
-rw-r--r--engines/scumm/POTFILES3
-rw-r--r--engines/scumm/actor.cpp2
-rw-r--r--engines/scumm/configure.engine5
-rw-r--r--engines/scumm/detection.cpp26
-rw-r--r--engines/scumm/detection_tables.h7
-rw-r--r--engines/scumm/gfx.cpp13
-rw-r--r--engines/scumm/he/script_v100he.cpp2
-rw-r--r--engines/scumm/he/script_v70he.cpp2
-rw-r--r--engines/scumm/he/script_v72he.cpp4
-rw-r--r--engines/scumm/imuse_digi/dimuse_sndmgr.cpp13
-rw-r--r--engines/scumm/insane/insane_enemy.cpp1
-rw-r--r--engines/scumm/module.mk33
-rw-r--r--engines/scumm/players/player_ad.cpp959
-rw-r--r--engines/scumm/players/player_ad.h190
-rw-r--r--engines/scumm/players/player_apple2.cpp (renamed from engines/scumm/player_apple2.cpp)2
-rw-r--r--engines/scumm/players/player_apple2.h (renamed from engines/scumm/player_apple2.h)4
-rw-r--r--engines/scumm/players/player_mac.cpp (renamed from engines/scumm/player_mac.cpp)2
-rw-r--r--engines/scumm/players/player_mac.h (renamed from engines/scumm/player_mac.h)4
-rw-r--r--engines/scumm/players/player_mod.cpp (renamed from engines/scumm/player_mod.cpp)2
-rw-r--r--engines/scumm/players/player_mod.h (renamed from engines/scumm/player_mod.h)4
-rw-r--r--engines/scumm/players/player_nes.cpp (renamed from engines/scumm/player_nes.cpp)2
-rw-r--r--engines/scumm/players/player_nes.h (renamed from engines/scumm/player_nes.h)4
-rw-r--r--engines/scumm/players/player_pce.cpp (renamed from engines/scumm/player_pce.cpp)2
-rw-r--r--engines/scumm/players/player_pce.h (renamed from engines/scumm/player_pce.h)4
-rw-r--r--engines/scumm/players/player_sid.cpp (renamed from engines/scumm/player_sid.cpp)2
-rw-r--r--engines/scumm/players/player_sid.h (renamed from engines/scumm/player_sid.h)4
-rw-r--r--engines/scumm/players/player_towns.cpp (renamed from engines/scumm/player_towns.cpp)2
-rw-r--r--engines/scumm/players/player_towns.h (renamed from engines/scumm/player_towns.h)4
-rw-r--r--engines/scumm/players/player_v1.cpp (renamed from engines/scumm/player_v1.cpp)2
-rw-r--r--engines/scumm/players/player_v1.h (renamed from engines/scumm/player_v1.h)6
-rw-r--r--engines/scumm/players/player_v2.cpp (renamed from engines/scumm/player_v2.cpp)2
-rw-r--r--engines/scumm/players/player_v2.h (renamed from engines/scumm/player_v2.h)6
-rw-r--r--engines/scumm/players/player_v2a.cpp (renamed from engines/scumm/player_v2a.cpp)2
-rw-r--r--engines/scumm/players/player_v2a.h (renamed from engines/scumm/player_v2a.h)6
-rw-r--r--engines/scumm/players/player_v2base.cpp (renamed from engines/scumm/player_v2base.cpp)2
-rw-r--r--engines/scumm/players/player_v2base.h (renamed from engines/scumm/player_v2base.h)4
-rw-r--r--engines/scumm/players/player_v2cms.cpp (renamed from engines/scumm/player_v2cms.cpp)2
-rw-r--r--engines/scumm/players/player_v2cms.h (renamed from engines/scumm/player_v2cms.h)6
-rw-r--r--engines/scumm/players/player_v3a.cpp (renamed from engines/scumm/player_v3a.cpp)2
-rw-r--r--engines/scumm/players/player_v3a.h (renamed from engines/scumm/player_v3a.h)6
-rw-r--r--engines/scumm/players/player_v3m.cpp (renamed from engines/scumm/player_v3m.cpp)2
-rw-r--r--engines/scumm/players/player_v3m.h (renamed from engines/scumm/player_v3m.h)6
-rw-r--r--engines/scumm/players/player_v4a.cpp (renamed from engines/scumm/player_v4a.cpp)2
-rw-r--r--engines/scumm/players/player_v4a.h (renamed from engines/scumm/player_v4a.h)4
-rw-r--r--engines/scumm/players/player_v5m.cpp (renamed from engines/scumm/player_v5m.cpp)2
-rw-r--r--engines/scumm/players/player_v5m.h (renamed from engines/scumm/player_v5m.h)6
-rw-r--r--engines/scumm/saveload.cpp101
-rw-r--r--engines/scumm/saveload.h2
-rw-r--r--engines/scumm/script_v5.cpp2
-rw-r--r--engines/scumm/script_v6.cpp2
-rw-r--r--engines/scumm/scumm-md5.h4
-rw-r--r--engines/scumm/scumm.cpp36
-rw-r--r--engines/scumm/scumm.h10
-rw-r--r--engines/scumm/sound.cpp26
-rw-r--r--engines/scumm/vars.cpp6
55 files changed, 1369 insertions, 190 deletions
diff --git a/engines/scumm/POTFILES b/engines/scumm/POTFILES
new file mode 100644
index 0000000000..6034320259
--- /dev/null
+++ b/engines/scumm/POTFILES
@@ -0,0 +1,3 @@
+engines/scumm/dialogs.cpp
+engines/scumm/help.cpp
+engines/scumm/scumm.cpp
diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp
index 5c148a7b57..4e14473921 100644
--- a/engines/scumm/actor.cpp
+++ b/engines/scumm/actor.cpp
@@ -1485,7 +1485,7 @@ void ScummEngine::playActorSounds() {
int sound;
for (i = 1; i < _numActors; i++) {
- if (_actors[i]->_cost.soundCounter && _actors[i]->isInCurrentRoom() && _actors[i]->_sound) {
+ if (_actors[i]->_cost.soundCounter && _actors[i]->isInCurrentRoom()) {
_currentScript = 0xFF;
if (_game.version == 0) {
sound = v0ActorSounds[i - 1] & 0x3F;
diff --git a/engines/scumm/configure.engine b/engines/scumm/configure.engine
new file mode 100644
index 0000000000..e1de788061
--- /dev/null
+++ b/engines/scumm/configure.engine
@@ -0,0 +1,5 @@
+# This file is included from the main "configure" script
+# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
+add_engine scumm "SCUMM" yes "scumm_7_8 he" "v0-v6 games"
+add_engine scumm_7_8 "v7 & v8 games" yes
+add_engine he "HE71+ games" yes
diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp
index 170ca0993c..aa7e60930a 100644
--- a/engines/scumm/detection.cpp
+++ b/engines/scumm/detection.cpp
@@ -1223,8 +1223,8 @@ const char *ScummMetaEngine::getOriginalCopyright() const {
}
namespace Scumm {
- extern bool getSavegameName(Common::InSaveFile *in, Common::String &desc, int heversion);
-}
+bool getSavegameName(Common::InSaveFile *in, Common::String &desc, int heversion);
+} // End of namespace Scumm
int ScummMetaEngine::getMaximumSaveSlot() const { return 99; }
@@ -1262,25 +1262,21 @@ void ScummMetaEngine::removeSaveState(const char *target, int slot) const {
}
SaveStateDescriptor ScummMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
- Common::String filename = ScummEngine::makeSavegameName(target, slot, false);
- Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename);
-
- if (!in)
- return SaveStateDescriptor();
-
Common::String saveDesc;
- Scumm::getSavegameName(in, saveDesc, 0); // FIXME: heversion?!?
- delete in;
+ Graphics::Surface *thumbnail = nullptr;
+ SaveStateMetaInfos infos;
+ memset(&infos, 0, sizeof(infos));
+ SaveStateMetaInfos *infoPtr = &infos;
- // TODO: Cleanup
- Graphics::Surface *thumbnail = ScummEngine::loadThumbnailFromSlot(target, slot);
+ // FIXME: heversion?!?
+ if (!ScummEngine::querySaveMetaInfos(target, slot, 0, saveDesc, thumbnail, infoPtr)) {
+ return SaveStateDescriptor();
+ }
SaveStateDescriptor desc(slot, saveDesc);
desc.setThumbnail(thumbnail);
- SaveStateMetaInfos infos;
- memset(&infos, 0, sizeof(infos));
- if (ScummEngine::loadInfosFromSlot(target, slot, &infos)) {
+ if (infoPtr) {
int day = (infos.date >> 24) & 0xFF;
int month = (infos.date >> 16) & 0xFF;
int year = infos.date & 0xFFFF;
diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h
index aaf84c1471..6717ea9b06 100644
--- a/engines/scumm/detection_tables.h
+++ b/engines/scumm/detection_tables.h
@@ -299,7 +299,11 @@ static const GameSettings gameVariantsTable[] = {
// Changed o_getResourceSize to cover all resource types
{"farm", "", 0, GID_HEGAME, 6, 73, MDT_NONE, GF_USE_KEY, UNK, GUIO3(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT)},
- {"puttzoo", "", 0, GID_HEGAME, 6, 73, MDT_NONE, GF_USE_KEY, UNK, GUIO3(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT)},
+ {"puttzoo", "", 0, GID_PUTTZOO, 6, 73, MDT_NONE, GF_USE_KEY, UNK, GUIO3(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT)},
+ {"puttzoo", "HE 72", 0, GID_PUTTZOO, 6, 72, MDT_NONE, GF_USE_KEY | GF_HE_985, UNK, GUIO3(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT)},
+ {"puttzoo", "HE 98.5", 0, GID_PUTTZOO, 6, 98, MDT_NONE, GF_USE_KEY | GF_HE_985, UNK, GUIO3(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT)},
+ {"puttzoo", "HE 99", 0, GID_PUTTZOO, 6, 99, MDT_NONE, GF_USE_KEY, UNK, GUIO3(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT)},
+ {"puttzoo", "HE 100", 0, GID_PUTTZOO, 6, 100, MDT_NONE, GF_USE_KEY, UNK, GUIO3(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT)},
// Added VAR_PLATFORM variable
{"jungle", "", 0, GID_HEGAME, 6, 74, MDT_NONE, GF_USE_KEY, UNK, GUIO3(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT)},
@@ -860,6 +864,7 @@ static const GameFilenamePattern gameFilenamesTable[] = {
{ "puttzoo", "Zoo Demo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 },
{ "puttzoo", "Putt-Putt Saves the Zoo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 },
{ "puttzoo", "game", kGenHEPC, Common::EN_ANY, Common::kPlatformIOS, 0 },
+ { "puttzoo", "pp3_unlocked", kGenHEPC, Common::EN_ANY, Common::kPlatformWindows, 0 },
{ "SamsFunShop", "SamsFunShop", kGenHEPC, UNK_LANG, UNK, 0 },
{ "SamsFunShop", "Sam's FunShop", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 },
diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index 4c1fdaf673..1bb4a28f65 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -1769,11 +1769,8 @@ void Gdi::drawBitmap(const byte *ptr, VirtScreen *vs, int x, const int y, const
// Check whether lights are turned on or not
const bool lightsOn = _vm->isLightOn();
- if (_vm->_game.features & GF_SMALL_HEADER) {
+ if ((_vm->_game.features & GF_SMALL_HEADER) || _vm->_game.version == 8) {
smap_ptr = ptr;
- } else if (_vm->_game.version == 8) {
- // Skip to the BSTR->WRAP->OFFS chunk
- smap_ptr = ptr + 24;
} else {
smap_ptr = _vm->findResource(MKTAG('S','M','A','P'), ptr);
assert(smap_ptr);
@@ -1887,8 +1884,14 @@ bool Gdi::drawStrip(byte *dstPtr, VirtScreen *vs, int x, int y, const int width,
smapLen = READ_LE_UINT32(smap_ptr);
if (stripnr * 4 + 4 < smapLen)
offset = READ_LE_UINT32(smap_ptr + stripnr * 4 + 4);
+ } else if (_vm->_game.version == 8) {
+ smapLen = READ_BE_UINT32(smap_ptr + 4);
+ // Skip to the BSTR->WRAP->OFFS chunk
+ smap_ptr += 24;
+ if (stripnr * 4 + 8 < smapLen)
+ offset = READ_LE_UINT32(smap_ptr + stripnr * 4 + 8);
} else {
- smapLen = READ_BE_UINT32(smap_ptr);
+ smapLen = READ_BE_UINT32(smap_ptr + 4);
if (stripnr * 4 + 8 < smapLen)
offset = READ_LE_UINT32(smap_ptr + stripnr * 4 + 8);
}
diff --git a/engines/scumm/he/script_v100he.cpp b/engines/scumm/he/script_v100he.cpp
index 6d030bb125..c26e3f57ea 100644
--- a/engines/scumm/he/script_v100he.cpp
+++ b/engines/scumm/he/script_v100he.cpp
@@ -2148,7 +2148,7 @@ void ScummEngine_v100he::o100_systemOps() {
break;
case 132:
// Confirm shutdown
- quitGame();
+ confirmExitDialog();
break;
case 133:
quitGame();
diff --git a/engines/scumm/he/script_v70he.cpp b/engines/scumm/he/script_v70he.cpp
index adb2fcac2e..9259e0db2f 100644
--- a/engines/scumm/he/script_v70he.cpp
+++ b/engines/scumm/he/script_v70he.cpp
@@ -309,7 +309,7 @@ void ScummEngine_v70he::o70_systemOps() {
break;
case 160:
// Confirm shutdown
- quitGame();
+ confirmExitDialog();
break;
case 244:
quitGame();
diff --git a/engines/scumm/he/script_v72he.cpp b/engines/scumm/he/script_v72he.cpp
index 7c5221e146..cfa2be7275 100644
--- a/engines/scumm/he/script_v72he.cpp
+++ b/engines/scumm/he/script_v72he.cpp
@@ -158,7 +158,7 @@ int ScummEngine_v72he::readArray(int array, int idx2, int idx1) {
ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array));
- if (ah == NULL || ah->data == NULL)
+ if (!ah)
error("readArray: invalid array %d (%d)", array, readVar(array));
if (idx2 < (int)FROM_LE_32(ah->dim2start) || idx2 > (int)FROM_LE_32(ah->dim2end) ||
@@ -1199,7 +1199,7 @@ void ScummEngine_v72he::o72_systemOps() {
break;
case 160:
// Confirm shutdown
- quitGame();
+ confirmExitDialog();
break;
case 244:
quitGame();
diff --git a/engines/scumm/imuse_digi/dimuse_sndmgr.cpp b/engines/scumm/imuse_digi/dimuse_sndmgr.cpp
index abd0d68e56..26e248fbca 100644
--- a/engines/scumm/imuse_digi/dimuse_sndmgr.cpp
+++ b/engines/scumm/imuse_digi/dimuse_sndmgr.cpp
@@ -443,11 +443,13 @@ ImuseDigiSndMgr::SoundDesc *ImuseDigiSndMgr::openSound(int32 soundId, const char
} else if (soundName[0] == 0) {
if (sound->bundle->decompressSampleByIndex(soundId, 0, 0x2000, &ptr, 0, header_outside) == 0 || ptr == NULL) {
closeSound(sound);
+ free(ptr);
return NULL;
}
} else {
if (sound->bundle->decompressSampleByName(soundName, 0, 0x2000, &ptr, header_outside) == 0 || ptr == NULL) {
closeSound(sound);
+ free(ptr);
return NULL;
}
}
@@ -667,15 +669,18 @@ int32 ImuseDigiSndMgr::getDataFromRegion(SoundDesc *soundDesc, int region, byte
if (scumm_stricmp(fileName, soundDesc->lastFileName) != 0) {
int32 offs = 0, len = 0;
Common::SeekableReadStream *cmpFile;
+#if defined(USE_FLAC) || defined(USE_VORBIS) || defined(USE_MAD)
uint8 soundMode = 0;
+#endif
sprintf(fileName, "%s_reg%03d.fla", soundDesc->name, region);
cmpFile = soundDesc->bundle->getFile(fileName, offs, len);
if (len) {
#ifndef USE_FLAC
error("FLAC library compiled support needed");
-#endif
+#else
soundMode = 3;
+#endif
}
if (!len) {
sprintf(fileName, "%s_reg%03d.ogg", soundDesc->name, region);
@@ -683,8 +688,9 @@ int32 ImuseDigiSndMgr::getDataFromRegion(SoundDesc *soundDesc, int region, byte
if (len) {
#ifndef USE_VORBIS
error("Vorbis library compiled support needed");
-#endif
+#else
soundMode = 2;
+#endif
}
}
if (!len) {
@@ -693,8 +699,9 @@ int32 ImuseDigiSndMgr::getDataFromRegion(SoundDesc *soundDesc, int region, byte
if (len) {
#ifndef USE_MAD
error("Mad library compiled support needed");
-#endif
+#else
soundMode = 1;
+#endif
}
}
assert(len);
diff --git a/engines/scumm/insane/insane_enemy.cpp b/engines/scumm/insane/insane_enemy.cpp
index 3876966fd1..d711b63342 100644
--- a/engines/scumm/insane/insane_enemy.cpp
+++ b/engines/scumm/insane/insane_enemy.cpp
@@ -1519,6 +1519,7 @@ void Insane::chooseEnemyWeaponAnim(int32 buttons) {
case INV_BOOT:
case INV_HAND:
case INV_DUST:
+ // fallthrough
default:
switchEnemyWeapon();
}
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index 28884d7f78..d43db1e5f1 100644
--- a/engines/scumm/module.mk
+++ b/engines/scumm/module.mk
@@ -35,22 +35,23 @@ MODULE_OBJS := \
midiparser_ro.o \
object.o \
palette.o \
- player_apple2.o \
- player_mac.o \
- player_mod.o \
- player_nes.o \
- player_pce.o \
- player_sid.o \
- player_towns.o \
- player_v1.o \
- player_v2.o \
- player_v2a.o \
- player_v2base.o \
- player_v2cms.o \
- player_v3a.o \
- player_v3m.o \
- player_v4a.o \
- player_v5m.o \
+ players/player_ad.o \
+ players/player_apple2.o \
+ players/player_mac.o \
+ players/player_mod.o \
+ players/player_nes.o \
+ players/player_pce.o \
+ players/player_sid.o \
+ players/player_towns.o \
+ players/player_v1.o \
+ players/player_v2.o \
+ players/player_v2a.o \
+ players/player_v2base.o \
+ players/player_v2cms.o \
+ players/player_v3a.o \
+ players/player_v3m.o \
+ players/player_v4a.o \
+ players/player_v5m.o \
resource_v2.o \
resource_v3.o \
resource_v4.o \
diff --git a/engines/scumm/players/player_ad.cpp b/engines/scumm/players/player_ad.cpp
new file mode 100644
index 0000000000..20630e1cb9
--- /dev/null
+++ b/engines/scumm/players/player_ad.cpp
@@ -0,0 +1,959 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "scumm/players/player_ad.h"
+#include "scumm/imuse/imuse.h"
+#include "scumm/scumm.h"
+#include "scumm/resource.h"
+
+#include "audio/fmopl.h"
+
+#include "common/textconsole.h"
+#include "common/config-manager.h"
+
+namespace Scumm {
+
+#define AD_CALLBACK_FREQUENCY 472
+
+Player_AD::Player_AD(ScummEngine *scumm, Audio::Mixer *mixer)
+ : _vm(scumm), _mixer(mixer), _rate(mixer->getOutputRate()) {
+ _opl2 = OPL::Config::create();
+ if (!_opl2->init(_rate)) {
+ error("Could not initialize OPL2 emulator");
+ }
+
+ _samplesPerCallback = _rate / AD_CALLBACK_FREQUENCY;
+ _samplesPerCallbackRemainder = _rate % AD_CALLBACK_FREQUENCY;
+ _samplesTillCallback = 0;
+ _samplesTillCallbackRemainder = 0;
+
+ memset(_registerBackUpTable, 0, sizeof(_registerBackUpTable));
+ writeReg(0x01, 0x00);
+ writeReg(0xBD, 0x00);
+ writeReg(0x08, 0x00);
+ writeReg(0x01, 0x20);
+
+ _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+
+ _engineMusicTimer = 0;
+ _soundPlaying = -1;
+
+ _curOffset = 0;
+
+ _sfxTimer = 4;
+ _rndSeed = 1;
+
+ memset(_channels, 0, sizeof(_channels));
+ memset(_sfxResource, 0, sizeof(_sfxResource));
+ memset(_sfxPriority, 0, sizeof(_sfxPriority));
+}
+
+Player_AD::~Player_AD() {
+ _mixer->stopHandle(_soundHandle);
+
+ stopAllSounds();
+ Common::StackLock lock(_mutex);
+ delete _opl2;
+ _opl2 = 0;
+}
+
+void Player_AD::setMusicVolume(int vol) {
+ // HACK: We ignore the parameter and set up the volume specified in the
+ // config manager. This allows us to differentiate between music and sfx
+ // volume changes.
+ setupVolume();
+}
+
+void Player_AD::startSound(int sound) {
+ Common::StackLock lock(_mutex);
+
+ // Query the sound resource
+ const byte *res = _vm->getResourceAddress(rtSound, sound);
+
+ if (res[2] == 0x80) {
+ // Stop the current sounds
+ stopAllSounds();
+
+ // Lock the new music resource
+ _soundPlaying = sound;
+ _vm->_res->lock(rtSound, _soundPlaying);
+
+ // Start the new music resource
+ _resource = res;
+ startMusic();
+ } else {
+ // Only try to start a sfx when no music is playing.
+ if (_soundPlaying == -1) {
+ const byte priority = res[0];
+ const byte channel = res[1];
+
+ // Check for out of bounds access
+ if (channel >= 3) {
+ warning("AdLib sfx resource %d uses channel %d", sound, channel);
+ return;
+ }
+
+ // Check whether the channel is free or the priority of the new
+ // sfx resource is above the old one.
+ if (_channels[channel * 3 + 0].state
+ || _channels[channel * 3 + 1].state
+ || _channels[channel * 3 + 2].state) {
+ if (_sfxPriority[channel] > priority) {
+ return;
+ }
+ }
+
+ // Lock the new resource
+ _sfxResource[channel] = sound;
+ _sfxPriority[channel] = priority;
+ _vm->_res->lock(rtSound, sound);
+
+ // Start the actual sfx resource
+ _resource = res;
+ startSfx();
+ }
+ }
+
+ // Setup the sound volume
+ setupVolume();
+}
+
+void Player_AD::stopSound(int sound) {
+ Common::StackLock lock(_mutex);
+
+ if (sound == _soundPlaying) {
+ stopAllSounds();
+ } else {
+ for (int i = 0; i < 3; ++i) {
+ if (_sfxResource[i] == sound) {
+ if (_channels[i * 3 + 0].state
+ || _channels[i * 3 + 1].state
+ || _channels[i * 3 + 2].state) {
+ // Unlock the sound resource
+ _vm->_res->unlock(rtSound, sound);
+
+ // Stop the actual sfx playback
+ _channels[i * 3 + 0].state = 0;
+ _channels[i * 3 + 1].state = 0;
+ _channels[i * 3 + 2].state = 0;
+ clearChannel(i * 3 + 0);
+ clearChannel(i * 3 + 1);
+ clearChannel(i * 3 + 2);
+ }
+ }
+ }
+ }
+}
+
+void Player_AD::stopAllSounds() {
+ Common::StackLock lock(_mutex);
+
+ // Unlock the music resource if present
+ if (_soundPlaying != -1) {
+ _vm->_res->unlock(rtSound, _soundPlaying);
+ _soundPlaying = -1;
+ }
+
+ // Stop the music playback
+ _curOffset = 0;
+
+ // Unloack all used sfx resources
+ for (int i = 0; i < 3; ++i) {
+ if (_channels[i * 3 + 0].state || _channels[i * 3 + 1].state || _channels[i * 3 + 2].state) {
+ _vm->_res->unlock(rtSound, _sfxResource[i]);
+ }
+ }
+
+ // Reset all the sfx channels
+ for (int i = 0; i < 9; ++i) {
+ _channels[i].state = 0;
+ clearChannel(i);
+ }
+
+ writeReg(0xBD, 0x00);
+}
+
+int Player_AD::getMusicTimer() {
+ return _engineMusicTimer;
+}
+
+int Player_AD::getSoundStatus(int sound) const {
+ return (sound == _soundPlaying);
+}
+
+void Player_AD::saveLoadWithSerializer(Serializer *ser) {
+ Common::StackLock lock(_mutex);
+
+ if (ser->getVersion() < VER(95)) {
+ IMuse *dummyImuse = IMuse::create(_vm->_system, NULL, NULL);
+ dummyImuse->save_or_load(ser, _vm, false);
+ delete dummyImuse;
+ return;
+ }
+
+ // TODO: Be nicer than the original and save the data to continue the
+ // currently played sound resources on load?
+}
+
+int Player_AD::readBuffer(int16 *buffer, const int numSamples) {
+ Common::StackLock lock(_mutex);
+
+ int len = numSamples;
+
+ while (len > 0) {
+ if (!_samplesTillCallback) {
+ // Run the update callback for music or sfx depending on which is
+ // active.
+ if (_curOffset) {
+ updateMusic();
+ } else {
+ updateSfx();
+ }
+
+ _samplesTillCallback = _samplesPerCallback;
+ _samplesTillCallbackRemainder += _samplesPerCallbackRemainder;
+ if (_samplesTillCallbackRemainder >= AD_CALLBACK_FREQUENCY) {
+ ++_samplesTillCallback;
+ _samplesTillCallbackRemainder -= AD_CALLBACK_FREQUENCY;
+ }
+ }
+
+ const int samplesToRead = MIN(len, _samplesTillCallback);
+ _opl2->readBuffer(buffer, samplesToRead);
+
+ buffer += samplesToRead;
+ len -= samplesToRead;
+ _samplesTillCallback -= samplesToRead;
+ }
+
+ return numSamples;
+}
+
+void Player_AD::setupVolume() {
+ // Setup the correct volume
+ int soundVolumeMusic = CLIP<int>(ConfMan.getInt("music_volume"), 0, Audio::Mixer::kMaxChannelVolume);
+ int soundVolumeSfx = CLIP<int>(ConfMan.getInt("sfx_volume"), 0, Audio::Mixer::kMaxChannelVolume);
+ if (ConfMan.hasKey("mute")) {
+ if (ConfMan.getBool("mute")) {
+ soundVolumeMusic = 0;
+ soundVolumeSfx = 0;
+ }
+ }
+
+ // In case a music is being played set the music volume. Set the sfx
+ // volume otherwise. This is safe because in the latter case either
+ // sfx are playing or there is no sound being played at all.
+ if (_soundPlaying != -1) {
+ _mixer->setChannelVolume(_soundHandle, soundVolumeMusic);
+ } else {
+ _mixer->setChannelVolume(_soundHandle, soundVolumeSfx);
+ }
+}
+
+void Player_AD::writeReg(int r, int v) {
+ if (r >= 0 && r < ARRAYSIZE(_registerBackUpTable)) {
+ _registerBackUpTable[r] = v;
+ }
+ _opl2->writeReg(r, v);
+}
+
+uint8 Player_AD::readReg(int r) const {
+ if (r >= 0 && r < ARRAYSIZE(_registerBackUpTable)) {
+ return _registerBackUpTable[r];
+ } else {
+ return 0;
+ }
+}
+
+void Player_AD::setupChannel(const uint channel, const byte *instrOffset) {
+ instrOffset += 2;
+ writeReg(0xC0 + channel, *instrOffset++);
+ setupOperator(_operatorOffsetTable[channel * 2 + 0], instrOffset);
+ setupOperator(_operatorOffsetTable[channel * 2 + 1], instrOffset);
+}
+
+void Player_AD::setupOperator(const uint opr, const byte *&instrOffset) {
+ writeReg(0x20 + opr, *instrOffset++);
+ writeReg(0x40 + opr, *instrOffset++);
+ writeReg(0x60 + opr, *instrOffset++);
+ writeReg(0x80 + opr, *instrOffset++);
+ writeReg(0xE0 + opr, *instrOffset++);
+}
+
+const int Player_AD::_operatorOffsetTable[18] = {
+ 0, 3, 1, 4,
+ 2, 5, 8, 11,
+ 9, 12, 10, 13,
+ 16, 19, 17, 20,
+ 18, 21
+};
+
+// Music
+
+void Player_AD::startMusic() {
+ memset(_instrumentOffset, 0, sizeof(_instrumentOffset));
+ memset(_channelLastEvent, 0, sizeof(_channelLastEvent));
+ memset(_channelFrequency, 0, sizeof(_channelFrequency));
+ memset(_channelB0Reg, 0, sizeof(_channelB0Reg));
+
+ _voiceChannels = 0;
+ uint instruments = _resource[10];
+ for (uint i = 0; i < instruments; ++i) {
+ const int instrIndex = _resource[11 + i] - 1;
+ if (0 <= instrIndex && instrIndex < 16) {
+ _instrumentOffset[instrIndex] = i * 16 + 16 + 3;
+ _voiceChannels |= _resource[_instrumentOffset[instrIndex] + 13];
+ }
+ }
+
+ if (_voiceChannels) {
+ _mdvdrState = 0x20;
+ _voiceChannels = 6;
+ } else {
+ _mdvdrState = 0;
+ _voiceChannels = 9;
+ }
+
+ _curOffset = 0x93;
+ // TODO: is this the same for Loom?
+ _nextEventTimer = 40;
+ _engineMusicTimer = 0;
+ _internalMusicTimer = 0;
+ _musicTimer = 0;
+
+ writeReg(0xBD, _mdvdrState);
+
+ const bool isLoom = (_vm->_game.id == GID_LOOM);
+ _timerLimit = isLoom ? 473 : 256;
+ _musicTicks = _resource[3] * (isLoom ? 2 : 1);
+ _loopFlag = (_resource[4] == 0);
+ _musicLoopStart = READ_LE_UINT16(_resource + 5);
+}
+
+void Player_AD::updateMusic() {
+ _musicTimer += _musicTicks;
+ if (_musicTimer < _timerLimit) {
+ return;
+ }
+ _musicTimer -= _timerLimit;
+
+ ++_internalMusicTimer;
+ if (_internalMusicTimer > 120) {
+ _internalMusicTimer = 0;
+ ++_engineMusicTimer;
+ }
+
+ --_nextEventTimer;
+ if (_nextEventTimer) {
+ return;
+ }
+
+ while (true) {
+ uint command = _resource[_curOffset++];
+ if (command == 0xFF) {
+ // META EVENT
+ // Get the command number.
+ command = _resource[_curOffset++];
+ if (command == 47) {
+ // End of track
+ if (_loopFlag) {
+ // In case the track is looping jump to the start.
+ _curOffset = _musicLoopStart;
+ _nextEventTimer = 0;
+ } else {
+ // Otherwise completely stop playback.
+ stopAllSounds();
+ }
+ return;
+ } else if (command == 88) {
+ // This is proposedly a debug information insertion. The CMS
+ // player code handles this differently, but is still using
+ // the same resources...
+ _curOffset += 5;
+ } else if (command == 81) {
+ // Change tempo. This is used exclusively in Loom.
+ const uint timing = _resource[_curOffset + 2] | (_resource[_curOffset + 1] << 8);
+ _musicTicks = 0x73000 / timing;
+ command = _resource[_curOffset++];
+ _curOffset += command;
+ } else {
+ // In case an unknown meta event occurs just skip over the
+ // data by using the length supplied.
+ command = _resource[_curOffset++];
+ _curOffset += command;
+ }
+ } else {
+ if (command >= 0x90) {
+ // NOTE ON
+ // Extract the channel number and save it in command.
+ command -= 0x90;
+
+ const uint instrOffset = _instrumentOffset[command];
+ if (instrOffset) {
+ if (_resource[instrOffset + 13] != 0) {
+ setupRhythm(_resource[instrOffset + 13], instrOffset);
+ } else {
+ int channel = findFreeChannel();
+ if (channel != -1) {
+ noteOff(channel);
+ setupChannel(channel, instrOffset);
+ _channelLastEvent[channel] = command + 0x90;
+ _channelFrequency[channel] = _resource[_curOffset];
+ setupFrequency(channel, _resource[_curOffset]);
+ }
+ }
+ }
+ } else {
+ // NOTE OFF
+ const uint note = _resource[_curOffset];
+ command += 0x10;
+
+ // Find the output channel which plays the note.
+ uint channel = 0xFF;
+ for (uint i = 0; i < _voiceChannels; ++i) {
+ if (_channelFrequency[i] == note && _channelLastEvent[i] == command) {
+ channel = i;
+ break;
+ }
+ }
+
+ if (channel != 0xFF) {
+ // In case a output channel playing the note was found,
+ // stop it.
+ noteOff(channel);
+ } else {
+ // In case there is no such note this will disable the
+ // rhythm instrument played on the channel.
+ command -= 0x90;
+ const uint instrOffset = _instrumentOffset[command];
+ if (instrOffset && _resource[instrOffset + 13] != 0) {
+ const uint rhythmInstr = _resource[instrOffset + 13];
+ if (rhythmInstr < 6) {
+ _mdvdrState &= _mdvdrTable[rhythmInstr] ^ 0xFF;
+ writeReg(0xBD, _mdvdrState);
+ }
+ }
+ }
+ }
+
+ _curOffset += 2;
+ }
+
+ // In case there is a delay till the next event stop handling.
+ if (_resource[_curOffset] != 0) {
+ break;
+ }
+ ++_curOffset;
+ }
+
+ _nextEventTimer = _resource[_curOffset++];
+ if (_nextEventTimer & 0x80) {
+ _nextEventTimer -= 0x80;
+ _nextEventTimer <<= 7;
+ _nextEventTimer |= _resource[_curOffset++];
+ }
+
+ _nextEventTimer >>= (_vm->_game.id == GID_LOOM) ? 2 : 1;
+ if (!_nextEventTimer) {
+ _nextEventTimer = 1;
+ }
+}
+
+void Player_AD::noteOff(uint channel) {
+ _channelLastEvent[channel] = 0;
+ writeReg(0xB0 + channel, _channelB0Reg[channel] & 0xDF);
+}
+
+int Player_AD::findFreeChannel() {
+ for (uint i = 0; i < _voiceChannels; ++i) {
+ if (!_channelLastEvent[i]) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+void Player_AD::setupFrequency(uint channel, int8 frequency) {
+ frequency -= 31;
+ if (frequency < 0) {
+ frequency = 0;
+ }
+
+ uint octave = 0;
+ while (frequency >= 12) {
+ frequency -= 12;
+ ++octave;
+ }
+
+ const uint noteFrequency = _noteFrequencies[frequency];
+ octave <<= 2;
+ octave |= noteFrequency >> 8;
+ octave |= 0x20;
+ writeReg(0xA0 + channel, noteFrequency & 0xFF);
+ _channelB0Reg[channel] = octave;
+ writeReg(0xB0 + channel, octave);
+}
+
+void Player_AD::setupRhythm(uint rhythmInstr, uint instrOffset) {
+ if (rhythmInstr == 1) {
+ setupChannel(6, instrOffset);
+ writeReg(0xA6, _resource[instrOffset++]);
+ writeReg(0xB6, _resource[instrOffset] & 0xDF);
+ _mdvdrState |= 0x10;
+ writeReg(0xBD, _mdvdrState);
+ } else if (rhythmInstr < 6) {
+ const byte *secondOperatorOffset = _resource + instrOffset + 8;
+ setupOperator(_rhythmOperatorTable[rhythmInstr], secondOperatorOffset);
+ writeReg(0xA0 + _rhythmChannelTable[rhythmInstr], _resource[instrOffset++]);
+ writeReg(0xB0 + _rhythmChannelTable[rhythmInstr], _resource[instrOffset++] & 0xDF);
+ writeReg(0xC0 + _rhythmChannelTable[rhythmInstr], _resource[instrOffset]);
+ _mdvdrState |= _mdvdrTable[rhythmInstr];
+ writeReg(0xBD, _mdvdrState);
+ }
+}
+
+const uint Player_AD::_noteFrequencies[12] = {
+ 0x200, 0x21E, 0x23F, 0x261,
+ 0x285, 0x2AB, 0x2D4, 0x300,
+ 0x32E, 0x35E, 0x390, 0x3C7
+};
+
+const uint Player_AD::_mdvdrTable[6] = {
+ 0x00, 0x10, 0x08, 0x04, 0x02, 0x01
+};
+
+const uint Player_AD::_rhythmOperatorTable[6] = {
+ 0x00, 0x00, 0x14, 0x12, 0x15, 0x11
+};
+
+const uint Player_AD::_rhythmChannelTable[6] = {
+ 0x00, 0x00, 0x07, 0x08, 0x08, 0x07
+};
+
+// SFX
+
+void Player_AD::startSfx() {
+ writeReg(0xBD, 0x00);
+
+ // The second byte of the resource defines the logical channel where
+ // the sound effect should be played.
+ const int startChannel = _resource[1] * 3;
+
+ // Clear the channel.
+ _channels[startChannel + 0].state = 0;
+ _channels[startChannel + 1].state = 0;
+ _channels[startChannel + 2].state = 0;
+
+ clearChannel(startChannel + 0);
+ clearChannel(startChannel + 1);
+ clearChannel(startChannel + 2);
+
+ // Set up the first channel to pick up playback.
+ _channels[startChannel].currentOffset = _channels[startChannel].startOffset = _resource + 2;
+ _channels[startChannel].state = 1;
+
+ // Scan for the start of the other channels and set them up if required.
+ int curChannel = startChannel + 1;
+ const byte *bufferPosition = _resource + 2;
+ uint8 command = 0;
+ while ((command = *bufferPosition) != 0xFF) {
+ switch (command) {
+ case 1:
+ // INSTRUMENT DEFINITION
+ bufferPosition += 15;
+ break;
+
+ case 2:
+ // NOTE DEFINITION
+ bufferPosition += 11;
+ break;
+
+ case 0x80:
+ // LOOP
+ bufferPosition += 1;
+ break;
+
+ default:
+ // START OF CHANNEL
+ bufferPosition += 1;
+ _channels[curChannel].currentOffset = bufferPosition;
+ _channels[curChannel].startOffset = bufferPosition;
+ _channels[curChannel].state = 1;
+ ++curChannel;
+ break;
+ }
+ }
+}
+
+void Player_AD::updateSfx() {
+ if (--_sfxTimer) {
+ return;
+ }
+ _sfxTimer = 4;
+
+ for (int i = 0; i <= 9; ++i) {
+ if (!_channels[i].state) {
+ continue;
+ }
+
+ updateChannel(i);
+ }
+}
+
+void Player_AD::clearChannel(int channel) {
+ writeReg(0xA0 + channel, 0x00);
+ writeReg(0xB0 + channel, 0x00);
+}
+
+void Player_AD::updateChannel(int channel) {
+ if (_channels[channel].state == 1) {
+ parseSlot(channel);
+ } else {
+ updateSlot(channel);
+ }
+}
+
+void Player_AD::parseSlot(int channel) {
+ while (true) {
+ const byte *curOffset = _channels[channel].currentOffset;
+
+ switch (*curOffset) {
+ case 1:
+ // INSTRUMENT DEFINITION
+ ++curOffset;
+ _channels[channel].instrumentData[0] = *(curOffset + 0);
+ _channels[channel].instrumentData[1] = *(curOffset + 2);
+ _channels[channel].instrumentData[2] = *(curOffset + 9);
+ _channels[channel].instrumentData[3] = *(curOffset + 8);
+ _channels[channel].instrumentData[4] = *(curOffset + 4);
+ _channels[channel].instrumentData[5] = *(curOffset + 3);
+ _channels[channel].instrumentData[6] = 0;
+
+ setupChannel(channel, curOffset);
+
+ writeReg(0xA0 + channel, *(curOffset + 0));
+ writeReg(0xB0 + channel, *(curOffset + 1) & 0xDF);
+
+ _channels[channel].currentOffset += 15;
+ break;
+
+ case 2:
+ // NOTE DEFINITION
+ ++curOffset;
+ _channels[channel].state = 2;
+ noteOffOn(channel);
+ parseNote(channel, 0, curOffset);
+ parseNote(channel, 1, curOffset);
+ return;
+
+ case 0x80:
+ // LOOP
+ _channels[channel].currentOffset = _channels[channel].startOffset;
+ break;
+
+ default:
+ // START OF CHANNEL
+ // When we encounter a start of another channel while playback
+ // it means that the current channel is finished. Thus, we will
+ // stop it.
+ clearChannel(channel);
+ _channels[channel].state = 0;
+
+ // If no channel of the sound effect is playing anymore, unlock
+ // the resource.
+ channel /= 3;
+ if (!_channels[channel + 0].state
+ && !_channels[channel + 1].state
+ && !_channels[channel + 2].state) {
+ _vm->_res->unlock(rtSound, _sfxResource[channel]);
+ }
+ return;
+ }
+ }
+}
+
+void Player_AD::updateSlot(int channel) {
+ const byte *curOffset = _channels[channel].currentOffset + 1;
+
+ for (int num = 0; num <= 1; ++num, curOffset += 5) {
+ if (!(*curOffset & 0x80)) {
+ continue;
+ }
+
+ const int note = channel * 2 + num;
+ bool updateNote = false;
+
+ if (_notes[note].state == 2) {
+ if (!--_notes[note].sustainTimer) {
+ updateNote = true;
+ }
+ } else {
+ updateNote = processNoteEnvelope(note, _notes[note].instrumentValue);
+
+ if (_notes[note].bias) {
+ writeRegisterSpecial(note, _notes[note].bias - _notes[note].instrumentValue, *curOffset & 0x07);
+ } else {
+ writeRegisterSpecial(note, _notes[note].instrumentValue, *curOffset & 0x07);
+ }
+ }
+
+ if (updateNote) {
+ if (processNote(note, curOffset)) {
+ if (!(*curOffset & 0x08)) {
+ _channels[channel].currentOffset += 11;
+ _channels[channel].state = 1;
+ continue;
+ } else if (*curOffset & 0x10) {
+ noteOffOn(channel);
+ }
+
+ _notes[note].state = -1;
+ processNote(note, curOffset);
+ }
+ }
+
+ if ((*curOffset & 0x20) && !--_notes[note].playTime) {
+ _channels[channel].currentOffset += 11;
+ _channels[channel].state = 1;
+ }
+ }
+}
+
+void Player_AD::parseNote(int channel, int num, const byte *offset) {
+ if (num) {
+ offset += 5;
+ }
+
+ if (*offset & 0x80) {
+ const int note = channel * 2 + num;
+ _notes[note].state = -1;
+ processNote(note, offset);
+ _notes[note].playTime = 0;
+
+ if (*offset & 0x20) {
+ _notes[note].playTime = (*(offset + 4) >> 4) * 118;
+ _notes[note].playTime += (*(offset + 4) & 0x0F) * 8;
+ }
+ }
+}
+
+bool Player_AD::processNote(int note, const byte *offset) {
+ if (++_notes[note].state == 4) {
+ return true;
+ }
+
+ const int instrumentDataOffset = *offset & 0x07;
+ _notes[note].bias = _noteBiasTable[instrumentDataOffset];
+
+ uint8 instrumentDataValue = 0;
+ if (_notes[note].state == 0) {
+ instrumentDataValue = _channels[note / 2].instrumentData[instrumentDataOffset];
+ }
+
+ uint8 noteInstrumentValue = readRegisterSpecial(note, instrumentDataValue, instrumentDataOffset);
+ if (_notes[note].bias) {
+ noteInstrumentValue = _notes[note].bias - noteInstrumentValue;
+ }
+ _notes[note].instrumentValue = noteInstrumentValue;
+
+ if (_notes[note].state == 2) {
+ _notes[note].sustainTimer = _numStepsTable[*(offset + 3) >> 4];
+
+ if (*offset & 0x40) {
+ _notes[note].sustainTimer = (((getRnd() << 8) * _notes[note].sustainTimer) >> 16) + 1;
+ }
+ } else {
+ int timer1, timer2;
+ if (_notes[note].state == 3) {
+ timer1 = *(offset + 3) & 0x0F;
+ timer2 = 0;
+ } else {
+ timer1 = *(offset + _notes[note].state + 1) >> 4;
+ timer2 = *(offset + _notes[note].state + 1) & 0x0F;
+ }
+
+ int adjustValue = ((_noteAdjustTable[timer2] * _noteAdjustScaleTable[instrumentDataOffset]) >> 16) - noteInstrumentValue;
+ setupNoteEnvelopeState(note, _numStepsTable[timer1], adjustValue);
+ }
+
+ return false;
+}
+
+void Player_AD::noteOffOn(int channel) {
+ const uint8 regValue = readReg(0xB0 | channel);
+ writeReg(0xB0 | channel, regValue & 0xDF);
+ writeReg(0xB0 | channel, regValue | 0x20);
+}
+
+void Player_AD::writeRegisterSpecial(int note, uint8 value, int offset) {
+ if (offset == 6) {
+ return;
+ }
+
+ // Division by 2 extracts the channel number out of the note.
+ note /= 2;
+
+ uint8 regNum;
+ if (_useOperatorTable[offset]) {
+ regNum = _operatorOffsetTable[_channelOperatorOffsetTable[offset] + note * 2];
+ } else {
+ regNum = _channelOffsetTable[note];
+ }
+
+ regNum += _baseRegisterTable[offset];
+
+ uint8 regValue = readReg(regNum) & (~_registerMaskTable[offset]);
+ regValue |= value << _registerShiftTable[offset];
+
+ writeReg(regNum, regValue);
+}
+
+uint8 Player_AD::readRegisterSpecial(int note, uint8 defaultValue, int offset) {
+ if (offset == 6) {
+ return 0;
+ }
+
+ // Division by 2 extracts the channel number out of the note.
+ note /= 2;
+
+ uint8 regNum;
+ if (_useOperatorTable[offset]) {
+ regNum = _operatorOffsetTable[_channelOperatorOffsetTable[offset] + note * 2];
+ } else {
+ regNum = _channelOffsetTable[note];
+ }
+
+ regNum += _baseRegisterTable[offset];
+
+ uint8 regValue;
+ if (defaultValue) {
+ regValue = defaultValue;
+ } else {
+ regValue = readReg(regNum);
+ }
+
+ regValue &= _registerMaskTable[offset];
+ regValue >>= _registerShiftTable[offset];
+
+ return regValue;
+}
+
+void Player_AD::setupNoteEnvelopeState(int note, int steps, int adjust) {
+ _notes[note].preIncrease = 0;
+ if (ABS(adjust) > steps) {
+ _notes[note].preIncrease = 1;
+ _notes[note].adjust = adjust / steps;
+ _notes[note].envelope.stepIncrease = ABS(adjust % steps);
+ } else {
+ _notes[note].adjust = adjust;
+ _notes[note].envelope.stepIncrease = ABS(adjust);
+ }
+
+ _notes[note].envelope.step = steps;
+ _notes[note].envelope.stepCounter = 0;
+ _notes[note].envelope.timer = steps;
+}
+
+bool Player_AD::processNoteEnvelope(int note, int &instrumentValue) {
+ if (_notes[note].preIncrease) {
+ instrumentValue += _notes[note].adjust;
+ }
+
+ _notes[note].envelope.stepCounter += _notes[note].envelope.stepIncrease;
+ if (_notes[note].envelope.stepCounter >= _notes[note].envelope.step) {
+ _notes[note].envelope.stepCounter -= _notes[note].envelope.step;
+
+ if (_notes[note].adjust < 0) {
+ --instrumentValue;
+ } else {
+ ++instrumentValue;
+ }
+ }
+
+ if (--_notes[note].envelope.timer) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+uint8 Player_AD::getRnd() {
+ if (_rndSeed & 1) {
+ _rndSeed >>= 1;
+ _rndSeed ^= 0xB8;
+ } else {
+ _rndSeed >>= 1;
+ }
+
+ return _rndSeed;
+}
+
+const uint Player_AD::_noteBiasTable[7] = {
+ 0x00, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x00
+};
+
+const uint Player_AD::_numStepsTable[16] = {
+ 1, 4, 6, 8,
+ 10, 14, 18, 24,
+ 36, 64, 100, 160,
+ 240, 340, 600, 1200
+};
+
+const uint Player_AD::_noteAdjustScaleTable[7] = {
+ 255, 7, 63, 15, 63, 15, 63
+};
+
+const uint Player_AD::_noteAdjustTable[16] = {
+ 0, 4369, 8738, 13107,
+ 17476, 21845, 26214, 30583,
+ 34952, 39321, 43690, 48059,
+ 52428, 46797, 61166, 65535
+};
+
+const bool Player_AD::_useOperatorTable[7] = {
+ false, false, true, true, true, true, false
+};
+
+const uint Player_AD::_channelOffsetTable[11] = {
+ 0, 1, 2, 3,
+ 4, 5, 6, 7,
+ 8, 8, 7
+};
+
+const uint Player_AD::_channelOperatorOffsetTable[7] = {
+ 0, 0, 1, 1, 0, 0, 0
+};
+
+const uint Player_AD::_baseRegisterTable[7] = {
+ 0xA0, 0xC0, 0x40, 0x20, 0x40, 0x20, 0x00
+};
+
+const uint Player_AD::_registerMaskTable[7] = {
+ 0xFF, 0x0E, 0x3F, 0x0F, 0x3F, 0x0F, 0x00
+};
+
+const uint Player_AD::_registerShiftTable[7] = {
+ 0, 1, 0, 0, 0, 0, 0
+};
+
+} // End of namespace Scumm
diff --git a/engines/scumm/players/player_ad.h b/engines/scumm/players/player_ad.h
new file mode 100644
index 0000000000..fbb65fbe24
--- /dev/null
+++ b/engines/scumm/players/player_ad.h
@@ -0,0 +1,190 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef SCUMM_PLAYERS_PLAYER_AD_H
+#define SCUMM_PLAYERS_PLAYER_AD_H
+
+#include "scumm/music.h"
+
+#include "audio/audiostream.h"
+#include "audio/mixer.h"
+
+#include "common/mutex.h"
+
+namespace OPL {
+class OPL;
+}
+
+namespace Scumm {
+
+class ScummEngine;
+
+/**
+ * Sound output for v3/v4 AdLib data.
+ */
+class Player_AD : public MusicEngine, public Audio::AudioStream {
+public:
+ Player_AD(ScummEngine *scumm, Audio::Mixer *mixer);
+ virtual ~Player_AD();
+
+ // MusicEngine API
+ virtual void setMusicVolume(int vol);
+ virtual void startSound(int sound);
+ virtual void stopSound(int sound);
+ virtual void stopAllSounds();
+ virtual int getMusicTimer();
+ virtual int getSoundStatus(int sound) const;
+
+ virtual void saveLoadWithSerializer(Serializer *ser);
+
+ // AudioStream API
+ virtual int readBuffer(int16 *buffer, const int numSamples);
+ virtual bool isStereo() const { return false; }
+ virtual bool endOfData() const { return false; }
+ virtual int getRate() const { return _rate; }
+
+private:
+ ScummEngine *const _vm;
+ Common::Mutex _mutex;
+ Audio::Mixer *const _mixer;
+ const int _rate;
+ Audio::SoundHandle _soundHandle;
+ void setupVolume();
+
+ OPL::OPL *_opl2;
+
+ int _samplesPerCallback;
+ int _samplesPerCallbackRemainder;
+ int _samplesTillCallback;
+ int _samplesTillCallbackRemainder;
+
+ int _soundPlaying;
+ int _engineMusicTimer;
+
+ // AdLib register utilities
+ uint8 _registerBackUpTable[256];
+ void writeReg(int r, int v);
+ uint8 readReg(int r) const;
+
+ // Instrument setup
+ void setupChannel(const uint channel, uint instrOffset) {
+ setupChannel(channel, _resource + instrOffset);
+ }
+ void setupChannel(const uint channel, const byte *instrOffset);
+ void setupOperator(const uint opr, const byte *&instrOffset);
+ static const int _operatorOffsetTable[18];
+
+ // Sound data
+ const byte *_resource;
+
+ // Music handling
+ void startMusic();
+ void updateMusic();
+ void noteOff(uint channel);
+ int findFreeChannel();
+ void setupFrequency(uint channel, int8 frequency);
+ void setupRhythm(uint rhythmInstr, uint instrOffset);
+
+ uint _timerLimit;
+ uint _musicTicks;
+ uint _musicTimer;
+ uint _internalMusicTimer;
+ bool _loopFlag;
+ uint _musicLoopStart;
+ uint _instrumentOffset[16];
+ uint _channelLastEvent[9];
+ uint _channelFrequency[9];
+ uint _channelB0Reg[9];
+
+ uint _mdvdrState;
+ uint _voiceChannels;
+
+ uint _curOffset;
+ uint _nextEventTimer;
+
+ static const uint _noteFrequencies[12];
+ static const uint _mdvdrTable[6];
+ static const uint _rhythmOperatorTable[6];
+ static const uint _rhythmChannelTable[6];
+
+ // SFX handling
+ void startSfx();
+ void updateSfx();
+ void clearChannel(int channel);
+ void updateChannel(int channel);
+ void parseSlot(int channel);
+ void updateSlot(int channel);
+ void parseNote(int channel, int num, const byte *offset);
+ bool processNote(int note, const byte *offset);
+ void noteOffOn(int channel);
+ void writeRegisterSpecial(int note, uint8 value, int offset);
+ uint8 readRegisterSpecial(int note, uint8 defaultValue, int offset);
+ void setupNoteEnvelopeState(int note, int steps, int adjust);
+ bool processNoteEnvelope(int note, int &instrumentValue);
+
+ int _sfxTimer;
+
+ int _sfxResource[3];
+ int _sfxPriority[3];
+
+ struct Channel {
+ int state;
+ const byte *currentOffset;
+ const byte *startOffset;
+ uint8 instrumentData[7];
+ } _channels[11];
+
+ uint8 _rndSeed;
+ uint8 getRnd();
+
+ struct Note {
+ int state;
+ int playTime;
+ int sustainTimer;
+ int instrumentValue;
+ int bias;
+ int preIncrease;
+ int adjust;
+
+ struct Envelope {
+ int stepIncrease;
+ int step;
+ int stepCounter;
+ int timer;
+ } envelope;
+ } _notes[22];
+
+ static const uint _noteBiasTable[7];
+ static const uint _numStepsTable[16];
+ static const uint _noteAdjustScaleTable[7];
+ static const uint _noteAdjustTable[16];
+ static const bool _useOperatorTable[7];
+ static const uint _channelOffsetTable[11];
+ static const uint _channelOperatorOffsetTable[7];
+ static const uint _baseRegisterTable[7];
+ static const uint _registerMaskTable[7];
+ static const uint _registerShiftTable[7];
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/player_apple2.cpp b/engines/scumm/players/player_apple2.cpp
index 58e4f78a94..87b8100f22 100644
--- a/engines/scumm/player_apple2.cpp
+++ b/engines/scumm/players/player_apple2.cpp
@@ -21,7 +21,7 @@
*/
#include "engines/engine.h"
-#include "scumm/player_apple2.h"
+#include "scumm/players/player_apple2.h"
#include "scumm/scumm.h"
namespace Scumm {
diff --git a/engines/scumm/player_apple2.h b/engines/scumm/players/player_apple2.h
index e1ec9d8946..9930a4f95d 100644
--- a/engines/scumm/player_apple2.h
+++ b/engines/scumm/players/player_apple2.h
@@ -20,8 +20,8 @@
*
*/
-#ifndef SCUMM_PLAYER_APPLEII_H
-#define SCUMM_PLAYER_APPLEII_H
+#ifndef SCUMM_PLAYERS_PLAYER_APPLEII_H
+#define SCUMM_PLAYERS_PLAYER_APPLEII_H
#include "common/mutex.h"
#include "common/scummsys.h"
diff --git a/engines/scumm/player_mac.cpp b/engines/scumm/players/player_mac.cpp
index a60736df5e..281eec5336 100644
--- a/engines/scumm/player_mac.cpp
+++ b/engines/scumm/players/player_mac.cpp
@@ -24,7 +24,7 @@
#include "common/translation.h"
#include "engines/engine.h"
#include "gui/message.h"
-#include "scumm/player_mac.h"
+#include "scumm/players/player_mac.h"
#include "scumm/resource.h"
#include "scumm/scumm.h"
#include "scumm/imuse/imuse.h"
diff --git a/engines/scumm/player_mac.h b/engines/scumm/players/player_mac.h
index 09307b4e57..7f9f42c34e 100644
--- a/engines/scumm/player_mac.h
+++ b/engines/scumm/players/player_mac.h
@@ -20,8 +20,8 @@
*
*/
-#ifndef SCUMM_PLAYER_MAC_H
-#define SCUMM_PLAYER_MAC_H
+#ifndef SCUMM_PLAYERS_PLAYER_MAC_H
+#define SCUMM_PLAYERS_PLAYER_MAC_H
#include "common/scummsys.h"
#include "common/util.h"
diff --git a/engines/scumm/player_mod.cpp b/engines/scumm/players/player_mod.cpp
index 6411f0a17a..abaa8c1fc5 100644
--- a/engines/scumm/player_mod.cpp
+++ b/engines/scumm/players/player_mod.cpp
@@ -21,7 +21,7 @@
*/
-#include "scumm/player_mod.h"
+#include "scumm/players/player_mod.h"
#include "audio/mixer.h"
#include "audio/rate.h"
#include "audio/decoders/raw.h"
diff --git a/engines/scumm/player_mod.h b/engines/scumm/players/player_mod.h
index 619d83541d..d4a5b16fca 100644
--- a/engines/scumm/player_mod.h
+++ b/engines/scumm/players/player_mod.h
@@ -20,8 +20,8 @@
*
*/
-#ifndef SCUMM_PLAYER_MOD_H
-#define SCUMM_PLAYER_MOD_H
+#ifndef SCUMM_PLAYERS_PLAYER_MOD_H
+#define SCUMM_PLAYERS_PLAYER_MOD_H
#include "scumm/scumm.h"
#include "audio/audiostream.h"
diff --git a/engines/scumm/player_nes.cpp b/engines/scumm/players/player_nes.cpp
index a6ffc9ed86..f55f1f9edd 100644
--- a/engines/scumm/player_nes.cpp
+++ b/engines/scumm/players/player_nes.cpp
@@ -23,7 +23,7 @@
#ifndef DISABLE_NES_APU
#include "engines/engine.h"
-#include "scumm/player_nes.h"
+#include "scumm/players/player_nes.h"
#include "scumm/scumm.h"
#include "audio/mixer.h"
diff --git a/engines/scumm/player_nes.h b/engines/scumm/players/player_nes.h
index be1617e0f6..f0b3e79aad 100644
--- a/engines/scumm/player_nes.h
+++ b/engines/scumm/players/player_nes.h
@@ -20,8 +20,8 @@
*
*/
-#ifndef SCUMM_PLAYER_NES_H
-#define SCUMM_PLAYER_NES_H
+#ifndef SCUMM_PLAYERS_PLAYER_NES_H
+#define SCUMM_PLAYERS_PLAYER_NES_H
#include "common/scummsys.h"
#include "scumm/music.h"
diff --git a/engines/scumm/player_pce.cpp b/engines/scumm/players/player_pce.cpp
index 8d886ee008..6d6e2fcde5 100644
--- a/engines/scumm/player_pce.cpp
+++ b/engines/scumm/players/player_pce.cpp
@@ -29,7 +29,7 @@
*/
#include <math.h>
-#include "player_pce.h"
+#include "scumm/players/player_pce.h"
#include "common/endian.h"
// PCE sound engine is only used by Loom, which requires 16bit color support
diff --git a/engines/scumm/player_pce.h b/engines/scumm/players/player_pce.h
index 427fb1ace6..ca2eddf58c 100644
--- a/engines/scumm/player_pce.h
+++ b/engines/scumm/players/player_pce.h
@@ -20,8 +20,8 @@
*
*/
-#ifndef SCUMM_PLAYER_PCE_H
-#define SCUMM_PLAYER_PCE_H
+#ifndef SCUMM_PLAYERS_PLAYER_PCE_H
+#define SCUMM_PLAYERS_PLAYER_PCE_H
#include "common/scummsys.h"
#include "common/mutex.h"
diff --git a/engines/scumm/player_sid.cpp b/engines/scumm/players/player_sid.cpp
index 7a609364e5..1b97ad16d4 100644
--- a/engines/scumm/player_sid.cpp
+++ b/engines/scumm/players/player_sid.cpp
@@ -23,7 +23,7 @@
#ifndef DISABLE_SID
#include "engines/engine.h"
-#include "scumm/player_sid.h"
+#include "scumm/players/player_sid.h"
#include "scumm/scumm.h"
#include "audio/mixer.h"
diff --git a/engines/scumm/player_sid.h b/engines/scumm/players/player_sid.h
index 12e3573575..a48ec793cd 100644
--- a/engines/scumm/player_sid.h
+++ b/engines/scumm/players/player_sid.h
@@ -20,8 +20,8 @@
*
*/
-#ifndef SCUMM_PLAYER_SID_H
-#define SCUMM_PLAYER_SID_H
+#ifndef SCUMM_PLAYERS_PLAYER_SID_H
+#define SCUMM_PLAYERS_PLAYER_SID_H
#include "common/mutex.h"
#include "common/scummsys.h"
diff --git a/engines/scumm/player_towns.cpp b/engines/scumm/players/player_towns.cpp
index 33e3e40e39..acbdbc7fb6 100644
--- a/engines/scumm/player_towns.cpp
+++ b/engines/scumm/players/player_towns.cpp
@@ -22,7 +22,7 @@
#include "scumm/sound.h"
-#include "scumm/player_towns.h"
+#include "scumm/players/player_towns.h"
namespace Scumm {
diff --git a/engines/scumm/player_towns.h b/engines/scumm/players/player_towns.h
index 5c76d7c6c7..2369b7da5f 100644
--- a/engines/scumm/player_towns.h
+++ b/engines/scumm/players/player_towns.h
@@ -20,8 +20,8 @@
*
*/
-#ifndef SCUMM_PLAYER_TOWNS_H
-#define SCUMM_PLAYER_TOWNS_H
+#ifndef SCUMM_PLAYERS_PLAYER_TOWNS_H
+#define SCUMM_PLAYERS_PLAYER_TOWNS_H
#include "scumm/scumm.h"
#include "scumm/imuse/imuse.h"
diff --git a/engines/scumm/player_v1.cpp b/engines/scumm/players/player_v1.cpp
index 8e784e9866..0fa1ee9361 100644
--- a/engines/scumm/player_v1.cpp
+++ b/engines/scumm/players/player_v1.cpp
@@ -22,7 +22,7 @@
#include "engines/engine.h"
-#include "scumm/player_v1.h"
+#include "scumm/players/player_v1.h"
#include "scumm/scumm.h"
namespace Scumm {
diff --git a/engines/scumm/player_v1.h b/engines/scumm/players/player_v1.h
index 9e6063adc9..ccd24c39df 100644
--- a/engines/scumm/player_v1.h
+++ b/engines/scumm/players/player_v1.h
@@ -20,10 +20,10 @@
*
*/
-#ifndef SCUMM_PLAYER_V1_H
-#define SCUMM_PLAYER_V1_H
+#ifndef SCUMM_PLAYERS_PLAYER_V1_H
+#define SCUMM_PLAYERS_PLAYER_V1_H
-#include "scumm/player_v2.h"
+#include "scumm/players/player_v2.h"
namespace Scumm {
diff --git a/engines/scumm/player_v2.cpp b/engines/scumm/players/player_v2.cpp
index 6910f5e0db..2429af2d8c 100644
--- a/engines/scumm/player_v2.cpp
+++ b/engines/scumm/players/player_v2.cpp
@@ -20,7 +20,7 @@
*
*/
-#include "scumm/player_v2.h"
+#include "scumm/players/player_v2.h"
#include "scumm/scumm.h"
namespace Scumm {
diff --git a/engines/scumm/player_v2.h b/engines/scumm/players/player_v2.h
index d932585b8e..33878ff08b 100644
--- a/engines/scumm/player_v2.h
+++ b/engines/scumm/players/player_v2.h
@@ -20,10 +20,10 @@
*
*/
-#ifndef SCUMM_PLAYER_V2_H
-#define SCUMM_PLAYER_V2_H
+#ifndef SCUMM_PLAYERS_PLAYER_V2_H
+#define SCUMM_PLAYERS_PLAYER_V2_H
-#include "scumm/player_v2base.h"
+#include "scumm/players/player_v2base.h"
namespace Scumm {
diff --git a/engines/scumm/player_v2a.cpp b/engines/scumm/players/player_v2a.cpp
index 07fc77b301..aeccb8b7cb 100644
--- a/engines/scumm/player_v2a.cpp
+++ b/engines/scumm/players/player_v2a.cpp
@@ -21,7 +21,7 @@
*/
#include "engines/engine.h"
-#include "scumm/player_v2a.h"
+#include "scumm/players/player_v2a.h"
#include "scumm/scumm.h"
namespace Scumm {
diff --git a/engines/scumm/player_v2a.h b/engines/scumm/players/player_v2a.h
index fe20b43846..12193635f2 100644
--- a/engines/scumm/player_v2a.h
+++ b/engines/scumm/players/player_v2a.h
@@ -20,12 +20,12 @@
*
*/
-#ifndef SCUMM_PLAYER_V2A_H
-#define SCUMM_PLAYER_V2A_H
+#ifndef SCUMM_PLAYERS_PLAYER_V2A_H
+#define SCUMM_PLAYERS_PLAYER_V2A_H
#include "common/scummsys.h"
#include "scumm/music.h"
-#include "scumm/player_mod.h"
+#include "scumm/players/player_mod.h"
class Mixer;
diff --git a/engines/scumm/player_v2base.cpp b/engines/scumm/players/player_v2base.cpp
index 0d3ad4b1b3..75f1518989 100644
--- a/engines/scumm/player_v2base.cpp
+++ b/engines/scumm/players/player_v2base.cpp
@@ -20,7 +20,7 @@
*
*/
-#include "scumm/player_v2base.h"
+#include "scumm/players/player_v2base.h"
#include "scumm/scumm.h"
#define FREQ_HZ 236 // Don't change!
diff --git a/engines/scumm/player_v2base.h b/engines/scumm/players/player_v2base.h
index eb9ed941ca..32bde95ddc 100644
--- a/engines/scumm/player_v2base.h
+++ b/engines/scumm/players/player_v2base.h
@@ -20,8 +20,8 @@
*
*/
-#ifndef SCUMM_PLAYER_V2BASE_H
-#define SCUMM_PLAYER_V2BASE_H
+#ifndef SCUMM_PLAYERS_PLAYER_V2BASE_H
+#define SCUMM_PLAYERS_PLAYER_V2BASE_H
#include "common/scummsys.h"
#include "common/mutex.h"
diff --git a/engines/scumm/player_v2cms.cpp b/engines/scumm/players/player_v2cms.cpp
index c1242e0645..8e903bf9d2 100644
--- a/engines/scumm/player_v2cms.cpp
+++ b/engines/scumm/players/player_v2cms.cpp
@@ -20,7 +20,7 @@
*
*/
-#include "scumm/player_v2cms.h"
+#include "scumm/players/player_v2cms.h"
#include "scumm/scumm.h"
#include "audio/mididrv.h"
#include "audio/mixer.h"
diff --git a/engines/scumm/player_v2cms.h b/engines/scumm/players/player_v2cms.h
index 905c7c141e..fe42720215 100644
--- a/engines/scumm/player_v2cms.h
+++ b/engines/scumm/players/player_v2cms.h
@@ -20,10 +20,10 @@
*
*/
-#ifndef SCUMM_PLAYER_V2CMS_H
-#define SCUMM_PLAYER_V2CMS_H
+#ifndef SCUMM_PLAYERS_PLAYER_V2CMS_H
+#define SCUMM_PLAYERS_PLAYER_V2CMS_H
-#include "scumm/player_v2base.h" // for channel_data
+#include "scumm/players/player_v2base.h" // for channel_data
class CMSEmulator;
diff --git a/engines/scumm/player_v3a.cpp b/engines/scumm/players/player_v3a.cpp
index 472cd1252b..ca0eedc90a 100644
--- a/engines/scumm/player_v3a.cpp
+++ b/engines/scumm/players/player_v3a.cpp
@@ -22,7 +22,7 @@
#include "engines/engine.h"
-#include "scumm/player_v3a.h"
+#include "scumm/players/player_v3a.h"
#include "scumm/scumm.h"
namespace Scumm {
diff --git a/engines/scumm/player_v3a.h b/engines/scumm/players/player_v3a.h
index 9449664e9b..3f84a74e51 100644
--- a/engines/scumm/player_v3a.h
+++ b/engines/scumm/players/player_v3a.h
@@ -20,12 +20,12 @@
*
*/
-#ifndef SCUMM_PLAYER_V3A_H
-#define SCUMM_PLAYER_V3A_H
+#ifndef SCUMM_PLAYERS_PLAYER_V3A_H
+#define SCUMM_PLAYERS_PLAYER_V3A_H
#include "common/scummsys.h"
#include "scumm/music.h"
-#include "scumm/player_mod.h"
+#include "scumm/players/player_mod.h"
class Mixer;
diff --git a/engines/scumm/player_v3m.cpp b/engines/scumm/players/player_v3m.cpp
index bf65ec797f..e30e31aff9 100644
--- a/engines/scumm/player_v3m.cpp
+++ b/engines/scumm/players/player_v3m.cpp
@@ -91,7 +91,7 @@
#include "common/translation.h"
#include "engines/engine.h"
#include "gui/message.h"
-#include "scumm/player_v3m.h"
+#include "scumm/players/player_v3m.h"
#include "scumm/scumm.h"
namespace Scumm {
diff --git a/engines/scumm/player_v3m.h b/engines/scumm/players/player_v3m.h
index 359bab32a9..615d736035 100644
--- a/engines/scumm/player_v3m.h
+++ b/engines/scumm/players/player_v3m.h
@@ -20,14 +20,14 @@
*
*/
-#ifndef SCUMM_PLAYER_V3M_H
-#define SCUMM_PLAYER_V3M_H
+#ifndef SCUMM_PLAYERS_PLAYER_V3M_H
+#define SCUMM_PLAYERS_PLAYER_V3M_H
#include "common/scummsys.h"
#include "common/util.h"
#include "common/mutex.h"
#include "scumm/music.h"
-#include "scumm/player_mac.h"
+#include "scumm/players/player_mac.h"
#include "audio/audiostream.h"
#include "audio/mixer.h"
diff --git a/engines/scumm/player_v4a.cpp b/engines/scumm/players/player_v4a.cpp
index e791736f0e..59f49625c3 100644
--- a/engines/scumm/player_v4a.cpp
+++ b/engines/scumm/players/player_v4a.cpp
@@ -21,7 +21,7 @@
*/
#include "engines/engine.h"
-#include "scumm/player_v4a.h"
+#include "scumm/players/player_v4a.h"
#include "scumm/scumm.h"
#include "common/file.h"
diff --git a/engines/scumm/player_v4a.h b/engines/scumm/players/player_v4a.h
index d01c70f295..c8c7b63ed2 100644
--- a/engines/scumm/player_v4a.h
+++ b/engines/scumm/players/player_v4a.h
@@ -20,8 +20,8 @@
*
*/
-#ifndef SCUMM_PLAYER_V4A_H
-#define SCUMM_PLAYER_V4A_H
+#ifndef SCUMM_PLAYERS_PLAYER_V4A_H
+#define SCUMM_PLAYERS_PLAYER_V4A_H
#include "common/scummsys.h"
#include "common/util.h"
diff --git a/engines/scumm/player_v5m.cpp b/engines/scumm/players/player_v5m.cpp
index 500f3bbc40..77b6d52a3c 100644
--- a/engines/scumm/player_v5m.cpp
+++ b/engines/scumm/players/player_v5m.cpp
@@ -76,7 +76,7 @@
#include "common/translation.h"
#include "engines/engine.h"
#include "gui/message.h"
-#include "scumm/player_v5m.h"
+#include "scumm/players/player_v5m.h"
#include "scumm/scumm.h"
namespace Scumm {
diff --git a/engines/scumm/player_v5m.h b/engines/scumm/players/player_v5m.h
index b2079ee331..e3a457f8af 100644
--- a/engines/scumm/player_v5m.h
+++ b/engines/scumm/players/player_v5m.h
@@ -20,14 +20,14 @@
*
*/
-#ifndef SCUMM_PLAYER_V5M_H
-#define SCUMM_PLAYER_V5M_H
+#ifndef SCUMM_PLAYERS_PLAYER_V5M_H
+#define SCUMM_PLAYERS_PLAYER_V5M_H
#include "common/scummsys.h"
#include "common/util.h"
#include "common/mutex.h"
#include "scumm/music.h"
-#include "scumm/player_mac.h"
+#include "scumm/players/player_mac.h"
#include "audio/audiostream.h"
#include "audio/mixer.h"
diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp
index 13e2500eef..848e288589 100644
--- a/engines/scumm/saveload.cpp
+++ b/engines/scumm/saveload.cpp
@@ -30,7 +30,7 @@
#include "scumm/charset.h"
#include "scumm/imuse_digi/dimuse.h"
#include "scumm/imuse/imuse.h"
-#include "scumm/player_towns.h"
+#include "scumm/players/player_towns.h"
#include "scumm/he/intern_he.h"
#include "scumm/object.h"
#include "scumm/resource.h"
@@ -636,100 +636,83 @@ bool ScummEngine::getSavegameName(int slot, Common::String &desc) {
return result;
}
-bool getSavegameName(Common::InSaveFile *in, Common::String &desc, int heversion) {
- SaveGameHeader hdr;
-
+namespace {
+bool loadAndCheckSaveGameHeader(Common::InSaveFile *in, int heversion, SaveGameHeader &hdr, Common::String *error = nullptr) {
if (!loadSaveGameHeader(in, hdr)) {
- desc = "Invalid savegame";
+ if (error) {
+ *error = "Invalid savegame";
+ }
return false;
}
- if (hdr.ver > CURRENT_VER)
+ if (hdr.ver > CURRENT_VER) {
hdr.ver = TO_LE_32(hdr.ver);
+ }
+
if (hdr.ver < VER(7) || hdr.ver > CURRENT_VER) {
- desc = "Invalid version";
+ if (error) {
+ *error = "Invalid version";
+ }
return false;
}
// We (deliberately) broke HE savegame compatibility at some point.
if (hdr.ver < VER(57) && heversion >= 60) {
- desc = "Unsupported version";
+ if (error) {
+ *error = "Unsupported version";
+ }
return false;
}
hdr.name[sizeof(hdr.name) - 1] = 0;
- desc = hdr.name;
return true;
}
+} // End of anonymous namespace
-Graphics::Surface *ScummEngine::loadThumbnailFromSlot(const char *target, int slot) {
- Common::SeekableReadStream *in;
+bool getSavegameName(Common::InSaveFile *in, Common::String &desc, int heversion) {
SaveGameHeader hdr;
- if (slot < 0)
- return 0;
-
- Common::String filename = ScummEngine::makeSavegameName(target, slot, false);
- if (!(in = g_system->getSavefileManager()->openForLoading(filename))) {
- return 0;
- }
-
- if (!loadSaveGameHeader(in, hdr)) {
- delete in;
- return 0;
+ if (!loadAndCheckSaveGameHeader(in, heversion, hdr, &desc)) {
+ return false;
}
- if (hdr.ver > CURRENT_VER)
- hdr.ver = TO_LE_32(hdr.ver);
- if (hdr.ver < VER(52)) {
- delete in;
- return 0;
- }
+ desc = hdr.name;
+ return true;
+}
- Graphics::Surface *thumb = 0;
- if (Graphics::checkThumbnailHeader(*in)) {
- thumb = Graphics::loadThumbnail(*in);
+bool ScummEngine::querySaveMetaInfos(const char *target, int slot, int heversion, Common::String &desc, Graphics::Surface *&thumbnail, SaveStateMetaInfos *&timeInfos) {
+ if (slot < 0) {
+ return false;
}
- delete in;
- return thumb;
-}
-
-bool ScummEngine::loadInfosFromSlot(const char *target, int slot, SaveStateMetaInfos *stuff) {
- Common::SeekableReadStream *in;
SaveGameHeader hdr;
+ const Common::String filename = ScummEngine::makeSavegameName(target, slot, false);
+ Common::ScopedPtr<Common::SeekableReadStream> in(g_system->getSavefileManager()->openForLoading(filename));
- if (slot < 0)
- return 0;
-
- Common::String filename = makeSavegameName(target, slot, false);
- if (!(in = g_system->getSavefileManager()->openForLoading(filename))) {
+ if (!in) {
return false;
}
- if (!loadSaveGameHeader(in, hdr)) {
- delete in;
+ if (!loadAndCheckSaveGameHeader(in.get(), heversion, hdr)) {
return false;
}
- if (hdr.ver > CURRENT_VER)
- hdr.ver = TO_LE_32(hdr.ver);
- if (hdr.ver < VER(56)) {
- delete in;
- return false;
- }
+ desc = hdr.name;
- if (!Graphics::skipThumbnail(*in)) {
- delete in;
- return false;
- }
+ if (hdr.ver > VER(52)) {
+ if (Graphics::checkThumbnailHeader(*in)) {
+ thumbnail = Graphics::loadThumbnail(*in);
+ }
- if (!loadInfos(in, stuff)) {
- delete in;
- return false;
+ if (hdr.ver > VER(57)) {
+ if (!loadInfos(in.get(), timeInfos)) {
+ return false;
+ }
+ } else {
+ timeInfos = nullptr;
+ }
}
- delete in;
return true;
}
@@ -783,7 +766,7 @@ bool ScummEngine::loadInfos(Common::SeekableReadStream *file, SaveStateMetaInfos
return true;
}
-void ScummEngine::saveInfos(Common::WriteStream* file) {
+void ScummEngine::saveInfos(Common::WriteStream *file) {
SaveInfoSection section;
section.type = MKTAG('I','N','F','O');
section.version = INFOSECTION_VERSION;
diff --git a/engines/scumm/saveload.h b/engines/scumm/saveload.h
index 7b2ff91ad3..c34be5816d 100644
--- a/engines/scumm/saveload.h
+++ b/engines/scumm/saveload.h
@@ -47,7 +47,7 @@ namespace Scumm {
* only saves/loads those which are valid for the version of the savegame
* which is being loaded/saved currently.
*/
-#define CURRENT_VER 94
+#define CURRENT_VER 95
/**
* An auxillary macro, used to specify savegame versions. We use this instead
diff --git a/engines/scumm/script_v5.cpp b/engines/scumm/script_v5.cpp
index 2d4c326b68..3da7000ae9 100644
--- a/engines/scumm/script_v5.cpp
+++ b/engines/scumm/script_v5.cpp
@@ -27,7 +27,7 @@
#include "scumm/scumm_v3.h"
#include "scumm/scumm_v5.h"
#include "scumm/sound.h"
-#include "scumm/player_towns.h"
+#include "scumm/players/player_towns.h"
#include "scumm/util.h"
#include "scumm/verbs.h"
diff --git a/engines/scumm/script_v6.cpp b/engines/scumm/script_v6.cpp
index a75e864e7a..6983c51f30 100644
--- a/engines/scumm/script_v6.cpp
+++ b/engines/scumm/script_v6.cpp
@@ -394,7 +394,7 @@ ScummEngine_v6::ArrayHeader *ScummEngine_v6::getArray(int array) {
int ScummEngine_v6::readArray(int array, int idx, int base) {
ArrayHeader *ah = getArray(array);
- if (ah == NULL || ah->data == NULL)
+ if (!ah)
error("readArray: invalid array %d (%d)", array, readVar(array));
// WORKAROUND bug #645711. This is clearly a script bug, as this script
diff --git a/engines/scumm/scumm-md5.h b/engines/scumm/scumm-md5.h
index f5609f1b03..ae80f306af 100644
--- a/engines/scumm/scumm-md5.h
+++ b/engines/scumm/scumm-md5.h
@@ -1,5 +1,5 @@
/*
- This file was generated by the md5table tool on Sat Aug 3 16:52:02 2013
+ This file was generated by the md5table tool on Fri Sep 27 05:44:12 2013
DO NOT EDIT MANUALLY!
*/
@@ -121,6 +121,7 @@ static const MD5Table md5table[] = {
{ "2723fea3dae0cb47768c424b145ae0e7", "tentacle", "Floppy", "Floppy", 7932, Common::EN_ANY, Common::kPlatformDOS },
{ "27b2ef1653089fe5b897d9cc89ce784f", "balloon", "HE 80", "", -1, Common::RU_RUS, Common::kPlatformWindows },
{ "27b3a4224ad63d5b04627595c1c1a025", "zak", "V2", "V2", -1, Common::IT_ITA, Common::kPlatformAmiga },
+ { "288fb75b24389733c29fa107fe8d44e8", "catalog", "HE CUP", "Preview", 10795148, Common::EN_USA, Common::kPlatformUnknown },
{ "28d24a33448fab6795850bc9f159a4a2", "atlantis", "FM-TOWNS", "Demo", 11170, Common::JA_JPN, Common::kPlatformFMTowns },
{ "28ef68ee3ed76d7e2ee8ee13c15fbd5b", "loom", "EGA", "EGA", 5748, Common::EN_ANY, Common::kPlatformDOS },
{ "28f07458f1b6c24e118a1ea056827701", "lost", "HE 99", "", -1, Common::NL_NLD, Common::kPlatformUnknown },
@@ -344,6 +345,7 @@ static const MD5Table md5table[] = {
{ "78c07ca088526d8d4446a4c2cb501203", "freddi3", "HE 99", "", -1, Common::FR_FRA, Common::kPlatformUnknown },
{ "7974365d3dc0f43a2748c975f91ff042", "monkey2", "", "", -1, Common::ES_ESP, Common::kPlatformDOS },
{ "79b05f628586837e7166e82b2279bb50", "loom", "PC-Engine", "", -1, Common::JA_JPN, Common::kPlatformPCEngine },
+ { "7b4ee071eecadc2d8cd0c3509110825c", "puttzoo", "HE 100", "Remastered", -1, Common::EN_ANY, Common::kPlatformWindows },
{ "7bad72e332a59f9fcc1d437f4edad32a", "puttcircus", "", "", -1, Common::RU_RUS, Common::kPlatformUnknown },
{ "7c2e76087027eeee9c8f8985f93a1cc5", "freddi4", "", "Demo", 13584, Common::EN_ANY, Common::kPlatformUnknown },
{ "7c8100e360e8ef05f88069d4cfa0afd1", "puttrace", "HE 99", "Demo", 13108, Common::EN_GRB, Common::kPlatformWindows },
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 6fddf4ba77..39fbae8147 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -45,25 +45,26 @@
#include "scumm/imuse_digi/dimuse.h"
#include "scumm/smush/smush_mixer.h"
#include "scumm/smush/smush_player.h"
-#include "scumm/player_towns.h"
+#include "scumm/players/player_towns.h"
#include "scumm/insane/insane.h"
#include "scumm/he/animation_he.h"
#include "scumm/he/intern_he.h"
#include "scumm/he/logic_he.h"
#include "scumm/he/sound_he.h"
#include "scumm/object.h"
-#include "scumm/player_nes.h"
-#include "scumm/player_sid.h"
-#include "scumm/player_pce.h"
-#include "scumm/player_apple2.h"
-#include "scumm/player_v1.h"
-#include "scumm/player_v2.h"
-#include "scumm/player_v2cms.h"
-#include "scumm/player_v2a.h"
-#include "scumm/player_v3a.h"
-#include "scumm/player_v3m.h"
-#include "scumm/player_v4a.h"
-#include "scumm/player_v5m.h"
+#include "scumm/players/player_ad.h"
+#include "scumm/players/player_nes.h"
+#include "scumm/players/player_sid.h"
+#include "scumm/players/player_pce.h"
+#include "scumm/players/player_apple2.h"
+#include "scumm/players/player_v1.h"
+#include "scumm/players/player_v2.h"
+#include "scumm/players/player_v2cms.h"
+#include "scumm/players/player_v2a.h"
+#include "scumm/players/player_v3a.h"
+#include "scumm/players/player_v3m.h"
+#include "scumm/players/player_v4a.h"
+#include "scumm/players/player_v5m.h"
#include "scumm/resource.h"
#include "scumm/he/resource_he.h"
#include "scumm/scumm_v0.h"
@@ -1841,6 +1842,15 @@ void ScummEngine::setupMusic(int midi) {
_musicEngine = _townsPlayer = new Player_Towns_v1(this, _mixer);
if (!_townsPlayer->init())
error("Failed to initialize FM-Towns audio driver");
+ } else if (_game.platform == Common::kPlatformDOS && (_sound->_musicType == MDT_ADLIB) && (_game.id == GID_LOOM || _game.id == GID_INDY3)) {
+ // For Indy3 DOS and Loom DOS we use an implementation of the original
+ // AD player when AdLib is selected. This fixes sound effects in those
+ // games (see for example bug #2027877 "INDY3: Non-Looping Sound
+ // Effects"). The player itself is also used in Monkey Island DOS
+ // EGA/VGA. However, we support multi MIDI for that game and we cannot
+ // support this with the Player_AD code at the moment. The reason here
+ // is that multi MIDI is supported internally by our iMuse output.
+ _musicEngine = new Player_AD(this, _mixer);
} else if (_game.version >= 3 && _game.heversion <= 62) {
MidiDriver *nativeMidiDriver = 0;
MidiDriver *adlibMidiDriver = 0;
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 021378c554..7d3a01b895 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -240,6 +240,7 @@ enum ScummGameId {
GID_FBEAR,
GID_PUTTMOON,
GID_FUNPACK,
+ GID_PUTTZOO,
GID_FREDDI3,
GID_BIRTHDAYRED,
GID_BIRTHDAYYELLOW,
@@ -625,15 +626,10 @@ public:
// thumbnail + info stuff
public:
- Graphics::Surface *loadThumbnailFromSlot(int slot) {
- return loadThumbnailFromSlot(_targetName.c_str(), slot);
- }
- static Graphics::Surface *loadThumbnailFromSlot(const char *target, int slot);
-
- static bool loadInfosFromSlot(const char *target, int slot, SaveStateMetaInfos *stuff);
+ static bool querySaveMetaInfos(const char *target, int slot, int heversion, Common::String &desc, Graphics::Surface *&thumbnail, SaveStateMetaInfos *&timeInfos);
protected:
- void saveInfos(Common::WriteStream* file);
+ void saveInfos(Common::WriteStream *file);
static bool loadInfos(Common::SeekableReadStream *file, SaveStateMetaInfos *stuff);
protected:
diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index 3f6290f8fc..071805752b 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -30,7 +30,7 @@
#include "scumm/file.h"
#include "scumm/imuse/imuse.h"
#include "scumm/imuse_digi/dimuse.h"
-#include "scumm/player_towns.h"
+#include "scumm/players/player_towns.h"
#include "scumm/resource.h"
#include "scumm/scumm.h"
#include "scumm/sound.h"
@@ -470,8 +470,10 @@ static int compareMP3OffsetTable(const void *a, const void *b) {
void Sound::startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle *handle) {
int num = 0, i;
- int size = 0;
int id = -1;
+#if defined(USE_FLAC) || defined(USE_VORBIS) || defined(USE_MAD)
+ int size = 0;
+#endif
Common::ScopedPtr<ScummFile> file;
if (_vm->_game.id == GID_CMI) {
@@ -562,10 +564,14 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle
num = result->num_tags;
}
offset = result->new_offset;
+#if defined(USE_FLAC) || defined(USE_VORBIS) || defined(USE_MAD)
size = result->compressed_size;
+#endif
} else {
offset += 8;
+#if defined(USE_FLAC) || defined(USE_VORBIS) || defined(USE_MAD)
size = -1;
+#endif
}
file.reset(new ScummFile());
@@ -1930,7 +1936,6 @@ int ScummEngine::readSoundResourceSmallHeader(ResId idx) {
// 8 bytes MTrk header
// 7 bytes MIDI tempo sysex
// + some default instruments
- byte *ptr;
if (_game.features & GF_OLD_BUNDLE) {
ad_size -= 4;
_fileHandle->seek(ad_offs + 4, SEEK_SET);
@@ -1938,10 +1943,17 @@ int ScummEngine::readSoundResourceSmallHeader(ResId idx) {
ad_size -= 6;
_fileHandle->seek(ad_offs, SEEK_SET);
}
- ptr = (byte *) calloc(ad_size, 1);
- _fileHandle->read(ptr, ad_size);
- convertADResource(_res, _game, idx, ptr, ad_size);
- free(ptr);
+ // For games using AD except Indy3 and Loom we are using our iMuse
+ // implementation. See output initialization in
+ // ScummEngine::setupMusic for more information.
+ if (_game.id != GID_INDY3 && _game.id != GID_LOOM) {
+ byte *ptr = (byte *)calloc(ad_size, 1);
+ _fileHandle->read(ptr, ad_size);
+ convertADResource(_res, _game, idx, ptr, ad_size);
+ free(ptr);
+ } else {
+ _fileHandle->read(_res->createResource(rtSound, idx, ad_size), ad_size);
+ }
return 1;
} else if (ro_offs != 0) {
_fileHandle->seek(ro_offs - 2, SEEK_SET);
diff --git a/engines/scumm/vars.cpp b/engines/scumm/vars.cpp
index 77c7daa0df..9c90d7575d 100644
--- a/engines/scumm/vars.cpp
+++ b/engines/scumm/vars.cpp
@@ -714,6 +714,12 @@ void ScummEngine_v99he::resetScummVars() {
VAR(140) = 0;
#endif
}
+
+ if (_game.id == GID_PUTTZOO && _game.heversion == 100 && _game.platform == Common::kPlatformWindows) {
+ // Specific to Nimbus Games version.
+ VAR(156) = 1;
+ VAR(157) = 0;
+ }
}
#endif