aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/mads/configure.engine3
-rw-r--r--engines/mads/detection.cpp171
-rw-r--r--engines/mads/detection_tables.h45
-rw-r--r--engines/mads/mads.cpp74
-rw-r--r--engines/mads/mads.h86
-rw-r--r--engines/mads/module.mk15
-rw-r--r--engines/mads/sound.cpp53
-rw-r--r--engines/mads/sound.h51
-rw-r--r--engines/mads/sound_nebular.cpp1097
-rw-r--r--engines/mads/sound_nebular.h354
10 files changed, 1949 insertions, 0 deletions
diff --git a/engines/mads/configure.engine b/engines/mads/configure.engine
new file mode 100644
index 0000000000..60d833e9e8
--- /dev/null
+++ b/engines/mads/configure.engine
@@ -0,0 +1,3 @@
+# This file is included from the main "configure" script
+# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
+add_engine mads "Rex Nebular and the Cosmic Gender Bender" no
diff --git a/engines/mads/detection.cpp b/engines/mads/detection.cpp
new file mode 100644
index 0000000000..4e56655f40
--- /dev/null
+++ b/engines/mads/detection.cpp
@@ -0,0 +1,171 @@
+/* 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 "mads/mads.h"
+
+#include "base/plugins.h"
+#include "common/savefile.h"
+#include "common/str-array.h"
+#include "common/memstream.h"
+#include "engines/advancedDetector.h"
+#include "common/system.h"
+#include "graphics/colormasks.h"
+#include "graphics/surface.h"
+
+#define MAX_SAVES 99
+
+namespace MADS {
+
+struct MADSGameDescription {
+ ADGameDescription desc;
+};
+
+uint32 MADSEngine::getFeatures() const {
+ return _gameDescription->desc.flags;
+}
+
+Common::Language MADSEngine::getLanguage() const {
+ return _gameDescription->desc.language;
+}
+
+Common::Platform MADSEngine::getPlatform() const {
+ return _gameDescription->desc.platform;
+}
+
+bool MADSEngine::getIsDemo() const {
+ return _gameDescription->desc.flags & ADGF_DEMO;
+}
+
+} // End of namespace MADS
+
+static const PlainGameDescriptor MADSGames[] = {
+ {"MADS", "MADS"},
+ {"nebular", "Rex Nebular and the Cosmic Gender Bender"},
+ {0, 0}
+};
+
+#include "mads/detection_tables.h"
+
+class MADSMetaEngine : public AdvancedMetaEngine {
+public:
+ MADSMetaEngine() : AdvancedMetaEngine(MADS::gameDescriptions, sizeof(MADS::MADSGameDescription), MADSGames) {
+ _maxScanDepth = 3;
+ }
+
+ virtual const char *getName() const {
+ return "MADS Engine";
+ }
+
+ virtual const char *getOriginalCopyright() const {
+ return "MADS (c)";
+ }
+
+ virtual bool hasFeature(MetaEngineFeature f) const;
+ virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
+ virtual SaveStateList listSaves(const char *target) const;
+ virtual int getMaximumSaveSlot() const;
+ virtual void removeSaveState(const char *target, int slot) const;
+ SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
+};
+
+bool MADSMetaEngine::hasFeature(MetaEngineFeature f) const {
+ return
+ (f == kSupportsListSaves) ||
+ (f == kSupportsLoadingDuringStartup) ||
+ (f == kSupportsDeleteSave) ||
+ (f == kSavesSupportMetaInfo) ||
+ (f == kSavesSupportThumbnail);
+}
+
+bool MADS::MADSEngine::hasFeature(EngineFeature f) const {
+ return
+ (f == kSupportsRTL) ||
+ (f == kSupportsLoadingDuringRuntime) ||
+ (f == kSupportsSavingDuringRuntime);
+}
+
+bool MADSMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+ const MADS::MADSGameDescription *gd = (const MADS::MADSGameDescription *)desc;
+ if (gd) {
+ *engine = new MADS::MADSEngine(syst, gd);
+ }
+ return gd != 0;
+}
+
+SaveStateList MADSMetaEngine::listSaves(const char *target) const {
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ Common::StringArray filenames;
+ Common::String saveDesc;
+ Common::String pattern = Common::String::format("%s.0??", target);
+
+ filenames = saveFileMan->listSavefiles(pattern);
+ sort(filenames.begin(), filenames.end()); // Sort to get the files in numerical order
+
+ SaveStateList saveList;
+ for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
+ const char *ext = strrchr(file->c_str(), '.');
+ int slot = ext ? atoi(ext + 1) : -1;
+
+ if (slot >= 0 && slot < MAX_SAVES) {
+ Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(*file);
+
+ if (in) {
+ delete in;
+ }
+ }
+ }
+
+ return saveList;
+}
+
+int MADSMetaEngine::getMaximumSaveSlot() const {
+ return MAX_SAVES;
+}
+
+void MADSMetaEngine::removeSaveState(const char *target, int slot) const {
+ Common::String filename = Common::String::format("%s.%03d", target, slot);
+ g_system->getSavefileManager()->removeSavefile(filename);
+}
+
+SaveStateDescriptor MADSMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
+ Common::String filename = Common::String::format("%s.%03d", target, slot);
+ Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(filename);
+
+ if (f) {
+ delete f;
+
+ // Create the return descriptor
+ SaveStateDescriptor desc(slot, "");
+
+ return desc;
+ }
+
+ return SaveStateDescriptor();
+}
+
+
+#if PLUGIN_ENABLED_DYNAMIC(MADS)
+REGISTER_PLUGIN_DYNAMIC(MADS, PLUGIN_TYPE_ENGINE, MADSMetaEngine);
+#else
+REGISTER_PLUGIN_STATIC(MADS, PLUGIN_TYPE_ENGINE, MADSMetaEngine);
+#endif
diff --git a/engines/mads/detection_tables.h b/engines/mads/detection_tables.h
new file mode 100644
index 0000000000..7a66029b9b
--- /dev/null
+++ b/engines/mads/detection_tables.h
@@ -0,0 +1,45 @@
+/* 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.
+ *
+ */
+
+namespace MADS {
+
+static const MADSGameDescription gameDescriptions[] = {
+ {
+ // Rex Nebular and the Cosmic Gender Bender DOS English
+ {
+ "nebular",
+ 0,
+ {
+ {"mpslabs.001", 0, "4df5c557b52abb5b661cf4befe5ae301", 1315354},
+ AD_LISTEND
+ },
+ Common::EN_ANY,
+ Common::kPlatformDOS,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NONE)
+ },
+ },
+
+ { AD_TABLE_END_MARKER }
+};
+
+} // End of namespace MADS
diff --git a/engines/mads/mads.cpp b/engines/mads/mads.cpp
new file mode 100644
index 0000000000..39ef195cd6
--- /dev/null
+++ b/engines/mads/mads.cpp
@@ -0,0 +1,74 @@
+/* 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 "mads/mads.h"
+#include "mads/sound.h"
+#include "common/scummsys.h"
+#include "common/config-manager.h"
+#include "common/debug-channels.h"
+#include "engines/util.h"
+#include "common/events.h"
+
+namespace MADS {
+
+MADSEngine *g_vm;
+
+MADSEngine::MADSEngine(OSystem *syst, const MADSGameDescription *gameDesc) :
+ Engine(syst), _randomSource("MADS") {
+ DebugMan.addDebugChannel(kDebugPath, "Path", "Pathfinding debug level");
+ DebugMan.addDebugChannel(kDebugScripts, "scripts", "Game scripts");
+}
+
+MADSEngine::~MADSEngine() {
+}
+
+void MADSEngine::initialise() {
+ _soundManager.setVm(this, _mixer);
+}
+
+static uint32 lastSoundFrame = 0;
+
+Common::Error MADSEngine::run() {
+ initGraphics(320, 200, false);
+ initialise();
+ _soundManager.test();
+
+ Common::Event e;
+ while (!shouldQuit()) {
+ g_system->getEventManager()->pollEvent(e);
+ g_system->delayMillis(10);
+
+ uint32 milli = g_system->getMillis();
+ if (milli > (lastSoundFrame + 50)) {
+ lastSoundFrame = milli;
+ _soundManager.poll();
+ }
+ }
+
+ return Common::kNoError;
+}
+
+int MADSEngine::getRandomNumber(int maxNumber) {
+ return _randomSource.getRandomNumber(maxNumber);
+}
+
+} // End of namespace MADS
diff --git a/engines/mads/mads.h b/engines/mads/mads.h
new file mode 100644
index 0000000000..b54c46ea96
--- /dev/null
+++ b/engines/mads/mads.h
@@ -0,0 +1,86 @@
+/* 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 MADS_MADS_H
+#define MADS_MADS_H
+
+#include "common/scummsys.h"
+#include "common/system.h"
+#include "common/error.h"
+#include "common/random.h"
+#include "common/util.h"
+#include "engines/engine.h"
+#include "graphics/surface.h"
+#include "mads/sound.h"
+
+/**
+ * This is the namespace of the MADS engine.
+ *
+ * Status of this engine: In Development
+ *
+ * Games using this engine:
+ * - Rex Nebular and the Cosmic Gender Bender
+ */
+namespace MADS {
+
+#define DEBUG_BASIC 1
+#define DEBUG_INTERMEDIATE 2
+#define DEBUG_DETAILED 3
+
+#define MAX_RESOLVE 1000
+
+enum MADSDebugChannels {
+ kDebugPath = 1 << 0,
+ kDebugScripts = 1 << 1
+};
+
+
+struct MADSGameDescription;
+
+
+class MADSEngine : public Engine {
+private:
+ const MADSGameDescription *_gameDescription;
+ Common::RandomSource _randomSource;
+ SoundManager _soundManager;
+
+ void initialise();
+protected:
+ // Engine APIs
+ virtual Common::Error run();
+ virtual bool hasFeature(EngineFeature f) const;
+public:
+ MADSEngine(OSystem *syst, const MADSGameDescription *gameDesc);
+ virtual ~MADSEngine();
+
+ uint32 getFeatures() const;
+ Common::Language getLanguage() const;
+ Common::Platform getPlatform() const;
+ uint16 getVersion() const;
+ bool getIsDemo() const;
+
+ int getRandomNumber(int maxNumber);
+};
+
+} // End of namespace MADS
+
+#endif /* MADS_MADS_H */
diff --git a/engines/mads/module.mk b/engines/mads/module.mk
new file mode 100644
index 0000000000..d8a26a2704
--- /dev/null
+++ b/engines/mads/module.mk
@@ -0,0 +1,15 @@
+MODULE := engines/mads
+
+MODULE_OBJS := \
+ detection.o \
+ sound.o \
+ sound_nebular.o \
+ mads.o
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_MADS), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/engines/mads/sound.cpp b/engines/mads/sound.cpp
new file mode 100644
index 0000000000..335afb4cdb
--- /dev/null
+++ b/engines/mads/sound.cpp
@@ -0,0 +1,53 @@
+/* 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 "audio/audiostream.h"
+#include "audio/decoders/raw.h"
+#include "common/memstream.h"
+#include "mads/sound.h"
+
+namespace MADS {
+
+SoundManager::SoundManager() {
+ _asound = nullptr;
+}
+
+SoundManager::~SoundManager() {
+ delete _asound;
+}
+
+void SoundManager::setVm(MADSEngine *vm, Audio::Mixer *mixer) {
+ _vm = vm;
+ _mixer = mixer;
+}
+
+void SoundManager::test() {
+ _asound = new Nebular::ASound1(_mixer);
+ _asound->command(28);
+}
+
+void SoundManager::poll() {
+ if (_asound)
+ _asound->poll();
+}
+
+} // End of namespace MADS
diff --git a/engines/mads/sound.h b/engines/mads/sound.h
new file mode 100644
index 0000000000..20b56ec4a1
--- /dev/null
+++ b/engines/mads/sound.h
@@ -0,0 +1,51 @@
+/* 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 MADS_SOUND_H
+#define MADS_SOUND_H
+
+#include "common/scummsys.h"
+#include "audio/audiostream.h"
+#include "audio/mixer.h"
+#include "mads/sound_nebular.h"
+
+namespace MADS {
+
+class MADSEngine;
+
+class SoundManager {
+private:
+ MADSEngine *_vm;
+ Audio::Mixer *_mixer;
+ Nebular::ASound *_asound;
+public:
+ SoundManager();
+ ~SoundManager();
+
+ void setVm(MADSEngine *vm, Audio::Mixer *mixer);
+ void test();
+ void poll();
+};
+
+} // End of namespace MADS
+
+#endif /* MADS_SOUND_H */
diff --git a/engines/mads/sound_nebular.cpp b/engines/mads/sound_nebular.cpp
new file mode 100644
index 0000000000..03fdc5ffae
--- /dev/null
+++ b/engines/mads/sound_nebular.cpp
@@ -0,0 +1,1097 @@
+/* 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 "audio/audiostream.h"
+#include "audio/decoders/raw.h"
+#include "common/algorithm.h"
+#include "common/debug.h"
+#include "common/memstream.h"
+#include "mads/sound.h"
+#include "mads/sound_nebular.h"
+
+namespace MADS {
+
+namespace Nebular {
+
+AdlibChannel::AdlibChannel() {
+ _activeCount = 0;
+ _field1 = 0;
+ _field2 = 0;
+ _field3 = 0;
+ _field4 = 0;
+ _field5 = 0;
+ _volume = 0;
+ _field7 = 0;
+ _field8 = 0;
+ _field9 = 0;
+ _fieldA = 0;
+ _fieldB = 0;
+ _fieldC = 0;
+ _fieldD = 0;
+ _fieldE = 0;
+ _ptr1 = nullptr;
+ _pSrc = nullptr;
+ _ptr3 = nullptr;
+ _ptr4 = nullptr;
+ _field17 = 0;
+ _field19 = 0;
+ _soundData = nullptr;
+ _field1D = 0;
+ _field1E = 0;
+ _field1F = 0;
+}
+
+void AdlibChannel::reset() {
+ _activeCount = 0;
+ _field1 = 0;
+ _field2 = 0;
+ _field3 = 0;
+}
+
+void AdlibChannel::enable(int flag) {
+ if (_activeCount) {
+ _fieldE = flag;
+ _soundData = nullptr; // TODO: Check original set it to flag??!
+ }
+}
+
+void AdlibChannel::setPtr2(byte *pData) {
+ _pSrc = pData;
+ _field2 = 0xFF;
+ _fieldA = 1;
+ _field9 = 1;
+}
+
+void AdlibChannel::load(byte *pData) {
+ _ptr1 = _pSrc = _ptr3 = pData;
+ _ptr4 = _soundData = pData;
+ _fieldA = 0xFF;
+ _activeCount = 1;
+ _fieldD = 64;
+ _field1 = 0;
+ _field1F = 0;
+ _field2 = _field3 = 0;
+ _volume = _field7 = 0;
+ _field1D = _field1E = 0;
+ _fieldE = 0;
+ _field9 = 0;
+ _fieldB = 0;
+ _field17 = 0;
+ _field19 = 0;
+}
+
+void AdlibChannel::check(byte *nullPtr) {
+ if (_activeCount && _fieldE) {
+ if (!_field1E) {
+ _pSrc = nullPtr;
+ _fieldE = 0;
+ } else {
+ _field2 = 0xFF;
+ _fieldA = 4;
+ if (!_field9)
+ _field9 = 1;
+ }
+ }
+}
+
+/*-----------------------------------------------------------------------*/
+
+SoundData::SoundData(Common::SeekableReadStream &s) {
+ _field0 = s.readByte();
+ _field1 = s.readByte();
+ _field2 = s.readByte();
+ _field3 = s.readByte();
+ _field4 = s.readByte();
+ _field5 = s.readByte();
+ _field6 = s.readByte();
+ _field7 = s.readByte();
+ _field8 = s.readByte();
+ _field9 = s.readByte();
+ _fieldA = s.readByte();
+ _fieldB = s.readByte();
+ _fieldC = s.readByte();
+ _fieldD = s.readByte();
+ _fieldE = s.readByte();
+ _field10 = s.readUint16LE();
+ _field12 = s.readUint16LE();
+ _field14 = s.readUint16LE();
+}
+
+/*-----------------------------------------------------------------------*/
+
+ASound::ASound(Audio::Mixer *mixer, const Common::String &filename, int dataOffset) {
+ // Open up the appropriate sound file
+ if (!_soundFile.open(filename))
+ error("Could not open file - %s", filename.c_str());
+
+ _dataOffset = dataOffset;
+ _mixer = mixer;
+ _opl = OPL::Config::create();
+ assert(_opl);
+ _opl->init(11025);
+
+ // Initialise fields
+ _activeChannelPtr = nullptr;
+ _soundDataPtr = nullptr;
+ _frameCounter = 0;
+ _isDisabled = false;
+ _v1 = 0;
+ _v2 = 0;
+ _activeChannelNumber = 0;
+ _freqMask1 = _freqMask2 = 0;
+ _freqBase1 = _freqBase2 = 0;
+ _channelNum1 = _channelNum2 = 0;
+ _v7 = 0;
+ _v8 = 0;
+ _v9 = 0;
+ _v10 = 0;
+ _pollResult = 0;
+ _resultFlag = 0;
+ _nullData[0] = _nullData[1] = 0;
+ Common::fill(&_ports[0], &_ports[256], 0);
+ _stateFlag = false;
+ _activeChannelReg = 0;
+ _v11 = 0;
+ _randomSeed = 1234;
+ _amDep = _vibDep = _splitPoint = true;
+
+ // Initialise the Adlib
+ adlibInit();
+
+ // Reset the adlib
+ command0();
+}
+
+ASound::~ASound() {
+ Common::List<CachedDataEntry>::iterator i;
+ for (i = _dataCache.begin(); i != _dataCache.end(); ++i)
+ delete[] (*i)._data;
+
+ delete _opl;
+}
+
+void ASound::adlibInit() {
+ // No implementation
+}
+
+int ASound::stop() {
+ command0();
+ int result = _pollResult;
+ _pollResult = 0;
+ return result;
+}
+
+int ASound::poll() {
+ // Update any playing sounds
+ update();
+
+ // Return result
+ int result = _pollResult;
+ _pollResult = 0;
+ return result;
+}
+
+void ASound::noise() {
+ int randomVal = getRandomNumber();
+
+ if (_v1) {
+ setFrequency(_channelNum1, (randomVal ^ 0xFFFF) & _freqMask1 + _freqBase1);
+ }
+
+ if (_v2) {
+ setFrequency(_channelNum2, randomVal & _freqMask2 + _freqBase2);
+ }
+}
+
+void ASound::write(int reg, int val) {
+ debug("reg %.2x = %.2x", reg, val);
+ _opl->write(reg, val);
+}
+
+int ASound::write2(int state, int reg, int val) {
+ // TODO: Original has a state parameter, not used when in Adlib mode?
+ _ports[reg] = val;
+ write(reg, val);
+ return state;
+}
+
+void ASound::channelOn(int reg, int volume) {
+ write2(8, reg, (_ports[reg] & 0xC0) | (volume & 0x3F));
+}
+
+void ASound::channelOff(int reg) {
+ write2(8, reg, _ports[reg] | 0x3F);
+}
+
+void ASound::resultCheck() {
+ if (_resultFlag != 1) {
+ _resultFlag = 1;
+ _pollResult = 1;
+ }
+}
+
+byte *ASound::loadData(int offset, int size) {
+ // First scan for an existing copy
+ Common::List<CachedDataEntry>::iterator i;
+ for (i = _dataCache.begin(); i != _dataCache.end(); ++i) {
+ CachedDataEntry &e = *i;
+ if (e._offset == offset)
+ return e._data;
+ }
+
+ // No existing entry found, so load up data and store as a new entry
+ CachedDataEntry rec;
+ rec._offset = offset;
+ rec._data = new byte[size];
+ _soundFile.seek(_dataOffset + offset);
+ _soundFile.read(rec._data, size);
+ _dataCache.push_back(rec);
+
+ // Return the data
+ return rec._data;
+}
+
+void ASound::playSound(int offset, int size) {
+ // Load the specified data block
+ playSound(loadData(offset, size));
+}
+
+void ASound::playSound(byte *pData) {
+ // Scan for a high level free channel
+ for (int i = 5; i < ADLIB_CHANNEL_COUNT; ++i) {
+ if (!_channels[i]._activeCount) {
+ _channels[i].load(pData);
+ return;
+ }
+ }
+
+ // None found, do a secondary scan for an interruptable channel
+ for (int i = ADLIB_CHANNEL_COUNT - 1; i >= ADLIB_CHANNEL_MIDWAY; --i) {
+ if (_channels[i]._fieldE == 0xFF) {
+ _channels[i].load(pData);
+ return;
+ }
+ }
+}
+
+bool ASound::isSoundActive(byte *pData) {
+ for (int i = 0; i < ADLIB_CHANNEL_MIDWAY; ++i) {
+ if (_channels[i]._activeCount && _channels[i]._soundData == pData)
+ return true;
+ }
+
+ return false;
+}
+
+void ASound::setFrequency(int channel, int freq) {
+ write2(8, 0xA0 + channel, freq & 0xFF);
+ write2(8, 0xB0 + channel, (freq >> 8) | 0x20);
+}
+
+int ASound::getRandomNumber() {
+ int v = 0x9248 + (int)_randomSeed;
+ _randomSeed = ((v >> 3) | (v << 13)) & 0xFFFF;
+ return _randomSeed;
+}
+
+void ASound::update() {
+ getRandomNumber();
+ if (_isDisabled)
+ return;
+
+ ++_frameCounter;
+ pollChannels();
+ checkChannels();
+
+ if (_v1 == _v2) {
+ if (_resultFlag != -1) {
+ _resultFlag = -1;
+ _pollResult = -1;
+ }
+ } else {
+ if (_v1) {
+ _freqBase1 += _v7;
+ if (!--_v1) {
+ if (!_v2 || _channelNum1 != _channelNum2) {
+ write2(8, 0xA0 + _channelNum1, 0);
+ write2(8, 0xB0 + _channelNum1, 0);
+ }
+ }
+ }
+
+ if (_v2) {
+ _freqBase2 += _v8;
+ if (!--_v2) {
+ if (!_v1 || _channelNum2 != _channelNum1) {
+ write2(8, 0xA0 + _channelNum2, 0);
+ write2(8, 0xB0 + _channelNum2, 0);
+ }
+ }
+ }
+ }
+}
+
+void ASound::pollChannels() {
+ _activeChannelNumber = 0;
+ for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i) {
+ _activeChannelPtr = &_channels[i];
+ pollActiveChannel();
+ }
+}
+
+void ASound::checkChannels() {
+ for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i)
+ _channels[i].check(_nullData);
+}
+
+void ASound::pollActiveChannel() {
+ bool flag = false;
+
+ AdlibChannel *chan = _activeChannelPtr;
+ if (chan->_activeCount) {
+ if (chan->_field8 > 0 && --chan->_field8 == 0)
+ updateOctave();
+
+ if (--_activeChannelPtr->_activeCount <= 0) {
+ for (;;) {
+ byte *pSrc = chan->_pSrc;
+ if (!(*pSrc & 0x80) || (*pSrc <= 0xF0)) {
+ if (flag)
+ updateActiveChannel();
+
+ chan->_field4 = *pSrc++;
+ chan->_activeCount = *pSrc++;
+ chan->_pSrc += 2;
+
+ if (!chan->_field4 || !chan->_activeCount) {
+ updateOctave();
+ } else {
+ chan->_field8 = chan->_activeCount - chan->_field7;
+ updateChannelState();
+ }
+
+ break;
+ } else {
+ flag = false;
+
+ switch ((~*pSrc) & 0xF) {
+ case 0:
+ if (!chan->_field17) {
+ if (*++pSrc == 0) {
+ chan->_pSrc += 2;
+ chan->_ptr3 = chan->_pSrc;
+ chan->_field17 = 0;
+ } else {
+ chan->_field17 = *pSrc;
+ chan->_pSrc = chan->_ptr3;
+ }
+ } else if (--chan->_field17) {
+ chan->_pSrc = chan->_ptr3;
+ } else {
+ chan->_pSrc += 2;
+ chan->_ptr3 = chan->_pSrc;
+ }
+ break;
+
+ case 1:
+ if (!chan->_field19) {
+ if (*++pSrc == 0) {
+ chan->_pSrc += 2;
+ chan->_ptr4 = chan->_pSrc;
+ chan->_ptr3 = chan->_pSrc;
+ chan->_field17 = 0;
+ chan->_field19 = 0;
+ } else {
+ chan->_field19 = *pSrc;
+ chan->_pSrc = chan->_ptr4;
+ chan->_ptr3 = chan->_ptr4;
+ }
+ } else if (--chan->_field19) {
+ chan->_ptr4 = chan->_pSrc;
+ chan->_ptr3 = chan->_pSrc;
+ } else {
+ chan->_pSrc += 2;
+ chan->_ptr4 = chan->_pSrc;
+ chan->_ptr3 = chan->_pSrc;
+ }
+ break;
+
+ case 2:
+ // Loop sound data
+ chan->_field1 = 0;
+ chan->_field2 = chan->_field3 = 0;
+ chan->_volume = chan->_field7 = 0;
+ chan->_field1D = chan->_field1E = 0;
+ chan->_field8 = 0;
+ chan->_field9 = 0;
+ chan->_fieldB = 0;
+ chan->_field17 = 0;
+ chan->_field19 = 0;
+ chan->_fieldD = 0x40;
+ chan->_ptr1 = chan->_soundData;
+ chan->_pSrc = chan->_soundData;
+ chan->_ptr3 = chan->_soundData;
+ chan->_ptr4 = chan->_soundData;
+
+ chan->_pSrc += 2;
+ break;
+
+ case 3:
+ chan->_field5 = *++pSrc;
+ chan->_pSrc += 2;
+ channelProc1(chan->_field5);
+ break;
+
+ case 4:
+ chan->_field7 = *++pSrc;
+ chan->_pSrc += 2;
+ break;
+
+ case 5:
+ chan->_field1 = *++pSrc;
+ chan->_pSrc += 2;
+ break;
+
+ case 6:
+ ++pSrc;
+ if (chan->_fieldE) {
+ chan->_volume = *pSrc >> 1;
+ flag = 1;
+ chan->_pSrc += 2;
+ } else {
+ chan->_pSrc += 2;
+ }
+ break;
+
+ case 7:
+ ++pSrc;
+ if (!chan->_fieldE) {
+ chan->_fieldA = *pSrc;
+ chan->_field2 = *++pSrc;
+ chan->_field9 = 1;
+ }
+
+ chan->_pSrc += 3;
+ break;
+
+ case 8:
+ chan->_field1D = *++pSrc;
+ chan->_pSrc += 2;
+ break;
+
+ case 9: {
+ int v1 = *++pSrc;
+ ++pSrc;
+ int v2 = (v1 - 1) & getRandomNumber();
+ int v3 = pSrc[v2];
+ int v4 = pSrc[v1];
+
+ pSrc[v4 + v1 + 1] = v3;
+ chan->_pSrc += v1 + 3;
+ break;
+ }
+
+ case 10:
+ ++pSrc;
+ if (chan->_fieldE) {
+ chan->_pSrc += 2;
+ } else {
+ chan->_field1E = *pSrc >> 1;
+ flag = 1;
+ chan->_pSrc += 2;
+ }
+ break;
+
+ case 11:
+ chan->_fieldD = *++pSrc;
+ flag = 1;
+ chan->_pSrc += 2;
+ break;
+
+ case 12:
+ chan->_fieldC = *++pSrc;
+ chan->_field3 = *++pSrc;
+ chan->_fieldB = 1;
+ chan->_pSrc += 2;
+ break;
+
+ case 13:
+ ++pSrc;
+ chan->_pSrc += 2;
+ break;
+
+ case 14:
+ chan->_field1F = *++pSrc;
+ chan->_pSrc += 2;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ if (chan->_field1)
+ updateFNumber();
+
+ flag = false;
+ if (chan->_field9 || chan->_field8) {
+ if (!--chan->_field9) {
+ chan->_field9 = chan->_fieldA;
+ if (chan->_field2) {
+ chan->_field9 = CLIP(chan->_field2 + chan->_field1E, 0, 63);
+ }
+ }
+
+ if (!--chan->_fieldB) {
+ chan->_fieldB = chan->_fieldC;
+ if (chan->_field3) {
+ chan->_fieldD = chan->_field3;
+ flag = true;
+ }
+ }
+
+ if (flag)
+ updateActiveChannel();
+ }
+ }
+
+ ++_activeChannelNumber;
+}
+
+void ASound::updateOctave() {
+ int reg = 0xB0 + _activeChannelNumber;
+ write2(8, reg, _ports[reg] & 0xDF);
+}
+
+static int _vList1[] = {
+ 0x200, 0x21E, 0x23F, 0x261, 0x285, 0x2AB,
+ 0x2D4, 0x2FF, 0x32D, 0x35D, 0x390, 0x3C7
+};
+
+void ASound::updateChannelState() {
+ updateActiveChannel();
+
+ if (_channelData[_activeChannelNumber]._field0) {
+ if (_channelNum1 == _activeChannelNumber)
+ _stateFlag = 0;
+ if (_channelNum2 == _activeChannelNumber)
+ _stateFlag = 1;
+
+ if (!_stateFlag) {
+ _stateFlag = 1;
+ if (_v1)
+ write2(8, 0xB0 + _channelNum1, _ports[0xB0 + _channelNum1] & 0xDF);
+
+ _channelNum1 = _activeChannelNumber;
+ _v1 = _channelData[_channelNum1]._field0;
+ _freqMask1 = _channelData[_channelNum1]._freqMask;
+ _freqBase1 = _channelData[_channelNum1]._freqBase;
+ _v7 = _channelData[_channelNum1]._field6;
+ } else {
+ _stateFlag = 0;
+ if (_v2)
+ write2(8, 0xB0 + _channelNum2, _ports[0xB0 + _channelNum2] & 0xDF);
+
+ _channelNum2 = _activeChannelNumber;
+ _v2 = _channelData[_channelNum2]._field0;
+ _freqMask2 = _channelData[_channelNum2]._freqMask;
+ _freqBase2 = _channelData[_channelNum2]._freqBase;
+ _v8 = _channelData[_channelNum2]._field6;
+ }
+
+ resultCheck();
+ } else {
+ int reg = 0xA0 + _activeChannelNumber;
+ int v = (_activeChannelPtr->_field4 + _activeChannelPtr->_field1F) / 12;
+ int vHi = v >> 8;
+ int vLo = v & 0xFF;
+ int val = _vList1[vHi] + vHi;
+ write2(8, reg, val & 0xFF);
+
+ reg += 0x10;
+ write2(8, reg, (_ports[reg] & 0x20) | (vLo << 2) | (val >> 8));
+
+ write2(8, reg, _ports[reg] | 0x20);
+ }
+}
+
+static const int outputIndexes[] = {
+ 3, 1, 4, 2, 5, 6, 9, 7, 10, 8, 11, 12, 15, 13, 16, 14, 17
+};
+static const int outputChannels[] = {
+ 0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21, 0
+};
+static const int volumeList[] = {
+ 0x3F, 0x3F, 0x36, 0x31, 0x2D, 0x2A, 0x28, 0x26, 0x24, 0x22, 0x21, 0x20, 0x1F, 0x1E, 0x1D, 0x1C,
+ 0x1B, 0x1A, 0x19, 0x19, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x14, 0x14, 0x13, 0x12, 0x12,
+ 0x11, 0x11, 0x10, 0x10, 0x0F, 0x0F, 0x0E, 0x0E, 0x0D, 0x0D, 0x0C, 0x0C, 0x0B, 0x0B, 0x0A, 0x0A,
+ 0x0A, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+void ASound::updateActiveChannel() {
+ int reg = 0x40 + outputChannels[outputIndexes[_activeChannelNumber * 2]];
+ int portVal = _ports[reg] & 0xFFC0;
+ int newVolume = CLIP(_activeChannelPtr->_volume + _activeChannelPtr->_field1E, 0, 63);
+
+ // TODO: Double-check _v5660 = 5600h
+ int val = CLIP(newVolume - volumeList[_activeChannelPtr->_fieldD], 0, 63);
+ val = (63 - val) | portVal;
+
+ int val2 = CLIP(newVolume - volumeList[-(_activeChannelPtr->_fieldD - 127)], 0, 63);
+ val2 = (63 - val2) | portVal;
+ write2(0, reg, val);
+ write2(2, reg, val2);
+}
+
+void ASound::channelProc1(int recIndex) {
+ _activeChannelReg = 0xB0 + _activeChannelNumber;
+ write2(8, _activeChannelReg, _ports[_activeChannelReg] & 0xDF);
+
+ _activeChannelReg = _activeChannelNumber;
+ _soundDataPtr = &_soundData[recIndex * 2];
+ _v11 = outputChannels[outputIndexes[_activeChannelReg * 2 - 1]];
+ channelProc2();
+
+ AdlibChannelData &cd = _channelData[_activeChannelNumber];
+ cd._field6 = _soundDataPtr->_field14;
+ cd._freqBase = _soundDataPtr->_field12;
+ cd._freqMask = _soundDataPtr->_field10;
+ cd._field0 = _soundDataPtr->_fieldE;
+
+ _soundDataPtr = &_soundData[recIndex * 2 + 1];
+ _v11 = outputChannels[outputIndexes[_activeChannelReg * 2]];
+ channelProc2();
+}
+
+void ASound::channelProc2() {
+ // Write out vib flags and split point
+ write2(8, 0x40 + _v11, 0x3F);
+ int depthRhythm = _ports[0xBD] & 0x3F | (_amDep ? 0x80 : 0) |
+ (_vibDep ? 0x40 : 0);
+ write2(8, 0xBD, depthRhythm);
+ write2(8, 8, _splitPoint ? 0x40 : 0);
+
+ int val = _soundDataPtr->_fieldA;
+ val = (val << 1) | (1 - _soundDataPtr->_fieldD);
+ write2(8, 0xC0 + _activeChannelReg, val);
+
+ val = (_soundDataPtr->_field0 << 4) | (_soundDataPtr->_field1 & 0xF);
+ write2(8, 0x60 + _v11, val);
+
+ val = (_soundDataPtr->_field2 << 4) | (_soundDataPtr->_field3 & 0xF);
+ write2(8, 0x80 + _v11, val);
+
+ val = (_soundDataPtr->_fieldB ? 0x80 : 0) | (_soundDataPtr->_fieldC ? 0x40 : 0)
+ | (_soundDataPtr->_field4 ? 0x20 : 0) | (_soundDataPtr->_field5 ? 0x10 : 0)
+ | (_soundDataPtr->_field9 & 0xF);
+ write2(8, 0x20 + _v11, val);
+
+ write2(8, 0xE0 + _v11, _soundDataPtr->_field8 & 3);
+
+ val = -(_soundDataPtr->_field6 & 0x3F - 0x3F) | (_soundDataPtr->_field7 << 6);
+ write2(8, 0x40 + _v11, val);
+}
+
+void ASound::updateFNumber() {
+ int loReg = 0xA0 + _activeChannelNumber;
+ int hiReg = 0xB0 + _activeChannelNumber;
+ int val1 = (_ports[hiReg] & 0x1F) << 8;
+ val1 += _ports[loReg] + _activeChannelPtr->_field1;
+ write2(8, loReg, val1);
+
+ int val2 = (_ports[hiReg] & 0x20) | (val1 >> 8);
+ write2(8, hiReg, val2);
+}
+
+int ASound::command0() {
+ bool isDisabled = _isDisabled;
+ _isDisabled = true;
+
+ for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i)
+ _channels[i].reset();
+
+ _v1 = 0;
+ _v2 = 0;
+ _freqMask1 = _freqMask2 = 0;
+ _freqBase1 = _freqBase2 = 0;
+ _v7 = 0;
+ _v8 = 0;
+
+ _isDisabled = isDisabled;
+ return 0;
+}
+
+int ASound::command1() {
+ for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i)
+ _channels[i].enable(0xFF);
+ return 0;
+}
+
+int ASound::command2() {
+ for (int i = 0; i < ADLIB_CHANNEL_MIDWAY; ++i)
+ _channels[i].setPtr2(_nullData);
+ return 0;
+}
+
+int ASound::command3() {
+ for (int i = 0; i < ADLIB_CHANNEL_MIDWAY; ++i)
+ _channels[i].enable(0xFF);
+ return 0;
+}
+
+int ASound::command4() {
+ for (int i = ADLIB_CHANNEL_MIDWAY; i < ADLIB_CHANNEL_COUNT; ++i)
+ _channels[i].setPtr2(_nullData);
+ return 0;
+}
+
+int ASound::command5() {
+ for (int i = 5; i < ADLIB_CHANNEL_COUNT; ++i)
+ _channels[i].enable(0xFF);
+ return 0;
+}
+
+int ASound::command6() {
+ _v9 = _v1;
+ _v1 = 0;
+ _v10 = _v2;
+ _v2 = 0;
+
+ channelOff(0x43);
+ channelOff(0x44);
+ channelOff(0x45);
+ channelOff(0x4B);
+ channelOff(0x4C);
+ channelOff(0x4D);
+ channelOff(0x53);
+ channelOff(0x54);
+ channelOff(0x55);
+
+ return 0;
+}
+
+int ASound::command7() {
+ channelOn(0x43, _channels[0]._volume);
+ channelOn(0x44, _channels[1]._volume);
+ channelOn(0x45, _channels[2]._volume);
+ channelOn(0x4B, _channels[3]._volume);
+ channelOn(0x4C, _channels[4]._volume);
+ channelOn(0x4D, _channels[5]._volume);
+
+ _v1 = _v9;
+ _v2 = _v10;
+
+ if (_v9 != _v10)
+ resultCheck();
+
+ _isDisabled = 0;
+ return _v10;
+}
+
+int ASound::command8() {
+ int result = 0;
+ for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i)
+ result |= _channels[i]._activeCount;
+
+ return result;
+}
+
+/*-----------------------------------------------------------------------*/
+
+const ASound1::CommandPtr ASound1::_commandList[42] = {
+ &ASound1::command0, &ASound1::command1, &ASound1::command2, &ASound1::command3,
+ &ASound1::command4, &ASound1::command5, &ASound1::command6, &ASound1::command7,
+ &ASound1::command8, &ASound1::command9, &ASound1::command10, &ASound1::command11,
+ &ASound1::command12, &ASound1::command13, &ASound1::command14, &ASound1::command15,
+ &ASound1::command16, &ASound1::command17, &ASound1::command18, &ASound1::command19,
+ &ASound1::command20, &ASound1::command21, &ASound1::command22, &ASound1::command23,
+ &ASound1::command24, &ASound1::command25, &ASound1::command26, &ASound1::command27,
+ &ASound1::command28, &ASound1::command29, &ASound1::command30, &ASound1::command31,
+ &ASound1::command32, &ASound1::command33, &ASound1::command34, &ASound1::command35,
+ &ASound1::command36, &ASound1::command37, &ASound1::command38, &ASound1::command39,
+ &ASound1::command40, &ASound1::command41
+};
+
+ASound1::ASound1(Audio::Mixer *mixer): ASound(mixer, "asound.001", 0x1520) {
+ _cmd23Toggle = false;
+
+ // Load sound data
+ _soundFile.seek(_dataOffset + 0x12C);
+ for (int i = 0; i < 98; ++i)
+ _soundData.push_back(SoundData(_soundFile));
+}
+
+int ASound1::command(int commandId) {
+ if (commandId > 41)
+ return 0;
+
+ _frameCounter = 0;
+ return (this->*_commandList[commandId])();
+}
+
+int ASound1::command9() {
+ playSound(0xC68, 12);
+ return 0;
+}
+
+int ASound1::command10() {
+ byte *pData1 = loadData(0x130E, 48);
+ if (!isSoundActive(pData1)) {
+ command1();
+ _channels[0].load(pData1);
+ _channels[1].load(loadData(0x133E, 392));
+ _channels[2].load(loadData(0x14C6, 46));
+ _channels[3].load(loadData(0x14F4, 48));
+ }
+
+ return 0;
+}
+
+int ASound1::command11() {
+ command111213();
+ _channels[0]._field1E = 0;
+ _channels[1]._field1E = 0;
+ return 0;
+}
+
+int ASound1::command12() {
+ command111213();
+ _channels[0]._field1E = 40;
+ _channels[1]._field1E = 0;
+ return 0;
+}
+
+int ASound1::command13() {
+ command111213();
+ _channels[0]._field1E = 40;
+ _channels[1]._field1E = 50;
+ return 0;
+}
+
+int ASound1::command14() {
+ playSound(0x1216, 248);
+ return 0;
+}
+
+int ASound1::command15() {
+ byte *pData1 = loadData(0x1524, 152);
+ if (!isSoundActive(pData1)) {
+ command1();
+ _channels[4].load(pData1);
+ _channels[5].load(loadData(0x15BC, 94));
+ _channels[6].load(loadData(0x161A, 94));
+ _channels[7].load(loadData(0x1678, 42));
+ _channels[8].load(loadData(0x16A2, 42));
+ }
+
+ return 0;
+}
+
+int ASound1::command16() {
+ playSound(0xC74, 14);
+ return 0;
+}
+
+int ASound1::command17() {
+ playSound(0xE9A, 10);
+ return 0;
+}
+
+int ASound1::command18() {
+ playSound(0xCA6, 20);
+ return 0;
+}
+
+int ASound1::command19() {
+ playSound(0xCBA, 74);
+ return 0;
+}
+
+int ASound1::command20() {
+ byte *pData = loadData(0xD18, 28);
+ if (!isSoundActive(pData))
+ playSound(pData);
+ return 0;
+}
+
+int ASound1::command21() {
+ playSound(0xD04, 20);
+ return 0;
+}
+
+int ASound1::command22() {
+ byte *pData = loadData(0xD34, 10);
+ pData[6] = (getRandomNumber() & 7) + 85;
+
+ if (!isSoundActive(pData))
+ playSound(pData);
+
+ return 0;
+}
+
+int ASound1::command23() {
+ _cmd23Toggle = !_cmd23Toggle;
+ playSound(_cmd23Toggle ? 0xD3E : 0xD46, 8);
+ return 0;
+}
+
+int ASound1::command24() {
+ playSound(0xD4E, 18);
+ playSound(0xD60, 20);
+ playSound(0xD74, 14);
+ return 0;
+}
+
+int ASound1::command25() {
+ byte *pData = loadData(0xD82, 16);
+ if (!isSoundActive(pData))
+ playSound(pData);
+
+ return 0;
+}
+
+int ASound1::command26() {
+ error("TODO: command26");
+ return 0;
+}
+
+int ASound1::command27() {
+ error("TODO: ASound::command27");
+ return 0;
+}
+
+int ASound1::command28() {
+ playSound(0xD92, 28);
+ return 0;
+}
+
+int ASound1::command29() {
+ error("TODO: ASound::command29");
+ return 0;
+}
+
+int ASound1::command30() {
+ error("TODO: ASound::command30");
+ return 0;
+}
+
+int ASound1::command31() {
+ byte *pData = loadData(0xDAE, 14);
+ if (!isSoundActive(pData))
+ playSound(pData);
+
+ return 0;
+}
+
+int ASound1::command32() {
+ error("TODO: ASound::command32");
+ return 0;
+}
+
+int ASound1::command33() {
+ playSound(0xDBC, 10);
+ playSound(0xDC6, 10);
+ return 0;
+}
+
+int ASound1::command34() {
+ int v = getRandomNumber() & 0x20;
+ if (!v)
+ v = 0x60;
+
+ byte *pData = loadData(0xDD0, 22);
+ pData[8] = pData[15] = v;
+ playSound(pData);
+ return 0;
+}
+
+int ASound1::command35() {
+ playSound(0xDE6, 16);
+ return 0;
+}
+
+int ASound1::command36() {
+ playSound(0xE10, 10);
+ command34();
+
+ return 0;
+}
+
+int ASound1::command37() {
+ playSound(0xE1A, 14);
+ return 0;
+}
+
+int ASound1::command38() {
+ playSound(0xE28, 114);
+ return 0;
+}
+
+int ASound1::command39() {
+ byte *pData1 = loadData(0x16CC, 82);
+ if (!isSoundActive(pData1)) {
+ _channels[5].load(pData1);
+ _channels[6].load(loadData(0x171E, 30));
+ _channels[7].load(loadData(0x173C, 40));
+ _channels[8].load(loadData(0x1764, 64));
+ }
+ return 0;
+}
+
+int ASound1::command40() {
+ playSound(0xDF6, 26);
+ return 0;
+}
+
+int ASound1::command41() {
+ playSound(0xC32, 34);
+ playSound(0xC54, 20);
+ return 0;
+}
+
+void ASound1::command111213() {
+ byte *pData1 = loadData(0xEF6, 408);
+ if (!isSoundActive(pData1)) {
+ command1();
+ _channels[0].load(pData1);
+ _channels[1].load(loadData(0x108E, 266));
+ _channels[2].load(loadData(0x1198, 66));
+ _channels[2].load(loadData(0x11DA, 60));
+ }
+}
+
+void ASound1::command2627293032() {
+ // TODO: This method takes a parameter off the stack for several levels up.
+ // i.e. something the caller's caller pushed onto the stack. Need to figure
+ // out a better way to pass parameters down if this is actually in use.
+}
+
+} // End of namespace Nebular
+
+} // End of namespace MADS
diff --git a/engines/mads/sound_nebular.h b/engines/mads/sound_nebular.h
new file mode 100644
index 0000000000..8f92389ee1
--- /dev/null
+++ b/engines/mads/sound_nebular.h
@@ -0,0 +1,354 @@
+/* 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 MADS_SOUND_NEBULAR_H
+#define MADS_SOUND_NEBULAR_H
+
+#include "common/scummsys.h"
+#include "common/file.h"
+#include "audio/audiostream.h"
+#include "audio/fmopl.h"
+#include "audio/mixer.h"
+
+namespace MADS {
+
+namespace Nebular {
+
+class SoundManager;
+
+/**
+ * Represents the data for a channel on the Adlib
+ */
+class AdlibChannel {
+public:
+ int _activeCount;
+ int _field1;
+ int _field2;
+ int _field3;
+ int _field4;
+ int _field5;
+ int _volume;
+ int _field7;
+ int _field8;
+ int _field9;
+ int _fieldA;
+ int _fieldB;
+ int _fieldC;
+ int _fieldD;
+ int _fieldE;
+ byte *_ptr1;
+ byte *_pSrc;
+ byte *_ptr3;
+ byte *_ptr4;
+ int _field17;
+ int _field19;
+ byte *_soundData;
+ int _field1D;
+ int _field1E;
+ int _field1F;
+public:
+ AdlibChannel();
+
+ void reset();
+ void enable(int flag);
+ void setPtr2(byte *pData);
+ void load(byte *pData);
+ void check(byte *nullPtr);
+};
+
+class AdlibChannelData {
+public:
+ int _field0;
+ int _freqMask;
+ int _freqBase;
+ int _field6;
+};
+
+class SoundData {
+public:
+ int _field0;
+ int _field1;
+ int _field2;
+ int _field3;
+ int _field4;
+ int _field5;
+ int _field6;
+ int _field7;
+ int _field8;
+ int _field9;
+ int _fieldA;
+ int _fieldB;
+ int _fieldC;
+ int _fieldD;
+ int _fieldE;
+ int _field10;
+ int _field12;
+ int _field14;
+
+ SoundData() {}
+ SoundData(Common::SeekableReadStream &s);
+};
+
+#define ADLIB_CHANNEL_COUNT 9
+#define ADLIB_CHANNEL_MIDWAY 5
+
+/**
+ * Base class for the sound player resource files
+ */
+class ASound {
+private:
+ struct CachedDataEntry {
+ int _offset;
+ byte *_data;
+ };
+ Common::List<CachedDataEntry> _dataCache;
+ uint16 _randomSeed;
+
+ /**
+ * Does the initial Adlib initialisation
+ */
+ void adlibInit();
+
+ /**
+ * Does on-going processing for the Adlib sounds being played
+ */
+ void update();
+
+ /**
+ * Polls each of the channels for updates
+ */
+ void pollChannels();
+
+ /**
+ * Checks the status of the channels
+ */
+ void checkChannels();
+
+ /**
+ * Polls the currently active channel
+ */
+ void pollActiveChannel();
+
+ /**
+ * Updates the octave of the currently active channel
+ */
+ void updateOctave();
+
+ void updateChannelState();
+ void updateActiveChannel();
+ void channelProc1(int recIndex);
+ void channelProc2();
+ void updateFNumber();
+protected:
+ /**
+ * Write a byte to an Adlib register
+ */
+ void write(int reg, int val);
+
+ /**
+ * Write a byte to an Adlib register, and store it in the _ports array
+ */
+ int write2(int state, int reg, int val);
+
+ /**
+ * Turn a channel on
+ */
+ void channelOn(int reg, int volume);
+
+ /**
+ * Turn a channel off
+ */
+ void channelOff(int reg);
+
+ /**
+ * Checks for whether a poll result needs to be set
+ */
+ void resultCheck();
+
+ /**
+ * Loads a data block from the sound file, caching the result for any future
+ * calls for the same data
+ */
+ byte *loadData(int offset, int size);
+
+ /**
+ * Play the specified sound
+ * @param offset Offset of sound data within sound player data segment
+ * @param size Size of sound data block
+ */
+ void playSound(int offset, int size);
+
+ /**
+ * Play the specified raw sound data
+ * @param pData Pointer to data block containing sound data
+ */
+ void playSound(byte *pData);
+
+ /**
+ * Checks to see whether the given block of data is already loaded into a channel.
+ */
+ bool isSoundActive(byte *pData);
+
+ /**
+ * Sets the frequency for a given channel.
+ */
+ void setFrequency(int channel, int freq);
+
+ /**
+ * Returns a 16-bit random number
+ */
+ int getRandomNumber();
+
+ int command0();
+ int command1();
+ int command2();
+ int command3();
+ int command4();
+ int command5();
+ int command6();
+ int command7();
+ int command8();
+public:
+ Audio::Mixer *_mixer;
+ FM_OPL *_opl;
+ AdlibChannel _channels[ADLIB_CHANNEL_COUNT];
+ AdlibChannel *_activeChannelPtr;
+ AdlibChannelData _channelData[11];
+ Common::Array<SoundData> _soundData;
+ SoundData *_soundDataPtr;
+ Common::File _soundFile;
+ int _dataOffset;
+ int _frameCounter;
+ bool _isDisabled;
+ int _v1;
+ int _v2;
+ int _activeChannelNumber;
+ int _freqMask1;
+ int _freqMask2;
+ int _freqBase1;
+ int _freqBase2;
+ int _channelNum1, _channelNum2;
+ int _v7;
+ int _v8;
+ int _v9;
+ int _v10;
+ int _pollResult;
+ int _resultFlag;
+ byte _nullData[2];
+ int _ports[256];
+ bool _stateFlag;
+ int _activeChannelReg;
+ int _v11;
+ bool _amDep, _vibDep, _splitPoint;
+public:
+ /**
+ * Constructor
+ * @param filename Specifies the adlib sound player file to use
+ * @param dataOffset Offset in the file of the data segment
+ */
+ ASound(Audio::Mixer *mixer, const Common::String &filename, int dataOffset);
+
+ /**
+ * Destructor
+ */
+ virtual ~ASound();
+
+ /**
+ * Execute a player command. Most commands represent sounds to play, but some
+ * low number commands also provide control operations
+ */
+ virtual int command(int commandId) = 0;
+
+ /**
+ * Stop all currently playing sounds
+ */
+ int stop();
+
+ /**
+ * Main poll method to allow sounds to progress
+ */
+ int poll();
+
+ /**
+ * General noise/note output
+ */
+ void noise();
+
+ /**
+ * Return the current frame counter
+ */
+ int getFrameCounter() { return _frameCounter; }
+};
+
+class ASound1: public ASound {
+private:
+ typedef int (ASound1::*CommandPtr)();
+ static const CommandPtr _commandList[42];
+ bool _cmd23Toggle;
+
+ int command9();
+ int command10();
+ int command11();
+ int command12();
+ int command13();
+ int command14();
+ int command15();
+ int command16();
+ int command17();
+ int command18();
+ int command19();
+ int command20();
+ int command21();
+ int command22();
+ int command23();
+ int command24();
+ int command25();
+ int command26();
+ int command27();
+ int command28();
+ int command29();
+ int command30();
+ int command31();
+ int command32();
+ int command33();
+ int command34();
+ int command35();
+ int command36();
+ int command37();
+ int command38();
+ int command39();
+ int command40();
+ int command41();
+
+ void command111213();
+ void command2627293032();
+public:
+ ASound1(Audio::Mixer *mixer);
+
+ virtual int command(int commandId);
+};
+
+} // End of namespace Nebular
+
+} // End of namespace MADS
+
+#endif /* MADS_SOUND_NEBULAR_H */