aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
authorJoseph-Eugene Winzer2018-03-23 01:42:58 +0100
committerThierry Crozat2018-04-15 18:22:57 +0100
commitb2dc8d54240ba96a3329b94b3d28ea0cba2dd73c (patch)
tree5a29aaefe98d8a4c14566e30ed20990bd898de97 /engines
parent07f5f34d42346fbe5bbe147d12a526e172433b08 (diff)
downloadscummvm-rg350-b2dc8d54240ba96a3329b94b3d28ea0cba2dd73c.tar.gz
scummvm-rg350-b2dc8d54240ba96a3329b94b3d28ea0cba2dd73c.tar.bz2
scummvm-rg350-b2dc8d54240ba96a3329b94b3d28ea0cba2dd73c.zip
SUPERNOVA: Adds resource abstraction
The resource abstraction includes loading of sound files and cursor graphics.
Diffstat (limited to 'engines')
-rw-r--r--engines/supernova/module.mk1
-rw-r--r--engines/supernova/msn_def.h6
-rw-r--r--engines/supernova/resman.cpp359
-rw-r--r--engines/supernova/resman.h61
-rw-r--r--engines/supernova/sound.cpp24
-rw-r--r--engines/supernova/sound.h5
-rw-r--r--engines/supernova/supernova.cpp275
-rw-r--r--engines/supernova/supernova.h31
8 files changed, 438 insertions, 324 deletions
diff --git a/engines/supernova/module.mk b/engines/supernova/module.mk
index 520c3ae8ce..8be8e72509 100644
--- a/engines/supernova/module.mk
+++ b/engines/supernova/module.mk
@@ -5,6 +5,7 @@ MODULE_OBJS := \
detection.o \
graphics.o \
supernova.o \
+ resman.o \
rooms.o \
sound.o \
state.o
diff --git a/engines/supernova/msn_def.h b/engines/supernova/msn_def.h
index 22a73582f6..828c573b27 100644
--- a/engines/supernova/msn_def.h
+++ b/engines/supernova/msn_def.h
@@ -50,12 +50,6 @@ enum MessagePosition {
kMessageTop
};
-struct AudioInfo {
- int _filenumber;
- int _offsetStart;
- int _offsetEnd;
-};
-
const int kColorBlack = 0;
const int kColorWhite25 = 1;
const int kColorWhite35 = 2;
diff --git a/engines/supernova/resman.cpp b/engines/supernova/resman.cpp
new file mode 100644
index 0000000000..d38405dddf
--- /dev/null
+++ b/engines/supernova/resman.cpp
@@ -0,0 +1,359 @@
+/* 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/decoders/raw.h"
+#include "audio/mixer.h"
+#include "audio/mods/protracker.h"
+#include "audio/audiostream.h"
+#include "common/memstream.h"
+#include "common/system.h"
+#include "graphics/cursorman.h"
+#include "graphics/palette.h"
+
+#include "supernova/resman.h"
+#include "supernova/supernova.h"
+
+namespace Supernova {
+
+struct AudioInfo {
+ int _filenumber;
+ int _offsetStart;
+ int _offsetEnd;
+};
+
+static Common::MemoryReadStream *convertToMod(const char *filename, int version = 1);
+
+static const AudioInfo audioInfo[kAudioNumSamples] = {
+ {44, 0, -1},
+ {45, 0, -1},
+ {46, 0, 2510},
+ {46, 2510, 4020},
+ {46, 4020, -1},
+ {47, 0, 24010},
+ {47, 24010, -1},
+ {48, 0, 2510},
+ {48, 2510, 10520},
+ {48, 10520, 13530},
+ {48, 13530, -1},
+ {50, 0, 12786},
+ {50, 12786, -1},
+ {51, 0, -1},
+ {53, 0, -1},
+ {54, 0, 8010},
+ {54, 8010, 24020},
+ {54, 24020, 30030},
+ {54, 30030, 31040},
+ {54, 31040, -1}
+};
+
+ResourceManager::ResourceManager(SupernovaEngine *vm)
+ : _audioRate(11931)
+ , _vm(vm) {
+ initSoundFiles();
+ initGraphics();
+}
+
+ResourceManager::~ResourceManager() {
+ delete _musicIntro;
+ delete _musicOutro;
+ for (int i = 0; i < kAudioNumSamples; ++i)
+ delete _soundSamples[i];
+}
+
+Audio::SeekableAudioStream *ResourceManager::getSoundStream(AudioIndex index) {
+ Audio::SeekableAudioStream *stream = _soundSamples[index];
+ stream->rewind();
+
+ return stream;
+}
+
+Audio::AudioStream *ResourceManager::getSoundStream(MusicIndex index) {
+ switch (index) {
+ case kMusicIntro:
+ return _musicIntro;
+ case kMusicOutro:
+ return _musicOutro;
+ default:
+ error("Invalid music constant in playAudio()");
+ }
+}
+
+void ResourceManager::initSoundFiles() {
+ // Sound
+ // Note:
+ // - samples start with a header of 6 bytes: 01 SS SS 00 AD 00
+ // where SS SS (LE uint16) is the size of the sound sample + 2
+ // - samples end with a footer of 4 bytes: 00 00
+ // Skip those in the buffer
+ Common::File file;
+
+ for (int i = 0; i < kAudioNumSamples; ++i) {
+ if (!file.open(Common::String::format("msn_data.%03d", audioInfo[i]._filenumber))) {
+ error("File %s could not be read!", file.getName());
+ }
+
+ int length = 0;
+ byte *buffer = nullptr;
+
+ if (audioInfo[i]._offsetEnd == -1) {
+ file.seek(0, SEEK_END);
+ length = file.pos() - audioInfo[i]._offsetStart - 10;
+ } else {
+ length = audioInfo[i]._offsetEnd - audioInfo[i]._offsetStart - 10;
+ }
+ buffer = new byte[length];
+ file.seek(audioInfo[i]._offsetStart + 6);
+ file.read(buffer, length);
+ file.close();
+
+ byte streamFlag = Audio::FLAG_UNSIGNED | Audio::FLAG_LITTLE_ENDIAN;
+ _soundSamples[i] = Audio::makeRawStream(buffer, length, _audioRate,
+ streamFlag, DisposeAfterUse::YES);
+ }
+
+ Common::MemoryReadStream *music;
+ music = convertToMod("msn_data.052");
+ _musicIntro = Audio::makeProtrackerStream(music);
+ delete music;
+
+ music = convertToMod("msn_data.049");
+ _musicOutro = Audio::makeProtrackerStream(music);
+ delete music;
+}
+
+void ResourceManager::initGraphics() {
+ _vm->_system->getPaletteManager()->setPalette(initVGAPalette, 0, 256);
+ initCursorGraphics();
+}
+
+void ResourceManager::initCursorGraphics() {
+ const uint16 *bufferNormal = reinterpret_cast<const uint16 *>(mouseNormal);
+ const uint16 *bufferWait = reinterpret_cast<const uint16 *>(mouseWait);
+ for (uint i = 0; i < sizeof(mouseNormal) / 4; ++i) {
+ for (uint bit = 0; bit < 16; ++bit) {
+ uint mask = 0x8000 >> bit;
+ uint bitIndex = i * 16 + bit;
+
+ _cursorNormal[bitIndex] = (READ_LE_UINT16(bufferNormal + i) & mask) ?
+ kColorCursorTransparent : kColorBlack;
+ if (READ_LE_UINT16(bufferNormal + i + 16) & mask)
+ _cursorNormal[bitIndex] = kColorLightRed;
+
+ _cursorWait[bitIndex] = (READ_LE_UINT16(bufferWait + i) & mask) ?
+ kColorCursorTransparent : kColorBlack;
+ if (READ_LE_UINT16(bufferWait + i + 16) & mask)
+ _cursorWait[bitIndex] = kColorLightRed;
+ }
+ }
+
+ CursorMan.replaceCursor(_cursorNormal, 16, 16, 0, 0, kColorCursorTransparent);
+ CursorMan.replaceCursorPalette(initVGAPalette, 0, 16);
+ CursorMan.showMouse(true);
+}
+
+static Common::MemoryReadStream *convertToMod(const char *filename, int version) {
+ // MSN format
+ struct {
+ uint16 seg;
+ uint16 start;
+ uint16 end;
+ uint16 loopStart;
+ uint16 loopEnd;
+ char volume;
+ char dummy[5];
+ } instr2[22];
+ int nbInstr2; // 22 for version1, 15 for version 2
+ int16 songLength;
+ char arrangement[128];
+ int16 patternNumber;
+ int32 note2[28][64][4];
+
+ nbInstr2 = ((version == 1) ? 22 : 15);
+
+ Common::File msnFile;
+ msnFile.open(filename);
+ if (!msnFile.isOpen()) {
+ warning("Data file '%s' not found", msnFile.getName());
+ return NULL;
+ }
+
+ for (int i = 0 ; i < nbInstr2 ; ++i) {
+ instr2[i].seg = msnFile.readUint16LE();
+ instr2[i].start = msnFile.readUint16LE();
+ instr2[i].end = msnFile.readUint16LE();
+ instr2[i].loopStart = msnFile.readUint16LE();
+ instr2[i].loopEnd = msnFile.readUint16LE();
+ instr2[i].volume = msnFile.readByte();
+ msnFile.read(instr2[i].dummy, 5);
+ }
+ songLength = msnFile.readSint16LE();
+ msnFile.read(arrangement, 128);
+ patternNumber = msnFile.readSint16LE();
+ for (int p = 0 ; p < patternNumber ; ++p) {
+ for (int n = 0 ; n < 64 ; ++n) {
+ for (int k = 0 ; k < 4 ; ++k) {
+ note2[p][n][k] = msnFile.readSint32LE();
+ }
+ }
+ }
+
+ /* MOD format */
+ struct {
+ char iname[22];
+ uint16 length;
+ char finetune;
+ char volume;
+ uint16 loopStart;
+ uint16 loopLength;
+ } instr[31];
+ int32 note[28][64][4];
+
+ // We can't recover some MOD effects since several of them are mapped to 0.
+ // Assume the MSN effect of value 0 is Arpeggio (MOD effect of value 0).
+ const char invConvEff[8] = {0, 1, 2, 3, 10, 12, 13 ,15};
+
+ // Reminder from convertToMsn
+ // 31 30 29 28 27 26 25 24 - 23 22 21 20 19 18 17 16 - 15 14 13 12 11 10 09 08 - 07 06 05 04 03 02 01 00
+ // h h h h g g g g f f f f e e e e d d d d c c c c b b b b a a a a
+ //
+ // MSN:
+ // hhhh (4 bits) Cleared to 0
+ // dddd c (5 bits) Sample index | after mapping through convInstr
+ // ccc (3 bits) Effect type | after mapping through convEff
+ // bbbb aaaa (8 bits) Effect value | unmodified
+ // gggg ffff eeee (12 bits) Sample period | unmodified
+ //
+ // MS2:
+ // hhhh (4 bits) Cleared to 0
+ // dddd (4 bits) Sample index | after mapping through convInstr
+ // cccc (4 bits) Effect type | unmodified
+ // bbbb aaaa (8 bits) Effect value | unmodified
+ // gggg ffff eeee (12 bits) Sample period | transformed (0xE000 / p) - 256
+ //
+ // MOD:
+ // hhhh dddd (8 bits) Sample index
+ // cccc (4 bits) Effect type for this channel/division
+ // bbbb aaaa (8 bits) Effect value
+ // gggg ffff eeee (12 bits) Sample period
+
+ // Can we recover the instruments mapping? I don't think so as part of the original instrument index is cleared.
+ // And it doesn't really matter as long as we are consistent.
+ // However we need to make sure 31 (or 15 in MS2) is mapped to 0 in MOD.
+ // We just add 1 to all other values, and this means a 1 <-> 1 mapping for the instruments
+ for (int p = 0; p < patternNumber; ++p) {
+ for (int n = 0; n < 64; ++n) {
+ for (int k = 0; k < 4; ++k) {
+ int32* l = &(note[p][n][k]);
+ *l = note2[p][n][k];
+ int32 i = 0;
+ if (nbInstr2 == 22) { // version 1
+ i = ((*l & 0xF800) >> 11);
+ int32 e = ((*l & 0x0700) >> 8);
+ int32 e1 = invConvEff[e];
+ *l &= 0x0FFF00FF;
+ *l |= (e1 << 8);
+ } else { // version 2
+ int32 h = (*l >> 16);
+ i = ((*l & 0xF000) >> 12);
+ *l &= 0x00000FFF;
+ if (h)
+ h = 0xE000 / (h + 256);
+ *l |= (h << 16);
+ if (i == 15)
+ i = 31;
+ }
+
+ // Add back index in note
+ if (i != 31) {
+ ++i;
+ *l |= ((i & 0x0F) << 12);
+ *l |= ((i & 0xF0) << 24);
+ }
+ }
+ }
+ }
+
+ for (int i = 0; i < 31; ++i) {
+ // iname is not stored in the mod file. Just set it to 'instrument#'
+ // finetune is not stored either. Assume 0.
+ memset(instr[i].iname, 0, 22);
+ sprintf(instr[i].iname, "instrument%d", i+1);
+ instr[i].length = 0;
+ instr[i].finetune = 0;
+ instr[i].volume = 0;
+ instr[i].loopStart = 0;
+ instr[i].loopLength = 0;
+
+ if (i < nbInstr2) {
+ instr[i].length = ((instr2[i].end - instr2[i].start) >> 1);
+ instr[i].loopStart = ((instr2[i].loopStart - instr2[i].start) >> 1);
+ instr[i].loopLength = (( instr2[i].loopEnd - instr2[i].loopStart) >> 1);
+ instr[i].volume = instr2[i].volume;
+ }
+ }
+
+ // The ciaaSpeed is kind of useless and not present in the MSN file.
+ // Traditionally 0x78 in SoundTracker. Was used in NoiseTracker as a restart point.
+ // ProTracker uses 0x7F. FastTracker uses it as a restart point, whereas ScreamTracker 3 uses 0x7F like ProTracker.
+ // You can use this to roughly detect which tracker made a MOD, and detection gets more accurate for more obscure MOD types.
+ char ciaaSpeed = 0x7F;
+
+ // The mark cannot be recovered either. Since we have 4 channels and 31 instrument it can be either ID='M.K.' or ID='4CHN'.
+ // Assume 'M.K.'
+ const char mark[4] = { 'M', '.', 'K', '.' };
+
+ Common::MemoryWriteStreamDynamic buffer(DisposeAfterUse::NO);
+
+ buffer.write(msnFile.getName(), 19);
+ buffer.writeByte(0);
+
+ for (int i = 0 ; i < 31 ; ++i) {
+ buffer.write(instr[i].iname, 22);
+ buffer.writeUint16BE(instr[i].length);
+ buffer.writeByte(instr[i].finetune);
+ buffer.writeByte(instr[i].volume);
+ buffer.writeUint16BE(instr[i].loopStart);
+ buffer.writeUint16BE(instr[i].loopLength);
+ }
+ buffer.writeByte((char)songLength);
+ buffer.writeByte(ciaaSpeed);
+ buffer.write(arrangement, 128);
+ buffer.write(mark, 4);
+
+ for (int p = 0 ; p < patternNumber ; ++p) {
+ for (int n = 0 ; n < 64 ; ++n) {
+ for (int k = 0 ; k < 4 ; ++k) {
+// buffer.writeUint32BE(*((uint32*)(note[p][n]+k)));
+ buffer.writeSint32BE(note[p][n][k]);
+ }
+ }
+ }
+
+ uint nb;
+ char buf[4096];
+ while ((nb = msnFile.read(buf, 4096)) > 0)
+ buffer.write(buf, nb);
+
+ return new Common::MemoryReadStream(buffer.getData(), buffer.size(), DisposeAfterUse::YES);
+}
+
+}
diff --git a/engines/supernova/resman.h b/engines/supernova/resman.h
new file mode 100644
index 0000000000..075b1e671f
--- /dev/null
+++ b/engines/supernova/resman.h
@@ -0,0 +1,61 @@
+/* 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 SUPERNOVA_RESOURCES_H
+#define SUPERNOVA_RESOURCES_H
+
+#include "common/ptr.h"
+
+#include "supernova/sound.h"
+
+namespace Audio {
+class SeekableAudioStream;
+}
+
+namespace Supernova {
+
+class ResourceManager {
+public:
+ ResourceManager(SupernovaEngine *vm);
+ ~ResourceManager();
+
+ Audio::SeekableAudioStream *getSoundStream(AudioIndex index);
+ Audio::AudioStream *getSoundStream(MusicIndex index);
+
+private:
+ void initSoundFiles();
+ void initGraphics();
+ void initCursorGraphics();
+
+private:
+ SupernovaEngine *_vm;
+ Audio::SeekableAudioStream *_soundSamples[kAudioNumSamples];
+ Audio::AudioStream *_musicIntro;
+ Audio::AudioStream *_musicOutro;
+ int _audioRate;
+ byte _cursorNormal[256];
+ byte _cursorWait[256];
+};
+
+}
+
+#endif
diff --git a/engines/supernova/sound.cpp b/engines/supernova/sound.cpp
index a306a4819a..03e33e8bc4 100644
--- a/engines/supernova/sound.cpp
+++ b/engines/supernova/sound.cpp
@@ -26,23 +26,19 @@
#include "audio/mods/protracker.h"
#include "common/system.h"
+#include "supernova/resman.h"
#include "supernova/sound.h"
#include "supernova/supernova.h"
namespace Supernova {
-Sound::Sound(SupernovaEngine *vm, Audio::Mixer *mixer)
+Sound::Sound(Audio::Mixer *mixer, ResourceManager *resMan)
: _mixer(mixer)
- , _vm(vm)
- , _rate(11931) {
+ , _resMan(resMan) {
}
void Sound::play(AudioIndex index) {
- Audio::AudioStream *stream;
- byte flags = Audio::FLAG_LITTLE_ENDIAN | Audio::FLAG_UNSIGNED;
- stream = Audio::makeRawStream(_vm->_soundSamples[index]._buffer,
- _vm->_soundSamples[index]._length,
- _rate, flags);
+ Audio::AudioStream *stream = _resMan->getSoundStream(index);
stop();
_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, stream,
@@ -50,17 +46,7 @@ void Sound::play(AudioIndex index) {
}
void Sound::play(MusicIndex index) {
- Audio::AudioStream *stream;
- switch (index) {
- case kMusicIntro:
- stream = Audio::makeProtrackerStream(_vm->_soundMusicIntro);
- break;
- case kMusicOutro:
- stream = Audio::makeProtrackerStream(_vm->_soundMusicOutro);
- break;
- default:
- error("Reuqested music file does not exist");
- }
+ Audio::AudioStream *stream = _resMan->getSoundStream(index);
stop();
_mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, stream,
diff --git a/engines/supernova/sound.h b/engines/supernova/sound.h
index a486fe5b59..f33a291835 100644
--- a/engines/supernova/sound.h
+++ b/engines/supernova/sound.h
@@ -63,7 +63,7 @@ class Sound {
public:
public:
- Sound(SupernovaEngine *vm, Audio::Mixer *mixer);
+ Sound(Audio::Mixer *mixer, ResourceManager *resMan);
void play(AudioIndex index);
void play(MusicIndex index);
@@ -71,9 +71,8 @@ public:
bool isPlaying();
private:
Audio::Mixer *_mixer;
- SupernovaEngine *_vm;
+ ResourceManager *_resMan;
Audio::SoundHandle _soundHandle;
- int _rate;
};
}
diff --git a/engines/supernova/supernova.cpp b/engines/supernova/supernova.cpp
index 68650babba..9410ce1f4d 100644
--- a/engines/supernova/supernova.cpp
+++ b/engines/supernova/supernova.cpp
@@ -20,7 +20,6 @@
*
*/
-#include "audio/mods/protracker.h"
#include "common/config-manager.h"
#include "common/debug.h"
#include "common/debug-channels.h"
@@ -42,34 +41,13 @@
#include "graphics/thumbnail.h"
#include "gui/saveload.h"
+#include "supernova/resman.h"
#include "supernova/sound.h"
#include "supernova/supernova.h"
#include "supernova/state.h"
namespace Supernova {
-const AudioInfo audioInfo[kAudioNumSamples] = {
- {44, 0, -1},
- {45, 0, -1},
- {46, 0, 2510},
- {46, 2510, 4020},
- {46, 4020, -1},
- {47, 0, 24010},
- {47, 24010, -1},
- {48, 0, 2510},
- {48, 2510, 10520},
- {48, 10520, 13530},
- {48, 13530, -1},
- {50, 0, 12786},
- {50, 12786, -1},
- {51, 0, -1},
- {53, 0, -1},
- {54, 0, 8010},
- {54, 8010, 24020},
- {54, 24020, 30030},
- {54, 30030, 31040},
- {54, 31040, -1}
-};
const Object Object::nullObject;
@@ -102,8 +80,6 @@ SupernovaEngine::SupernovaEngine(OSystem *syst)
, _console(NULL)
, _gm(NULL)
, _currentImage(NULL)
- , _soundMusicIntro(NULL)
- , _soundMusicOutro(NULL)
, _rnd("supernova")
, _brightness(255)
, _menuBrightness(255)
@@ -132,8 +108,7 @@ SupernovaEngine::~SupernovaEngine() {
delete _console;
delete _gm;
delete _sound;
- delete _soundMusicIntro;
- delete _soundMusicOutro;
+ delete _resMan;
}
Common::Error SupernovaEngine::run() {
@@ -166,17 +141,11 @@ void SupernovaEngine::init() {
if (status.getCode() != Common::kNoError)
error("Failed reading game strings");
- initData();
- initPalette();
-
- _sound = new Sound(this, _mixer);
+ _resMan = new ResourceManager(this);
+ _sound = new Sound(_mixer, _resMan);
_gm = new GameManager(this, _sound);
_console = new Console(this, _gm);
- CursorMan.replaceCursor(_mouseNormal, 16, 16, 0, 0, kColorCursorTransparent);
- CursorMan.replaceCursorPalette(initVGAPalette, 0, 16);
- CursorMan.showMouse(true);
-
setTotalPlayTime(0);
int saveSlot = ConfMan.getInt("save_slot");
@@ -263,57 +232,6 @@ Common::Error SupernovaEngine::loadGameStrings() {
return Common::kReadingFailed;
}
-void SupernovaEngine::initData() {
- // Sound
- // Note:
- // - samples start with a header of 6 bytes: 01 SS SS 00 AD 00
- // where SS SS (LE uint16) is the size of the sound sample + 2
- // - samples end with a footer of 4 bytes: 00 00
- // Skip those in the buffer
- Common::File file;
-
- for (int i = 0; i < kAudioNumSamples; ++i) {
- if (!file.open(Common::String::format("msn_data.%03d", audioInfo[i]._filenumber))) {
- error("File %s could not be read!", file.getName());
- }
-
- if (audioInfo[i]._offsetEnd == -1) {
- file.seek(0, SEEK_END);
- _soundSamples[i]._length = file.pos() - audioInfo[i]._offsetStart - 10;
- } else {
- _soundSamples[i]._length = audioInfo[i]._offsetEnd - audioInfo[i]._offsetStart - 10;
- }
- _soundSamples[i]._buffer = new byte[_soundSamples[i]._length];
- file.seek(audioInfo[i]._offsetStart + 6);
- file.read(_soundSamples[i]._buffer, _soundSamples[i]._length);
- file.close();
- }
-
- _soundMusicIntro = convertToMod("msn_data.052");
- _soundMusicOutro = convertToMod("msn_data.049");
-
- // Cursor
- const uint16 *bufferNormal = reinterpret_cast<const uint16 *>(mouseNormal);
- const uint16 *bufferWait = reinterpret_cast<const uint16 *>(mouseWait);
- for (uint i = 0; i < sizeof(mouseNormal) / 4; ++i) {
- for (uint bit = 0; bit < 16; ++bit) {
- uint mask = 0x8000 >> bit;
- uint bitIndex = i * 16 + bit;
-
- _mouseNormal[bitIndex] = (READ_LE_UINT16(bufferNormal + i) & mask) ? kColorCursorTransparent : kColorBlack;
- if (READ_LE_UINT16(bufferNormal + i + 16) & mask)
- _mouseNormal[bitIndex] = kColorLightRed;
- _mouseWait[bitIndex] = (READ_LE_UINT16(bufferWait + i) & mask) ? kColorCursorTransparent : kColorBlack;
- if (READ_LE_UINT16(bufferWait + i + 16) & mask)
- _mouseWait[bitIndex] = kColorLightRed;
- }
- }
-}
-
-void SupernovaEngine::initPalette() {
- _system->getPaletteManager()->setPalette(initVGAPalette, 0, 256);
-}
-
void SupernovaEngine::playSound(AudioIndex sample) {
_sound->play(sample);
}
@@ -781,191 +699,6 @@ bool SupernovaEngine::quitGameDialog() {
return quit;
}
-Common::MemoryReadStream *SupernovaEngine::convertToMod(const char *filename, int version) {
- // MSN format
- struct {
- uint16 seg;
- uint16 start;
- uint16 end;
- uint16 loopStart;
- uint16 loopEnd;
- char volume;
- char dummy[5];
- } instr2[22];
- int nbInstr2; // 22 for version1, 15 for version 2
- int16 songLength;
- char arrangement[128];
- int16 patternNumber;
- int32 note2[28][64][4];
-
- nbInstr2 = ((version == 1) ? 22 : 15);
-
- Common::File msnFile;
- msnFile.open(filename);
- if (!msnFile.isOpen()) {
- warning("Data file '%s' not found", msnFile.getName());
- return NULL;
- }
-
- for (int i = 0 ; i < nbInstr2 ; ++i) {
- instr2[i].seg = msnFile.readUint16LE();
- instr2[i].start = msnFile.readUint16LE();
- instr2[i].end = msnFile.readUint16LE();
- instr2[i].loopStart = msnFile.readUint16LE();
- instr2[i].loopEnd = msnFile.readUint16LE();
- instr2[i].volume = msnFile.readByte();
- msnFile.read(instr2[i].dummy, 5);
- }
- songLength = msnFile.readSint16LE();
- msnFile.read(arrangement, 128);
- patternNumber = msnFile.readSint16LE();
- for (int p = 0 ; p < patternNumber ; ++p) {
- for (int n = 0 ; n < 64 ; ++n) {
- for (int k = 0 ; k < 4 ; ++k) {
- note2[p][n][k] = msnFile.readSint32LE();
- }
- }
- }
-
- /* MOD format */
- struct {
- char iname[22];
- uint16 length;
- char finetune;
- char volume;
- uint16 loopStart;
- uint16 loopLength;
- } instr[31];
- int32 note[28][64][4];
-
- // We can't recover some MOD effects since several of them are mapped to 0.
- // Assume the MSN effect of value 0 is Arpeggio (MOD effect of value 0).
- const char invConvEff[8] = {0, 1, 2, 3, 10, 12, 13 ,15};
-
- // Reminder from convertToMsn
- // 31 30 29 28 27 26 25 24 - 23 22 21 20 19 18 17 16 - 15 14 13 12 11 10 09 08 - 07 06 05 04 03 02 01 00
- // h h h h g g g g f f f f e e e e d d d d c c c c b b b b a a a a
- //
- // MSN:
- // hhhh (4 bits) Cleared to 0
- // dddd c (5 bits) Sample index | after mapping through convInstr
- // ccc (3 bits) Effect type | after mapping through convEff
- // bbbb aaaa (8 bits) Effect value | unmodified
- // gggg ffff eeee (12 bits) Sample period | unmodified
- //
- // MS2:
- // hhhh (4 bits) Cleared to 0
- // dddd (4 bits) Sample index | after mapping through convInstr
- // cccc (4 bits) Effect type | unmodified
- // bbbb aaaa (8 bits) Effect value | unmodified
- // gggg ffff eeee (12 bits) Sample period | transformed (0xE000 / p) - 256
- //
- // MOD:
- // hhhh dddd (8 bits) Sample index
- // cccc (4 bits) Effect type for this channel/division
- // bbbb aaaa (8 bits) Effect value
- // gggg ffff eeee (12 bits) Sample period
-
- // Can we recover the instruments mapping? I don't think so as part of the original instrument index is cleared.
- // And it doesn't really matter as long as we are consistent.
- // However we need to make sure 31 (or 15 in MS2) is mapped to 0 in MOD.
- // We just add 1 to all other values, and this means a 1 <-> 1 mapping for the instruments
- for (int p = 0; p < patternNumber; ++p) {
- for (int n = 0; n < 64; ++n) {
- for (int k = 0; k < 4; ++k) {
- int32* l = &(note[p][n][k]);
- *l = note2[p][n][k];
- int32 i = 0;
- if (nbInstr2 == 22) { // version 1
- i = ((*l & 0xF800) >> 11);
- int32 e = ((*l & 0x0700) >> 8);
- int32 e1 = invConvEff[e];
- *l &= 0x0FFF00FF;
- *l |= (e1 << 8);
- } else { // version 2
- int32 h = (*l >> 16);
- i = ((*l & 0xF000) >> 12);
- *l &= 0x00000FFF;
- if (h)
- h = 0xE000 / (h + 256);
- *l |= (h << 16);
- if (i == 15)
- i = 31;
- }
-
- // Add back index in note
- if (i != 31) {
- ++i;
- *l |= ((i & 0x0F) << 12);
- *l |= ((i & 0xF0) << 24);
- }
- }
- }
- }
-
- for (int i = 0; i < 31; ++i) {
- // iname is not stored in the mod file. Just set it to 'instrument#'
- // finetune is not stored either. Assume 0.
- memset(instr[i].iname, 0, 22);
- sprintf(instr[i].iname, "instrument%d", i+1);
- instr[i].length = 0;
- instr[i].finetune = 0;
- instr[i].volume = 0;
- instr[i].loopStart = 0;
- instr[i].loopLength = 0;
-
- if (i < nbInstr2) {
- instr[i].length = ((instr2[i].end - instr2[i].start) >> 1);
- instr[i].loopStart = ((instr2[i].loopStart - instr2[i].start) >> 1);
- instr[i].loopLength = (( instr2[i].loopEnd - instr2[i].loopStart) >> 1);
- instr[i].volume = instr2[i].volume;
- }
- }
-
- // The ciaaSpeed is kind of useless and not present in the MSN file.
- // Traditionally 0x78 in SoundTracker. Was used in NoiseTracker as a restart point.
- // ProTracker uses 0x7F. FastTracker uses it as a restart point, whereas ScreamTracker 3 uses 0x7F like ProTracker.
- // You can use this to roughly detect which tracker made a MOD, and detection gets more accurate for more obscure MOD types.
- char ciaaSpeed = 0x7F;
-
- // The mark cannot be recovered either. Since we have 4 channels and 31 instrument it can be either ID='M.K.' or ID='4CHN'.
- // Assume 'M.K.'
- const char mark[4] = { 'M', '.', 'K', '.' };
-
- Common::MemoryWriteStreamDynamic buffer(DisposeAfterUse::NO);
-
- buffer.write(msnFile.getName(), 19);
- buffer.writeByte(0);
-
- for (int i = 0 ; i < 31 ; ++i) {
- buffer.write(instr[i].iname, 22);
- buffer.writeUint16BE(instr[i].length);
- buffer.writeByte(instr[i].finetune);
- buffer.writeByte(instr[i].volume);
- buffer.writeUint16BE(instr[i].loopStart);
- buffer.writeUint16BE(instr[i].loopLength);
- }
- buffer.writeByte((char)songLength);
- buffer.writeByte(ciaaSpeed);
- buffer.write(arrangement, 128);
- buffer.write(mark, 4);
-
- for (int p = 0 ; p < patternNumber ; ++p) {
- for (int n = 0 ; n < 64 ; ++n) {
- for (int k = 0 ; k < 4 ; ++k) {
-// buffer.writeUint32BE(*((uint32*)(note[p][n]+k)));
- buffer.writeSint32BE(note[p][n][k]);
- }
- }
- }
-
- uint nb;
- char buf[4096];
- while ((nb = msnFile.read(buf, 4096)) > 0)
- buffer.write(buf, nb);
-
- return new Common::MemoryReadStream(buffer.getData(), buffer.size(), DisposeAfterUse::YES);
-}
bool SupernovaEngine::canLoadGameStateCurrently() {
return _allowLoadGame;
diff --git a/engines/supernova/supernova.h b/engines/supernova/supernova.h
index a4e7620e6f..589d3f5a06 100644
--- a/engines/supernova/supernova.h
+++ b/engines/supernova/supernova.h
@@ -23,9 +23,6 @@
#ifndef SUPERNOVA_SUPERNOVA_H
#define SUPERNOVA_SUPERNOVA_H
-#include "audio/audiostream.h"
-#include "audio/mixer.h"
-#include "audio/decoders/raw.h"
#include "common/array.h"
#include "common/events.h"
#include "common/random.h"
@@ -77,21 +74,13 @@ private:
ScreenBuffer *_last;
};
-struct SoundSample {
- SoundSample()
- : _buffer(NULL)
- , _length(0)
- {}
-
- ~SoundSample() {
- delete[] _buffer;
- }
-
- byte *_buffer;
- int _length;
-};
class GuiElement;
+class ResourceManager;
+class Sound;
+class console;
+class GameManager;
+
class SupernovaEngine : public Engine {
public:
explicit SupernovaEngine(OSystem *syst);
@@ -103,13 +92,9 @@ public:
GameManager *_gm;
Console *_console;
Sound *_sound;
+ ResourceManager *_resMan;
ScreenBufferStack _screenBuffer;
- byte _mouseNormal[256];
- byte _mouseWait[256];
MSNImageDecoder *_currentImage;
- SoundSample _soundSamples[kAudioNumSamples];
- Common::MemoryReadStream *_soundMusicIntro;
- Common::MemoryReadStream *_soundMusicOutro;
int _screenWidth;
int _screenHeight;
bool _allowLoadGame;
@@ -130,8 +115,6 @@ public:
int textWidth(const uint16 key);
Common::Error loadGameStrings();
void init();
- void initData();
- void initPalette();
void paletteFadeIn();
void paletteFadeOut();
void paletteBrightness();
@@ -208,8 +191,6 @@ public:
renderText(text.c_str());
}
- Common::MemoryReadStream *convertToMod(const char *filename, int version = 1);
-
virtual Common::Error loadGameState(int slot);
virtual bool canLoadGameStateCurrently();
virtual Common::Error saveGameState(int slot, const Common::String &desc);