diff options
111 files changed, 2230 insertions, 2528 deletions
@@ -142,6 +142,14 @@ ScummVM Team Eugene Sandulenko David Turner + Mortevielle: + Arnaud Boutonne + Paul Gilbert + + Neverhood: + Benjamin Haisch + Filippos Karapetis + Parallaction: peres diff --git a/audio/softsynth/mt32/AReverbModel.cpp b/audio/softsynth/mt32/AReverbModel.cpp deleted file mode 100644 index 1d63832157..0000000000 --- a/audio/softsynth/mt32/AReverbModel.cpp +++ /dev/null @@ -1,277 +0,0 @@ -/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "mt32emu.h" - -#if MT32EMU_USE_REVERBMODEL == 1 - -#include "AReverbModel.h" - -// Analysing of state of reverb RAM address lines gives exact sizes of the buffers of filters used. This also indicates that -// the reverb model implemented in the real devices consists of three series allpass filters preceded by a non-feedback comb (or a delay with a LPF) -// and followed by three parallel comb filters - -namespace MT32Emu { - -// Because LA-32 chip makes it's output available to process by the Boss chip with a significant delay, -// the Boss chip puts to the buffer the LA32 dry output when it is ready and performs processing of the _previously_ latched data. -// Of course, the right way would be to use a dedicated variable for this, but our reverb model is way higher level, -// so we can simply increase the input buffer size. -static const Bit32u PROCESS_DELAY = 1; - -// Default reverb settings for modes 0-2. These correspond to CM-32L / LAPC-I "new" reverb settings. MT-32 reverb is a bit different. -// Found by tracing reverb RAM data lines (thanks go to Lord_Nightmare & balrog). - -static const Bit32u NUM_ALLPASSES = 3; -static const Bit32u NUM_COMBS = 4; // Well, actually there are 3 comb filters, but the entrance LPF + delay can be perfectly processed via a comb here. - -static const Bit32u MODE_0_ALLPASSES[] = {994, 729, 78}; -static const Bit32u MODE_0_COMBS[] = {705 + PROCESS_DELAY, 2349, 2839, 3632}; -static const Bit32u MODE_0_OUTL[] = {2349, 141, 1960}; -static const Bit32u MODE_0_OUTR[] = {1174, 1570, 145}; -static const Bit32u MODE_0_COMB_FACTOR[] = {0x3C, 0x60, 0x60, 0x60}; -static const Bit32u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, - 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, - 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; -static const Bit32u MODE_0_LEVELS[] = {10*1, 10*3, 10*5, 10*7, 11*9, 11*12, 11*15, 13*15}; -static const Bit32u MODE_0_LPF_AMP = 6; - -static const Bit32u MODE_1_ALLPASSES[] = {1324, 809, 176}; -static const Bit32u MODE_1_COMBS[] = {961 + PROCESS_DELAY, 2619, 3545, 4519}; -static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518}; -static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274}; -static const Bit32u MODE_1_COMB_FACTOR[] = {0x30, 0x60, 0x60, 0x60}; -static const Bit32u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, - 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, - 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; -static const Bit32u MODE_1_LEVELS[] = {10*1, 10*3, 11*5, 11*7, 11*9, 11*12, 11*15, 14*15}; -static const Bit32u MODE_1_LPF_AMP = 6; - -static const Bit32u MODE_2_ALLPASSES[] = {969, 644, 157}; -static const Bit32u MODE_2_COMBS[] = {116 + PROCESS_DELAY, 2259, 2839, 3539}; -static const Bit32u MODE_2_OUTL[] = {2259, 718, 1769}; -static const Bit32u MODE_2_OUTR[] = {1136, 2128, 1}; -static const Bit32u MODE_2_COMB_FACTOR[] = {0, 0x20, 0x20, 0x20}; -static const Bit32u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0, - 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0, - 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0}; -static const Bit32u MODE_2_LEVELS[] = {10*1, 10*3, 11*5, 11*7, 11*9, 11*12, 12*15, 14*15}; -static const Bit32u MODE_2_LPF_AMP = 8; - -static const AReverbSettings REVERB_MODE_0_SETTINGS = {MODE_0_ALLPASSES, MODE_0_COMBS, MODE_0_OUTL, MODE_0_OUTR, MODE_0_COMB_FACTOR, MODE_0_COMB_FEEDBACK, MODE_0_LEVELS, MODE_0_LPF_AMP}; -static const AReverbSettings REVERB_MODE_1_SETTINGS = {MODE_1_ALLPASSES, MODE_1_COMBS, MODE_1_OUTL, MODE_1_OUTR, MODE_1_COMB_FACTOR, MODE_1_COMB_FEEDBACK, MODE_1_LEVELS, MODE_1_LPF_AMP}; -static const AReverbSettings REVERB_MODE_2_SETTINGS = {MODE_2_ALLPASSES, MODE_2_COMBS, MODE_2_OUTL, MODE_2_OUTR, MODE_2_COMB_FACTOR, MODE_2_COMB_FEEDBACK, MODE_2_LEVELS, MODE_2_LPF_AMP}; - -static const AReverbSettings * const REVERB_SETTINGS[] = {&REVERB_MODE_0_SETTINGS, &REVERB_MODE_1_SETTINGS, &REVERB_MODE_2_SETTINGS, &REVERB_MODE_0_SETTINGS}; - -RingBuffer::RingBuffer(const Bit32u newsize) : size(newsize), index(0) { - buffer = new float[size]; -} - -RingBuffer::~RingBuffer() { - delete[] buffer; - buffer = NULL; -} - -float RingBuffer::next() { - if (++index >= size) { - index = 0; - } - return buffer[index]; -} - -bool RingBuffer::isEmpty() const { - if (buffer == NULL) return true; - - float *buf = buffer; - float max = 0.001f; - for (Bit32u i = 0; i < size; i++) { - if ((*buf < -max) || (*buf > max)) return false; - buf++; - } - return true; -} - -void RingBuffer::mute() { - float *buf = buffer; - for (Bit32u i = 0; i < size; i++) { - *buf++ = 0; - } -} - -AllpassFilter::AllpassFilter(const Bit32u useSize) : RingBuffer(useSize) {} - -float AllpassFilter::process(const float in) { - // This model corresponds to the allpass filter implementation of the real CM-32L device - // found from sample analysis - - const float bufferOut = next(); - - // store input - feedback / 2 - buffer[index] = in - 0.5f * bufferOut; - - // return buffer output + feedforward / 2 - return bufferOut + 0.5f * buffer[index]; -} - -CombFilter::CombFilter(const Bit32u useSize) : RingBuffer(useSize) {} - -void CombFilter::process(const float in) { - // This model corresponds to the comb filter implementation of the real CM-32L device - // found from sample analysis - - // the previously stored value - float last = buffer[index]; - - // prepare input + feedback - float filterIn = in + next() * feedbackFactor; - - // store input + feedback processed by a low-pass filter - buffer[index] = filterFactor * last - filterIn; -} - -float CombFilter::getOutputAt(const Bit32u outIndex) const { - return buffer[(size + index - outIndex) % size]; -} - -void CombFilter::setFeedbackFactor(const float useFeedbackFactor) { - feedbackFactor = useFeedbackFactor; -} - -void CombFilter::setFilterFactor(const float useFilterFactor) { - filterFactor = useFilterFactor; -} - -AReverbModel::AReverbModel(const ReverbMode mode) : allpasses(NULL), combs(NULL), currentSettings(*REVERB_SETTINGS[mode]) {} - -AReverbModel::~AReverbModel() { - close(); -} - -void AReverbModel::open() { - allpasses = new AllpassFilter*[NUM_ALLPASSES]; - for (Bit32u i = 0; i < NUM_ALLPASSES; i++) { - allpasses[i] = new AllpassFilter(currentSettings.allpassSizes[i]); - } - combs = new CombFilter*[NUM_COMBS]; - for (Bit32u i = 0; i < NUM_COMBS; i++) { - combs[i] = new CombFilter(currentSettings.combSizes[i]); - combs[i]->setFilterFactor(currentSettings.filterFactor[i] / 256.0f); - } - lpfAmp = currentSettings.lpfAmp / 16.0f; - mute(); -} - -void AReverbModel::close() { - if (allpasses != NULL) { - for (Bit32u i = 0; i < NUM_ALLPASSES; i++) { - if (allpasses[i] != NULL) { - delete allpasses[i]; - allpasses[i] = NULL; - } - } - delete[] allpasses; - allpasses = NULL; - } - if (combs != NULL) { - for (Bit32u i = 0; i < NUM_COMBS; i++) { - if (combs[i] != NULL) { - delete combs[i]; - combs[i] = NULL; - } - } - delete[] combs; - combs = NULL; - } -} - -void AReverbModel::mute() { - if (allpasses == NULL || combs == NULL) return; - for (Bit32u i = 0; i < NUM_ALLPASSES; i++) { - allpasses[i]->mute(); - } - for (Bit32u i = 0; i < NUM_COMBS; i++) { - combs[i]->mute(); - } -} - -void AReverbModel::setParameters(Bit8u time, Bit8u level) { -// FIXME: wetLevel definitely needs ramping when changed -// Although, most games don't set reverb level during MIDI playback - if (combs == NULL) return; - level &= 7; - time &= 7; - for (Bit32u i = 0; i < NUM_COMBS; i++) { - combs[i]->setFeedbackFactor(currentSettings.decayTimes[(i << 3) + time] / 256.0f); - } - wetLevel = (level == 0 && time == 0) ? 0.0f : 0.5f * lpfAmp * currentSettings.wetLevels[level] / 256.0f; -} - -bool AReverbModel::isActive() const { - for (Bit32u i = 0; i < NUM_ALLPASSES; i++) { - if (!allpasses[i]->isEmpty()) return true; - } - for (Bit32u i = 0; i < NUM_COMBS; i++) { - if (!combs[i]->isEmpty()) return true; - } - return false; -} - -void AReverbModel::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) { - float dry, link, outL1; - - for (unsigned long i = 0; i < numSamples; i++) { - dry = wetLevel * (*inLeft + *inRight); - - // Get the last stored sample before processing in order not to loose it - link = combs[0]->getOutputAt(currentSettings.combSizes[0] - 1); - - combs[0]->process(-dry); - - link = allpasses[0]->process(link); - link = allpasses[1]->process(link); - link = allpasses[2]->process(link); - - // If the output position is equal to the comb size, get it now in order not to loose it - outL1 = 1.5f * combs[1]->getOutputAt(currentSettings.outLPositions[0] - 1); - - combs[1]->process(link); - combs[2]->process(link); - combs[3]->process(link); - - link = outL1 + 1.5f * combs[2]->getOutputAt(currentSettings.outLPositions[1]); - link += combs[3]->getOutputAt(currentSettings.outLPositions[2]); - *outLeft = link; - - link = 1.5f * combs[1]->getOutputAt(currentSettings.outRPositions[0]); - link += 1.5f * combs[2]->getOutputAt(currentSettings.outRPositions[1]); - link += combs[3]->getOutputAt(currentSettings.outRPositions[2]); - *outRight = link; - - inLeft++; - inRight++; - outLeft++; - outRight++; - } -} - -} - -#endif diff --git a/audio/softsynth/mt32/AReverbModel.h b/audio/softsynth/mt32/AReverbModel.h deleted file mode 100644 index c992478907..0000000000 --- a/audio/softsynth/mt32/AReverbModel.h +++ /dev/null @@ -1,87 +0,0 @@ -/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef MT32EMU_A_REVERB_MODEL_H -#define MT32EMU_A_REVERB_MODEL_H - -namespace MT32Emu { - -struct AReverbSettings { - const Bit32u * const allpassSizes; - const Bit32u * const combSizes; - const Bit32u * const outLPositions; - const Bit32u * const outRPositions; - const Bit32u * const filterFactor; - const Bit32u * const decayTimes; - const Bit32u * const wetLevels; - const Bit32u lpfAmp; -}; - -class RingBuffer { -protected: - float *buffer; - const Bit32u size; - Bit32u index; - -public: - RingBuffer(const Bit32u size); - virtual ~RingBuffer(); - float next(); - bool isEmpty() const; - void mute(); -}; - -class AllpassFilter : public RingBuffer { -public: - AllpassFilter(const Bit32u size); - float process(const float in); -}; - -class CombFilter : public RingBuffer { - float feedbackFactor; - float filterFactor; - -public: - CombFilter(const Bit32u size); - void process(const float in); - float getOutputAt(const Bit32u outIndex) const; - void setFeedbackFactor(const float useFeedbackFactor); - void setFilterFactor(const float useFilterFactor); -}; - -class AReverbModel : public ReverbModel { - AllpassFilter **allpasses; - CombFilter **combs; - - const AReverbSettings ¤tSettings; - float lpfAmp; - float wetLevel; - void mute(); - -public: - AReverbModel(const ReverbMode mode); - ~AReverbModel(); - void open(); - void close(); - void setParameters(Bit8u time, Bit8u level); - void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples); - bool isActive() const; -}; - -} - -#endif diff --git a/audio/softsynth/mt32/BReverbModel.cpp b/audio/softsynth/mt32/BReverbModel.cpp index cc0219b741..c16f7f17da 100644 --- a/audio/softsynth/mt32/BReverbModel.cpp +++ b/audio/softsynth/mt32/BReverbModel.cpp @@ -15,10 +15,8 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +//#include <memory.h> #include "mt32emu.h" - -#if MT32EMU_USE_REVERBMODEL == 2 - #include "BReverbModel.h" // Analysing of state of reverb RAM address lines gives exact sizes of the buffers of filters used. This also indicates that @@ -62,9 +60,9 @@ static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518}; static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274}; static const Bit32u MODE_1_COMB_FACTOR[] = {0x80, 0x60, 0x60, 0x60}; static const Bit32u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, - 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, - 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; + 0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; static const Bit32u MODE_1_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xE0}; static const Bit32u MODE_1_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; static const Bit32u MODE_1_LPF_AMP = 0x60; @@ -103,7 +101,11 @@ static const BReverbSettings * const REVERB_SETTINGS[] = {&REVERB_MODE_0_SETTING // This algorithm tries to emulate exactly Boss multiplication operation (at least this is what we see on reverb RAM data lines). // Also LA32 is suspected to use the similar one to perform PCM interpolation and ring modulation. -static Bit32s weirdMul(Bit32s a, Bit8u addMask, Bit8u carryMask) { +static Sample weirdMul(Sample a, Bit8u addMask, Bit8u carryMask) { + (void)carryMask; +#if MT32EMU_USE_FLOAT_SAMPLES + return a * addMask / 256.0f; +#elif MT32EMU_BOSS_REVERB_PRECISE_MODE Bit8u mask = 0x80; Bit32s res = 0; for (int i = 0; i < 8; i++) { @@ -113,10 +115,13 @@ static Bit32s weirdMul(Bit32s a, Bit8u addMask, Bit8u carryMask) { mask >>= 1; } return res; +#else + return Sample(((Bit32s)a * addMask) >> 8); +#endif } RingBuffer::RingBuffer(Bit32u newsize) : size(newsize), index(0) { - buffer = new Bit16s[size]; + buffer = new Sample[size]; } RingBuffer::~RingBuffer() { @@ -124,7 +129,7 @@ RingBuffer::~RingBuffer() { buffer = NULL; } -Bit32s RingBuffer::next() { +Sample RingBuffer::next() { if (++index >= size) { index = 0; } @@ -134,52 +139,69 @@ Bit32s RingBuffer::next() { bool RingBuffer::isEmpty() const { if (buffer == NULL) return true; - Bit16s *buf = buffer; +#if MT32EMU_USE_FLOAT_SAMPLES + Sample max = 0.001f; +#else + Sample max = 8; +#endif + Sample *buf = buffer; for (Bit32u i = 0; i < size; i++) { - if (*buf < -8 || *buf > 8) return false; + if (*buf < -max || *buf > max) return false; buf++; } return true; } void RingBuffer::mute() { - Bit16s *buf = buffer; +#if MT32EMU_USE_FLOAT_SAMPLES + Sample *buf = buffer; for (Bit32u i = 0; i < size; i++) { *buf++ = 0; } +#else + memset(buffer, 0, size * sizeof(Sample)); +#endif } AllpassFilter::AllpassFilter(const Bit32u useSize) : RingBuffer(useSize) {} -Bit32s AllpassFilter::process(const Bit32s in) { +Sample AllpassFilter::process(const Sample in) { // This model corresponds to the allpass filter implementation of the real CM-32L device // found from sample analysis - Bit16s bufferOut = next(); + const Sample bufferOut = next(); +#if MT32EMU_USE_FLOAT_SAMPLES + // store input - feedback / 2 + buffer[index] = in - 0.5f * bufferOut; + + // return buffer output + feedforward / 2 + return bufferOut + 0.5f * buffer[index]; +#else // store input - feedback / 2 buffer[index] = in - (bufferOut >> 1); // return buffer output + feedforward / 2 return bufferOut + (buffer[index] >> 1); +#endif } CombFilter::CombFilter(const Bit32u useSize, const Bit32u useFilterFactor) : RingBuffer(useSize), filterFactor(useFilterFactor) {} -void CombFilter::process(const Bit32s in) { +void CombFilter::process(const Sample in) { // This model corresponds to the comb filter implementation of the real CM-32L device // the previously stored value - Bit32s last = buffer[index]; + const Sample last = buffer[index]; // prepare input + feedback - Bit32s filterIn = in + weirdMul(next(), feedbackFactor, 0xF0 /* Maybe 0x80 ? */); + const Sample filterIn = in + weirdMul(next(), feedbackFactor, 0xF0 /* Maybe 0x80 ? */); // store input + feedback processed by a low-pass filter buffer[index] = weirdMul(last, filterFactor, 0x40) - filterIn; } -Bit32s CombFilter::getOutputAt(const Bit32u outIndex) const { +Sample CombFilter::getOutputAt(const Bit32u outIndex) const { return buffer[(size + index - outIndex) % size]; } @@ -190,15 +212,15 @@ void CombFilter::setFeedbackFactor(const Bit32u useFeedbackFactor) { DelayWithLowPassFilter::DelayWithLowPassFilter(const Bit32u useSize, const Bit32u useFilterFactor, const Bit32u useAmp) : CombFilter(useSize, useFilterFactor), amp(useAmp) {} -void DelayWithLowPassFilter::process(const Bit32s in) { +void DelayWithLowPassFilter::process(const Sample in) { // the previously stored value - Bit32s last = buffer[index]; + const Sample last = buffer[index]; // move to the next index next(); // low-pass filter process - Bit32s lpfOut = weirdMul(last, filterFactor, 0xFF) + in; + Sample lpfOut = weirdMul(last, filterFactor, 0xFF) + in; // store lpfOut multiplied by LPF amp factor buffer[index] = weirdMul(lpfOut, amp, 0xFF); @@ -206,26 +228,26 @@ void DelayWithLowPassFilter::process(const Bit32s in) { TapDelayCombFilter::TapDelayCombFilter(const Bit32u useSize, const Bit32u useFilterFactor) : CombFilter(useSize, useFilterFactor) {} -void TapDelayCombFilter::process(const Bit32s in) { +void TapDelayCombFilter::process(const Sample in) { // the previously stored value - Bit32s last = buffer[index]; + const Sample last = buffer[index]; // move to the next index next(); // prepare input + feedback // Actually, the size of the filter varies with the TIME parameter, the feedback sample is taken from the position just below the right output - Bit32s filterIn = in + weirdMul(getOutputAt(outR + MODE_3_FEEDBACK_DELAY), feedbackFactor, 0xF0); + const Sample filterIn = in + weirdMul(getOutputAt(outR + MODE_3_FEEDBACK_DELAY), feedbackFactor, 0xF0); // store input + feedback processed by a low-pass filter buffer[index] = weirdMul(last, filterFactor, 0xF0) - filterIn; } -Bit32s TapDelayCombFilter::getLeftOutput() const { +Sample TapDelayCombFilter::getLeftOutput() const { return getOutputAt(outL + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY); } -Bit32s TapDelayCombFilter::getRightOutput() const { +Sample TapDelayCombFilter::getRightOutput() const { return getOutputAt(outR + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY); } @@ -327,14 +349,14 @@ bool BReverbModel::isActive() const { return false; } -void BReverbModel::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) { - Bit32s dry, link, outL1, outR1; +void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample *outLeft, Sample *outRight, unsigned long numSamples) { + Sample dry; - for (unsigned long i = 0; i < numSamples; i++) { + while (numSamples > 0) { if (tapDelayMode) { - dry = Bit32s(*inLeft * 8192.0f) + Bit32s(*inRight * 8192.0f); + dry = *inLeft + *inRight; } else { - dry = Bit32s(*inLeft * 8192.0f) / 2 + Bit32s(*inRight * 8192.0f) / 2; + dry = *inLeft / 2 + *inRight / 2; } // Looks like dryAmp doesn't change in MT-32 but it does in CM-32L / LAPC-I @@ -343,44 +365,53 @@ void BReverbModel::process(const float *inLeft, const float *inRight, float *out if (tapDelayMode) { TapDelayCombFilter *comb = static_cast<TapDelayCombFilter *> (*combs); comb->process(dry); - *outLeft = weirdMul(comb->getLeftOutput(), wetLevel, 0xFF) / 8192.0f; - *outRight = weirdMul(comb->getRightOutput(), wetLevel, 0xFF) / 8192.0f; + *outLeft = weirdMul(comb->getLeftOutput(), wetLevel, 0xFF); + *outRight = weirdMul(comb->getRightOutput(), wetLevel, 0xFF); } else { - // Get the last stored sample before processing in order not to loose it - link = combs[0]->getOutputAt(currentSettings.combSizes[0] - 1); + // If the output position is equal to the comb size, get it now in order not to loose it + Sample link = combs[0]->getOutputAt(currentSettings.combSizes[0] - 1); // Entrance LPF. Note, comb.process() differs a bit here. combs[0]->process(dry); +#if !MT32EMU_USE_FLOAT_SAMPLES // This introduces reverb noise which actually makes output from the real Boss chip nondeterministic link = link - 1; +#endif link = allpasses[0]->process(link); link = allpasses[1]->process(link); link = allpasses[2]->process(link); // If the output position is equal to the comb size, get it now in order not to loose it - outL1 = combs[1]->getOutputAt(currentSettings.outLPositions[0] - 1); - outL1 += outL1 >> 1; + Sample outL1 = combs[1]->getOutputAt(currentSettings.outLPositions[0] - 1); combs[1]->process(link); combs[2]->process(link); combs[3]->process(link); - link = combs[2]->getOutputAt(currentSettings.outLPositions[1]); - link += link >> 1; - link += outL1; - link += combs[3]->getOutputAt(currentSettings.outLPositions[2]); - *outLeft = weirdMul(link, wetLevel, 0xFF) / 8192.0f; + Sample outL2 = combs[2]->getOutputAt(currentSettings.outLPositions[1]); + Sample outL3 = combs[3]->getOutputAt(currentSettings.outLPositions[2]); + Sample outR1 = combs[1]->getOutputAt(currentSettings.outRPositions[0]); + Sample outR2 = combs[2]->getOutputAt(currentSettings.outRPositions[1]); + Sample outR3 = combs[3]->getOutputAt(currentSettings.outRPositions[2]); + +#if MT32EMU_USE_FLOAT_SAMPLES + *outLeft = 1.5f * (outL1 + outL2) + outL3; + *outRight = 1.5f * (outR1 + outR2) + outR3; +#else + outL1 += outL1 >> 1; + outL2 += outL2 >> 1; + *outLeft = outL1 + outL2 + outL3; - outR1 = combs[1]->getOutputAt(currentSettings.outRPositions[0]); outR1 += outR1 >> 1; - link = combs[2]->getOutputAt(currentSettings.outRPositions[1]); - link += link >> 1; - link += outR1; - link += combs[3]->getOutputAt(currentSettings.outRPositions[2]); - *outRight = weirdMul(link, wetLevel, 0xFF) / 8192.0f; + outR2 += outR2 >> 1; + *outRight = outR1 + outR2 + outR3; +#endif + *outLeft = weirdMul(*outLeft, wetLevel, 0xFF); + *outRight = weirdMul(*outRight, wetLevel, 0xFF); } + numSamples--; inLeft++; inRight++; outLeft++; @@ -389,5 +420,3 @@ void BReverbModel::process(const float *inLeft, const float *inRight, float *out } } - -#endif diff --git a/audio/softsynth/mt32/BReverbModel.h b/audio/softsynth/mt32/BReverbModel.h index d6fcb73c13..7cf431db0d 100644 --- a/audio/softsynth/mt32/BReverbModel.h +++ b/audio/softsynth/mt32/BReverbModel.h @@ -36,14 +36,14 @@ struct BReverbSettings { class RingBuffer { protected: - Bit16s *buffer; + Sample *buffer; const Bit32u size; Bit32u index; public: RingBuffer(const Bit32u size); virtual ~RingBuffer(); - Bit32s next(); + Sample next(); bool isEmpty() const; void mute(); }; @@ -51,7 +51,7 @@ public: class AllpassFilter : public RingBuffer { public: AllpassFilter(const Bit32u size); - Bit32s process(const Bit32s in); + Sample process(const Sample in); }; class CombFilter : public RingBuffer { @@ -61,8 +61,8 @@ protected: public: CombFilter(const Bit32u size, const Bit32u useFilterFactor); - virtual void process(const Bit32s in); // Actually, no need to make it virtual, but for sure - Bit32s getOutputAt(const Bit32u outIndex) const; + virtual void process(const Sample in); + Sample getOutputAt(const Bit32u outIndex) const; void setFeedbackFactor(const Bit32u useFeedbackFactor); }; @@ -71,7 +71,7 @@ class DelayWithLowPassFilter : public CombFilter { public: DelayWithLowPassFilter(const Bit32u useSize, const Bit32u useFilterFactor, const Bit32u useAmp); - void process(const Bit32s in); + void process(const Sample in); void setFeedbackFactor(const Bit32u) {} }; @@ -81,13 +81,13 @@ class TapDelayCombFilter : public CombFilter { public: TapDelayCombFilter(const Bit32u useSize, const Bit32u useFilterFactor); - void process(const Bit32s in); - Bit32s getLeftOutput() const; - Bit32s getRightOutput() const; + void process(const Sample in); + Sample getLeftOutput() const; + Sample getRightOutput() const; void setOutputPositions(const Bit32u useOutL, const Bit32u useOutR); }; -class BReverbModel : public ReverbModel { +class BReverbModel { AllpassFilter **allpasses; CombFilter **combs; @@ -100,10 +100,12 @@ class BReverbModel : public ReverbModel { public: BReverbModel(const ReverbMode mode); ~BReverbModel(); + // After construction or a close(), open() must be called at least once before any other call (with the exception of close()). void open(); + // May be called multiple times without an open() in between. void close(); void setParameters(Bit8u time, Bit8u level); - void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples); + void process(const Sample *inLeft, const Sample *inRight, Sample *outLeft, Sample *outRight, unsigned long numSamples); bool isActive() const; }; diff --git a/audio/softsynth/mt32/DelayReverb.cpp b/audio/softsynth/mt32/DelayReverb.cpp deleted file mode 100644 index d80c98acbc..0000000000 --- a/audio/softsynth/mt32/DelayReverb.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -//#include <cmath> -//#include <cstring> -#include "mt32emu.h" -#include "DelayReverb.h" - -namespace MT32Emu { - -// CONFIRMED: The values below are found via analysis of digital samples and tracing reverb RAM address / data lines. Checked with all time and level combinations. -// Obviously: -// rightDelay = (leftDelay - 2) * 2 + 2 -// echoDelay = rightDelay - 1 -// Leaving these separate in case it's useful for work on other reverb modes... -static const Bit32u REVERB_TIMINGS[8][3]= { - // {leftDelay, rightDelay, feedbackDelay} - {402, 802, 801}, - {626, 1250, 1249}, - {962, 1922, 1921}, - {1490, 2978, 2977}, - {2258, 4514, 4513}, - {3474, 6946, 6945}, - {5282, 10562, 10561}, - {8002, 16002, 16001} -}; - -// Reverb amp is found as dryAmp * wetAmp -static const Bit32u REVERB_AMP[8] = {0x20*0x18, 0x50*0x18, 0x50*0x28, 0x50*0x40, 0x50*0x60, 0x50*0x80, 0x50*0xA8, 0x50*0xF8}; -static const Bit32u REVERB_FEEDBACK67 = 0x60; -static const Bit32u REVERB_FEEDBACK = 0x68; -static const float LPF_VALUE = 0x68 / 256.0f; - -static const Bit32u BUFFER_SIZE = 16384; - -DelayReverb::DelayReverb() { - buf = NULL; - setParameters(0, 0); -} - -DelayReverb::~DelayReverb() { - delete[] buf; -} - -void DelayReverb::open() { - if (buf == NULL) { - delete[] buf; - - buf = new float[BUFFER_SIZE]; - - recalcParameters(); - - // mute buffer - bufIx = 0; - if (buf != NULL) { - for (unsigned int i = 0; i < BUFFER_SIZE; i++) { - buf[i] = 0.0f; - } - } - } -} - -void DelayReverb::close() { - delete[] buf; - buf = NULL; -} - -// This method will always trigger a flush of the buffer -void DelayReverb::setParameters(Bit8u newTime, Bit8u newLevel) { - time = newTime; - level = newLevel; - recalcParameters(); -} - -void DelayReverb::recalcParameters() { - // Number of samples between impulse and eventual appearance on the left channel - delayLeft = REVERB_TIMINGS[time][0]; - // Number of samples between impulse and eventual appearance on the right channel - delayRight = REVERB_TIMINGS[time][1]; - // Number of samples between a response and that response feeding back/echoing - delayFeedback = REVERB_TIMINGS[time][2]; - - if (level < 3 || time < 6) { - feedback = REVERB_FEEDBACK / 256.0f; - } else { - feedback = REVERB_FEEDBACK67 / 256.0f; - } - - // Overall output amp - amp = (level == 0 && time == 0) ? 0.0f : REVERB_AMP[level] / 65536.0f; -} - -void DelayReverb::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) { - if (buf == NULL) return; - - for (unsigned int sampleIx = 0; sampleIx < numSamples; sampleIx++) { - // The ring buffer write index moves backwards; reads are all done with positive offsets. - Bit32u bufIxPrev = (bufIx + 1) % BUFFER_SIZE; - Bit32u bufIxLeft = (bufIx + delayLeft) % BUFFER_SIZE; - Bit32u bufIxRight = (bufIx + delayRight) % BUFFER_SIZE; - Bit32u bufIxFeedback = (bufIx + delayFeedback) % BUFFER_SIZE; - - // Attenuated input samples and feedback response are directly added to the current ring buffer location - float lpfIn = amp * (inLeft[sampleIx] + inRight[sampleIx]) + feedback * buf[bufIxFeedback]; - - // Single-pole IIR filter found on real devices - buf[bufIx] = buf[bufIxPrev] * LPF_VALUE - lpfIn; - - outLeft[sampleIx] = buf[bufIxLeft]; - outRight[sampleIx] = buf[bufIxRight]; - - bufIx = (BUFFER_SIZE + bufIx - 1) % BUFFER_SIZE; - } -} - -bool DelayReverb::isActive() const { - if (buf == NULL) return false; - - float *b = buf; - float max = 0.001f; - for (Bit32u i = 0; i < BUFFER_SIZE; i++) { - if ((*b < -max) || (*b > max)) return true; - b++; - } - return false; -} - -} diff --git a/audio/softsynth/mt32/DelayReverb.h b/audio/softsynth/mt32/DelayReverb.h deleted file mode 100644 index c8003832b5..0000000000 --- a/audio/softsynth/mt32/DelayReverb.h +++ /dev/null @@ -1,50 +0,0 @@ -/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef MT32EMU_DELAYREVERB_H -#define MT32EMU_DELAYREVERB_H - -namespace MT32Emu { - -class DelayReverb : public ReverbModel { -private: - Bit8u time; - Bit8u level; - - Bit32u bufIx; - float *buf; - - Bit32u delayLeft; - Bit32u delayRight; - Bit32u delayFeedback; - - float amp; - float feedback; - - void recalcParameters(); - -public: - DelayReverb(); - ~DelayReverb(); - void open(); - void close(); - void setParameters(Bit8u time, Bit8u level); - void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples); - bool isActive() const; -}; -} -#endif diff --git a/audio/softsynth/mt32/FreeverbModel.cpp b/audio/softsynth/mt32/FreeverbModel.cpp deleted file mode 100644 index bd9c70b6f4..0000000000 --- a/audio/softsynth/mt32/FreeverbModel.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "mt32emu.h" -#include "FreeverbModel.h" - -#include "freeverb.h" - -namespace MT32Emu { - -FreeverbModel::FreeverbModel(float useScaleTuning, float useFiltVal, float useWet, Bit8u useRoom, float useDamp) { - freeverb = NULL; - scaleTuning = useScaleTuning; - filtVal = useFiltVal; - wet = useWet; - room = useRoom; - damp = useDamp; -} - -FreeverbModel::~FreeverbModel() { - delete freeverb; -} - -void FreeverbModel::open() { - if (freeverb == NULL) { - freeverb = new revmodel(scaleTuning); - } - freeverb->mute(); - - // entrance Lowpass filter factor - freeverb->setfiltval(filtVal); - - // decay speed of high frequencies in the wet signal - freeverb->setdamp(damp); -} - -void FreeverbModel::close() { - delete freeverb; - freeverb = NULL; -} - -void FreeverbModel::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) { - freeverb->process(inLeft, inRight, outLeft, outRight, numSamples); -} - -void FreeverbModel::setParameters(Bit8u time, Bit8u level) { - // wet signal level - // FIXME: need to implement some sort of reverb level ramping - freeverb->setwet((float)level / 7.0f * wet); - - // wet signal decay speed - static float roomTable[] = { - 0.25f, 0.37f, 0.54f, 0.71f, 0.78f, 0.86f, 0.93f, 1.00f, - -1.00f, -0.50f, 0.00f, 0.30f, 0.51f, 0.64f, 0.77f, 0.90f, - 0.50f, 0.57f, 0.70f, 0.77f, 0.85f, 0.93f, 0.96f, 1.01f}; - freeverb->setroomsize(roomTable[8 * room + time]); -} - -bool FreeverbModel::isActive() const { - // FIXME: Not bothering to do this properly since we'll be replacing Freeverb soon... - return false; -} - -} diff --git a/audio/softsynth/mt32/FreeverbModel.h b/audio/softsynth/mt32/FreeverbModel.h deleted file mode 100644 index 5ea11f1f40..0000000000 --- a/audio/softsynth/mt32/FreeverbModel.h +++ /dev/null @@ -1,44 +0,0 @@ -/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef MT32EMU_FREEVERB_MODEL_H -#define MT32EMU_FREEVERB_MODEL_H - -class revmodel; - -namespace MT32Emu { - -class FreeverbModel : public ReverbModel { - revmodel *freeverb; - float scaleTuning; - float filtVal; - float wet; - Bit8u room; - float damp; -public: - FreeverbModel(float useScaleTuning, float useFiltVal, float useWet, Bit8u useRoom, float useDamp); - ~FreeverbModel(); - void open(); - void close(); - void setParameters(Bit8u time, Bit8u level); - void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples); - bool isActive() const; -}; - -} - -#endif diff --git a/audio/softsynth/mt32/LegacyWaveGenerator.cpp b/audio/softsynth/mt32/LA32FloatWaveGenerator.cpp index 35ca975018..486942b75c 100644 --- a/audio/softsynth/mt32/LegacyWaveGenerator.cpp +++ b/audio/softsynth/mt32/LA32FloatWaveGenerator.cpp @@ -18,9 +18,7 @@ //#include <cmath> #include "mt32emu.h" #include "mmath.h" -#include "LegacyWaveGenerator.h" - -#if MT32EMU_ACCURATE_WG == 1 +#include "LA32FloatWaveGenerator.h" namespace MT32Emu { @@ -317,7 +315,7 @@ void LA32PartialPair::generateNextSample(const PairType useMaster, const Bit32u } } -Bit16s LA32PartialPair::nextOutSample() { +float LA32PartialPair::nextOutSample() { float outputSample; if (ringModulated) { float ringModulatedSample = masterOutputSample * slaveOutputSample; @@ -325,7 +323,7 @@ Bit16s LA32PartialPair::nextOutSample() { } else { outputSample = masterOutputSample + slaveOutputSample; } - return Bit16s(outputSample * 8192.0f); + return outputSample; } void LA32PartialPair::deactivate(const PairType useMaster) { @@ -343,5 +341,3 @@ bool LA32PartialPair::isActive(const PairType useMaster) const { } } - -#endif // #if MT32EMU_ACCURATE_WG == 1 diff --git a/audio/softsynth/mt32/LegacyWaveGenerator.h b/audio/softsynth/mt32/LA32FloatWaveGenerator.h index 81c1b9c713..9046160083 100644 --- a/audio/softsynth/mt32/LegacyWaveGenerator.h +++ b/audio/softsynth/mt32/LA32FloatWaveGenerator.h @@ -15,8 +15,6 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#if MT32EMU_ACCURATE_WG == 1 - #ifndef MT32EMU_LA32_WAVE_GENERATOR_H #define MT32EMU_LA32_WAVE_GENERATOR_H @@ -130,7 +128,7 @@ public: void generateNextSample(const PairType master, const Bit32u amp, const Bit16u pitch, const Bit32u cutoff); // Perform mixing / ring modulation and return the result - Bit16s nextOutSample(); + float nextOutSample(); // Deactivate the WG engine void deactivate(const PairType master); @@ -142,5 +140,3 @@ public: } // namespace MT32Emu #endif // #ifndef MT32EMU_LA32_WAVE_GENERATOR_H - -#endif // #if MT32EMU_ACCURATE_WG == 1 diff --git a/audio/softsynth/mt32/LA32WaveGenerator.cpp b/audio/softsynth/mt32/LA32WaveGenerator.cpp index 80650699fb..9ffc2ca32e 100644 --- a/audio/softsynth/mt32/LA32WaveGenerator.cpp +++ b/audio/softsynth/mt32/LA32WaveGenerator.cpp @@ -20,7 +20,9 @@ #include "mmath.h" #include "LA32WaveGenerator.h" -#if MT32EMU_ACCURATE_WG == 0 +#if MT32EMU_USE_FLOAT_SAMPLES +#include "LA32FloatWaveGenerator.cpp" +#else namespace MT32Emu { @@ -129,7 +131,8 @@ void LA32WaveGenerator::advancePosition() { computePositions(highLinearLength, lowLinearLength, resonanceWaveLengthFactor); // resonancePhase computation hack - *(int*)&resonancePhase = ((resonanceSinePosition >> 18) + (phase > POSITIVE_FALLING_SINE_SEGMENT ? 2 : 0)) & 3; + int *resonancePhaseAlias = (int *)&resonancePhase; + *resonancePhaseAlias = ((resonanceSinePosition >> 18) + (phase > POSITIVE_FALLING_SINE_SEGMENT ? 2 : 0)) & 3; } void LA32WaveGenerator::generateNextSquareWaveLogSample() { @@ -415,4 +418,4 @@ bool LA32PartialPair::isActive(const PairType useMaster) const { } -#endif // #if MT32EMU_ACCURATE_WG == 0 +#endif // #if MT32EMU_USE_FLOAT_SAMPLES diff --git a/audio/softsynth/mt32/LA32WaveGenerator.h b/audio/softsynth/mt32/LA32WaveGenerator.h index 37a4aead85..4bc04c78d3 100644 --- a/audio/softsynth/mt32/LA32WaveGenerator.h +++ b/audio/softsynth/mt32/LA32WaveGenerator.h @@ -15,7 +15,9 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#if MT32EMU_ACCURATE_WG == 0 +#if MT32EMU_USE_FLOAT_SAMPLES +#include "LA32FloatWaveGenerator.h" +#else #ifndef MT32EMU_LA32_WAVE_GENERATOR_H #define MT32EMU_LA32_WAVE_GENERATOR_H @@ -243,4 +245,4 @@ public: #endif // #ifndef MT32EMU_LA32_WAVE_GENERATOR_H -#endif // #if MT32EMU_ACCURATE_WG == 0 +#endif // #if MT32EMU_USE_FLOAT_SAMPLES diff --git a/audio/softsynth/mt32/Part.cpp b/audio/softsynth/mt32/Part.cpp index 88404316eb..8a0daf3b38 100644 --- a/audio/softsynth/mt32/Part.cpp +++ b/audio/softsynth/mt32/Part.cpp @@ -67,18 +67,12 @@ Part::Part(Synth *useSynth, unsigned int usePartNum) { pitchBend = 0; activePartialCount = 0; memset(patchCache, 0, sizeof(patchCache)); - for (int i = 0; i < MT32EMU_MAX_POLY; i++) { - freePolys.prepend(new Poly(this)); - } } Part::~Part() { while (!activePolys.isEmpty()) { delete activePolys.takeFirst(); } - while (!freePolys.isEmpty()) { - delete freePolys.takeFirst(); - } } void Part::setDataEntryMSB(unsigned char midiDataEntryMSB) { @@ -431,23 +425,10 @@ void Part::noteOn(unsigned int midiKey, unsigned int velocity) { playPoly(patchCache, NULL, midiKey, key, velocity); } -void Part::abortPoly(Poly *poly) { - if (poly->startAbort()) { - while (poly->isActive()) { - if (!synth->prerender()) { - synth->printDebug("%s (%s): Ran out of prerender space to abort poly gracefully", name, currentInstr); - poly->terminate(); - break; - } - } - } -} - bool Part::abortFirstPoly(unsigned int key) { for (Poly *poly = activePolys.getFirst(); poly != NULL; poly = poly->getNext()) { if (poly->getKey() == key) { - abortPoly(poly); - return true; + return poly->startAbort(); } } return false; @@ -456,8 +437,7 @@ bool Part::abortFirstPoly(unsigned int key) { bool Part::abortFirstPoly(PolyState polyState) { for (Poly *poly = activePolys.getFirst(); poly != NULL; poly = poly->getNext()) { if (poly->getState() == polyState) { - abortPoly(poly); - return true; + return poly->startAbort(); } } return false; @@ -474,8 +454,7 @@ bool Part::abortFirstPoly() { if (activePolys.isEmpty()) { return false; } - abortPoly(activePolys.getFirst()); - return true; + return activePolys.getFirst()->startAbort(); } void Part::playPoly(const PatchCache cache[4], const MemParams::RhythmTemp *rhythmTemp, unsigned int midiKey, unsigned int key, unsigned int velocity) { @@ -489,6 +468,7 @@ void Part::playPoly(const PatchCache cache[4], const MemParams::RhythmTemp *rhyt if ((patchTemp->patch.assignMode & 2) == 0) { // Single-assign mode abortFirstPoly(key); + if (synth->isAbortingPoly()) return; } if (!synth->partialManager->freePartials(needPartials, partNum)) { @@ -498,12 +478,13 @@ void Part::playPoly(const PatchCache cache[4], const MemParams::RhythmTemp *rhyt #endif return; } + if (synth->isAbortingPoly()) return; - if (freePolys.isEmpty()) { + Poly *poly = synth->partialManager->assignPolyToPart(this); + if (poly == NULL) { synth->printDebug("%s (%s): No free poly to play key %d (velocity %d)", name, currentInstr, midiKey, velocity); return; } - Poly *poly = freePolys.takeFirst(); if (patchTemp->patch.assignMode & 1) { // Priority to data first received activePolys.prepend(poly); @@ -596,6 +577,10 @@ unsigned int Part::getActivePartialCount() const { return activePartialCount; } +const Poly *Part::getFirstActivePoly() const { + return activePolys.getFirst(); +} + unsigned int Part::getActiveNonReleasingPartialCount() const { unsigned int activeNonReleasingPartialCount = 0; for (Poly *poly = activePolys.getFirst(); poly != NULL; poly = poly->getNext()) { @@ -606,11 +591,15 @@ unsigned int Part::getActiveNonReleasingPartialCount() const { return activeNonReleasingPartialCount; } +Synth *Part::getSynth() const { + return synth; +} + void Part::partialDeactivated(Poly *poly) { activePartialCount--; if (!poly->isActive()) { activePolys.remove(poly); - freePolys.prepend(poly); + synth->partialManager->polyFreed(poly); synth->polyStateChanged(partNum); } } diff --git a/audio/softsynth/mt32/Part.h b/audio/softsynth/mt32/Part.h index b6585880fe..2aeeaf5bd7 100644 --- a/audio/softsynth/mt32/Part.h +++ b/audio/softsynth/mt32/Part.h @@ -51,13 +51,11 @@ private: unsigned int activePartialCount; PatchCache patchCache[4]; - PolyList freePolys; PolyList activePolys; void setPatch(const PatchParam *patch); unsigned int midiKeyToKey(unsigned int midiKey); - void abortPoly(Poly *poly); bool abortFirstPoly(unsigned int key); protected: @@ -110,8 +108,10 @@ public: virtual void setTimbre(TimbreParam *timbre); virtual unsigned int getAbsTimbreNum() const; const char *getCurrentInstr() const; + const Poly *getFirstActivePoly() const; unsigned int getActivePartialCount() const; unsigned int getActiveNonReleasingPartialCount() const; + Synth *getSynth() const; const MemParams::PatchTemp *getPatchTemp() const; diff --git a/audio/softsynth/mt32/Partial.cpp b/audio/softsynth/mt32/Partial.cpp index b80a028515..c7848f02d8 100644 --- a/audio/softsynth/mt32/Partial.cpp +++ b/audio/softsynth/mt32/Partial.cpp @@ -131,6 +131,7 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us } // FIXME: Sample analysis suggests that the use of panVal is linear, but there are some some quirks that still need to be resolved. + // FIXME: I suppose this should be panVal / 8 and undoubtly integer, clarify ASAP stereoVolume.leftVol = panVal / 7.0f; stereoVolume.rightVol = 1.0f - stereoVolume.leftVol; @@ -198,9 +199,6 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us if (!hasRingModulatingSlave()) { la32Pair.deactivate(LA32PartialPair::SLAVE); } - // Temporary integration hack - stereoVolume.leftVol /= 8192.0f; - stereoVolume.rightVol /= 8192.0f; } Bit32u Partial::getAmpValue() { @@ -232,7 +230,7 @@ Bit32u Partial::getCutoffValue() { return (tvf->getBaseCutoff() << 18) + cutoffModifierRampVal; } -unsigned long Partial::generateSamples(Bit16s *partialBuf, unsigned long length) { +unsigned long Partial::generateSamples(Sample *partialBuf, unsigned long length) { if (!isActive() || alreadyOutputed) { return 0; } @@ -258,7 +256,7 @@ unsigned long Partial::generateSamples(Bit16s *partialBuf, unsigned long length) } } } - *partialBuf++ = la32Pair.nextOutSample(); + *(partialBuf++) = la32Pair.nextOutSample(); } unsigned long renderedSamples = sampleNum; sampleNum = 0; @@ -288,7 +286,18 @@ Synth *Partial::getSynth() const { return synth; } -bool Partial::produceOutput(float *leftBuf, float *rightBuf, unsigned long length) { +TVA *Partial::getTVA() const { + return tva; +} + +void Partial::backupCache(const PatchCache &cache) { + if (patchCache == &cache) { + cachebackup = cache; + patchCache = &cachebackup; + } +} + +bool Partial::produceOutput(Sample *leftBuf, Sample *rightBuf, unsigned long length) { if (!isActive() || alreadyOutputed || isRingModulatingSlave()) { return false; } @@ -296,14 +305,18 @@ bool Partial::produceOutput(float *leftBuf, float *rightBuf, unsigned long lengt synth->printDebug("[Partial %d] *** ERROR: poly is NULL at Partial::produceOutput()!", debugPartialNum); return false; } - unsigned long numGenerated = generateSamples(myBuffer, length); + Sample buffer[MAX_SAMPLES_PER_RUN]; + unsigned long numGenerated = generateSamples(buffer, length); for (unsigned int i = 0; i < numGenerated; i++) { - *leftBuf++ = myBuffer[i] * stereoVolume.leftVol; - *rightBuf++ = myBuffer[i] * stereoVolume.rightVol; - } - for (; numGenerated < length; numGenerated++) { - *leftBuf++ = 0.0f; - *rightBuf++ = 0.0f; +#if MT32EMU_USE_FLOAT_SAMPLES + *(leftBuf++) += buffer[i] * stereoVolume.leftVol; + *(rightBuf++) += buffer[i] * stereoVolume.rightVol; +#else + *leftBuf = Synth::clipBit16s((Bit32s)*leftBuf + Bit32s(buffer[i] * stereoVolume.leftVol)); + *rightBuf = Synth::clipBit16s((Bit32s)*rightBuf + Bit32s(buffer[i] * stereoVolume.rightVol)); + leftBuf++; + rightBuf++; +#endif } return true; } diff --git a/audio/softsynth/mt32/Partial.h b/audio/softsynth/mt32/Partial.h index 21b1bfe376..358cb9d2d9 100644 --- a/audio/softsynth/mt32/Partial.h +++ b/audio/softsynth/mt32/Partial.h @@ -44,8 +44,6 @@ private: int structurePosition; // 0 or 1 of a structure pair StereoVolume stereoVolume; - Bit16s myBuffer[MAX_SAMPLES_PER_RUN]; - // Only used for PCM partials int pcmNum; // FIXME: Give this a better name (e.g. pcmWaveInfo) @@ -56,6 +54,11 @@ private: int pulseWidthVal; Poly *poly; + Partial *pair; + + TVA *tva; + TVP *tvp; + TVF *tvf; LA32Ramp ampRamp; LA32Ramp cutoffModifierRamp; @@ -63,18 +66,13 @@ private: // TODO: This should be owned by PartialPair LA32PartialPair la32Pair; + const PatchCache *patchCache; + PatchCache cachebackup; + Bit32u getAmpValue(); Bit32u getCutoffValue(); public: - const PatchCache *patchCache; - TVA *tva; - TVP *tvp; - TVF *tvf; - - PatchCache cachebackup; - - Partial *pair; bool alreadyOutputed; Partial(Synth *synth, int debugPartialNum); @@ -97,14 +95,17 @@ public: bool isPCM() const; const ControlROMPCMStruct *getControlROMPCMStruct() const; Synth *getSynth() const; + TVA *getTVA() const; + + void backupCache(const PatchCache &cache); // Returns true only if data written to buffer // This function (unlike the one below it) returns processed stereo samples // made from combining this single partial with its pair, if it has one. - bool produceOutput(float *leftBuf, float *rightBuf, unsigned long length); + bool produceOutput(Sample *leftBuf, Sample *rightBuf, unsigned long length); // This function writes mono sample output to the provided buffer, and returns the number of samples written - unsigned long generateSamples(Bit16s *partialBuf, unsigned long length); + unsigned long generateSamples(Sample *partialBuf, unsigned long length); }; } diff --git a/audio/softsynth/mt32/PartialManager.cpp b/audio/softsynth/mt32/PartialManager.cpp index 436e7a353e..905b5b8cf3 100644 --- a/audio/softsynth/mt32/PartialManager.cpp +++ b/audio/softsynth/mt32/PartialManager.cpp @@ -25,19 +25,26 @@ namespace MT32Emu { PartialManager::PartialManager(Synth *useSynth, Part **useParts) { synth = useSynth; parts = useParts; - for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { + partialTable = new Partial *[synth->getPartialCount()]; + freePolys = new Poly *[synth->getPartialCount()]; + firstFreePolyIndex = 0; + for (unsigned int i = 0; i < synth->getPartialCount(); i++) { partialTable[i] = new Partial(synth, i); + freePolys[i] = new Poly(); } } PartialManager::~PartialManager(void) { - for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { + for (unsigned int i = 0; i < synth->getPartialCount(); i++) { delete partialTable[i]; + if (freePolys[i] != NULL) delete freePolys[i]; } + delete[] partialTable; + delete[] freePolys; } void PartialManager::clearAlreadyOutputed() { - for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { + for (unsigned int i = 0; i < synth->getPartialCount(); i++) { partialTable[i]->alreadyOutputed = false; } } @@ -46,12 +53,12 @@ bool PartialManager::shouldReverb(int i) { return partialTable[i]->shouldReverb(); } -bool PartialManager::produceOutput(int i, float *leftBuf, float *rightBuf, Bit32u bufferLength) { +bool PartialManager::produceOutput(int i, Sample *leftBuf, Sample *rightBuf, Bit32u bufferLength) { return partialTable[i]->produceOutput(leftBuf, rightBuf, bufferLength); } void PartialManager::deactivateAll() { - for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { + for (unsigned int i = 0; i < synth->getPartialCount(); i++) { partialTable[i]->deactivate(); } } @@ -69,7 +76,7 @@ Partial *PartialManager::allocPartial(int partNum) { Partial *outPartial = NULL; // Get the first inactive partial - for (int partialNum = 0; partialNum < MT32EMU_MAX_PARTIALS; partialNum++) { + for (unsigned int partialNum = 0; partialNum < synth->getPartialCount(); partialNum++) { if (!partialTable[partialNum]->isActive()) { outPartial = partialTable[partialNum]; break; @@ -83,7 +90,7 @@ Partial *PartialManager::allocPartial(int partNum) { unsigned int PartialManager::getFreePartialCount(void) { int count = 0; - for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { + for (unsigned int i = 0; i < synth->getPartialCount(); i++) { if (!partialTable[i]->isActive()) { count++; } @@ -94,7 +101,7 @@ unsigned int PartialManager::getFreePartialCount(void) { // This function is solely used to gather data for debug output at the moment. void PartialManager::getPerPartPartialUsage(unsigned int perPartPartialUsage[9]) { memset(perPartPartialUsage, 0, 9 * sizeof(unsigned int)); - for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { + for (unsigned int i = 0; i < synth->getPartialCount(); i++) { if (partialTable[i]->isActive()) { perPartPartialUsage[partialTable[i]->getOwnerPart()]++; } @@ -189,7 +196,7 @@ bool PartialManager::freePartials(unsigned int needed, int partNum) { break; } #endif - if (getFreePartialCount() >= needed) { + if (synth->isAbortingPoly() || getFreePartialCount() >= needed) { return true; } } @@ -206,7 +213,7 @@ bool PartialManager::freePartials(unsigned int needed, int partNum) { if (!abortFirstPolyPreferHeldWhereReserveExceeded(partNum)) { break; } - if (getFreePartialCount() >= needed) { + if (synth->isAbortingPoly() || getFreePartialCount() >= needed) { return true; } } @@ -222,7 +229,7 @@ bool PartialManager::freePartials(unsigned int needed, int partNum) { if (!abortFirstPolyPreferHeldWhereReserveExceeded(-1)) { break; } - if (getFreePartialCount() >= needed) { + if (synth->isAbortingPoly() || getFreePartialCount() >= needed) { return true; } } @@ -233,7 +240,7 @@ bool PartialManager::freePartials(unsigned int needed, int partNum) { if (!parts[partNum]->abortFirstPolyPreferHeld()) { break; } - if (getFreePartialCount() >= needed) { + if (synth->isAbortingPoly() || getFreePartialCount() >= needed) { return true; } } @@ -243,10 +250,39 @@ bool PartialManager::freePartials(unsigned int needed, int partNum) { } const Partial *PartialManager::getPartial(unsigned int partialNum) const { - if (partialNum > MT32EMU_MAX_PARTIALS - 1) { + if (partialNum > synth->getPartialCount() - 1) { return NULL; } return partialTable[partialNum]; } +Poly *PartialManager::assignPolyToPart(Part *part) { + if (firstFreePolyIndex < synth->getPartialCount()) { + Poly *poly = freePolys[firstFreePolyIndex]; + freePolys[firstFreePolyIndex] = NULL; + firstFreePolyIndex++; + poly->setPart(part); + return poly; + } + return NULL; +} + +void PartialManager::polyFreed(Poly *poly) { + if (0 == firstFreePolyIndex) { + synth->printDebug("Cannot return freed poly, currently active polys:\n"); + for (Bit32u partNum = 0; partNum < 9; partNum++) { + const Poly *activePoly = synth->getPart(partNum)->getFirstActivePoly(); + Bit32u polyCount = 0; + while (activePoly != NULL) { + activePoly->getNext(); + polyCount++; + } + synth->printDebug("Part: %i, active poly count: %i\n", partNum, polyCount); + } + } + poly->setPart(NULL); + firstFreePolyIndex--; + freePolys[firstFreePolyIndex] = poly; +} + } diff --git a/audio/softsynth/mt32/PartialManager.h b/audio/softsynth/mt32/PartialManager.h index a1c9266ea1..229b6e8121 100644 --- a/audio/softsynth/mt32/PartialManager.h +++ b/audio/softsynth/mt32/PartialManager.h @@ -24,17 +24,17 @@ class Synth; class PartialManager { private: - Synth *synth; // Only used for sending debug output + Synth *synth; Part **parts; - - Partial *partialTable[MT32EMU_MAX_PARTIALS]; + Poly **freePolys; + Partial **partialTable; Bit8u numReservedPartialsForPart[9]; + Bit32u firstFreePolyIndex; bool abortFirstReleasingPolyWhereReserveExceeded(int minPart); bool abortFirstPolyPreferHeldWhereReserveExceeded(int minPart); public: - PartialManager(Synth *synth, Part **parts); ~PartialManager(); Partial *allocPartial(int partNum); @@ -43,10 +43,12 @@ public: bool freePartials(unsigned int needed, int partNum); unsigned int setReserve(Bit8u *rset); void deactivateAll(); - bool produceOutput(int i, float *leftBuf, float *rightBuf, Bit32u bufferLength); + bool produceOutput(int i, Sample *leftBuf, Sample *rightBuf, Bit32u bufferLength); bool shouldReverb(int i); void clearAlreadyOutputed(); const Partial *getPartial(unsigned int partialNum) const; + Poly *assignPolyToPart(Part *part); + void polyFreed(Poly *poly); }; } diff --git a/audio/softsynth/mt32/Poly.cpp b/audio/softsynth/mt32/Poly.cpp index 46574f8967..1554881270 100644 --- a/audio/softsynth/mt32/Poly.cpp +++ b/audio/softsynth/mt32/Poly.cpp @@ -19,8 +19,8 @@ namespace MT32Emu { -Poly::Poly(Part *usePart) { - part = usePart; +Poly::Poly() { + part = NULL; key = 255; velocity = 255; sustain = false; @@ -32,10 +32,21 @@ Poly::Poly(Part *usePart) { next = NULL; } +void Poly::setPart(Part *usePart) { + part = usePart; +} + void Poly::reset(unsigned int newKey, unsigned int newVelocity, bool newSustain, Partial **newPartials) { if (isActive()) { - // FIXME: Throw out some big ugly debug output with a lot of exclamation marks - we should never get here - terminate(); + // This should never happen + part->getSynth()->printDebug("Resetting active poly. Active partial count: %i\n", activePartialCount); + for (int i = 0; i < 4; i++) { + if (partials[i] != NULL && partials[i]->isActive()) { + partials[i]->deactivate(); + activePartialCount--; + } + } + state = POLY_Inactive; } key = newKey; @@ -92,41 +103,24 @@ bool Poly::startDecay() { } bool Poly::startAbort() { - if (state == POLY_Inactive) { + if (state == POLY_Inactive || part->getSynth()->isAbortingPoly()) { return false; } for (int t = 0; t < 4; t++) { Partial *partial = partials[t]; if (partial != NULL) { partial->startAbort(); + part->getSynth()->abortingPoly = this; } } return true; } -void Poly::terminate() { - if (state == POLY_Inactive) { - return; - } - for (int t = 0; t < 4; t++) { - Partial *partial = partials[t]; - if (partial != NULL) { - partial->deactivate(); - } - } - if (state != POLY_Inactive) { - // FIXME: Throw out lots of debug output - this should never happen - // (Deactivating the partials above should've made them each call partialDeactivated(), ultimately changing the state to POLY_Inactive) - state = POLY_Inactive; - } -} - void Poly::backupCacheToPartials(PatchCache cache[4]) { for (int partialNum = 0; partialNum < 4; partialNum++) { Partial *partial = partials[partialNum]; - if (partial != NULL && partial->patchCache == &cache[partialNum]) { - partial->cachebackup = cache[partialNum]; - partial->patchCache = &partial->cachebackup; + if (partial != NULL) { + partial->backupCache(cache[partialNum]); } } } @@ -171,11 +165,14 @@ void Poly::partialDeactivated(Partial *partial) { } if (activePartialCount == 0) { state = POLY_Inactive; + if (part->getSynth()->abortingPoly == this) { + part->getSynth()->abortingPoly = NULL; + } } part->partialDeactivated(this); } -Poly *Poly::getNext() { +Poly *Poly::getNext() const { return next; } diff --git a/audio/softsynth/mt32/Poly.h b/audio/softsynth/mt32/Poly.h index 068cf73d35..33abc35fdf 100644 --- a/audio/softsynth/mt32/Poly.h +++ b/audio/softsynth/mt32/Poly.h @@ -44,13 +44,13 @@ private: Poly *next; public: - Poly(Part *part); + Poly(); + void setPart(Part *usePart); void reset(unsigned int key, unsigned int velocity, bool sustain, Partial **partials); bool noteOff(bool pedalHeld); bool stopPedalHold(); bool startDecay(); bool startAbort(); - void terminate(); void backupCacheToPartials(PatchCache cache[4]); @@ -63,7 +63,7 @@ public: void partialDeactivated(Partial *partial); - Poly *getNext(); + Poly *getNext() const; void setNext(Poly *poly); }; diff --git a/audio/softsynth/mt32/Structures.h b/audio/softsynth/mt32/Structures.h index 43d2d1f226..421e427fc0 100644 --- a/audio/softsynth/mt32/Structures.h +++ b/audio/softsynth/mt32/Structures.h @@ -38,6 +38,12 @@ typedef signed short int Bit16s; typedef unsigned char Bit8u; typedef signed char Bit8s; +#if MT32EMU_USE_FLOAT_SAMPLES +typedef float Sample; +#else +typedef Bit16s Sample; +#endif + // The following structures represent the MT-32's memory // Since sysex allows this memory to be written to in blocks of bytes, // we keep this packed so that we can copy data into the various diff --git a/audio/softsynth/mt32/Synth.cpp b/audio/softsynth/mt32/Synth.cpp index 1e1be06bc9..b76dc58b5f 100644 --- a/audio/softsynth/mt32/Synth.cpp +++ b/audio/softsynth/mt32/Synth.cpp @@ -26,15 +26,7 @@ #include "mt32emu.h" #include "mmath.h" #include "PartialManager.h" - -#if MT32EMU_USE_REVERBMODEL == 1 -#include "AReverbModel.h" -#elif MT32EMU_USE_REVERBMODEL == 2 #include "BReverbModel.h" -#else -#include "FreeverbModel.h" -#endif -#include "DelayReverb.h" namespace MT32Emu { @@ -50,84 +42,22 @@ static const ControlROMMap ControlROMMaps[7] = { // (Note that all but CM-32L ROM actually have 86 entries for rhythmTemp) }; -static inline Bit16s *streamOffset(Bit16s *stream, Bit32u pos) { - return stream == NULL ? NULL : stream + pos; -} +static inline void muteStream(Sample *stream, Bit32u len) { + if (stream == NULL) return; -static inline void clearIfNonNull(Bit16s *stream, Bit32u len) { - if (stream != NULL) { - memset(stream, 0, len * sizeof(Bit16s)); - } -} - -static inline void mix(float *target, const float *stream, Bit32u len) { - while (len--) { - *target += *stream; - stream++; - target++; - } -} - -static inline void clearFloats(float *leftBuf, float *rightBuf, Bit32u len) { +#if MT32EMU_USE_FLOAT_SAMPLES // FIXME: Use memset() where compatibility is guaranteed (if this turns out to be a win) while (len--) { - *leftBuf++ = 0.0f; - *rightBuf++ = 0.0f; - } -} - -static inline Bit16s clipBit16s(Bit32s a) { - // Clamp values above 32767 to 32767, and values below -32768 to -32768 - if ((a + 32768) & ~65535) { - return (a >> 31) ^ 32767; - } - return a; -} - -static void floatToBit16s_nice(Bit16s *target, const float *source, Bit32u len, float outputGain) { - float gain = outputGain * 16384.0f; - while (len--) { - // Since we're not shooting for accuracy here, don't worry about the rounding mode. - *target = clipBit16s((Bit32s)(*source * gain)); - source++; - target++; - } -} - -static void floatToBit16s_pure(Bit16s *target, const float *source, Bit32u len, float /*outputGain*/) { - while (len--) { - *target = clipBit16s((Bit32s)floor(*source * 8192.0f)); - source++; - target++; - } -} - -static void floatToBit16s_reverb(Bit16s *target, const float *source, Bit32u len, float outputGain) { - float gain = outputGain * 8192.0f; - while (len--) { - *target = clipBit16s((Bit32s)floor(*source * gain)); - source++; - target++; - } -} - -static void floatToBit16s_generation1(Bit16s *target, const float *source, Bit32u len, float outputGain) { - float gain = outputGain * 8192.0f; - while (len--) { - *target = clipBit16s((Bit32s)floor(*source * gain)); - *target = (*target & 0x8000) | ((*target << 1) & 0x7FFE); - source++; - target++; + *stream++ = 0.0f; } +#else + memset(stream, 0, len * sizeof(Sample)); +#endif } -static void floatToBit16s_generation2(Bit16s *target, const float *source, Bit32u len, float outputGain) { - float gain = outputGain * 8192.0f; - while (len--) { - *target = clipBit16s((Bit32s)floor(*source * gain)); - *target = (*target & 0x8000) | ((*target << 1) & 0x7FFE) | ((*target >> 14) & 0x0001); - source++; - target++; +static inline void advanceStreamPosition(Sample *&stream, Bit32u posDelta) { + if (stream != NULL) { + stream += posDelta; } } @@ -155,28 +85,19 @@ Synth::Synth(ReportHandler *useReportHandler) { isDefaultReportHandler = false; } -#if MT32EMU_USE_REVERBMODEL == 1 - reverbModels[REVERB_MODE_ROOM] = new AReverbModel(REVERB_MODE_ROOM); - reverbModels[REVERB_MODE_HALL] = new AReverbModel(REVERB_MODE_HALL); - reverbModels[REVERB_MODE_PLATE] = new AReverbModel(REVERB_MODE_PLATE); - reverbModels[REVERB_MODE_TAP_DELAY] = new DelayReverb(); -#elif MT32EMU_USE_REVERBMODEL == 2 reverbModels[REVERB_MODE_ROOM] = new BReverbModel(REVERB_MODE_ROOM); reverbModels[REVERB_MODE_HALL] = new BReverbModel(REVERB_MODE_HALL); reverbModels[REVERB_MODE_PLATE] = new BReverbModel(REVERB_MODE_PLATE); reverbModels[REVERB_MODE_TAP_DELAY] = new BReverbModel(REVERB_MODE_TAP_DELAY); -#else - reverbModels[REVERB_MODE_ROOM] = new FreeverbModel(0.76f, 0.687770909f, 0.63f, 0, 0.5f); - reverbModels[REVERB_MODE_HALL] = new FreeverbModel(2.0f, 0.712025098f, 0.86f, 1, 0.5f); - reverbModels[REVERB_MODE_PLATE] = new FreeverbModel(0.4f, 0.939522749f, 0.38f, 2, 0.05f); - reverbModels[REVERB_MODE_TAP_DELAY] = new DelayReverb(); -#endif reverbModel = NULL; setDACInputMode(DACInputMode_NICE); + setMIDIDelayMode(MIDIDelayMode_DELAY_SHORT_MESSAGES_ONLY); setOutputGain(1.0f); - setReverbOutputGain(0.68f); + setReverbOutputGain(1.0f); partialManager = NULL; + midiQueue = NULL; + lastReceivedMIDIEventTimestamp = 0; memset(parts, 0, sizeof(parts)); renderedSampleCount = 0; } @@ -197,8 +118,8 @@ void ReportHandler::showLCDMessage(const char *data) { } void ReportHandler::printDebug(const char *fmt, va_list list) { - vprintf(fmt, list); - printf("\n"); + vprintf(fmt, list); + printf("\n"); } void Synth::polyStateChanged(int partNum) { @@ -236,35 +157,37 @@ bool Synth::isReverbOverridden() const { } void Synth::setDACInputMode(DACInputMode mode) { - switch(mode) { - case DACInputMode_GENERATION1: - la32FloatToBit16sFunc = floatToBit16s_generation1; - reverbFloatToBit16sFunc = floatToBit16s_reverb; - break; - case DACInputMode_GENERATION2: - la32FloatToBit16sFunc = floatToBit16s_generation2; - reverbFloatToBit16sFunc = floatToBit16s_reverb; - break; - case DACInputMode_PURE: - la32FloatToBit16sFunc = floatToBit16s_pure; - reverbFloatToBit16sFunc = floatToBit16s_pure; - break; - case DACInputMode_NICE: - default: - la32FloatToBit16sFunc = floatToBit16s_nice; - reverbFloatToBit16sFunc = floatToBit16s_reverb; - break; - } + dacInputMode = mode; +} + +DACInputMode Synth::getDACInputMode() const { + return dacInputMode; +} + +void Synth::setMIDIDelayMode(MIDIDelayMode mode) { + midiDelayMode = mode; +} + +MIDIDelayMode Synth::getMIDIDelayMode() const { + return midiDelayMode; } void Synth::setOutputGain(float newOutputGain) { outputGain = newOutputGain; } +float Synth::getOutputGain() const { + return outputGain; +} + void Synth::setReverbOutputGain(float newReverbOutputGain) { reverbOutputGain = newReverbOutputGain; } +float Synth::getReverbOutputGain() const { + return reverbOutputGain; +} + bool Synth::loadControlROM(const ROMImage &controlROMImage) { if (&controlROMImage == NULL) return false; Common::File *file = controlROMImage.getFile(); @@ -343,9 +266,9 @@ bool Synth::loadPCMROM(const ROMImage &pcmROMImage) { bool Synth::initPCMList(Bit16u mapAddress, Bit16u count) { ControlROMPCMStruct *tps = (ControlROMPCMStruct *)&controlROMData[mapAddress]; for (int i = 0; i < count; i++) { - size_t rAddr = tps[i].pos * 0x800; - size_t rLenExp = (tps[i].len & 0x70) >> 4; - size_t rLen = 0x800 << rLenExp; + Bit32u rAddr = tps[i].pos * 0x800; + Bit32u rLenExp = (tps[i].len & 0x70) >> 4; + Bit32u rLen = 0x800 << rLenExp; if (rAddr + rLen > pcmROMSize) { printDebug("Control ROM error: Wave map entry %d points to invalid PCM address 0x%04X, length 0x%04X", i, rAddr, rLen); return false; @@ -407,17 +330,18 @@ bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, int count, int startTi return true; } -bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage) { +bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount) { if (isOpen) { return false; } - prerenderReadIx = prerenderWriteIx = 0; + partialCount = usePartialCount; + abortingPoly = NULL; #if MT32EMU_MONITOR_INIT printDebug("Initialising Constant Tables"); #endif #if !MT32EMU_REDUCE_REVERB_MEMORY - for (int i = 0; i < 4; i++) { - reverbModels[i]->open(useProp.sampleRate); + for (int i = REVERB_MODE_ROOM; i <= REVERB_MODE_TAP_DELAY; i++) { + reverbModels[i]->open(); } #endif @@ -554,6 +478,8 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage) { // For resetting mt32 mid-execution mt32default = mt32ram; + midiQueue = new MidiEventQueue(); + isOpen = true; isEnabled = false; @@ -568,6 +494,9 @@ void Synth::close() { return; } + delete midiQueue; + midiQueue = NULL; + delete partialManager; partialManager = NULL; @@ -588,12 +517,78 @@ void Synth::close() { isOpen = false; } -void Synth::playMsg(Bit32u msg) { +void Synth::flushMIDIQueue() { + if (midiQueue != NULL) { + for (;;) { + const MidiEvent *midiEvent = midiQueue->peekMidiEvent(); + if (midiEvent == NULL) break; + if (midiEvent->sysexData == NULL) { + playMsgNow(midiEvent->shortMessageData); + } else { + playSysexNow(midiEvent->sysexData, midiEvent->sysexLength); + } + midiQueue->dropMidiEvent(); + } + lastReceivedMIDIEventTimestamp = renderedSampleCount; + } +} + +void Synth::setMIDIEventQueueSize(Bit32u useSize) { + if (midiQueue != NULL) { + flushMIDIQueue(); + delete midiQueue; + midiQueue = new MidiEventQueue(useSize); + } +} + +Bit32u Synth::getShortMessageLength(Bit32u msg) { + if ((msg & 0xF0) == 0xF0) return 1; + // NOTE: This calculation isn't quite correct + // as it doesn't consider the running status byte + return ((msg & 0xE0) == 0xC0) ? 2 : 3; +} + +Bit32u Synth::addMIDIInterfaceDelay(Bit32u len, Bit32u timestamp) { + Bit32u transferTime = Bit32u((double)len * MIDI_DATA_TRANSFER_RATE); + // Dealing with wrapping + if (Bit32s(timestamp - lastReceivedMIDIEventTimestamp) < 0) { + timestamp = lastReceivedMIDIEventTimestamp; + } + timestamp += transferTime; + lastReceivedMIDIEventTimestamp = timestamp; + return timestamp; +} + +bool Synth::playMsg(Bit32u msg) { + return playMsg(msg, renderedSampleCount); +} + +bool Synth::playMsg(Bit32u msg, Bit32u timestamp) { + if (midiQueue == NULL) return false; + if (midiDelayMode != MIDIDelayMode_IMMEDIATE) { + timestamp = addMIDIInterfaceDelay(getShortMessageLength(msg), timestamp); + } + return midiQueue->pushShortMessage(msg, timestamp); +} + +bool Synth::playSysex(const Bit8u *sysex, Bit32u len) { + return playSysex(sysex, len, renderedSampleCount); +} + +bool Synth::playSysex(const Bit8u *sysex, Bit32u len, Bit32u timestamp) { + if (midiQueue == NULL) return false; + if (midiDelayMode == MIDIDelayMode_DELAY_ALL) { + timestamp = addMIDIInterfaceDelay(len, timestamp); + } + return midiQueue->pushSysex(sysex, len, timestamp); +} + +void Synth::playMsgNow(Bit32u msg) { // FIXME: Implement active sensing unsigned char code = (unsigned char)((msg & 0x0000F0) >> 4); unsigned char chan = (unsigned char)(msg & 0x00000F); - unsigned char note = (unsigned char)((msg & 0x00FF00) >> 8); - unsigned char velocity = (unsigned char)((msg & 0xFF0000) >> 16); + unsigned char note = (unsigned char)((msg & 0x007F00) >> 8); + unsigned char velocity = (unsigned char)((msg & 0x7F0000) >> 16); isEnabled = true; //printDebug("Playing chan %d, code 0x%01x note: 0x%02x", chan, code, note); @@ -606,11 +601,6 @@ void Synth::playMsg(Bit32u msg) { return; } playMsgOnPart(part, code, note, velocity); - - // This ensures minimum 1-sample delay between sequential MIDI events - // Without this, a sequence of NoteOn and immediately succeeding NoteOff messages is always silent - // Technically, it's also impossible to send events through the MIDI interface faster than about each millisecond - prerender(); } void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity) { @@ -692,7 +682,7 @@ void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char #if MT32EMU_MONITOR_MIDI > 0 printDebug("Unknown MIDI Control code: 0x%02x - vel 0x%02x", note, velocity); #endif - break; + return; } break; @@ -709,13 +699,12 @@ void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char #if MT32EMU_MONITOR_MIDI > 0 printDebug("Unknown Midi code: 0x%01x - %02x - %02x", code, note, velocity); #endif - break; + return; } - - //midiOutShortMsg(m_out, msg); + reportHandler->onMIDIMessagePlayed(); } -void Synth::playSysex(const Bit8u *sysex, Bit32u len) { +void Synth::playSysexNow(const Bit8u *sysex, Bit32u len) { if (len < 2) { printDebug("playSysex: Message is too short for sysex (%d bytes)", len); } @@ -810,6 +799,7 @@ void Synth::readSysex(unsigned char /*device*/, const Bit8u * /*sysex*/, Bit32u } void Synth::writeSysex(unsigned char device, const Bit8u *sysex, Bit32u len) { + reportHandler->onMIDIMessagePlayed(); Bit32u addr = (sysex[0] << 16) | (sysex[1] << 8) | (sysex[2]); addr = MT32EMU_MEMADDR(addr); sysex += 3; @@ -1232,7 +1222,7 @@ void Synth::refreshSystemReverbParameters() { reportHandler->onNewReverbTime(mt32ram.system.reverbTime); reportHandler->onNewReverbLevel(mt32ram.system.reverbLevel); - ReverbModel *newReverbModel = reverbModels[mt32ram.system.reverbMode]; + BReverbModel *newReverbModel = reverbModels[mt32ram.system.reverbMode]; #if MT32EMU_REDUCE_REVERB_MEMORY if (reverbModel != newReverbModel) { if (reverbModel != NULL) { @@ -1308,179 +1298,226 @@ void Synth::reset() { isEnabled = false; } -void Synth::render(Bit16s *stream, Bit32u len) { - if (!isEnabled) { - memset(stream, 0, len * sizeof(Bit16s) * 2); - return; +MidiEvent::~MidiEvent() { + if (sysexData != NULL) { + delete[] sysexData; } - while (len > 0) { - Bit32u thisLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len; - renderStreams(tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, thisLen); - for (Bit32u i = 0; i < thisLen; i++) { - stream[0] = clipBit16s((Bit32s)tmpNonReverbLeft[i] + (Bit32s)tmpReverbDryLeft[i] + (Bit32s)tmpReverbWetLeft[i]); - stream[1] = clipBit16s((Bit32s)tmpNonReverbRight[i] + (Bit32s)tmpReverbDryRight[i] + (Bit32s)tmpReverbWetRight[i]); - stream += 2; - } - len -= thisLen; +} + +void MidiEvent::setShortMessage(Bit32u useShortMessageData, Bit32u useTimestamp) { + if (sysexData != NULL) { + delete[] sysexData; } + shortMessageData = useShortMessageData; + timestamp = useTimestamp; + sysexData = NULL; + sysexLength = 0; } -bool Synth::prerender() { - int newPrerenderWriteIx = (prerenderWriteIx + 1) % MAX_PRERENDER_SAMPLES; - if (newPrerenderWriteIx == prerenderReadIx) { - // The prerender buffer is full - return false; +void MidiEvent::setSysex(const Bit8u *useSysexData, Bit32u useSysexLength, Bit32u useTimestamp) { + if (sysexData != NULL) { + delete[] sysexData; } - doRenderStreams( - prerenderNonReverbLeft + prerenderWriteIx, - prerenderNonReverbRight + prerenderWriteIx, - prerenderReverbDryLeft + prerenderWriteIx, - prerenderReverbDryRight + prerenderWriteIx, - prerenderReverbWetLeft + prerenderWriteIx, - prerenderReverbWetRight + prerenderWriteIx, - 1); - prerenderWriteIx = newPrerenderWriteIx; + shortMessageData = 0; + timestamp = useTimestamp; + sysexLength = useSysexLength; + Bit8u *dstSysexData = new Bit8u[sysexLength]; + sysexData = dstSysexData; + memcpy(dstSysexData, useSysexData, sysexLength); +} + +MidiEventQueue::MidiEventQueue(Bit32u useRingBufferSize) : ringBufferSize(useRingBufferSize) { + ringBuffer = new MidiEvent[ringBufferSize]; + memset(ringBuffer, 0, ringBufferSize * sizeof(MidiEvent)); + reset(); +} + +MidiEventQueue::~MidiEventQueue() { + delete[] ringBuffer; +} + +void MidiEventQueue::reset() { + startPosition = 0; + endPosition = 0; +} + +bool MidiEventQueue::pushShortMessage(Bit32u shortMessageData, Bit32u timestamp) { + unsigned int newEndPosition = (endPosition + 1) % ringBufferSize; + // Is ring buffer full? + if (startPosition == newEndPosition) return false; + ringBuffer[endPosition].setShortMessage(shortMessageData, timestamp); + endPosition = newEndPosition; return true; } -static inline void maybeCopy(Bit16s *out, Bit32u outPos, Bit16s *in, Bit32u inPos, Bit32u len) { - if (out == NULL) { - return; - } - memcpy(out + outPos, in + inPos, len * sizeof(Bit16s)); +bool MidiEventQueue::pushSysex(const Bit8u *sysexData, Bit32u sysexLength, Bit32u timestamp) { + unsigned int newEndPosition = (endPosition + 1) % ringBufferSize; + // Is ring buffer full? + if (startPosition == newEndPosition) return false; + ringBuffer[endPosition].setSysex(sysexData, sysexLength, timestamp); + endPosition = newEndPosition; + return true; } -void Synth::copyPrerender(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u pos, Bit32u len) { - maybeCopy(nonReverbLeft, pos, prerenderNonReverbLeft, prerenderReadIx, len); - maybeCopy(nonReverbRight, pos, prerenderNonReverbRight, prerenderReadIx, len); - maybeCopy(reverbDryLeft, pos, prerenderReverbDryLeft, prerenderReadIx, len); - maybeCopy(reverbDryRight, pos, prerenderReverbDryRight, prerenderReadIx, len); - maybeCopy(reverbWetLeft, pos, prerenderReverbWetLeft, prerenderReadIx, len); - maybeCopy(reverbWetRight, pos, prerenderReverbWetRight, prerenderReadIx, len); +const MidiEvent *MidiEventQueue::peekMidiEvent() { + return (startPosition == endPosition) ? NULL : &ringBuffer[startPosition]; } -void Synth::checkPrerender(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u &pos, Bit32u &len) { - if (prerenderReadIx > prerenderWriteIx) { - // There's data in the prerender buffer, and the write index has wrapped. - Bit32u prerenderCopyLen = MAX_PRERENDER_SAMPLES - prerenderReadIx; - if (prerenderCopyLen > len) { - prerenderCopyLen = len; - } - copyPrerender(nonReverbLeft, nonReverbRight, reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, pos, prerenderCopyLen); - len -= prerenderCopyLen; - pos += prerenderCopyLen; - prerenderReadIx = (prerenderReadIx + prerenderCopyLen) % MAX_PRERENDER_SAMPLES; - } - if (prerenderReadIx < prerenderWriteIx) { - // There's data in the prerender buffer, and the write index is ahead of the read index. - Bit32u prerenderCopyLen = prerenderWriteIx - prerenderReadIx; - if (prerenderCopyLen > len) { - prerenderCopyLen = len; - } - copyPrerender(nonReverbLeft, nonReverbRight, reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, pos, prerenderCopyLen); - len -= prerenderCopyLen; - pos += prerenderCopyLen; - prerenderReadIx += prerenderCopyLen; - } - if (prerenderReadIx == prerenderWriteIx) { - // If the ring buffer's empty, reset it to start at 0 to minimise wrapping, - // which requires two writes instead of one. - prerenderReadIx = prerenderWriteIx = 0; - } -} - -void Synth::renderStreams(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u len) { - if (!isEnabled) { - clearIfNonNull(nonReverbLeft, len); - clearIfNonNull(nonReverbRight, len); - clearIfNonNull(reverbDryLeft, len); - clearIfNonNull(reverbDryRight, len); - clearIfNonNull(reverbWetLeft, len); - clearIfNonNull(reverbWetRight, len); - return; +void MidiEventQueue::dropMidiEvent() { + // Is ring buffer empty? + if (startPosition != endPosition) { + startPosition = (startPosition + 1) % ringBufferSize; } - Bit32u pos = 0; +} - // First, check for data in the prerender buffer and spit that out before generating anything new. - // Note that the prerender buffer is rarely used - see comments elsewhere for details. - checkPrerender(nonReverbLeft, nonReverbRight, reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, pos, len); +void Synth::render(Sample *stream, Bit32u len) { + Sample tmpNonReverbLeft[MAX_SAMPLES_PER_RUN]; + Sample tmpNonReverbRight[MAX_SAMPLES_PER_RUN]; + Sample tmpReverbDryLeft[MAX_SAMPLES_PER_RUN]; + Sample tmpReverbDryRight[MAX_SAMPLES_PER_RUN]; + Sample tmpReverbWetLeft[MAX_SAMPLES_PER_RUN]; + Sample tmpReverbWetRight[MAX_SAMPLES_PER_RUN]; while (len > 0) { Bit32u thisLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len; - doRenderStreams( - streamOffset(nonReverbLeft, pos), - streamOffset(nonReverbRight, pos), - streamOffset(reverbDryLeft, pos), - streamOffset(reverbDryRight, pos), - streamOffset(reverbWetLeft, pos), - streamOffset(reverbWetRight, pos), - thisLen); + renderStreams(tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, thisLen); + for (Bit32u i = 0; i < thisLen; i++) { +#if MT32EMU_USE_FLOAT_SAMPLES + *(stream++) = tmpNonReverbLeft[i] + tmpReverbDryLeft[i] + tmpReverbWetLeft[i]; + *(stream++) = tmpNonReverbRight[i] + tmpReverbDryRight[i] + tmpReverbWetRight[i]; +#else + *(stream++) = clipBit16s((Bit32s)tmpNonReverbLeft[i] + (Bit32s)tmpReverbDryLeft[i] + (Bit32s)tmpReverbWetLeft[i]); + *(stream++) = clipBit16s((Bit32s)tmpNonReverbRight[i] + (Bit32s)tmpReverbDryRight[i] + (Bit32s)tmpReverbWetRight[i]); +#endif + } len -= thisLen; - pos += thisLen; } } -// FIXME: Using more temporary buffers than we need to -void Synth::doRenderStreams(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u len) { - clearFloats(&tmpBufMixLeft[0], &tmpBufMixRight[0], len); - if (!reverbEnabled) { - for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { - if (partialManager->produceOutput(i, &tmpBufPartialLeft[0], &tmpBufPartialRight[0], len)) { - mix(&tmpBufMixLeft[0], &tmpBufPartialLeft[0], len); - mix(&tmpBufMixRight[0], &tmpBufPartialRight[0], len); - } - } - if (nonReverbLeft != NULL) { - la32FloatToBit16sFunc(nonReverbLeft, &tmpBufMixLeft[0], len, outputGain); - } - if (nonReverbRight != NULL) { - la32FloatToBit16sFunc(nonReverbRight, &tmpBufMixRight[0], len, outputGain); - } - clearIfNonNull(reverbDryLeft, len); - clearIfNonNull(reverbDryRight, len); - clearIfNonNull(reverbWetLeft, len); - clearIfNonNull(reverbWetRight, len); - } else { - for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { - if (!partialManager->shouldReverb(i)) { - if (partialManager->produceOutput(i, &tmpBufPartialLeft[0], &tmpBufPartialRight[0], len)) { - mix(&tmpBufMixLeft[0], &tmpBufPartialLeft[0], len); - mix(&tmpBufMixRight[0], &tmpBufPartialRight[0], len); +void Synth::renderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len) { + while (len > 0) { + // We need to ensure zero-duration notes will play so add minimum 1-sample delay. + Bit32u thisLen = 1; + if (!isAbortingPoly()) { + const MidiEvent *nextEvent = midiQueue->peekMidiEvent(); + Bit32s samplesToNextEvent = (nextEvent != NULL) ? Bit32s(nextEvent->timestamp - renderedSampleCount) : MAX_SAMPLES_PER_RUN; + if (samplesToNextEvent > 0) { + thisLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len; + if (thisLen > (Bit32u)samplesToNextEvent) { + thisLen = samplesToNextEvent; + } + } else { + if (nextEvent->sysexData == NULL) { + playMsgNow(nextEvent->shortMessageData); + // If a poly is aborting we don't drop the event from the queue. + // Instead, we'll return to it again when the abortion is done. + if (!isAbortingPoly()) { + midiQueue->dropMidiEvent(); + } + } else { + playSysexNow(nextEvent->sysexData, nextEvent->sysexLength); + midiQueue->dropMidiEvent(); } } } - if (nonReverbLeft != NULL) { - la32FloatToBit16sFunc(nonReverbLeft, &tmpBufMixLeft[0], len, outputGain); + doRenderStreams(nonReverbLeft, nonReverbRight, reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, thisLen); + advanceStreamPosition(nonReverbLeft, thisLen); + advanceStreamPosition(nonReverbRight, thisLen); + advanceStreamPosition(reverbDryLeft, thisLen); + advanceStreamPosition(reverbDryRight, thisLen); + advanceStreamPosition(reverbWetLeft, thisLen); + advanceStreamPosition(reverbWetRight, thisLen); + len -= thisLen; + } +} + +void Synth::convertSamplesToOutput(Sample *target, const Sample *source, Bit32u len, bool reverb) { + if (target == NULL) return; + + if (dacInputMode == DACInputMode_PURE) { + memcpy(target, source, len * sizeof(Sample)); + return; + } + +#if MT32EMU_USE_FLOAT_SAMPLES + float gain = reverb ? reverbOutputGain * CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR : 2.0f * outputGain; + while (len--) { + *(target++) = *(source++) * gain; + } +#else + float gain = reverb ? reverbOutputGain * CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR : outputGain; + if (!reverb) { + switch (dacInputMode) { + case DACInputMode_NICE: + // Since we're not shooting for accuracy here, don't worry about the rounding mode. + gain *= 2.0f; + break; + case DACInputMode_GENERATION1: + while (len--) { + *target = clipBit16s(Bit32s(*source * gain)); + *target = (*target & 0x8000) | ((*target << 1) & 0x7FFE); + source++; + target++; + } + return; + case DACInputMode_GENERATION2: + while (len--) { + *target = clipBit16s(Bit32s(*source * gain)); + *target = (*target & 0x8000) | ((*target << 1) & 0x7FFE) | ((*target >> 14) & 0x0001); + source++; + target++; + } + return; + default: + break; } - if (nonReverbRight != NULL) { - la32FloatToBit16sFunc(nonReverbRight, &tmpBufMixRight[0], len, outputGain); + } + while (len--) { + *(target++) = clipBit16s(Bit32s(*(source++) * gain)); + } +#endif +} + +void Synth::doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len) { + if (isEnabled) { + Sample tmpBufMixLeft[MAX_SAMPLES_PER_RUN], tmpBufMixRight[MAX_SAMPLES_PER_RUN]; + muteStream(tmpBufMixLeft, len); + muteStream(tmpBufMixRight, len); + for (unsigned int i = 0; i < getPartialCount(); i++) { + if (!reverbEnabled || !partialManager->shouldReverb(i)) { + partialManager->produceOutput(i, tmpBufMixLeft, tmpBufMixRight, len); + } } + convertSamplesToOutput(nonReverbLeft, tmpBufMixLeft, len, false); + convertSamplesToOutput(nonReverbRight, tmpBufMixRight, len, false); + } else { + muteStream(nonReverbLeft, len); + muteStream(nonReverbRight, len); + } - clearFloats(&tmpBufMixLeft[0], &tmpBufMixRight[0], len); - for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { + if (isEnabled && reverbEnabled) { + Sample tmpBufMixLeft[MAX_SAMPLES_PER_RUN], tmpBufMixRight[MAX_SAMPLES_PER_RUN]; + muteStream(tmpBufMixLeft, len); + muteStream(tmpBufMixRight, len); + for (unsigned int i = 0; i < getPartialCount(); i++) { if (partialManager->shouldReverb(i)) { - if (partialManager->produceOutput(i, &tmpBufPartialLeft[0], &tmpBufPartialRight[0], len)) { - mix(&tmpBufMixLeft[0], &tmpBufPartialLeft[0], len); - mix(&tmpBufMixRight[0], &tmpBufPartialRight[0], len); - } + partialManager->produceOutput(i, tmpBufMixLeft, tmpBufMixRight, len); } } - if (reverbDryLeft != NULL) { - la32FloatToBit16sFunc(reverbDryLeft, &tmpBufMixLeft[0], len, outputGain); - } - if (reverbDryRight != NULL) { - la32FloatToBit16sFunc(reverbDryRight, &tmpBufMixRight[0], len, outputGain); - } + convertSamplesToOutput(reverbDryLeft, tmpBufMixLeft, len, false); + convertSamplesToOutput(reverbDryRight, tmpBufMixRight, len, false); - // FIXME: Note that on the real devices, reverb input and output are signed linear 16-bit (well, kinda, there's some fudging) PCM, not float. - reverbModel->process(&tmpBufMixLeft[0], &tmpBufMixRight[0], &tmpBufReverbOutLeft[0], &tmpBufReverbOutRight[0], len); - if (reverbWetLeft != NULL) { - reverbFloatToBit16sFunc(reverbWetLeft, &tmpBufReverbOutLeft[0], len, reverbOutputGain); - } - if (reverbWetRight != NULL) { - reverbFloatToBit16sFunc(reverbWetRight, &tmpBufReverbOutRight[0], len, reverbOutputGain); - } + Sample tmpBufReverbOutLeft[MAX_SAMPLES_PER_RUN], tmpBufReverbOutRight[MAX_SAMPLES_PER_RUN]; + reverbModel->process(tmpBufMixLeft, tmpBufMixRight, tmpBufReverbOutLeft, tmpBufReverbOutRight, len); + convertSamplesToOutput(reverbWetLeft, tmpBufReverbOutLeft, len, true); + convertSamplesToOutput(reverbWetRight, tmpBufReverbOutRight, len, true); + } else { + muteStream(reverbDryLeft, len); + muteStream(reverbDryRight, len); + muteStream(reverbWetLeft, len); + muteStream(reverbWetRight, len); } + partialManager->clearAlreadyOutputed(); renderedSampleCount += len; } @@ -1489,19 +1526,14 @@ void Synth::printPartialUsage(unsigned long sampleOffset) { unsigned int partialUsage[9]; partialManager->getPerPartPartialUsage(partialUsage); if (sampleOffset > 0) { - printDebug("[+%lu] Partial Usage: 1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d R: %02d TOTAL: %02d", sampleOffset, partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7], partialUsage[8], MT32EMU_MAX_PARTIALS - partialManager->getFreePartialCount()); + printDebug("[+%lu] Partial Usage: 1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d R: %02d TOTAL: %02d", sampleOffset, partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7], partialUsage[8], getPartialCount() - partialManager->getFreePartialCount()); } else { - printDebug("Partial Usage: 1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d R: %02d TOTAL: %02d", partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7], partialUsage[8], MT32EMU_MAX_PARTIALS - partialManager->getFreePartialCount()); + printDebug("Partial Usage: 1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d R: %02d TOTAL: %02d", partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7], partialUsage[8], getPartialCount() - partialManager->getFreePartialCount()); } } bool Synth::hasActivePartials() const { - if (prerenderReadIx != prerenderWriteIx) { - // Data in the prerender buffer means that the current isActive() states are "in the future". - // It also means that partials are definitely active at this render point. - return true; - } - for (int partialNum = 0; partialNum < MT32EMU_MAX_PARTIALS; partialNum++) { + for (unsigned int partialNum = 0; partialNum < getPartialCount(); partialNum++) { if (partialManager->getPartial(partialNum)->isActive()) { return true; } @@ -1509,6 +1541,10 @@ bool Synth::hasActivePartials() const { return false; } +bool Synth::isAbortingPoly() const { + return abortingPoly != NULL; +} + bool Synth::isActive() const { if (hasActivePartials()) { return true; @@ -1523,6 +1559,10 @@ const Partial *Synth::getPartial(unsigned int partialNum) const { return partialManager->getPartial(partialNum); } +unsigned int Synth::getPartialCount() const { + return partialCount; +} + const Part *Synth::getPart(unsigned int partNum) const { if (partNum > 8) { return NULL; diff --git a/audio/softsynth/mt32/Synth.h b/audio/softsynth/mt32/Synth.h index b85e7ae507..783d6e2747 100644 --- a/audio/softsynth/mt32/Synth.h +++ b/audio/softsynth/mt32/Synth.h @@ -27,6 +27,7 @@ class Partial; class PartialManager; class Part; class ROMImage; +class BReverbModel; /** * Methods for emulating the connection between the LA32 and the DAC, which involves @@ -44,6 +45,7 @@ enum DACInputMode { // * Much less likely to overdrive than any other mode. // * Half the volume of any of the other modes, meaning its volume relative to the reverb // output when mixed together directly will sound wrong. + // * Output gain is ignored for both LA32 and reverb output. // * Perfect for developers while debugging :) DACInputMode_PURE, @@ -58,7 +60,17 @@ enum DACInputMode { DACInputMode_GENERATION2 }; -typedef void (*FloatToBit16sFunc)(Bit16s *target, const float *source, Bit32u len, float outputGain); +enum MIDIDelayMode { + // Process incoming MIDI events immediately. + MIDIDelayMode_IMMEDIATE, + + // Delay incoming short MIDI messages as if they where transferred via a MIDI cable to a real hardware unit and immediate sysex processing. + // This ensures more accurate timing of simultaneous NoteOn messages. + MIDIDelayMode_DELAY_SHORT_MESSAGES_ONLY, + + // Delay all incoming MIDI events as if they where transferred via a MIDI cable to a real hardware unit. + MIDIDelayMode_DELAY_ALL +}; const Bit8u SYSEX_MANUFACTURER_ROLAND = 0x41; @@ -217,18 +229,6 @@ public: ResetMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Reset, MT32EMU_MEMADDR(0x7F0000), 0x3FFF, 1) {} }; -class ReverbModel { -public: - virtual ~ReverbModel() {} - // After construction or a close(), open() will be called at least once before any other call (with the exception of close()). - virtual void open() = 0; - // May be called multiple times without an open() in between. - virtual void close() = 0; - virtual void setParameters(Bit8u time, Bit8u level) = 0; - virtual void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) = 0; - virtual bool isActive() const = 0; -}; - class ReportHandler { friend class Synth; @@ -244,22 +244,63 @@ protected: virtual void onErrorControlROM() {} virtual void onErrorPCMROM() {} virtual void showLCDMessage(const char *message); + virtual void onMIDIMessagePlayed() {} virtual void onDeviceReset() {} virtual void onDeviceReconfig() {} virtual void onNewReverbMode(Bit8u /* mode */) {} virtual void onNewReverbTime(Bit8u /* time */) {} virtual void onNewReverbLevel(Bit8u /* level */) {} - virtual void onPartStateChanged(int /* partNum */, bool /* hasActiveNonReleasingPolys */) {} virtual void onPolyStateChanged(int /* partNum */) {} - virtual void onPartialStateChanged(int /* partialNum */, int /* oldPartialPhase */, int /* newPartialPhase */) {} virtual void onProgramChanged(int /* partNum */, int /* bankNum */, const char * /* patchName */) {} }; +/** + * Used to safely store timestamped MIDI events in a local queue. + */ +struct MidiEvent { + Bit32u shortMessageData; + const Bit8u *sysexData; + Bit32u sysexLength; + Bit32u timestamp; + + ~MidiEvent(); + void setShortMessage(Bit32u shortMessageData, Bit32u timestamp); + void setSysex(const Bit8u *sysexData, Bit32u sysexLength, Bit32u timestamp); +}; + +/** + * Simple queue implementation using a ring buffer to store incoming MIDI event before the synth actually processes it. + * It is intended to: + * - get rid of prerenderer while retaining graceful partial abortion + * - add fair emulation of the MIDI interface delays + * - extend the synth interface with the default implementation of a typical rendering loop. + * THREAD SAFETY: + * It is safe to use either in a single thread environment or when there are only two threads - one performs only reading + * and one performs only writing. More complicated usage requires external synchronisation. + */ +class MidiEventQueue { +private: + MidiEvent *ringBuffer; + Bit32u ringBufferSize; + volatile Bit32u startPosition; + volatile Bit32u endPosition; + +public: + MidiEventQueue(Bit32u ringBufferSize = DEFAULT_MIDI_EVENT_QUEUE_SIZE); + ~MidiEventQueue(); + void reset(); + bool pushShortMessage(Bit32u shortMessageData, Bit32u timestamp); + bool pushSysex(const Bit8u *sysexData, Bit32u sysexLength, Bit32u timestamp); + const MidiEvent *peekMidiEvent(); + void dropMidiEvent(); +}; + class Synth { friend class Part; friend class RhythmPart; friend class Poly; friend class Partial; +friend class PartialManager; friend class Tables; friend class MemoryRegion; friend class TVA; @@ -286,20 +327,22 @@ private: Bit16s *pcmROMData; size_t pcmROMSize; // This is in 16-bit samples, therefore half the number of bytes in the ROM - Bit8s chantable[32]; - - Bit32u renderedSampleCount; + unsigned int partialCount; + Bit8s chantable[32]; // FIXME: Need explanation why 32 is set, obviously it should be 16 + MidiEventQueue *midiQueue; + volatile Bit32u lastReceivedMIDIEventTimestamp; + volatile Bit32u renderedSampleCount; MemParams mt32ram, mt32default; - ReverbModel *reverbModels[4]; - ReverbModel *reverbModel; + BReverbModel *reverbModels[4]; + BReverbModel *reverbModel; bool reverbEnabled; bool reverbOverridden; - FloatToBit16sFunc la32FloatToBit16sFunc; - FloatToBit16sFunc reverbFloatToBit16sFunc; + MIDIDelayMode midiDelayMode; + DACInputMode dacInputMode; float outputGain; float reverbOutputGain; @@ -311,41 +354,18 @@ private: PartialManager *partialManager; Part *parts[9]; - // FIXME: We can reorganise things so that we don't need all these separate tmpBuf, tmp and prerender buffers. - // This should be rationalised when things have stabilised a bit (if prerender buffers don't die in the mean time). - - float tmpBufPartialLeft[MAX_SAMPLES_PER_RUN]; - float tmpBufPartialRight[MAX_SAMPLES_PER_RUN]; - float tmpBufMixLeft[MAX_SAMPLES_PER_RUN]; - float tmpBufMixRight[MAX_SAMPLES_PER_RUN]; - float tmpBufReverbOutLeft[MAX_SAMPLES_PER_RUN]; - float tmpBufReverbOutRight[MAX_SAMPLES_PER_RUN]; - - Bit16s tmpNonReverbLeft[MAX_SAMPLES_PER_RUN]; - Bit16s tmpNonReverbRight[MAX_SAMPLES_PER_RUN]; - Bit16s tmpReverbDryLeft[MAX_SAMPLES_PER_RUN]; - Bit16s tmpReverbDryRight[MAX_SAMPLES_PER_RUN]; - Bit16s tmpReverbWetLeft[MAX_SAMPLES_PER_RUN]; - Bit16s tmpReverbWetRight[MAX_SAMPLES_PER_RUN]; - - // These ring buffers are only used to simulate delays present on the real device. - // In particular, when a partial needs to be aborted to free it up for use by a new Poly, + // When a partial needs to be aborted to free it up for use by a new Poly, // the controller will busy-loop waiting for the sound to finish. - Bit16s prerenderNonReverbLeft[MAX_PRERENDER_SAMPLES]; - Bit16s prerenderNonReverbRight[MAX_PRERENDER_SAMPLES]; - Bit16s prerenderReverbDryLeft[MAX_PRERENDER_SAMPLES]; - Bit16s prerenderReverbDryRight[MAX_PRERENDER_SAMPLES]; - Bit16s prerenderReverbWetLeft[MAX_PRERENDER_SAMPLES]; - Bit16s prerenderReverbWetRight[MAX_PRERENDER_SAMPLES]; - int prerenderReadIx; - int prerenderWriteIx; - - bool prerender(); - void copyPrerender(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u pos, Bit32u len); - void checkPrerender(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u &pos, Bit32u &len); - void doRenderStreams(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u len); - - void playAddressedSysex(unsigned char channel, const Bit8u *sysex, Bit32u len); + // We emulate this by delaying new MIDI events processing until abortion finishes. + Poly *abortingPoly; + + Bit32u getShortMessageLength(Bit32u msg); + Bit32u addMIDIInterfaceDelay(Bit32u len, Bit32u timestamp); + + void convertSamplesToOutput(Sample *target, const Sample *source, Bit32u len, bool reverb); + bool isAbortingPoly() const; + void doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len); + void readSysex(unsigned char channel, const Bit8u *sysex, Bit32u len) const; void initMemoryRegions(); void deleteMemoryRegions(); @@ -375,6 +395,14 @@ private: void printDebug(const char *fmt, ...); public: + static inline Bit16s clipBit16s(Bit32s sample) { + // Clamp values above 32767 to 32767, and values below -32768 to -32768 + if ((sample + 32768) & ~65535) { + return (sample >> 31) ^ 32767; + } + return (Bit16s)sample; + } + static Bit8u calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum); // Optionally sets callbacks for reporting various errors, information and debug messages @@ -384,18 +412,44 @@ public: // Used to initialise the MT-32. Must be called before any other function. // Returns true if initialization was sucessful, otherwise returns false. // controlROMImage and pcmROMImage represent Control and PCM ROM images for use by synth. - bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage); + // usePartialCount sets the maximum number of partials playing simultaneously for this session. + bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount = DEFAULT_MAX_PARTIALS); // Closes the MT-32 and deallocates any memory used by the synthesizer void close(void); - // Sends a 4-byte MIDI message to the MT-32 for immediate playback - void playMsg(Bit32u msg); + // All the enqueued events are processed by the synth immediately. + void flushMIDIQueue(); + + // Sets size of the internal MIDI event queue. + // The queue is flushed before reallocation. + void setMIDIEventQueueSize(Bit32u); + + // Enqueues a MIDI event for subsequent playback. + // The minimum delay involves the delay introduced while the event is transferred via MIDI interface + // and emulation of the MCU busy-loop while it frees partials for use by a new Poly. + // Calls from multiple threads must be synchronised, although, + // no synchronisation is required with the rendering thread. + + // The MIDI event will be processed not before the specified timestamp. + // The timestamp is measured as the global rendered sample count since the synth was created. + bool playMsg(Bit32u msg, Bit32u timestamp); + bool playSysex(const Bit8u *sysex, Bit32u len, Bit32u timestamp); + // The MIDI event will be processed ASAP. + bool playMsg(Bit32u msg); + bool playSysex(const Bit8u *sysex, Bit32u len); + + // WARNING: + // The methods below don't ensure minimum 1-sample delay between sequential MIDI events, + // and a sequence of NoteOn and immediately succeeding NoteOff messages is always silent. + + // Sends a 4-byte MIDI message to the MT-32 for immediate playback. + void playMsgNow(Bit32u msg); void playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity); // Sends a string of Sysex commands to the MT-32 for immediate interpretation // The length is in bytes - void playSysex(const Bit8u *sysex, Bit32u len); + void playSysexNow(const Bit8u *sysex, Bit32u len); void playSysexWithoutFraming(const Bit8u *sysex, Bit32u len); void playSysexWithoutHeader(unsigned char device, unsigned char command, const Bit8u *sysex, Bit32u len); void writeSysex(unsigned char channel, const Bit8u *sysex, Bit32u len); @@ -405,20 +459,31 @@ public: void setReverbOverridden(bool reverbOverridden); bool isReverbOverridden() const; void setDACInputMode(DACInputMode mode); + DACInputMode getDACInputMode() const; + void setMIDIDelayMode(MIDIDelayMode mode); + MIDIDelayMode getMIDIDelayMode() const; // Sets output gain factor. Applied to all output samples and unrelated with the synth's Master volume. + // Ignored in DACInputMode_PURE void setOutputGain(float); + float getOutputGain() const; // Sets output gain factor for the reverb wet output. setOutputGain() doesn't change reverb output gain. + // Note: We're currently emulate CM-32L/CM-64 reverb quite accurately and the reverb output level closely + // corresponds to the level of digital capture. Although, according to the CM-64 PCB schematic, + // there is a difference in the reverb analogue circuit, and the resulting output gain is 0.68 + // of that for LA32 analogue output. This factor is applied to the reverb output gain. + // Ignored in DACInputMode_PURE void setReverbOutputGain(float); + float getReverbOutputGain() const; // Renders samples to the specified output stream. // The length is in frames, not bytes (in 16-bit stereo, // one frame is 4 bytes). - void render(Bit16s *stream, Bit32u len); + void render(Sample *stream, Bit32u len); // Renders samples to the specified output streams (any or all of which may be NULL). - void renderStreams(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u len); + void renderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len); // Returns true when there is at least one active partial, otherwise false. bool hasActivePartials() const; @@ -428,6 +493,9 @@ public: const Partial *getPartial(unsigned int partialNum) const; + // Returns the maximum number of partials playing simultaneously. + unsigned int getPartialCount() const; + void readMemory(Bit32u addr, Bit32u len, Bit8u *data); // partNum should be 0..7 for Part 1..8, or 8 for Rhythm diff --git a/audio/softsynth/mt32/TVP.cpp b/audio/softsynth/mt32/TVP.cpp index c3e64c18d0..8f68245753 100644 --- a/audio/softsynth/mt32/TVP.cpp +++ b/audio/softsynth/mt32/TVP.cpp @@ -181,7 +181,7 @@ void TVP::updatePitch() { pitch = (Bit16u)newPitch; // FIXME: We're doing this here because that's what the CM-32L does - we should probably move this somewhere more appropriate in future. - partial->tva->recalcSustain(); + partial->getTVA()->recalcSustain(); } void TVP::targetPitchOffsetReached() { diff --git a/audio/softsynth/mt32/Tables.h b/audio/softsynth/mt32/Tables.h index 8b4580df0e..bfb80e121e 100644 --- a/audio/softsynth/mt32/Tables.h +++ b/audio/softsynth/mt32/Tables.h @@ -25,6 +25,11 @@ namespace MT32Emu { // The output from the synth is supposed to be resampled to convert the sample rate. const unsigned int SAMPLE_RATE = 32000; +// MIDI interface data transfer rate in samples. Used to simulate the transfer delay. +const double MIDI_DATA_TRANSFER_RATE = (double)SAMPLE_RATE / 31250.0 * 8.0; + +const float CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR = 0.68f; + const int MIDDLEC = 60; class Synth; diff --git a/audio/softsynth/mt32/freeverb.cpp b/audio/softsynth/mt32/freeverb.cpp deleted file mode 100644 index 181b878596..0000000000 --- a/audio/softsynth/mt32/freeverb.cpp +++ /dev/null @@ -1,324 +0,0 @@ -// Allpass filter implementation -// -// Written by Jezar at Dreampoint, June 2000 -// http://www.dreampoint.co.uk -// This code is public domain - -#include "freeverb.h" - -allpass::allpass() -{ - bufidx = 0; -} - -void allpass::setbuffer(float *buf, int size) -{ - buffer = buf; - bufsize = size; -} - -void allpass::mute() -{ - for (int i=0; i<bufsize; i++) - buffer[i]=0; -} - -void allpass::setfeedback(float val) -{ - feedback = val; -} - -float allpass::getfeedback() -{ - return feedback; -} - -void allpass::deletebuffer() -{ - delete[] buffer; - buffer = 0; -} -// Comb filter implementation -// -// Written by Jezar at Dreampoint, June 2000 -// http://www.dreampoint.co.uk -// This code is public domain - -comb::comb() -{ - filterstore = 0; - bufidx = 0; -} - -void comb::setbuffer(float *buf, int size) -{ - buffer = buf; - bufsize = size; -} - -void comb::mute() -{ - for (int i=0; i<bufsize; i++) - buffer[i]=0; -} - -void comb::setdamp(float val) -{ - damp1 = val; - damp2 = 1-val; -} - -float comb::getdamp() -{ - return damp1; -} - -void comb::setfeedback(float val) -{ - feedback = val; -} - -float comb::getfeedback() -{ - return feedback; -} - -void comb::deletebuffer() -{ - delete[] buffer; - buffer = 0; -} -// Reverb model implementation -// -// Written by Jezar at Dreampoint, June 2000 -// Modifications by Jerome Fisher, 2009, 2011 -// http://www.dreampoint.co.uk -// This code is public domain - -revmodel::revmodel(float scaletuning) -{ - int i; - int bufsize; - - // Allocate buffers for the components - for (i = 0; i < numcombs; i++) { - bufsize = int(scaletuning * combtuning[i]); - combL[i].setbuffer(new float[bufsize], bufsize); - bufsize += int(scaletuning * stereospread); - combR[i].setbuffer(new float[bufsize], bufsize); - } - for (i = 0; i < numallpasses; i++) { - bufsize = int(scaletuning * allpasstuning[i]); - allpassL[i].setbuffer(new float[bufsize], bufsize); - allpassL[i].setfeedback(0.5f); - bufsize += int(scaletuning * stereospread); - allpassR[i].setbuffer(new float[bufsize], bufsize); - allpassR[i].setfeedback(0.5f); - } - - // Set default values - dry = initialdry; - wet = initialwet*scalewet; - damp = initialdamp*scaledamp; - roomsize = (initialroom*scaleroom) + offsetroom; - width = initialwidth; - mode = initialmode; - update(); - - // Buffer will be full of rubbish - so we MUST mute them - mute(); -} - -revmodel::~revmodel() -{ - int i; - - for (i = 0; i < numcombs; i++) { - combL[i].deletebuffer(); - combR[i].deletebuffer(); - } - for (i = 0; i < numallpasses; i++) { - allpassL[i].deletebuffer(); - allpassR[i].deletebuffer(); - } -} - -void revmodel::mute() -{ - int i; - - if (getmode() >= freezemode) - return; - - for (i=0;i<numcombs;i++) - { - combL[i].mute(); - combR[i].mute(); - } - for (i=0;i<numallpasses;i++) - { - allpassL[i].mute(); - allpassR[i].mute(); - } - - // Init LPF history - filtprev1 = 0; - filtprev2 = 0; -} - -void revmodel::process(const float *inputL, const float *inputR, float *outputL, float *outputR, long numsamples) -{ - float outL,outR,input; - - while (numsamples-- > 0) - { - int i; - - outL = outR = 0; - input = (*inputL + *inputR) * gain; - - // Implementation of 2-stage IIR single-pole low-pass filter - // found at the entrance of reverb processing on real devices - filtprev1 += (input - filtprev1) * filtval; - filtprev2 += (filtprev1 - filtprev2) * filtval; - input = filtprev2; - - int s = -1; - // Accumulate comb filters in parallel - for (i=0; i<numcombs; i++) - { - outL += s * combL[i].process(input); - outR += s * combR[i].process(input); - s = -s; - } - - // Feed through allpasses in series - for (i=0; i<numallpasses; i++) - { - outL = allpassL[i].process(outL); - outR = allpassR[i].process(outR); - } - - // Calculate output REPLACING anything already there - *outputL = outL*wet1 + outR*wet2; - *outputR = outR*wet1 + outL*wet2; - - inputL++; - inputR++; - outputL++; - outputR++; - } -} - -void revmodel::update() -{ -// Recalculate internal values after parameter change - - int i; - - wet1 = wet*(width/2 + 0.5f); - wet2 = wet*((1-width)/2); - - if (mode >= freezemode) - { - roomsize1 = 1; - damp1 = 0; - gain = muted; - } - else - { - roomsize1 = roomsize; - damp1 = damp; - gain = fixedgain; - } - - for (i=0; i<numcombs; i++) - { - combL[i].setfeedback(roomsize1); - combR[i].setfeedback(roomsize1); - } - - for (i=0; i<numcombs; i++) - { - combL[i].setdamp(damp1); - combR[i].setdamp(damp1); - } -} - -// The following get/set functions are not inlined, because -// speed is never an issue when calling them, and also -// because as you develop the reverb model, you may -// wish to take dynamic action when they are called. - -void revmodel::setroomsize(float value) -{ - roomsize = (value*scaleroom) + offsetroom; - update(); -} - -float revmodel::getroomsize() -{ - return (roomsize-offsetroom)/scaleroom; -} - -void revmodel::setdamp(float value) -{ - damp = value*scaledamp; - update(); -} - -float revmodel::getdamp() -{ - return damp/scaledamp; -} - -void revmodel::setwet(float value) -{ - wet = value*scalewet; - update(); -} - -float revmodel::getwet() -{ - return wet/scalewet; -} - -void revmodel::setdry(float value) -{ - dry = value*scaledry; -} - -float revmodel::getdry() -{ - return dry/scaledry; -} - -void revmodel::setwidth(float value) -{ - width = value; - update(); -} - -float revmodel::getwidth() -{ - return width; -} - -void revmodel::setmode(float value) -{ - mode = value; - update(); -} - -float revmodel::getmode() -{ - if (mode >= freezemode) - return 1; - else - return 0; -} - -void revmodel::setfiltval(float value) -{ - filtval = value; -} diff --git a/audio/softsynth/mt32/freeverb.h b/audio/softsynth/mt32/freeverb.h deleted file mode 100644 index ae4d48169e..0000000000 --- a/audio/softsynth/mt32/freeverb.h +++ /dev/null @@ -1,189 +0,0 @@ -#ifndef _freeverb_ -#define _freeverb_ - -// Reverb model tuning values -// -// Written by Jezar at Dreampoint, June 2000 -// http://www.dreampoint.co.uk -// This code is public domain - -const int numcombs = 8; -const int numallpasses = 4; -const float muted = 0; -const float fixedgain = 0.015f; -const float scalewet = 3; -const float scaledry = 2; -const float scaledamp = 0.4f; -const float scaleroom = 0.28f; -const float offsetroom = 0.7f; -const float initialroom = 0.5f; -const float initialdamp = 0.5f; -const float initialwet = 1/scalewet; -const float initialdry = 0; -const float initialwidth = 1; -const float initialmode = 0; -const float freezemode = 0.5f; -const int stereospread = 23; - -const int combtuning[] = {1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617}; -const int allpasstuning[] = {556, 441, 341, 225}; - -// Macro for killing denormalled numbers -// -// Written by Jezar at Dreampoint, June 2000 -// http://www.dreampoint.co.uk -// Based on IS_DENORMAL macro by Jon Watte -// This code is public domain - -static inline float undenormalise(float x) { - union { - float f; - unsigned int i; - } u; - u.f = x; - if ((u.i & 0x7f800000) == 0) { - return 0.0f; - } - return x; -} - -// Allpass filter declaration -// -// Written by Jezar at Dreampoint, June 2000 -// http://www.dreampoint.co.uk -// This code is public domain - -class allpass -{ -public: - allpass(); - void setbuffer(float *buf, int size); - void deletebuffer(); - inline float process(float inp); - void mute(); - void setfeedback(float val); - float getfeedback(); -// private: - float feedback; - float *buffer; - int bufsize; - int bufidx; -}; - - -// Big to inline - but crucial for speed - -inline float allpass::process(float input) -{ - float output; - float bufout; - - bufout = undenormalise(buffer[bufidx]); - - output = -input + bufout; - buffer[bufidx] = input + (bufout*feedback); - - if (++bufidx>=bufsize) bufidx = 0; - - return output; -} - -// Comb filter class declaration -// -// Written by Jezar at Dreampoint, June 2000 -// http://www.dreampoint.co.uk -// This code is public domain - -class comb -{ -public: - comb(); - void setbuffer(float *buf, int size); - void deletebuffer(); - inline float process(float inp); - void mute(); - void setdamp(float val); - float getdamp(); - void setfeedback(float val); - float getfeedback(); -private: - float feedback; - float filterstore; - float damp1; - float damp2; - float *buffer; - int bufsize; - int bufidx; -}; - - -// Big to inline - but crucial for speed - -inline float comb::process(float input) -{ - float output; - - output = undenormalise(buffer[bufidx]); - - filterstore = undenormalise((output*damp2) + (filterstore*damp1)); - - buffer[bufidx] = input + (filterstore*feedback); - - if (++bufidx>=bufsize) bufidx = 0; - - return output; -} - -// Reverb model declaration -// -// Written by Jezar at Dreampoint, June 2000 -// Modifications by Jerome Fisher, 2009 -// http://www.dreampoint.co.uk -// This code is public domain - -class revmodel -{ -public: - revmodel(float scaletuning); - ~revmodel(); - void mute(); - void process(const float *inputL, const float *inputR, float *outputL, float *outputR, long numsamples); - void setroomsize(float value); - float getroomsize(); - void setdamp(float value); - float getdamp(); - void setwet(float value); - float getwet(); - void setdry(float value); - float getdry(); - void setwidth(float value); - float getwidth(); - void setmode(float value); - float getmode(); - void setfiltval(float value); -private: - void update(); -private: - float gain; - float roomsize,roomsize1; - float damp,damp1; - float wet,wet1,wet2; - float dry; - float width; - float mode; - - // LPF stuff - float filtval; - float filtprev1; - float filtprev2; - - // Comb filters - comb combL[numcombs]; - comb combR[numcombs]; - - // Allpass filters - allpass allpassL[numallpasses]; - allpass allpassR[numallpasses]; -}; - -#endif//_freeverb_ diff --git a/audio/softsynth/mt32/module.mk b/audio/softsynth/mt32/module.mk index e7afdfd2b4..1c8aa125ab 100644 --- a/audio/softsynth/mt32/module.mk +++ b/audio/softsynth/mt32/module.mk @@ -1,24 +1,19 @@ MODULE := audio/softsynth/mt32 MODULE_OBJS := \ - AReverbModel.o \ BReverbModel.o \ - DelayReverb.o \ - FreeverbModel.o \ LA32Ramp.o \ LA32WaveGenerator.o \ - LegacyWaveGenerator.o \ Part.o \ Partial.o \ PartialManager.o \ Poly.o \ ROMInfo.o \ Synth.o \ + Tables.o \ TVA.o \ TVF.o \ - TVP.o \ - Tables.o \ - freeverb.o + TVP.o # Include common rules include $(srcdir)/rules.mk diff --git a/audio/softsynth/mt32/mt32emu.h b/audio/softsynth/mt32/mt32emu.h index 971a0886d5..ab963886ac 100644 --- a/audio/softsynth/mt32/mt32emu.h +++ b/audio/softsynth/mt32/mt32emu.h @@ -60,27 +60,24 @@ #define MT32EMU_MONITOR_TVF 0 // Configuration -// The maximum number of partials playing simultaneously -#define MT32EMU_MAX_PARTIALS 32 -// The maximum number of notes playing simultaneously per part. -// No point making it more than MT32EMU_MAX_PARTIALS, since each note needs at least one partial. -#define MT32EMU_MAX_POLY 32 // If non-zero, deletes reverb buffers that are not in use to save memory. // If zero, keeps reverb buffers for all modes around all the time to avoid allocating/freeing in the critical path. #define MT32EMU_REDUCE_REVERB_MEMORY 1 -// 0: Use legacy Freeverb -// 1: Use Accurate Reverb model aka AReverb -// 2: Use Bit-perfect Boss Reverb model aka BReverb (for developers, not much practical use) -#define MT32EMU_USE_REVERBMODEL 1 +// 0: Maximum speed at the cost of a bit lower emulation accuracy. +// 1: Maximum achievable emulation accuracy. +#define MT32EMU_BOSS_REVERB_PRECISE_MODE 0 -// 0: Use refined wave generator based on logarithmic fixed-point computations and LUTs -// 1: Use legacy accurate wave generator based on float computations -#define MT32EMU_ACCURATE_WG 0 +// 0: Use 16-bit signed samples and refined wave generator based on logarithmic fixed-point computations and LUTs. Maximum emulation accuracy and speed. +// 1: Use float samples in the wave generator and renderer. Maximum output quality and minimum noise. +#define MT32EMU_USE_FLOAT_SAMPLES 0 namespace MT32Emu { +// The default value for the maximum number of partials playing simultaneously. +const unsigned int DEFAULT_MAX_PARTIALS = 32; + // The higher this number, the more memory will be used, but the more samples can be processed in one run - // various parts of sample generation can be processed more efficiently in a single run. // A run's maximum length is that given to Synth::render(), so giving a value here higher than render() is ever @@ -90,11 +87,14 @@ namespace MT32Emu // This value must be >= 1. const unsigned int MAX_SAMPLES_PER_RUN = 4096; -// This determines the amount of memory available for simulating delays. -// If set too low, partials aborted to allow other partials to play will not end gracefully, but will terminate -// abruptly and potentially cause a pop/crackle in the audio output. -// This value must be >= 1. -const unsigned int MAX_PRERENDER_SAMPLES = 1024; +// The default size of the internal MIDI event queue. +// It holds the incoming MIDI events before the rendering engine actually processes them. +// The main goal is to fairly emulate the real hardware behaviour which obviously +// uses an internal MIDI event queue to gather incoming data as well as the delays +// introduced by transferring data via the MIDI interface. +// This also facilitates building of an external rendering loop +// as the queue stores timestamped MIDI events. +const unsigned int DEFAULT_MIDI_EVENT_QUEUE_SIZE = 1024; } #include "Structures.h" @@ -103,7 +103,6 @@ const unsigned int MAX_PRERENDER_SAMPLES = 1024; #include "Poly.h" #include "LA32Ramp.h" #include "LA32WaveGenerator.h" -#include "LegacyWaveGenerator.h" #include "TVA.h" #include "TVP.h" #include "TVF.h" diff --git a/backends/platform/tizen/application.cpp b/backends/platform/tizen/application.cpp index 8236ebef67..a73efacf58 100644 --- a/backends/platform/tizen/application.cpp +++ b/backends/platform/tizen/application.cpp @@ -31,7 +31,7 @@ Application *TizenScummVM::createInstance() { return new TizenScummVM(); } -TizenScummVM::TizenScummVM() : _appForm(0) { +TizenScummVM::TizenScummVM() : _appForm(NULL) { logEntered(); } @@ -41,7 +41,7 @@ TizenScummVM::~TizenScummVM() { TizenSystem *system = (TizenSystem *)g_system; system->destroyBackend(); delete system; - g_system = 0; + g_system = NULL; } } @@ -68,17 +68,19 @@ bool TizenScummVM::OnAppTerminating(AppRegistry &appRegistry, bool forcedTermina } void TizenScummVM::OnUserEventReceivedN(RequestId requestId, IList *args) { + logEntered(); MessageBox messageBox; int modalResult; + String *message; - logEntered(); - - if (requestId == USER_MESSAGE_EXIT) { + switch (requestId) { + case USER_MESSAGE_EXIT: // normal program termination Terminate(); - } else if (requestId == USER_MESSAGE_EXIT_ERR) { + break; + + case USER_MESSAGE_EXIT_ERR: // assertion failure termination - String *message = NULL; if (args) { message = (String *)args->GetAt(0); } @@ -88,12 +90,15 @@ void TizenScummVM::OnUserEventReceivedN(RequestId requestId, IList *args) { messageBox.Construct(L"Oops...", *message, MSGBOX_STYLE_OK); messageBox.ShowAndWait(modalResult); Terminate(); - } else if (requestId == USER_MESSAGE_EXIT_ERR_CONFIG) { + break; + + case USER_MESSAGE_EXIT_ERR_CONFIG: // the config file was corrupted messageBox.Construct(L"Config file corrupted", L"Settings have been reverted, please restart.", MSGBOX_STYLE_OK); messageBox.ShowAndWait(modalResult); Terminate(); + break; } } @@ -132,7 +137,6 @@ void TizenScummVM::pauseGame(bool pause) { if (pause && g_engine && !g_engine->isPaused()) { _appForm->pushKey(Common::KEYCODE_SPACE); } - if (g_system) { ((TizenSystem *)g_system)->setMute(pause); } diff --git a/backends/platform/tizen/audio.cpp b/backends/platform/tizen/audio.cpp index 313a10eaa8..f9ac80a583 100644 --- a/backends/platform/tizen/audio.cpp +++ b/backends/platform/tizen/audio.cpp @@ -26,8 +26,9 @@ #include "backends/platform/tizen/audio.h" #include "backends/platform/tizen/system.h" -#define TIMER_INTERVAL 10 -#define VOLUME 99 +#define TIMER_INTERVAL 10 +#define VOLUME 96 +#define MIN_TIMER_INTERVAL 5 AudioThread::AudioThread() : _mixer(0), @@ -38,6 +39,7 @@ AudioThread::AudioThread() : _ready(0), _interval(TIMER_INTERVAL), _playing(-1), + _size(0), _muted(true) { } @@ -70,7 +72,7 @@ void AudioThread::setMute(bool on) { if (on) { _timer->Cancel(); } else { - _timer->StartAsRepeatable(_interval); + _timer->Start(_interval); } } } @@ -105,13 +107,14 @@ bool AudioThread::OnStart(void) { } } + _size = _audioBuffer[0].GetCapacity(); _timer = new Timer(); if (!_timer || IsFailed(_timer->Construct(*this))) { AppLog("Failed to create audio timer"); return false; } - if (IsFailed(_timer->StartAsRepeatable(_interval))) { + if (IsFailed(_timer->Start(_interval))) { AppLog("failed to start audio timer"); return false; } @@ -137,6 +140,7 @@ void AudioThread::OnStop(void) { if (_audioOut) { _audioOut->Reset(); + _audioOut->Unprepare(); delete _audioOut; } } @@ -161,21 +165,33 @@ void AudioThread::OnAudioOutBufferEndReached(Tizen::Media::AudioOut &src) { _tail = (_tail + 1) % NUM_AUDIO_BUFFERS; _ready--; } else { - // audio buffer empty: decrease timer inverval + // audio buffer empty: decrease timer interval _playing = -1; + _interval -= 1; + if (_interval < MIN_TIMER_INTERVAL) { + _interval = MIN_TIMER_INTERVAL; + } } + } void AudioThread::OnTimerExpired(Timer &timer) { if (_ready < NUM_AUDIO_BUFFERS) { - uint len = _audioBuffer[_head].GetCapacity(); - int samples = _mixer->mixCallback((byte *)_audioBuffer[_head].GetPointer(), len); - if (samples) { - _head = (_head + 1) % NUM_AUDIO_BUFFERS; - _ready++; + if (_playing != _head) { + if (_mixer->mixCallback((byte *)_audioBuffer[_head].GetPointer(), _size)) { + _head = (_head + 1) % NUM_AUDIO_BUFFERS; + _ready++; + } } + } else { + // audio buffer full: restore timer interval + _interval = TIMER_INTERVAL; } + if (_ready && _playing == -1) { OnAudioOutBufferEndReached(*_audioOut); } + + _timer->Start(_interval); } + diff --git a/backends/platform/tizen/audio.h b/backends/platform/tizen/audio.h index 8d7835042d..a304231578 100644 --- a/backends/platform/tizen/audio.h +++ b/backends/platform/tizen/audio.h @@ -54,6 +54,7 @@ public: bool isSilentMode(); void setMute(bool on); +private: bool OnStart(void); void OnStop(void); void OnAudioOutErrorOccurred(Tizen::Media::AudioOut &src, result r); @@ -62,12 +63,11 @@ public: void OnAudioOutBufferEndReached(Tizen::Media::AudioOut &src); void OnTimerExpired(Timer &timer); -private: Audio::MixerImpl *_mixer; Tizen::Base::Runtime::Timer *_timer; Tizen::Media::AudioOut *_audioOut; Tizen::Base::ByteBuffer _audioBuffer[NUM_AUDIO_BUFFERS]; - int _head, _tail, _ready, _interval, _playing; + int _head, _tail, _ready, _interval, _playing, _size; bool _muted; }; diff --git a/backends/platform/tizen/form.cpp b/backends/platform/tizen/form.cpp index 5050699ca9..10d51cc610 100644 --- a/backends/platform/tizen/form.cpp +++ b/backends/platform/tizen/form.cpp @@ -52,7 +52,7 @@ TizenAppForm::TizenAppForm() : _eventQueueLock(NULL), _state(kInitState), _buttonState(kLeftButton), - _shortcut(kShowKeypad) { + _shortcut(kEscapeKey) { } result TizenAppForm::Construct() { @@ -157,6 +157,8 @@ result TizenAppForm::OnInitializing(void) { AddOrientationEventListener(*this); AddTouchEventListener(*this); SetMultipointTouchEnabled(true); + SetFormBackEventListener(this); + SetFormMenuEventListener(this); // set focus to enable receiving key events SetEnabled(true); @@ -316,16 +318,16 @@ void TizenAppForm::invokeShortcut() { case kControlMouse: setButtonShortcut(); break; - + case kEscapeKey: pushKey(Common::KEYCODE_ESCAPE); break; - + case kGameMenu: _buttonState = kLeftButton; pushKey(Common::KEYCODE_F5); break; - + case kShowKeypad: showKeypad(); break; @@ -354,8 +356,6 @@ void TizenAppForm::OnTouchDoublePressed(const Control &source, if (_buttonState != kMoveOnly) { pushEvent(_buttonState == kLeftButton ? Common::EVENT_LBUTTONDOWN : Common::EVENT_RBUTTONDOWN, currentPosition); - pushEvent(_buttonState == kLeftButton ? Common::EVENT_LBUTTONDOWN : Common::EVENT_RBUTTONDOWN, - currentPosition); } } @@ -417,3 +417,16 @@ void TizenAppForm::OnTouchReleased(const Control &source, } } +void TizenAppForm::OnFormBackRequested(Form &source) { + logEntered(); + if (_state == kActiveState) { + invokeShortcut(); + } +} + +void TizenAppForm::OnFormMenuRequested(Form &source) { + logEntered(); + if (_state == kActiveState) { + setShortcut(); + } +} diff --git a/backends/platform/tizen/form.h b/backends/platform/tizen/form.h index 64c447d409..e419c14d24 100644 --- a/backends/platform/tizen/form.h +++ b/backends/platform/tizen/form.h @@ -29,6 +29,8 @@ #include <FBase.h> #include <FUiITouchEventListener.h> #include <FUiITextEventListener.h> +#include <FUiCtrlIFormBackEventListener.h> +#include <FUiCtrlIFormMenuEventListener.h> #include "config.h" #include "common/scummsys.h" @@ -40,6 +42,7 @@ using namespace Tizen::Ui; using namespace Tizen::Graphics; using namespace Tizen::Base::Runtime; +using namespace Tizen::Ui::Controls; // // TizenAppForm @@ -48,7 +51,9 @@ class TizenAppForm : public Controls::Form, public IRunnable, public IOrientationEventListener, - public ITouchEventListener { + public ITouchEventListener, + public IFormBackEventListener, + public IFormMenuEventListener { public: TizenAppForm(); @@ -89,6 +94,8 @@ private: void OnTouchReleased(const Control &source, const Point ¤tPosition, const TouchEventInfo &touchInfo); + void OnFormBackRequested(Form &source); + void OnFormMenuRequested(Form &source); void pushEvent(Common::EventType type, const Point ¤tPosition); void terminate(); diff --git a/backends/platform/tizen/fs.cpp b/backends/platform/tizen/fs.cpp index f8b32f4239..8145cd5638 100644 --- a/backends/platform/tizen/fs.cpp +++ b/backends/platform/tizen/fs.cpp @@ -339,7 +339,6 @@ bool TizenFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, boo if (_isVirtualDir && mode != Common::FSNode::kListFilesOnly && _path == "/") { // present well known TIZEN file system areas myList.push_back(new TizenFilesystemNode(kData)); - myList.push_back(new TizenFilesystemNode(kResource)); myList.push_back(new TizenFilesystemNode(kSdCard)); myList.push_back(new TizenFilesystemNode(kMedia)); myList.push_back(new TizenFilesystemNode(kShared)); diff --git a/backends/platform/tizen/graphics.cpp b/backends/platform/tizen/graphics.cpp index bf255cd264..2cafb9f781 100644 --- a/backends/platform/tizen/graphics.cpp +++ b/backends/platform/tizen/graphics.cpp @@ -100,6 +100,8 @@ void TizenGraphicsManager::setFeatureState(OSystem::Feature f, bool enable) { } void TizenGraphicsManager::setReady() { + logEntered(); + _appForm->GetVisualElement()->SetShowState(true); _initState = false; } @@ -176,7 +178,9 @@ bool TizenGraphicsManager::loadEgl() { systemError("eglMakeCurrent() failed"); return false; } - + if (!_initState) { + _appForm->GetVisualElement()->SetShowState(true); + } logLeaving(); return true; } @@ -213,6 +217,7 @@ void TizenGraphicsManager::internUpdateScreen() { void TizenGraphicsManager::unloadGFXMode() { logEntered(); + _appForm->GetVisualElement()->SetShowState(false); if (_eglDisplay != EGL_NO_DISPLAY) { eglMakeCurrent(_eglDisplay, NULL, NULL, NULL); diff --git a/backends/platform/tizen/system.cpp b/backends/platform/tizen/system.cpp index 54d92146e5..3448dc1421 100644 --- a/backends/platform/tizen/system.cpp +++ b/backends/platform/tizen/system.cpp @@ -513,13 +513,15 @@ TizenAppForm *systemStart(Tizen::App::Application *app) { } if (E_SUCCESS != appForm->Construct() || - E_SUCCESS != appFrame->AddControl(*appForm)) { + E_SUCCESS != appFrame->AddControl(appForm)) { delete appForm; AppLog("Failed to construct appForm"); return NULL; } appFrame->SetCurrentForm(appForm); + appForm->GetVisualElement()->SetShowState(false); + logLeaving(); return appForm; } @@ -531,7 +533,7 @@ void systemError(const char *message) { AppLog("Fatal system error: %s", message); if (strspn(message, "Config file buggy:") > 0) { - Tizen::Io::File::Remove(DEFAULT_CONFIG_FILE); + Tizen::Io::File::Remove(App::GetInstance()->GetAppDataPath() + DEFAULT_CONFIG_FILE); Application::GetInstance()->SendUserEvent(USER_MESSAGE_EXIT_ERR_CONFIG, NULL); } else { ArrayList *args = new ArrayList(); diff --git a/devtools/create_mortdat/create_mortdat.cpp b/devtools/create_mortdat/create_mortdat.cpp index 00b9b1ce4a..5a491eea2f 100644 --- a/devtools/create_mortdat/create_mortdat.cpp +++ b/devtools/create_mortdat/create_mortdat.cpp @@ -204,16 +204,19 @@ void writeGameStrings() { /** * Write out the data for the English menu */ -void writeMenuBlock() { +void writeMenuData(const char *menuData, int languageId) { // Write out a section header to the output file and the menu data const char menuHeader[4] = { 'M', 'E', 'N', 'U' }; outputFile.write(menuHeader, 4); // Section Id - outputFile.writeWord(strlen(menuDataEn) / 8); // Section size + int size = strlen(menuData) / 8 + 1; // Language code + Menu data size + outputFile.writeWord(size); + + outputFile.writeByte(languageId); // Write each 8-characters block as a byte (one bit per character) // ' ' -> 0, anything else -> 1 byte value; int valueCpt = 0; - const char* str = menuDataEn; + const char* str = menuData; while (*str != 0) { if (*(str++) != ' ') value |= (1 << (7 - valueCpt)); @@ -226,6 +229,11 @@ void writeMenuBlock() { } } +void writeMenuBlock() { + writeMenuData(menuDataEn, 1); + writeMenuData(menuDataDe, 2); +} + void writeVerbNums(const int *verbs, int languageId) { // Write out a section header to the output file const char menuHeader[4] = { 'V', 'E', 'R', 'B' }; diff --git a/devtools/create_mortdat/create_mortdat.h b/devtools/create_mortdat/create_mortdat.h index 1ebbbe37e0..e5007ae653 100644 --- a/devtools/create_mortdat/create_mortdat.h +++ b/devtools/create_mortdat/create_mortdat.h @@ -24,7 +24,7 @@ */ #define VERSION_MAJOR 1 -#define VERSION_MINOR 1 +#define VERSION_MINOR 2 enum AccessMode { kFileReadMode = 1, diff --git a/devtools/create_mortdat/menudata.h b/devtools/create_mortdat/menudata.h index 6a5f8dcfad..ec8724135c 100644 --- a/devtools/create_mortdat/menudata.h +++ b/devtools/create_mortdat/menudata.h @@ -76,18 +76,68 @@ const char *menuDataEn = "@@@ @@@ @@@ @@@ " " "; -const int verbsFr[26] = { 0x301, 0x302, 0x303, 0x304, 0x305, 0x306, 0x307, 0x308, - 0x309, 0x30a, 0x30b, 0x30c, 0x30d, 0x30e, 0x30f, 0x310, - 0x311, 0x312, 0x313, 0x314, 0x315, 0x401, 0x402, 0x403, - 0x404, 0x405 }; +const char *menuDataDe = + " @@@ @@ " + " @ @ " + " @ @ @@ @ @@@@" + " @ @ @ @@@ @ @" + " @ @ @ @ @ @ @ " + " @ @ @ @ @ @ @ " + "@@@ @@@ @@ @@ @@ @@@@ " + " " + " @@@@@@ @ @@ " + " @ @ @ " + " @ @ @@ @@@@ @ " + " @@@@ @ @ @ @@@ " + " @ @ @ @ @ @ " + " @ @ @ @ @ @ @ " + "@@ @@ @@@ @@@ @@ @@" + " " + " @ @@ @ " + " @@@ @ @ " + " @ @ @ @@ @@@@ " + " @ @ @ @ @ " + " @@@@@ @@@ @ " + " @ @ @ @ @ " + "@@@ @@@ @@@ @@ @@ " + " " + " @@@ @@ " + " @ @ " + " @ @@@@ @ " + " @ @ @ @@@ " + " @ @ @ @ " + " @ @ @ @ @ " + " @@@ @@@ @@ @@ " + " " + " @@@@@@ " + " @ @ @@ " + " @ @ @@@ @ @@@ " + " @@@@ @ @ @@@@ @ @" + " @ @ @@@@@ @ @ @@@@ " + " @ @ @ @ @ @ " + "@@ @@ @@@ @@@ @@@ " + " " + " @@@@@ @ " + " @ @ @ " + " @ @ @@@@ @@@@ " + " @ @ @ @ @ " + " @ @ @ @ @ " + " @ @ @ @ @ " + "@@@@@ @@@@ @@ " + " "; + +const int verbsFr[26] = { 0x301, 0x302, 0x303, 0x304, 0x305, 0x306, 0x307, 0x308, + 0x309, 0x30a, 0x30b, 0x30c, 0x30d, 0x30e, 0x30f, 0x310, + 0x311, 0x312, 0x313, 0x314, 0x315, 0x401, 0x402, 0x403, + 0x404, 0x405 }; const int verbsEn[26] = { 0x301, 0x315, 0x305, 0x310, 0x309, 0x304, 0x302, 0x30f, - 0x306, 0x30d, 0x30e, 0x303, 0x30c, 0x30b, 0x313, 0x30a, - 0x311, 0x312, 0x307, 0x308, 0x314, 0x401, 0x405, 0x404, - 0x403, 0x402 }; + 0x306, 0x30d, 0x30e, 0x303, 0x30c, 0x30b, 0x313, 0x30a, + 0x311, 0x312, 0x307, 0x308, 0x314, 0x401, 0x405, 0x404, + 0x403, 0x402 }; -const int verbsDe[26] = { 0x30a, 0x310, 0x313, 0x301, 0x315, 0x308, 0x303, 0x306, - 0x30c, 0x311, 0x314, 0x309, 0x30b, 0x30f, 0x30e, 0x304, - 0x307, 0x30d, 0x312, 0x302, 0x305, 0x405, 0x402, 0x404, - 0x403, 0x401 }; +const int verbsDe[26] = { 0x304, 0x314, 0x307, 0x310, 0x315, 0x308, 0x311, 0x306, + 0x30c, 0x301, 0x30d, 0x309, 0x312, 0x30f, 0x30e, 0x302, + 0x30a, 0x313, 0x303, 0x30b, 0x305, 0x405, 0x402, 0x404, + 0x403, 0x401 }; #endif diff --git a/devtools/credits.pl b/devtools/credits.pl index 7d39730c63..45018a5633 100755 --- a/devtools/credits.pl +++ b/devtools/credits.pl @@ -638,6 +638,16 @@ begin_credits("Credits"); add_person("David Turner", "digitall", ""); end_section(); + begin_section("Mortevielle"); + add_person("Arnaud Boutonné", "Strangerke", ""); + add_person("Paul Gilbert", "dreammaster", ""); + end_section(); + + begin_section("Neverhood"); + add_person("Benjamin Haisch", "john_doe", ""); + add_person("Filippos Karapetis", "[md5]", ""); + end_section(); + begin_section("Parallaction"); add_person("", "peres", ""); end_section(); diff --git a/dists/engine-data/mort.dat b/dists/engine-data/mort.dat Binary files differindex ae9579ee13..0d6a44206d 100644 --- a/dists/engine-data/mort.dat +++ b/dists/engine-data/mort.dat diff --git a/engines/agi/detection_tables.h b/engines/agi/detection_tables.h index 0c2c3ed3be..f1bb079ffc 100644 --- a/engines/agi/detection_tables.h +++ b/engines/agi/detection_tables.h @@ -642,6 +642,7 @@ static const AGIGameDescription gameDescriptions[] = { FANMADE("AGI Piano (v1.0)", "8778b3d89eb93c1d50a70ef06ef10310"), FANMADE("AGI Quest (v1.46-TJ0)", "1cf1a5307c1a0a405f5039354f679814"), GAME("tetris", "", "7a874e2db2162e7a4ce31c9130248d8a", 0x2917, GID_FANMADE), + FANMADE("AGI Tetris (1998)", "1afcbc25bfafded2d5fb82de9da0bd9a"), FANMADE_V("AGI Trek (Demo)", "c02882b8a8245b629c91caf7eb78eafe", 0x2440), FANMADE_F("AGI256 Demo", "79261ac143b2e2773b2753674733b0d5", GF_AGI256), FANMADE_F("AGI256-2 Demo", "3cad9b3aff1467cebf0c5c5b110985c5", GF_AGI256_2), diff --git a/engines/drascula/actors.cpp b/engines/drascula/actors.cpp index 9d5d6550fa..e0983809fa 100644 --- a/engines/drascula/actors.cpp +++ b/engines/drascula/actors.cpp @@ -135,7 +135,7 @@ void DrasculaEngine::startWalking() { else characterMoved = 0; } - startTime = getTime(); + _startTime = getTime(); } void DrasculaEngine::moveCharacters() { @@ -239,7 +239,7 @@ void DrasculaEngine::moveCharacters() { factor_red[curY + curHeight], frontSurface, screenSurface); } } else if (characterMoved == 1) { - curPos[0] = _frameX[num_frame]; + curPos[0] = _frameX[_characterFrame]; curPos[1] = frame_y + DIF_MASK_HARE; curPos[2] = curX; curPos[3] = curY; @@ -369,13 +369,11 @@ void DrasculaEngine::quadrant_4() { } void DrasculaEngine::increaseFrameNum() { - timeDiff = getTime() - startTime; - - if (timeDiff >= 6) { - startTime = getTime(); - num_frame++; - if (num_frame == 6) - num_frame = 0; + if (getTime() - _startTime >= 6) { + _startTime = getTime(); + _characterFrame++; + if (_characterFrame == 6) + _characterFrame = 0; if (curDirection == kDirectionUp) { curX -= stepX; diff --git a/engines/drascula/animation.cpp b/engines/drascula/animation.cpp index 52cab6cd32..1145c8c3ff 100644 --- a/engines/drascula/animation.cpp +++ b/engines/drascula/animation.cpp @@ -426,7 +426,7 @@ void DrasculaEngine::animation_2_1() { if ((term_int == 1) || (getScan() == Common::KEYCODE_ESCAPE) || shouldQuit()) break; - roomNumber = 16; + _roomNumber = 16; if ((term_int == 1) || (getScan() == Common::KEYCODE_ESCAPE) || shouldQuit()) break; @@ -512,7 +512,7 @@ void DrasculaEngine::animation_2_1() { // room number to -1 for the same purpose // Also check animation_9_6(), where the same hack was used by // the original - roomNumber = -1; + _roomNumber = -1; if ((term_int == 1) || (getScan() == Common::KEYCODE_ESCAPE) || shouldQuit()) break; pause(8); @@ -734,7 +734,7 @@ void DrasculaEngine::animation_14_2() { void DrasculaEngine::asco() { loadPic(roomDisk, drawSurface3); - loadPic(roomNumber, bgSurface, HALF_PAL); + loadPic(_roomNumber, bgSurface, HALF_PAL); black(); updateRoom(); updateScreen(); @@ -1661,7 +1661,7 @@ void DrasculaEngine::animation_9_6() { // We set the room number to -1 for the same purpose. // Also check animation_2_1(), where the same hack was used // by the original - roomNumber = -2; + _roomNumber = -2; loadPic("nota2.alg", bgSurface, HALF_PAL); black(); trackProtagonist = 1; @@ -2176,9 +2176,9 @@ void DrasculaEngine::animation_5_4(){ void DrasculaEngine::animation_6_4() { debug(4, "animation_6_4()"); - int prevRoom = roomNumber; + int prevRoom = _roomNumber; - roomNumber = 26; + _roomNumber = 26; clearRoom(); loadPic(26, bgSurface, HALF_PAL); loadPic("aux26.alg", drawSurface3); @@ -2191,11 +2191,11 @@ void DrasculaEngine::animation_6_4() { updateScreen(); pause(40); talk_igor(26, kIgorFront); - roomNumber = prevRoom; + _roomNumber = prevRoom; clearRoom(); loadPic(96, frontSurface); loadPic(roomDisk, drawSurface3); - loadPic(roomNumber, bgSurface, HALF_PAL); + loadPic(_roomNumber, bgSurface, HALF_PAL); selectVerb(kVerbNone); updateRoom(); } @@ -2224,7 +2224,7 @@ void DrasculaEngine::activatePendulum() { flags[1] = 2; hare_se_ve = 0; - roomNumber = 102; + _roomNumber = 102; loadPic(102, bgSurface, HALF_PAL); loadPic("an_p1.alg", drawSurface3); loadPic("an_p2.alg", extraSurface); diff --git a/engines/drascula/console.cpp b/engines/drascula/console.cpp index 426b2ade67..c0d2748ec3 100644 --- a/engines/drascula/console.cpp +++ b/engines/drascula/console.cpp @@ -41,7 +41,7 @@ bool Console::Cmd_Room(int argc, const char **argv) { int roomNum = atoi(argv[1]); - _vm->loadedDifferentChapter = 0; + _vm->_loadedDifferentChapter = false; _vm->enterRoom(roomNum); _vm->selectVerb(kVerbNone); _vm->clearRoom(); diff --git a/engines/drascula/converse.cpp b/engines/drascula/converse.cpp index d045d683fc..95a5f7d87f 100644 --- a/engines/drascula/converse.cpp +++ b/engines/drascula/converse.cpp @@ -216,7 +216,7 @@ void DrasculaEngine::converse(int index) { phrase3_bottom = phrase2_bottom + 8 * print_abc_opc(phrase3, phrase2_bottom + 2, game3); phrase4_bottom = phrase3_bottom + 8 * print_abc_opc(phrase4, phrase3_bottom + 2, kDialogOptionUnselected); - if (mouseY > 0 && mouseY < phrase1_bottom) { + if (_mouseY > 0 && _mouseY < phrase1_bottom) { if (game1 == kDialogOptionClicked && _color != kColorWhite) color_abc(kColorWhite); else if (game1 != kDialogOptionClicked && _color != kColorLightGreen) @@ -224,13 +224,13 @@ void DrasculaEngine::converse(int index) { print_abc_opc(phrase1, 2, kDialogOptionSelected); - if (leftMouseButton == 1) { + if (_leftMouseButton == 1) { delay(100); game1 = kDialogOptionClicked; talk(phrase1, sound1); response(answer1); } - } else if (mouseY > phrase1_bottom && mouseY < phrase2_bottom) { + } else if (_mouseY > phrase1_bottom && _mouseY < phrase2_bottom) { if (game2 == kDialogOptionClicked && _color != kColorWhite) color_abc(kColorWhite); else if (game2 != kDialogOptionClicked && _color != kColorLightGreen) @@ -238,13 +238,13 @@ void DrasculaEngine::converse(int index) { print_abc_opc(phrase2, phrase1_bottom + 2, kDialogOptionSelected); - if (leftMouseButton == 1) { + if (_leftMouseButton == 1) { delay(100); game2 = kDialogOptionClicked; talk(phrase2, sound2); response(answer2); } - } else if (mouseY > phrase2_bottom && mouseY < phrase3_bottom) { + } else if (_mouseY > phrase2_bottom && _mouseY < phrase3_bottom) { if (game3 == kDialogOptionClicked && _color != kColorWhite) color_abc(kColorWhite); else if (game3 != kDialogOptionClicked && _color != kColorLightGreen) @@ -252,16 +252,16 @@ void DrasculaEngine::converse(int index) { print_abc_opc(phrase3, phrase2_bottom + 2, kDialogOptionSelected); - if (leftMouseButton == 1) { + if (_leftMouseButton == 1) { delay(100); game3 = kDialogOptionClicked; talk(phrase3, sound3); response(answer3); } - } else if (mouseY > phrase3_bottom && mouseY < phrase4_bottom) { + } else if (_mouseY > phrase3_bottom && _mouseY < phrase4_bottom) { print_abc_opc(phrase4, phrase3_bottom + 2, kDialogOptionSelected); - if (leftMouseButton == 1) { + if (_leftMouseButton == 1) { delay(100); talk(phrase4, sound4); breakOut = 1; diff --git a/engines/drascula/drascula.cpp b/engines/drascula/drascula.cpp index 804881cf9a..cde00baa32 100644 --- a/engines/drascula/drascula.cpp +++ b/engines/drascula/drascula.cpp @@ -91,10 +91,10 @@ DrasculaEngine::DrasculaEngine(OSystem *syst, const DrasculaGameDescription *gam _color = 0; blinking = 0; - mouseX = 0; - mouseY = 0; - leftMouseButton = 0; - rightMouseButton = 0; + _mouseX = 0; + _mouseY = 0; + _leftMouseButton = 0; + _rightMouseButton = 0; *textName = 0; _rnd = new Common::RandomSource("drascula"); @@ -197,7 +197,7 @@ Common::Error DrasculaEngine::run() { syncSoundSettings(); currentChapter = 1; // values from 1 to 6 will start each part of game - loadedDifferentChapter = 0; + _loadedDifferentChapter = false; setTotalPlayTime(0); // Check if a save is loaded from the launcher @@ -218,7 +218,7 @@ Common::Error DrasculaEngine::run() { curX = -1; characterMoved = 0; trackProtagonist = 3; - num_frame = 0; + _characterFrame = 0; hare_se_ve = 1; checkFlags = 1; doBreak = 0; @@ -231,9 +231,6 @@ Common::Error DrasculaEngine::run() { curWidth = CHARACTER_WIDTH; feetHeight = FEET_HEIGHT; - talkHeight = TALK_HEIGHT; - talkWidth = TALK_WIDTH; - hasAnswer = 0; savedTime = 0; breakOut = 0; @@ -246,7 +243,7 @@ Common::Error DrasculaEngine::run() { globalSpeed = 0; curExcuseLook = 0; curExcuseAction = 0; - roomNumber = 0; + _roomNumber = 0; for (i = 0; i < 8; i++) actorFrames[i] = 0; @@ -268,7 +265,7 @@ Common::Error DrasculaEngine::run() { loadPic("aux13.alg", bgSurface, COMPLETE_PAL); loadPic(96, frontSurface); } else if (currentChapter == 4) { - if (loadedDifferentChapter == 0) + if (!_loadedDifferentChapter) animation_castle(); loadPic(96, frontSurface); clearRoom(); @@ -330,7 +327,7 @@ void DrasculaEngine::endChapter() { bool DrasculaEngine::runCurrentChapter() { int n; - rightMouseButton = 0; + _rightMouseButton = 0; previousMusic = -1; @@ -360,14 +357,14 @@ bool DrasculaEngine::runCurrentChapter() { if (currentChapter == 1) { pickObject(28); - if (loadedDifferentChapter == 0) + if (!_loadedDifferentChapter) animation_1_1(); selectVerb(kVerbNone); loadPic("2aux62.alg", drawSurface2); trackProtagonist = 1; objExit = 104; - if (loadedDifferentChapter != 0) { + if (_loadedDifferentChapter) { if (!loadGame(_currentSaveSlot)) { return true; } @@ -383,7 +380,7 @@ bool DrasculaEngine::runCurrentChapter() { addObject(kItemPhone); trackProtagonist = 3; objExit = 162; - if (loadedDifferentChapter == 0) + if (!_loadedDifferentChapter) enterRoom(14); else { if (!loadGame(_currentSaveSlot)) { @@ -401,7 +398,7 @@ bool DrasculaEngine::runCurrentChapter() { flags[1] = 1; trackProtagonist = 1; objExit = 99; - if (loadedDifferentChapter == 0) + if (!_loadedDifferentChapter) enterRoom(20); else { if (!loadGame(_currentSaveSlot)) { @@ -415,7 +412,7 @@ bool DrasculaEngine::runCurrentChapter() { addObject(kItemReefer2); addObject(kItemOneCoin2); objExit = 100; - if (loadedDifferentChapter == 0) { + if (!_loadedDifferentChapter) { enterRoom(21); trackProtagonist = 0; curX = 235; @@ -437,7 +434,7 @@ bool DrasculaEngine::runCurrentChapter() { addObject(20); trackProtagonist = 1; objExit = 100; - if (loadedDifferentChapter == 0) { + if (!_loadedDifferentChapter) { enterRoom(45); } else { if (!loadGame(_currentSaveSlot)) { @@ -450,7 +447,7 @@ bool DrasculaEngine::runCurrentChapter() { trackProtagonist = 1; objExit = 104; - if (loadedDifferentChapter == 0) { + if (!_loadedDifferentChapter) { enterRoom(58); animation_1_6(); } else { @@ -480,13 +477,13 @@ bool DrasculaEngine::runCurrentChapter() { // lead to an incorrect setting of the protagonist's tracking flag (above). This // made the character start walking off screen, as his actual position was // different than the displayed one - if (roomNumber == 3 && (curX == 279) && (curY + curHeight == 101)) { + if (_roomNumber == 3 && (curX == 279) && (curY + curHeight == 101)) { gotoObject(178, 121); gotoObject(169, 135); - } else if (roomNumber == 14 && (curX == 214) && (curY + curHeight == 121)) { + } else if (_roomNumber == 14 && (curX == 214) && (curY + curHeight == 121)) { walkToObject = 1; gotoObject(190, 130); - } else if (roomNumber == 14 && (curX == 246) && (curY + curHeight == 112)) { + } else if (_roomNumber == 14 && (curX == 246) && (curY + curHeight == 112)) { walkToObject = 1; gotoObject(190, 130); } @@ -518,12 +515,12 @@ bool DrasculaEngine::runCurrentChapter() { checkObjects(); #ifdef _WIN32_WCE - if (rightMouseButton) { + if (_rightMouseButton) { if (_menuScreen) { #else - if (rightMouseButton == 1 && _menuScreen) { + if (_rightMouseButton == 1 && _menuScreen) { #endif - rightMouseButton = 0; + _rightMouseButton = 0; delay(100); if (currentChapter == 2) { loadPic(menuBackground, cursorSurface); @@ -549,10 +546,10 @@ bool DrasculaEngine::runCurrentChapter() { // Do not show the inventory screen in chapter 5, if the right mouse button is clicked // while the plug (object 16) is held // Fixes bug #2059621 - "DRASCULA: Plug bug" - if (rightMouseButton == 1 && !_menuScreen && + if (_rightMouseButton == 1 && !_menuScreen && !(currentChapter == 5 && pickedObject == 16)) { #endif - rightMouseButton = 0; + _rightMouseButton = 0; delay(100); characterMoved = 0; if (trackProtagonist == 2) @@ -580,19 +577,19 @@ bool DrasculaEngine::runCurrentChapter() { } #endif - if (leftMouseButton == 1 && _menuBar) { + if (_leftMouseButton == 1 && _menuBar) { delay(100); selectVerbFromBar(); - } else if (leftMouseButton == 1 && takeObject == 0) { + } else if (_leftMouseButton == 1 && takeObject == 0) { delay(100); if (verify1()) return true; - } else if (leftMouseButton == 1 && takeObject == 1) { + } else if (_leftMouseButton == 1 && takeObject == 1) { if (verify2()) return true; } - _menuBar = (mouseY < 24 && !_menuScreen) ? true : false; + _menuBar = (_mouseY < 24 && !_menuScreen) ? true : false; Common::KeyCode key = getScan(); if (key == Common::KEYCODE_F1 && !_menuScreen) { @@ -644,11 +641,11 @@ bool DrasculaEngine::runCurrentChapter() { } else if (key == Common::KEYCODE_TILDE || key == Common::KEYCODE_BACKQUOTE) { _console->attach(); _console->onFrame(); - } else if (currentChapter == 6 && key == Common::KEYCODE_0 && roomNumber == 61) { + } else if (currentChapter == 6 && key == Common::KEYCODE_0 && _roomNumber == 61) { loadPic("alcbar.alg", bgSurface, 255); } - if (leftMouseButton != 0 || rightMouseButton != 0 || key != 0) + if (_leftMouseButton != 0 || _rightMouseButton != 0 || key != 0) framesWithoutAction = 0; if (framesWithoutAction == 15000) { @@ -670,8 +667,8 @@ bool DrasculaEngine::verify1() { removeObject(); else { for (l = 0; l < numRoomObjs; l++) { - if (mouseX >= _objectX1[l] && mouseY >= _objectY1[l] - && mouseX <= _objectX2[l] && mouseY <= _objectY2[l] && doBreak == 0) { + if (_mouseX >= _objectX1[l] && _mouseY >= _objectY1[l] + && _mouseX <= _objectX2[l] && _mouseY <= _objectY2[l] && doBreak == 0) { if (exitRoom(l)) return true; if (doBreak == 1) @@ -679,13 +676,13 @@ bool DrasculaEngine::verify1() { } } - if (mouseX > curX && mouseY > curY - && mouseX < curX + curWidth && mouseY < curY + curHeight) + if (_mouseX > curX && _mouseY > curY + && _mouseX < curX + curWidth && _mouseY < curY + curHeight) doBreak = 1; for (l = 0; l < numRoomObjs; l++) { - if (mouseX > _objectX1[l] && mouseY > _objectY1[l] - && mouseX < _objectX2[l] && mouseY < _objectY2[l] && doBreak == 0) { + if (_mouseX > _objectX1[l] && _mouseY > _objectY1[l] + && _mouseX < _objectX2[l] && _mouseY < _objectY2[l] && doBreak == 0) { roomX = roomObjX[l]; roomY = roomObjY[l]; trackFinal = trackObj[l]; @@ -696,8 +693,8 @@ bool DrasculaEngine::verify1() { } if (doBreak == 0) { - roomX = CLIP(mouseX, floorX1, floorX2); - roomY = CLIP(mouseY, floorY1 + feetHeight, floorY2); + roomX = CLIP(_mouseX, floorX1, floorX2); + roomY = CLIP(_mouseY, floorY1 + feetHeight, floorY2); startWalking(); } doBreak = 0; @@ -718,8 +715,8 @@ bool DrasculaEngine::verify2() { return true; } else { for (l = 0; l < numRoomObjs; l++) { - if (mouseX > _objectX1[l] && mouseY > _objectY1[l] - && mouseX < _objectX2[l] && mouseY < _objectY2[l] && visible[l] == 1) { + if (_mouseX > _objectX1[l] && _mouseY > _objectY1[l] + && _mouseX < _objectX2[l] && _mouseY < _objectY2[l] && visible[l] == 1) { trackFinal = trackObj[l]; walkToObject = 1; gotoObject(roomObjX[l], roomObjY[l]); @@ -779,20 +776,20 @@ void DrasculaEngine::updateEvents() { case Common::EVENT_KEYUP: break; case Common::EVENT_MOUSEMOVE: - mouseX = event.mouse.x; - mouseY = event.mouse.y; + _mouseX = event.mouse.x; + _mouseY = event.mouse.y; break; case Common::EVENT_LBUTTONDOWN: - leftMouseButton = 1; + _leftMouseButton = 1; break; case Common::EVENT_LBUTTONUP: - leftMouseButton = 0; + _leftMouseButton = 0; break; case Common::EVENT_RBUTTONDOWN: // We changed semantic and react only on button up event break; case Common::EVENT_RBUTTONUP: - rightMouseButton = 1; + _rightMouseButton = 1; break; default: break; diff --git a/engines/drascula/drascula.h b/engines/drascula/drascula.h index c4d1c4c761..944191b5fb 100644 --- a/engines/drascula/drascula.h +++ b/engines/drascula/drascula.h @@ -403,7 +403,7 @@ public: int actorFrames[8]; int previousMusic, roomMusic; - int roomNumber; + int _roomNumber; char roomDisk[20]; char currentData[20]; int numRoomObjs; @@ -428,18 +428,17 @@ public: int flags[NUM_FLAGS]; int frame_y; - int curX, curY, characterMoved, curDirection, trackProtagonist, num_frame; + int curX, curY, characterMoved, curDirection, trackProtagonist, _characterFrame; int hare_se_ve; // TODO: what is this for? int roomX, roomY, checkFlags; int doBreak; int stepX, stepY; int curHeight, curWidth, feetHeight; - int talkHeight, talkWidth; int floorX1, floorY1, floorX2, floorY2; int lowerLimit, upperLimit; int trackFinal, walkToObject; int objExit; - int timeDiff, startTime; + int _startTime; int hasAnswer; int savedTime; int breakOut; @@ -454,14 +453,11 @@ public: int framesWithoutAction; int term_int; int currentChapter; - int loadedDifferentChapter; + bool _loadedDifferentChapter; int _currentSaveSlot; int _color; int musicStopped; - int mouseX; - int mouseY; - int leftMouseButton; - int rightMouseButton; + int _mouseX, _mouseY, _leftMouseButton, _rightMouseButton; Common::KeyState _keyBuffer[KEYBUFSIZE]; int _keyBufferHead; diff --git a/engines/drascula/graphics.cpp b/engines/drascula/graphics.cpp index 31d03a94a7..b28de669b6 100644 --- a/engines/drascula/graphics.cpp +++ b/engines/drascula/graphics.cpp @@ -82,7 +82,7 @@ void DrasculaEngine::moveCursor() { } else if (!_menuScreen && _color != kColorLightGreen) color_abc(kColorLightGreen); if (_hasName && !_menuScreen) - centerText(textName, mouseX, mouseY); + centerText(textName, _mouseX, _mouseY); if (_menuScreen) showMenu(); else if (_menuBar) @@ -417,8 +417,8 @@ void DrasculaEngine::screenSaver() { delete stream; updateEvents(); - xr = mouseX; - yr = mouseY; + xr = _mouseX; + yr = _mouseY; while (!shouldQuit()) { // efecto(bgSurface); @@ -480,18 +480,18 @@ void DrasculaEngine::screenSaver() { // end of efecto() updateEvents(); - if (rightMouseButton == 1 || leftMouseButton == 1) + if (_rightMouseButton == 1 || _leftMouseButton == 1) break; - if (mouseX != xr) + if (_mouseX != xr) break; - if (mouseY != yr) + if (_mouseY != yr) break; } // fin_ghost(); free(copia); free(ghost); - loadPic(roomNumber, bgSurface, HALF_PAL); + loadPic(_roomNumber, bgSurface, HALF_PAL); showCursor(); } diff --git a/engines/drascula/interface.cpp b/engines/drascula/interface.cpp index fca8040f59..f0b6d12027 100644 --- a/engines/drascula/interface.cpp +++ b/engines/drascula/interface.cpp @@ -51,7 +51,7 @@ bool DrasculaEngine::isCursorVisible() { void DrasculaEngine::selectVerbFromBar() { for (int n = 0; n < 7; n++) { - if (mouseX > _verbBarX[n] && mouseX < _verbBarX[n + 1] && n > 0) { + if (_mouseX > _verbBarX[n] && _mouseX < _verbBarX[n + 1] && n > 0) { selectVerb(n); return; } @@ -143,7 +143,7 @@ void DrasculaEngine::clearMenu() { int n, verbActivated = 1; for (n = 0; n < 7; n++) { - if (mouseX > _verbBarX[n] && mouseX < _verbBarX[n + 1]) + if (_mouseX > _verbBarX[n] && _mouseX < _verbBarX[n + 1]) verbActivated = 0; copyRect(OBJWIDTH * n, OBJHEIGHT * verbActivated, _verbBarX[n], 2, OBJWIDTH, OBJHEIGHT, cursorSurface, screenSurface); @@ -165,8 +165,8 @@ void DrasculaEngine::showMap() { _hasName = false; for (int l = 0; l < numRoomObjs; l++) { - if (mouseX > _objectX1[l] && mouseY > _objectY1[l] - && mouseX < _objectX2[l] && mouseY < _objectY2[l] + if (_mouseX > _objectX1[l] && _mouseY > _objectY1[l] + && _mouseX < _objectX2[l] && _mouseY < _objectY2[l] && visible[l] == 1) { strcpy(textName, objName[l]); _hasName = true; diff --git a/engines/drascula/objects.cpp b/engines/drascula/objects.cpp index f9f68c3317..35dfd3162a 100644 --- a/engines/drascula/objects.cpp +++ b/engines/drascula/objects.cpp @@ -91,8 +91,8 @@ void DrasculaEngine::gotoObject(int pointX, int pointY) { updateRoom(); updateScreen(); - // roomNumber -2 is end credits. Do not show cursor there - if (cursorVisible && roomNumber != -2) + // _roomNumber -2 is end credits. Do not show cursor there + if (cursorVisible && _roomNumber != -2) showCursor(); } @@ -100,8 +100,8 @@ void DrasculaEngine::checkObjects() { int l, veo = 0; for (l = 0; l < numRoomObjs; l++) { - if (mouseX > _objectX1[l] && mouseY > _objectY1[l] - && mouseX < _objectX2[l] && mouseY < _objectY2[l] + if (_mouseX > _objectX1[l] && _mouseY > _objectY1[l] + && _mouseX < _objectX2[l] && _mouseY < _objectY2[l] && visible[l] == 1 && isDoor[l] == 0) { strcpy(textName, objName[l]); _hasName = true; @@ -109,8 +109,8 @@ void DrasculaEngine::checkObjects() { } } - if (mouseX > curX + 2 && mouseY > curY + 2 - && mouseX < curX + curWidth - 2 && mouseY < curY + curHeight - 2) { + if (_mouseX > curX + 2 && _mouseY > curY + 2 + && _mouseX < curX + curWidth - 2 && _mouseY < curY + curHeight - 2) { if (currentChapter == 2 || veo == 0) { strcpy(textName, "hacker"); _hasName = true; @@ -223,9 +223,9 @@ int DrasculaEngine::whichObject() { int n; for (n = 1; n < ARRAYSIZE(inventoryObjects); n++) { - if (mouseX > _itemLocations[n].x && mouseY > _itemLocations[n].y && - mouseX < _itemLocations[n].x + OBJWIDTH && - mouseY < _itemLocations[n].y + OBJHEIGHT) { + if (_mouseX > _itemLocations[n].x && _mouseY > _itemLocations[n].y && + _mouseX < _itemLocations[n].x + OBJWIDTH && + _mouseY < _itemLocations[n].y + OBJHEIGHT) { return n; } } @@ -237,69 +237,69 @@ void DrasculaEngine::updateVisible() { if (currentChapter == 1) { // nothing } else if (currentChapter == 2) { - if (roomNumber == 2 && flags[40] == 0) + if (_roomNumber == 2 && flags[40] == 0) visible[3] = 0; - else if (roomNumber == 3 && flags[3] == 1) + else if (_roomNumber == 3 && flags[3] == 1) visible[8] = 0; - else if (roomNumber == 6 && flags[1] == 1 && flags[10] == 0) { + else if (_roomNumber == 6 && flags[1] == 1 && flags[10] == 0) { visible[2] = 0; visible[4] = 1; - } else if (roomNumber == 7 && flags[35] == 1) + } else if (_roomNumber == 7 && flags[35] == 1) visible[3] = 0; - else if (roomNumber == 14 && flags[5] == 1) + else if (_roomNumber == 14 && flags[5] == 1) visible[4] = 0; - else if (roomNumber == 18 && flags[28] == 1) + else if (_roomNumber == 18 && flags[28] == 1) visible[2] = 0; } else if (currentChapter == 3) { // nothing } else if (currentChapter == 4) { - if (roomNumber == 23 && flags[0] == 0 && flags[11] == 0) + if (_roomNumber == 23 && flags[0] == 0 && flags[11] == 0) visible[2] = 1; - if (roomNumber == 23 && flags[0] == 1 && flags[11] == 0) + if (_roomNumber == 23 && flags[0] == 1 && flags[11] == 0) visible[2] = 0; - if (roomNumber == 21 && flags[10] == 1) + if (_roomNumber == 21 && flags[10] == 1) visible[2] = 0; - if (roomNumber == 22 && flags[26] == 1) { + if (_roomNumber == 22 && flags[26] == 1) { visible[2] = 0; visible[1] = 1; } - if (roomNumber == 22 && flags[27] == 1) + if (_roomNumber == 22 && flags[27] == 1) visible[3] = 0; - if (roomNumber == 26 && flags[21] == 0) + if (_roomNumber == 26 && flags[21] == 0) strcpy(objName[2], _textmisc[0]); - if (roomNumber == 26 && flags[18] == 1) + if (_roomNumber == 26 && flags[18] == 1) visible[2] = 0; - if (roomNumber == 26 && flags[12] == 1) + if (_roomNumber == 26 && flags[12] == 1) visible[1] = 0; - if (roomNumber == 35 && flags[14] == 1) + if (_roomNumber == 35 && flags[14] == 1) visible[2] = 0; - if (roomNumber == 35 && flags[17] == 1) + if (_roomNumber == 35 && flags[17] == 1) visible[3] = 1; - if (roomNumber == 35 && flags[15] == 1) + if (_roomNumber == 35 && flags[15] == 1) visible[1] = 0; } else if (currentChapter == 5) { - if (roomNumber == 49 && flags[6] == 1) + if (_roomNumber == 49 && flags[6] == 1) visible[2] = 0; - if (roomNumber == 49 && flags[6] == 0) + if (_roomNumber == 49 && flags[6] == 0) visible[1] = 0; - if (roomNumber == 49 && flags[6] == 1) + if (_roomNumber == 49 && flags[6] == 1) visible[1] = 1; - if (roomNumber == 45 && flags[6] == 1) + if (_roomNumber == 45 && flags[6] == 1) visible[3] = 1; - if (roomNumber == 53 && flags[2] == 1) + if (_roomNumber == 53 && flags[2] == 1) visible[3] = 0; - if (roomNumber == 54 && flags[13] == 1) + if (_roomNumber == 54 && flags[13] == 1) visible[3] = 0; - if (roomNumber == 55 && flags[8] == 1) + if (_roomNumber == 55 && flags[8] == 1) visible[1] = 0; } else if (currentChapter == 6) { - if (roomNumber == 58 && flags[8] == 0) + if (_roomNumber == 58 && flags[8] == 0) isDoor[1] = 0; - if (roomNumber == 58 && flags[8] == 1) + if (_roomNumber == 58 && flags[8] == 1) isDoor[1] = 1; - if (roomNumber == 59) + if (_roomNumber == 59) isDoor[1] = 0; - if (roomNumber == 60) { + if (_roomNumber == 60) { trackDrascula = 0; drasculaX = 155; drasculaY = 69; diff --git a/engines/drascula/rooms.cpp b/engines/drascula/rooms.cpp index 9f707eaa07..25f3da0080 100644 --- a/engines/drascula/rooms.cpp +++ b/engines/drascula/rooms.cpp @@ -1087,7 +1087,7 @@ bool DrasculaEngine::room_102(int fl) { void DrasculaEngine::updateRefresh() { // Check generic updaters for (int i = 0; i < _roomUpdatesSize; i++) { - if (_roomUpdates[i].roomNum == roomNumber) { + if (_roomUpdates[i].roomNum == _roomNumber) { if (_roomUpdates[i].flag < 0 || flags[_roomUpdates[i].flag] == _roomUpdates[i].flagValue) { if (_roomUpdates[i].type == 0) { @@ -1107,25 +1107,25 @@ void DrasculaEngine::updateRefresh() { // Call room-specific updater char rm[20]; - sprintf(rm, "update_%d", roomNumber); + sprintf(rm, "update_%d", _roomNumber); for (uint i = 0; i < _roomHandlers->roomUpdaters.size(); i++) { if (!strcmp(rm, _roomHandlers->roomUpdaters[i]->desc)) { - debug(8, "Calling room updater %d", roomNumber); + debug(8, "Calling room updater %d", _roomNumber); (this->*(_roomHandlers->roomUpdaters[i]->proc))(); break; } } - if (roomNumber == 10) + if (_roomNumber == 10) showMap(); - else if (roomNumber == 45) + else if (_roomNumber == 45) showMap(); } void DrasculaEngine::updateRefresh_pre() { // Check generic preupdaters for (int i = 0; i < _roomPreUpdatesSize; i++) { - if (_roomPreUpdates[i].roomNum == roomNumber) { + if (_roomPreUpdates[i].roomNum == _roomNumber) { if (_roomPreUpdates[i].flag < 0 || flags[_roomPreUpdates[i].flag] == _roomPreUpdates[i].flagValue) { if (_roomPreUpdates[i].type == 0) { @@ -1145,16 +1145,16 @@ void DrasculaEngine::updateRefresh_pre() { // Call room-specific preupdater char rm[20]; - sprintf(rm, "update_%d_pre", roomNumber); + sprintf(rm, "update_%d_pre", _roomNumber); for (uint i = 0; i < _roomHandlers->roomPreupdaters.size(); i++) { if (!strcmp(rm, _roomHandlers->roomPreupdaters[i]->desc)) { - debug(8, "Calling room preupdater %d", roomNumber); + debug(8, "Calling room preupdater %d", _roomNumber); (this->*(_roomHandlers->roomPreupdaters[i]->proc))(); break; } } - if (currentChapter == 1 && roomNumber == 16) + if (currentChapter == 1 && _roomNumber == 16) placeBJ(); } @@ -1577,12 +1577,12 @@ bool DrasculaEngine::checkAction(int fl) { hasAnswer = 0; } else if (currentChapter == 2) { // Note: the original check was strcmp(num_room, "18.alg") - if (pickedObject == 11 && fl == 50 && flags[22] == 0 && roomNumber != 18) + if (pickedObject == 11 && fl == 50 && flags[22] == 0 && _roomNumber != 18) talk(315); else hasAnswer = 0; } else if (currentChapter == 3) { - if (roomNumber == 13) { + if (_roomNumber == 13) { if (room(13, fl)) { showCursor(); return true; @@ -1590,13 +1590,13 @@ bool DrasculaEngine::checkAction(int fl) { } else hasAnswer = 0; } else if (currentChapter == 4) { - if (roomNumber == 28) + if (_roomNumber == 28) talk(178); else if (pickedObject == 8 && fl == 50 && flags[18] == 0) talk(481); else if (pickedObject == 12 && fl == 50 && flags[18] == 0) talk(487); - else if (roomNumber == 21) { + else if (_roomNumber == 21) { if (room(21, fl)) { showCursor(); return true; @@ -1604,7 +1604,7 @@ bool DrasculaEngine::checkAction(int fl) { } else hasAnswer = 0; } else if (currentChapter == 5) { - if (roomNumber == 56) { + if (_roomNumber == 56) { if (room(56, fl)) { showCursor(); return true; @@ -1616,9 +1616,9 @@ bool DrasculaEngine::checkAction(int fl) { talk(308); else if (pickedObject == kVerbLook && fl == 50 && flags[0] == 0) talk(310); - else if (roomNumber == 102) + else if (_roomNumber == 102) room(102, fl); - else if (roomNumber == 60) { + else if (_roomNumber == 60) { if (room(60, fl)) { showCursor(); return true; @@ -1632,7 +1632,7 @@ bool DrasculaEngine::checkAction(int fl) { if (hasAnswer == 0) { hasAnswer = 1; - room(roomNumber, fl); + room(_roomNumber, fl); } if (hasAnswer == 0 && (_hasName || _menuScreen)) @@ -1684,7 +1684,7 @@ void DrasculaEngine::enterRoom(int roomIndex) { TextResourceParser p(stream, DisposeAfterUse::YES); - p.parseInt(roomNumber); + p.parseInt(_roomNumber); p.parseInt(roomMusic); p.parseString(roomDisk); p.parseInt(palLevel); @@ -1778,7 +1778,7 @@ void DrasculaEngine::enterRoom(int roomIndex) { } loadPic(roomDisk, drawSurface3); - loadPic(roomNumber, bgSurface, HALF_PAL); + loadPic(_roomNumber, bgSurface, HALF_PAL); copyBackground(0, 171, 0, 0, OBJWIDTH, OBJHEIGHT, backSurface, drawSurface3); @@ -1808,14 +1808,14 @@ void DrasculaEngine::enterRoom(int roomIndex) { } } - if (roomNumber == 24) { + if (_roomNumber == 24) { for (l = floorY1 - 1; l > 74; l--) { factor_red[l] = (int)(upperLimit - pequegnez); pequegnez = pequegnez + chiquez; } } - if (currentChapter == 5 && roomNumber == 54) { + if (currentChapter == 5 && _roomNumber == 54) { for (l = floorY1 - 1; l > 84; l--) { factor_red[l] = (int)(upperLimit - pequegnez); pequegnez = pequegnez + chiquez; @@ -1853,13 +1853,13 @@ void DrasculaEngine::enterRoom(int roomIndex) { isDoor[7] = 0; if (currentChapter == 2) { - if (roomNumber == 14 && flags[39] == 1) + if (_roomNumber == 14 && flags[39] == 1) roomMusic = 16; - else if (roomNumber == 15 && flags[39] == 1) + else if (_roomNumber == 15 && flags[39] == 1) roomMusic = 16; - if (roomNumber == 14 && flags[5] == 1) + if (_roomNumber == 14 && flags[5] == 1) roomMusic = 0; - else if (roomNumber == 15 && flags[5] == 1) + else if (_roomNumber == 15 && flags[5] == 1) roomMusic = 0; if (previousMusic != roomMusic && roomMusic != 0) @@ -1872,21 +1872,21 @@ void DrasculaEngine::enterRoom(int roomIndex) { } if (currentChapter == 2) { - if (roomNumber == 9 || roomNumber == 2 || roomNumber == 14 || roomNumber == 18) + if (_roomNumber == 9 || _roomNumber == 2 || _roomNumber == 14 || _roomNumber == 18) savedTime = getTime(); } if (currentChapter == 4) { - if (roomNumber == 26) + if (_roomNumber == 26) savedTime = getTime(); } - if (currentChapter == 4 && roomNumber == 24 && flags[29] == 1) + if (currentChapter == 4 && _roomNumber == 24 && flags[29] == 1) animation_7_4(); if (currentChapter == 5) { - if (roomNumber == 45) + if (_roomNumber == 45) hare_se_ve = 0; - if (roomNumber == 49 && flags[7] == 0) { + if (_roomNumber == 49 && flags[7] == 0) { playTalkSequence(4); // sequence 4, chapter 5 } } diff --git a/engines/drascula/saveload.cpp b/engines/drascula/saveload.cpp index 996c9d3f03..61d6f0b4af 100644 --- a/engines/drascula/saveload.cpp +++ b/engines/drascula/saveload.cpp @@ -263,7 +263,7 @@ bool DrasculaEngine::loadGame(int slot) { if (savedChapter != currentChapter) { _currentSaveSlot = slot; currentChapter = savedChapter - 1; - loadedDifferentChapter = 1; + _loadedDifferentChapter = true; delete in; return false; } @@ -283,7 +283,7 @@ bool DrasculaEngine::loadGame(int slot) { takeObject = in->readSint32LE(); pickedObject = in->readSint32LE(); - loadedDifferentChapter = 0; + _loadedDifferentChapter = false; if (!sscanf(currentData, "%d.ald", &roomNum)) { error("Bad save format"); } @@ -383,10 +383,10 @@ bool DrasculaEngine::saveLoadScreen() { updateScreen(); updateEvents(); - if (leftMouseButton == 1) { + if (_leftMouseButton == 1) { // Check if the user has clicked on a save slot for (n = 0; n < NUM_SAVES; n++) { - if (mouseX > 115 && mouseY > 27 + (9 * n) && mouseX < 115 + 175 && mouseY < 27 + 10 + (9 * n)) { + if (_mouseX > 115 && _mouseY > 27 + (9 * n) && _mouseX < 115 + 175 && _mouseY < 27 + 10 + (9 * n)) { selectedSlot = n; selectedName = _saveNames[selectedSlot]; if (selectedName.empty()) { @@ -399,14 +399,14 @@ bool DrasculaEngine::saveLoadScreen() { } // Check if the user has clicked in the text area above the save slots - if (mouseX > 117 && mouseY > 15 && mouseX < 295 && mouseY < 24 && !selectedName.empty()) { + if (_mouseX > 117 && _mouseY > 15 && _mouseX < 295 && _mouseY < 24 && !selectedName.empty()) { selectedName = enterName(selectedName); if (!selectedName.empty()) _saveNames[selectedSlot] = selectedName; // update save name } // Check if the user has clicked a button - if (mouseX > 208 && mouseY > 123 && mouseX < 282 && mouseY < 149) { + if (_mouseX > 208 && _mouseY > 123 && _mouseX < 282 && _mouseY < 149) { // "Save" button if (selectedName.empty()) { print_abc("Please select a slot", 117, 15); @@ -415,14 +415,14 @@ bool DrasculaEngine::saveLoadScreen() { } else { selectVerb(kVerbNone); clearRoom(); - loadPic(roomNumber, bgSurface, HALF_PAL); + loadPic(_roomNumber, bgSurface, HALF_PAL); updateRoom(); updateScreen(); saveGame(selectedSlot + 1, _saveNames[selectedSlot]); return true; } - } else if (mouseX > 125 && mouseY > 123 && mouseX < 199 && mouseY < 149) { + } else if (_mouseX > 125 && _mouseY > 123 && _mouseX < 199 && _mouseY < 149) { // "Load" button if (selectedName.empty()) { print_abc("Please select a slot", 117, 15); @@ -431,19 +431,19 @@ bool DrasculaEngine::saveLoadScreen() { } else { return loadGame(selectedSlot + 1); } - } else if (mouseX > 168 && mouseY > 154 && mouseX < 242 && mouseY < 180) { + } else if (_mouseX > 168 && _mouseY > 154 && _mouseX < 242 && _mouseY < 180) { // "Play" button break; } - } // if (leftMouseButton == 1) + } // if (_leftMouseButton == 1) - leftMouseButton = 0; + _leftMouseButton = 0; delay(10); } selectVerb(kVerbNone); clearRoom(); - loadPic(roomNumber, bgSurface, HALF_PAL); + loadPic(_roomNumber, bgSurface, HALF_PAL); return true; } diff --git a/engines/drascula/sound.cpp b/engines/drascula/sound.cpp index 112f6fd06c..59b5e1d237 100644 --- a/engines/drascula/sound.cpp +++ b/engines/drascula/sound.cpp @@ -36,9 +36,9 @@ namespace Drascula { void DrasculaEngine::updateVolume(Audio::Mixer::SoundType soundType, int prevVolume) { int vol = _mixer->getVolumeForSoundType(soundType) / 16; - if (mouseY < prevVolume && vol < 15) + if (_mouseY < prevVolume && vol < 15) vol++; - if (mouseY > prevVolume && vol > 0) + if (_mouseY > prevVolume && vol > 0) vol--; _mixer->setVolumeForSoundType(soundType, vol * 16); } @@ -78,23 +78,23 @@ void DrasculaEngine::volumeControls() { while (getScan()) ; - if (rightMouseButton == 1) { + if (_rightMouseButton == 1) { // Clear this to avoid going straight to the inventory - rightMouseButton = 0; + _rightMouseButton = 0; delay(100); break; } - if (leftMouseButton == 1) { + if (_leftMouseButton == 1) { delay(100); - if (mouseX > 80 && mouseX < 121) { + if (_mouseX > 80 && _mouseX < 121) { updateVolume(Audio::Mixer::kPlainSoundType, masterVolumeY); } - if (mouseX > 136 && mouseX < 178) { + if (_mouseX > 136 && _mouseX < 178) { updateVolume(Audio::Mixer::kSpeechSoundType, voiceVolumeY); } - if (mouseX > 192 && mouseX < 233) { + if (_mouseX > 192 && _mouseX < 233) { updateVolume(Audio::Mixer::kMusicSoundType, musicVolumeY); } } diff --git a/engines/drascula/talk.cpp b/engines/drascula/talk.cpp index a326852e96..6aabd91c6a 100644 --- a/engines/drascula/talk.cpp +++ b/engines/drascula/talk.cpp @@ -381,11 +381,11 @@ void DrasculaEngine::talk(const char *said, const char *filename) { int face; if (currentChapter == 6) { - if (flags[0] == 0 && roomNumber == 102) { + if (flags[0] == 0 && _roomNumber == 102) { talk_pen(said, filename, 0); return; } - if (flags[0] == 0 && roomNumber == 58) { + if (flags[0] == 0 && _roomNumber == 58) { talk_pen(said, filename, 1); return; } @@ -397,7 +397,7 @@ void DrasculaEngine::talk(const char *said, const char *filename) { } if (currentChapter == 4) { - if (roomNumber == 24 || flags[29] == 0) { + if (_roomNumber == 24 || flags[29] == 0) { color_abc(kColorYellow); } } else { @@ -413,59 +413,59 @@ void DrasculaEngine::talk(const char *said, const char *filename) { updateRefresh_pre(); if (currentChapter == 2) - copyBackground(curX, curY, OBJWIDTH + 1, 0, curWidth, talkHeight - 1, screenSurface, drawSurface3); + copyBackground(curX, curY, OBJWIDTH + 1, 0, curWidth, TALK_HEIGHT - 1, screenSurface, drawSurface3); else copyBackground(curX, curY, OBJWIDTH + 1, 0, (int)(((float)curWidth / 100) * factor_red[MIN(201, curY + curHeight)]), - (int)(((float)(talkHeight - 1) / 100) * factor_red[MIN(201, curY + curHeight)]), + (int)(((float)(TALK_HEIGHT - 1) / 100) * factor_red[MIN(201, curY + curHeight)]), screenSurface, drawSurface3); moveCharacters(); if (currentChapter == 2) { if (!strcmp(menuBackground, "99.alg") || !strcmp(menuBackground, "994.alg")) - copyBackground(OBJWIDTH + 1, 0, curX, curY, curWidth, talkHeight - 1, drawSurface3, screenSurface); + copyBackground(OBJWIDTH + 1, 0, curX, curY, curWidth, TALK_HEIGHT - 1, drawSurface3, screenSurface); } else { copyBackground(OBJWIDTH + 1, 0, curX, curY, (int)(((float)curWidth / 100) * factor_red[MIN(201, curY + curHeight)]), - (int)(((float)(talkHeight - 1) / 100) * factor_red[MIN(201, curY + curHeight)]), + (int)(((float)(TALK_HEIGHT - 1) / 100) * factor_red[MIN(201, curY + curHeight)]), drawSurface3, screenSurface); } if (trackProtagonist == 0) { if (currentChapter == 2) - copyRect(x_talk_izq[face], y_mask_talk, curX + 8, curY - 1, talkWidth, talkHeight, + copyRect(x_talk_izq[face], y_mask_talk, curX + 8, curY - 1, TALK_WIDTH, TALK_HEIGHT, extraSurface, screenSurface); else reduce_hare_chico(x_talk_izq[face], y_mask_talk, curX + (int)((8.0f / 100) * factor_red[MIN(201, curY + curHeight)]), - curY, talkWidth, talkHeight, factor_red[MIN(201, curY + curHeight)], + curY, TALK_WIDTH, TALK_HEIGHT, factor_red[MIN(201, curY + curHeight)], extraSurface, screenSurface); updateRefresh(); } else if (trackProtagonist == 1) { if (currentChapter == 2) - copyRect(x_talk_dch[face], y_mask_talk, curX + 12, curY, talkWidth, talkHeight, + copyRect(x_talk_dch[face], y_mask_talk, curX + 12, curY, TALK_WIDTH, TALK_HEIGHT, extraSurface, screenSurface); else reduce_hare_chico(x_talk_dch[face], y_mask_talk, curX + (int)((12.0f / 100) * factor_red[MIN(201, curY + curHeight)]), - curY, talkWidth, talkHeight, factor_red[MIN(201, curY + curHeight)], extraSurface, screenSurface); + curY, TALK_WIDTH, TALK_HEIGHT, factor_red[MIN(201, curY + curHeight)], extraSurface, screenSurface); updateRefresh(); } else if (trackProtagonist == 2) { if (currentChapter == 2) - copyRect(x_talk_izq[face], y_mask_talk, curX + 12, curY, talkWidth, talkHeight, + copyRect(x_talk_izq[face], y_mask_talk, curX + 12, curY, TALK_WIDTH, TALK_HEIGHT, frontSurface, screenSurface); else reduce_hare_chico(x_talk_izq[face], y_mask_talk, talkOffset + curX + (int)((12.0f / 100) * factor_red[MIN(201, curY + curHeight)]), - curY, talkWidth, talkHeight, factor_red[MIN(201, curY + curHeight)], + curY, TALK_WIDTH, TALK_HEIGHT, factor_red[MIN(201, curY + curHeight)], frontSurface, screenSurface); updateRefresh(); } else if (trackProtagonist == 3) { if (currentChapter == 2) - copyRect(x_talk_dch[face], y_mask_talk, curX + 8, curY, talkWidth, talkHeight, + copyRect(x_talk_dch[face], y_mask_talk, curX + 8, curY, TALK_WIDTH, TALK_HEIGHT, frontSurface, screenSurface); else reduce_hare_chico(x_talk_dch[face], y_mask_talk, talkOffset + curX + (int)((8.0f / 100) * factor_red[MIN(201, curY + curHeight)]), - curY, talkWidth,talkHeight, factor_red[MIN(201, curY + curHeight)], + curY, TALK_WIDTH,TALK_HEIGHT, factor_red[MIN(201, curY + curHeight)], frontSurface, screenSurface); updateRefresh(); } @@ -827,47 +827,47 @@ void DrasculaEngine::talk_sync(const char *said, const char *filename, const cha updateRefresh_pre(); if (currentChapter == 2) - copyBackground(curX, curY, OBJWIDTH + 1, 0, curWidth, talkHeight - 1, screenSurface, drawSurface3); + copyBackground(curX, curY, OBJWIDTH + 1, 0, curWidth, TALK_HEIGHT - 1, screenSurface, drawSurface3); else copyBackground(curX, curY, OBJWIDTH + 1, 0, (int)(((float)curWidth / 100) * factor_red[curY + curHeight]), - (int)(((float)(talkHeight - 1) / 100) * factor_red[curY + curHeight]), screenSurface, drawSurface3); + (int)(((float)(TALK_HEIGHT - 1) / 100) * factor_red[curY + curHeight]), screenSurface, drawSurface3); moveCharacters(); if (currentChapter == 2) { if (curHeight != 56) - copyBackground(OBJWIDTH + 1, 0, curX, curY, curWidth, talkHeight - 1, drawSurface3, screenSurface); + copyBackground(OBJWIDTH + 1, 0, curX, curY, curWidth, TALK_HEIGHT - 1, drawSurface3, screenSurface); } else copyBackground(OBJWIDTH + 1, 0, curX, curY, (int)(((float)curWidth / 100) * factor_red[curY + curHeight]), - (int)(((float)(talkHeight - 1) / 100) * factor_red[curY + curHeight]), drawSurface3, screenSurface); + (int)(((float)(TALK_HEIGHT - 1) / 100) * factor_red[curY + curHeight]), drawSurface3, screenSurface); if (trackProtagonist == 0) { if (currentChapter == 2) - copyRect(x_talk_izq[face], y_mask_talk, curX + 8, curY - 1, talkWidth, talkHeight, extraSurface, screenSurface); + copyRect(x_talk_izq[face], y_mask_talk, curX + 8, curY - 1, TALK_WIDTH, TALK_HEIGHT, extraSurface, screenSurface); else reduce_hare_chico(x_talk_izq[face], y_mask_talk, (int)(curX + (8.0f / 100) * factor_red[curY + curHeight]), - curY, talkWidth, talkHeight, factor_red[curY + curHeight], extraSurface, screenSurface); + curY, TALK_WIDTH, TALK_HEIGHT, factor_red[curY + curHeight], extraSurface, screenSurface); updateRefresh(); } else if (trackProtagonist == 1) { if (currentChapter == 2) - copyRect(x_talk_dch[face], y_mask_talk, curX + 12, curY, talkWidth, talkHeight, extraSurface, screenSurface); + copyRect(x_talk_dch[face], y_mask_talk, curX + 12, curY, TALK_WIDTH, TALK_HEIGHT, extraSurface, screenSurface); else reduce_hare_chico(x_talk_dch[face], y_mask_talk, (int)(curX + (12.0f / 100) * factor_red[curY + curHeight]), - curY, talkWidth, talkHeight, factor_red[curY + curHeight], extraSurface, screenSurface); + curY, TALK_WIDTH, TALK_HEIGHT, factor_red[curY + curHeight], extraSurface, screenSurface); updateRefresh(); } else if (trackProtagonist == 2) { if (currentChapter == 2) - copyRect(x_talk_izq[face], y_mask_talk, curX + 12, curY, talkWidth, talkHeight, frontSurface, screenSurface); + copyRect(x_talk_izq[face], y_mask_talk, curX + 12, curY, TALK_WIDTH, TALK_HEIGHT, frontSurface, screenSurface); else reduce_hare_chico(x_talk_izq[face], y_mask_talk, (int)(talkOffset + curX + (12.0f / 100) * factor_red[curY + curHeight]), curY, - talkWidth, talkHeight, factor_red[curY + curHeight], frontSurface, screenSurface); + TALK_WIDTH, TALK_HEIGHT, factor_red[curY + curHeight], frontSurface, screenSurface); updateRefresh(); } else if (trackProtagonist == 3) { if (currentChapter == 2) - copyRect(x_talk_dch[face], y_mask_talk, curX + 8, curY, talkWidth, talkHeight, frontSurface, screenSurface); + copyRect(x_talk_dch[face], y_mask_talk, curX + 8, curY, TALK_WIDTH, TALK_HEIGHT, frontSurface, screenSurface); else reduce_hare_chico(x_talk_dch[face], y_mask_talk, (int)(talkOffset + curX + (8.0f / 100) * factor_red[curY + curHeight]), curY, - talkWidth, talkHeight, factor_red[curY + curHeight], frontSurface, screenSurface); + TALK_WIDTH, TALK_HEIGHT, factor_red[curY + curHeight], frontSurface, screenSurface); updateRefresh(); } diff --git a/engines/mohawk/myst.cpp b/engines/mohawk/myst.cpp index 8140817eb3..975c6c2a21 100644 --- a/engines/mohawk/myst.cpp +++ b/engines/mohawk/myst.cpp @@ -418,6 +418,7 @@ void MohawkEngine_Myst::changeToStack(uint16 stack, uint16 card, uint16 linkSrcS _sound->stopSound(); _sound->stopBackgroundMyst(); + _video->stopVideos(); if (linkSrcSound) _sound->playSoundBlocking(linkSrcSound); diff --git a/engines/mohawk/myst_stacks/myst.cpp b/engines/mohawk/myst_stacks/myst.cpp index f17d765c99..dc5f433ce7 100644 --- a/engines/mohawk/myst_stacks/myst.cpp +++ b/engines/mohawk/myst_stacks/myst.cpp @@ -691,6 +691,7 @@ void Myst::toggleVar(uint16 var) { else _state.courtyardImageBoxes |= mask; } + break; case 41: // Vault white page if (_globals.ending != 4) { if (_dockVaultState == 1) { diff --git a/engines/mortevielle/actions.cpp b/engines/mortevielle/actions.cpp index c06f19dadc..b68dd48b0f 100644 --- a/engines/mortevielle/actions.cpp +++ b/engines/mortevielle/actions.cpp @@ -91,31 +91,41 @@ void MortevielleEngine::fctMove() { oldMenu = (_menu._moveMenu[menuChoice]._menuId << 8) | _menu._moveMenu[menuChoice]._actionId; } - if (_coreVar._currPlace == MOUNTAIN) { + switch (_coreVar._currPlace) { + case MOUNTAIN: if (menuChoice == 1) gotoManorFront(); else if (menuChoice == 2) checkManorDistance(); _menu.setDestinationText(_coreVar._currPlace); return; - } else if (_coreVar._currPlace == INSIDE_WELL) { + case INSIDE_WELL: if (menuChoice == 1) floodedInWell(); else if (menuChoice == 2) gotoManorBack(); _menu.setDestinationText(_coreVar._currPlace); return; - } else if ((_coreVar._currPlace == BUREAU) && (menuChoice == 1)) - menuChoice = 6; - else if (_coreVar._currPlace == KITCHEN) { + case BUREAU: + if (menuChoice == 1) + menuChoice = 6; + break; + case KITCHEN: if (menuChoice == 2) menuChoice = 6; else if (menuChoice == 5) menuChoice = 16; - } else if ((_coreVar._currPlace == CELLAR) && (menuChoice == 3)) - menuChoice = 6; - else if (((_coreVar._currPlace == LANDING) || (_coreVar._currPlace == ROOM26)) && (menuChoice == 4)) - menuChoice = 6; + break; + case CELLAR: + if (menuChoice == 3) + menuChoice = 6; + break; + case LANDING: + case ROOM26: + if (menuChoice == 4) + menuChoice = 6; + break; + } if ((_coreVar._currPlace > MOUNTAIN) && (_coreVar._currPlace != ROOM26)) menuChoice += 10; @@ -132,32 +142,40 @@ void MortevielleEngine::fctMove() { else if ((_coreVar._currPlace == WELL) && (menuChoice > 13) && (menuChoice != 17)) menuChoice = 15; - if (menuChoice == 1) + switch (menuChoice) { + case 1: _coreVar._currPlace = BUREAU; - else if (menuChoice == 2) + break; + case 2: _coreVar._currPlace = KITCHEN; - else if (menuChoice == 3) + break; + case 3: _coreVar._currPlace = CELLAR; - else if (menuChoice == 4) + break; + case 4: _coreVar._currPlace = LANDING; - else if (menuChoice == 5) - menuChoice = 12; - else if (menuChoice == 6) - menuChoice = 11; - - if (menuChoice == 11) - gotoDiningRoom(); - else if (menuChoice == 12) + break; + case 5: + case 12: gotoManorFront(); - else if (menuChoice == 13) + break; + case 6: + case 11: + gotoDiningRoom(); + break; + case 13: _coreVar._currPlace = CHAPEL; - else if (menuChoice == 14) + break; + case 14: _coreVar._currPlace = WELL; - else if (menuChoice == 15) + break; + case 15: checkManorDistance(); - else if (menuChoice == 16) + break; + case 16: gotoManorBack(); - else if (menuChoice == 17) { + break; + case 17: if ((_coreVar._wellObjectId != 120) && (_coreVar._wellObjectId != 140)) _crep = 997; else if (_coreVar._wellObjectId == 120) @@ -169,7 +187,9 @@ void MortevielleEngine::fctMove() { _coreVar._currPlace = INSIDE_WELL; prepareDisplayText(); } + break; } + if ((menuChoice < 5) || (menuChoice == 13) || (menuChoice == 14)) prepareDisplayText(); resetRoomVariables(_coreVar._currPlace); @@ -503,9 +523,12 @@ void MortevielleEngine::fctSearch() { setCoordinates(7); if (_num != 0) { int i; - for (i = 1; (i <= 6) && (_num != _openObjects[i]); i++) - ; - if (_num == _openObjects[i]) { + for (i = 1; i <= 6; i++) { + if (_num == _openObjects[i]) + break; + } + + if (i <= 6) { if (_currBitIndex > 0) _coreVar._faithScore += 3; @@ -606,9 +629,20 @@ void MortevielleEngine::fctOpen() { _coreVar._faithScore += 2; ++_openObjCount; int i; - for (i = 1; (i <= 6) && (_openObjects[i] != 0) && (_openObjects[i] != _num); i++) - ; - if (_openObjects[i] != _num) { + for (i = 1; (i <= 6); i++) { + if ((_openObjects[i] == 0) || (_openObjects[i] == _num)) + break; + } + + if (i > 6) { + warning("Unexpected action: Too many open objects"); + return; + } + + if (_openObjects[i] == _num) + // display "Already Opened" + _crep = 18; + else { if (!( ((_num == 3) && ((_coreVar._currPlace == OWN_ROOM) || (_coreVar._currPlace == JULIA_ROOM) || (_coreVar._currPlace == BLUE_ROOM) @@ -641,9 +675,7 @@ void MortevielleEngine::fctOpen() { _crep = _tabdon[kAouvr + (tmpPlace * 7) + _num - 1]; if (_crep == 254) _crep = 999; - } else - // display "Already Opened" - _crep = 18; + } } } @@ -702,10 +734,10 @@ void MortevielleEngine::fctPlace() { _soundManager.startSpeech(6, -9, 1); // Do you want to enter the hidden passage? - int answer = _dialogManager.show(getEngineString(S_YES_NO), 1); + int answer = _dialogManager.show(getEngineString(S_YES_NO)); if (answer == 1) { Common::String alertTxt = getString(582); - _dialogManager.show(alertTxt, 1); + _dialogManager.show(alertTxt); bool enterPassageFl = _dialogManager.showKnowledgeCheck(); _mouse.hideMouse(); @@ -732,7 +764,7 @@ void MortevielleEngine::fctPlace() { displayAnimFrame(1, 2); displayAnimFrame(1, 1); alertTxt = getString(577); - _dialogManager.show(alertTxt, 1); + _dialogManager.show(alertTxt); displayAnimFrame(2, 1); _crep = 166; } @@ -801,7 +833,7 @@ void MortevielleEngine::fctTurn() { if ((_coreVar._currPlace == ATTIC) && (_coreVar._atticRodHoleObjectId == 159) && (_coreVar._atticBallHoleObjectId == 141)) { handleDescriptionText(2, 167); _soundManager.startSpeech(7, 9, 1); - int answer = _dialogManager.show(getEngineString(S_YES_NO), 1); + int answer = _dialogManager.show(getEngineString(S_YES_NO)); if (answer == 1) _endGame = true; else @@ -811,7 +843,7 @@ void MortevielleEngine::fctTurn() { handleDescriptionText(2, 175); clearVerbBar(); _soundManager.startSpeech(6, -9, 1); - int answer = _dialogManager.show(getEngineString(S_YES_NO), 1); + int answer = _dialogManager.show(getEngineString(S_YES_NO)); if (answer == 1) { _coreVar._currPlace = CRYPT; prepareDisplayText(); @@ -884,9 +916,12 @@ void MortevielleEngine::fctClose() { setCoordinates(7); if (_num != 0) { int i; - for (i = 1; (i <= 6) && (_num != _openObjects[i]); ++i) - ; - if (_num == _openObjects[i]) { + for (i = 1; i <= 6; ++i) { + if (_num == _openObjects[i]) + break; + } + + if (i <= 6) { displayAnimFrame(2, _num); _crep = 998; _openObjects[i] = 0; @@ -914,7 +949,7 @@ void MortevielleEngine::fctKnock() { displayTextInVerbBar(getEngineString(S_HIT)); if (_coreVar._currPlace == LANDING) { - _dialogManager.show(getEngineString(S_BEFORE_USE_DEP_MENU), 1); + _dialogManager.show(getEngineString(S_BEFORE_USE_DEP_MENU)); return; } @@ -979,9 +1014,12 @@ void MortevielleEngine::fctSelfPut() { _crep = 997; else { int i; - for (i = 1; (i <= 6) && (_num != _openObjects[i]); i++) - ; - if (_num == _openObjects[i]) { + for (i = 1; i <= 6; i++) { + if (_num == _openObjects[i]) + break; + } + + if (i <= 6) { _curSearchObjId = objId; _crep = 999; } else @@ -1002,12 +1040,15 @@ void MortevielleEngine::fctSelfPut() { if (_num == 1) { if (_coreVar._atticBallHoleObjectId != 0) _crep = 188; - else + else { _coreVar._atticBallHoleObjectId = _coreVar._selectedObjectId; + displayAnimFrame(1, 7); + } } else if (_coreVar._atticRodHoleObjectId != 0) { _crep = 188; } else { _coreVar._atticRodHoleObjectId = _coreVar._selectedObjectId; + displayAnimFrame(1, 6); } } @@ -1220,7 +1261,7 @@ void MortevielleEngine::fctSleep() { if (hour > 23) hour = 0; prepareRoom(); - answer = _dialogManager.show(getEngineString(S_YES_NO), 1); + answer = _dialogManager.show(getEngineString(S_YES_NO)); _anyone = false; } while (answer != 1); _crep = 998; @@ -1310,7 +1351,7 @@ void MortevielleEngine::fctWait() { return; } handleDescriptionText(2, 102); - answer = _dialogManager.show(getEngineString(S_YES_NO), 1); + answer = _dialogManager.show(getEngineString(S_YES_NO)); } while (answer != 2); _crep = 998; if (!_anyone) @@ -1626,7 +1667,7 @@ void MortevielleEngine::askRestart() { _day = 0; handleDescriptionText(2, 180); - int answer = _dialogManager.show(getEngineString(S_YES_NO), 1); + int answer = _dialogManager.show(getEngineString(S_YES_NO)); _quitGame = (answer != 1); } diff --git a/engines/mortevielle/detection.cpp b/engines/mortevielle/detection.cpp index 8e2eab52b8..ee9cb40c99 100644 --- a/engines/mortevielle/detection.cpp +++ b/engines/mortevielle/detection.cpp @@ -30,6 +30,7 @@ namespace Mortevielle { struct MortevielleGameDescription { ADGameDescription desc; Common::Language originalLanguage; + uint8 dataFeature; }; uint32 MortevielleEngine::getGameFlags() const { return _gameDescription->desc.flags; } @@ -38,6 +39,8 @@ Common::Language MortevielleEngine::getLanguage() const { return _gameDescriptio Common::Language MortevielleEngine::getOriginalLanguage() const { return _gameDescription->originalLanguage; } +bool MortevielleEngine::useOriginalData() const { return _gameDescription->dataFeature == kUseOriginalData; } + } static const PlainGameDescriptor MortevielleGame[] = { @@ -53,6 +56,9 @@ public: MortevielleGame) { _md5Bytes = 512; _singleid = "mortevielle"; + // Use kADFlagUseExtraAsHint to distinguish between original and improved versions + // (i.e. use or not of the game data file). + _flags = kADFlagUseExtraAsHint; } virtual const char *getName() const { return "Mortevielle"; diff --git a/engines/mortevielle/detection_tables.h b/engines/mortevielle/detection_tables.h index 9bb5fbea87..8d0cd5630c 100644 --- a/engines/mortevielle/detection_tables.h +++ b/engines/mortevielle/detection_tables.h @@ -37,7 +37,7 @@ static const MortevielleGameDescription MortevielleGameDescriptions[] = { Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO0() - }, Common::FR_FRA + }, Common::FR_FRA, kUseOriginalData }, // German { @@ -53,7 +53,24 @@ static const MortevielleGameDescription MortevielleGameDescriptions[] = { Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO0() - }, Common::DE_DEU + }, Common::DE_DEU, kUseOriginalData + }, + + // German, improved translation + { + { + "mortevielle", + "Improved Translation", + { + {"menual.mor", 0, "792aea282b07a1d74c4a4abeabc90c19", 144}, + {"dxx.mor", 0, "949e68e829ecd5ad29e36a00347a9e7e", 207744}, + AD_LISTEND + }, + Common::DE_DEU, + Common::kPlatformDOS, + ADGF_NO_FLAGS, + GUIO0() + }, Common::DE_DEU, kUseEngineDataFile }, // DOS English version doesn't exist. Technically, they are French or German versions, @@ -73,7 +90,7 @@ static const MortevielleGameDescription MortevielleGameDescriptions[] = { Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO0() - }, Common::FR_FRA + }, Common::FR_FRA, kUseEngineDataFile }, // English on top of German version @@ -90,10 +107,10 @@ static const MortevielleGameDescription MortevielleGameDescriptions[] = { Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO0() - }, Common::DE_DEU + }, Common::DE_DEU, kUseEngineDataFile }, - { AD_TABLE_END_MARKER , Common::EN_ANY} + { AD_TABLE_END_MARKER , Common::EN_ANY, kUseEngineDataFile} }; } // End of namespace Mortevielle diff --git a/engines/mortevielle/dialogs.cpp b/engines/mortevielle/dialogs.cpp index b8d197da2a..9a2ade60ab 100644 --- a/engines/mortevielle/dialogs.cpp +++ b/engines/mortevielle/dialogs.cpp @@ -39,7 +39,7 @@ namespace Mortevielle { * Alert function - Show * @remarks Originally called 'do_alert' */ -int DialogManager::show(const Common::String &msg, int n) { +int DialogManager::show(const Common::String &msg) { // Make a copy of the current screen surface for later restore _vm->_backgroundSurface.copyFrom(_vm->_screenSurface); @@ -57,13 +57,12 @@ int DialogManager::show(const Common::String &msg, int n) { decodeAlertDetails(msg, caseNumb, lignNumb, colNumb, alertStr, caseStr); - int i = 0; Common::Point curPos; if (alertStr == "") { drawAlertBox(10, 5, colNumb); } else { drawAlertBox(8, 7, colNumb); - i = 0; + int i = 0; _vm->_screenSurface._textPos.y = 70; do { curPos.x = 320; @@ -165,10 +164,10 @@ int DialogManager::show(const Common::String &msg, int n) { _vm->setMouseClick(false); _vm->_mouse.hideMouse(); if (!test3) { - id = n; - setPosition(n, coldep, esp); + id = 1; + setPosition(1, coldep, esp); Common::String tmp4(" "); - tmp4 += buttonStr[n]; + tmp4 += buttonStr[1]; tmp4 += " "; _vm->_screenSurface.drawString(tmp4, 1); } @@ -229,13 +228,13 @@ void DialogManager::setPosition(int ji, int coldep, int esp) { * Alert function - Draw Alert Box * @remarks Originally called 'fait_boite' */ -void DialogManager::drawAlertBox(int lidep, int nli, int tx) { - if (tx > 640) - tx = 640; - int x = 320 - ((uint)tx / 2); - int y = (lidep - 1) * 8; - int xx = x + tx; - int yy = y + (nli * 8); +void DialogManager::drawAlertBox(int firstLine, int lineNum, int width) { + if (width > 640) + width = 640; + int x = 320 - ((uint)width / 2); + int y = (firstLine - 1) * 8; + int xx = x + width; + int yy = y + (lineNum * 8); _vm->_screenSurface.fillRect(15, Common::Rect(x, y, xx, yy)); _vm->_screenSurface.fillRect(0, Common::Rect(x, y + 2, xx, y + 4)); _vm->_screenSurface.fillRect(0, Common::Rect(x, yy - 4, xx, yy - 2)); diff --git a/engines/mortevielle/dialogs.h b/engines/mortevielle/dialogs.h index af667e40c5..3f30851960 100644 --- a/engines/mortevielle/dialogs.h +++ b/engines/mortevielle/dialogs.h @@ -48,11 +48,11 @@ private: void decodeAlertDetails(Common::String inputStr, int &choiceNumb, int &lineNumb, int &col, Common::String &choiceStr, Common::String &choiceListStr); void setPosition(int ji, int coldep, int esp); - void drawAlertBox(int lidep, int nli, int tx); + void drawAlertBox(int firstLine, int lineNum, int width); void setButtonText(Common::String c, int coldep, int nbcase, Common::String *str, int esp); public: void setParent(MortevielleEngine *vm); - int show(const Common::String &msg, int n); + int show(const Common::String &msg); void drawF3F8(); void checkForF8(int SpeechNum, bool drawFrame2Fl); int waitForF3F8(); diff --git a/engines/mortevielle/menu.cpp b/engines/mortevielle/menu.cpp index 9e03e83127..641a527c98 100644 --- a/engines/mortevielle/menu.cpp +++ b/engines/mortevielle/menu.cpp @@ -155,30 +155,57 @@ void Menu::readVerbNums(Common::File &f, int dataSize) { * Setup a menu's contents * @remarks Originally called 'menut' */ -void Menu::setText(int menuId, int actionId, Common::String name) { +void Menu::setText(MenuItem item, Common::String name) { Common::String s = name; - while (s.size() < 22) - s += ' '; - - switch (menuId) { + switch (item._menuId) { case MENU_INVENTORY: - if (actionId != 7) { - _inventoryStringArray[actionId] = s; - _inventoryStringArray[actionId].insertChar(' ', 0); + if (item._actionId != 7) { + while (s.size() < 22) + s += ' '; + + _inventoryStringArray[item._actionId] = s; + _inventoryStringArray[item._actionId].insertChar(' ', 0); } break; - case MENU_MOVE: - _moveStringArray[actionId] = s; + case MENU_MOVE: { + // If the first character isn't '*' or ' ' then it's missing a heading space + char c = s[0]; + if (c != '*' && c != ' ') + s.insertChar(' ', 0); + + while (s.size() < 22) + s += ' '; + + _moveStringArray[item._actionId] = s; + } break; - case MENU_ACTION: - _actionStringArray[actionId] = s; + case MENU_ACTION: { + // If the first character isn't '*' or ' ' then it's missing a heading space + char c = s[0]; + if (c != '*' && c != ' ') + s.insertChar(' ', 0); + + while (s.size() < 10) + s += ' '; + + _actionStringArray[item._actionId] = s; + } break; - case MENU_SELF: - _selfStringArray[actionId] = s; + case MENU_SELF: { + // If the first character isn't '*' or ' ' then it's missing a heading space + char c = s[0]; + if (c != '*' && c != ' ') + s.insertChar(' ', 0); + + while (s.size() < 10) + s += ' '; + + _selfStringArray[item._actionId] = s; + } break; case MENU_DISCUSS: - _discussStringArray[actionId] = s; + _discussStringArray[item._actionId] = s; break; default: break; @@ -200,11 +227,11 @@ void Menu::setDestinationText(int roomId) { nomp = _vm->getString(_vm->_destinationArray[destinationId][roomId] + kMenuPlaceStringIndex); while (nomp.size() < 20) nomp += ' '; - setText(_moveMenu[destinationId + 1]._menuId, _moveMenu[destinationId + 1]._actionId, nomp); + setText(_moveMenu[destinationId + 1], nomp); } nomp = "* "; for (int i = 7; i >= destinationId + 1; --i) - setText(_moveMenu[i]._menuId, _moveMenu[i]._actionId, nomp); + setText(_moveMenu[i], nomp); } /** @@ -212,26 +239,26 @@ void Menu::setDestinationText(int roomId) { * @param menuId Menu number * @param actionId Item index */ -void Menu::disableMenuItem(int menuId, int actionId) { - switch (menuId) { +void Menu::disableMenuItem(MenuItem item) { + switch (item._menuId) { case MENU_INVENTORY: - if (actionId > 6) { - _inventoryStringArray[actionId].setChar('<', 0); - _inventoryStringArray[actionId].setChar('>', 21); + if (item._actionId > 6) { + _inventoryStringArray[item._actionId].setChar('<', 0); + _inventoryStringArray[item._actionId].setChar('>', 21); } else - _inventoryStringArray[actionId].setChar('*', 0); + _inventoryStringArray[item._actionId].setChar('*', 0); break; case MENU_MOVE: - _moveStringArray[actionId].setChar('*', 0); + _moveStringArray[item._actionId].setChar('*', 0); break; case MENU_ACTION: - _actionStringArray[actionId].setChar('*', 0); + _actionStringArray[item._actionId].setChar('*', 0); break; case MENU_SELF: - _selfStringArray[actionId].setChar('*', 0); + _selfStringArray[item._actionId].setChar('*', 0); break; case MENU_DISCUSS: - _discussStringArray[actionId].setChar('*', 0); + _discussStringArray[item._actionId].setChar('*', 0); break; default: break; @@ -244,23 +271,23 @@ void Menu::disableMenuItem(int menuId, int actionId) { * @param actionId Item index * @remarks Originally called menu_enable */ -void Menu::enableMenuItem(int menuId, int actionId) { - switch (menuId) { +void Menu::enableMenuItem(MenuItem item) { + switch (item._menuId) { case MENU_INVENTORY: - _inventoryStringArray[actionId].setChar(' ', 0); - _inventoryStringArray[actionId].setChar(' ', 21); + _inventoryStringArray[item._actionId].setChar(' ', 0); + _inventoryStringArray[item._actionId].setChar(' ', 21); break; case MENU_MOVE: - _moveStringArray[actionId].setChar(' ', 0); + _moveStringArray[item._actionId].setChar(' ', 0); break; case MENU_ACTION: - _actionStringArray[actionId].setChar(' ', 0); + _actionStringArray[item._actionId].setChar(' ', 0); break; case MENU_SELF: - _selfStringArray[actionId].setChar(' ', 0); + _selfStringArray[item._actionId].setChar(' ', 0); break; case MENU_DISCUSS: - _discussStringArray[actionId].setChar(' ', 0); + _discussStringArray[item._actionId].setChar(' ', 0); break; default: break; @@ -570,12 +597,30 @@ void Menu::setParent(MortevielleEngine *vm) { void Menu::initMenu() { Common::File f; - bool enMenuLoaded = false; - if (_vm->getLanguage() == Common::EN_ANY) { - // Open the mort.dat file + bool menuLoaded = false; + // First try to read it from mort.dat if useOriginalData() is false + if (!_vm->useOriginalData()) { if (!f.open(MORT_DAT)) warning("File %s not found. Using default menu from game data", MORT_DAT); else { + // Figure out what language Id is needed + byte desiredLanguageId; + switch(_vm->getLanguage()) { + case Common::EN_ANY: + desiredLanguageId = MORTDAT_LANG_ENGLISH; + break; + case Common::FR_FRA: + desiredLanguageId = MORTDAT_LANG_FRENCH; + break; + case Common::DE_DEU: + desiredLanguageId = MORTDAT_LANG_GERMAN; + break; + default: + warning("Language not supported, switching to English"); + desiredLanguageId = MORTDAT_LANG_ENGLISH; + break; + } + // Validate the data file header char fileId[4]; f.read(fileId, 4); @@ -590,12 +635,20 @@ void Menu::initMenu() { f.read(dataType, 4); dataSize = f.readUint16LE(); if (!strncmp(dataType, "MENU", 4)) { - // MENU section - if (dataSize <= 7 * 24) { + // Read in the language + byte languageId = f.readByte(); + --dataSize; + + // If the language isn't correct, then skip the entire block + if (languageId != desiredLanguageId) { + f.skip(dataSize); + continue; + } + if (dataSize == 6 * 24) { f.read(_charArr, dataSize); - enMenuLoaded = true; + menuLoaded = true; } else - warning("Wrong size %d for menu data. Expected %d or less", dataSize, 7*24); + warning("Wrong size %d for menu data. Expected %d or less", dataSize, 6 * 24); break; } else { // Other sections @@ -605,12 +658,13 @@ void Menu::initMenu() { } // Close the file f.close(); - if (!enMenuLoaded) - warning("Failed to load English menu. Will use default menu from game data instead"); + if (!menuLoaded) + warning("Failed to load menu from mort.dat. Will use default menu from game data instead."); } } - if (!enMenuLoaded) { + if (!menuLoaded) { + // Load menu from game data using the original language if (_vm->getOriginalLanguage() == Common::FR_FRA) { if (!f.open("menufr.mor")) error("Missing file - menufr.mor"); @@ -618,7 +672,7 @@ void Menu::initMenu() { if (!f.open("menual.mor")) error("Missing file - menual.mor"); } - f.read(_charArr, 7 * 24); + f.read(_charArr, 6 * 24); f.close(); } @@ -631,13 +685,16 @@ void Menu::initMenu() { _moveStringArray[i] = "* "; for (int i = 1; i < 22; i++) { _actionStringArray[i] = _vm->getString(i + kMenuActionStringIndex); - + if ((_actionStringArray[i][0] != '*') && (_actionStringArray[i][0] != ' ')) + _actionStringArray[i].insertChar(' ', 0); while (_actionStringArray[i].size() < 10) _actionStringArray[i] += ' '; if (i < 9) { if (i < 6) { _selfStringArray[i] = _vm->getString(i + kMenuSelfStringIndex); + if ((_selfStringArray[i][0] != '*') && (_selfStringArray[i][0] != ' ')) + _selfStringArray[i].insertChar(' ', 0); while (_selfStringArray[i].size() < 10) _selfStringArray[i] += ' '; } @@ -654,7 +711,7 @@ void Menu::initMenu() { _inventoryMenu[i]._menuId = MENU_INVENTORY; _inventoryMenu[i]._actionId = i; if (i > 6) - disableMenuItem(_inventoryMenu[i]._menuId, _inventoryMenu[i]._actionId); + disableMenuItem(_inventoryMenu[i]); } _msg3 = OPCODE_NONE; _msg4 = OPCODE_NONE; @@ -669,13 +726,21 @@ void Menu::initMenu() { */ void Menu::setSearchMenu() { for (int i = 1; i <= 7; ++i) - disableMenuItem(MENU_MOVE, _moveMenu[i]._actionId); + disableMenuItem(_moveMenu[i]); for (int i = 1; i <= 11; ++i) - disableMenuItem(_actionMenu[i]._menuId, _actionMenu[i]._actionId); + disableMenuItem(_actionMenu[i]); + + MenuItem miSound; + miSound._menuId = _opcodeSound >> 8; + miSound._actionId = _opcodeSound & 0xFF; - setText(_opcodeSound >> 8, _opcodeSound & 0xFF, _vm->getEngineString(S_SUITE)); - setText(_opcodeLift >> 8, _opcodeLift & 0xFF, _vm->getEngineString(S_STOP)); + MenuItem miLift; + miLift._menuId = _opcodeLift >> 8; + miLift._actionId = _opcodeLift & 0xFF; + + setText(miSound, _vm->getEngineString(S_SUITE)); + setText(miLift, _vm->getEngineString(S_STOP)); } /** @@ -685,10 +750,18 @@ void Menu::setSearchMenu() { void Menu::unsetSearchMenu() { setDestinationText(_vm->_coreVar._currPlace); for (int i = 1; i <= 11; ++i) - enableMenuItem(_actionMenu[i]._menuId, _actionMenu[i]._actionId); + enableMenuItem(_actionMenu[i]); + + MenuItem miSound; + miSound._menuId = _opcodeSound >> 8; + miSound._actionId = _opcodeSound & 0xFF; + + MenuItem miLift; + miLift._menuId = _opcodeLift >> 8; + miLift._actionId = _opcodeLift & 0xFF; - setText(_opcodeSound >> 8, _opcodeSound & 0xFF, _vm->getEngineString(S_PROBE)); - setText(_opcodeLift >> 8, _opcodeLift & 0xFF, _vm->getEngineString(S_RAISE)); + setText(miSound, _vm->getEngineString(S_PROBE)); + setText(miLift, _vm->getEngineString(S_RAISE)); } /** @@ -704,15 +777,15 @@ void Menu::setInventoryText() { ++cy; int r = _vm->_coreVar._inventory[i] + 400; nomp = _vm->getString(r - 501 + kInventoryStringIndex); - setText(_inventoryMenu[cy]._menuId, _inventoryMenu[cy]._actionId, nomp); - enableMenuItem(_inventoryMenu[i]._menuId, _inventoryMenu[i]._actionId); + setText(_inventoryMenu[cy], nomp); + enableMenuItem(_inventoryMenu[i]); } } if (cy < 6) { for (int i = cy + 1; i <= 6; ++i) { - setText(_inventoryMenu[i]._menuId, _inventoryMenu[i]._actionId, " "); - disableMenuItem(_inventoryMenu[i]._menuId, _inventoryMenu[i]._actionId); + setText(_inventoryMenu[i], " "); + disableMenuItem(_inventoryMenu[i]); } } } diff --git a/engines/mortevielle/menu.h b/engines/mortevielle/menu.h index 9afa99cfa8..debf5b09b6 100644 --- a/engines/mortevielle/menu.h +++ b/engines/mortevielle/menu.h @@ -42,7 +42,7 @@ enum { const int OPCODE_NONE = 0; -struct menuItem { +struct MenuItem { int _menuId; int _actionId; }; @@ -51,7 +51,7 @@ class Menu { private: MortevielleEngine *_vm; - byte _charArr[7][24]; + byte _charArr[6][24]; int _msg3; int _msg4; @@ -71,9 +71,9 @@ public: Common::String _actionStringArray[22]; Common::String _selfStringArray[7]; Common::String _discussStringArray[9]; - menuItem _discussMenu[9]; - menuItem _inventoryMenu[9]; - menuItem _moveMenu[8]; + MenuItem _discussMenu[9]; + MenuItem _inventoryMenu[9]; + MenuItem _moveMenu[8]; int _opcodeAttach; int _opcodeWait; @@ -101,15 +101,15 @@ public: int _opcodeSRead; int _opcodeSPut; int _opcodeSLook; - menuItem _actionMenu[12]; + MenuItem _actionMenu[12]; void setParent(MortevielleEngine *vm); void readVerbNums(Common::File &f, int dataSize); - void setText(int menuId, int actionId, Common::String name); + void setText(MenuItem item, Common::String name); void setDestinationText(int roomId); void setInventoryText(); - void disableMenuItem(int menuId, int actionId); - void enableMenuItem(int menuId, int actionId); + void disableMenuItem(MenuItem item); + void enableMenuItem(MenuItem item); void displayMenu(); void drawMenu(); void menuUp(int msgId); diff --git a/engines/mortevielle/mortevielle.cpp b/engines/mortevielle/mortevielle.cpp index e958d0bada..f13f8cb65b 100644 --- a/engines/mortevielle/mortevielle.cpp +++ b/engines/mortevielle/mortevielle.cpp @@ -347,10 +347,16 @@ Common::Error MortevielleEngine::run() { if (loadSlot == 0) // Show the game introduction showIntroduction(); + else { + _caff = 51; + _text.taffich(); + } // Either load the initial game state savegame, or the specified savegame number adzon(); - _savegameManager.loadSavegame(generateSaveFilename(loadSlot)); + resetVariables(); + if (loadSlot != 0) + _savegameManager.loadSavegame(generateSaveFilename(loadSlot)); // Run the main game loop mainGame(); diff --git a/engines/mortevielle/mortevielle.h b/engines/mortevielle/mortevielle.h index c9eebe1347..4c096c1f71 100644 --- a/engines/mortevielle/mortevielle.h +++ b/engines/mortevielle/mortevielle.h @@ -62,6 +62,11 @@ enum { MORTDAT_LANG_GERMAN = 2 }; +enum { + kUseOriginalData = 0, + kUseEngineDataFile = 1 +}; + // Static string list enum { S_YES_NO = 0, S_GO_TO = 1, S_SOMEONE_ENTERS = 2, S_COOL = 3, S_LOURDE = 4, @@ -130,16 +135,6 @@ enum Places { DOOR = 25, ROOM26 = 26, COAT_ARMS = 27 }; -struct nhom { - byte _id; /* number between 0 and 32 */ - byte _hom[4]; -}; - -struct CgaPalette { - byte _p; - nhom _a[16]; -}; - struct Pattern { byte _tay, _tax; byte _des[kMaxPatt + 1][kMaxPatt + 1]; @@ -179,7 +174,6 @@ private: Common::StringArray _engineStrings; Common::StringArray _gameStrings; - Pattern _patternArr[15]; int _menuOpcode; bool _inMainGameLoop; // Flag when the main game loop is active @@ -223,7 +217,6 @@ private: int _startHour; int _endHour; Common::Point _stdPal[91][17]; - CgaPalette _cgaPal[91]; int _x26KeyCount; int _roomDoorId; @@ -256,7 +249,6 @@ private: void mainGame(); void playGame(); void handleAction(); - void displayCGAPattern(int n, Pattern *p, nhom *pal); void loadPalette(); void loadTexts(); void loadCFIEC(); @@ -457,6 +449,7 @@ public: uint32 getGameFlags() const; Common::Language getLanguage() const; Common::Language getOriginalLanguage() const; + bool useOriginalData() const; static Common::String generateSaveFilename(const Common::String &target, int slot); Common::String generateSaveFilename(int slot) { return generateSaveFilename(_targetName, slot); } diff --git a/engines/mortevielle/outtext.cpp b/engines/mortevielle/outtext.cpp index 4629a47c36..d50f4005de 100644 --- a/engines/mortevielle/outtext.cpp +++ b/engines/mortevielle/outtext.cpp @@ -164,8 +164,6 @@ void TextHandler::loadAniFile(Common::String filename, int32 skipSize, int lengt } void TextHandler::taffich() { - static const byte rang[16] = {15, 14, 11, 7, 13, 12, 10, 6, 9, 5, 3, 1, 2, 4, 8, 0}; - static const byte tran1[] = { 121, 121, 138, 139, 120 }; static const byte tran2[] = { 150, 150, 152, 152, 100, 110, 159, 100, 100 }; diff --git a/engines/mortevielle/saveload.cpp b/engines/mortevielle/saveload.cpp index 651ed07b65..c14a03cd60 100644 --- a/engines/mortevielle/saveload.cpp +++ b/engines/mortevielle/saveload.cpp @@ -72,15 +72,16 @@ void SavegameManager::sync_save(Common::Serializer &sz) { * Inner code for loading a saved game * @remarks Originally called 'takesav' */ -void SavegameManager::loadSavegame(const Common::String &filename) { +bool SavegameManager::loadSavegame(const Common::String &filename) { // Try loading first from the save area Common::SeekableReadStream *stream = g_system->getSavefileManager()->openForLoading(filename); Common::File f; if (stream == NULL) { - if (!f.open(filename)) - error("Unable to open save file '%s'", filename.c_str()); - + if (!f.open(filename)) { + warning("Unable to open save file '%s'", filename.c_str()); + return false; + } stream = f.readStream(f.size()); f.close(); } @@ -107,6 +108,8 @@ void SavegameManager::loadSavegame(const Common::String &filename) { // Close the stream delete stream; + + return true; } /** @@ -115,14 +118,15 @@ void SavegameManager::loadSavegame(const Common::String &filename) { Common::Error SavegameManager::loadGame(const Common::String &filename) { g_vm->_mouse.hideMouse(); g_vm->displayEmptyHand(); - loadSavegame(filename); - - /* Initialization */ - g_vm->charToHour(); - g_vm->initGame(); - g_vm->gameLoaded(); - g_vm->_mouse.showMouse(); - return Common::kNoError; + if (loadSavegame(filename)) { + /* Initialization */ + g_vm->charToHour(); + g_vm->initGame(); + g_vm->gameLoaded(); + g_vm->_mouse.showMouse(); + return Common::kNoError; + } else + return Common::kUnknownError; } /** diff --git a/engines/mortevielle/saveload.h b/engines/mortevielle/saveload.h index 0121a04d8e..79747e6889 100644 --- a/engines/mortevielle/saveload.h +++ b/engines/mortevielle/saveload.h @@ -57,7 +57,7 @@ private: void sync_save(Common::Serializer &sz); public: void setParent(MortevielleEngine *vm); - void loadSavegame(const Common::String &filename); + bool loadSavegame(const Common::String &filename); Common::Error loadGame(const Common::String &filename); Common::Error saveGame(int n, const Common::String &saveName); Common::Error loadGame(int slot); diff --git a/engines/mortevielle/sound.cpp b/engines/mortevielle/sound.cpp index 3bea96b92f..b670246726 100644 --- a/engines/mortevielle/sound.cpp +++ b/engines/mortevielle/sound.cpp @@ -741,6 +741,10 @@ void SoundManager::startSpeech(int rep, int ht, int typ) { uint16 savph[501]; int tempo; + // Hack to avoid a crash in the ending version. To be removed when the speech are implemented + if ((rep == 141) && (typ == 0)) + return; + if (_vm->_soundOff) return; diff --git a/engines/mortevielle/utils.cpp b/engines/mortevielle/utils.cpp index 52e3b7c29b..5ca29d849c 100644 --- a/engines/mortevielle/utils.cpp +++ b/engines/mortevielle/utils.cpp @@ -67,9 +67,10 @@ bool MortevielleEngine::keyPressed() { * @remarks Originally called 'get_ch' */ int MortevielleEngine::getChar() { + bool end = false; // If there isn't any pending keypress, wait until there is - while (!shouldQuit() && _keypresses.empty()) { - keyPressed(); + while (!shouldQuit() && !end) { + end = keyPressed(); } // Return the top keypress @@ -290,7 +291,7 @@ void MortevielleEngine::handleAction() { if (_menu._menuSelected && (_currMenu == MENU_LOAD)) _savegameManager.loadGame((_currAction & 15) - 1); if (inkey == '\103') { /* F9 */ - temps = _dialogManager.show(_hintPctMessage, 1); + temps = _dialogManager.show(_hintPctMessage); return; } else if (inkey == '\77') { if ((_menuOpcode != OPCODE_NONE) && ((_currMenu == MENU_ACTION) || (_currMenu == MENU_SELF))) { @@ -502,48 +503,48 @@ void MortevielleEngine::showPeoplePresent(int bitIndex) { int xp = 580 - (_screenSurface.getStringWidth("LEO") / 2); for (int i = 1; i <= 8; ++i) - _menu.disableMenuItem(_menu._discussMenu[i]._menuId, _menu._discussMenu[i]._actionId); + _menu.disableMenuItem(_menu._discussMenu[i]); clearUpperRightPart(); if ((bitIndex & 128) == 128) { _screenSurface.putxy(xp, 24); _screenSurface.drawString("LEO", 4); - _menu.enableMenuItem(_menu._discussMenu[1]._menuId, _menu._discussMenu[1]._actionId); + _menu.enableMenuItem(_menu._discussMenu[1]); } if ((bitIndex & 64) == 64) { _screenSurface.putxy(xp, 32); _screenSurface.drawString("PAT", 4); - _menu.enableMenuItem(_menu._discussMenu[2]._menuId, _menu._discussMenu[2]._actionId); + _menu.enableMenuItem(_menu._discussMenu[2]); } if ((bitIndex & 32) == 32) { _screenSurface.putxy(xp, 40); _screenSurface.drawString("GUY", 4); - _menu.enableMenuItem(_menu._discussMenu[3]._menuId, _menu._discussMenu[3]._actionId); + _menu.enableMenuItem(_menu._discussMenu[3]); } if ((bitIndex & 16) == 16) { _screenSurface.putxy(xp, 48); _screenSurface.drawString("EVA", 4); - _menu.enableMenuItem(_menu._discussMenu[4]._menuId, _menu._discussMenu[4]._actionId); + _menu.enableMenuItem(_menu._discussMenu[4]); } if ((bitIndex & 8) == 8) { _screenSurface.putxy(xp, 56); _screenSurface.drawString("BOB", 4); - _menu.enableMenuItem(_menu._discussMenu[5]._menuId, _menu._discussMenu[5]._actionId); + _menu.enableMenuItem(_menu._discussMenu[5]); } if ((bitIndex & 4) == 4) { _screenSurface.putxy(xp, 64); _screenSurface.drawString("LUC", 4); - _menu.enableMenuItem(_menu._discussMenu[6]._menuId, _menu._discussMenu[6]._actionId); + _menu.enableMenuItem(_menu._discussMenu[6]); } if ((bitIndex & 2) == 2) { _screenSurface.putxy(xp, 72); _screenSurface.drawString("IDA", 4); - _menu.enableMenuItem(_menu._discussMenu[7]._menuId, _menu._discussMenu[7]._actionId); + _menu.enableMenuItem(_menu._discussMenu[7]); } if ((bitIndex & 1) == 1) { _screenSurface.putxy(xp, 80); _screenSurface.drawString("MAX", 4); - _menu.enableMenuItem(_menu._discussMenu[8]._menuId, _menu._discussMenu[8]._actionId); + _menu.enableMenuItem(_menu._discussMenu[8]); } _currBitIndex = bitIndex; } @@ -697,7 +698,7 @@ int MortevielleEngine::getPresenceStatsRedRoom() { */ void MortevielleEngine::displayAloneText() { for (int i = 1; i <= 8; ++i) - _menu.disableMenuItem(_menu._discussMenu[i]._menuId, _menu._discussMenu[i]._actionId); + _menu.disableMenuItem(_menu._discussMenu[i]); Common::String sYou = getEngineString(S_YOU); Common::String sAre = getEngineString(S_ARE); @@ -1334,7 +1335,7 @@ void MortevielleEngine::startDialog(int16 rep) { _mouse.hideMouse(); Common::String dialogStr = getString(rep + kDialogStringIndex); - _text.displayStr(dialogStr, 230, 4, 65, 24, 5); + _text.displayStr(dialogStr, 230, 4, 65, 26, 5); _dialogManager.drawF3F8(); key = 0; @@ -1712,7 +1713,7 @@ int MortevielleEngine::getRandomNumber(int minval, int maxval) { * @remarks Originally called 'aldepl' */ void MortevielleEngine::showMoveMenuAlert() { - _dialogManager.show(getEngineString(S_USE_DEP_MENU), 1); + _dialogManager.show(getEngineString(S_USE_DEP_MENU)); } /** @@ -1949,28 +1950,11 @@ void MortevielleEngine::setPal(int n) { } /** - * Engine function - Display a CGA pattern, using a specified palette - * @remarks Originally called 'outbloc' - */ -void MortevielleEngine::displayCGAPattern(int n, Pattern *p, nhom *pal) { - int addr = n * 404 + 0xd700; - - WRITE_LE_UINT16(&_curPict[addr], p->_tax); - WRITE_LE_UINT16(&_curPict[addr + 2], p->_tay); - addr += 4; - for (int i = 0; i < p->_tax; ++i) { - for (int j = 0; j < p->_tay; ++j) - _curPict[addr + j * p->_tax + i] = pal[n]._hom[p->_des[i + 1][j + 1]]; - } -} - -/** * Engine function - Load Palette from File * @remarks Originally called 'charpal' */ void MortevielleEngine::loadPalette() { Common::File f; - byte b; if (!f.open("fxx.mor")) { if (f.open("mfxx.mor")) @@ -1996,27 +1980,8 @@ void MortevielleEngine::loadPalette() { if (!f.open("cxx.mor")) error("Missing file - cxx.mor"); - for (int j = 0; j <= 90; ++j) { - _cgaPal[j]._p = f.readByte(); - for (int i = 0; i <= 15; ++i) { - nhom &with = _cgaPal[j]._a[i]; - - b = f.readByte(); - with._id = (uint)b >> 4; - with._hom[0] = ((uint)b >> 2) & 3; - with._hom[1] = b & 3; - } - } + // Skip CGA Palette and Patterns - _cgaPal[10]._a[9] = _cgaPal[10]._a[5]; - for (int j = 0; j <= 14; ++j) { - _patternArr[j]._tax = f.readByte(); - _patternArr[j]._tay = f.readByte(); - for (int i = 1; i <= 20; ++i) { - for (int k = 1; k <= 20; ++k) - _patternArr[j]._des[i][k] = f.readByte(); - } - } f.close(); } @@ -2029,8 +1994,8 @@ void MortevielleEngine::loadTexts() { Common::File ntpFile; _txxFileFl = false; - if (getLanguage() == Common::EN_ANY) { - warning("English version expected - Switching to DAT file"); + if (!useOriginalData()) { + warning("Using improved translation from DAT file"); return; } @@ -2534,7 +2499,7 @@ void MortevielleEngine::handleDescriptionText(int f, int mesgId) { displayTextInDescriptionBar(8, 182, 103, mesgId); if ((mesgId == 68) || (mesgId == 69)) _coreVar._availableQuestion[40] = '*'; - if ((mesgId == 104) && (_caff == CELLAR)) { + else if ((mesgId == 104) && (_caff == CELLAR)) { _coreVar._availableQuestion[36] = '*'; if (_coreVar._availableQuestion[39] == '*') { _coreVar._pctHintFound[3] = '*'; @@ -2663,8 +2628,8 @@ void MortevielleEngine::displayItemInHand(int objId) { if (objId != 500) strp = getString(objId - 501 + kInventoryStringIndex); - _menu.setText(_menu._inventoryMenu[8]._menuId, _menu._inventoryMenu[8]._actionId, strp); - _menu.disableMenuItem(_menu._inventoryMenu[8]._menuId, _menu._inventoryMenu[8]._actionId); + _menu.setText(_menu._inventoryMenu[8], strp); + _menu.disableMenuItem(_menu._inventoryMenu[8]); } /** @@ -2811,34 +2776,54 @@ int MortevielleEngine::getPresence(int roomId) { displayAloneText(); else { int h = 0; - if (roomId == DINING_ROOM) + switch (roomId) { + case DINING_ROOM: pres = getPresenceStatsDiningRoom(h); - else if (roomId == BUREAU) + break; + case BUREAU: pres = getPresenceStatsBureau(h); - else if (roomId == KITCHEN) + break; + case KITCHEN: pres = getPresenceStatsKitchen(); - else if ((roomId == ATTIC) || (roomId == CELLAR)) + break; + case ATTIC: + case CELLAR: pres = getPresenceStatsAttic(); - else if ((roomId == LANDING) || (roomId == ROOM26)) + break; + case LANDING: + case ROOM26: pres = getPresenceStatsLanding(); - else if (roomId == CHAPEL) + break; + case CHAPEL: pres = getPresenceStatsChapel(h); + break; + } pres += _coreVar._faithScore; rand = getRandomNumber(1, 100); if (rand > pres) { displayAloneText(); retVal = 0; } else { - if (roomId == DINING_ROOM) + switch (roomId) { + case DINING_ROOM: pres = setPresenceDiningRoom(h); - else if (roomId == BUREAU) + break; + case BUREAU: pres = setPresenceBureau(h); - else if ((roomId == KITCHEN) || (roomId == ATTIC) || (roomId == CELLAR)) + break; + case KITCHEN: + case ATTIC: + case CELLAR: pres = setPresenceKitchen(); - else if ((roomId == LANDING) || (roomId == ROOM26)) + break; + case LANDING: + case ROOM26: pres = setPresenceLanding(); - else if (roomId == CHAPEL) + break; + case CHAPEL: pres = setPresenceChapel(h); + break; + } retVal = pres; } } @@ -2902,18 +2887,27 @@ void MortevielleEngine::drawPicture() { displayAnimFrame(1, _openObjects[i]); } - if (_caff == ATTIC) { + switch (_caff) { + case ATTIC: if (_coreVar._atticBallHoleObjectId == 141) displayAnimFrame(1, 7); if (_coreVar._atticRodHoleObjectId == 159) displayAnimFrame(1, 6); - } else if ((_caff == CELLAR) && (_coreVar._cellarObjectId == 151)) - displayAnimFrame(1, 2); - else if ((_caff == SECRET_PASSAGE) && (_coreVar._secretPassageObjectId == 143)) - displayAnimFrame(1, 1); - else if ((_caff == WELL) && (_coreVar._wellObjectId != 0)) - displayAnimFrame(1, 1); + break; + case CELLAR: + if (_coreVar._cellarObjectId == 151) + displayAnimFrame(1, 2); + break; + case SECRET_PASSAGE: + if (_coreVar._secretPassageObjectId == 143) + displayAnimFrame(1, 1); + break; + case WELL: + if (_coreVar._wellObjectId != 0) + displayAnimFrame(1, 1); + break; + } } if (_caff < ROOM26) @@ -3226,7 +3220,7 @@ void MortevielleEngine::displayStatusArrow() { } while (!(qust || inRect || _anyone)); if (qust && (touch == '\103')) - _dialogManager.show(_hintPctMessage, 1); + _dialogManager.show(_hintPctMessage); } while (!((touch == '\73') || ((touch == '\104') && (_x != 0) && (_y != 0)) || (_anyone) || (inRect))); if (touch == '\73') diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp index 09c348f273..883a4d965b 100644 --- a/engines/sci/detection.cpp +++ b/engines/sci/detection.cpp @@ -105,9 +105,8 @@ static const PlainGameDescriptor s_sciGameTitles[] = { // === SCI2.1 games ======================================================== {"chest", "Inside the Chest"}, // aka Behind the Developer's Shield {"gk2", "The Beast Within: A Gabriel Knight Mystery"}, - // TODO: Inside The Chest/Behind the Developer's Shield {"kq7", "King's Quest VII: The Princeless Bride"}, - // TODO: King's Questions + {"kquestions", "King's Questions"}, {"lsl6hires", "Leisure Suit Larry 6: Shape Up or Slip Out!"}, {"mothergoosehires","Mixed-Up Mother Goose Deluxe"}, {"phantasmagoria", "Phantasmagoria"}, @@ -161,6 +160,7 @@ static const GameIdStrToEnum s_gameIdStrToEnum[] = { { "kq5", GID_KQ5 }, { "kq6", GID_KQ6 }, { "kq7", GID_KQ7 }, + { "kquestions", GID_KQUESTIONS }, { "laurabow", GID_LAURABOW }, { "laurabow2", GID_LAURABOW2 }, { "lighthouse", GID_LIGHTHOUSE }, @@ -242,6 +242,7 @@ static const OldNewIdTableEntry s_oldNewTable[] = { // kq5 is the same // kq6 is the same { "kq7cd", "kq7", SCI_VERSION_NONE }, + { "quizgame-demo", "kquestions", SCI_VERSION_NONE }, { "mm1", "laurabow", SCI_VERSION_NONE }, { "cb1", "laurabow", SCI_VERSION_NONE }, { "lb2", "laurabow2", SCI_VERSION_NONE }, diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h index 7c4638a992..92e77cead9 100644 --- a/engines/sci/detection_tables.h +++ b/engines/sci/detection_tables.h @@ -1656,6 +1656,14 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO | ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // King's Questions mini-game from the King's Quest Collection + // SCI interpreter version 2.000.000 + {"kquestions", "", { + {"resource.000", 0, "9b1cddecd4f0720d83661ba7aed28891", 162697}, + {"resource.map", 0, "93a2251fa64e729d7a7d2fe56b217c8e", 502}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO3(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_FB01_MIDI) }, + #endif // ENABLE_SCI32 // Laura Bow - English Amiga diff --git a/engines/sci/engine/features.cpp b/engines/sci/engine/features.cpp index 6005ac50be..c26c787fbd 100644 --- a/engines/sci/engine/features.cpp +++ b/engines/sci/engine/features.cpp @@ -467,9 +467,9 @@ bool GameFeatures::autoDetectSci21KernelType() { // seen it happen in the RAMA demo, thus we can assume that the // game is using a SCI2.1 table - // HACK: The Inside the Chest Demo doesn't have sounds at all, but - // it's using a SCI2 kernel - if (g_sci->getGameId() == GID_CHEST) { + // HACK: The Inside the Chest Demo and King's Questions minigame + // don't have sounds at all, but they're using a SCI2 kernel + if (g_sci->getGameId() == GID_CHEST || g_sci->getGameId() == GID_KQUESTIONS) { _sci21KernelType = SCI_VERSION_2; return true; } diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp index 20c5c52178..d4dddb6faf 100644 --- a/engines/sci/engine/script_patches.cpp +++ b/engines/sci/engine/script_patches.cpp @@ -953,10 +953,10 @@ const uint16 qfg1vgaPatchDialogHeader[] = { // When clicking on the crusher in room 331, Ego approaches him to talk to him, // an action that is handled by moveToCrusher::changeState in script 331. The -// scripts set Ego to move close to the crusher, but when Ego is running instead +// scripts set Ego to move close to the crusher, but when Ego is sneaking instead // of walking, the target coordinates specified by script 331 are never reached, // as Ego is making larger steps, and never reaches the required spot. This is an -// edge case that can occur when Ego is set to run. Normally, when clicking on +// edge case that can occur when Ego is set to sneak. Normally, when clicking on // the crusher, ego is supposed to move close to position 79, 165. We change it // to 85, 165, which is not an edge case thus the freeze is avoided. // Fixes bug #3585189. @@ -976,6 +976,25 @@ const uint16 qfg1vgaPatchMoveToCrusher[] = { PATCH_END }; +// Same pathfinding bug as above, where Ego is set to move to an impossible +// spot when sneaking. In GuardsTrumpet::changeState, we change the final +// location where Ego is moved from 111, 111 to 114, 114. Fixes bug #3604939. +const byte qfg1vgaSignatureMoveToCastleGate[] = { + 7, + 0x51, 0x1f, // class MoveTo + 0x36, // push + 0x39, 0x6f, // pushi 6f (111 - x) + 0x3c, // dup (111 - y) + 0x7c, // pushSelf + 0 +}; + +const uint16 qfg1vgaPatchMoveToCastleGate[] = { + PATCH_ADDTOOFFSET | +3, + 0x39, 0x72, // pushi 72 (114 - x) + PATCH_END +}; + // script, description, magic DWORD, adjust const SciScriptSignature qfg1vgaSignatures[] = { { 215, "fight event issue", 1, PATCH_MAGICDWORD(0x6d, 0x76, 0x51, 0x07), -1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents }, @@ -983,6 +1002,7 @@ const SciScriptSignature qfg1vgaSignatures[] = { { 814, "window text temp space", 1, PATCH_MAGICDWORD(0x3f, 0xba, 0x87, 0x00), 0, qfg1vgaSignatureTempSpace, qfg1vgaPatchTempSpace }, { 814, "dialog header offset", 3, PATCH_MAGICDWORD(0x5b, 0x04, 0x80, 0x36), 0, qfg1vgaSignatureDialogHeader, qfg1vgaPatchDialogHeader }, { 331, "moving to crusher", 1, PATCH_MAGICDWORD(0x51, 0x1f, 0x36, 0x39), 0, qfg1vgaSignatureMoveToCrusher, qfg1vgaPatchMoveToCrusher }, + { 41, "moving to castle gate", 1, PATCH_MAGICDWORD(0x51, 0x1f, 0x36, 0x39), 0, qfg1vgaSignatureMoveToCastleGate, qfg1vgaPatchMoveToCastleGate }, SCI_SIGNATUREENTRY_TERMINATOR }; diff --git a/engines/sci/graphics/picture.cpp b/engines/sci/graphics/picture.cpp index af372640da..91c72456a8 100644 --- a/engines/sci/graphics/picture.cpp +++ b/engines/sci/graphics/picture.cpp @@ -236,7 +236,9 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos byte *ptr = NULL; byte *headerPtr = inbuffer + headerPos; byte *rlePtr = inbuffer + rlePos; - int16 displaceX, displaceY; + // displaceX, displaceY fields are ignored, and may contain garbage + // (e.g. pic 261 in Dr. Brain 1 Spanish - bug #3614914) + //int16 displaceX, displaceY; byte priority = _addToFlag ? _priority : 0; byte clearColor; bool compression = true; @@ -251,8 +253,8 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos // Width/height here are always LE, even in Mac versions width = READ_LE_UINT16(headerPtr + 0); height = READ_LE_UINT16(headerPtr + 2); - displaceX = (signed char)headerPtr[4]; - displaceY = (unsigned char)headerPtr[5]; + //displaceX = (signed char)headerPtr[4]; + //displaceY = (unsigned char)headerPtr[5]; if (_resourceType == SCI_PICTURE_TYPE_SCI11) // SCI1.1 uses hardcoded clearcolor for pictures, even if cel header specifies otherwise clearColor = _screen->getColorWhite(); @@ -262,16 +264,16 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos } else { width = READ_SCI11ENDIAN_UINT16(headerPtr + 0); height = READ_SCI11ENDIAN_UINT16(headerPtr + 2); - displaceX = READ_SCI11ENDIAN_UINT16(headerPtr + 4); // probably signed?!? - displaceY = READ_SCI11ENDIAN_UINT16(headerPtr + 6); // probably signed?!? + //displaceX = READ_SCI11ENDIAN_UINT16(headerPtr + 4); // probably signed?!? + //displaceY = READ_SCI11ENDIAN_UINT16(headerPtr + 6); // probably signed?!? clearColor = headerPtr[8]; if (headerPtr[9] == 0) compression = false; } #endif - if (displaceX || displaceY) - error("unsupported embedded cel-data in picture"); + //if (displaceX || displaceY) + // error("unsupported embedded cel-data in picture"); // We will unpack cel-data into a temporary buffer and then plot it to screen // That needs to be done cause a mirrored picture may be requested diff --git a/engines/sci/sci.h b/engines/sci/sci.h index 3b9844b326..0a75e115fd 100644 --- a/engines/sci/sci.h +++ b/engines/sci/sci.h @@ -138,6 +138,7 @@ enum SciGameId { GID_KQ5, GID_KQ6, GID_KQ7, + GID_KQUESTIONS, GID_LAURABOW, GID_LAURABOW2, GID_LIGHTHOUSE, 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/gfx.cpp b/engines/scumm/gfx.cpp index 4c1fdaf673..4d65ccc88a 100644 --- a/engines/scumm/gfx.cpp +++ b/engines/scumm/gfx.cpp @@ -1888,7 +1888,7 @@ bool Gdi::drawStrip(byte *dstPtr, VirtScreen *vs, int x, int y, const int width, if (stripnr * 4 + 4 < smapLen) offset = READ_LE_UINT32(smap_ptr + stripnr * 4 + 4); } 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_v72he.cpp b/engines/scumm/he/script_v72he.cpp index d4920cf430..42bf9a4bb6 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) || 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/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/tsage/blue_force/blueforce_scenes0.cpp b/engines/tsage/blue_force/blueforce_scenes0.cpp index 95598babc6..f1f00599e0 100644 --- a/engines/tsage/blue_force/blueforce_scenes0.cpp +++ b/engines/tsage/blue_force/blueforce_scenes0.cpp @@ -888,6 +888,7 @@ void Scene60::Action1::signal() { break; case 4: remove(); + break; case 5: setDelay(120); break; diff --git a/engines/tsage/converse.cpp b/engines/tsage/converse.cpp index 909da62541..8ae6a0d97e 100644 --- a/engines/tsage/converse.cpp +++ b/engines/tsage/converse.cpp @@ -858,6 +858,11 @@ void StripManager::signal() { ++obj44Idx; if (_obj44List[obj44Idx]._field16[0]) { + // WORKAROUND: The _lookupList isn't always correctly initialised. But it always + // seems to be set to the R2_GLOBALS._stripManager_lookupList, so manually set it + if (!_lookupList) + _lookupList = R2_GLOBALS._stripManager_lookupList; + int f16Index = _lookupList[_obj44List[obj44Idx]._field16[0] - 1]; listId = _obj44List[obj44Idx]._field16[f16Index]; @@ -868,13 +873,16 @@ void StripManager::signal() { choiceStr = (const char *)&_script[0] + _obj44List[obj44Idx]._list[listIdx]._scriptOffset; } else { - for (int listIdx = 0; listIdx < OBJ0A_LIST_SIZE; ++listIdx) { + for (int listIdx = idx; listIdx < (OBJ0A_LIST_SIZE - 1); ++listIdx) { obj44._list[listIdx]._id = obj44._list[listIdx + 1]._id; obj44._list[listIdx]._scriptOffset = obj44._list[listIdx + 1]._scriptOffset; if (!obj44._list[listIdx + 1]._id) obj44._list[listIdx]._id = 0; } + + --idx; + continue; } } } diff --git a/engines/tsage/core.cpp b/engines/tsage/core.cpp index 6b4e2963a5..b632aa8e27 100644 --- a/engines/tsage/core.cpp +++ b/engines/tsage/core.cpp @@ -2111,8 +2111,13 @@ int SceneObject::getFrameCount() { void SceneObject::animEnded() { _animateMode = ANIM_MODE_NONE; - if (_endAction) - _endAction->signal(); + if (_endAction) { + Action *endAction = _endAction; + if (g_vm->getGameID() == GType_Ringworld2) + _endAction = NULL; + + endAction->signal(); + } } int SceneObject::changeFrame() { @@ -4242,9 +4247,10 @@ void SceneHandler::process(Event &event) { // Scan the item list to find one the mouse is within SynchronizedList<SceneItem *>::iterator i; for (i = g_globals->_sceneItems.begin(); i != g_globals->_sceneItems.end(); ++i) { - if ((*i)->contains(event.mousePos)) { + SceneItem *item = *i; + if (item->contains(event.mousePos)) { // Pass the action to the item - bool handled = (*i)->startAction(g_globals->_events.getCursor(), event); + bool handled = item->startAction(g_globals->_events.getCursor(), event); if (!handled) // Item wasn't handled, keep scanning continue; diff --git a/engines/tsage/globals.cpp b/engines/tsage/globals.cpp index 5c4fa967e5..6d1f4ced5c 100644 --- a/engines/tsage/globals.cpp +++ b/engines/tsage/globals.cpp @@ -389,7 +389,7 @@ void Ringworld2Globals::reset() { _scrollFollower = &_player; // Reset fields - Common::fill(&_fadePaletteMap[0][0], &_fadePaletteMap[10][256], 0); + Common::fill(&_fadePaletteMap[0][0], &_fadePaletteMap[9][256], 0); Common::fill(&_paletteMap[0], &_paletteMap[4096], 0); _fadePaletteFlag = false; @@ -404,7 +404,7 @@ void Ringworld2Globals::reset() { _v565E7 = 0; _v565E9 = -5; _v565EB = 26; - _v565F5 = 0; + _foodCount = 0; _v565F6 = 0; _v565F8 = 0; _v565FA = 0; @@ -449,7 +449,7 @@ void Ringworld2Globals::reset() { _v56613[(17 * 4) + 1] = 1; _v566A6 = 3800; - _v566A3 = 2; + _landerSuitNumber = 2; _v566A4 = 1; _v566A5 = 0; _v566A8 = 5; @@ -519,11 +519,11 @@ void Ringworld2Globals::synchronize(Serializer &s) { s.syncAsSint16LE(_v565E7); s.syncAsSint16LE(_v565E9); s.syncAsSint16LE(_v565EB); - s.syncAsSint16LE(_v565F5); + s.syncAsSint16LE(_foodCount); s.syncAsSint16LE(_v565F6); s.syncAsSint16LE(_v565F8); s.syncAsSint16LE(_v565FA); - s.syncAsSint16LE(_v566A3); + s.syncAsSint16LE(_landerSuitNumber); s.syncAsSint16LE(_v566A6); s.syncAsSint16LE(_v56A93); s.syncAsSint16LE(_scene1925CurrLevel); // _v56A9C diff --git a/engines/tsage/globals.h b/engines/tsage/globals.h index ed27ff0556..e91a74b2ca 100644 --- a/engines/tsage/globals.h +++ b/engines/tsage/globals.h @@ -269,7 +269,7 @@ public: int _v565E7; int _v565E9; int _v565EB; - int _v565F5; + int _foodCount; int _v565F6; int _v565F8; int _v565FA; @@ -280,7 +280,7 @@ public: byte _v566A4; byte _v566A5; int _v566A6; - byte _v566A3; + byte _landerSuitNumber; byte _v566A8; byte _v566A9; byte _v566AA; diff --git a/engines/tsage/ringworld2/ringworld2_logic.cpp b/engines/tsage/ringworld2/ringworld2_logic.cpp index 2a8e50bd67..7104e1f80c 100644 --- a/engines/tsage/ringworld2/ringworld2_logic.cpp +++ b/engines/tsage/ringworld2/ringworld2_logic.cpp @@ -1325,7 +1325,7 @@ GfxSurface SceneActor::getFrame() { /*--------------------------------------------------------------------------*/ -SceneArea::SceneArea(): EventHandler() { +SceneArea::SceneArea(): SceneItem() { _enabled = true; _insideArea = false; _savedCursorNum = CURSOR_NONE; @@ -1429,6 +1429,7 @@ void SceneExit::process(Event &event) { /*--------------------------------------------------------------------------*/ void SceneAreaObject::remove() { + R2_GLOBALS._sceneItems.remove(this); _object1.remove(); SceneArea::remove(); --R2_GLOBALS._insetUp; @@ -1438,19 +1439,22 @@ void SceneAreaObject::process(Event &event) { if (_insetCount == R2_GLOBALS._insetUp) { CursorType cursor = R2_GLOBALS._events.getCursor(); - if (_bounds.contains(event.mousePos)) { + if (_object1._bounds.contains(event.mousePos)) { // Cursor moving in bounded area if (cursor == _cursorNum) { R2_GLOBALS._events.setCursor(_savedCursorNum); } } else if (event.mousePos.y < 168) { - if (_cursorNum != cursor) + if (_cursorNum != cursor) { // Cursor moved outside bounded area - R2_GLOBALS._events.setCursor(_savedCursorNum); - + _savedCursorNum = R2_GLOBALS._events.getCursor(); + R2_GLOBALS._events.setCursor(CURSOR_INVALID); + } + if (event.eventType == EVENT_BUTTON_DOWN) { - R2_GLOBALS._events.setCursor(_savedCursorNum); event.handled = true; + R2_GLOBALS._events.setCursor(_savedCursorNum); + remove(); } } } @@ -1470,7 +1474,7 @@ void SceneAreaObject::setDetails(int visage, int strip, int frameNumber, const C } void SceneAreaObject::setDetails(int resNum, int lookLineNum, int talkLineNum, int useLineNum) { - ((SceneHotspot *)(this))->setDetails(resNum, lookLineNum, talkLineNum, useLineNum, + _object1.setDetails(resNum, lookLineNum, talkLineNum, useLineNum, 2, (SceneItem *)NULL); } diff --git a/engines/tsage/ringworld2/ringworld2_logic.h b/engines/tsage/ringworld2/ringworld2_logic.h index 1b4b7fca1f..62a596146b 100644 --- a/engines/tsage/ringworld2/ringworld2_logic.h +++ b/engines/tsage/ringworld2/ringworld2_logic.h @@ -42,9 +42,8 @@ public: static Scene *createScene(int sceneNumber); }; -class SceneArea: public EventHandler { +class SceneArea: public SceneItem { public: - Rect _bounds; bool _enabled; bool _insideArea; CursorType _cursorNum; @@ -54,9 +53,12 @@ public: SceneArea(); void setDetails(const Rect &bounds, CursorType cursor); + virtual Common::String getClassName() { return "SceneArea"; } virtual void synchronize(Serializer &s); virtual void remove(); virtual void process(Event &event); + virtual bool startAction(CursorType action, Event &event) { return false; } + virtual void doAction(int action) {} }; class SceneExit: public SceneArea { diff --git a/engines/tsage/ringworld2/ringworld2_scenes0.cpp b/engines/tsage/ringworld2/ringworld2_scenes0.cpp index 8d35fc7222..34aaf98e51 100644 --- a/engines/tsage/ringworld2/ringworld2_scenes0.cpp +++ b/engines/tsage/ringworld2/ringworld2_scenes0.cpp @@ -392,11 +392,11 @@ void Scene100::dispatch() { * *--------------------------------------------------------------------------*/ -bool Scene125::Object5::startAction(CursorType action, Event &event) { +bool Scene125::Food::startAction(CursorType action, Event &event) { if (action == CURSOR_USE) return true; - else - return SceneActor::startAction(action, event); + + return SceneActor::startAction(action, event); } /*--------------------------------------------------------------------------*/ @@ -631,9 +631,9 @@ void Scene125::postInit(SceneObjectList *OwnerList) { _infoDisk.setPosition(Common::Point(47, 167)); } - _object6.postInit(); - _object6.setup(162, 1, 1); - _object6.setPosition(Common::Point(214, 168)); + _foodDispenser.postInit(); + _foodDispenser.setup(162, 1, 1); + _foodDispenser.setPosition(Common::Point(214, 168)); _diskSlot.setDetails(Rect(27, 145, 81, 159), 126, 9, -1, -1, 1, NULL); _item3.setDetails(Rect(144, 119, 286, 167), 126, 6, 7, 8, 1, NULL); @@ -656,8 +656,8 @@ void Scene125::signal() { _icon4.postInit(); _icon4._sceneRegionId = 5; - _sceneMode = 2; setAction(&_sequenceManager, this, 127, &_icon1, &_icon2, &_icon3, &_icon4, &R2_GLOBALS._player, NULL); + _sceneMode = 2; break; case 2: _icon1.setup(160, 1, 1); @@ -698,7 +698,7 @@ void Scene125::signal() { _icon6._sceneRegionId = 8; consoleAction(5); - R2_GLOBALS._player.enableControl(); + R2_GLOBALS._player.enableControl(CURSOR_USE); R2_GLOBALS._player._canWalk = false; break; case 10: @@ -971,14 +971,17 @@ void Scene125::consoleAction(int id) { case 15: consoleAction(3); - if (R2_GLOBALS._v565F5 < 3) { + if (R2_GLOBALS._foodCount < 3) { R2_GLOBALS._player.disableControl(); - _object5.postInit(); - _object5.setup(162, 2, 2); - _object5.setPosition(Common::Point(216, UI_INTERFACE_Y)); + _food.postInit(); + _food.setup(162, 2, 2); + _food.setPosition(Common::Point(216, UI_INTERFACE_Y)); + + R2_GLOBALS._foodCount += 2; - R2_GLOBALS._v565F5 += 2; - } else if (R2_GLOBALS._v565F5 == 3) { + _sceneMode = 128; + this->setAction(&_sequenceManager, this, 128, &_foodDispenser, &_food, NULL); + } else if (R2_GLOBALS._foodCount == 3) { SceneItem::display2(126, 13); } else { SceneItem::display2(126, 14); @@ -987,13 +990,16 @@ void Scene125::consoleAction(int id) { case 16: consoleAction(3); - if (R2_GLOBALS._v565F5 < 4) { + if (R2_GLOBALS._foodCount < 4) { R2_GLOBALS._player.disableControl(); - _object5.postInit(); - _object5.setup(162, 2, 3); - _object5.setPosition(Common::Point(218, UI_INTERFACE_Y)); + _food.postInit(); + _food.setup(162, 2, 3); + _food.setPosition(Common::Point(218, UI_INTERFACE_Y)); + + ++R2_GLOBALS._foodCount; - ++R2_GLOBALS._v565F5; + _sceneMode = 128; + this->setAction(&_sequenceManager, this, 128, &_foodDispenser, &_food, NULL); } else { SceneItem::display2(126, 15); } @@ -1001,13 +1007,16 @@ void Scene125::consoleAction(int id) { case 17: consoleAction(3); - if (R2_GLOBALS._v565F5 < 4) { + if (R2_GLOBALS._foodCount < 4) { R2_GLOBALS._player.disableControl(); - _object5.postInit(); - _object5.setup(162, 2, 1); - _object5.setPosition(Common::Point(215, UI_INTERFACE_Y)); + _food.postInit(); + _food.setup(162, 2, 1); + _food.setPosition(Common::Point(215, UI_INTERFACE_Y)); - ++R2_GLOBALS._v565F5; + ++R2_GLOBALS._foodCount; + + _sceneMode = 128; + this->setAction(&_sequenceManager, this, 128, &_foodDispenser, &_food, NULL); } else { SceneItem::display2(126, 16); } @@ -2026,7 +2035,7 @@ void Scene200::EastExit::changeScene() { Scene200 *scene = (Scene200 *)R2_GLOBALS._sceneManager._scene; _enabled = false; - R2_GLOBALS._player.disableControl(); + R2_GLOBALS._player.disableControl(CURSOR_WALK); scene->_sceneMode = 206; scene->setAction(&scene->_sequenceManager, scene, 206, &R2_GLOBALS._player, NULL); } @@ -2035,7 +2044,7 @@ void Scene200::WestExit::changeScene() { Scene200 *scene = (Scene200 *)R2_GLOBALS._sceneManager._scene; _enabled = false; - R2_GLOBALS._player.disableControl(); + R2_GLOBALS._player.disableControl(CURSOR_WALK); scene->_sceneMode = 208; scene->setAction(&scene->_sequenceManager, scene, 208, &R2_GLOBALS._player, NULL); } @@ -3142,7 +3151,7 @@ bool Scene300::Doorway::startAction(CursorType action, Event &event) { if (action == CURSOR_USE) { if ((R2_GLOBALS._player._characterIndex == R2_QUINN) && - (!R2_GLOBALS.getFlag(44) || R2_GLOBALS._player._characterScene[R2_MIRANDA] == 500)) { + (!R2_GLOBALS.getFlag(44) || R2_GLOBALS._player._characterScene[R2_SEEKER] == 500)) { R2_GLOBALS._player.disableControl(); scene->_sceneMode = 301; scene->setAction(&scene->_sequenceManager1, scene, 301, &R2_GLOBALS._player, this, NULL); @@ -3559,7 +3568,7 @@ void Scene300::signal() { break; case 16: - if (_stripManager._field2E8 == 1) { + if (_stripManager._exitMode == 1) { R2_GLOBALS._player.setAction(NULL); R2_GLOBALS._sceneManager.changeScene(1000); } else { @@ -4813,6 +4822,11 @@ void Scene400::dispatch() { * *--------------------------------------------------------------------------*/ +Scene500::PanelDialog::Button::Button() { + _buttonId = 0; + _buttonDown = false; +} + bool Scene500::ControlPanel::startAction(CursorType action, Event &event) { Scene500 *scene = (Scene500 *)R2_GLOBALS._sceneManager._scene; @@ -4835,17 +4849,18 @@ bool Scene500::ControlPanel::startAction(CursorType action, Event &event) { /*--------------------------------------------------------------------------*/ -bool Scene500::Object2::startAction(CursorType action, Event &event) { +bool Scene500::Seeker::startAction(CursorType action, Event &event) { Scene500 *scene = (Scene500 *)R2_GLOBALS._sceneManager._scene; if (action == CURSOR_TALK) { R2_GLOBALS._player.disableControl(); - if (R2_GLOBALS._player._characterIndex == 1) { + if (R2_GLOBALS._player._characterIndex == R2_QUINN) { scene->_stripNumber = R2_GLOBALS.getFlag(26) ? 1101 : 1103; } else { scene->_stripNumber = R2_GLOBALS.getFlag(26) ? 1102 : 1105; } + scene->_sceneMode = 524; scene->setAction(&scene->_sequenceManager1, scene, 524, &R2_GLOBALS._player, NULL); return true; } else { @@ -4853,7 +4868,7 @@ bool Scene500::Object2::startAction(CursorType action, Event &event) { } } -bool Scene500::Object3::startAction(CursorType action, Event &event) { +bool Scene500::Suit::startAction(CursorType action, Event &event) { Scene500 *scene = (Scene500 *)R2_GLOBALS._sceneManager._scene; switch (action) { @@ -4864,20 +4879,20 @@ bool Scene500::Object3::startAction(CursorType action, Event &event) { case CURSOR_USE: if (R2_GLOBALS._player._characterIndex == R2_QUINN) { if ((_strip != 3) && (_strip != 7)) - SceneItem::display2(500, _strip); + SceneItem::display2(500, _strip + 25); else if (R2_GLOBALS.getFlag(26)) { R2_GLOBALS._player.disableControl(); scene->_stripNumber = 1103; scene->_sceneMode = 524; - scene->setAction(&scene->_sequenceManager1, scene, 524, &R2_GLOBALS._player, NULL); + scene->setAction(&scene->_sequenceManager1, scene, 512, &R2_GLOBALS._player, NULL); } else if (!R2_GLOBALS.getFlag(28)) SceneItem::display2(500, 41); - else if (!R2_GLOBALS.getFlag(40)) + else if (!R2_GLOBALS.getFlag(25)) SceneItem::display2(500, 40); else { R2_GLOBALS._player.disableControl(); scene->_sceneMode = 512; - scene->setAction(&scene->_sequenceManager1, scene, 524, &R2_GLOBALS._player, &scene->_object3, NULL); + scene->setAction(&scene->_sequenceManager1, scene, 512, &R2_GLOBALS._player, &scene->_suit, NULL); R2_GLOBALS.setFlag(26); } } else { @@ -4893,7 +4908,7 @@ bool Scene500::Object3::startAction(CursorType action, Event &event) { else { R2_GLOBALS._player.disableControl(); scene->_sceneMode = 515; - scene->setAction(&scene->_sequenceManager1, scene, 515, &R2_GLOBALS._player, &scene->_object3, NULL); + scene->setAction(&scene->_sequenceManager1, scene, 515, &R2_GLOBALS._player, &scene->_suit, NULL); R2_GLOBALS.setFlag(28); } return true; @@ -4903,12 +4918,7 @@ bool Scene500::Object3::startAction(CursorType action, Event &event) { return true; default: - if (action < R2_LAST_INVENT) { - SceneItem::display2(500, action); - return true; - } else { - return SceneActor::startAction(action, event); - } + return SceneActor::startAction(action, event); } } @@ -4976,7 +4986,7 @@ bool Scene500::AirLock::startAction(CursorType action, Event &event) { R2_GLOBALS._player.disableControl(); scene->_sceneMode = (R2_GLOBALS._player._characterIndex == R2_QUINN) ? 521 : 522; scene->setAction(&scene->_sequenceManager1, scene, scene->_sceneMode, &R2_GLOBALS._player, - &scene->_object2, &scene->_airLock, NULL); + &scene->_seeker, &scene->_airLock, NULL); return true; } else { return SceneActor::startAction(action, event); @@ -5045,7 +5055,36 @@ bool Scene500::Locker2::startAction(CursorType action, Event &event) { } } -bool Scene500::Object::startAction(CursorType action, Event &event) { +/*--------------------------------------------------------------------------*/ + +void Scene500::PanelDialog::setDetails(int visage, int strip, int frameNumber, + const Common::Point &pt) { + SceneAreaObject::setDetails(visage, strip, frameNumber, pt); + SceneAreaObject::setDetails(500, 43, 32, 45); + + _button1.setupButton(1); + _button2.setupButton(2); + _button3.setupButton(3); +} + +void Scene500::PanelDialog::remove() { + Scene500 *scene = (Scene500 *)BF_GLOBALS._sceneManager._scene; + scene->_sceneAreas.remove(&_button1); + scene->_sceneAreas.remove(&_button2); + scene->_sceneAreas.remove(&_button3); + + _button1.remove(); + _button2.remove(); + _button3.remove(); + + SceneAreaObject::remove(); + + R2_GLOBALS._player.disableControl(); + scene->_sceneMode = 511; + scene->setAction(&scene->_sequenceManager1, scene, 511, &R2_GLOBALS._player, NULL); +} + +bool Scene500::PanelDialog::Button::startAction(CursorType action, Event &event) { if (action == CURSOR_USE) { return false; } else { @@ -5053,6 +5092,119 @@ bool Scene500::Object::startAction(CursorType action, Event &event) { } } +void Scene500::PanelDialog::Button::setupButton(int buttonId) { + _buttonId = buttonId; + _buttonDown = false; + SceneActor::postInit(); + setup(500, 7, 1); + fixPriority(251); + + switch (_buttonId) { + case 1: + setPosition(Common::Point(139, 78)); + break; + case 2: + setPosition(Common::Point(139, 96)); + break; + case 3: + setPosition(Common::Point(139, 114)); + break; + default: + break; + } + + Scene500 *scene = (Scene500 *)BF_GLOBALS._sceneManager._scene; + scene->_sceneAreas.push_front(this); +} + +void Scene500::PanelDialog::Button::synchronize(Serializer &s) { + SceneActor::synchronize(s); + + s.syncAsSint16LE(_buttonId); + s.syncAsSint16LE(_buttonDown); +} + +void Scene500::PanelDialog::Button::process(Event &event) { + if ((event.eventType == EVENT_BUTTON_DOWN) && + (R2_GLOBALS._events.getCursor() == CURSOR_USE) && + _bounds.contains(event.mousePos) && !_buttonDown) { + _buttonDown = true; + event.handled = true; + setFrame(2); + } + + if ((event.eventType == EVENT_BUTTON_UP) && _buttonDown) { + setFrame(1); + _buttonDown = false; + event.handled = true; + + doButtonPress(); + } +} + +void Scene500::PanelDialog::Button::doButtonPress() { + Scene500 *scene = (Scene500 *)R2_GLOBALS._sceneManager._scene; + + if (R2_GLOBALS.getFlag(28)) { + SceneItem::display2(500, 48); + } else { + R2_GLOBALS._player.disableControl(); + scene->_sceneMode = _buttonId; + + switch (_buttonId) { + case 1: + if (--R2_GLOBALS._landerSuitNumber == 0) + R2_GLOBALS._landerSuitNumber = 3; + + if (R2_GLOBALS.getFlag(35)) { + scene->_sceneMode = 5; + scene->setAction(&scene->_sequenceManager1, scene, 509, &scene->_object1, + &scene->_suit, &scene->_object8, NULL); + } else { + scene->_sound1.play(127); + scene->_object1.animate(ANIM_MODE_6, scene); + } + break; + + case 2: + if (++R2_GLOBALS._landerSuitNumber == 4) + R2_GLOBALS._v566A4 = 1; + + if (R2_GLOBALS.getFlag(35)) { + scene->_sceneMode = 6; + scene->setAction(&scene->_sequenceManager1, scene, 509, &scene->_object1, + &scene->_suit, &scene->_object8, NULL); + } else { + scene->_sound1.play(127); + scene->_object1.animate(ANIM_MODE_6, scene); + } + break; + + case 3: + if (R2_GLOBALS.getFlag(35)) { + scene->_sceneMode = 509; + scene->setAction(&scene->_sequenceManager1, scene, 509, &scene->_object1, + &scene->_suit, &scene->_object8, NULL); + } else { + scene->_suit.postInit(); + scene->_suit.hide(); + scene->_suit._effect = 1; + scene->_suit.setDetails(500, -1, -1, -1, 2, (SceneItem *)NULL); + scene->_suit.setup(502, R2_GLOBALS._landerSuitNumber + 2, 1); + + scene->setAction(&scene->_sequenceManager1, scene, 508, + &R2_GLOBALS._player, &scene->_object1, &scene->_suit, + &scene->_object8, NULL); + R2_GLOBALS.setFlag(35); + } + break; + + default: + break; + } + } +} + /*--------------------------------------------------------------------------*/ void Scene500::postInit(SceneObjectList *OwnerList) { @@ -5071,23 +5223,23 @@ void Scene500::postInit(SceneObjectList *OwnerList) { if (R2_GLOBALS._player._characterIndex == R2_QUINN) { R2_GLOBALS._walkRegions.disableRegion(1); - _object2.postInit(); - _object2._effect = 1; - _object2.setup(1505, 1, 1); - _object2._moveDiff.x = 5; - _object2.setPosition(Common::Point(42, 151)); - _object2.setDetails(500, 34, 35, 36, 1, (SceneItem *)NULL); + _seeker.postInit(); + _seeker._effect = 1; + _seeker.setup(1505, 1, 1); + _seeker._moveDiff.x = 5; + _seeker.setPosition(Common::Point(42, 151)); + _seeker.setDetails(500, 34, 35, 36, 1, (SceneItem *)NULL); } else if (R2_GLOBALS._player._characterScene[R2_QUINN] == 500) { - _object2.postInit(); - _object2._effect = 1; - _object2.setup(R2_GLOBALS.getFlag(26) ? 1500 : 10, 1, 1); - _object2.setPosition(Common::Point(42, 151)); + _seeker.postInit(); + _seeker._effect = 1; + _seeker.setup(R2_GLOBALS.getFlag(26) ? 1500 : 10, 1, 1); + _seeker.setPosition(Common::Point(42, 151)); R2_GLOBALS._walkRegions.disableRegion(1); R2_GLOBALS._walkRegions.disableRegion(2); R2_GLOBALS._walkRegions.disableRegion(3); - _object2.setDetails(500, 37, 38, -1, 1, (SceneItem *)NULL); + _seeker.setDetails(500, 37, 38, -1, 1, (SceneItem *)NULL); } } @@ -5167,16 +5319,16 @@ void Scene500::postInit(SceneObjectList *OwnerList) { } else { _object8.setup(500, 8, 7); - _object3.postInit(); - _object3._effect = 1; - _object3.setPosition(Common::Point(247, 52)); - _object3.setDetails(500, -1, -1, -1, 2, (SceneItem *)NULL); + _suit.postInit(); + _suit._effect = 1; + _suit.setPosition(Common::Point(247, 52)); + _suit.setDetails(500, -1, -1, -1, 2, (SceneItem *)NULL); if (!R2_GLOBALS.getFlag(26)) { if (R2_GLOBALS.getFlag(28)) - _object3.setup(502, 7, 2); + _suit.setup(502, 7, 2); else - _object3.setup(502, R2_GLOBALS._v566A3 + 2, 7); + _suit.setup(502, R2_GLOBALS._landerSuitNumber + 2, 7); } } @@ -5222,7 +5374,7 @@ void Scene500::signal() { _object1.animate(ANIM_MODE_6, this); R2_GLOBALS.clearFlag(35); - _object3.remove(); + _suit.remove(); R2_GLOBALS._player.enableControl(); break; case 6: @@ -5231,7 +5383,7 @@ void Scene500::signal() { _object1.animate(ANIM_MODE_5, this); R2_GLOBALS.clearFlag(35); - _object3.remove(); + _suit.remove(); R2_GLOBALS._player.enableControl(); break; case 7: @@ -5239,14 +5391,14 @@ void Scene500::signal() { _object8.animate(ANIM_MODE_6, this); R2_GLOBALS.clearFlag(35); - _object3.remove(); + _suit.remove(); R2_GLOBALS._player.enableControl(); break; case 500: R2_GLOBALS._sceneManager.changeScene(700); break; case 501: - if (R2_GLOBALS._player._characterScene[R2_MIRANDA] == 500) { + if (R2_GLOBALS._player._characterScene[R2_SEEKER] == 500) { _stripNumber = 1100; _sceneMode = 523; setAction(&_sequenceManager1, this, 523, &R2_GLOBALS._player, NULL); @@ -5277,7 +5429,7 @@ void Scene500::signal() { break; case 506: case 518: - R2_GLOBALS.setFlag(11); + R2_GLOBALS.setFlag(12); R2_GLOBALS._player.enableControl(); break; case 507: @@ -5287,12 +5439,12 @@ void Scene500::signal() { break; case 509: R2_GLOBALS.clearFlag(35); - _object3.remove(); + _suit.remove(); R2_GLOBALS._player.enableControl(); break; case 510: R2_GLOBALS._player.enableControl(); - _area1.setDetails(500, 6, 1, Common::Point(160, 120)); + _panelDialog.setDetails(500, 6, 1, Common::Point(160, 120)); R2_GLOBALS._player.enableControl(); break; case 513: @@ -5435,7 +5587,8 @@ GfxSurface Scene600::Actor4::getFrame() { } bool Scene600::Doorway::startAction(CursorType action, Event &event) { - if ((action < CURSOR_WALK) && (action >= R2CURSORS_START)) + // CHECKME: The original is checking is action == 0. To be verified. + if (action == INV_NONE) return false; if (action != CURSOR_USE) @@ -5547,7 +5700,8 @@ bool Scene600::Laser::startAction(CursorType action, Event &event) { bool Scene600::Aerosol::startAction(CursorType action, Event &event) { Scene600 *scene = (Scene600 *)R2_GLOBALS._sceneManager._scene; - if ((action < CURSOR_WALK) && (action >= R2CURSORS_START)) { + // CHECKME: The original is checking is action == 0. To be verified. + if (action == INV_NONE) { return false; } else if (action == CURSOR_USE) { R2_GLOBALS._player.disableControl(); @@ -5868,26 +6022,26 @@ bool Scene700::Item11::startAction(CursorType action, Event &event) { return NamedHotspot::startAction(action, event); } -bool Scene700::Item12::startAction(CursorType action, Event &event) { +bool Scene700::HandGrip::startAction(CursorType action, Event &event) { Scene700 *scene = (Scene700 *)R2_GLOBALS._sceneManager._scene; switch (action) { case R2_CABLE_HARNESS: R2_GLOBALS._player.disableControl(); - scene->_actor5.postInit(); - scene->_actor5.setup(701, 3, 2); - scene->_actor5.setPosition(Common::Point(243, 98)); - scene->_actor5.setDetails(700, 37, -1, -1, 2, (SceneItem *) NULL); - scene->_actor5.hide(); + scene->_cable.postInit(); + scene->_cable.setup(701, 3, 2); + scene->_cable.setPosition(Common::Point(243, 98)); + scene->_cable.setDetails(700, 37, -1, -1, 2, (SceneItem *) NULL); + scene->_cable.hide(); scene->_sceneMode = 20; break; case R2_ATTRACTOR_CABLE_HARNESS: R2_GLOBALS._player.disableControl(); - scene->_actor5.postInit(); - scene->_actor5.setup(701, 2, 8); - scene->_actor5.setPosition(Common::Point(243, 98)); - scene->_actor5.setDetails(700, 38, -1, -1, 2, (SceneItem *) NULL); - scene->_actor5.hide(); + scene->_cable.postInit(); + scene->_cable.setup(701, 2, 8); + scene->_cable.setPosition(Common::Point(243, 98)); + scene->_cable.setDetails(700, 38, -1, -1, 2, (SceneItem *) NULL); + scene->_cable.hide(); scene->_sceneMode = 21; break; default: @@ -5895,7 +6049,7 @@ bool Scene700::Item12::startAction(CursorType action, Event &event) { break; } - scene->setAction(&scene->_sequenceManager, scene, 707, &R2_GLOBALS._player, &scene->_actor5, NULL); + scene->setAction(&scene->_sequenceManager, scene, 707, &R2_GLOBALS._player, &scene->_cable, NULL); return true; } @@ -5947,7 +6101,7 @@ bool Scene700::Actor4::startAction(CursorType action, Event &event) { return true; } -bool Scene700::Actor5::startAction(CursorType action, Event &event) { +bool Scene700::Cable::startAction(CursorType action, Event &event) { Scene700 *scene = (Scene700 *)R2_GLOBALS._sceneManager._scene; switch (action) { @@ -5981,9 +6135,9 @@ bool Scene700::Actor5::startAction(CursorType action, Event &event) { break; case R2_ATTRACTOR_UNIT: R2_GLOBALS._player.disableControl(); - if (R2_INVENTORY.getObjectScene(R2_CABLE_HARNESS) == 700) { + if (R2_INVENTORY.getObjectScene(R2_CABLE_HARNESS) != 700) { scene->_sceneMode = 706; - scene->setAction(&scene->_sequenceManager, scene, 706, &R2_GLOBALS._player, &scene->_actor5, NULL); + scene->setAction(&scene->_sequenceManager, scene, 706, &R2_GLOBALS._player, &scene->_cable, NULL); } else { scene->_sceneMode = 15; Common::Point pt(_position.x - 12, _position.y + 1); @@ -6065,20 +6219,20 @@ void Scene700::postInit(SceneObjectList *OwnerList) { _actor9.setDetails(700, 33, -1, 35, 1, (SceneItem *) NULL); if ((R2_INVENTORY.getObjectScene(R2_CABLE_HARNESS) != 1) && (R2_INVENTORY.getObjectScene(R2_ATTRACTOR_CABLE_HARNESS) != 1)) { - _actor5.postInit(); - _actor5.fixPriority(10); + _cable.postInit(); + _cable.fixPriority(10); switch (R2_INVENTORY.getObjectScene(R2_ATTRACTOR_CABLE_HARNESS)) { case 0: switch (R2_INVENTORY.getObjectScene(R2_CABLE_HARNESS)) { case 0: - _actor5.setup(701, 3, 2); - _actor5.setPosition(Common::Point(243, 98)); - _actor5.setDetails(700, 37, -1, -1, 1, (SceneItem *) NULL); + _cable.setup(701, 3, 2); + _cable.setPosition(Common::Point(243, 98)); + _cable.setDetails(700, 37, -1, -1, 1, (SceneItem *) NULL); break; case 700: - _actor5.setup(701, 3, 1); - _actor5.setPosition(Common::Point(356 - (R2_GLOBALS._v565EB * 8), 148 - (((R2_GLOBALS._v565E9 + 10) / 5) * 4))); - _actor5.setDetails(700, 37, -1, -1, 1, (SceneItem *) NULL); + _cable.setup(701, 3, 1); + _cable.setPosition(Common::Point(356 - (R2_GLOBALS._v565EB * 8), 148 - (((R2_GLOBALS._v565E9 + 10) / 5) * 4))); + _cable.setDetails(700, 37, -1, -1, 1, (SceneItem *) NULL); break; default: break; @@ -6088,22 +6242,22 @@ void Scene700::postInit(SceneObjectList *OwnerList) { switch (R2_INVENTORY.getObjectScene(R2_CABLE_HARNESS)) { case 0: if ((R2_GLOBALS._v565E5 != 0) && (R2_GLOBALS._electromagnetChangeAmount == 20) && (R2_GLOBALS._electromagnetZoom == 70)) - _actor5.setup(701, 2, 1); + _cable.setup(701, 2, 1); else - _actor5.setup(701, 2, 8); - _actor5.setPosition(Common::Point(243, 98)); - _actor5.fixPriority(77); - _actor5.setDetails(700, 38, -1, -1, 1, (SceneItem *) NULL); + _cable.setup(701, 2, 8); + _cable.setPosition(Common::Point(243, 98)); + _cable.fixPriority(77); + _cable.setDetails(700, 38, -1, -1, 1, (SceneItem *) NULL); break; case 700: - _actor5.setup(701, 1, 8); + _cable.setup(701, 1, 8); if (R2_GLOBALS._v565E7 == 0) { - _actor5.setPosition(Common::Point(356 - (R2_GLOBALS._v565EB * 8), 148 - (((R2_GLOBALS._v565E9 + 10) / 5) * 4))); + _cable.setPosition(Common::Point(356 - (R2_GLOBALS._v565EB * 8), 148 - (((R2_GLOBALS._v565E9 + 10) / 5) * 4))); } else { - _actor5.setup(701, 1, 1); - _actor5.setPosition(Common::Point(_actor1._position.x + 1, _actor1._position.y + 120)); + _cable.setup(701, 1, 1); + _cable.setPosition(Common::Point(_actor1._position.x + 1, _actor1._position.y + 120)); } - _actor5.setDetails(700, 38, -1, -1, 1, (SceneItem *) NULL); + _cable.setDetails(700, 38, -1, -1, 1, (SceneItem *) NULL); break; default: break; @@ -6118,7 +6272,7 @@ void Scene700::postInit(SceneObjectList *OwnerList) { _actor4.setPosition(Common::Point(454, 117)); _actor4.setDetails(700, 27, -1, -1, 1, (SceneItem *) NULL); - _item12.setDetails(Rect(234, 90, 252, 110), 700, 39, -1, -1, 1, NULL); + _handGrip.setDetails(Rect(234, 90, 252, 110), 700, 39, -1, -1, 1, NULL); _item6.setDetails(Rect(91, 158, 385, 167), 700, 6, -1, 8, 1, NULL); _item2.setDetails(Rect(47, 115, 149, 124), 700, 40, -1, 41, 1, NULL); _item3.setDetails(Rect(151, 108, 187, 124), 700, 40, -1, 41, 1, NULL); @@ -6223,7 +6377,7 @@ void Scene700::signal() { break; case 11: _sceneMode = 12; - _actor5.remove(); + _cable.remove(); R2_GLOBALS._player.animate(ANIM_MODE_6, this); break; case 12: @@ -6248,13 +6402,13 @@ void Scene700::signal() { break; case 16: _sceneMode = 17; - _actor5.setup(701, 1, 8); - _actor5.setDetails(700, 38, -1, -1, 3, (SceneItem *) NULL); - if ((R2_GLOBALS._v565E5 != 0) && (_actor5._position.x == _actor1._position.x + 1) && (_actor5._position.x == 148 - (((R2_GLOBALS._electromagnetChangeAmount + 10) / 5) * 4))) { - _actor5.animate(ANIM_MODE_6, NULL); - Common::Point pt(_actor5._position.x, _actor1._position.y + 120); + _cable.setup(701, 1, 8); + _cable.setDetails(700, 38, -1, -1, 3, (SceneItem *) NULL); + if ((R2_GLOBALS._v565E5 != 0) && (_cable._position.x == _actor1._position.x + 1) && (_cable._position.x == 148 - (((R2_GLOBALS._electromagnetChangeAmount + 10) / 5) * 4))) { + _cable.animate(ANIM_MODE_6, NULL); + Common::Point pt(_cable._position.x, _actor1._position.y + 120); NpcMover *mover = new NpcMover(); - _actor5.addMover(mover, &pt, NULL); + _cable.addMover(mover, &pt, NULL); R2_GLOBALS._v565E7 = 1; } R2_GLOBALS._player.animate(ANIM_MODE_6, this); @@ -6274,9 +6428,9 @@ void Scene700::signal() { R2_GLOBALS._player.enableControl(); break; case 21: - _actor5.fixPriority(77); + _cable.fixPriority(77); if ((R2_GLOBALS._v565E5 != 0) && (R2_GLOBALS._electromagnetChangeAmount == 20) && (R2_GLOBALS._electromagnetZoom == 70)) - _actor5.animate(ANIM_MODE_6, NULL); + _cable.animate(ANIM_MODE_6, NULL); R2_INVENTORY.setObjectScene(R2_ATTRACTOR_CABLE_HARNESS, 700); R2_GLOBALS._player.enableControl(); @@ -6291,10 +6445,10 @@ void Scene700::signal() { R2_GLOBALS._sceneManager.changeScene(900); break; case 706: - _actor5.setDetails(700, 38, -1, -1, 3, (SceneItem *) NULL); - _actor5.fixPriority(77); + _cable.setDetails(700, 38, -1, -1, 3, (SceneItem *) NULL); + _cable.fixPriority(77); if ((R2_GLOBALS._v565E5 != 0) && (R2_GLOBALS._electromagnetChangeAmount == 20) && (R2_GLOBALS._electromagnetZoom == 70)) - _actor5.animate(ANIM_MODE_6, NULL); + _cable.animate(ANIM_MODE_6, NULL); R2_INVENTORY.setObjectScene(R2_ATTRACTOR_UNIT, 0); R2_INVENTORY.setObjectScene(R2_ATTRACTOR_CABLE_HARNESS, 700); R2_GLOBALS._player.enableControl(); diff --git a/engines/tsage/ringworld2/ringworld2_scenes0.h b/engines/tsage/ringworld2/ringworld2_scenes0.h index bc30743aca..c1558312b0 100644 --- a/engines/tsage/ringworld2/ringworld2_scenes0.h +++ b/engines/tsage/ringworld2/ringworld2_scenes0.h @@ -102,7 +102,7 @@ public: class Scene125: public SceneExt { /* Objects */ - class Object5: public SceneActor { + class Food: public SceneActor { public: virtual bool startAction(CursorType action, Event &event); }; @@ -136,7 +136,7 @@ public: ASoundExt _sound1; NamedHotspot _background, _item2, _item3; DiskSlot _diskSlot; - SceneActor _object1, _object2, _object3, _object4, _object5, _object6, _infoDisk; + SceneActor _object1, _object2, _object3, _object4, _food, _foodDispenser, _infoDisk; Icon _icon1, _icon2, _icon3, _icon4, _icon5, _icon6; SequenceManager _sequenceManager; SceneText _sceneText; @@ -524,6 +524,31 @@ public: }; class Scene500: public SceneExt { + /* Dialogs */ + class PanelDialog: public SceneAreaObject { + class Button: public SceneActor { + private: + int _buttonId; + bool _buttonDown; + + void doButtonPress(); + public: + Button(); + virtual Common::String getClassName() { return "Scene500_Button"; } + virtual void process(Event &event); + virtual bool startAction(CursorType action, Event &event); + virtual void synchronize(Serializer &s); + + void setupButton(int buttonId); + }; + public: + Button _button1, _button2, _button3; + + virtual Common::String getClassName() { return "Scene500_PanelWindow"; } + virtual void remove(); + void setDetails(int visage, int strip, int frameNumber, const Common::Point &pt); + }; + /* Items */ class ControlPanel: public SceneHotspot { public: @@ -531,11 +556,11 @@ class Scene500: public SceneExt { }; /* Objects */ - class Object2: public SceneActor { + class Seeker: public SceneActor { public: virtual bool startAction(CursorType action, Event &event); }; - class Object3: public SceneActor { + class Suit: public SceneActor { public: virtual bool startAction(CursorType action, Event &event); }; @@ -570,10 +595,6 @@ class Scene500: public SceneExt { public: virtual bool startAction(CursorType action, Event &event); }; - class Object: public SceneActor { - public: - virtual bool startAction(CursorType action, Event &event); - }; public: int _stripNumber; byte _buffer[2710]; @@ -582,8 +603,8 @@ public: SceneHotspot _background, _item2; ControlPanel _controlPanel; SceneActor _object1; - Object2 _object2; - Object3 _object3; + Seeker _seeker; + Suit _suit; Doorway _doorway; OxygenTanks _tanks1, _tanks2; AirLock _airLock; @@ -592,8 +613,7 @@ public: SonicStunner _sonicStunner; Locker1 _locker1; Locker2 _locker2; - SceneAreaObject _area1; - Object _obj1, _obj2, _obj3; + PanelDialog _panelDialog; ASoundExt _sound1; SequenceManager _sequenceManager1, _sequenceManager2; public: @@ -679,7 +699,7 @@ class Scene700: public SceneExt { public: virtual bool startAction(CursorType action, Event &event); }; - class Item12 : public NamedHotspot { + class HandGrip : public NamedHotspot { public: virtual bool startAction(CursorType action, Event &event); }; @@ -696,7 +716,7 @@ class Scene700: public SceneExt { public: virtual bool startAction(CursorType action, Event &event); }; - class Actor5 : public SceneActor { + class Cable : public SceneActor { public: virtual bool startAction(CursorType action, Event &event); }; @@ -716,12 +736,12 @@ public: NamedHotspot _item9; NamedHotspot _item10; Item11 _item11; - Item12 _item12; + HandGrip _handGrip; SceneActor _actor1; Actor2 _actor2; Actor3 _actor3; Actor4 _actor4; - Actor5 _actor5; + Cable _cable; Actor6 _actor6; Actor6 _actor7; Actor6 _actor8; diff --git a/engines/tsage/ringworld2/ringworld2_scenes1.cpp b/engines/tsage/ringworld2/ringworld2_scenes1.cpp index 021ccd44dc..1258e464ff 100644 --- a/engines/tsage/ringworld2/ringworld2_scenes1.cpp +++ b/engines/tsage/ringworld2/ringworld2_scenes1.cpp @@ -39,6 +39,7 @@ Scene1000::Scene1000(): SceneExt() { R2_GLOBALS._uiElements._active = false; _gameTextSpeaker._displayMode = 9; _fieldD2E = 0; + _field412 = 0; } void Scene1000::postInit(SceneObjectList *OwnerList) { @@ -1203,7 +1204,7 @@ void Scene1100::signal() { } break; case 54: - if (_stripManager._field2E8 == 1) { + if (_stripManager._exitMode == 1) { R2_GLOBALS._player.disableControl(); _sceneMode = 1125; setAction(&_sequenceManager1, this, 1125, &R2_GLOBALS._player, &_seeker, NULL); @@ -5976,7 +5977,8 @@ void Scene1337::subCF31D() { if ((_arrunkObj1337[1]._arr2[i]._field34 == 0) && (!subC2687(_arrunkObj1337[1]._arr3[0]._field34))) { subC340B(&_arrunkObj1337[1]._arr1[tmpVal], &_arrunkObj1337[1]._arr2[i]); found = true; - } + break; + } } } @@ -5986,8 +5988,10 @@ void Scene1337::subCF31D() { tmpVal = subC274D(1); int tmpVal2 = subC331B(1); - if ((tmpVal != -1) && ( tmpVal2 != -1)) + if ((tmpVal != -1) && ( tmpVal2 != -1)) { subC358E(&_arrunkObj1337[1]._arr1[tmpVal], tmpVal2); + found = true; + } if (found) return; @@ -7771,7 +7775,7 @@ void Scene1550::postInit(SceneObjectList *OwnerList) { SceneExt::postInit(); if (R2_GLOBALS._sceneManager._previousScene == -1) - R2_GLOBALS.setFlag(R2_ATTRACTOR_CABLE_HARNESS); + R2_GLOBALS.setFlag(16); if ((R2_GLOBALS._player._characterScene[1] != 1550) && (R2_GLOBALS._player._characterScene[1] != 1580)) { R2_GLOBALS._player._characterScene[1] = 1550; diff --git a/engines/tsage/ringworld2/ringworld2_scenes2.cpp b/engines/tsage/ringworld2/ringworld2_scenes2.cpp index 9246c4b6a4..bfed1ebeef 100644 --- a/engines/tsage/ringworld2/ringworld2_scenes2.cpp +++ b/engines/tsage/ringworld2/ringworld2_scenes2.cpp @@ -769,6 +769,7 @@ Scene2000::Scene2000(): SceneExt() { } _exitingFlag = false; + _mazePlayerMode = 0; } void Scene2000::postInit(SceneObjectList *OwnerList) { diff --git a/engines/tsage/ringworld2/ringworld2_speakers.cpp b/engines/tsage/ringworld2/ringworld2_speakers.cpp index 8d91787272..a0b0bd4ddb 100644 --- a/engines/tsage/ringworld2/ringworld2_speakers.cpp +++ b/engines/tsage/ringworld2/ringworld2_speakers.cpp @@ -204,13 +204,12 @@ void VisualSpeaker::setText(const Common::String &msg) { _sceneText._textMode = _textMode; _sceneText.setup(s); - //_sceneText.clone(); - _sceneText.setPosition(_textPos); _sceneText.fixPriority(256); // If subtitles are turned off, don't show the text - if (!(R2_GLOBALS._speechSubtitles & SPEECH_TEXT)) { + if ((R2_GLOBALS._speechSubtitles & SPEECH_VOICE) && + !(R2_GLOBALS._speechSubtitles & SPEECH_TEXT)) { _sceneText.hide(); } @@ -1173,6 +1172,51 @@ SpeakerQuinn::SpeakerQuinn(): VisualSpeaker() { _numFrames = 0; } +void SpeakerQuinn::proc15() { + int v = _speakerMode; + + if (!_object2) { + if (R2_GLOBALS._player._characterIndex == R2_QUINN) { + _object2 = &R2_GLOBALS._player; + } else { + assert(R2_GLOBALS._sceneManager._sceneNumber == 500); + Scene500 *scene = (Scene500 *)R2_GLOBALS._sceneManager._scene; + _object2 = &scene->_seeker; + } + + _object2->hide(); + + _object1.postInit(); + _object1._effect = _object2->_effect; + _object1._shade = _object2->_shade; + _object1.setPosition(_object2->_position); + + if (_object2->_mover) + _object2->addMover(NULL); + } + + if (v == 0) { + _object1.animate(ANIM_MODE_2, NULL); + } else { + ((SceneItem *)_action)->_sceneRegionId = 0; + + switch (_object2->_visage) { + case 10: + _object1.setup(4021, (v == 1) ? 5 : 7, 1); + break; + + case 1500: + _object1.setup(4021, (v == 1) ? 1 : 3, 1); + break; + + default: + break; + } + + _object1.animate(ANIM_MODE_5, this); + } +} + void SpeakerQuinn300::proc15() { int v = _speakerMode; @@ -1944,6 +1988,43 @@ SpeakerSeeker::SpeakerSeeker(): VisualSpeaker() { _numFrames = 0; } +void SpeakerSeeker::proc15() { + int v = _speakerMode; + + if (!_object2) { + if (R2_GLOBALS._player._characterIndex == R2_SEEKER) { + _object2 = &R2_GLOBALS._player; + } else { + assert(R2_GLOBALS._sceneManager._sceneNumber == 500); + Scene500 *scene = (Scene500 *)R2_GLOBALS._sceneManager._scene; + _object2 = &scene->_seeker; + } + + _object2->hide(); + _object1.postInit(); + + _object1._effect = _object2->_effect; + _object1._shade = _object2->_shade; + _object1.setPosition(_object2->_position); + + if (_object2->_mover) + _object2->addMover(NULL); + } + + if (v == 0) { + _object1.animate(ANIM_MODE_2, NULL); + } else { + ((SceneItem *)_action)->_sceneRegionId = 0; + + if (v == 1) + _object1.setup(4041, 3, 1); + else + _object1.setup(4041, 1, 1); + + _object1.animate(ANIM_MODE_5, this); + } +} + void SpeakerSeeker300::proc15() { int v = _speakerMode; diff --git a/engines/tsage/ringworld2/ringworld2_speakers.h b/engines/tsage/ringworld2/ringworld2_speakers.h index 532e02576c..73e6dcf750 100644 --- a/engines/tsage/ringworld2/ringworld2_speakers.h +++ b/engines/tsage/ringworld2/ringworld2_speakers.h @@ -274,6 +274,7 @@ class SpeakerQuinn : public VisualSpeaker { public: SpeakerQuinn(); virtual Common::String getClassName() { return "SpeakerQuinn"; } + virtual void proc15(); }; class SpeakerQuinn300 : public SpeakerQuinn { @@ -412,6 +413,7 @@ class SpeakerSeeker : public VisualSpeaker { public: SpeakerSeeker(); virtual Common::String getClassName() { return "SpeakerSeeker"; } + virtual void proc15(); }; class SpeakerSeeker300 : public SpeakerSeeker { diff --git a/engines/wintermute/base/font/base_font_truetype.cpp b/engines/wintermute/base/font/base_font_truetype.cpp index 3059a69047..e073f27970 100644 --- a/engines/wintermute/base/font/base_font_truetype.cpp +++ b/engines/wintermute/base/font/base_font_truetype.cpp @@ -272,7 +272,7 @@ BaseSurface *BaseFontTT::renderTextToTexture(const WideString &text, int width, // void drawString(Surface *dst, const Common::String &str, int x, int y, int w, uint32 color, TextAlign align = kTextAlignLeft, int deltax = 0, bool useEllipsis = true) const; Graphics::Surface *surface = new Graphics::Surface(); if (_deletableFont) { // We actually have a TTF - surface->create((uint16)width, (uint16)(_lineHeight * lines.size()), Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24)); + surface->create((uint16)width, (uint16)(_lineHeight * lines.size()), _gameRef->_renderer->getPixelFormat()); } else { // We are using a fallback, they can't do 32bpp surface->create((uint16)width, (uint16)(_lineHeight * lines.size()), Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0)); } @@ -285,7 +285,7 @@ BaseSurface *BaseFontTT::renderTextToTexture(const WideString &text, int width, } BaseSurface *retSurface = _gameRef->_renderer->createSurface(); - Graphics::Surface *convertedSurface = surface->convertTo(Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24)); + Graphics::Surface *convertedSurface = surface->convertTo(_gameRef->_renderer->getPixelFormat()); retSurface->putSurface(*convertedSurface, true); convertedSurface->free(); surface->free(); diff --git a/engines/wintermute/base/gfx/osystem/base_render_osystem.cpp b/engines/wintermute/base/gfx/osystem/base_render_osystem.cpp index 668105457f..e6d769c653 100644 --- a/engines/wintermute/base/gfx/osystem/base_render_osystem.cpp +++ b/engines/wintermute/base/gfx/osystem/base_render_osystem.cpp @@ -84,7 +84,6 @@ BaseRenderOSystem::~BaseRenderOSystem() { delete _renderSurface; _blankSurface->free(); delete _blankSurface; - TransparentSurface::destroyLookup(); } ////////////////////////////////////////////////////////////////////////// @@ -127,7 +126,7 @@ bool BaseRenderOSystem::initRenderer(int width, int height, bool windowed) { _windowed = !ConfMan.getBool("fullscreen"); - Graphics::PixelFormat format(4, 8, 8, 8, 8, 16, 8, 0, 24); + Graphics::PixelFormat format(4, 8, 8, 8, 8, 24, 16, 8, 0); g_system->beginGFXTransaction(); g_system->initSize(_width, _height, &format); OSystem::TransactionError gfxError = g_system->endGFXTransaction(); diff --git a/engines/wintermute/base/gfx/osystem/base_surface_osystem.cpp b/engines/wintermute/base/gfx/osystem/base_surface_osystem.cpp index d4c5905c4b..6506abd29c 100644 --- a/engines/wintermute/base/gfx/osystem/base_surface_osystem.cpp +++ b/engines/wintermute/base/gfx/osystem/base_surface_osystem.cpp @@ -48,7 +48,7 @@ namespace Wintermute { BaseSurfaceOSystem::BaseSurfaceOSystem(BaseGame *inGame) : BaseSurface(inGame) { _surface = new Graphics::Surface(); _alphaMask = nullptr; - _hasAlpha = true; + _alphaType = TransparentSurface::ALPHA_FULL; _lockPixels = nullptr; _lockPitch = 0; _loaded = false; @@ -71,22 +71,37 @@ BaseSurfaceOSystem::~BaseSurfaceOSystem() { renderer->invalidateTicketsFromSurface(this); } -bool hasTransparency(Graphics::Surface *surf) { +TransparentSurface::AlphaType hasTransparencyType(const Graphics::Surface *surf) { if (surf->format.bytesPerPixel != 4) { - warning("hasTransparency:: non 32 bpp surface passed as argument"); - return false; + warning("hasTransparencyType:: non 32 bpp surface passed as argument"); + return TransparentSurface::ALPHA_OPAQUE; } uint8 r, g, b, a; + bool seenAlpha = false; + bool seenFullAlpha = false; for (int i = 0; i < surf->h; i++) { + if (seenFullAlpha) { + break; + } for (int j = 0; j < surf->w; j++) { uint32 pix = *(uint32 *)surf->getBasePtr(j, i); surf->format.colorToARGB(pix, a, r, g, b); if (a != 255) { - return true; + seenAlpha = true; + if (a != 0) { + seenFullAlpha = true; + break; + } } } } - return false; + if (seenFullAlpha) { + return TransparentSurface::ALPHA_FULL; + } else if (seenAlpha) { + return TransparentSurface::ALPHA_BINARY; + } else { + return TransparentSurface::ALPHA_OPAQUE; + } } ////////////////////////////////////////////////////////////////////////// @@ -170,7 +185,7 @@ bool BaseSurfaceOSystem::finishLoad() { trans.applyColorKey(_ckRed, _ckGreen, _ckBlue, replaceAlpha); } - _hasAlpha = hasTransparency(_surface); + _alphaType = hasTransparencyType(_surface); _valid = true; _gameRef->addMem(_width * _height * 4); @@ -422,17 +437,11 @@ bool BaseSurfaceOSystem::drawSprite(int x, int y, Rect32 *rect, Rect32 *newRect, // TODO: This actually requires us to have the SAME source-offsets every time, // But no checking is in place for that yet. - // TODO: Optimize by not doing alpha-blits if we lack or disable alpha - - bool hasAlpha = false; - - if (_hasAlpha && !transform._alphaDisable) { - hasAlpha = true; - } - - if (transform._alphaDisable) { - warning("BaseSurfaceOSystem::drawSprite - AlphaDisable ignored"); + // Optimize by not doing alpha-blits if we lack alpha + if (_alphaType == TransparentSurface::ALPHA_OPAQUE && !transform._alphaDisable) { + transform._alphaDisable = true; } + renderer->drawSurface(this, _surface, &srcRect, &position, transform); return STATUS_OK; } @@ -447,7 +456,11 @@ bool BaseSurfaceOSystem::putSurface(const Graphics::Surface &surface, bool hasAl _loaded = true; _surface->free(); _surface->copyFrom(surface); - _hasAlpha = hasAlpha; + if (hasAlpha) { + _alphaType = TransparentSurface::ALPHA_FULL; + } else { + _alphaType = TransparentSurface::ALPHA_OPAQUE; + } BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_gameRef->_renderer); renderer->invalidateTicketsFromSurface(this); diff --git a/engines/wintermute/base/gfx/osystem/base_surface_osystem.h b/engines/wintermute/base/gfx/osystem/base_surface_osystem.h index da86833517..6cf19d00fb 100644 --- a/engines/wintermute/base/gfx/osystem/base_surface_osystem.h +++ b/engines/wintermute/base/gfx/osystem/base_surface_osystem.h @@ -30,6 +30,7 @@ #define WINTERMUTE_BASE_SURFACESDL_H #include "graphics/surface.h" +#include "engines/wintermute/graphics/transparent_surface.h" #include "engines/wintermute/base/gfx/base_surface.h" #include "common/list.h" @@ -81,6 +82,7 @@ public: return _height; } + TransparentSurface::AlphaType getAlphaType() const { return _alphaType; } private: Graphics::Surface *_surface; bool _loaded; @@ -90,7 +92,7 @@ private: uint32 getPixelAt(Graphics::Surface *surface, int x, int y); uint32 _rotation; - bool _hasAlpha; + TransparentSurface::AlphaType _alphaType; void *_lockPixels; int _lockPitch; byte *_alphaMask; diff --git a/engines/wintermute/base/gfx/osystem/render_ticket.cpp b/engines/wintermute/base/gfx/osystem/render_ticket.cpp index 98739e0778..b1720c1b0b 100644 --- a/engines/wintermute/base/gfx/osystem/render_ticket.cpp +++ b/engines/wintermute/base/gfx/osystem/render_ticket.cpp @@ -28,6 +28,7 @@ #include "engines/wintermute/base/gfx/osystem/render_ticket.h" +#include "engines/wintermute/base/gfx/osystem/base_surface_osystem.h" #include "engines/wintermute/graphics/transform_tools.h" #include "common/textconsole.h" @@ -104,7 +105,13 @@ void RenderTicket::drawToSurface(Graphics::Surface *_targetSurface) const { clipRect.setWidth(getSurface()->w); clipRect.setHeight(getSurface()->h); - src._enableAlphaBlit = !_transform._alphaDisable; + if (_owner) { + if (_transform._alphaDisable) { + src._alphaMode = TransparentSurface::ALPHA_OPAQUE; + } else { + src._alphaMode = _owner->getAlphaType(); + } + } src.blit(*_targetSurface, _dstRect.left, _dstRect.top, _transform._flip, &clipRect, _transform._rgbaMod, clipRect.width(), clipRect.height()); } @@ -118,7 +125,13 @@ void RenderTicket::drawToSurface(Graphics::Surface *_targetSurface, Common::Rect clipRect->setHeight(getSurface()->h); } - src._enableAlphaBlit = !_transform._alphaDisable; + if (_owner) { + if (_transform._alphaDisable) { + src._alphaMode = TransparentSurface::ALPHA_OPAQUE; + } else { + src._alphaMode = _owner->getAlphaType(); + } + } src.blit(*_targetSurface, dstRect->left, dstRect->top, _transform._flip, clipRect, _transform._rgbaMod, clipRect->width(), clipRect->height()); if (doDelete) { delete clipRect; diff --git a/engines/wintermute/detection_tables.h b/engines/wintermute/detection_tables.h index 702c0b28ba..63f5078c12 100644 --- a/engines/wintermute/detection_tables.h +++ b/engines/wintermute/detection_tables.h @@ -40,16 +40,20 @@ static const PlainGameDescriptor wintermuteGames[] = { {"dreaming", "Des Reves Elastiques Avec Mille Insectes Nommes Georges"}, {"dirtysplit", "Dirty Split"}, {"dreamscape", "Dreamscape"}, + {"escapemansion", "Escape from the Mansion"}, {"ghostsheet", "Ghost in the Sheet"}, {"hamlet", "Hamlet or the last game without MMORPS features, shaders and product placement"}, {"jamesperis", "James Peris: No License Nor Control"}, + {"looky", "Looky"}, {"julia", "J.U.L.I.A."}, {"mirage", "Mirage"}, {"pigeons", "Pigeons in the Park"}, {"reversion1", "Reversion: The Escape"}, {"reversion2", "Reversion: The Meeting"}, {"rosemary", "Rosemary"}, + {"shaban", "Shaban"}, {"shinestar", "The Shine of a Star"}, + {"spacemadness", "Space Madness"}, {"thebox", "The Box"}, {"tib", "Fairy Tales About Toshechka and Boshechka"}, {"tradestory", "The Trader of Stories"}, @@ -284,6 +288,26 @@ static const ADGameDescription gameDescriptions[] = { ADGF_UNSTABLE, GUIO0() }, + // Escape from the Mansion + { + "escapemansion", + "Beta 1", + AD_ENTRY1s("data.dcp", "d8e348b2312cc36a929cad75f12e0b3a", 21452380), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // Escape from the Mansion + { + "escapemansion", + "Beta 2", + AD_ENTRY1s("data.dcp", "ded5fa6c5f2afdaf2cafb53e52cd3dd8", 21455763), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, // Ghosts in the Sheet { "ghostsheet", @@ -379,6 +403,48 @@ static const ADGameDescription gameDescriptions[] = { ADGF_DEMO, GUIO0() }, + // Looky Demo (English) + { + "looky", + "Demo", + { + {"english.dcp", 0, "1388e1dd320f4d553dea3b0316812f9d", 1358442}, + {"data.dcp", 0, "7074bcd7bc7ad7eb04c271aafb964c32", 13815660}, + AD_LISTEND + }, + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // Looky Demo (German) + { + "looky", + "Demo", + { + {"german.dcp", 0, "606c048426dfbe94442b59fd34a5c76e", 14339496}, + {"data.dcp", 0, "7074bcd7bc7ad7eb04c271aafb964c32", 13815660}, + AD_LISTEND + }, + Common::DE_DEU, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // Looky (German) + { + "looky", + "", + { + {"german.dcp", 0, "bf4c2b8c26342342441a6d64934ab832", 107027865}, + {"data.dcp", 0, "50de0beaa5ad621aa9f020df901d1e74", 1342214}, + AD_LISTEND + }, + Common::DE_DEU, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, // Mirage { "mirage", @@ -625,6 +691,16 @@ static const ADGameDescription gameDescriptions[] = { ADGF_UNSTABLE, GUIO0() }, + // Shaban + { + "shaban", + "", + AD_ENTRY1s("data.dcp", "35f702ca9baabc5c620e0be230195c8a", 755388466), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, // The Shine of a Star { "shinestar", @@ -635,6 +711,16 @@ static const ADGameDescription gameDescriptions[] = { ADGF_UNSTABLE, GUIO0() }, + // Space Madness + { + "spacemadness", + "1.0.2", + AD_ENTRY1s("data.dcp", "b9b83135dc7a9e1b4b5f50195dbeb630", 39546622), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, // The Box { "thebox", diff --git a/engines/wintermute/graphics/transparent_surface.cpp b/engines/wintermute/graphics/transparent_surface.cpp index e375322ae9..249d30f7d9 100644 --- a/engines/wintermute/graphics/transparent_surface.cpp +++ b/engines/wintermute/graphics/transparent_surface.cpp @@ -140,16 +140,9 @@ void TransparentSurface::copyPixelNearestNeighbor(float projX, float projY, int } #endif -byte *TransparentSurface::_lookup = nullptr; +TransparentSurface::TransparentSurface() : Surface(), _alphaMode(ALPHA_FULL) {} -void TransparentSurface::destroyLookup() { - delete[] _lookup; - _lookup = nullptr; -} - -TransparentSurface::TransparentSurface() : Surface(), _enableAlphaBlit(true) {} - -TransparentSurface::TransparentSurface(const Surface &surf, bool copyData) : Surface(), _enableAlphaBlit(true) { +TransparentSurface::TransparentSurface(const Surface &surf, bool copyData) : Surface(), _alphaMode(ALPHA_FULL) { if (copyData) { copyFrom(surf); } else { @@ -168,9 +161,9 @@ void doBlitOpaque(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pit byte *in, *out; #ifdef SCUMM_LITTLE_ENDIAN - const int aIndex = 3; -#else const int aIndex = 0; +#else + const int aIndex = 3; #endif for (uint32 i = 0; i < height; i++) { @@ -186,42 +179,60 @@ void doBlitOpaque(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pit } } -void TransparentSurface::generateLookup() { - _lookup = new byte[256 * 256]; - for (int i = 0; i < 256; i++) { - for (int j = 0; j < 256; j++) { - _lookup[(i << 8) + j] = (i * j) >> 8; +void doBlitBinary(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep) { + byte *in, *out; + +#ifdef SCUMM_LITTLE_ENDIAN + const int aIndex = 0; +#else + const int aIndex = 3; +#endif + const int aShift = 0;//img->format.aShift; + + for (uint32 i = 0; i < height; i++) { + out = outo; + in = ino; + for (uint32 j = 0; j < width; j++) { + uint32 pix = *(uint32 *)in; + int a = (pix >> aShift) & 0xff; + in += inStep; + + if (a == 0) { // Full transparency + out += 4; + } else { // Full opacity (Any value not exactly 0 is Opaque here) + *(uint32 *)out = pix; + out[aIndex] = 0xFF; + out += 4; + } } + outo += pitch; + ino += inoStep; } } -void TransparentSurface::doBlitAlpha(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep) { +void doBlitAlpha(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep) { byte *in, *out; - if (!_lookup) { - generateLookup(); - } - #ifdef SCUMM_LITTLE_ENDIAN - const int aIndex = 3; - const int bIndex = 0; - const int gIndex = 1; - const int rIndex = 2; -#else const int aIndex = 0; - const int bIndex = 3; + const int bIndex = 1; const int gIndex = 2; - const int rIndex = 1; + const int rIndex = 3; +#else + const int aIndex = 3; + const int bIndex = 2; + const int gIndex = 1; + const int rIndex = 0; #endif - const int bShift = 0;//img->format.bShift; - const int gShift = 8;//img->format.gShift; - const int rShift = 16;//img->format.rShift; - const int aShift = 24;//img->format.aShift; + const int bShift = 8;//img->format.bShift; + const int gShift = 16;//img->format.gShift; + const int rShift = 24;//img->format.rShift; + const int aShift = 0;//img->format.aShift; - const int bShiftTarget = 0;//target.format.bShift; - const int gShiftTarget = 8;//target.format.gShift; - const int rShiftTarget = 16;//target.format.rShift; + const int bShiftTarget = 8;//target.format.bShift; + const int gShiftTarget = 16;//target.format.gShift; + const int rShiftTarget = 24;//target.format.rShift; for (uint32 i = 0; i < height; i++) { out = outo; @@ -255,13 +266,9 @@ void TransparentSurface::doBlitAlpha(byte *ino, byte *outo, uint32 width, uint32 default: // alpha blending outa = 255; - - outb = _lookup[(((oPix >> bShiftTarget) & 0xff)) + ((255 - a) << 8)]; - outg = _lookup[(((oPix >> gShiftTarget) & 0xff)) + ((255 - a) << 8)]; - outr = _lookup[(((oPix >> rShiftTarget) & 0xff)) + ((255 - a) << 8)]; - outb += _lookup[b + (a << 8)]; - outg += _lookup[g + (a << 8)]; - outr += _lookup[r + (a << 8)]; + outb = ((b * a) + ((oPix >> bShiftTarget) & 0xff) * (255-a)) >> 8; + outg = ((g * a) + ((oPix >> gShiftTarget) & 0xff) * (255-a)) >> 8; + outr = ((r * a) + ((oPix >> rShiftTarget) & 0xff) * (255-a)) >> 8; out[aIndex] = outa; out[bIndex] = outb; @@ -391,29 +398,32 @@ Common::Rect TransparentSurface::blit(Graphics::Surface &target, int posX, int p byte *in, *out; #ifdef SCUMM_LITTLE_ENDIAN - const int aIndex = 3; - const int bIndex = 0; - const int gIndex = 1; - const int rIndex = 2; -#else const int aIndex = 0; - const int bIndex = 3; + const int bIndex = 1; const int gIndex = 2; - const int rIndex = 1; + const int rIndex = 3; +#else + const int aIndex = 3; + const int bIndex = 2; + const int gIndex = 1; + const int rIndex = 0; #endif - const int bShift = 0;//img->format.bShift; - const int gShift = 8;//img->format.gShift; - const int rShift = 16;//img->format.rShift; - const int aShift = 24;//img->format.aShift; - const int bShiftTarget = 0;//target.format.bShift; - const int gShiftTarget = 8;//target.format.gShift; - const int rShiftTarget = 16;//target.format.rShift; + const int bShift = 8;//img->format.bShift; + const int gShift = 16;//img->format.gShift; + const int rShift = 24;//img->format.rShift; + const int aShift = 0;//img->format.aShift; + + const int bShiftTarget = 8;//target.format.bShift; + const int gShiftTarget = 16;//target.format.gShift; + const int rShiftTarget = 24;//target.format.rShift; if (ca == 255 && cb == 255 && cg == 255 && cr == 255) { - if (_enableAlphaBlit) { + if (_alphaMode == ALPHA_FULL) { doBlitAlpha(ino, outo, img->w, img->h, target.pitch, inStep, inoStep); - } else { + } else if (_alphaMode == ALPHA_BINARY) { + doBlitBinary(ino, outo, img->w, img->h, target.pitch, inStep, inoStep); + } else if (_alphaMode == ALPHA_OPAQUE) { doBlitOpaque(ino, outo, img->w, img->h, target.pitch, inStep, inoStep); } } else { diff --git a/engines/wintermute/graphics/transparent_surface.h b/engines/wintermute/graphics/transparent_surface.h index 9d06f3e006..598aaa55d7 100644 --- a/engines/wintermute/graphics/transparent_surface.h +++ b/engines/wintermute/graphics/transparent_surface.h @@ -75,7 +75,13 @@ struct TransparentSurface : public Graphics::Surface { FLIP_VH = FLIP_H | FLIP_V }; - bool _enableAlphaBlit; + enum AlphaType { + ALPHA_OPAQUE = 0, + ALPHA_BINARY = 1, + ALPHA_FULL = 2 + }; + + AlphaType _alphaMode; /** @brief renders the surface to another surface @@ -114,11 +120,6 @@ struct TransparentSurface : public Graphics::Surface { TransparentSurface *scale(uint16 newWidth, uint16 newHeight) const; TransparentSurface *rotoscale(const TransformStruct &transform) const; - static byte *_lookup; - static void destroyLookup(); -private: - static void doBlitAlpha(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep); - static void generateLookup(); }; /** diff --git a/engines/wintermute/utils/string_util.cpp b/engines/wintermute/utils/string_util.cpp index 23abb5d579..e8e078aba8 100644 --- a/engines/wintermute/utils/string_util.cpp +++ b/engines/wintermute/utils/string_util.cpp @@ -146,26 +146,21 @@ Utf8String StringUtil::wideToUtf8(const WideString &WideStr) { return ""; } -// Currently this only does Ansi->ISO 8859, and only for carets. -char simpleAnsiToWide(const AnsiString &str, uint32 &offset) { - byte c = str[offset]; - - if (c == 146) { - offset++; - return 39; // Replace right-quote with apostrophe - } else { - offset++; - return c; - } -} - ////////////////////////////////////////////////////////////////////////// WideString StringUtil::ansiToWide(const AnsiString &str) { // TODO: This function gets called a lot, so warnings like these drown out the usefull information Common::String converted = ""; uint32 index = 0; while (index != str.size()) { - converted += simpleAnsiToWide(str, index); + byte c = str[index]; + if (c == 146) { + converted += (char)39; // Replace right-quote with apostrophe + } else if (c == 133) { + converted += Common::String("..."); // Replace ...-symbol with ... + } else { + converted += c; + } + index++; } // using default os locale! diff --git a/engines/wintermute/wintermute.cpp b/engines/wintermute/wintermute.cpp index a878944661..0a6be4caf8 100644 --- a/engines/wintermute/wintermute.cpp +++ b/engines/wintermute/wintermute.cpp @@ -105,7 +105,7 @@ bool WintermuteEngine::hasFeature(EngineFeature f) const { Common::Error WintermuteEngine::run() { // Initialize graphics using following: - Graphics::PixelFormat format(4, 8, 8, 8, 8, 16, 8, 0, 24); + Graphics::PixelFormat format(4, 8, 8, 8, 8, 24, 16, 8, 0); initGraphics(800, 600, true, &format); if (g_system->getScreenFormat() != format) { error("Wintermute currently REQUIRES 32bpp"); diff --git a/graphics/VectorRendererSpec.cpp b/graphics/VectorRendererSpec.cpp index 280bd6ec76..491a9d7f42 100644 --- a/graphics/VectorRendererSpec.cpp +++ b/graphics/VectorRendererSpec.cpp @@ -350,12 +350,14 @@ VectorRenderer *createRenderer(int mode) { return new VectorRendererSpec<uint32>(format); else if (g_system->getOverlayFormat().bytesPerPixel == 2) return new VectorRendererSpec<uint16>(format); + break; #ifndef DISABLE_FANCY_THEMES case GUI::ThemeEngine::kGfxAntialias: if (g_system->getOverlayFormat().bytesPerPixel == 4) return new VectorRendererAA<uint32>(format); else if (g_system->getOverlayFormat().bytesPerPixel == 2) return new VectorRendererAA<uint16>(format); + break; #endif default: break; diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp index a6e61e8f41..688654d208 100644 --- a/gui/ThemeEngine.cpp +++ b/gui/ThemeEngine.cpp @@ -522,6 +522,12 @@ void ThemeEngine::setGraphicsMode(GraphicsMode mode) { delete _vectorRenderer; _vectorRenderer = Graphics::createRenderer(mode); _vectorRenderer->setSurface(&_screen); + + // Since we reinitialized our screen surfaces we know nothing has been + // drawn so far. Sometimes we still end up with dirty screen bits in the + // list. Clearing it avoids invalid overlay writes when the backend + // resizes the overlay. + _dirtyScreen.clear(); } void WidgetDrawData::calcBackgroundOffset() { diff --git a/gui/credits.h b/gui/credits.h index 70f79ac9a5..3a4d7769f6 100644 --- a/gui/credits.h +++ b/gui/credits.h @@ -170,6 +170,15 @@ static const char *credits[] = { "C0""Eugene Sandulenko", "C0""David Turner", "", +"C1""Mortevielle", +"A0""Arnaud Boutonne", +"C0""Arnaud Boutonn\351", +"C0""Paul Gilbert", +"", +"C1""Neverhood", +"C0""Benjamin Haisch", +"C0""Filippos Karapetis", +"", "C1""Parallaction", "C0""peres", "", diff --git a/video/avi_decoder.cpp b/video/avi_decoder.cpp index 92a60fcccf..34bc8c4a7a 100644 --- a/video/avi_decoder.cpp +++ b/video/avi_decoder.cpp @@ -65,9 +65,10 @@ namespace Video { #define ID_VEDT MKTAG('v','e','d','t') #define ID_IDX1 MKTAG('i','d','x','1') #define ID_STRD MKTAG('s','t','r','d') -//#define ID_INFO MKTAG('I','N','F','O') +#define ID_INFO MKTAG('I','N','F','O') #define ID_ISFT MKTAG('I','S','F','T') #define ID_DISP MKTAG('D','I','S','P') +#define ID_PRMI MKTAG('P','R','M','I') // Codec tags #define ID_RLE MKTAG('R','L','E',' ') @@ -170,15 +171,25 @@ void AVIDecoder::handleList(uint32 listSize) { debug(0, "Found LIST of type %s", tag2str(listType)); - if (listType == ID_MOVI) { - // If we found the movie block + switch (listType) { + case ID_MOVI: // Movie List + // We found the movie block _foundMovieList = true; _movieListStart = curPos; _fileStream->skip(listSize); return; - } else if (listType == ID_HDRL) { + case ID_HDRL: // Header List // Mark the header as decoded _decodedHeader = true; + break; + case ID_INFO: // Metadata + case ID_PRMI: // Unknown metadata, should be safe to ignore + // Ignore metadata + _fileStream->skip(listSize); + return; + case ID_STRL: // Stream list + default: // (Just hope we can parse it!) + break; } while ((_fileStream->pos() - curPos) < listSize) |