aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
Diffstat (limited to 'engines')
-rw-r--r--engines/agi/sound.cpp23
-rw-r--r--engines/agi/sound.h1
-rw-r--r--engines/agos/animation.cpp4
-rw-r--r--engines/agos/input.cpp4
-rw-r--r--engines/agos/midi.cpp6
-rw-r--r--engines/agos/oracle.cpp2
-rw-r--r--engines/agos/saveload.cpp20
-rw-r--r--engines/agos/script.cpp2
-rw-r--r--engines/agos/script_e1.cpp8
-rw-r--r--engines/agos/script_e2.cpp4
-rw-r--r--engines/agos/script_s1.cpp12
-rw-r--r--engines/agos/script_ww.cpp4
-rw-r--r--engines/agos/verb.cpp3
-rw-r--r--engines/agos/window.cpp2
-rw-r--r--engines/cine/anim.cpp204
-rw-r--r--engines/cine/anim.h5
-rw-r--r--engines/cine/bg.cpp6
-rw-r--r--engines/cine/cine.cpp53
-rw-r--r--engines/cine/cine.h6
-rw-r--r--engines/cine/detection.cpp34
-rw-r--r--engines/cine/gfx.cpp173
-rw-r--r--engines/cine/gfx.h10
-rw-r--r--engines/cine/main_loop.cpp110
-rw-r--r--engines/cine/main_loop.h5
-rw-r--r--engines/cine/msg.cpp45
-rw-r--r--engines/cine/object.cpp9
-rw-r--r--engines/cine/object.h15
-rw-r--r--engines/cine/pal.cpp36
-rw-r--r--engines/cine/pal.h2
-rw-r--r--engines/cine/part.cpp71
-rw-r--r--engines/cine/part.h8
-rw-r--r--engines/cine/prc.cpp2
-rw-r--r--engines/cine/script_fw.cpp10
-rw-r--r--engines/cine/script_os.cpp24
-rw-r--r--engines/cine/sound.cpp1
-rw-r--r--engines/cine/texte.cpp186
-rw-r--r--engines/cine/texte.h24
-rw-r--r--engines/cine/various.cpp266
-rw-r--r--engines/cine/various.h10
-rw-r--r--engines/drascula/detection.cpp32
-rw-r--r--engines/drascula/drascula.cpp51
-rw-r--r--engines/drascula/interface.cpp25
-rw-r--r--engines/drascula/saveload.cpp8
-rw-r--r--engines/drascula/sound.cpp29
-rw-r--r--engines/gob/mult_v2.cpp9
-rw-r--r--engines/kyra/detection.cpp285
-rw-r--r--engines/kyra/gui_lok.cpp7
-rw-r--r--engines/kyra/gui_lok.h2
-rw-r--r--engines/kyra/gui_v2.cpp1
-rw-r--r--engines/kyra/gui_v2.h2
-rw-r--r--engines/kyra/kyra_hof.cpp48
-rw-r--r--engines/kyra/kyra_hof.h27
-rw-r--r--engines/kyra/kyra_lok.h2
-rw-r--r--engines/kyra/kyra_mr.cpp10
-rw-r--r--engines/kyra/kyra_v1.cpp12
-rw-r--r--engines/kyra/kyra_v1.h16
-rw-r--r--engines/kyra/lol.cpp10
-rw-r--r--engines/kyra/resource.cpp154
-rw-r--r--engines/kyra/saveload.cpp15
-rw-r--r--engines/kyra/scene_mr.cpp3
-rw-r--r--engines/kyra/screen.cpp9
-rw-r--r--engines/kyra/script_tim.cpp10
-rw-r--r--engines/kyra/sequences_hof.cpp226
-rw-r--r--engines/kyra/sound.cpp17
-rw-r--r--engines/kyra/sound.h20
-rw-r--r--engines/kyra/sound_adlib.cpp20
-rw-r--r--engines/kyra/sound_towns.cpp2
-rw-r--r--engines/kyra/staticres.cpp171
-rw-r--r--engines/parallaction/balloons.cpp510
-rw-r--r--engines/parallaction/callables_br.cpp2
-rw-r--r--engines/parallaction/callables_ns.cpp44
-rw-r--r--engines/parallaction/debug.cpp20
-rw-r--r--engines/parallaction/dialogue.cpp2
-rw-r--r--engines/parallaction/disk.h2
-rw-r--r--engines/parallaction/disk_br.cpp25
-rw-r--r--engines/parallaction/exec.h2
-rw-r--r--engines/parallaction/exec_br.cpp88
-rw-r--r--engines/parallaction/exec_ns.cpp166
-rw-r--r--engines/parallaction/graphics.cpp8
-rw-r--r--engines/parallaction/graphics.h40
-rw-r--r--engines/parallaction/objects.cpp81
-rw-r--r--engines/parallaction/objects.h128
-rw-r--r--engines/parallaction/parallaction.cpp38
-rw-r--r--engines/parallaction/parallaction.h14
-rw-r--r--engines/parallaction/parallaction_br.cpp6
-rw-r--r--engines/parallaction/parallaction_ns.cpp14
-rw-r--r--engines/parallaction/parser.cpp196
-rw-r--r--engines/parallaction/parser.h138
-rw-r--r--engines/parallaction/parser_br.cpp135
-rw-r--r--engines/parallaction/parser_ns.cpp61
-rw-r--r--engines/parallaction/saveload.cpp8
-rw-r--r--engines/parallaction/walk.cpp8
-rw-r--r--engines/queen/queen.h4
-rw-r--r--engines/saga/displayinfo.h6
-rw-r--r--engines/saga/interface.cpp39
-rw-r--r--engines/saga/itedata.cpp14
-rw-r--r--engines/saga/itedata.h2
-rw-r--r--engines/saga/saga.h3
-rw-r--r--engines/saga/sprite.cpp8
-rw-r--r--engines/scumm/boxes.cpp20
-rw-r--r--engines/scumm/charset.cpp22
-rw-r--r--engines/scumm/charset.h14
-rw-r--r--engines/scumm/detection.cpp7
-rw-r--r--engines/scumm/dialogs.cpp43
-rw-r--r--engines/scumm/gfx.cpp2
-rw-r--r--engines/scumm/saveload.cpp8
-rw-r--r--engines/scumm/saveload.h8
-rw-r--r--engines/scumm/script.cpp8
-rw-r--r--engines/scumm/script_v5.cpp2
-rw-r--r--engines/scumm/script_v6.cpp9
-rw-r--r--engines/scumm/scumm.h10
-rw-r--r--engines/scumm/string.cpp2
-rw-r--r--engines/sky/control.cpp2
-rw-r--r--engines/sky/logic.cpp3
-rw-r--r--engines/sky/sound.cpp15
-rw-r--r--engines/sky/sound.h2
-rw-r--r--engines/tinsel/detection.cpp59
-rw-r--r--engines/tinsel/scn.cpp2
-rw-r--r--engines/tinsel/tinsel.h4
-rw-r--r--engines/touche/graphics.cpp5
-rw-r--r--engines/touche/graphics.h2
-rw-r--r--engines/touche/staticres.cpp5
-rw-r--r--engines/touche/touche.cpp9
-rw-r--r--engines/touche/touche.h7
124 files changed, 3207 insertions, 1508 deletions
diff --git a/engines/agi/sound.cpp b/engines/agi/sound.cpp
index 77f79272f8..9fe8fbf41a 100644
--- a/engines/agi/sound.cpp
+++ b/engines/agi/sound.cpp
@@ -435,13 +435,10 @@ void IIgsMidiChannel::stopSounds() {
_gsChannels.clear();
}
-static int16 *buffer;
-
int SoundMgr::initSound() {
int r = -1;
- buffer = _sndBuffer = (int16 *)calloc(2, BUFFER_SIZE);
-
+ memset(_sndBuffer, 0, BUFFER_SIZE << 1);
_env = false;
switch (_vm->_soundemu) {
@@ -478,7 +475,6 @@ int SoundMgr::initSound() {
void SoundMgr::deinitSound() {
debugC(3, kDebugLevelSound, "()");
_mixer->stopHandle(_soundHandle);
- free(_sndBuffer);
}
void SoundMgr::stopNote(int i) {
@@ -1185,7 +1181,7 @@ bool SoundMgr::loadInstruments() {
return _gsSound.loadWaveFile(waveFsnode->getPath(), *exeInfo) && _gsSound.loadInstrumentHeaders(exeFsnode->getPath(), *exeInfo);
}
-static void fillAudio(void *udata, int16 *stream, uint len) {
+void SoundMgr::fillAudio(void *udata, int16 *stream, uint len) {
SoundMgr *soundMgr = (SoundMgr *)udata;
uint32 p = 0;
static uint32 n = 0, s = 0;
@@ -1193,32 +1189,34 @@ static void fillAudio(void *udata, int16 *stream, uint len) {
len <<= 2;
debugC(5, kDebugLevelSound, "(%p, %p, %d)", (void *)udata, (void *)stream, len);
- memcpy(stream, (uint8 *)buffer + s, p = n);
+ memcpy(stream, (uint8 *)_sndBuffer + s, p = n);
for (n = 0, len -= p; n < len; p += n, len -= n) {
soundMgr->playSound();
n = soundMgr->mixSound() << 1;
if (len < n) {
- memcpy((uint8 *)stream + p, buffer, len);
+ memcpy((uint8 *)stream + p, _sndBuffer, len);
s = len;
n -= s;
return;
} else {
- memcpy((uint8 *)stream + p, buffer, n);
+ memcpy((uint8 *)stream + p, _sndBuffer, n);
}
}
soundMgr->playSound();
n = soundMgr->mixSound() << 1;
- memcpy((uint8 *)stream + p, buffer, s = len);
+ memcpy((uint8 *)stream + p, _sndBuffer, s = len);
n -= s;
}
-SoundMgr::SoundMgr(AgiBase *agi, Audio::Mixer *pMixer) {
+SoundMgr::SoundMgr(AgiBase *agi, Audio::Mixer *pMixer) : _chn() {
_vm = agi;
_mixer = pMixer;
_sampleRate = pMixer->getOutputRate();
_endflag = -1;
_playingSound = -1;
- _sndBuffer = 0;
+ _env = false;
+ _playing = false;
+ _sndBuffer = (int16 *)calloc(2, BUFFER_SIZE);
_waveform = 0;
}
@@ -1231,6 +1229,7 @@ void SoundMgr::setVolume(uint8 volume) {
}
SoundMgr::~SoundMgr() {
+ free(_sndBuffer);
}
} // End of namespace Agi
diff --git a/engines/agi/sound.h b/engines/agi/sound.h
index f1c2782421..a1f079891f 100644
--- a/engines/agi/sound.h
+++ b/engines/agi/sound.h
@@ -472,6 +472,7 @@ private:
const int16 *_waveform;
void premixerCall(int16 *buf, uint len);
+ void fillAudio(void *udata, int16 *stream, uint len);
public:
void unloadSound(int);
diff --git a/engines/agos/animation.cpp b/engines/agos/animation.cpp
index c92f834a3b..3e1b9b0611 100644
--- a/engines/agos/animation.cpp
+++ b/engines/agos/animation.cpp
@@ -151,7 +151,7 @@ void MoviePlayer::play() {
startSound();
- while (_frameNum < _framesCount)
+ while (_frameNum < _framesCount && !_vm->_quit)
handleNextFrame();
closeFile();
@@ -167,7 +167,7 @@ void MoviePlayer::play() {
_vm->_system->setPalette(palette, 0, 256);
}
- _vm->fillBackGroundFromBack();
+ _vm->fillBackGroundFromBack();
_vm->_fastFadeOutFlag = true;
}
diff --git a/engines/agos/input.cpp b/engines/agos/input.cpp
index d36549f187..6f4cd09947 100644
--- a/engines/agos/input.cpp
+++ b/engines/agos/input.cpp
@@ -123,7 +123,7 @@ void AGOSEngine::setup_cond_c_helper() {
clearName();
_lastNameOn = last;
- for (;;) {
+ while (!_quit) {
_lastHitArea = NULL;
_lastHitArea3 = 0;
_leftButtonDown = 0;
@@ -145,7 +145,7 @@ void AGOSEngine::setup_cond_c_helper() {
}
delay(100);
- } while (_lastHitArea3 == (HitArea *) -1 || _lastHitArea3 == 0);
+ } while ((_lastHitArea3 == (HitArea *) -1 || _lastHitArea3 == 0) && !_quit);
if (_lastHitArea == NULL) {
} else if (_lastHitArea->id == 0x7FFB) {
diff --git a/engines/agos/midi.cpp b/engines/agos/midi.cpp
index 3114b24549..891e9bde95 100644
--- a/engines/agos/midi.cpp
+++ b/engines/agos/midi.cpp
@@ -538,7 +538,11 @@ void MidiPlayer::loadXMIDI(Common::File *in, bool sfx) {
error("Expected 'FORM' tag but found '%c%c%c%c' instead", buf[0], buf[1], buf[2], buf[3]);
}
- MidiParser *parser = MidiParser::createParser_XMIDI();
+ // In the DOS version of Simon the Sorcerer 2, the music contains lots
+ // of XMIDI callback controller events. As far as we know, they aren't
+ // actually used, so we disable the callback handler explicitly.
+
+ MidiParser *parser = MidiParser::createParser_XMIDI(NULL);
parser->setMidiDriver(this);
parser->setTimerRate(_driver->getBaseTempo());
if (!parser->loadMusic(p->data, size))
diff --git a/engines/agos/oracle.cpp b/engines/agos/oracle.cpp
index a113c8e2ea..2d2feb7b9e 100644
--- a/engines/agos/oracle.cpp
+++ b/engines/agos/oracle.cpp
@@ -459,7 +459,7 @@ void AGOSEngine_Feeble::saveUserGame(int slot) {
}
windowPutChar(window, 0x7f);
- for (;;) {
+ while (!_quit) {
_keyPressed.reset();
delay(1);
diff --git a/engines/agos/saveload.cpp b/engines/agos/saveload.cpp
index 4a5c43e706..9779630d47 100644
--- a/engines/agos/saveload.cpp
+++ b/engines/agos/saveload.cpp
@@ -279,11 +279,11 @@ restart:
name = buf;
_saveGameNameLen = 0;
- for (;;) {
+ while (!_quit) {
windowPutChar(window, 128);
_keyPressed.reset();
- for (;;) {
+ while (!_quit) {
delay(10);
if (_keyPressed.ascii && _keyPressed.ascii < 128) {
i = _keyPressed.ascii;
@@ -443,7 +443,7 @@ void AGOSEngine_Elvira2::userGame(bool load) {
name = buf + 192;
- for (;;) {
+ while (!_quit) {
windowPutChar(window, 128);
_saveLoadEdit = true;
@@ -516,7 +516,7 @@ int AGOSEngine_Elvira2::userGameGetKey(bool *b, char *buf, uint maxChar) {
_keyPressed.reset();
- for (;;) {
+ while (!_quit) {
_lastHitArea = NULL;
_lastHitArea3 = NULL;
@@ -526,7 +526,7 @@ int AGOSEngine_Elvira2::userGameGetKey(bool *b, char *buf, uint maxChar) {
return _keyPressed.ascii;
}
delay(10);
- } while (_lastHitArea3 == 0);
+ } while (_lastHitArea3 == 0 && !_quit);
ha = _lastHitArea;
if (ha == NULL || ha->id < 200) {
@@ -543,6 +543,8 @@ int AGOSEngine_Elvira2::userGameGetKey(bool *b, char *buf, uint maxChar) {
return ha->id - 200;
}
}
+
+ return 225;
}
void AGOSEngine_Simon1::listSaveGames(char *dst) {
@@ -706,7 +708,7 @@ restart:;
_saveGameNameLen++;
}
- for (;;) {
+ while (!_quit) {
windowPutChar(window, 127);
_saveLoadEdit = true;
@@ -785,7 +787,7 @@ int AGOSEngine_Simon1::userGameGetKey(bool *b, char *buf, uint maxChar) {
_keyPressed.reset();
- for (;;) {
+ while (!_quit) {
_lastHitArea = NULL;
_lastHitArea3 = NULL;
@@ -795,7 +797,7 @@ int AGOSEngine_Simon1::userGameGetKey(bool *b, char *buf, uint maxChar) {
return _keyPressed.ascii;
}
delay(10);
- } while (_lastHitArea3 == 0);
+ } while (_lastHitArea3 == 0 && !_quit);
ha = _lastHitArea;
if (ha == NULL || ha->id < 205) {
@@ -824,6 +826,8 @@ int AGOSEngine_Simon1::userGameGetKey(bool *b, char *buf, uint maxChar) {
return ha->id - 208;
}
}
+
+ return 205;
}
void AGOSEngine::disableFileBoxes() {
diff --git a/engines/agos/script.cpp b/engines/agos/script.cpp
index 6758aec511..fa132ec26f 100644
--- a/engines/agos/script.cpp
+++ b/engines/agos/script.cpp
@@ -1012,7 +1012,7 @@ int AGOSEngine::runScript() {
executeOpcode(_opcode);
} while (getScriptCondition() != flag && !getScriptReturn() && !_quit);
- return getScriptReturn();
+ return (_quit) ? 1 : getScriptReturn();
}
Child *nextSub(Child *sub, int16 key) {
diff --git a/engines/agos/script_e1.cpp b/engines/agos/script_e1.cpp
index c7e1d6736e..9b572e347b 100644
--- a/engines/agos/script_e1.cpp
+++ b/engines/agos/script_e1.cpp
@@ -1053,11 +1053,11 @@ uint AGOSEngine::confirmYesOrNo(uint16 x, uint16 y) {
ha->priority = 999;
ha->window = 0;
- for (;;) {
+ while (!_quit) {
_lastHitArea = NULL;
_lastHitArea3 = NULL;
- for (;;) {
+ while (!_quit) {
if (_lastHitArea3 != 0)
break;
delay(1);
@@ -1102,11 +1102,11 @@ uint AGOSEngine::continueOrQuit() {
ha->priority = 999;
ha->window = 0;
- for (;;) {
+ while (!_quit) {
_lastHitArea = NULL;
_lastHitArea3 = NULL;
- for (;;) {
+ while (!_quit) {
if (_lastHitArea3 != 0)
break;
delay(1);
diff --git a/engines/agos/script_e2.cpp b/engines/agos/script_e2.cpp
index da9afc5a7d..6f6db8efb4 100644
--- a/engines/agos/script_e2.cpp
+++ b/engines/agos/script_e2.cpp
@@ -370,11 +370,11 @@ void AGOSEngine_Elvira2::oe2_pauseGame() {
uint32 pauseTime = getTime();
haltAnimation();
- for (;;) {
+ while (!_quit) {
_lastHitArea = NULL;
_lastHitArea3 = NULL;
- for (;;) {
+ while (!_quit) {
if (processSpecialKeys() != 0 || _lastHitArea3 != 0)
break;
delay(1);
diff --git a/engines/agos/script_s1.cpp b/engines/agos/script_s1.cpp
index 51918b9515..6183a3fb20 100644
--- a/engines/agos/script_s1.cpp
+++ b/engines/agos/script_s1.cpp
@@ -339,18 +339,8 @@ void AGOSEngine_Simon1::os1_pauseGame() {
break;
}
- for (;;) {
+ while (!_quit) {
delay(1);
-#ifdef _WIN32_WCE
- if (isSmartphone()) {
- if (_keyPressed.keycode) {
- if (_keyPressed.keycode == Common::KEYCODE_RETURN)
- _quit = true;
- else
- break;
- }
- }
-#endif
if (_keyPressed.keycode == keyYes)
_quit = true;
else if (_keyPressed.keycode == keyNo)
diff --git a/engines/agos/script_ww.cpp b/engines/agos/script_ww.cpp
index 5fd83312c3..8dc915f6e8 100644
--- a/engines/agos/script_ww.cpp
+++ b/engines/agos/script_ww.cpp
@@ -368,11 +368,11 @@ void AGOSEngine_Waxworks::oww_pauseGame() {
uint32 pauseTime = getTime();
haltAnimation();
- for (;;) {
+ while (!_quit) {
_lastHitArea = NULL;
_lastHitArea3 = NULL;
- for (;;) {
+ while (!_quit) {
if (_lastHitArea3 != 0)
break;
delay(1);
diff --git a/engines/agos/verb.cpp b/engines/agos/verb.cpp
index 963bd6bd86..c8f6d40f1f 100644
--- a/engines/agos/verb.cpp
+++ b/engines/agos/verb.cpp
@@ -343,6 +343,9 @@ void AGOSEngine::handleVerbClicked(uint verb) {
Subroutine *sub;
int result;
+ if (_quit)
+ return;
+
_objectItem = _hitAreaObjectItem;
if (_objectItem == _dummyItem2) {
_objectItem = me();
diff --git a/engines/agos/window.cpp b/engines/agos/window.cpp
index e25bd6b438..e1f986b92e 100644
--- a/engines/agos/window.cpp
+++ b/engines/agos/window.cpp
@@ -298,7 +298,7 @@ void AGOSEngine::waitWindow(WindowBlock *window) {
ha->id = 0x7FFF;
ha->priority = 999;
- for (;;) {
+ while (!_quit) {
_lastHitArea = NULL;
_lastHitArea3 = NULL;
diff --git a/engines/cine/anim.cpp b/engines/cine/anim.cpp
index 8dbccebedf..a670328c12 100644
--- a/engines/cine/anim.cpp
+++ b/engines/cine/anim.cpp
@@ -49,7 +49,7 @@ struct AnimHeader2Struct {
uint16 field_E;
};
-AnimData animDataTable[NUM_MAX_ANIMDATA];
+Common::Array<AnimData> animDataTable;
static const AnimDataEntry transparencyData[] = {
{"ALPHA", 0xF},
@@ -511,14 +511,15 @@ int emptyAnimSpace(int start = 0) {
/*! \brief Load SPL data into animDataTable
* \param resourceName SPL filename
- * \param idx Target index in animDataTable
+ * \param idx Target index in animDataTable (-1 if any empty space will do)
+ * \return The number of the animDataTable entry after the loaded SPL data (-1 if error)
*/
-void loadSpl(const char *resourceName, int16 idx) {
+int loadSpl(const char *resourceName, int16 idx) {
int16 foundFileIdx = findFileInBundle(resourceName);
int entry;
if (foundFileIdx < 0) {
- return;
+ return -1;
}
byte *dataPtr = readBundleFile(foundFileIdx);
@@ -528,13 +529,20 @@ void loadSpl(const char *resourceName, int16 idx) {
animDataTable[entry].load(dataPtr, ANIM_RAW, partBuffer[foundFileIdx].unpackedSize, 1, foundFileIdx, 0, currentPartName);
free(dataPtr);
+ return entry + 1;
}
/*! \brief Load 1bpp mask
* \param resourceName Mask filename
+ * \param idx Target index in animDataTable (-1 if any empty space will do)
+ * \return The number of the animDataTable entry after the loaded mask (-1 if error)
*/
-void loadMsk(const char *resourceName) {
+int loadMsk(const char *resourceName, int16 idx) {
int16 foundFileIdx = findFileInBundle(resourceName);
+ if (foundFileIdx < 0) {
+ return -1;
+ }
+
int entry = 0;
byte *dataPtr = readBundleFile(foundFileIdx);
byte *ptr;
@@ -544,21 +552,28 @@ void loadMsk(const char *resourceName) {
loadAnimHeader(animHeader, readS);
ptr = dataPtr + 0x16;
+ entry = idx < 0 ? emptyAnimSpace() : idx;
+ assert(entry >= 0);
for (int16 i = 0; i < animHeader.numFrames; i++, entry++) {
- entry = emptyAnimSpace(entry);
- assert(entry >= 0);
animDataTable[entry].load(ptr, ANIM_MASK, animHeader.frameWidth, animHeader.frameHeight, foundFileIdx, i, currentPartName);
ptr += animHeader.frameWidth * animHeader.frameHeight;
}
free(dataPtr);
+ return entry;
}
/*! \brief Load animation
* \param resourceName Animation filename
+ * \param idx Target index in animDataTable (-1 if any empty space will do)
+ * \return The number of the animDataTable entry after the loaded animation (-1 if error)
*/
-void loadAni(const char *resourceName) {
+int loadAni(const char *resourceName, int16 idx) {
int16 foundFileIdx = findFileInBundle(resourceName);
+ if (foundFileIdx < 0) {
+ return -1;
+ }
+
int entry = 0;
byte *dataPtr = readBundleFile(foundFileIdx);
byte *ptr;
@@ -571,10 +586,10 @@ void loadAni(const char *resourceName) {
transparentColor = getAnimTransparentColor(resourceName);
- for (int16 i = 0; i < animHeader.numFrames; i++, entry++) {
- entry = emptyAnimSpace(entry);
- assert(entry >= 0);
+ entry = idx < 0 ? emptyAnimSpace() : idx;
+ assert(entry >= 0);
+ for (int16 i = 0; i < animHeader.numFrames; i++, entry++) {
// special case transparency handling
if (!strcmp(resourceName, "L2202.ANI")) {
transparentColor = i < 2 ? 0 : 7;
@@ -587,6 +602,7 @@ void loadAni(const char *resourceName) {
}
free(dataPtr);
+ return entry;
}
/*! \brief Decode 16 color image with palette
@@ -642,16 +658,21 @@ void convert8BBP2(byte *dest, byte *source, int16 width, int16 height) {
/*! \brief Load image set
* \param resourceName Image set filename
- * \param idx Target index in animDataTable
+ * \param idx Target index in animDataTable (-1 if any empty space will do)
+ * \return The number of the animDataTable entry after the loaded image set (-1 if error)
*/
-void loadSet(const char *resourceName, int16 idx) {
+int loadSet(const char *resourceName, int16 idx) {
AnimHeader2Struct header2;
uint16 numSpriteInAnim;
int16 foundFileIdx = findFileInBundle(resourceName);
- int16 entry = idx >= 0 ? idx : 0;
+ int16 entry;
byte *ptr, *startOfDataPtr, *dataPtr, *origDataPtr;
int type;
+ if (foundFileIdx < 0) {
+ return -1;
+ }
+
origDataPtr = dataPtr = readBundleFile(foundFileIdx);
assert(!memcmp(dataPtr, "SET", 3));
ptr = dataPtr + 4;
@@ -661,6 +682,9 @@ void loadSet(const char *resourceName, int16 idx) {
startOfDataPtr = ptr + numSpriteInAnim * 0x10;
+ entry = idx < 0 ? emptyAnimSpace() : idx;
+ assert(entry >= 0);
+
for (int16 i = 0; i < numSpriteInAnim; i++, entry++) {
Common::MemoryReadStream readS(ptr, 0x10);
@@ -674,9 +698,6 @@ void loadSet(const char *resourceName, int16 idx) {
ptr += 0x10;
- entry = idx < 0 ? emptyAnimSpace(entry) : idx + i;
- assert(entry >= 0);
-
dataPtr = startOfDataPtr + header2.field_0;
if (header2.type == 1) {
@@ -693,78 +714,59 @@ void loadSet(const char *resourceName, int16 idx) {
}
free(origDataPtr);
+ return entry;
}
/*! \brief Load SEQ data into animDataTable
* \param resourceName SEQ data filename
- * \param idx Target index in animDataTable
+ * \param idx Target index in animDataTable (-1 if any empty space will do)
+ * \return The number of the animDataTable entry after the loaded SEQ data (-1 if error)
*/
-void loadSeq(const char *resourceName, int16 idx) {
+int loadSeq(const char *resourceName, int16 idx) {
int16 foundFileIdx = findFileInBundle(resourceName);
+ if (foundFileIdx < 0) {
+ return -1;
+ }
+
byte *dataPtr = readBundleFile(foundFileIdx);
int entry = idx < 0 ? emptyAnimSpace() : idx;
animDataTable[entry].load(dataPtr+0x16, ANIM_RAW, partBuffer[foundFileIdx].unpackedSize-0x16, 1, foundFileIdx, 0, currentPartName);
free(dataPtr);
+ return entry + 1;
}
-void loadResource(const char *resourceName) {
- /* byte isMask = 0; */
- /* byte isSpl = 0; */
-
+/*! \brief Load a resource into animDataTable
+ * \param resourceName Resource's filename
+ * \param idx Target index in animDataTable (-1 if any empty space will do)
+ * \return The number of the animDataTable entry after the loaded resource (-1 if error)
+ * \todo Implement loading of all resource types
+ */
+int loadResource(const char *resourceName, int16 idx) {
+ int result = -1; // Return an error by default
if (strstr(resourceName, ".SPL")) {
- loadSpl(resourceName, -1);
- return;
+ result = loadSpl(resourceName, idx);
} else if (strstr(resourceName, ".MSK")) {
- loadMsk(resourceName);
- return;
+ result = loadMsk(resourceName, idx);
} else if (strstr(resourceName, ".ANI")) {
- loadAni(resourceName);
- return;
+ result = loadAni(resourceName, idx);
} else if (strstr(resourceName, ".ANM")) {
- loadAni(resourceName);
- return;
+ result = loadAni(resourceName, idx);
} else if (strstr(resourceName, ".SET")) {
- loadSet(resourceName, -1);
- return;
+ result = loadSet(resourceName, idx);
} else if (strstr(resourceName, ".SEQ")) {
- loadSeq(resourceName, -1);
- return;
- } else if (strstr(resourceName, "ECHEC")) { // Echec (French) means failure
- exitEngine = 1;
- return;
- }
-
- error("loadResource: Cannot determine type for '%s'", resourceName);
-}
-
-/*! \todo There seems to be some additional resource file that is not loaded
- */
-void loadAbs(const char *resourceName, uint16 idx) {
- /* byte isMask = 0; */
- /* byte isSpl = 0; */
-
- if (strstr(resourceName, ".SET")) {
- loadSet(resourceName, idx);
- return;
+ result = loadSeq(resourceName, idx);
} else if (strstr(resourceName, ".H32")) {
- warning("Ignoring file %s (load at %d)", resourceName, idx);
- return;
- } else if (strstr(resourceName, ".SEQ")) {
- loadSeq(resourceName, idx);
- return;
- } else if (strstr(resourceName, ".SPL")) {
- loadSpl(resourceName, idx);
- return;
+ warning("loadResource: Ignoring file '%s' (Load at %d)", resourceName, idx);
} else if (strstr(resourceName, ".AMI")) {
- warning("Ignoring file %s (load at %d)", resourceName, idx);
- return;
- } else if (strstr(resourceName, ".ANI")) {
- warning("Ignoring file %s (load at %d)", resourceName, idx);
- return;
+ warning("loadResource: Ignoring file '%s' (Load at %d)", resourceName, idx);
+ } else if (strstr(resourceName, "ECHEC")) { // Echec (French) means failure
+ exitEngine = 1;
+ } else {
+ error("loadResource: Cannot determine type for '%s'", resourceName);
}
- error("loadAbs: Cannot determine type for '%s'", resourceName);
+ return result;
}
/*! \brief Load animDataTable from save
@@ -776,17 +778,9 @@ void loadAbs(const char *resourceName, uint16 idx) {
* at a time.
*/
void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGameFormat saveGameFormat) {
- int16 currentAnim, foundFileIdx;
- int8 isMask = 0, isSpl = 0;
- byte *dataPtr, *ptr;
- char *animName, part[256];
- byte transparentColor = 0;
- AnimHeaderStruct animHeader;
-
+ int16 currentAnim, foundFileIdx, frame;
+ char *animName, part[256], name[10];
uint16 width, height, bpp, var1;
- int16 frame;
- char name[10];
- int type;
strcpy(part, currentPartName);
@@ -795,11 +789,8 @@ void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGam
const int entrySize = ((saveGameFormat == ANIMSIZE_23) ? 23 : 30);
const int fileStartPos = fHandle.pos();
- for (currentAnim = 0; currentAnim < NUM_MAX_ANIMDATA; currentAnim += animHeader.numFrames) {
- // Initialize the number of frames variable to a sane number.
- // This is needed when using continue later in this function.
- animHeader.numFrames = 1;
-
+ currentAnim = 0;
+ while (currentAnim < NUM_MAX_ANIMDATA) {
// Seek to the start of the current animation's entry
fHandle.seek(fileStartPos + currentAnim * entrySize);
// Read in the current animation entry
@@ -826,6 +817,7 @@ void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGam
// Don't try to load invalid entries.
if (foundFileIdx < 0 || !validPtr) {
+ currentAnim++; // Jump over the invalid entry
continue;
}
@@ -836,52 +828,10 @@ void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGam
}
animName = partBuffer[foundFileIdx].partName;
- ptr = dataPtr = readBundleFile(foundFileIdx);
-
- // isSpl and isMask are mutually exclusive cases
- isSpl = (strstr(animName, ".SPL")) ? 1 : 0;
- isMask = (strstr(animName, ".MSK")) ? 1 : 0;
-
- if (isSpl) {
- width = (uint16) partBuffer[foundFileIdx].unpackedSize;
- height = 1;
- animHeader.numFrames = 1;
- type = ANIM_RAW;
- } else {
- Common::MemoryReadStream readS(ptr, 0x16);
- loadAnimHeader(animHeader, readS);
- ptr += 0x16;
-
- width = animHeader.frameWidth;
- height = animHeader.frameHeight;
-
- if (isMask) {
- type = ANIM_MASK;
- } else {
- type = ANIM_MASKSPRITE;
- }
- }
-
- loadRelatedPalette(animName);
- transparentColor = getAnimTransparentColor(animName);
- // Make sure we load at least one frame and also that we
- // don't overflow the animDataTable by writing beyond its end.
- animHeader.numFrames = CLIP<uint16>(animHeader.numFrames, 1, NUM_MAX_ANIMDATA - currentAnim);
-
- // Load the frames
- for (frame = 0; frame < animHeader.numFrames; frame++) {
- // special case transparency handling
- if (!strcmp(animName, "L2202.ANI")) {
- transparentColor = (frame < 2) ? 0 : 7;
- } else if (!strcmp(animName, "L4601.ANI")) {
- transparentColor = (frame < 1) ? 0xE : 0;
- }
-
- // Load a single frame
- animDataTable[currentAnim + frame].load(ptr + frame * width * height, type, width, height, foundFileIdx, frame, name, transparentColor);
- }
-
- free(dataPtr);
+ loadRelatedPalette(animName); // Is this for Future Wars only?
+ const int16 prevAnim = currentAnim;
+ currentAnim = loadResource(animName, currentAnim);
+ assert(currentAnim > prevAnim); // Make sure we advance forward
}
loadPart(part);
diff --git a/engines/cine/anim.h b/engines/cine/anim.h
index b0ce55f7ee..8b1541eb3f 100644
--- a/engines/cine/anim.h
+++ b/engines/cine/anim.h
@@ -150,12 +150,11 @@ public:
#define NUM_MAX_ANIMDATA 255
-extern AnimData animDataTable[NUM_MAX_ANIMDATA];
+extern Common::Array<AnimData> animDataTable;
void freeAnimDataTable(void);
void freeAnimDataRange(byte startIdx, byte numIdx);
-void loadResource(const char *resourceName);
-void loadAbs(const char *resourceName, uint16 idx);
+int loadResource(const char *resourceName, int16 idx = -1);
void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGameFormat saveGameFormat);
void generateMask(const byte *sprite, byte *mask, uint16 size, byte transparency);
diff --git a/engines/cine/bg.cpp b/engines/cine/bg.cpp
index 2a4e7f0ab1..45bfae7925 100644
--- a/engines/cine/bg.cpp
+++ b/engines/cine/bg.cpp
@@ -56,7 +56,7 @@ byte loadCtFW(const char *ctName) {
header[i] = readS.readUint16BE();
}
- gfxConvertSpriteToRaw(page3Raw, ptr + 0x80, 160, 200);
+ gfxConvertSpriteToRaw(collisionPage, ptr + 0x80, 160, 200);
free(dataPtr);
return 0;
@@ -74,10 +74,10 @@ byte loadCtOS(const char *ctName) {
ptr += 2;
if (bpp == 8) {
- memcpy(page3Raw, ptr + 256 * 3, 320 * 200);
+ memcpy(collisionPage, ptr + 256 * 3, 320 * 200);
renderer->loadCt256(ptr, ctName);
} else {
- gfxConvertSpriteToRaw(page3Raw, ptr + 32, 160, 200);
+ gfxConvertSpriteToRaw(collisionPage, ptr + 32, 160, 200);
renderer->loadCt16(ptr, ctName);
}
diff --git a/engines/cine/cine.cpp b/engines/cine/cine.cpp
index f6778b6457..9eb751835e 100644
--- a/engines/cine/cine.cpp
+++ b/engines/cine/cine.cpp
@@ -57,6 +57,10 @@ CineEngine::CineEngine(OSystem *syst, const CINEGameDescription *gameDesc) : Eng
// Setup mixer
_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
+ // Use music volume for plain sound types (At least the Adlib player uses a plain sound type
+ // so previously the music and sfx volume controls didn't affect it at all).
+ // FIXME: Make Adlib player differentiate between playing sound effects and music and remove this.
+ _mixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, ConfMan.getInt("music_volume"));
g_cine = this;
@@ -65,14 +69,9 @@ CineEngine::CineEngine(OSystem *syst, const CINEGameDescription *gameDesc) : Eng
CineEngine::~CineEngine() {
if (g_cine->getGameType() == Cine::GType_OS) {
- freePoldatDat();
freeErrmessDat();
}
Common::clearAllSpecialDebugLevels();
-
- free(palPtr);
- free(partBuffer);
- free(textDataPtr);
}
int CineEngine::init() {
@@ -100,13 +99,43 @@ int CineEngine::go() {
mainLoop(1);
delete renderer;
- delete[] page3Raw;
+ delete[] collisionPage;
delete g_sound;
return 0;
}
+int CineEngine::getTimerDelay() const {
+ return (10923000 * _timerDelayMultiplier) / 1193180;
+}
+
+/*! \brief Modify game speed
+ * \param speedChange Negative values slow game down, positive values speed it up, zero does nothing
+ * \return Timer delay multiplier's value after the game speed change
+ */
+int CineEngine::modifyGameSpeed(int speedChange) {
+ // If we want more speed we decrement the timer delay multiplier and vice versa.
+ _timerDelayMultiplier = CLIP(_timerDelayMultiplier - speedChange, 1, 50);
+ return _timerDelayMultiplier;
+}
void CineEngine::initialize() {
+ // Resize object table to its correct size and reset all its elements
+ objectTable.resize(NUM_MAX_OBJECT);
+ resetObjectTable();
+
+ // Resize animation data table to its correct size and reset all its elements
+ animDataTable.resize(NUM_MAX_ANIMDATA);
+ freeAnimDataTable();
+
+ // Resize zone data table to its correct size and reset all its elements
+ zoneData.resize(NUM_MAX_ZONE);
+ Common::set_to(zoneData.begin(), zoneData.end(), 0);
+
+ // Resize zone query table to its correct size and reset all its elements
+ zoneQuery.resize(NUM_MAX_ZONE);
+ Common::set_to(zoneQuery.begin(), zoneQuery.end(), 0);
+
+ _timerDelayMultiplier = 12; // Set default speed
setupOpcodes();
initLanguage(g_cine->getLanguage());
@@ -117,16 +146,17 @@ void CineEngine::initialize() {
renderer = new FWRenderer;
}
- page3Raw = new byte[320 * 200];
- textDataPtr = (byte *)malloc(8000);
+ collisionPage = new byte[320 * 200];
- partBuffer = (PartBuffer *)malloc(NUM_MAX_PARTDATA * sizeof(PartBuffer));
+ // Clear part buffer as there's nothing loaded into it yet.
+ // Its size will change when loading data into it with the loadPart function.
+ partBuffer.clear();
if (g_cine->getGameType() == Cine::GType_OS) {
readVolCnf();
}
- loadTextData("texte.dat", textDataPtr);
+ loadTextData("texte.dat");
if (g_cine->getGameType() == Cine::GType_OS && !(g_cine->getFeatures() & GF_DEMO)) {
loadPoldatDat("poldat.dat");
@@ -142,8 +172,7 @@ void CineEngine::initialize() {
freeAnimDataTable();
overlayList.clear();
messageTable.clear();
-
- memset(objectTable, 0, sizeof(objectTable));
+ resetObjectTable();
var8 = 0;
diff --git a/engines/cine/cine.h b/engines/cine/cine.h
index eaae555812..6011036eb1 100644
--- a/engines/cine/cine.h
+++ b/engines/cine/cine.h
@@ -59,7 +59,8 @@ enum CineGameType {
enum CineGameFeatures {
GF_CD = 1 << 0,
GF_DEMO = 1 << 1,
- GF_ALT_FONT = 1 << 2
+ GF_ALT_FONT = 1 << 2,
+ GF_CRYPTED_BOOT_PRC = 1 << 3
};
struct CINEGameDescription;
@@ -86,6 +87,8 @@ public:
bool loadSaveDirectory(void);
void makeSystemMenu(void);
+ int modifyGameSpeed(int speedChange);
+ int getTimerDelay() const;
const CINEGameDescription *_gameDescription;
Common::File _partFileHandle;
@@ -109,6 +112,7 @@ private:
void readVolCnf();
bool _preLoad;
+ int _timerDelayMultiplier;
};
extern CineEngine *g_cine;
diff --git a/engines/cine/detection.cpp b/engines/cine/detection.cpp
index 8c940bcfd4..b15f30c35c 100644
--- a/engines/cine/detection.cpp
+++ b/engines/cine/detection.cpp
@@ -76,6 +76,25 @@ static const CINEGameDescription gameDescriptions[] = {
0,
},
+ // This is a CD version of Future Wars published by Sony.
+ // This version has a crypted AUTO00.PRC.
+ {
+ {
+ "fw",
+ "Sony CD version",
+ {
+ { "AUTO00.PRC", 0, "4fe1e7930b38e3c63f0f2474d471bf8f", -1},
+ { "PART01", 0, "61d003202d301c29dd399acfb1354310", -1},
+ { NULL, 0, NULL, 0}
+ },
+ Common::EN_USA,
+ Common::kPlatformPC,
+ Common::ADGF_CD
+ },
+ GType_FW,
+ GF_CD | GF_CRYPTED_BOOT_PRC,
+ },
+
{
// This is the version included in the UK "Classic Collection"
{
@@ -251,6 +270,21 @@ static const CINEGameDescription gameDescriptions[] = {
},
{
+ // This is a 16 color PC version (It came on three 720kB 3.5" disks).
+ // The protagonist is named John Glames in this version.
+ {
+ "os",
+ "",
+ AD_ENTRY1("procs1", "9629129b86979fa592c1787385bf3695"),
+ Common::EN_GRB,
+ Common::kPlatformPC,
+ Common::ADGF_NO_FLAGS
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
{
"os",
"",
diff --git a/engines/cine/gfx.cpp b/engines/cine/gfx.cpp
index cbddf0fc59..cb900e8850 100644
--- a/engines/cine/gfx.cpp
+++ b/engines/cine/gfx.cpp
@@ -31,12 +31,13 @@
#include "common/endian.h"
#include "common/system.h"
+#include "common/events.h"
#include "graphics/cursorman.h"
namespace Cine {
-byte *page3Raw;
+byte *collisionPage;
FWRenderer *renderer = NULL;
static const byte mouseCursorNormal[] = {
@@ -91,7 +92,7 @@ static const byte cursorPalette[] = {
*/
FWRenderer::FWRenderer() : _background(NULL), _palette(NULL), _cmd(""),
_cmdY(0), _messageBg(0), _backBuffer(new byte[_screenSize]),
- _activeLowPal(NULL), _changePal(0) {
+ _activeLowPal(NULL), _changePal(0), _showCollisionPage(false) {
assert(_backBuffer);
@@ -125,6 +126,7 @@ void FWRenderer::clear() {
_cmdY = 0;
_messageBg = 0;
_changePal = 0;
+ _showCollisionPage = false;
}
/*! \brief Draw 1bpp sprite using selected color
@@ -225,14 +227,18 @@ void FWRenderer::drawCommand() {
* \param x Top left message box corner coordinate
* \param y Top left message box corner coordinate
* \param width Message box width
- * \param color Message box background color
+ * \param color Message box background color (Or if negative draws only the text)
+ * \note Negative colors are used in Operation Stealth's timed cutscenes
+ * (e.g. when first meeting The Movement for the Liberation of Santa Paragua).
*/
-void FWRenderer::drawMessage(const char *str, int x, int y, int width, byte color) {
+void FWRenderer::drawMessage(const char *str, int x, int y, int width, int color) {
int i, tx, ty, tw;
int line = 0, words = 0, cw = 0;
int space = 0, extraSpace = 0;
- drawPlainBox(x, y, width, 4, color);
+ if (color >= 0) {
+ drawPlainBox(x, y, width, 4, color);
+ }
tx = x + 4;
ty = str[0] ? y - 5 : y + 4;
tw = width - 8;
@@ -252,7 +258,9 @@ void FWRenderer::drawMessage(const char *str, int x, int y, int width, byte colo
}
ty += 9;
- drawPlainBox(x, ty, width, 9, color);
+ if (color >= 0) {
+ drawPlainBox(x, ty, width, 9, color);
+ }
tx = x + 4;
}
@@ -269,33 +277,56 @@ void FWRenderer::drawMessage(const char *str, int x, int y, int width, byte colo
}
ty += 9;
- drawPlainBox(x, ty, width, 4, color);
- drawDoubleBorder(x, y, width, ty - y + 4, 2);
+ if (color >= 0) {
+ drawPlainBox(x, ty, width, 4, color);
+ drawDoubleBorder(x, y, width, ty - y + 4, 2);
+ }
}
/*! \brief Draw rectangle on screen
* \param x Top left corner coordinate
* \param y Top left corner coordinate
- * \param width Rectangle width
- * \param height Rectangle height
+ * \param width Rectangle width (Negative values draw the box horizontally flipped)
+ * \param height Rectangle height (Negative values draw the box vertically flipped)
* \param color Fill color
+ * \note An on-screen rectangle's drawn width is always at least one.
+ * \note An on-screen rectangle's drawn height is always at least one.
*/
void FWRenderer::drawPlainBox(int x, int y, int width, int height, byte color) {
- int i;
- byte *dest = _backBuffer + y * 320 + x;
+ // Make width's and height's absolute values at least one
+ // which forces this function to always draw something if the
+ // drawing position is inside screen bounds. This fixes at least
+ // the showing of the oxygen gauge meter in Operation Stealth's
+ // first arcade sequence where this function is called with a
+ // height of zero.
+ if (width == 0) {
+ width = 1;
+ }
+ if (height == 0) {
+ height = 1;
+ }
+ // Handle horizontally flipped boxes
if (width < 0) {
- x += width;
- width = -width;
+ width = ABS(width);
+ x -= width;
}
+ // Handle vertically flipped boxes
if (height < 0) {
- y += height;
- height = -height;
+ height = ABS(height);
+ y -= height;
}
- for (i = 0; i < height; i++) {
- memset(dest + i * 320, color, width);
+ // Clip the rectangle to screen dimensions
+ Common::Rect boxRect(x, y, x + width, y + height);
+ Common::Rect screenRect(320, 200);
+ boxRect.clip(screenRect);
+
+ // Draw the filled rectangle
+ byte *dest = _backBuffer + boxRect.top * 320 + boxRect.left;
+ for (int i = 0; i < boxRect.height(); i++) {
+ memset(dest + i * 320, color, boxRect.width());
}
}
@@ -335,8 +366,8 @@ int FWRenderer::drawChar(char character, int x, int y) {
if (character == ' ') {
x += 5;
- } else if ((width = fontParamTable[(unsigned char)character].characterWidth)) {
- idx = fontParamTable[(unsigned char)character].characterIdx;
+ } else if ((width = g_cine->_textHandler.fontParamTable[(unsigned char)character].characterWidth)) {
+ idx = g_cine->_textHandler.fontParamTable[(unsigned char)character].characterIdx;
drawSpriteRaw(g_cine->_textHandler.textTable[idx][0], g_cine->_textHandler.textTable[idx][1], 16, 8, _backBuffer, x, y);
x += width + 1;
}
@@ -405,7 +436,7 @@ void FWRenderer::renderOverlay(const Common::List<overlay>::iterator &it) {
switch (it->type) {
// color sprite
case 0:
- sprite = animDataTable + objectTable[it->objIdx].frame;
+ sprite = &animDataTable[objectTable[it->objIdx].frame];
len = sprite->_realWidth * sprite->_height;
mask = new byte[len];
memcpy(mask, sprite->mask(), len);
@@ -422,6 +453,7 @@ void FWRenderer::renderOverlay(const Common::List<overlay>::iterator &it) {
_messageLen += messageTable[it->objIdx].size();
drawMessage(messageTable[it->objIdx].c_str(), it->x, it->y, it->width, it->color);
+ waitForPlayerClick = 1;
break;
// action failure message
@@ -433,12 +465,13 @@ void FWRenderer::renderOverlay(const Common::List<overlay>::iterator &it) {
width = width > 300 ? 300 : width;
drawMessage(failureMessages[idx], (320 - width) / 2, 80, width, 4);
+ waitForPlayerClick = 1;
break;
// bitmap
case 4:
assert(it->objIdx < NUM_MAX_OBJECT);
- obj = objectTable + it->objIdx;
+ obj = &objectTable[it->objIdx];
if (obj->frame < 0) {
return;
@@ -480,16 +513,28 @@ void FWRenderer::drawFrame() {
blit();
}
+/*!
+ * \brief Turn on or off the showing of the collision page.
+ * If turned on the blitting routine shows the collision page instead of the back buffer.
+ * \note Useful for debugging collision page related problems.
+ */
+void FWRenderer::showCollisionPage(bool state) {
+ _showCollisionPage = state;
+}
+
/*! \brief Update screen
*/
void FWRenderer::blit() {
- g_system->copyRectToScreen(_backBuffer, 320, 0, 0, 320, 200);
+ // Show the back buffer or the collision page. Normally the back
+ // buffer but showing the collision page is useful for debugging.
+ byte *source = (_showCollisionPage ? collisionPage : _backBuffer);
+ g_system->copyRectToScreen(source, 320, 0, 0, 320, 200);
}
/*! \brief Set player command string
* \param cmd New command string
*/
-void FWRenderer::setCommand(const char *cmd) {
+void FWRenderer::setCommand(Common::String cmd) {
_cmd = cmd;
}
@@ -617,6 +662,11 @@ void FWRenderer::saveBgNames(Common::OutSaveFile &fHandle) {
fHandle.write(_bgName, 13);
}
+const char *FWRenderer::getBgName(uint idx) const {
+ assert(idx == 0);
+ return _bgName;
+}
+
/*! \brief Restore active and backup palette from save
* \param fHandle Savefile open for reading
*/
@@ -985,8 +1035,8 @@ int OSRenderer::drawChar(char character, int x, int y) {
if (character == ' ') {
x += 5;
- } else if ((width = fontParamTable[(unsigned char)character].characterWidth)) {
- idx = fontParamTable[(unsigned char)character].characterIdx;
+ } else if ((width = g_cine->_textHandler.fontParamTable[(unsigned char)character].characterWidth)) {
+ idx = g_cine->_textHandler.fontParamTable[(unsigned char)character].characterIdx;
drawSpriteRaw2(g_cine->_textHandler.textTable[idx][0], 0, 16, 8, _backBuffer, x, y);
x += width + 1;
}
@@ -1011,8 +1061,12 @@ void OSRenderer::drawBackground() {
assert(scroll);
- memcpy(_backBuffer, main + mainShift, mainSize);
- memcpy(_backBuffer + mainSize, scroll, mainShift);
+ if (mainSize > 0) { // Just a precaution
+ memcpy(_backBuffer, main + mainShift, mainSize);
+ }
+ if (mainShift > 0) { // Just a precaution
+ memcpy(_backBuffer + mainSize, scroll, mainShift);
+ }
}
}
@@ -1021,10 +1075,11 @@ void OSRenderer::drawBackground() {
* \todo Add handling of type 22 overlays
*/
void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) {
- int len;
+ int len, idx, width, height;
objectStruct *obj;
AnimData *sprite;
byte *mask;
+ byte color;
switch (it->type) {
// color sprite
@@ -1032,7 +1087,7 @@ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) {
if (objectTable[it->objIdx].frame < 0) {
break;
}
- sprite = animDataTable + objectTable[it->objIdx].frame;
+ sprite = &animDataTable[objectTable[it->objIdx].frame];
len = sprite->_realWidth * sprite->_height;
mask = new byte[len];
generateMask(sprite->data(), mask, len, objectTable[it->objIdx].part);
@@ -1041,6 +1096,32 @@ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) {
delete[] mask;
break;
+ // game message
+ case 2:
+ if (it->objIdx >= messageTable.size()) {
+ return;
+ }
+
+ _messageLen += messageTable[it->objIdx].size();
+ drawMessage(messageTable[it->objIdx].c_str(), it->x, it->y, it->width, it->color);
+ if (it->color >= 0) { // This test isn't in Future Wars's implementation
+ waitForPlayerClick = 1;
+ }
+ break;
+
+ // action failure message
+ case 3:
+ idx = it->objIdx * 4 + g_cine->_rnd.getRandomNumber(3);
+ len = strlen(failureMessages[idx]);
+ _messageLen += len;
+ width = 6 * len + 20;
+ width = width > 300 ? 300 : width;
+
+ // The used color here differs from Future Wars
+ drawMessage(failureMessages[idx], (320 - width) / 2, 80, width, _messageBg);
+ waitForPlayerClick = 1;
+ break;
+
// bitmap
case 4:
if (objectTable[it->objIdx].frame >= 0) {
@@ -1051,16 +1132,37 @@ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) {
// masked background
case 20:
assert(it->objIdx < NUM_MAX_OBJECT);
- obj = objectTable + it->objIdx;
- sprite = animDataTable + obj->frame;
+ var5 = it->x; // A global variable updated here!
+ obj = &objectTable[it->objIdx];
+ sprite = &animDataTable[obj->frame];
- if (obj->frame < 0 || it->x > 8 || !_bgTable[it->x].bg || sprite->_bpp != 1) {
+ if (obj->frame < 0 || it->x < 0 || it->x > 8 || !_bgTable[it->x].bg || sprite->_bpp != 1) {
break;
}
maskBgOverlay(_bgTable[it->x].bg, sprite->data(), sprite->_realWidth, sprite->_height, _backBuffer, obj->x, obj->y);
break;
+ // FIXME: Implement correct drawing of type 21 overlays.
+ // Type 21 overlays aren't just filled rectangles, I found their drawing routine
+ // from Operation Stealth's drawSprite routine. So they're likely some kind of sprites
+ // and it's just a coincidence that the oxygen meter during the first arcade sequence
+ // works even somehow currently. I tried the original under DOSBox and the oxygen gauge
+ // is a long red bar that gets shorter as the air runs out.
+ case 21:
+ // A filled rectangle:
+ case 22:
+ // TODO: Check it this implementation really works correctly (Some things might be wrong, needs testing).
+ assert(it->objIdx < NUM_MAX_OBJECT);
+ obj = &objectTable[it->objIdx];
+ color = obj->part & 0x0F;
+ width = obj->frame;
+ height = obj->costume;
+ drawPlainBox(obj->x, obj->y, width, height, color);
+ debug(5, "renderOverlay: type=%d, x=%d, y=%d, width=%d, height=%d, color=%d",
+ it->type, obj->x, obj->y, width, height, color);
+ break;
+
// something else
default:
FWRenderer::renderOverlay(it);
@@ -1332,6 +1434,11 @@ void OSRenderer::saveBgNames(Common::OutSaveFile &fHandle) {
}
}
+const char *OSRenderer::getBgName(uint idx) const {
+ assert(idx < 9);
+ return _bgTable[idx].name;
+}
+
/*! \brief Fade to black
* \bug Operation Stealth sometimes seems to fade to black using
* transformPalette resulting in double fadeout
diff --git a/engines/cine/gfx.h b/engines/cine/gfx.h
index 6a3aa1ef89..c07214028c 100644
--- a/engines/cine/gfx.h
+++ b/engines/cine/gfx.h
@@ -62,13 +62,14 @@ protected:
byte *_backBuffer; ///< Screen backbuffer
uint16 *_activeLowPal; ///< Active 16 color palette
int _changePal; ///< Load active palette to video backend on next frame
+ bool _showCollisionPage; ///< Should we show the collision page instead of the back buffer? Used for debugging.
void fillSprite(const objectStruct &obj, uint8 color = 0);
void drawMaskedSprite(const objectStruct &obj, const byte *mask);
virtual void drawSprite(const objectStruct &obj);
void drawCommand();
- void drawMessage(const char *str, int x, int y, int width, byte color);
+ void drawMessage(const char *str, int x, int y, int width, int color);
void drawPlainBox(int x, int y, int width, int height, byte color);
void drawBorder(int x, int y, int width, int height, byte color);
void drawDoubleBorder(int x, int y, int width, int height, byte color);
@@ -94,7 +95,7 @@ public:
void drawFrame();
void blit();
- void setCommand(const char *cmd);
+ void setCommand(Common::String cmd);
virtual void incrustMask(const objectStruct &obj, uint8 color = 0);
virtual void incrustSprite(const objectStruct &obj);
@@ -111,6 +112,7 @@ public:
virtual uint getScroll() const;
virtual void removeBg(unsigned int idx);
virtual void saveBgNames(Common::OutSaveFile &fHandle);
+ virtual const char *getBgName(uint idx = 0) const;
virtual void refreshPalette();
virtual void reloadPalette();
@@ -123,6 +125,7 @@ public:
void drawInputBox(const char *info, const char *input, int cursor, int x, int y, int width);
virtual void fadeToBlack();
+ void showCollisionPage(bool state);
};
/*! \brief Operation Stealth renderer
@@ -168,6 +171,7 @@ public:
uint getScroll() const;
void removeBg(unsigned int idx);
void saveBgNames(Common::OutSaveFile &fHandle);
+ const char *getBgName(uint idx = 0) const;
void refreshPalette();
void reloadPalette();
@@ -181,7 +185,7 @@ public:
void gfxDrawSprite(byte *src4, uint16 sw, uint16 sh, byte *dst4, int16 sx, int16 sy);
-extern byte *page3Raw;
+extern byte *collisionPage;
extern FWRenderer *renderer;
void setMouseCursor(int cursor);
diff --git a/engines/cine/main_loop.cpp b/engines/cine/main_loop.cpp
index e5e670c973..e2402a4c8d 100644
--- a/engines/cine/main_loop.cpp
+++ b/engines/cine/main_loop.cpp
@@ -125,11 +125,74 @@ static void processEvent(Common::Event &event) {
g_cine->makeSystemMenu();
}
break;
+ case Common::KEYCODE_F11:
+ renderer->showCollisionPage(true);
+ break;
+ case Common::KEYCODE_MINUS:
+ case Common::KEYCODE_KP_MINUS:
+ g_cine->modifyGameSpeed(-1); // Slower
+ break;
+ case Common::KEYCODE_PLUS:
+ case Common::KEYCODE_KP_PLUS:
+ g_cine->modifyGameSpeed(+1); // Faster
+ break;
+ case Common::KEYCODE_LEFT:
+ case Common::KEYCODE_KP4:
+ moveUsingKeyboard(-1, 0); // Left
+ break;
+ case Common::KEYCODE_RIGHT:
+ case Common::KEYCODE_KP6:
+ moveUsingKeyboard(+1, 0); // Right
+ break;
+ case Common::KEYCODE_UP:
+ case Common::KEYCODE_KP8:
+ moveUsingKeyboard(0, +1); // Up
+ break;
+ case Common::KEYCODE_DOWN:
+ case Common::KEYCODE_KP2:
+ moveUsingKeyboard(0, -1); // Down
+ break;
+ case Common::KEYCODE_KP9:
+ moveUsingKeyboard(+1, +1); // Up & Right
+ break;
+ case Common::KEYCODE_KP7:
+ moveUsingKeyboard(-1, +1); // Up & Left
+ break;
+ case Common::KEYCODE_KP1:
+ moveUsingKeyboard(-1, -1); // Down & Left
+ break;
+ case Common::KEYCODE_KP3:
+ moveUsingKeyboard(+1, -1); // Down & Right
+ break;
default:
lastKeyStroke = event.kbd.keycode;
break;
}
break;
+ case Common::EVENT_KEYUP:
+ switch (event.kbd.keycode) {
+ case Common::KEYCODE_F11:
+ renderer->showCollisionPage(false);
+ break;
+ case Common::KEYCODE_KP5: // Emulated left mouse button click
+ case Common::KEYCODE_LEFT: // Left
+ case Common::KEYCODE_KP4: // Left
+ case Common::KEYCODE_RIGHT: // Right
+ case Common::KEYCODE_KP6: // Right
+ case Common::KEYCODE_UP: // Up
+ case Common::KEYCODE_KP8: // Up
+ case Common::KEYCODE_DOWN: // Down
+ case Common::KEYCODE_KP2: // Down
+ case Common::KEYCODE_KP9: // Up & Right
+ case Common::KEYCODE_KP7: // Up & Left
+ case Common::KEYCODE_KP1: // Down & Left
+ case Common::KEYCODE_KP3: // Down & Right
+ // Stop ego movement made with keyboard when releasing a known key
+ moveUsingKeyboard(0, 0);
+ break;
+ default:
+ break;
+ }
default:
break;
}
@@ -138,7 +201,7 @@ static void processEvent(Common::Event &event) {
void manageEvents() {
Common::EventManager *eventMan = g_system->getEventManager();
- uint32 nextFrame = g_system->getMillis() + kGameTimerDelay * kGameSpeed;
+ uint32 nextFrame = g_system->getMillis() + g_cine->getTimerDelay();
do {
Common::Event event;
while (eventMan->pollEvent(event)) {
@@ -227,7 +290,7 @@ void CineEngine::mainLoop(int bootScriptIdx) {
menuCommandLen = 0;
playerCommand = -1;
- strcpy(commandBuffer, "");
+ commandBuffer = "";
globalVars[VAR_MOUSE_X_POS] = 0;
globalVars[VAR_MOUSE_Y_POS] = 0;
@@ -247,14 +310,44 @@ void CineEngine::mainLoop(int bootScriptIdx) {
}
do {
+ // HACK: Force amount of oxygen left to maximum during Operation Stealth's first arcade sequence.
+ // This makes it possible to pass the arcade sequence for now.
+ // FIXME: Remove the hack and make the first arcade sequence normally playable.
+ if (g_cine->getGameType() == Cine::GType_OS) {
+ Common::String bgName(renderer->getBgName());
+ // Check if the background is one of the three backgrounds
+ // that are only used during the first arcade sequence.
+ if (bgName == "28.PI1" || bgName == "29.PI1" || bgName == "30.PI1") {
+ static const uint oxygenObjNum = 202, maxOxygen = 264;
+ // Force the amount of oxygen left to the maximum.
+ objectTable[oxygenObjNum].x = maxOxygen;
+ }
+ }
+
+ // HACK: In Operation Stealth after the first arcade sequence jump player's position to avoid getting stuck.
+ // After the first arcade sequence the player comes up stairs from
+ // the water in Santa Paragua's downtown in front of the flower shop.
+ // Previously he was completely stuck after getting up the stairs.
+ // If the background is the one used in the flower shop scene ("21.PI1")
+ // and the player is at the exact location after getting up the stairs
+ // then we just nudge him a tiny bit away from the stairs and voila, he's free!
+ // Maybe the real problem behind all this is collision data related as it looks
+ // like there's some boundary right there near position (204, 110) which we can
+ // jump over by moving the character to (204, 109). The script handling the
+ // flower shop scene is AIRPORT.PRC's 13th script.
+ // FIXME: Remove the hack and solve what's really causing the problem in the first place.
+ if (g_cine->getGameType() == Cine::GType_OS) {
+ if (scumm_stricmp(renderer->getBgName(), "21.PI1") == 0 && objectTable[1].x == 204 && objectTable[1].y == 110) {
+ objectTable[1].y--; // Move the player character upward on-screen by one pixel
+ }
+ }
+
stopMusicAfterFadeOut();
di = executePlayerInput();
// Clear the zoneQuery table (Operation Stealth specific)
if (g_cine->getGameType() == Cine::GType_OS) {
- for (uint i = 0; i < NUM_MAX_ZONE; i++) {
- zoneQuery[i] = 0;
- }
+ Common::set_to(zoneQuery.begin(), zoneQuery.end(), 0);
}
if (g_cine->getGameType() == Cine::GType_OS) {
@@ -279,6 +372,11 @@ void CineEngine::mainLoop(int bootScriptIdx) {
renderer->drawFrame();
}
+ // NOTE: In the original Future Wars and Operation Stealth messages
+ // were removed when running the drawOverlays function which is
+ // currently called from the renderer's drawFrame function.
+ removeMessages();
+
if (waitForPlayerClick) {
playerAction = false;
@@ -308,8 +406,6 @@ void CineEngine::mainLoop(int bootScriptIdx) {
} while (mouseButton != 0);
waitForPlayerClick = 0;
-
- removeMessages();
}
if (checkForPendingDataLoadSwitch) {
diff --git a/engines/cine/main_loop.h b/engines/cine/main_loop.h
index a2f828fd34..c729b324ca 100644
--- a/engines/cine/main_loop.h
+++ b/engines/cine/main_loop.h
@@ -28,11 +28,6 @@
namespace Cine {
-enum {
- kGameTimerDelay = 1000 / (1193180 / 10923),
- kGameSpeed = 12
-};
-
void mainLoop(int bootScriptIdx);
void manageEvents();
diff --git a/engines/cine/msg.cpp b/engines/cine/msg.cpp
index 55eb627309..c826db3bf3 100644
--- a/engines/cine/msg.cpp
+++ b/engines/cine/msg.cpp
@@ -34,29 +34,40 @@ namespace Cine {
Common::StringList messageTable;
void loadMsg(char *pMsgName) {
- int i, count, len;
- byte *ptr, *dataPtr;
- const char *messagePtr;
+ uint32 sourceSize;
checkDataDisk(-1);
-
messageTable.clear();
-
- ptr = dataPtr = readBundleFile(findFileInBundle(pMsgName));
+ byte *dataPtr = readBundleFile(findFileInBundle(pMsgName), &sourceSize);
setMouseCursor(MOUSE_CURSOR_DISK);
- count = READ_BE_UINT16(ptr);
- ptr += 2;
-
- messagePtr = (const char*)(ptr + 2 * count);
-
- for (i = 0; i < count; i++) {
- len = READ_BE_UINT16(ptr);
- ptr += 2;
-
- messageTable.push_back(messagePtr);
- messagePtr += len;
+ uint count = READ_BE_UINT16(dataPtr);
+ uint messageLenPos = 2;
+ uint messageDataPos = messageLenPos + 2 * count;
+
+ // Read in the messages
+ for (uint i = 0; i < count; i++) {
+ // Read message's length
+ uint messageLen = READ_BE_UINT16(dataPtr + messageLenPos);
+ messageLenPos += 2;
+
+ // Store the read message.
+ // This code works around input data that has empty strings residing outside the input
+ // buffer (e.g. message indexes 58-254 in BATEAU.MSG in PROCS08 in Operation Stealth).
+ if (messageDataPos < sourceSize) {
+ messageTable.push_back((const char *)(dataPtr + messageDataPos));
+ } else {
+ if (messageLen > 0) { // Only warn about overflowing non-empty strings
+ warning("loadMsg(%s): message (%d. / %d) is overflowing the input buffer. Replacing it with an empty string", pMsgName, i + 1, count);
+ } else {
+ debugC(5, kCineDebugPart, "loadMsg(%s): empty message (%d. / %d) resides outside input buffer", pMsgName, i + 1, count);
+ }
+ // Message resides outside the input buffer so we replace it with an empty string
+ messageTable.push_back("");
+ }
+ // Jump to the next message
+ messageDataPos += messageLen;
}
free(dataPtr);
diff --git a/engines/cine/object.cpp b/engines/cine/object.cpp
index c02e01c8ce..9781975f7c 100644
--- a/engines/cine/object.cpp
+++ b/engines/cine/object.cpp
@@ -35,9 +35,16 @@
namespace Cine {
-objectStruct objectTable[NUM_MAX_OBJECT];
+Common::Array<objectStruct> objectTable;
Common::List<overlay> overlayList;
+/*! \brief Resets all elements in the object table. */
+void resetObjectTable() {
+ for (Common::Array<objectStruct>::iterator it = objectTable.begin(); it != objectTable.end(); it++) {
+ it->clear();
+ }
+}
+
void loadObject(char *pObjectName) {
uint16 numEntry;
uint16 entrySize;
diff --git a/engines/cine/object.h b/engines/cine/object.h
index 7ad65eb75f..3bf6cdcc42 100644
--- a/engines/cine/object.h
+++ b/engines/cine/object.h
@@ -38,6 +38,17 @@ struct objectStruct {
int16 costume;
char name[20];
uint16 part;
+
+ /*! \brief Sets all member variables to zero. */
+ void clear() {
+ this->x = 0;
+ this->y = 0;
+ this->mask = 0;
+ this->frame = 0;
+ this->costume = 0;
+ memset(this->name, 0, sizeof(this->name));
+ this->part = 0;
+ }
};
struct overlay {
@@ -52,10 +63,10 @@ struct overlay {
#define NUM_MAX_OBJECT 255
#define NUM_MAX_VAR 255
-extern objectStruct objectTable[NUM_MAX_OBJECT];
-
+extern Common::Array<objectStruct> objectTable;
extern Common::List<overlay> overlayList;
+void resetObjectTable();
void loadObject(char *pObjectName);
void setupObject(byte objIdx, uint16 param1, uint16 param2, uint16 param3, uint16 param4);
void modifyObjectParam(byte objIdx, byte paramIdx, int16 newValue);
diff --git a/engines/cine/pal.cpp b/engines/cine/pal.cpp
index 3e6f5adf40..7f6307c640 100644
--- a/engines/cine/pal.cpp
+++ b/engines/cine/pal.cpp
@@ -28,10 +28,7 @@
namespace Cine {
-uint16 palEntriesCount;
-
-PalEntry *palPtr = NULL;
-
+Common::Array<PalEntry> palArray;
static byte paletteBuffer1[16];
static byte paletteBuffer2[16];
@@ -41,27 +38,20 @@ void loadPal(const char *fileName) {
removeExtention(buffer, fileName);
strcat(buffer, ".PAL");
-
- if (palPtr) {
- free(palPtr);
- palPtr = NULL;
- }
-
- palEntriesCount = 0;
+ palArray.clear();
Common::File palFileHandle;
if (!palFileHandle.open(buffer))
error("loadPal(): Cannot open file %s", fileName);
- palEntriesCount = palFileHandle.readUint16LE();
+ uint16 palEntriesCount = palFileHandle.readUint16LE();
palFileHandle.readUint16LE(); // entry size
- palPtr = (PalEntry *)malloc(palEntriesCount * sizeof(PalEntry));
- assert(palPtr);
- for (int i = 0; i < palEntriesCount; ++i) {
- palFileHandle.read(palPtr[i].name, 10);
- palFileHandle.read(palPtr[i].pal1, 16);
- palFileHandle.read(palPtr[i].pal2, 16);
+ palArray.resize(palEntriesCount);
+ for (uint i = 0; i < palArray.size(); ++i) {
+ palFileHandle.read(palArray[i].name, 10);
+ palFileHandle.read(palArray[i].pal1, 16);
+ palFileHandle.read(palArray[i].pal2, 16);
}
palFileHandle.close();
}
@@ -81,8 +71,8 @@ int16 findPaletteFromName(const char *fileName) {
position++;
}
- for (i = 0; i < palEntriesCount; i++) {
- if (!strcmp(buffer, palPtr[i].name)) {
+ for (i = 0; i < palArray.size(); i++) {
+ if (!strcmp(buffer, palArray[i].name)) {
return i;
}
}
@@ -105,9 +95,9 @@ void loadRelatedPalette(const char *fileName) {
paletteBuffer1[i] = paletteBuffer2[i] = (i << 4) + i;
}
} else {
- assert(paletteIndex < palEntriesCount);
- memcpy(paletteBuffer1, palPtr[paletteIndex].pal1, 16);
- memcpy(paletteBuffer2, palPtr[paletteIndex].pal2, 16);
+ assert(paletteIndex < (int32)palArray.size());
+ memcpy(paletteBuffer1, palArray[paletteIndex].pal1, 16);
+ memcpy(paletteBuffer2, palArray[paletteIndex].pal2, 16);
}
}
diff --git a/engines/cine/pal.h b/engines/cine/pal.h
index 768cf0d27d..819680973c 100644
--- a/engines/cine/pal.h
+++ b/engines/cine/pal.h
@@ -34,7 +34,7 @@ struct PalEntry {
byte pal2[16];
};
-extern PalEntry *palPtr;
+extern Common::Array<PalEntry> palArray;
void loadPal(const char *fileName);
diff --git a/engines/cine/part.cpp b/engines/cine/part.cpp
index 88f2dcef52..657471be4e 100644
--- a/engines/cine/part.cpp
+++ b/engines/cine/part.cpp
@@ -31,13 +31,10 @@
namespace Cine {
-uint16 numElementInPart;
-
-PartBuffer *partBuffer;
+Common::Array<PartBuffer> partBuffer;
void loadPart(const char *partName) {
- memset(partBuffer, 0, sizeof(PartBuffer) * NUM_MAX_PARTDATA);
- numElementInPart = 0;
+ partBuffer.clear();
g_cine->_partFileHandle.close();
@@ -48,13 +45,14 @@ void loadPart(const char *partName) {
setMouseCursor(MOUSE_CURSOR_DISK);
- numElementInPart = g_cine->_partFileHandle.readUint16BE();
+ uint16 numElementInPart = g_cine->_partFileHandle.readUint16BE();
+ partBuffer.resize(numElementInPart);
g_cine->_partFileHandle.readUint16BE(); // entry size
if (currentPartName != partName)
strcpy(currentPartName, partName);
- for (uint16 i = 0; i < numElementInPart; i++) {
+ for (uint16 i = 0; i < partBuffer.size(); i++) {
g_cine->_partFileHandle.read(partBuffer[i].partName, 14);
partBuffer[i].offset = g_cine->_partFileHandle.readUint32BE();
partBuffer[i].packedSize = g_cine->_partFileHandle.readUint32BE();
@@ -190,7 +188,7 @@ void CineEngine::readVolCnf() {
int16 findFileInBundle(const char *fileName) {
if (g_cine->getGameType() == Cine::GType_OS) {
// look first in currently loaded resource file
- for (int i = 0; i < numElementInPart; i++) {
+ for (uint i = 0; i < partBuffer.size(); i++) {
if (!scumm_stricmp(fileName, partBuffer[i].partName)) {
return i;
}
@@ -204,7 +202,7 @@ int16 findFileInBundle(const char *fileName) {
const char *part = (*it)._value;
loadPart(part);
}
- for (int i = 0; i < numElementInPart; i++) {
+ for (uint i = 0; i < partBuffer.size(); i++) {
if (!scumm_stricmp(fileName, partBuffer[i].partName)) {
return i;
}
@@ -212,26 +210,35 @@ int16 findFileInBundle(const char *fileName) {
return -1;
}
-void readFromPart(int16 idx, byte *dataPtr) {
+void readFromPart(int16 idx, byte *dataPtr, uint32 maxSize) {
setMouseCursor(MOUSE_CURSOR_DISK);
g_cine->_partFileHandle.seek(partBuffer[idx].offset, SEEK_SET);
- g_cine->_partFileHandle.read(dataPtr, partBuffer[idx].packedSize);
+ g_cine->_partFileHandle.read(dataPtr, MIN(partBuffer[idx].packedSize, maxSize));
}
-byte *readBundleFile(int16 foundFileIdx) {
- assert(foundFileIdx >= 0 && foundFileIdx < numElementInPart);
+byte *readBundleFile(int16 foundFileIdx, uint32 *size) {
+ assert(foundFileIdx >= 0 && foundFileIdx < (int32)partBuffer.size());
+ bool error = false;
byte *dataPtr = (byte *)calloc(partBuffer[foundFileIdx].unpackedSize, 1);
- if (partBuffer[foundFileIdx].unpackedSize != partBuffer[foundFileIdx].packedSize) {
- byte *unpackBuffer = (byte *)malloc(partBuffer[foundFileIdx].packedSize);
- readFromPart(foundFileIdx, unpackBuffer);
+ readFromPart(foundFileIdx, dataPtr, partBuffer[foundFileIdx].unpackedSize);
+ if (partBuffer[foundFileIdx].unpackedSize > partBuffer[foundFileIdx].packedSize) {
CineUnpacker cineUnpacker;
- if (!cineUnpacker.unpack(unpackBuffer, partBuffer[foundFileIdx].packedSize, dataPtr, partBuffer[foundFileIdx].unpackedSize)) {
- warning("Error unpacking '%s' from bundle file '%s'", partBuffer[foundFileIdx].partName, currentPartName);
- }
- free(unpackBuffer);
- } else {
- readFromPart(foundFileIdx, dataPtr);
+ error = !cineUnpacker.unpack(dataPtr, partBuffer[foundFileIdx].packedSize, dataPtr, partBuffer[foundFileIdx].unpackedSize);
+ } else if (partBuffer[foundFileIdx].unpackedSize < partBuffer[foundFileIdx].packedSize) {
+ // Unpacked size of a file should never be less than its packed size
+ error = true;
+ } else { // partBuffer[foundFileIdx].unpackedSize == partBuffer[foundFileIdx].packedSize
+ debugC(5, kCineDebugPart, "Loaded non-compressed file '%s' from bundle file '%s'", partBuffer[foundFileIdx].partName, currentPartName);
+ }
+
+ if (error) {
+ warning("Error unpacking '%s' from bundle file '%s'", partBuffer[foundFileIdx].partName, currentPartName);
+ }
+
+ // Set the size variable if a pointer to it has been given
+ if (size != NULL) {
+ *size = partBuffer[foundFileIdx].unpackedSize;
}
return dataPtr;
@@ -259,7 +266,13 @@ byte *readBundleSoundFile(const char *entryName, uint32 *size) {
return data;
}
-byte *readFile(const char *filename) {
+/*! \brief Rotate byte value to the left by n bits */
+byte rolByte(byte value, uint n) {
+ n %= 8;
+ return (byte) ((value << n) | (value >> (8 - n)));
+}
+
+byte *readFile(const char *filename, bool crypted) {
Common::File in;
in.open(filename);
@@ -272,6 +285,16 @@ byte *readFile(const char *filename) {
byte *dataPtr = (byte *)malloc(size);
in.read(dataPtr, size);
+ // The Sony published CD version of Future Wars has its
+ // AUTO00.PRC file's bytes rotated to the right by one.
+ // So we decode the so called crypting by rotating all
+ // the bytes to the left by one.
+ if (crypted) {
+ for (uint index = 0; index < size; index++) {
+ dataPtr[index] = rolByte(dataPtr[index], 1);
+ }
+ }
+
return dataPtr;
}
@@ -284,7 +307,7 @@ void dumpBundle(const char *fileName) {
strcpy(tmpPart, currentPartName);
loadPart(fileName);
- for (int i = 0; i < numElementInPart; i++) {
+ for (uint i = 0; i < partBuffer.size(); i++) {
byte *data = readBundleFile(i);
debug(0, "%s", partBuffer[i].partName);
diff --git a/engines/cine/part.h b/engines/cine/part.h
index 2a979e4879..755f843b4a 100644
--- a/engines/cine/part.h
+++ b/engines/cine/part.h
@@ -37,18 +37,18 @@ struct PartBuffer {
#define NUM_MAX_PARTDATA 255
-extern PartBuffer *partBuffer;
+extern Common::Array<PartBuffer> partBuffer;
void loadPart(const char *partName);
void closePart(void);
int16 findFileInBundle(const char *fileName);
-void readFromPart(int16 idx, byte *dataPtr);
+void readFromPart(int16 idx, byte *dataPtr, uint32 maxSize);
-byte *readBundleFile(int16 foundFileIdx);
+byte *readBundleFile(int16 foundFileIdx, uint32 *size = NULL);
byte *readBundleSoundFile(const char *entryName, uint32 *size = 0);
-byte *readFile(const char *filename);
+byte *readFile(const char *filename, bool crypted = false);
void checkDataDisk(int16 param);
diff --git a/engines/cine/prc.cpp b/engines/cine/prc.cpp
index 27b1044620..5d789f9b3b 100644
--- a/engines/cine/prc.cpp
+++ b/engines/cine/prc.cpp
@@ -61,7 +61,7 @@ bool loadPrc(const char *pPrcName) {
checkDataDisk(-1);
if ((g_cine->getGameType() == Cine::GType_FW) &&
(!scumm_stricmp(pPrcName, BOOT_PRC_NAME) || !scumm_stricmp(pPrcName, "demo.prc"))) {
- scriptPtr = dataPtr = readFile(pPrcName);
+ scriptPtr = dataPtr = readFile(pPrcName, (g_cine->getFeatures() & GF_CRYPTED_BOOT_PRC) != 0);
} else {
scriptPtr = dataPtr = readBundleFile(findFileInBundle(pPrcName));
}
diff --git a/engines/cine/script_fw.cpp b/engines/cine/script_fw.cpp
index e761a0c8e4..97f45488f2 100644
--- a/engines/cine/script_fw.cpp
+++ b/engines/cine/script_fw.cpp
@@ -1319,6 +1319,7 @@ int FWScript::o1_loadBg() {
return 0;
}
+/*! \brief Load collision table data */
int FWScript::o1_loadCt() {
const char *param = getNextString();
@@ -1789,7 +1790,14 @@ int16 checkCollision(int16 objIdx, int16 x, int16 y, int16 numZones, int16 zoneI
int16 result = 0;
for (int16 i = 0; i < numZones; i++) {
- idx = getZoneFromPositionRaw(page3Raw, lx + i, ly, 320);
+ // Don't try to read data in Operation Stealth if position isn't in 320x200 screen bounds.
+ if (g_cine->getGameType() == Cine::GType_OS) {
+ if ((lx + i) < 0 || (lx + i) > 319 || ly < 0 || ly > 199) {
+ continue;
+ }
+ }
+
+ idx = getZoneFromPositionRaw(collisionPage, lx + i, ly, 320);
assert(idx >= 0 && idx < NUM_MAX_ZONE);
diff --git a/engines/cine/script_os.cpp b/engines/cine/script_os.cpp
index a764281758..e8f16ebfcc 100644
--- a/engines/cine/script_os.cpp
+++ b/engines/cine/script_os.cpp
@@ -365,6 +365,7 @@ FWScript *OSScriptInfo::create(const RawObjectScript &script, int16 index, const
// OPERATION STEALTH opcodes
// ------------------------------------------------------------------------
+/*! \brief Load collision table data */
int FWScript::o2_loadCt() {
const char *param = getNextString();
@@ -636,7 +637,28 @@ int FWScript::o2_loadAbs() {
const char *param2 = getNextString();
debugC(5, kCineDebugScript, "Line: %d: loadABS(%d,%s)", _line, param1, param2);
- loadAbs(param2, param1);
+ // Load the resource to an absolute position
+ if (loadResource(param2, param1) == -1) { // Check if the loading failed
+ // WORKAROUND: In the 256 color PC version of Operation Stealth when
+ // walking out of the airport in Santa Paragua to the street the
+ // player character should be seen as a grey silhuette while walking
+ // behind the glass. But actually the player character is completely
+ // invisible when walking behind the glass because the animation files
+ // used are wrongly loaded. In AIRPORT.PRC's 6th script there are
+ // calls loadAbs("JOHN01.ANI", 73) and loadAbs("JOHN02.ANI", 37) to
+ // load the animations involved but no such files are found with the
+ // game. Corresponding SET-files are found though. As it worked and
+ // looked fine when I tried loading them instead of the missing ANI
+ // files I'm doing so here. NOTE: At least the German Amiga version
+ // of Operation Stealth seems to have all the files involved
+ // (JOHN01.ANI, JOHN02.ANI, JOHN01.SET and JOHN02.SET).
+ if (scumm_stricmp(param2, "JOHN01.ANI") == 0 && param1 == 73) {
+ loadResource("JOHN01.SET", param1);
+ } else if (scumm_stricmp(param2, "JOHN02.ANI") == 0 && param1 == 37) {
+ loadResource("JOHN02.SET", param1);
+ }
+ }
+
return 0;
}
diff --git a/engines/cine/sound.cpp b/engines/cine/sound.cpp
index f26032fe98..3618350476 100644
--- a/engines/cine/sound.cpp
+++ b/engines/cine/sound.cpp
@@ -604,6 +604,7 @@ bool PCSoundFxPlayer::load(const char *song) {
_instrumentsData[i] = NULL;
char instrument[64];
+ memset(instrument, 0, 64); // Clear the data first
memcpy(instrument, _sfxData + 20 + i * 30, 12);
instrument[63] = '\0';
diff --git a/engines/cine/texte.cpp b/engines/cine/texte.cpp
index e4fd334926..33c16159ec 100644
--- a/engines/cine/texte.cpp
+++ b/engines/cine/texte.cpp
@@ -29,70 +29,53 @@
namespace Cine {
-byte *textDataPtr;
-
const char **failureMessages;
const CommandeType *defaultActionCommand;
const CommandeType *systemMenu;
const CommandeType *confirmMenu;
const char **otherMessages;
-const char *commandPrepositionOn;
+const char *defaultCommandPreposition;
+const char **commandPrepositionTable;
void generateMask(const byte *sprite, byte *mask, uint16 size, byte transparency);
-void loadTextData(const char *pFileName, byte *pDestinationBuffer) {
- Common::File pFileHandle;
- uint16 entrySize;
- uint16 numEntry;
- uint16 i;
- byte *tempBuffer;
- uint16 dataSize;
-
- assert(pFileName);
- assert(pDestinationBuffer);
+void loadTextData(const char *filename) {
+ Common::File fileHandle;
+ assert(filename);
- if (!pFileHandle.open(pFileName))
- error("loadTextData(): Cannot open file %s", pFileName);
+ if (!fileHandle.open(filename))
+ error("loadTextData(): Cannot open file %s", filename);
- entrySize = pFileHandle.readUint16BE();
- numEntry = pFileHandle.readUint16BE();
+ uint entrySize = fileHandle.readUint16BE();
+ uint numEntry = fileHandle.readUint16BE();
- dataSize = numEntry * entrySize;
- pFileHandle.read(pDestinationBuffer, numEntry * entrySize);
-
- tempBuffer = pDestinationBuffer;
+ uint sourceSize = numEntry * entrySize;
+ Common::Array<byte> source;
+ source.resize(sourceSize);
+ fileHandle.read(source.begin(), sourceSize);
+ const int fontHeight = 8;
+ const int fontWidth = (g_cine->getGameType() == Cine::GType_FW) ? 16 : 8;
+ uint numCharacters;
+ uint bytesPerCharacter;
if (g_cine->getGameType() == Cine::GType_FW) {
- int numCharacters;
- if (g_cine->getFeatures() & GF_ALT_FONT) {
- numCharacters = 85;
- } else {
- numCharacters = 78;
- }
-
- dataSize = dataSize / numCharacters;
-
- loadRelatedPalette(pFileName);
-
- for (i = 0; i < numCharacters; i++) {
- gfxConvertSpriteToRaw(g_cine->_textHandler.textTable[i][0], tempBuffer, 16, 8);
- generateMask(g_cine->_textHandler.textTable[i][0], g_cine->_textHandler.textTable[i][1], 16 * 8, 0);
- tempBuffer += dataSize;
- }
+ numCharacters = (g_cine->getFeatures() & GF_ALT_FONT) ? 85 : 78;
+ bytesPerCharacter = sourceSize / numCharacters; // TODO: Check if this could be replaced with fontWidth * fontHeight
+ loadRelatedPalette(filename);
} else {
- for (i = 0; i < 90; i++) {
- gfxConvertSpriteToRaw(g_cine->_textHandler.textTable[i][0], tempBuffer, 8, 8);
- generateMask(g_cine->_textHandler.textTable[i][0], g_cine->_textHandler.textTable[i][1], 8 * 8, 0);
- tempBuffer += 0x40;
- }
+ numCharacters = 90;
+ bytesPerCharacter = fontWidth * fontHeight;
}
- pFileHandle.close();
-}
+ for (uint i = 0; i < numCharacters; i++) {
+ gfxConvertSpriteToRaw(g_cine->_textHandler.textTable[i][0], &source[i * bytesPerCharacter], fontWidth, fontHeight);
+ generateMask(g_cine->_textHandler.textTable[i][0], g_cine->_textHandler.textTable[i][1], fontWidth * fontHeight, 0);
+ }
-const CharacterEntry *fontParamTable;
+ fileHandle.close();
+}
-const CharacterEntry fontParamTable_standard[256] = {
+static const CharacterEntry fontParamTable_standard[NUM_FONT_CHARS] = {
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
@@ -129,7 +112,7 @@ const CharacterEntry fontParamTable_standard[256] = {
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}
};
-const CharacterEntry fontParamTable_alt[256] = {
+static const CharacterEntry fontParamTable_alt[NUM_FONT_CHARS] = {
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
@@ -208,6 +191,16 @@ void initLanguage(Common::Language lang) {
"NOACTION"
};
+ static const char *commandPrepositionTable_EN[] = {
+ "", // EXAMINE
+ "", // TAKE
+ "", // INVENTORY
+ "on", // USE
+ "", // OPERATE
+ "to", // SPEAK
+ "" // NOACTION
+ };
+
static const CommandeType systemMenu_EN[] = {
"Pause",
"Restart Game",
@@ -223,9 +216,8 @@ void initLanguage(Common::Language lang) {
"PAUSE",
"Loading | %s",
"Loading canceled ...",
- "No baclup in the drive...",
- "Please enter the backup name",
- "on"
+ "No backup in the drive...",
+ "Please enter the backup name"
};
static const CommandeType confirmMenu_EN[] = {
@@ -276,6 +268,16 @@ void initLanguage(Common::Language lang) {
"NOACTION"
};
+ static const char *commandPrepositionTable_FR[] = {
+ "", // EXAMINER
+ "", // PRENDRE
+ "", // INVENTAIRE
+ "sur", // UTILISER
+ "", // ACTIONNER
+ "a", // PARLER
+ "" // NOACTION
+ };
+
static const CommandeType systemMenu_FR[] = {
"Pause",
"Nouvelle partie",
@@ -297,8 +299,7 @@ void initLanguage(Common::Language lang) {
"Sauvegarde de | %s",
"Sauvegarde Annul\x82""e ...",
"Aucune sauvegarde dans le lecteur ...",
- "Veuillez entrer le Nom de la Sauvegarde .",
- "sur"
+ "Veuillez entrer le Nom de la Sauvegarde ."
};
static const char *failureMessages_ES[] = {
@@ -344,6 +345,16 @@ void initLanguage(Common::Language lang) {
"NOACTION"
};
+ static const char *commandPrepositionTable_ES[] = {
+ "", // EXAMINAR
+ "", // COGER
+ "", // INVENTARIO
+ "donde", // USAR
+ "", // ACCIONAR
+ "a", // HABLAR
+ "" // NOACTION
+ };
+
static const CommandeType systemMenu_ES[] = {
"Pause",
"Nueva partida",
@@ -365,8 +376,7 @@ void initLanguage(Common::Language lang) {
"Gabacion de| %s",
"Rrabacion anulada",
"No hay partidas grabadas en este disco...",
- "Teclea el nombre de la partida grabada",
- "donde"
+ "Teclea el nombre de la partida grabada"
};
static const char *failureMessages_DE[] = {
@@ -403,15 +413,25 @@ void initLanguage(Common::Language lang) {
};
static const CommandeType defaultActionCommand_DE[] = {
- "Pr\x81""fe",
+ "Pr\x81""fe", // FIXME? The third letter should be Latin Small Letter U with diaeresis
"Nimm",
"Bestand",
"Benutze",
- "Bet\x84tige",
+ "Bet\x84tige", // FIXME? The fourth letter should be Latin Small Letter A with diaeresis
"Sprich",
"NOACTION"
};
+ static const char *commandPrepositionTable_DE[] = {
+ "", // Prufe
+ "", // Nimm
+ "", // Bestand
+ "gegen", // Benutze
+ "", // Betatige
+ "a", // Sprich
+ "" // NOACTION
+ };
+
static const CommandeType systemMenu_DE[] = {
"Pause",
"Spiel Neu Starten",
@@ -433,8 +453,7 @@ void initLanguage(Common::Language lang) {
"Er L\x84""dt | %s",
"Ladevorgang Abgebrochen...",
"Kein Backup im Laufwerk...",
- "Geben Sie den Namen|der Sicherungsdiskette ein",
- "gegen"
+ "Geben Sie den Namen|der Sicherungsdiskette ein"
};
static const char *failureMessages_IT[] = {
@@ -480,6 +499,16 @@ void initLanguage(Common::Language lang) {
"NOACTION"
};
+ static const char *commandPrepositionTable_IT[] = {
+ "", // ESAMINARE
+ "", // PRENDERE
+ "", // INVENTARIO
+ "su", // UTILIZZARE
+ "", // AZIONARE
+ "a", // PARLARE
+ "" // NOACTION
+ };
+
static const CommandeType systemMenu_IT[] = {
"Pausa",
"Parte nuova",
@@ -501,8 +530,7 @@ void initLanguage(Common::Language lang) {
"Caricamento di| %s",
"Caricamento annullato...",
"Nessun salvataggio su questo disco...",
- "Vogliate accedere con il nome del salvataggio",
- "su"
+ "Vogliate accedere con il nome del salvataggio"
};
switch (lang) {
@@ -512,7 +540,8 @@ void initLanguage(Common::Language lang) {
systemMenu = systemMenu_FR;
confirmMenu = confirmMenu_FR;
otherMessages = otherMessages_FR;
- commandPrepositionOn = otherMessages_FR[7];
+ defaultCommandPreposition = commandPrepositionTable_FR[3];
+ commandPrepositionTable = commandPrepositionTable_FR;
break;
case Common::ES_ESP:
@@ -521,7 +550,8 @@ void initLanguage(Common::Language lang) {
systemMenu = systemMenu_ES;
confirmMenu = confirmMenu_ES;
otherMessages = otherMessages_ES;
- commandPrepositionOn = otherMessages_ES[7];
+ defaultCommandPreposition = commandPrepositionTable_ES[3];
+ commandPrepositionTable = commandPrepositionTable_ES;
break;
case Common::DE_DEU:
@@ -530,7 +560,8 @@ void initLanguage(Common::Language lang) {
systemMenu = systemMenu_DE;
confirmMenu = confirmMenu_DE;
otherMessages = otherMessages_DE;
- commandPrepositionOn = otherMessages_DE[7];
+ defaultCommandPreposition = commandPrepositionTable_DE[3];
+ commandPrepositionTable = commandPrepositionTable_DE;
break;
case Common::IT_ITA:
@@ -539,7 +570,8 @@ void initLanguage(Common::Language lang) {
systemMenu = systemMenu_IT;
confirmMenu = confirmMenu_IT;
otherMessages = otherMessages_IT;
- commandPrepositionOn = otherMessages_IT[7];
+ defaultCommandPreposition = commandPrepositionTable_IT[3];
+ commandPrepositionTable = commandPrepositionTable_IT;
break;
default:
@@ -548,14 +580,17 @@ void initLanguage(Common::Language lang) {
systemMenu = systemMenu_EN;
confirmMenu = confirmMenu_EN;
otherMessages = otherMessages_EN;
- commandPrepositionOn = otherMessages_EN[7];
+ defaultCommandPreposition = commandPrepositionTable_EN[3];
+ commandPrepositionTable = commandPrepositionTable_EN;
break;
}
if (g_cine->getFeatures() & GF_ALT_FONT) {
- fontParamTable = fontParamTable_alt;
+ // Copy alternative font parameter table to the current font parameter table
+ Common::copy(fontParamTable_alt, fontParamTable_alt + NUM_FONT_CHARS, g_cine->_textHandler.fontParamTable);
} else {
- fontParamTable = fontParamTable_standard;
+ // Copy standard font parameter to the current font parameter table
+ Common::copy(fontParamTable_standard, fontParamTable_standard + NUM_FONT_CHARS, g_cine->_textHandler.fontParamTable);
}
}
@@ -590,25 +625,16 @@ void loadPoldatDat(const char *fname) {
in.open(fname);
if (in.isOpen()) {
- CharacterEntry *ptr = (CharacterEntry *)malloc(sizeof(CharacterEntry) * 256);
-
- for (int i = 0; i < 256; i++) {
- ptr[i].characterIdx = (int)in.readByte();
- ptr[i].characterWidth = (int)in.readByte();
+ for (int i = 0; i < NUM_FONT_CHARS; i++) {
+ g_cine->_textHandler.fontParamTable[i].characterIdx = in.readByte();
+ g_cine->_textHandler.fontParamTable[i].characterWidth = in.readByte();
}
- fontParamTable = ptr;
-
in.close();
} else {
error("Cannot open file %s for reading", fname);
}
}
-void freePoldatDat() {
- free(const_cast<Cine::CharacterEntry *>(fontParamTable));
- fontParamTable = 0;
-}
-
/*! \brief Fit a substring of text into one line of fixed width text box
* \param str Text to fit
* \param maxWidth Text box width
@@ -633,7 +659,7 @@ int fitLine(const char *str, int maxWidth, int &words, int &width) {
bkpWidth = width;
bkpLen = i + 1;
} else {
- charWidth = fontParamTable[(unsigned char)str[i]].characterWidth + 1;
+ charWidth = g_cine->_textHandler.fontParamTable[(unsigned char)str[i]].characterWidth + 1;
width += charWidth;
}
diff --git a/engines/cine/texte.h b/engines/cine/texte.h
index f471c3c49e..bc4beac492 100644
--- a/engines/cine/texte.h
+++ b/engines/cine/texte.h
@@ -33,10 +33,17 @@ namespace Cine {
typedef char CommandeType[20];
-extern byte *textDataPtr;
+// Number of characters in a font
+#define NUM_FONT_CHARS 256
+
+struct CharacterEntry {
+ byte characterIdx;
+ byte characterWidth;
+};
struct TextHandler {
- byte textTable[256][2][16 * 8];
+ byte textTable[NUM_FONT_CHARS][2][16 * 8];
+ CharacterEntry fontParamTable[NUM_FONT_CHARS];
};
extern const char **failureMessages;
@@ -44,20 +51,13 @@ extern const CommandeType *defaultActionCommand;
extern const CommandeType *systemMenu;
extern const CommandeType *confirmMenu;
extern const char **otherMessages;
-extern const char *commandPrepositionOn;
-
-struct CharacterEntry {
- byte characterIdx;
- byte characterWidth;
-};
-
-extern const CharacterEntry *fontParamTable;
+extern const char *defaultCommandPreposition;
+extern const char **commandPrepositionTable;
-void loadTextData(const char *pFileName, byte *pDestinationBuffer);
+void loadTextData(const char *filename);
void loadErrmessDat(const char *fname);
void freeErrmessDat(void);
void loadPoldatDat(const char *fname);
-void freePoldatDat(void);
int fitLine(const char *ptr, int maxWidth, int &words, int &width);
diff --git a/engines/cine/various.cpp b/engines/cine/various.cpp
index 2fcb015fcd..e96e03b03c 100644
--- a/engines/cine/various.cpp
+++ b/engines/cine/various.cpp
@@ -78,7 +78,7 @@ byte _danKeysPressed;
int16 playerCommand;
-char commandBuffer[80];
+Common::String commandBuffer;
char currentPrcName[20];
char currentRelName[20];
char currentObjectName[20];
@@ -95,11 +95,23 @@ int16 saveVar2;
byte isInPause = 0;
-// TODO: Implement inputVar0's changes in the program
-// Currently inputVar0 isn't updated anywhere even though it's used at least in processSeqListElement.
-uint16 inputVar0 = 0;
-byte inputVar1 = 0;
-uint16 inputVar2 = 0, inputVar3 = 0;
+/*! \brief Values used by the xMoveKeyb variable */
+enum xMoveKeybEnums {
+ kKeybMoveCenterX = 0,
+ kKeybMoveRight = 1,
+ kKeybMoveLeft = 2
+};
+
+/*! \brief Values used by the yMoveKeyb variable */
+enum yMoveKeybEnums {
+ kKeybMoveCenterY = 0,
+ kKeybMoveDown = 1,
+ kKeybMoveUp = 2
+};
+
+uint16 xMoveKeyb = kKeybMoveCenterX;
+bool egoMovedWithKeyboard = false;
+uint16 yMoveKeyb = kKeybMoveCenterY;
SelectedObjStruct currentSelectedObject;
@@ -114,9 +126,34 @@ CommandeType objectListCommand[20];
int16 objListTab[20];
uint16 exitEngine;
-uint16 zoneData[NUM_MAX_ZONE];
-uint16 zoneQuery[NUM_MAX_ZONE]; //!< Only exists in Operation Stealth
+Common::Array<uint16> zoneData;
+Common::Array<uint16> zoneQuery; //!< Only exists in Operation Stealth
+
+/*! \brief Move the player character using the keyboard
+ * \param x Negative values move left, positive right, zero not at all
+ * \param y Negative values move down, positive up, zero not at all
+ * NOTE: If both x and y are zero then the character stops
+ * FIXME: This seems to only work in Operation Stealth. May need code changes somewhere else...
+ */
+void moveUsingKeyboard(int x, int y) {
+ if (x > 0) {
+ xMoveKeyb = kKeybMoveRight;
+ } else if (x < 0) {
+ xMoveKeyb = kKeybMoveLeft;
+ } else {
+ xMoveKeyb = kKeybMoveCenterX;
+ }
+
+ if (y > 0) {
+ yMoveKeyb = kKeybMoveUp;
+ } else if (y < 0) {
+ yMoveKeyb = kKeybMoveDown;
+ } else {
+ yMoveKeyb = kKeybMoveCenterY;
+ }
+ egoMovedWithKeyboard = x || y;
+}
void stopMusicAfterFadeOut(void) {
// if (g_sfxPlayer->_fadeOutCounter != 0 && g_sfxPlayer->_fadeOutCounter < 100) {
@@ -141,7 +178,6 @@ void addPlayerCommandMessage(int16 cmd) {
tmp.type = 3;
overlayList.push_back(tmp);
- waitForPlayerClick = 1;
}
int16 getRelEntryForObject(uint16 param1, uint16 param2, SelectedObjStruct *pSelectedObject) {
@@ -189,6 +225,15 @@ int16 getObjectUnderCursor(uint16 x, uint16 y) {
frame = ABS((int16)(objectTable[it->objIdx].frame));
part = objectTable[it->objIdx].part;
+ // Additional case for negative frame values in Operation Stealth
+ if (g_cine->getGameType() == Cine::GType_OS && objectTable[it->objIdx].frame < 0) {
+ if ((it->type == 1) && (x >= objX) && (objX + frame >= x) && (y >= objY) && (objY + part >= y)) {
+ return it->objIdx;
+ } else {
+ continue;
+ }
+ }
+
if (it->type == 0) {
threshold = animDataTable[frame]._var1;
} else {
@@ -201,16 +246,19 @@ int16 getObjectUnderCursor(uint16 x, uint16 y) {
xdif = x - objX;
ydif = y - objY;
- if ((xdif < 0) || ((threshold << 4) <= xdif) || (ydif < 0) || (ydif >= height) || !animDataTable[frame].data()) {
+ if ((xdif < 0) || ((threshold << 4) <= xdif) || (ydif <= 0) || (ydif >= height) || !animDataTable[frame].data()) {
continue;
}
if (g_cine->getGameType() == Cine::GType_OS) {
+ // This test isn't present in Operation Stealth's PC version's disassembly
+ // but removing it makes things crash sometimes (e.g. when selecting a verb
+ // and moving the mouse cursor around the floor in the airport's bathroom).
if (xdif >= width) {
continue;
}
- if (it->type == 0 && animDataTable[frame].getColor(xdif, ydif) != part) {
+ if (it->type == 0 && animDataTable[frame].getColor(xdif, ydif) != (part & 0x0F)) {
return it->objIdx;
} else if (it->type == 1 && gfxGetBit(xdif, ydif, animDataTable[frame].data(), animDataTable[frame]._width * 4)) {
return it->objIdx;
@@ -270,6 +318,18 @@ void saveCommandVariables(Common::OutSaveFile &out) {
}
}
+/*! \brief Save the 80 bytes long command buffer padded to that length with zeroes. */
+void saveCommandBuffer(Common::OutSaveFile &out) {
+ // Let's make sure there's space for the trailing zero
+ // (That's why we subtract one from the maximum command buffer size here).
+ uint32 size = MIN<uint32>(commandBuffer.size(), kMaxCommandBufferSize - 1);
+ out.write(commandBuffer.c_str(), size);
+ // Write the rest as zeroes (Here we also write the string's trailing zero)
+ for (uint i = 0; i < kMaxCommandBufferSize - size; i++) {
+ out.writeByte(0);
+ }
+}
+
void saveAnimDataTable(Common::OutSaveFile &out) {
out.writeUint16BE(NUM_MAX_ANIMDATA); // Entry count
out.writeUint16BE(0x1E); // Entry size
@@ -570,16 +630,7 @@ void CineEngine::resetEngine() {
relTable.clear();
scriptTable.clear();
messageTable.clear();
-
- for (int i = 0; i < NUM_MAX_OBJECT; i++) {
- objectTable[i].x = 0;
- objectTable[i].y = 0;
- objectTable[i].part = 0;
- objectTable[i].name[0] = 0;
- objectTable[i].frame = 0;
- objectTable[i].mask = 0;
- objectTable[i].costume = 0;
- }
+ resetObjectTable();
globalVars.reset();
@@ -596,7 +647,7 @@ void CineEngine::resetEngine() {
playerCommand = -1;
isDrawCommandEnabled = 0;
- strcpy(commandBuffer, "");
+ commandBuffer = "";
globalVars[VAR_MOUSE_X_POS] = 0;
globalVars[VAR_MOUSE_Y_POS] = 0;
@@ -797,7 +848,10 @@ bool CineEngine::loadTempSaveOS(Common::SeekableReadStream &in) {
globalVars.load(in, NUM_MAX_VAR);
loadZoneData(in);
loadCommandVariables(in);
- in.read(commandBuffer, 0x50);
+ char tempCommandBuffer[kMaxCommandBufferSize];
+ in.read(tempCommandBuffer, kMaxCommandBufferSize);
+ commandBuffer = tempCommandBuffer;
+ renderer->setCommand(commandBuffer);
loadZoneQuery(in);
// TODO: Use the loaded string (Current music name (String, 13 bytes)).
@@ -934,7 +988,9 @@ bool CineEngine::loadPlainSaveFW(Common::SeekableReadStream &in, CineSaveGameFor
loadCommandVariables(in);
// At 0x22A9 (i.e. 0x22A1 + 4 * 2):
- in.read(commandBuffer, 0x50);
+ char tempCommandBuffer[kMaxCommandBufferSize];
+ in.read(tempCommandBuffer, kMaxCommandBufferSize);
+ commandBuffer = tempCommandBuffer;
renderer->setCommand(commandBuffer);
// At 0x22F9 (i.e. 0x22A9 + 0x50):
@@ -1082,7 +1138,7 @@ void CineEngine::makeSaveFW(Common::OutSaveFile &out) {
globalVars.save(out, NUM_MAX_VAR);
saveZoneData(out);
saveCommandVariables(out);
- out.write(commandBuffer, 0x50);
+ saveCommandBuffer(out);
out.writeUint16BE(renderer->_cmdY);
out.writeUint16BE(bgVar0);
@@ -1297,7 +1353,7 @@ void CineEngine::makeSaveOS(Common::OutSaveFile &out) {
globalVars.save(out, NUM_MAX_VAR);
saveZoneData(out);
saveCommandVariables(out);
- out.write(commandBuffer, 0x50);
+ saveCommandBuffer(out);
saveZoneQuery(out);
// FIXME: Save a proper name here, saving an empty string currently.
@@ -1360,19 +1416,38 @@ void drawDoubleMessageBox(int16 x, int16 y, int16 width, int16 currentY, int16 c
}
void processInventory(int16 x, int16 y) {
- int16 listSize = buildObjectListCommand(-2);
uint16 button;
+ int menuWidth;
+ int listSize;
+ int commandParam;
+
+ if (g_cine->getGameType() == Cine::GType_FW) {
+ menuWidth = 140;
+ commandParam = -2;
+ } else { // Operation Stealth
+ menuWidth = 160;
+ commandParam = -3;
+ }
+
+ listSize = buildObjectListCommand(commandParam);
if (!listSize)
return;
- renderer->drawMenu(objectListCommand, listSize, x, y, 140, -1);
+ renderer->drawMenu(objectListCommand, listSize, x, y, menuWidth, -1);
renderer->blit();
do {
manageEvents();
getMouseData(mouseUpdateStatus, &button, &dummyU16, &dummyU16);
} while (!button);
+
+ do {
+ manageEvents();
+ getMouseData(mouseUpdateStatus, &button, &dummyU16, &dummyU16);
+ } while (button);
+
+ // TODO: Both Future Wars and Operation Stealth call showMouse, drawMouse or something similar here.
}
int16 buildObjectListCommand(int16 param) {
@@ -1416,6 +1491,8 @@ int16 selectSubObject(int16 x, int16 y, int16 param) {
return objListTab[selectedObject];
}
+// TODO: Make separate functions for Future Wars's and Operation Stealth's version of this function, this is getting too messy
+// TODO: Add support for using the different prepositions for different verbs (Doesn't work currently)
void makeCommandLine(void) {
uint16 x, y;
@@ -1423,9 +1500,9 @@ void makeCommandLine(void) {
commandVar2 = -10;
if (playerCommand != -1) {
- strcpy(commandBuffer, defaultActionCommand[playerCommand]);
+ commandBuffer = defaultActionCommand[playerCommand];
} else {
- strcpy(commandBuffer, "");
+ commandBuffer = "";
}
if ((playerCommand != -1) && (choiceResultTable[playerCommand] == 2)) { // need object selection ?
@@ -1440,8 +1517,12 @@ void makeCommandLine(void) {
}
if (si < 0) {
- playerCommand = -1;
- strcpy(commandBuffer, "");
+ if (g_cine->getGameType() == Cine::GType_OS) {
+ canUseOnObject = 0;
+ } else { // Future Wars
+ playerCommand = -1;
+ commandBuffer = "";
+ }
} else {
if (g_cine->getGameType() == Cine::GType_OS) {
if (si >= 8000) {
@@ -1454,23 +1535,28 @@ void makeCommandLine(void) {
commandVar3[0] = si;
commandVar1 = 1;
-
- strcat(commandBuffer, " ");
- strcat(commandBuffer, objectTable[commandVar3[0]].name);
- strcat(commandBuffer, " ");
- strcat(commandBuffer, commandPrepositionOn);
+ commandBuffer += " ";
+ commandBuffer += objectTable[commandVar3[0]].name;
+ commandBuffer += " ";
+ if (g_cine->getGameType() == Cine::GType_OS) {
+ commandBuffer += commandPrepositionTable[playerCommand];
+ } else { // Future Wars
+ commandBuffer += defaultCommandPreposition;
+ }
}
- } else {
+ }
+
+ if (g_cine->getGameType() == Cine::GType_OS || !(playerCommand != -1 && choiceResultTable[playerCommand] == 2)) {
if (playerCommand == 2) {
getMouseData(mouseUpdateStatus, &dummyU16, &x, &y);
processInventory(x, y + 8);
playerCommand = -1;
commandVar1 = 0;
- strcpy(commandBuffer, "");
+ commandBuffer = "";
}
}
- if (g_cine->getGameType() == Cine::GType_OS) {
+ if (g_cine->getGameType() == Cine::GType_OS && playerCommand != 2) {
if (playerCommand != -1 && canUseOnObject != 0) { // call use on sub object
int16 si;
@@ -1478,34 +1564,38 @@ void makeCommandLine(void) {
si = selectSubObject(x, y + 8, -subObjectUseTable[playerCommand]);
- if (si) {
+ if (si >= 0) {
if (si >= 8000) {
si -= 8000;
}
commandVar3[commandVar1] = si;
-
commandVar1++;
-
- // TODO: add command message draw
+ commandBuffer += " ";
+ commandBuffer += objectTable[si].name;
}
+ }
- isDrawCommandEnabled = 1;
+ isDrawCommandEnabled = 1;
- if (playerCommand != -1 && choiceResultTable[playerCommand] == commandVar1) {
- SelectedObjStruct obj;
- obj.idx = commandVar3[0];
- obj.param = commandVar3[1];
- int16 di = getRelEntryForObject(playerCommand, commandVar1, &obj);
+ if (playerCommand != -1 && choiceResultTable[playerCommand] == commandVar1) {
+ SelectedObjStruct obj;
+ obj.idx = commandVar3[0];
+ obj.param = commandVar3[1];
+ int16 di = getRelEntryForObject(playerCommand, commandVar1, &obj);
- if (di != -1) {
- runObjectScript(di);
- }
- }
+ if (di != -1) {
+ runObjectScript(di);
+ } // TODO: else addFailureMessage(playerCommand)
+
+ playerCommand = -1;
+ commandVar1 = 0;
+ commandBuffer = "";
}
}
- if (!disableSystemMenu) {
+ if (g_cine->getGameType() == Cine::GType_OS || !disableSystemMenu) {
+ isDrawCommandEnabled = 1;
renderer->setCommand(commandBuffer);
}
}
@@ -1678,6 +1768,11 @@ uint16 executePlayerInput(void) {
}
if (allowPlayerInput) {
+ if (isDrawCommandEnabled) {
+ renderer->setCommand(commandBuffer);
+ isDrawCommandEnabled = 0;
+ }
+
getMouseData(mouseUpdateStatus, &mouseButton, &mouseX, &mouseY);
while (mouseButton && currentEntry < 200) {
@@ -1716,8 +1811,9 @@ uint16 executePlayerInput(void) {
commandVar3[commandVar1] = si;
commandVar1++;
- strcat(commandBuffer, " ");
- strcat(commandBuffer, objectTable[si].name);
+ commandBuffer += " ";
+ commandBuffer += objectTable[si].name;
+
isDrawCommandEnabled = 1;
@@ -1739,8 +1835,8 @@ uint16 executePlayerInput(void) {
playerCommand = -1;
commandVar1 = 0;
- strcpy(commandBuffer, "");
- renderer->setCommand("");
+ commandBuffer = "";
+ renderer->setCommand(commandBuffer);
}
} else {
globalVars[VAR_MOUSE_X_POS] = mouseX;
@@ -1761,13 +1857,7 @@ uint16 executePlayerInput(void) {
if (commandVar2 != objIdx) {
if (objIdx != -1) {
- char command[256];
-
- strcpy(command, commandBuffer);
- strcat(command, " ");
- strcat(command, objectTable[objIdx].name);
-
- renderer->setCommand(command);
+ renderer->setCommand(commandBuffer + " " + objectTable[objIdx].name);
} else {
isDrawCommandEnabled = 1;
}
@@ -1858,8 +1948,8 @@ uint16 executePlayerInput(void) {
var_2 = 0;
}
- if (inputVar1 && allowPlayerInput) { // use keyboard
- inputVar1 = 0;
+ if (egoMovedWithKeyboard && allowPlayerInput) { // use keyboard
+ egoMovedWithKeyboard = false;
switch (globalVars[VAR_MOUSE_X_MODE]) {
case 1:
@@ -1891,8 +1981,8 @@ uint16 executePlayerInput(void) {
globalVars[VAR_MOUSE_X_POS] = mouseX;
globalVars[VAR_MOUSE_Y_POS] = mouseY;
} else {
- if (inputVar2) {
- if (inputVar2 == 2) {
+ if (xMoveKeyb) {
+ if (xMoveKeyb == kKeybMoveLeft) {
globalVars[VAR_MOUSE_X_POS] = 1;
} else {
globalVars[VAR_MOUSE_X_POS] = 320;
@@ -1901,8 +1991,8 @@ uint16 executePlayerInput(void) {
globalVars[VAR_MOUSE_X_POS] = mouseX;
}
- if (inputVar3) {
- if (inputVar3 == 2) {
+ if (yMoveKeyb) {
+ if (yMoveKeyb == kKeybMoveUp) {
globalVars[VAR_MOUSE_Y_POS] = 1;
} else {
globalVars[VAR_MOUSE_Y_POS] = 200;
@@ -1943,6 +2033,14 @@ uint16 executePlayerInput(void) {
}
}
+ // Update Operation Stealth specific global variables.
+ // This fixes swimming at the bottom of the ocean after
+ // having been thrown into it with the girl.
+ if (g_cine->getGameType() == Cine::GType_OS) {
+ globalVars[251] = globalVars[VAR_MOUSE_X_POS];
+ globalVars[252] = globalVars[VAR_MOUSE_Y_POS];
+ }
+
return var_5E;
}
@@ -1984,9 +2082,22 @@ void drawSprite(Common::List<overlay>::iterator it, const byte *spritePtr, const
void removeMessages() {
Common::List<overlay>::iterator it;
+ bool remove;
for (it = overlayList.begin(); it != overlayList.end(); ) {
- if (it->type == 2 || it->type == 3) {
+ if (g_cine->getGameType() == Cine::GType_OS) {
+ // NOTE: These are really removeOverlay calls that have been deferred.
+ // In Operation Stealth's disassembly elements are removed from the
+ // overlay list right in the drawOverlays function (And actually in
+ // some other places too) and that's where incrementing a the overlay's
+ // last parameter by one if it's negative and testing it for positivity
+ // comes from too.
+ remove = it->type == 3 || (it->type == 2 && (it->color >= 0 || ++it->color >= 0));
+ } else { // Future Wars
+ remove = it->type == 2 || it->type == 3;
+ }
+
+ if (remove) {
it = overlayList.erase(it);
} else {
++it;
@@ -2069,7 +2180,6 @@ void addMessage(byte param1, int16 param2, int16 param3, int16 param4, int16 par
tmp.color = param5;
overlayList.push_back(tmp);
- waitForPlayerClick = 1;
}
Common::List<SeqListElement> seqList;
@@ -2328,9 +2438,9 @@ void processSeqListElement(SeqListElement &element) {
}
computeMove1(element, ptr1[4] + x, ptr1[5] + y, param1, param2, x2, y2);
} else {
- if (inputVar0 && allowPlayerInput) {
+ if (xMoveKeyb && allowPlayerInput) {
int16 adder = param1 + 1;
- if (inputVar0 != 1) {
+ if (xMoveKeyb != kKeybMoveRight) {
adder = -adder;
}
// FIXME: In Operation Stealth's disassembly global variable 251 is used here
@@ -2339,9 +2449,9 @@ void processSeqListElement(SeqListElement &element) {
globalVars[VAR_MOUSE_X_POS] = globalVars[251] = ptr1[4] + x + adder;
}
- if (inputVar1 && allowPlayerInput) {
+ if (yMoveKeyb && allowPlayerInput) {
int16 adder = param2 + 1;
- if (inputVar1 != 1) {
+ if (yMoveKeyb != kKeybMoveDown) {
adder = -adder;
}
// TODO: Name currently unnamed global variable 252
diff --git a/engines/cine/various.h b/engines/cine/various.h
index d87679ca08..0ee77c1b47 100644
--- a/engines/cine/various.h
+++ b/engines/cine/various.h
@@ -33,6 +33,9 @@
namespace Cine {
+// Maximum size of the command buffer including the trailing zero
+#define kMaxCommandBufferSize 80
+
void initLanguage(Common::Language lang);
int16 makeMenuChoice(const CommandeType commandList[], uint16 height, uint16 X, uint16 Y, uint16 width, bool recheckValue = false);
@@ -85,7 +88,7 @@ extern byte _danKeysPressed;
extern int16 playerCommand;
-extern char commandBuffer[80];
+extern Common::String commandBuffer;
extern char currentPrcName[20];
extern char currentRelName[20];
@@ -129,8 +132,8 @@ struct SelectedObjStruct {
};
#define NUM_MAX_ZONE 16
-extern uint16 zoneData[NUM_MAX_ZONE];
-extern uint16 zoneQuery[NUM_MAX_ZONE];
+extern Common::Array<uint16> zoneData;
+extern Common::Array<uint16> zoneQuery;
void addMessage(byte param1, int16 param2, int16 param3, int16 param4, int16 param5);
@@ -145,6 +148,7 @@ void processSeqList(void);
void resetGfxEntityEntry(uint16 objIdx);
bool makeTextEntryMenu(const char *caption, char *string, int strLen, int y);
+void moveUsingKeyboard(int x, int y);
} // End of namespace Cine
diff --git a/engines/drascula/detection.cpp b/engines/drascula/detection.cpp
index a426857fbd..9832d58294 100644
--- a/engines/drascula/detection.cpp
+++ b/engines/drascula/detection.cpp
@@ -195,6 +195,38 @@ static const DrasculaGameDescription gameDescriptions[] = {
},
},
+ {
+ // Drascula Spanish version (ScummVM repacked files)
+ {
+ "drascula",
+ 0,
+ {
+ {"packet.001", 0, "c6a8697396e213a18472542d5f547cb4", 32847563},
+ {"packet.004", 0, "a289d3cf80d50f25ec569b653248437e", 17205838},
+ {NULL, 0, NULL, 0}
+ },
+ Common::ES_ESP,
+ Common::kPlatformPC,
+ GF_PACKED
+ },
+ },
+
+ {
+ // Drascula Italian version (ScummVM repacked files)
+ {
+ "drascula",
+ 0,
+ {
+ {"packet.001", 0, "c6a8697396e213a18472542d5f547cb4", 32847563},
+ {"packet.005", 0, "f80e10e37000a2201eabf8dad82c7f64", 16184223},
+ {NULL, 0, NULL, 0}
+ },
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ GF_PACKED
+ },
+ },
+
{ AD_TABLE_END_MARKER }
};
diff --git a/engines/drascula/drascula.cpp b/engines/drascula/drascula.cpp
index 1cbe2ae0e1..2d24978f21 100644
--- a/engines/drascula/drascula.cpp
+++ b/engines/drascula/drascula.cpp
@@ -162,6 +162,12 @@ int DrasculaEngine::init() {
_textmisc = 0;
_textd1 = 0;
+ _color = 0;
+ blinking = 0;
+ leftMouseButton = 0;
+ rightMouseButton = 0;
+ *textName = 0;
+
if (!loadDrasculaDat())
return 1;
@@ -286,6 +292,8 @@ void DrasculaEngine::endChapter() {
bool DrasculaEngine::runCurrentChapter() {
int n;
+ rightMouseButton = 0;
+
if (_lang == kSpanish)
textSurface = extraSurface;
else
@@ -457,23 +465,45 @@ bool DrasculaEngine::runCurrentChapter() {
playMusic(roomMusic);
}
+ delay(25);
+#ifndef _WIN32_WCE
+ // FIXME
+ // This and the following #ifndefs disable the excess updateEvents() calls *within* the game loop.
+ // Events such as keypresses or mouse clicks are dropped on the ground with no processing
+ // by these calls. They are properly handled by the implicit call through getScan() below.
+ // It is not a good practice to not process events and indeed this created problems with synthesized
+ // events in the wince port.
updateEvents();
+#endif
if (menuScreen == 0 && takeObject == 1)
checkObjects();
+#ifdef _WIN32_WCE
+ if (rightMouseButton)
+ if (menuScreen) {
+#else
if (rightMouseButton == 1 && menuScreen == 1) {
- delay(100);
+#endif
if (currentChapter == 2)
loadPic(menuBackground, backSurface);
else
loadPic(99, backSurface);
setPalette((byte *)&gamePalette);
menuScreen = 0;
+#ifndef _WIN32_WCE
+ // FIXME: This call here is in hope that it will catch the rightmouseup event so the
+ // next if block won't be executed. This too is not a good coding practice. I've recoded it
+ // with a mutual exclusive if block for the menu. I would commit this properly but I cannot test
+ // for other (see Desktop) ports right now.
updateEvents();
+#endif
+#ifdef _WIN32_WCE
+ } else {
+#else
}
if (rightMouseButton == 1 && menuScreen == 0) {
- delay(100);
+#endif
characterMoved = 0;
if (trackProtagonist == 2)
trackProtagonist = 1;
@@ -486,15 +516,15 @@ bool DrasculaEngine::runCurrentChapter() {
else
loadPic("icons.alg", backSurface);
menuScreen = 1;
+#ifndef _WIN32_WCE
updateEvents();
+#endif
withoutVerb();
}
if (leftMouseButton == 1 && menuBar == 1) {
- delay(100);
selectVerbFromBar();
} else if (leftMouseButton == 1 && takeObject == 0) {
- delay(100);
if (verify1())
return true;
} else if (leftMouseButton == 1 && takeObject == 1) {
@@ -674,7 +704,11 @@ void DrasculaEngine::updateEvents() {
AudioCD.updateCD();
+#ifdef _WIN32_WCE
+ if (eventMan->pollEvent(event)) {
+#else
while (eventMan->pollEvent(event)) {
+#endif
switch (event.type) {
case Common::EVENT_KEYDOWN:
_keyPressed = event.kbd;
@@ -987,9 +1021,14 @@ char ***DrasculaEngine::loadTexts(Common::File &in) {
}
void DrasculaEngine::freeTexts(char ***ptr) {
+ if (!ptr)
+ return;
+
for (int lang = 0; lang < _numLangs; lang++) {
- free(ptr[lang][0] - DATAALIGNMENT);
- free(ptr[lang]);
+ if (ptr[lang]) {
+ free(ptr[lang][0]);
+ free(ptr[lang]);
+ }
}
free(ptr);
}
diff --git a/engines/drascula/interface.cpp b/engines/drascula/interface.cpp
index 6e86788007..ef1d1cc7a3 100644
--- a/engines/drascula/interface.cpp
+++ b/engines/drascula/interface.cpp
@@ -114,7 +114,8 @@ void DrasculaEngine::clearMenu() {
}
void DrasculaEngine::enterName() {
- Common::KeyCode key;
+ Common::KeyCode key, prevkey = Common::KEYCODE_INVALID;
+ int counter = 0;
int v = 0, h = 0;
char select2[23];
strcpy(select2, " ");
@@ -123,17 +124,25 @@ void DrasculaEngine::enterName() {
copyBackground(115, 14, 115, 14, 176, 9, bgSurface, screenSurface);
print_abc(select2, 117, 15);
updateScreen();
+ _system->delayMillis(100);
+
key = getScan();
- delay(70);
- if (key != 0) {
+
+ // Artifically decrease repeat rate.
+ // Part of bug fix#2017432 DRASCULA: Typing is slow when you save a game
+ // Alternative is to roll our own event loop
+ if (key == prevkey)
+ if (++counter == 3) {
+ counter = 0;
+ prevkey = Common::KEYCODE_INVALID;
+ }
+
+ if (key != 0 && key != prevkey) {
+ prevkey = key;
if (key >= 0 && key <= 0xFF && isalpha(key))
select2[v] = tolower(key);
- else if ((key == Common::KEYCODE_LCTRL) || (key == Common::KEYCODE_RCTRL))
- select2[v] = '\164';
- else if (key >= Common::KEYCODE_0 && key <= Common::KEYCODE_9)
+ else if ((key >= Common::KEYCODE_0 && key <= Common::KEYCODE_9) || key == Common::KEYCODE_SPACE)
select2[v] = key;
- else if (key == Common::KEYCODE_SPACE)
- select2[v] = '\167';
else if (key == Common::KEYCODE_ESCAPE)
break;
else if (key == Common::KEYCODE_RETURN) {
diff --git a/engines/drascula/saveload.cpp b/engines/drascula/saveload.cpp
index de82899462..6f88a58fbb 100644
--- a/engines/drascula/saveload.cpp
+++ b/engines/drascula/saveload.cpp
@@ -59,6 +59,8 @@ bool DrasculaEngine::saveLoadScreen() {
select[0] = 0;
+ _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
+
for (;;) {
y = 27;
copyBackground(0, 0, 0, 0, 320, 200, bgSurface, screenSurface);
@@ -140,8 +142,10 @@ bool DrasculaEngine::saveLoadScreen() {
}
if (mouseX > 125 && mouseY > 123 && mouseX < 199 && mouseY < 149 && selectionMade == 1) {
- if (!loadGame(file))
+ if (!loadGame(file)) {
+ _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
return false;
+ }
break;
} else if (mouseX > 208 && mouseY > 123 && mouseX < 282 && mouseY < 149 && selectionMade == 1) {
saveGame(file);
@@ -172,6 +176,8 @@ bool DrasculaEngine::saveLoadScreen() {
loadPic(roomNumber, bgSurface, HALF_PAL);
selectionMade = 0;
+ _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
+
return true;
}
diff --git a/engines/drascula/sound.cpp b/engines/drascula/sound.cpp
index 840d6c7cb5..2eb40e2e30 100644
--- a/engines/drascula/sound.cpp
+++ b/engines/drascula/sound.cpp
@@ -37,23 +37,25 @@ void DrasculaEngine::updateVolume(Audio::Mixer::SoundType soundType, int prevVol
}
void DrasculaEngine::volumeControls() {
- int masterVolume, voiceVolume, musicVolume;
-
copyRect(1, 56, 73, 63, 177, 97, tableSurface, screenSurface);
updateScreen(73, 63, 73, 63, 177, 97, screenSurface);
- masterVolume = 72 + 61 - ((_mixer->getVolumeForSoundType(Audio::Mixer::kPlainSoundType) / 16) * 4);
- voiceVolume = 72 + 61 - ((_mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType) / 16) * 4);
- musicVolume = 72 + 61 - ((_mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType) / 16) * 4);
-
for (;;) {
+ int masterVolume = CLIP((_mixer->getVolumeForSoundType(Audio::Mixer::kPlainSoundType) / 16), 0, 15);
+ int voiceVolume = CLIP((_mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType) / 16), 0, 15);
+ int musicVolume = CLIP((_mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType) / 16), 0, 15);
+
+ int masterVolumeY = 72 + 61 - masterVolume * 4;
+ int voiceVolumeY = 72 + 61 - voiceVolume * 4;
+ int musicVolumeY = 72 + 61 - musicVolume * 4;
+
updateRoom();
copyRect(1, 56, 73, 63, 177, 97, tableSurface, screenSurface);
- copyBackground(183, 56, 82, masterVolume, 39, 2 + ((_mixer->getVolumeForSoundType(Audio::Mixer::kPlainSoundType) / 16) * 4), tableSurface, screenSurface);
- copyBackground(183, 56, 138, voiceVolume, 39, 2 + ((_mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType) / 16) * 4), tableSurface, screenSurface);
- copyBackground(183, 56, 194, musicVolume, 39, 2 + ((_mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType) / 16) * 4), tableSurface, screenSurface);
+ copyBackground(183, 56, 82, masterVolumeY, 39, 2 + masterVolume * 4, tableSurface, screenSurface);
+ copyBackground(183, 56, 138, voiceVolumeY, 39, 2 + voiceVolume * 4, tableSurface, screenSurface);
+ copyBackground(183, 56, 194, musicVolumeY, 39, 2 + musicVolume * 4, tableSurface, screenSurface);
setCursorTable();
@@ -68,18 +70,15 @@ void DrasculaEngine::volumeControls() {
if (leftMouseButton == 1) {
delay(100);
if (mouseX > 80 && mouseX < 121) {
- updateVolume(Audio::Mixer::kPlainSoundType, mouseY);
- masterVolume = 72 + 61 - ((_mixer->getVolumeForSoundType(Audio::Mixer::kPlainSoundType) / 16) * 4);
+ updateVolume(Audio::Mixer::kPlainSoundType, masterVolumeY);
}
if (mouseX > 136 && mouseX < 178) {
- updateVolume(Audio::Mixer::kSFXSoundType, mouseY);
- voiceVolume = 72 + 61 - ((_mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType) / 16) * 4);
+ updateVolume(Audio::Mixer::kSFXSoundType, voiceVolumeY);
}
if (mouseX > 192 && mouseX < 233) {
- updateVolume(Audio::Mixer::kMusicSoundType, mouseY);
- musicVolume = 72 + 61 - ((_mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType) / 16) * 4);
+ updateVolume(Audio::Mixer::kMusicSoundType, musicVolumeY);
}
}
diff --git a/engines/gob/mult_v2.cpp b/engines/gob/mult_v2.cpp
index 20a81174e5..6bd4ddb625 100644
--- a/engines/gob/mult_v2.cpp
+++ b/engines/gob/mult_v2.cpp
@@ -510,10 +510,11 @@ void Mult_v2::playMultInit() {
if (!_animSurf) {
int16 width, height;
- for (int i = 0; i < _objCount; i++) {
- delete _objects[i].pPosX;
- delete _objects[i].pPosY;
- }
+ if (_objects)
+ for (int i = 0; i < _objCount; i++) {
+ delete _objects[i].pPosX;
+ delete _objects[i].pPosY;
+ }
delete[] _objects;
diff --git a/engines/kyra/detection.cpp b/engines/kyra/detection.cpp
index 2d592069d2..0c6eb29665 100644
--- a/engines/kyra/detection.cpp
+++ b/engines/kyra/detection.cpp
@@ -56,6 +56,7 @@ namespace {
#define KYRA2_FLOPPY_FLAGS FLAGS(false, false, false, false, false, false, Kyra::GI_KYRA2)
#define KYRA2_FLOPPY_CMP_FLAGS FLAGS(false, false, false, false, false, true, Kyra::GI_KYRA2)
#define KYRA2_CD_FLAGS FLAGS(false, false, true, false, false, false, Kyra::GI_KYRA2)
+#define KYRA2_CD_FAN_FLAGS(x, y) FLAGS_FAN(x, y, false, false, true, false, false, false, Kyra::GI_KYRA2)
#define KYRA2_CD_DEMO_FLAGS FLAGS(true, false, true, false, false, false, Kyra::GI_KYRA2)
#define KYRA2_DEMO_FLAGS FLAGS(true, false, false, false, false, false, Kyra::GI_KYRA2)
#define KYRA2_TOWNS_FLAGS FLAGS(false, false, false, false, false, false, Kyra::GI_KYRA2)
@@ -66,8 +67,14 @@ namespace {
#define KYRA3_CD_FAN_FLAGS(x, y) FLAGS_FAN(x, y, false, false, true, false, true, false, Kyra::GI_KYRA3)
#define LOL_CD_FLAGS FLAGS(false, false, true, false, false, false, Kyra::GI_LOL)
+#define LOL_FLOPPY_FLAGS FLAGS(false, false, false, false, false, false, Kyra::GI_LOL)
+#define LOL_FLOPPY_CMP_FLAGS FLAGS(false, false, false, false, false, true, Kyra::GI_LOL)
+#define LOL_PC98_FLAGS FLAGS(false, false, false, false, false, false, Kyra::GI_LOL)
+#define LOL_PC98_SJIS_FLAGS FLAGS(false, false, false, true, false, false, Kyra::GI_LOL)
+#define LOL_DEMO_FLAGS FLAGS(true, false, false, false, false, false, Kyra::GI_KYRA2)
const KYRAGameDescription adGameDescs[] = {
+ /* disable these targets until they get supported
{
{
"kyra1",
@@ -79,6 +86,7 @@ const KYRAGameDescription adGameDescs[] = {
},
KYRA1_FLOPPY_CMP_FLAGS
},
+
{
{
"kyra1",
@@ -90,6 +98,8 @@ const KYRAGameDescription adGameDescs[] = {
},
KYRA1_FLOPPY_CMP_FLAGS
},
+ */
+
{
{
"kyra1",
@@ -210,7 +220,7 @@ const KYRAGameDescription adGameDescs[] = {
{ // FM-Towns version
{
"kyra1",
- 0,
+ "CD",
{
{ "EMC.PAK", 0, "a046bb0b422061aab8e4c4689400343a", -1 },
{ "TWMUSIC.PAK", 0, "e53bca3a3e3fb49107d59463ec387a59", -1 },
@@ -218,14 +228,14 @@ const KYRAGameDescription adGameDescs[] = {
},
Common::EN_ANY,
Common::kPlatformFMTowns,
- Common::ADGF_NO_FLAGS
+ Common::ADGF_CD
},
KYRA1_TOWNS_FLAGS
},
{
{
"kyra1",
- 0,
+ "CD",
{
{ "JMC.PAK", 0, "9c5707a2a478e8167e44283246612d2c", -1 },
{ "TWMUSIC.PAK", 0, "e53bca3a3e3fb49107d59463ec387a59", -1 },
@@ -233,7 +243,7 @@ const KYRAGameDescription adGameDescs[] = {
},
Common::JA_JPN,
Common::kPlatformFMTowns,
- Common::ADGF_NO_FLAGS
+ Common::ADGF_CD
},
KYRA1_TOWNS_SJIS_FLAGS
},
@@ -421,6 +431,77 @@ const KYRAGameDescription adGameDescs[] = {
KYRA2_CD_FLAGS
},
+ // Italian fan translation, see fr#2003504 "KYRA: add support for Italian version of Kyrandia 2&3"
+ { // CD version
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("FATE.PAK", "30487f3b8d7790c7857f4769ff2dd125"),
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ Common::ADGF_DROPLANGUAGE | Common::ADGF_CD
+ },
+ KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
+ },
+ {
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("FATE.PAK", "30487f3b8d7790c7857f4769ff2dd125"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ Common::ADGF_DROPLANGUAGE | Common::ADGF_CD
+ },
+ KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
+ },
+ {
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("FATE.PAK", "30487f3b8d7790c7857f4769ff2dd125"),
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ Common::ADGF_DROPLANGUAGE | Common::ADGF_CD
+ },
+ KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
+ },
+
+ {
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("FATE.PAK", "39772ff82e42c4c520050518deb82e64"),
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ Common::ADGF_DROPLANGUAGE | Common::ADGF_CD
+ },
+ KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
+ },
+
+ {
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("FATE.PAK", "39772ff82e42c4c520050518deb82e64"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ Common::ADGF_DROPLANGUAGE | Common::ADGF_CD
+ },
+ KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
+ },
+
+ {
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("FATE.PAK", "39772ff82e42c4c520050518deb82e64"),
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ Common::ADGF_DROPLANGUAGE | Common::ADGF_CD
+ },
+ KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
+ },
+
{ // Interactive Demo
{
"kyra2",
@@ -457,11 +538,11 @@ const KYRAGameDescription adGameDescs[] = {
KYRA2_CD_DEMO_FLAGS
},
- { // Non-Interactive Demo
+ { // Non-Interactive Demos
{
"kyra2",
"Demo",
- AD_ENTRY1("GENERAL.PAK", "35825783e5b60755fd520360079f9c15"),
+ AD_ENTRY1("VOC.PAK", "ecb3561b63749158172bf21528cf5f45"),
Common::EN_ANY,
Common::kPlatformPC,
Common::ADGF_DEMO
@@ -472,44 +553,44 @@ const KYRAGameDescription adGameDescs[] = {
{ // FM-Towns
{
"kyra2",
- 0,
+ "CD",
AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"),
Common::EN_ANY,
Common::kPlatformFMTowns,
- Common::ADGF_NO_FLAGS
+ Common::ADGF_CD
},
KYRA2_TOWNS_FLAGS
},
{
{
"kyra2",
- 0,
+ "CD",
AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"),
Common::JA_JPN,
Common::kPlatformFMTowns,
- Common::ADGF_NO_FLAGS
+ Common::ADGF_CD
},
KYRA2_TOWNS_SJIS_FLAGS
},
{ // PC-9821
{
"kyra2",
- 0,
+ "CD",
AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"),
Common::EN_ANY,
Common::kPlatformPC98,
- Common::ADGF_NO_FLAGS
+ Common::ADGF_CD
},
KYRA2_TOWNS_FLAGS
},
{
{
"kyra2",
- 0,
+ "CD",
AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"),
Common::JA_JPN,
Common::kPlatformPC98,
- Common::ADGF_NO_FLAGS
+ Common::ADGF_CD
},
KYRA2_TOWNS_SJIS_FLAGS
},
@@ -610,6 +691,53 @@ const KYRAGameDescription adGameDescs[] = {
KYRA3_CD_INS_FLAGS
},
+ // Mac version
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
+ { "AUD.PAK", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ Common::ADGF_DROPLANGUAGE
+ },
+ KYRA3_CD_INS_FLAGS
+ },
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
+ { "AUD.PAK", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::DE_DEU,
+ Common::kPlatformMacintosh,
+ Common::ADGF_DROPLANGUAGE
+ },
+ KYRA3_CD_INS_FLAGS
+ },
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
+ { "AUD.PAK", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::FR_FRA,
+ Common::kPlatformMacintosh,
+ Common::ADGF_DROPLANGUAGE
+ },
+ KYRA3_CD_INS_FLAGS
+ },
+
// Spanish fan translation, see fr#1994040 "KYRA3: Add support for Spanish fan translation"
{
{
@@ -657,7 +785,7 @@ const KYRAGameDescription adGameDescs[] = {
KYRA3_CD_FAN_FLAGS(Common::ES_ESP, Common::EN_ANY)
},
- // Itlian fan translation, see fr#2003504 "KYRA: add support for Italian version of Kyrandia 2&3"
+ // Italian fan translation, see fr#2003504 "KYRA: add support for Italian version of Kyrandia 2&3"
{
{
"kyra3",
@@ -752,7 +880,134 @@ const KYRAGameDescription adGameDescs[] = {
},
LOL_CD_FLAGS
},
+
+ {
+ {
+ "lol",
+ "CD",
+ {
+ { "GENERAL.PAK", 0, "9e4bab499b7ea9337b91ac29fcba6d13", -1 },
+ { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ Common::ADGF_DROPLANGUAGE | Common::ADGF_CD
+ },
+ LOL_CD_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ "CD",
+ {
+ { "GENERAL.PAK", 0, "9e4bab499b7ea9337b91ac29fcba6d13", -1 },
+ { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ Common::ADGF_DROPLANGUAGE | Common::ADGF_CD
+ },
+ LOL_CD_FLAGS
+ },
+ {
+ {
+ "lol",
+ "CD",
+ {
+ { "GENERAL.PAK", 0, "9e4bab499b7ea9337b91ac29fcba6d13", -1 },
+ { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ Common::ADGF_DROPLANGUAGE | Common::ADGF_CD
+ },
+ LOL_CD_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ 0,
+ {
+ { "WESTWOOD.1", 0, "3c61cb7de5b2ec452f5851f5075207ee", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ Common::ADGF_NO_FLAGS
+ },
+ LOL_FLOPPY_CMP_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ "Extracted",
+ {
+ { "GENERAL.PAK", 0, "996e66e81054d36249907a1d8158da3d", -1 },
+ { "CHAPTER7.PAK", 0, "cabee57f00d6d84b65a732b6868a4959", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ Common::ADGF_NO_FLAGS
+ },
+ LOL_FLOPPY_FLAGS
+ },
+
+ /* disable these targets until they get supported
+ {
+ {
+ "lol",
+ 0,
+ {
+ { "GENERAL.PAK", 0, "3fe6539b9b09084c0984eaf7170464e9", -1 },
+ { "MUS.PAK", 0, "008dc69d8cbcdb6bae30e270fab26e76", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC98,
+ Common::ADGF_NO_FLAGS
+ },
+ LOL_PC98_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ 0,
+ {
+ { "GENERAL.PAK", 0, "3fe6539b9b09084c0984eaf7170464e9", -1 },
+ { "MUS.PAK", 0, "008dc69d8cbcdb6bae30e270fab26e76", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::JA_JPN,
+ Common::kPlatformPC98,
+ Common::ADGF_NO_FLAGS
+ },
+ LOL_PC98_SJIS_FLAGS
+ },*/
+
+ {
+ {
+ "lol",
+ "Demo",
+ {
+ { "GENERAL.PAK", 0, "e94863d86c4597a2d581d05481c152ba", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ Common::ADGF_NO_FLAGS
+ },
+ LOL_DEMO_FLAGS
+ },
+
{ AD_TABLE_END_MARKER, FLAGS(0, 0, 0, 0, 0, 0, 0) }
};
diff --git a/engines/kyra/gui_lok.cpp b/engines/kyra/gui_lok.cpp
index 35b343fc25..47ce698e50 100644
--- a/engines/kyra/gui_lok.cpp
+++ b/engines/kyra/gui_lok.cpp
@@ -530,7 +530,7 @@ int GUI_LoK::resumeGame(Button *button) {
void GUI_LoK::setupSavegames(Menu &menu, int num) {
Common::InSaveFile *in;
- static char savenames[5][31];
+ static char savenames[5][35];
uint8 startSlot;
assert(num <= 5);
@@ -549,7 +549,8 @@ void GUI_LoK::setupSavegames(Menu &menu, int num) {
KyraEngine_v1::SaveHeader header;
for (int i = startSlot; i < num && uint(_savegameOffset + i) < _saveSlots.size(); i++) {
if ((in = _vm->openSaveForReading(_vm->getSavegameFilename(_saveSlots[i + _savegameOffset]), header))) {
- strncpy(savenames[i], header.description.c_str(), 31);
+ strncpy(savenames[i], header.description.c_str(), ARRAYSIZE(savenames[0]));
+ savenames[i][34] = 0;
menu.item[i].itemString = savenames[i];
menu.item[i].enabled = 1;
menu.item[i].saveSlot = _saveSlots[i + _savegameOffset];
@@ -673,7 +674,7 @@ void GUI_LoK::updateSavegameString() {
length = strlen(_savegameName);
if (_keyPressed.ascii > 31 && _keyPressed.ascii < 127) {
- if (length < 31) {
+ if (length < ARRAYSIZE(_savegameName)-1) {
_savegameName[length] = _keyPressed.ascii;
_savegameName[length+1] = 0;
redrawTextfield();
diff --git a/engines/kyra/gui_lok.h b/engines/kyra/gui_lok.h
index 49081c7ae2..16b7ef9183 100644
--- a/engines/kyra/gui_lok.h
+++ b/engines/kyra/gui_lok.h
@@ -162,7 +162,7 @@ private:
bool _menuRestoreScreen;
uint8 _toplevelMenu;
int _savegameOffset;
- char _savegameName[31];
+ char _savegameName[35];
const char *_specialSavegameString;
Common::KeyState _keyPressed;
int8 _mouseWheel;
diff --git a/engines/kyra/gui_v2.cpp b/engines/kyra/gui_v2.cpp
index 2819c4f077..e4cec760fa 100644
--- a/engines/kyra/gui_v2.cpp
+++ b/engines/kyra/gui_v2.cpp
@@ -456,6 +456,7 @@ void GUI_v2::setupSavegameNames(Menu &menu, int num) {
for (int i = startSlot; i < num && uint(_savegameOffset + i) < _saveSlots.size(); ++i) {
if ((in = _vm->openSaveForReading(_vm->getSavegameFilename(_saveSlots[i + _savegameOffset]), header)) != 0) {
strncpy(getTableString(menu.item[i].itemId), header.description.c_str(), 80);
+ getTableString(menu.item[i].itemId)[79] = 0;
menu.item[i].saveSlot = _saveSlots[i + _savegameOffset];
menu.item[i].enabled = true;
delete in;
diff --git a/engines/kyra/gui_v2.h b/engines/kyra/gui_v2.h
index 161752627b..60b7f0ab86 100644
--- a/engines/kyra/gui_v2.h
+++ b/engines/kyra/gui_v2.h
@@ -188,7 +188,7 @@ protected:
// save menu
bool _noSaveProcess;
int _saveSlot;
- char _saveDescription[0x50];
+ char _saveDescription[0x51];
int saveMenu(Button *caller);
int clickSaveSlot(Button *caller);
diff --git a/engines/kyra/kyra_hof.cpp b/engines/kyra/kyra_hof.cpp
index d3de621707..33a8af91f1 100644
--- a/engines/kyra/kyra_hof.cpp
+++ b/engines/kyra/kyra_hof.cpp
@@ -196,14 +196,18 @@ void KyraEngine_HoF::pauseEngineIntern(bool pause) {
_seqWsaChatTimeout += pausedTime;
_seqWsaChatFrameTimeout += pausedTime;
- for (int x = 0; x < 10; x++) {
- if (_activeText[x].duration != -1)
- _activeText[x].startTime += pausedTime;
+ if (_activeText) {
+ for (int x = 0; x < 10; x++) {
+ if (_activeText[x].duration != -1)
+ _activeText[x].startTime += pausedTime;
+ }
}
- for (int x = 0; x < 8; x++) {
- if (_activeWSA[x].flags != -1)
- _activeWSA[x].nextFrame += pausedTime;
+ if (_activeWSA) {
+ for (int x = 0; x < 8; x++) {
+ if (_activeWSA[x].flags != -1)
+ _activeWSA[x].nextFrame += pausedTime;
+ }
}
_nextIdleAnim += pausedTime;
@@ -251,7 +255,7 @@ int KyraEngine_HoF::init() {
_abortIntroFlag = false;
if (_sequenceStrings) {
- for (int i = 0; i < 33; i++)
+ for (int i = 0; i < MIN(33, _sequenceStringsSize); i++)
_sequenceStringsDuration[i] = (int) strlen(_sequenceStrings[i]) * 8;
}
@@ -278,7 +282,10 @@ int KyraEngine_HoF::go() {
seq_showStarcraftLogo();
if (_flags.isDemo && !_flags.isTalkie) {
- seq_playSequences(kSequenceDemoVirgin, kSequenceDemoFisher);
+ if (_flags.gameID == GI_LOL)
+ seq_playSequences(kSequenceLolDemoScene1, kSequenceLolDemoScene6);
+ else
+ seq_playSequences(kSequenceDemoVirgin, kSequenceDemoFisher);
_menuChoice = 4;
} else {
seq_playSequences(kSequenceVirgin, kSequenceZanfaun);
@@ -292,10 +299,12 @@ int KyraEngine_HoF::go() {
if (_menuChoice != 4) {
// load just the pak files needed for ingame
_res->loadPakFile(StaticResource::staticDataFilename());
- if (_flags.platform == Common::kPlatformPC && _flags.isTalkie)
- _res->loadFileList("FILEDATA.FDT");
- else
+ if (_flags.platform == Common::kPlatformPC && _flags.isTalkie) {
+ if (!_res->loadFileList("FILEDATA.FDT"))
+ error("couldn't load 'FILEDATA.FDT'");
+ } else {
_res->loadFileList(_ingamePakList, _ingamePakListSize);
+ }
if (_flags.platform == Common::kPlatformPC98)
_res->loadPakFile("AUDIO.PAK");
@@ -1501,15 +1510,19 @@ void KyraEngine_HoF::openTalkFile(int newFile) {
_oldTalkFile = -1;
}
- if (newFile == 0) {
+ if (newFile == 0)
strcpy(talkFilename, "ANYTALK.TLK");
- _res->loadPakFile(talkFilename);
- } else {
+ else
sprintf(talkFilename, "CH%dVOC.TLK", newFile);
- _res->loadPakFile(talkFilename);
- }
_oldTalkFile = newFile;
+
+ if (!_res->loadPakFile(talkFilename)) {
+ if (speechEnabled()) {
+ warning("Couldn't load file '%s' falling back to text only mode", talkFilename);
+ _configVoice = 0;
+ }
+ }
}
void KyraEngine_HoF::snd_playVoiceFile(int id) {
@@ -1545,7 +1558,8 @@ void KyraEngine_HoF::playVoice(int high, int low) {
if (!_flags.isTalkie)
return;
int vocFile = high * 10000 + low * 10;
- snd_playVoiceFile(vocFile);
+ if (speechEnabled())
+ snd_playVoiceFile(vocFile);
}
void KyraEngine_HoF::snd_playSoundEffect(int track, int volume) {
diff --git a/engines/kyra/kyra_hof.h b/engines/kyra/kyra_hof.h
index 866dd55d16..f6e887c648 100644
--- a/engines/kyra/kyra_hof.h
+++ b/engines/kyra/kyra_hof.h
@@ -97,6 +97,20 @@ enum kNestedSequencesDemo {
kSequenceDemoDig
};
+enum kSequencesLolDemo {
+ kSequenceLolDemoScene1 = 0,
+ kSequenceLolDemoText1,
+ kSequenceLolDemoScene2,
+ kSequenceLolDemoText2,
+ kSequenceLolDemoScene3,
+ kSequenceLolDemoText3,
+ kSequenceLolDemoScene4,
+ kSequenceLolDemoText4,
+ kSequenceLolDemoScene5,
+ kSequenceLolDemoText5,
+ kSequenceLolDemoScene6
+};
+
class WSAMovie_v2;
class KyraEngine_HoF;
class TextDisplayer_HoF;
@@ -242,6 +256,14 @@ protected:
int seq_demoBail(WSAMovie_v2 *wsaObj, int x, int y, int frm);
int seq_demoDig(WSAMovie_v2 *wsaObj, int x, int y, int frm);
+ int seq_lolDemoScene1(WSAMovie_v2 *wsaObj, int x, int y, int frm);
+ int seq_lolDemoScene2(WSAMovie_v2 *wsaObj, int x, int y, int frm);
+ int seq_lolDemoScene3(WSAMovie_v2 *wsaObj, int x, int y, int frm);
+ int seq_lolDemoScene4(WSAMovie_v2 *wsaObj, int x, int y, int frm);
+ int seq_lolDemoScene5(WSAMovie_v2 *wsaObj, int x, int y, int frm);
+ int seq_lolDemoText5(WSAMovie_v2 *wsaObj, int x, int y, int frm);
+ int seq_lolDemoScene6(WSAMovie_v2 *wsaObj, int x, int y, int frm);
+
void seq_sequenceCommand(int command);
void seq_loadNestedSequence(int wsaNum, int seqNum);
void seq_nestedSequenceFrame(int command, int wsaNum);
@@ -264,7 +286,7 @@ protected:
WSAMovie_v2 * wsa, int firstframe, int lastframe, int wsaXpos, int wsaYpos);
void seq_finaleActorScreen();
void seq_displayScrollText(uint8 *data, const ScreenDim *d, int tempPage1, int tempPage2, int speed, int step, Screen::FontId fid1, Screen::FontId fid2, const uint8 *shapeData = 0, const char *const *specialData = 0);
- void seq_scrollPage();
+ void seq_scrollPage(int bottom, int top);
void seq_showStarcraftLogo();
void seq_init();
@@ -280,8 +302,7 @@ protected:
static const int8 _dosTrackMap[];
static const int _dosTrackMapSize;
- const AudioDataStruct *_soundData;
-
+ AudioDataStruct _soundData[3];
protected:
// game initialization
void startup();
diff --git a/engines/kyra/kyra_lok.h b/engines/kyra/kyra_lok.h
index cb3062847e..1def95ddbf 100644
--- a/engines/kyra/kyra_lok.h
+++ b/engines/kyra/kyra_lok.h
@@ -633,7 +633,7 @@ protected:
int _soundFilesIntroSize;
const int32 *_cdaTrackTable;
int _cdaTrackTableSize;
- const AudioDataStruct * _soundData;
+ AudioDataStruct _soundData[3];
// positions of the inventory
static const uint16 _itemPosX[];
diff --git a/engines/kyra/kyra_mr.cpp b/engines/kyra/kyra_mr.cpp
index a4e5b58364..a61253199b 100644
--- a/engines/kyra/kyra_mr.cpp
+++ b/engines/kyra/kyra_mr.cpp
@@ -508,7 +508,8 @@ void KyraEngine_MR::snd_playVoiceFile(int file) {
char filename[16];
snprintf(filename, 16, "%.08u", (uint)file);
- _voiceSoundChannel = _soundDigital->playSound(filename, 0xFE, Audio::Mixer::kSpeechSoundType, 255);
+ if (speechEnabled())
+ _voiceSoundChannel = _soundDigital->playSound(filename, 0xFE, Audio::Mixer::kSpeechSoundType, 255);
}
bool KyraEngine_MR::snd_voiceIsPlaying() {
@@ -802,7 +803,12 @@ void KyraEngine_MR::openTalkFile(int file) {
}
_currentTalkFile = file;
- _res->loadPakFile(talkFilename);
+ if (!_res->loadPakFile(talkFilename)) {
+ if (speechEnabled()) {
+ warning("Couldn't load file '%s' falling back to text only mode", talkFilename);
+ _configVoice = 0;
+ }
+ }
}
#pragma mark -
diff --git a/engines/kyra/kyra_v1.cpp b/engines/kyra/kyra_v1.cpp
index 85c03dc1bb..bc46d8e1f5 100644
--- a/engines/kyra/kyra_v1.cpp
+++ b/engines/kyra/kyra_v1.cpp
@@ -114,7 +114,7 @@ int KyraEngine_v1::init() {
_sound = new SoundTownsPC98_v2(this, _mixer);
} else if (_flags.platform == Common::kPlatformPC98) {
if (_flags.gameID == GI_KYRA1)
- _sound = new SoundTowns/*SoundPC98*/(this, _mixer);
+ _sound = new SoundPC98(this, _mixer);
else
_sound = new SoundTownsPC98_v2(this, _mixer);
} else if (midiDriver == MD_ADLIB) {
@@ -152,6 +152,16 @@ int KyraEngine_v1::init() {
_res = new Resource(this);
assert(_res);
_res->reset();
+
+ if (_flags.isDemo) {
+ // HACK: check whether this is the HOF demo or the LOL demo.
+ // The LOL demo needs to be detected and run as KyraEngine_HoF,
+ // but the static resource loader and the sequence player will
+ // need correct IDs.
+ if (_res->exists("scene1.cps"))
+ _flags.gameID = GI_LOL;
+ }
+
_staticres = new StaticResource(this);
assert(_staticres);
if (!_staticres->init())
diff --git a/engines/kyra/kyra_v1.h b/engines/kyra/kyra_v1.h
index 50cabc421e..738a3fc8ec 100644
--- a/engines/kyra/kyra_v1.h
+++ b/engines/kyra/kyra_v1.h
@@ -34,8 +34,8 @@
#include "kyra/script.h"
namespace Common {
-class InSaveFile;
-class OutSaveFile;
+class SeekableReadStream;
+class WriteStream;
} // end of namespace Common
class KyraMetaEngine;
@@ -70,9 +70,9 @@ enum {
struct AudioDataStruct {
const char * const *_fileList;
- const int _fileListLen;
- const void * const _cdaTracks;
- const int _cdaNumTracks;
+ int _fileListLen;
+ const void * _cdaTracks;
+ int _cdaNumTracks;
};
// TODO: this is just the start of makeing the debug output of the kyra engine a bit more useable
@@ -299,10 +299,10 @@ protected:
kRSHEIoError = 3
};
- static kReadSaveHeaderError readSaveHeader(Common::InSaveFile *file, SaveHeader &header);
+ static kReadSaveHeaderError readSaveHeader(Common::SeekableReadStream *file, SaveHeader &header);
- Common::InSaveFile *openSaveForReading(const char *filename, SaveHeader &header);
- Common::OutSaveFile *openSaveForWriting(const char *filename, const char *saveName) const;
+ Common::SeekableReadStream *openSaveForReading(const char *filename, SaveHeader &header);
+ Common::WriteStream *openSaveForWriting(const char *filename, const char *saveName) const;
};
} // End of namespace Kyra
diff --git a/engines/kyra/lol.cpp b/engines/kyra/lol.cpp
index ef1121baa0..6624dd91ea 100644
--- a/engines/kyra/lol.cpp
+++ b/engines/kyra/lol.cpp
@@ -87,6 +87,9 @@ int LoLEngine::init() {
_screen->setAnimBlockPtr(10000);
_screen->setScreenDim(0);
+ if (!_sound->init())
+ error("Couldn't init sound");
+
return 0;
}
@@ -256,11 +259,14 @@ void LoLEngine::setupPrologueData(bool load) {
"xxx/intro9.pak"
};
- char filename[32];
+ char filepath[32];
+ char *filename = filepath;
for (uint i = 0; i < ARRAYSIZE(fileList); ++i) {
strcpy(filename, fileList[i]);
memcpy(filename, _languageExt[_lang], 3);
-
+ if (!_flags.isTalkie)
+ filename += 4;
+
if (load) {
if (!_res->loadPakFile(filename))
error("Couldn't load file: '%s'", filename);
diff --git a/engines/kyra/resource.cpp b/engines/kyra/resource.cpp
index 92818aafe1..5da6bb3873 100644
--- a/engines/kyra/resource.cpp
+++ b/engines/kyra/resource.cpp
@@ -55,12 +55,10 @@ bool Resource::reset() {
if (!dir.exists() || !dir.isDirectory())
error("invalid game path '%s'", dir.getPath().c_str());
- if (_vm->game() != GI_LOL) {
- if (!loadPakFile(StaticResource::staticDataFilename()) || !StaticResource::checkKyraDat()) {
- Common::String errorMessage = "You're missing the '" + StaticResource::staticDataFilename() + "' file or it got corrupted, (re)get it from the ScummVM website";
- _vm->GUIErrorMessage(errorMessage);
- error(errorMessage.c_str());
- }
+ if (!loadPakFile(StaticResource::staticDataFilename()) || !StaticResource::checkKyraDat()) {
+ Common::String errorMessage = "You're missing the '" + StaticResource::staticDataFilename() + "' file or it got corrupted, (re)get it from the ScummVM website";
+ _vm->GUIErrorMessage(errorMessage);
+ error(errorMessage.c_str());
}
if (_vm->game() == GI_KYRA1) {
@@ -91,17 +89,23 @@ bool Resource::reset() {
return true;
} else if (_vm->game() == GI_KYRA3) {
- if (_vm->gameFlags().useInstallerPackage)
- loadPakFile("WESTWOOD.001");
+ if (_vm->gameFlags().useInstallerPackage) {
+ if (!loadPakFile("WESTWOOD.001"))
+ error("couldn't load file: 'WESTWOOD.001'");
+ }
// Add default file directories
Common::File::addDefaultDirectory(ConfMan.get("path") + "malcolm");
Common::File::addDefaultDirectory(ConfMan.get("path") + "MALCOLM");
- loadFileList("FILEDATA.FDT");
+ if (!loadFileList("FILEDATA.FDT"))
+ error("couldn't load file: 'FILEDATA.FDT'");
return true;
} else if (_vm->game() == GI_LOL) {
+ if (_vm->gameFlags().useInstallerPackage)
+ tryLoadCompFiles();
+
return true;
}
@@ -180,8 +184,14 @@ bool Resource::loadPakFile(const Common::String &filename) {
for (ResArchiveLoader::FileList::iterator i = files.begin(); i != files.end(); ++i) {
iter = _map.find(i->filename);
if (iter == _map.end()) {
+ // We do an internal check for a file in gamepath with same filename to
+ // allow overwriting files inside archives with plain files inside the
+ // game directory
+ checkFile(i->filename);
+
// A new file entry, so we just insert it into the file map.
- _map[i->filename] = i->entry;
+ if (_map.find(i->filename) == _map.end())
+ _map[i->filename] = i->entry;
} else if (!iter->_value.parent.empty()) {
if (!iter->_value.parent.equalsIgnoreCase(filename)) {
ResFileMap::iterator oldParent = _map.find(iter->_value.parent);
@@ -349,7 +359,17 @@ bool Resource::loadFileToBuf(const char *file, void *buf, uint32 maxSize) {
Common::SeekableReadStream *Resource::getFileStream(const Common::String &file) {
CompFileMap::iterator compEntry;
- if (Common::File::exists(file)) {
+ if ((compEntry = _compFiles.find(file)) != _compFiles.end())
+ return new Common::MemoryReadStream(compEntry->_value.data, compEntry->_value.size, false);
+
+ if (!isAccessable(file))
+ return 0;
+
+ ResFileMap::const_iterator iter = _map.find(file);
+ if (iter == _map.end())
+ return 0;
+
+ if (iter->_value.parent.empty()) {
Common::File *stream = new Common::File();
if (!stream->open(file)) {
delete stream;
@@ -357,28 +377,15 @@ Common::SeekableReadStream *Resource::getFileStream(const Common::String &file)
error("Couldn't open file '%s'", file.c_str());
}
return stream;
- } else if ((compEntry = _compFiles.find(file)) != _compFiles.end()) {
- return new Common::MemoryReadStream(compEntry->_value.data, compEntry->_value.size, false);
} else {
- if (!isAccessable(file))
- return 0;
-
- ResFileMap::const_iterator iter = _map.find(file);
- if (iter == _map.end())
- return 0;
-
- if (!iter->_value.parent.empty()) {
- Common::SeekableReadStream *parent = getFileStream(iter->_value.parent);
- assert(parent);
+ Common::SeekableReadStream *parent = getFileStream(iter->_value.parent);
+ assert(parent);
- ResFileMap::const_iterator parentIter = _map.find(iter->_value.parent);
- const ResArchiveLoader *loader = getLoader(parentIter->_value.type);
- assert(loader);
+ ResFileMap::const_iterator parentIter = _map.find(iter->_value.parent);
+ const ResArchiveLoader *loader = getLoader(parentIter->_value.type);
+ assert(loader);
- return loader->loadFileFromArchive(file, parent, iter->_value);
- } else {
- error("Couldn't open file '%s'", file.c_str());
- }
+ return loader->loadFileFromArchive(file, parent, iter->_value);
}
return 0;
@@ -410,7 +417,19 @@ void Resource::checkFile(const Common::String &file) {
if (_map.find(file) == _map.end()) {
CompFileMap::const_iterator iter;
- if (Common::File::exists(file)) {
+ if ((iter = _compFiles.find(file)) != _compFiles.end()) {
+ ResFileEntry entry;
+ entry.parent = "";
+ entry.size = iter->_value.size;
+ entry.mounted = false;
+ entry.preload = false;
+ entry.prot = false;
+ entry.type = ResFileEntry::kAutoDetect;
+ entry.offset = 0;
+ _map[file] = entry;
+
+ detectFileTypes();
+ } else if (Common::File::exists(file)) {
Common::File temp;
if (temp.open(file)) {
ResFileEntry entry;
@@ -426,18 +445,6 @@ void Resource::checkFile(const Common::String &file) {
detectFileTypes();
}
- } else if ((iter = _compFiles.find(file)) != _compFiles.end()) {
- ResFileEntry entry;
- entry.parent = "";
- entry.size = iter->_value.size;
- entry.mounted = false;
- entry.preload = false;
- entry.prot = false;
- entry.type = ResFileEntry::kAutoDetect;
- entry.offset = 0;
- _map[file] = entry;
-
- detectFileTypes();
}
}
}
@@ -1256,10 +1263,17 @@ uint8 FileExpander::calcCmdAndIndex(const uint8 *tbl, int16 &para) {
class CompLoaderInsHof : public CompArchiveLoader {
public:
- bool checkForFiles() const;
- bool loadFile(CompFileMap &loadTo) const;
+ CompLoaderInsHof() {
+ _fileExtP = "%03d";
+ _checkFile1 = "WESTWOOD.001";
+ _checkFile2 = "WESTWOOD.002";
+ _containerOffset = 6;
+ }
-private:
+ virtual bool checkForFiles() const;
+ virtual bool loadFile(CompFileMap &loadTo) const;
+
+protected:
struct Archive {
Common::String filename;
uint32 firstFile;
@@ -1268,10 +1282,25 @@ private:
uint32 endOffset;
uint32 totalSize;
};
+
+ const char *_fileExtP;
+ const char *_checkFile1;
+ const char *_checkFile2;
+ uint8 _containerOffset;
+};
+
+class CompLoaderInsLol : public CompLoaderInsHof {
+public:
+ CompLoaderInsLol() {
+ _fileExtP = "%d";
+ _checkFile1 = "WESTWOOD.1";
+ _checkFile2 = "WESTWOOD.2";
+ _containerOffset = 0;
+ }
};
bool CompLoaderInsHof::checkForFiles() const {
- return (Common::File::exists("WESTWOOD.001") && Common::File::exists("WESTWOOD.002"));
+ return (Common::File::exists(_checkFile1) && Common::File::exists(_checkFile2));
}
bool CompLoaderInsHof::loadFile(CompFileMap &loadTo) const {
@@ -1293,7 +1322,7 @@ bool CompLoaderInsHof::loadFile(CompFileMap &loadTo) const {
Common::List<Archive> archives;
for (int8 currentFile = 1; currentFile; currentFile++) {
- sprintf(filenameExt, "%03d", currentFile);
+ sprintf(filenameExt, _fileExtP, currentFile);
filenameTemp = filenameBase + Common::String(filenameExt);
if (!tmpFile.open(filenameTemp)) {
@@ -1309,9 +1338,9 @@ bool CompLoaderInsHof::loadFile(CompFileMap &loadTo) const {
if (startFile) {
size -= 4;
if (fileId == currentFile) {
- size -= 6;
- pos += 6;
- tmpFile.seek(6, SEEK_CUR);
+ size -= _containerOffset;
+ pos += _containerOffset;
+ tmpFile.seek(_containerOffset, SEEK_CUR);
} else {
size = size + 1 - pos;
}
@@ -1355,6 +1384,7 @@ bool CompLoaderInsHof::loadFile(CompFileMap &loadTo) const {
uint8 *outbuffer = 0;
uint32 inPart1 = 0;
uint32 inPart2 = 0;
+ uint8 compressionType = 0;
Common::String entryStr;
pos = 0;
@@ -1366,7 +1396,7 @@ bool CompLoaderInsHof::loadFile(CompFileMap &loadTo) const {
for (Common::List<Archive>::iterator a = archives.begin(); a != archives.end(); ++a) {
startFile = true;
for (uint32 i = a->firstFile; i != (a->lastFile + 1); i++) {
- sprintf(filenameExt, "%03d", i);
+ sprintf(filenameExt, _fileExtP, i);
filenameTemp = a->filename + Common::String(filenameExt);
if (!tmpFile.open(filenameTemp)) {
@@ -1389,7 +1419,12 @@ bool CompLoaderInsHof::loadFile(CompFileMap &loadTo) const {
tmpFile.seek(1);
tmpFile.read(inbuffer + inPart1, inPart2);
inPart2 = 0;
- exp.process(outbuffer, inbuffer, outsize, insize);
+
+ if (compressionType > 0)
+ exp.process(outbuffer, inbuffer, outsize, insize);
+ else
+ memcpy(outbuffer, inbuffer, outsize);
+
delete[] inbuffer;
inbuffer = 0;
newEntry.data = outbuffer;
@@ -1418,7 +1453,7 @@ bool CompLoaderInsHof::loadFile(CompFileMap &loadTo) const {
}
}
- sprintf(filenameExt, "%03d", i + 1);
+ sprintf(filenameExt, _fileExtP, i + 1);
filenameTemp = a->filename + Common::String(filenameExt);
Common::File tmpFile2;
@@ -1434,8 +1469,7 @@ bool CompLoaderInsHof::loadFile(CompFileMap &loadTo) const {
uint32 id = READ_LE_UINT32(hdr);
if (id == 0x04034B50) {
- if (hdr[8] != 8)
- error("compression type not implemented");
+ compressionType = hdr[8];
insize = READ_LE_UINT32(hdr + 18);
outsize = READ_LE_UINT32(hdr + 22);
@@ -1463,7 +1497,12 @@ bool CompLoaderInsHof::loadFile(CompFileMap &loadTo) const {
} else {
tmpFile.read(inbuffer, insize);
inPart2 = 0;
- exp.process(outbuffer, inbuffer, outsize, insize);
+
+ if (compressionType > 0)
+ exp.process(outbuffer, inbuffer, outsize, insize);
+ else
+ memcpy(outbuffer, inbuffer, outsize);
+
delete[] inbuffer;
inbuffer = 0;
newEntry.data = outbuffer;
@@ -1497,6 +1536,7 @@ void Resource::initializeLoaders() {
_loaders.push_back(LoaderList::value_type(new ResLoaderTlk()));
_compLoaders.push_back(CompLoaderList::value_type(new CompLoaderInsHof()));
+ _compLoaders.push_back(CompLoaderList::value_type(new CompLoaderInsLol()));
}
const ResArchiveLoader *Resource::getLoader(ResFileEntry::kType type) const {
diff --git a/engines/kyra/saveload.cpp b/engines/kyra/saveload.cpp
index 22f934ba69..31c5e15fa6 100644
--- a/engines/kyra/saveload.cpp
+++ b/engines/kyra/saveload.cpp
@@ -37,7 +37,7 @@
namespace Kyra {
-KyraEngine_v1::kReadSaveHeaderError KyraEngine_v1::readSaveHeader(Common::InSaveFile *in, SaveHeader &header) {
+KyraEngine_v1::kReadSaveHeaderError KyraEngine_v1::readSaveHeader(Common::SeekableReadStream *in, SaveHeader &header) {
uint32 type = in->readUint32BE();
header.originalSave = false;
header.oldHeader = false;
@@ -95,6 +95,9 @@ KyraEngine_v1::kReadSaveHeaderError KyraEngine_v1::readSaveHeader(Common::InSave
if (header.version <= 8) {
char buffer[31];
in->read(buffer, 31);
+ // WORKAROUND: Old savegames could contain a missing termination 0 at the
+ // end so we manually add it.
+ buffer[30] = 0;
header.description = buffer;
} else {
header.description = "";
@@ -108,10 +111,10 @@ KyraEngine_v1::kReadSaveHeaderError KyraEngine_v1::readSaveHeader(Common::InSave
return (in->ioFailed() ? kRSHEIoError : kRSHENoError);
}
-Common::InSaveFile *KyraEngine_v1::openSaveForReading(const char *filename, SaveHeader &header) {
+Common::SeekableReadStream *KyraEngine_v1::openSaveForReading(const char *filename, SaveHeader &header) {
debugC(9, kDebugLevelMain, "KyraEngine_v1::openSaveForReading('%s', -)", filename);
- Common::InSaveFile *in = 0;
+ Common::SeekableReadStream *in = 0;
if (!(in = _saveFileMan->openForLoading(filename)))
return 0;
@@ -159,12 +162,12 @@ Common::InSaveFile *KyraEngine_v1::openSaveForReading(const char *filename, Save
return in;
}
-Common::OutSaveFile *KyraEngine_v1::openSaveForWriting(const char *filename, const char *saveName) const {
+Common::WriteStream *KyraEngine_v1::openSaveForWriting(const char *filename, const char *saveName) const {
debugC(9, kDebugLevelMain, "KyraEngine_v1::openSaveForWriting('%s', '%s')", filename, saveName);
if (_quitFlag)
return 0;
- Common::OutSaveFile *out = 0;
+ Common::WriteStream *out = 0;
if (!(out = _saveFileMan->openForSaving(filename))) {
warning("Can't create file '%s', game not saved", filename);
return 0;
@@ -209,7 +212,7 @@ bool KyraEngine_v1::saveFileLoadable(int slot) {
return false;
SaveHeader header;
- Common::InSaveFile *in = openSaveForReading(getSavegameFilename(slot), header);
+ Common::SeekableReadStream *in = openSaveForReading(getSavegameFilename(slot), header);
if (in) {
delete in;
diff --git a/engines/kyra/scene_mr.cpp b/engines/kyra/scene_mr.cpp
index e4a3a5c54e..53c0cb5380 100644
--- a/engines/kyra/scene_mr.cpp
+++ b/engines/kyra/scene_mr.cpp
@@ -378,10 +378,11 @@ void KyraEngine_MR::loadSceneMsc() {
_screen->loadBitmap(filename, 5, 5, 0, true);
// HACK
- uint8 data[320*200];
+ uint8 *data = new uint8[320*200];
_screen->copyRegionToBuffer(5, 0, 0, 320, 200, data);
_screen->clearPage(5);
_screen->copyBlockToPage(5, 0, _maskPageMinY, 320, height, data);
+ delete[] data;
musicUpdate(0);
}
diff --git a/engines/kyra/screen.cpp b/engines/kyra/screen.cpp
index 74f7bc6de9..0cde066cc0 100644
--- a/engines/kyra/screen.cpp
+++ b/engines/kyra/screen.cpp
@@ -413,7 +413,9 @@ void Screen::fadePalette(const uint8 *palData, int delay, const UpdateFunctor *u
void Screen::getFadeParams(const uint8 *palette, int delay, int &delayInc, int &diff) {
debugC(9, kDebugLevelScreen, "Screen::getFadeParams(%p, %d, %p, %p)", (const void *)palette, delay, (const void *)&delayInc, (const void *)&diff);
uint8 maxDiff = 0;
- for (int i = 0; i < 768; ++i) {
+
+ const int colors = (_vm->gameFlags().platform == Common::kPlatformAmiga ? 32 : 256) * 3;
+ for (int i = 0; i < colors; ++i) {
diff = ABS(palette[i] - _screenPalette[i]);
maxDiff = MAX<uint8>(maxDiff, diff);
}
@@ -438,7 +440,8 @@ int Screen::fadePalStep(const uint8 *palette, int diff) {
memcpy(fadePal, _screenPalette, 768);
bool needRefresh = false;
- for (int i = 0; i < 768; ++i) {
+ const int colors = (_vm->gameFlags().platform == Common::kPlatformAmiga ? 32 : 256) * 3;
+ for (int i = 0; i < colors; ++i) {
int c1 = palette[i];
int c2 = fadePal[i];
if (c1 != c2) {
@@ -476,7 +479,7 @@ void Screen::setPaletteIndex(uint8 index, uint8 red, uint8 green, uint8 blue) {
void Screen::setScreenPalette(const uint8 *palData) {
debugC(9, kDebugLevelScreen, "Screen::setScreenPalette(%p)", (const void *)palData);
- int colors = (_vm->gameFlags().platform == Common::kPlatformAmiga ? 32 : 256);
+ const int colors = (_vm->gameFlags().platform == Common::kPlatformAmiga ? 32 : 256);
if (palData != _screenPalette)
memcpy(_screenPalette, palData, colors*3);
diff --git a/engines/kyra/script_tim.cpp b/engines/kyra/script_tim.cpp
index 7993fb8de6..9b215b2c03 100644
--- a/engines/kyra/script_tim.cpp
+++ b/engines/kyra/script_tim.cpp
@@ -559,15 +559,7 @@ int TIMInterpreter::cmd_playVocFile(const uint16 *param) {
int TIMInterpreter::cmd_loadSoundFile(const uint16 *param) {
const char *file = (const char *)(_currentTim->text + READ_LE_UINT16(_currentTim->text + (param[0]<<1)));
-
- static char * fileList[] = { 0 };
- fileList[0] = _audioFilename;
- static AudioDataStruct audioList = { fileList, 1, 0, 0 };
-
- strncpy(_audioFilename, file, sizeof(_audioFilename));
-
- _vm->sound()->setSoundList(&audioList);
- _vm->sound()->loadSoundFile(0);
+ _vm->sound()->loadSoundFile(file);
return 1;
}
diff --git a/engines/kyra/sequences_hof.cpp b/engines/kyra/sequences_hof.cpp
index 169c319347..635db3629c 100644
--- a/engines/kyra/sequences_hof.cpp
+++ b/engines/kyra/sequences_hof.cpp
@@ -50,7 +50,7 @@ void KyraEngine_HoF::seq_playSequences(int startSeq, int endSeq) {
_sound->setSoundList(&_soundData[(startSeq > kSequenceZanfaun) ? kMusicFinale : kMusicIntro]);
_sound->loadSoundFile(0);
- _screen->_charWidth = -2;
+ _screen->_charWidth = (_flags.gameID == GI_LOL) ? 0 : -2;
memset(_activeWSA, 0, sizeof(ActiveWSA) * 8);
for (int i = 0; i < 8; ++i)
@@ -300,8 +300,8 @@ void KyraEngine_HoF::seq_playSequences(int startSeq, int endSeq) {
_eventList.clear();
seqNum = kSequenceFirates;
}
- } else if (seqNum == kSequenceDemoFisher && !(_abortIntroFlag || skipFlag())) {
- seqNum = kSequenceDemoVirgin;
+ } else if (seqNum == endSeq && !(_abortIntroFlag || skipFlag())) {
+ seqNum = 0;
}
if (_menuChoice) {
@@ -1722,7 +1722,7 @@ int KyraEngine_HoF::seq_demoFisher(WSAMovie_v2 *wsaObj, int x, int y, int frm) {
_seqScrollTextCounter = 0;
}
- seq_scrollPage();
+ seq_scrollPage(24, 144);
_seqFrameCounter++;
if (_seqFrameCounter < 0x256 || _seqFrameCounter > 0x31c) {
if (_seqFrameCounter < 0x174 || _seqFrameCounter > 0x1d7) {
@@ -1740,7 +1740,7 @@ int KyraEngine_HoF::seq_demoFisher(WSAMovie_v2 *wsaObj, int x, int y, int frm) {
}
} else {
- seq_scrollPage();
+ seq_scrollPage(24, 144);
}
return 0;
}
@@ -1796,6 +1796,182 @@ int KyraEngine_HoF::seq_demoDig(WSAMovie_v2 *wsaObj, int x, int y, int frm) {
return frm;
}
+int KyraEngine_HoF::seq_lolDemoScene1(WSAMovie_v2 *wsaObj, int x, int y, int frm) {
+ uint8 *tmpPal = _screen->getPalette(2);
+
+ if (!(_seqFrameCounter % 100)) {
+ if (_seqFrameCounter == 0) {
+ _sound->haltTrack();
+ _sound->playTrack(6);
+ }
+ memcpy(tmpPal, _screen->getPalette(0), 0x300);
+ for (int i = 3; i < 0x300; i++) {
+ tmpPal[i] = ((int)tmpPal[i] * 120) / 64;
+ if (tmpPal[i] > 0x3f)
+ tmpPal[i] = 0x3f;
+ }
+ seq_playTalkText(_rnd.getRandomBit());
+ _screen->setScreenPalette(tmpPal);
+ _screen->updateScreen();
+ delay(8);
+ } else {
+ _screen->setScreenPalette(_screen->getPalette(0));
+ _screen->updateScreen();
+ if (_seqFrameCounter == 40)
+ seq_playTalkText(3);
+ }
+
+ _seqFrameCounter++;
+ return frm;
+}
+
+int KyraEngine_HoF::seq_lolDemoScene2(WSAMovie_v2 *wsaObj, int x, int y, int frm) {
+ switch (_seqFrameCounter - 17) {
+ case 0:
+ _seqFrameDelay = 8;
+ break;
+ case 3:
+ case 6:
+ case 9:
+ seq_playTalkText(8);
+ break;
+ case 15:
+ seq_playTalkText(9);
+ break;
+ case 18:
+ seq_playTalkText(2);
+ break;
+ default:
+ break;
+ }
+ _seqFrameCounter++;
+ return frm;
+}
+
+int KyraEngine_HoF::seq_lolDemoScene3(WSAMovie_v2 *wsaObj, int x, int y, int frm) {
+ if (_seqFrameCounter == 1)
+ seq_playTalkText(6);
+ else if (frm == 26)
+ seq_playTalkText(7);
+
+ _seqFrameCounter++;
+ return frm;
+}
+
+int KyraEngine_HoF::seq_lolDemoScene4(WSAMovie_v2 *wsaObj, int x, int y, int frm) {
+ switch (_seqFrameCounter) {
+ case 11:
+ case 14:
+ case 17:
+ case 20:
+ seq_playTalkText(8);
+ break;
+ case 22:
+ seq_playTalkText(11);
+ break;
+ case 24:
+ seq_playTalkText(8);
+ break;
+ case 30:
+ seq_playTalkText(15);
+ break;
+ case 34:
+ seq_playTalkText(14);
+ break;
+ case 38:
+ seq_playTalkText(13);
+ break;
+ case 42:
+ seq_playTalkText(12);
+ break;
+ default:
+ break;
+ }
+
+ _seqFrameCounter++;
+ return frm;
+}
+
+int KyraEngine_HoF::seq_lolDemoScene5(WSAMovie_v2 *wsaObj, int x, int y, int frm) {
+ switch (_seqFrameCounter++) {
+ case 0:
+ case 4:
+ case 6:
+ case 8:
+ case 10:
+ case 14:
+ case 16:
+ case 18:
+ case 20:
+ case 22:
+ case 24:
+ case 26:
+ case 28:
+ case 30:
+ seq_playTalkText(15);
+ break;
+ case 32:
+ seq_playTalkText(16);
+ break;
+ case 42:
+ seq_playTalkText(6);
+ break;
+ default:
+ break;
+ }
+ return frm;
+}
+
+int KyraEngine_HoF::seq_lolDemoText5(WSAMovie_v2 *wsaObj, int x, int y, int frm) {
+ if (_seqFrameCounter++ == 100)
+ seq_playTalkText(5);
+ return frm;
+}
+
+int KyraEngine_HoF::seq_lolDemoScene6(WSAMovie_v2 *wsaObj, int x, int y, int frm) {
+ while (_seqScrollTextCounter < 0x122) {
+ _seqEndTime = _system->getMillis() + 6 * _tickLength;
+ if (!_seqFrameCounter) {
+ _screen->loadBitmap("adtext.cps", 4, 4, 0);
+ _screen->loadBitmap("adtext2.cps", 6, 6, 0);
+ _screen->copyPageMemory(6, 0, 4, 64000, 1024);
+ _screen->copyPageMemory(6, 1023, 6, 0, 64000);
+ _seqScrollTextCounter = 0;
+ }
+
+ if (_seqFrameCounter % 175) {
+ _screen->setScreenPalette(_screen->getPalette(0));
+ } else {
+ uint8 *tmpPal = _screen->getPalette(2);
+ memcpy(tmpPal, _screen->getPalette(0), 0x300);
+ for (int i = 3; i < 0x300; i++) {
+ tmpPal[i] = ((int)tmpPal[i] * 120) / 64;
+ if (tmpPal[i] > 0x3f)
+ tmpPal[i] = 0x3f;
+ }
+ seq_playTalkText(_rnd.getRandomBit());
+ _screen->setScreenPalette(tmpPal);
+ _screen->updateScreen();
+ delay(8);
+ }
+
+ if (_seqFrameCounter == 40 || _seqFrameCounter == 80 || _seqFrameCounter == 150 || _seqFrameCounter == 300)
+ seq_playTalkText(3);
+
+ _screen->copyPage(12, 2);
+ seq_scrollPage(70, 130);
+ _screen->copyPage(2, 0);
+ _screen->updateScreen();
+ _seqFrameCounter++;
+ if (_seqFrameCounter < 128 || _seqFrameCounter > 207)
+ _seqScrollTextCounter++;
+ delayUntil(_seqEndTime);
+ }
+ _screen->copyPage(2, 12);
+
+ return 0;
+}
+
uint32 KyraEngine_HoF::seq_activeTextsTimeLeft() {
uint32 res = 0;
@@ -1892,16 +2068,14 @@ void KyraEngine_HoF::seq_sequenceCommand(int command) {
switch (command) {
case 0:
memset(pal, 0, 0x300);
- _screen->fadePalette(pal, 16);
+ _screen->fadePalette(pal, 36);
memcpy (_screen->getPalette(0), pal, 0x300);
memcpy (_screen->getPalette(1), pal, 0x300);
break;
case 1:
memset(pal, 0x3F, 0x300);
- //////////XXX
- //////////Unused anyway (at least by fm-towns intro/outro)
-
+ seq_playTalkText(_rnd.getRandomBit());
_screen->fadePalette(pal, 16);
memcpy (_screen->getPalette(0), pal, 0x300);
memcpy (_screen->getPalette(1), pal, 0x300);
@@ -2575,32 +2749,34 @@ void KyraEngine_HoF::seq_displayScrollText(uint8 *data, const ScreenDim *d, int
delete[] textData;
}
-void KyraEngine_HoF::seq_scrollPage() {
+void KyraEngine_HoF::seq_scrollPage(int bottom, int top) {
int dstY, dstH, srcH;
static const ScreenDim d = { 0x00, 0x00, 0x28, 0x320, 0xFF, 0xFE, 0x00, 0x00 };
- if (_seqScrollTextCounter - 143 < 0) {
- dstY = 144 - _seqScrollTextCounter;
+ if (_seqScrollTextCounter - (top - 1) < 0) {
+ dstY = top - _seqScrollTextCounter;
dstH = _seqScrollTextCounter;
srcH = 0;
} else {
dstY = 0;
- srcH = _seqScrollTextCounter - 144;
- dstH = (400 - srcH <= 144) ? 400 - srcH : 144;
+ srcH = _seqScrollTextCounter - top;
+ dstH = (400 - srcH <= top) ? 400 - srcH : top;
}
if (dstH > 0) {
- for (int i = 0; i < 4; i++) {
- const ItemAnimData_v1 *def = &_demoAnimData[i];
- ActiveItemAnim *a = &_activeItemAnim[i];
-
- _screen->fillRect(12, def->y - 8, 28, def->y + 8, 0, 4);
- _screen->drawShape(4, getShapePtr(def->itemIndex + def->frames[a->currentFrame]), 12, def->y - 8, 0, 0);
- if(_seqFrameCounter % 2 == 0)
- a->currentFrame = ++a->currentFrame % 20;
+ if (_demoAnimData) {
+ for (int i = 0; i < 4; i++) {
+ const ItemAnimData_v1 *def = &_demoAnimData[i];
+ ActiveItemAnim *a = &_activeItemAnim[i];
+
+ _screen->fillRect(12, def->y - 8, 28, def->y + 8, 0, 4);
+ _screen->drawShape(4, getShapePtr(def->itemIndex + def->frames[a->currentFrame]), 12, def->y - 8, 0, 0);
+ if(_seqFrameCounter % 2 == 0)
+ a->currentFrame = ++a->currentFrame % 20;
+ }
}
- _screen->copyRegionEx(4, 0, srcH, 2, 2, dstY + 24, 320, dstH, &d);
+ _screen->copyRegionEx(4, 0, srcH, 2, 2, dstY + bottom, 320, dstH, &d);
}
}
@@ -2656,6 +2832,10 @@ void KyraEngine_HoF::seq_init() {
_res->loadFileList(_sequencePakList, _sequencePakListSize);
int numShp = -1;
+
+ if (_flags.gameID == GI_LOL)
+ return;
+
if (_flags.isDemo && !_flags.isTalkie) {
_demoAnimData = _staticres->loadShapeAnimData_v1(k2SeqplayShapeAnimData, _itemAnimDataSize);
uint8 *shp = _res->fileData("icons.shp", 0);
diff --git a/engines/kyra/sound.cpp b/engines/kyra/sound.cpp
index c8749dc06b..073639e4ca 100644
--- a/engines/kyra/sound.cpp
+++ b/engines/kyra/sound.cpp
@@ -104,6 +104,11 @@ int32 Sound::voicePlay(const char *file, bool isSfx) {
fileSize = 0;
}
+ if (!audioStream) {
+ warning("Couldn't load sound file '%s'", file);
+ return 0;
+ }
+
_soundChannels[h].file = file;
_mixer->playInputStream(isSfx ? Audio::Mixer::kSFXSoundType : Audio::Mixer::kSpeechSoundType, &_soundChannels[h].channelHandle, audioStream);
@@ -323,7 +328,17 @@ struct DeleterArray {
void SoundMidiPC::loadSoundFile(uint file) {
Common::StackLock lock(_mutex);
- Common::String filename = fileListEntry(file);
+ internalLoadFile(fileListEntry(file));
+}
+
+void SoundMidiPC::loadSoundFile(Common::String file) {
+ Common::StackLock lock(_mutex);
+
+ internalLoadFile(file);
+}
+
+void SoundMidiPC::internalLoadFile(Common::String file) {
+ Common::String filename = file;
filename += ".";
filename += _useC55 ? "C55" : "XMI";
diff --git a/engines/kyra/sound.h b/engines/kyra/sound.h
index cebfdf491f..e5294eb15d 100644
--- a/engines/kyra/sound.h
+++ b/engines/kyra/sound.h
@@ -120,6 +120,12 @@ public:
virtual void loadSoundFile(uint file) = 0;
/**
+ * Load a sound file for playing music
+ * and sound effects from.
+ */
+ virtual void loadSoundFile(Common::String file) = 0;
+
+ /**
* Plays the specified track.
*
* @param track track number
@@ -215,8 +221,6 @@ protected:
int _musicEnabled;
bool _sfxEnabled;
- int _currentTheme;
-
KyraEngine_v1 *_vm;
Audio::Mixer *_mixer;
@@ -260,6 +264,7 @@ public:
void process();
void loadSoundFile(uint file);
+ void loadSoundFile(Common::String file);
void playTrack(uint8 track);
void haltTrack();
@@ -269,6 +274,8 @@ public:
void beginFadeOut();
private:
+ void internalLoadFile(Common::String file);
+
void play(uint8 track);
void unk1();
@@ -280,7 +287,8 @@ private:
uint8 _trackEntries[500];
uint8 *_soundDataPtr;
int _sfxPlayingSound;
- uint _soundFileLoaded;
+
+ Common::String _soundFileLoaded;
uint8 _sfxPriority;
uint8 _sfxFourthByteOfSong;
@@ -316,6 +324,7 @@ public:
void updateVolumeSettings();
void loadSoundFile(uint file);
+ void loadSoundFile(Common::String file);
void playTrack(uint8 track);
void haltTrack();
@@ -343,6 +352,7 @@ public:
bool isMT32() const { return _nativeMT32; }
private:
+ void internalLoadFile(Common::String file);
void updateChannelVolume(uint8 vol);
static void onTimer(void *data);
@@ -397,6 +407,7 @@ public:
void process();
void loadSoundFile(uint file);
+ void loadSoundFile(Common::String) {}
void playTrack(uint8 track);
void haltTrack();
@@ -454,6 +465,7 @@ public:
void process() {}
void loadSoundFile(uint file) {}
+ void loadSoundFile(Common::String) {}
void playTrack(uint8 track);
void haltTrack();
@@ -480,6 +492,7 @@ public:
void process();
void loadSoundFile(uint file) {}
+ void loadSoundFile(Common::String) {}
void playTrack(uint8 track);
void haltTrack();
@@ -513,6 +526,7 @@ public:
void setSoundList(const AudioDataStruct * list) { _music->setSoundList(list); _sfx->setSoundList(list); }
bool hasSoundFile(uint file) const { return _music->hasSoundFile(file) && _sfx->hasSoundFile(file); }
void loadSoundFile(uint file) { _music->loadSoundFile(file); _sfx->loadSoundFile(file); }
+ void loadSoundFile(Common::String file) { _music->loadSoundFile(file); _sfx->loadSoundFile(file); }
void playTrack(uint8 track) { _music->playTrack(track); }
void haltTrack() { _music->haltTrack(); }
diff --git a/engines/kyra/sound_adlib.cpp b/engines/kyra/sound_adlib.cpp
index 0ceb288b8a..62551d2b09 100644
--- a/engines/kyra/sound_adlib.cpp
+++ b/engines/kyra/sound_adlib.cpp
@@ -2225,7 +2225,7 @@ SoundAdlibPC::SoundAdlibPC(KyraEngine_v1 *vm, Audio::Mixer *mixer)
assert(_driver);
_sfxPlayingSound = -1;
- _soundFileLoaded = (uint)-1;
+ _soundFileLoaded.clear();
if (_v2) {
// TODO: Figure out if Kyra 2 uses sound triggers at all.
@@ -2269,7 +2269,7 @@ void SoundAdlibPC::playTrack(uint8 track) {
// sync for each loop. To avoid that, we declare that all four
// of the song channels have to jump "in sync".
- if (track == 4 && scumm_stricmp(fileListEntry(_soundFileLoaded), "KYRA1B") == 0)
+ if (track == 4 && _soundFileLoaded.equalsIgnoreCase("KYRA1B.ADL"))
_driver->setSyncJumpMask(0x000F);
else
_driver->setSyncJumpMask(0);
@@ -2348,6 +2348,15 @@ void SoundAdlibPC::beginFadeOut() {
}
void SoundAdlibPC::loadSoundFile(uint file) {
+ internalLoadFile(fileListEntry(file));
+}
+
+void SoundAdlibPC::loadSoundFile(Common::String file) {
+ internalLoadFile(file);
+}
+
+void SoundAdlibPC::internalLoadFile(Common::String file) {
+ file += ".ADL";
if (_soundFileLoaded == file)
return;
@@ -2356,12 +2365,9 @@ void SoundAdlibPC::loadSoundFile(uint file) {
uint8 *file_data = 0; uint32 file_size = 0;
- char filename[25];
- sprintf(filename, "%s.ADL", fileListEntry(file));
-
- file_data = _vm->resource()->fileData(filename, &file_size);
+ file_data = _vm->resource()->fileData(file.c_str(), &file_size);
if (!file_data) {
- warning("Couldn't find music file: '%s'", filename);
+ warning("Couldn't find music file: '%s'", file.c_str());
return;
}
diff --git a/engines/kyra/sound_towns.cpp b/engines/kyra/sound_towns.cpp
index 0f2b916c9d..ec1962a58f 100644
--- a/engines/kyra/sound_towns.cpp
+++ b/engines/kyra/sound_towns.cpp
@@ -2363,7 +2363,7 @@ TownsPC98_OpnDriver::TownsPC98_OpnDriver(Audio::Mixer *mixer, OpnType type) :
_numSSG(type == OD_TOWNS ? 0 : 3), _hasADPCM(type == OD_TYPE86 ? true : false),
_numChan(type == OD_TYPE26 ? 3 : 6), _hasStereo(type == OD_TYPE26 ? false : true) {
setTempo(84);
- _baserate = (double)getRate() / 10368.0;
+ _baserate = (486202500.0 / (double)getRate()) / 10368.0;
}
TownsPC98_OpnDriver::~TownsPC98_OpnDriver() {
diff --git a/engines/kyra/staticres.cpp b/engines/kyra/staticres.cpp
index 9a4b40902e..bb63c24c36 100644
--- a/engines/kyra/staticres.cpp
+++ b/engines/kyra/staticres.cpp
@@ -43,7 +43,7 @@
namespace Kyra {
-#define RESFILE_VERSION 28
+#define RESFILE_VERSION 31
bool StaticResource::checkKyraDat() {
Common::File kyraDat;
@@ -279,6 +279,16 @@ bool StaticResource::init() {
{ 0, 0, 0 }
};
+ static const FilenameTable lolStaticRes[] = {
+ // Demo Sequence Player
+ { k2SeqplayPakFiles, kStringList, "S_PAKFILES.TXT" },
+ { k2SeqplayStrings, kLanguageList, "S_STRINGS." },
+ { k2SeqplaySfxFiles, kStringList, "S_SFXFILES.TXT" },
+ { k2SeqplaySeqData, k2SeqData, "S_DATA.SEQ" },
+ { k2SeqplayIntroTracks, kStringList, "S_INTRO.TRA" },
+ { 0, 0, 0 }
+ };
+
if (_vm->game() == GI_KYRA1) {
_builtIn = 0;
_filenameTable = kyra1StaticRes;
@@ -289,7 +299,10 @@ bool StaticResource::init() {
_builtIn = 0;
_filenameTable = kyra3StaticRes;
} else if (_vm->game() == GI_LOL) {
- return true;
+ if (!_vm->gameFlags().isDemo)
+ return true;
+ _builtIn = 0;
+ _filenameTable = lolStaticRes;
} else {
error("StaticResource: Unknown game ID");
}
@@ -920,6 +933,8 @@ const char *StaticResource::getFilename(const char *name) {
filename += ".K2";
else if (_vm->gameFlags().gameID == GI_KYRA3)
filename += ".K3";
+ else if (_vm->gameFlags().gameID == GI_LOL)
+ filename += ".LOL";
if (_vm->gameFlags().isTalkie && _vm->gameFlags().gameID != GI_KYRA3)
filename += ".CD";
@@ -1037,38 +1052,51 @@ void KyraEngine_LoK::initStaticResource() {
}
// audio data tables
-#if 0
static const char *tIntro98[] = { "intro%d.dat" };
static const char *tIngame98[] = { "kyram%d.dat" };
-#endif
-
- static const AudioDataStruct soundData_PC[] = {
- { _soundFilesIntro, _soundFilesIntroSize, 0, 0 },
- { _soundFiles, _soundFilesSize, 0, 0 },
- { 0, 0, 0, 0}
- };
-
- static const AudioDataStruct soundData_TOWNS[] = {
- { _soundFiles, _soundFilesSize, _cdaTrackTable, _cdaTrackTableSize },
- { _soundFiles, _soundFilesSize, _cdaTrackTable, _cdaTrackTableSize },
- { 0, 0, 0, 0}
- };
-
-#if 0
- static const AudioDataStruct soundData_PC98[] = {
- { tIntro98, 1, 0, 0 },
- { tIngame98, 1, 0, 0 },
- { 0, 0, 0, 0}
- };
-#endif
-
- if (_flags.platform == Common::kPlatformPC)
- _soundData = soundData_PC;
- else if (_flags.platform == Common::kPlatformFMTowns)
- _soundData = soundData_TOWNS;
- else if (_flags.platform == Common::kPlatformPC98)
- _soundData = soundData_TOWNS/*soundData_PC98*/;
+ if (_flags.platform == Common::kPlatformPC) {
+ _soundData[0]._fileList = _soundFilesIntro;
+ _soundData[0]._fileListLen = _soundFilesIntroSize;
+ _soundData[0]._cdaTracks = 0;
+ _soundData[0]._cdaNumTracks = 0;
+ _soundData[1]._fileList = _soundFiles;
+ _soundData[1]._fileListLen = _soundFilesSize;
+ _soundData[1]._cdaTracks = 0;
+ _soundData[1]._cdaNumTracks = 0;
+ _soundData[2]._fileList = 0;
+ _soundData[2]._fileListLen = 0;
+ _soundData[2]._cdaTracks = 0;
+ _soundData[2]._cdaNumTracks = 0;
+ } else if (_flags.platform == Common::kPlatformFMTowns) {
+ _soundData[0]._fileList = _soundFiles;
+ _soundData[0]._fileListLen = _soundFilesSize;
+ _soundData[0]._cdaTracks = _cdaTrackTable;
+ _soundData[0]._cdaNumTracks = _cdaTrackTableSize;
+ _soundData[1]._fileList = _soundFiles;
+ _soundData[1]._fileListLen = _soundFilesSize;
+ _soundData[1]._cdaTracks = _cdaTrackTable;
+ _soundData[1]._cdaNumTracks = _cdaTrackTableSize;
+ _soundData[2]._fileList = 0;
+ _soundData[2]._fileListLen = 0;
+ _soundData[2]._cdaTracks = 0;
+ _soundData[2]._cdaNumTracks = 0;
+ } else if (_flags.platform == Common::kPlatformPC98) {
+ _soundData[0]._fileList = tIntro98;
+ _soundData[0]._fileListLen = 1;
+ _soundData[0]._cdaTracks = 0;
+ _soundData[0]._cdaNumTracks = 0;
+ _soundData[1]._fileList = tIngame98;
+ _soundData[1]._fileListLen = 1;
+ _soundData[1]._cdaTracks = 0;
+ _soundData[1]._cdaNumTracks = 0;
+ _soundData[2]._fileList = 0;
+ _soundData[2]._fileListLen = 0;
+ _soundData[2]._cdaTracks = 0;
+ _soundData[2]._cdaNumTracks = 0;
+ } else {
+ memset(_soundData, 0, sizeof(_soundData));
+ }
}
void KyraEngine_LoK::loadMouseShapes() {
@@ -1266,38 +1294,50 @@ void KyraEngine_HoF::initStaticResource() {
static const char *fmtMusicFileListFinale[] = { "finale%d.twn" };
static const char *fmtMusicFileListIngame[] = { "km%02d.twn" };
-#if 0
static const char *pc98MusicFileListIntro[] = { "intro%d.86" };
static const char *pc98MusicFileListFinale[] = { "finale%d.86" };
static const char *pc98MusicFileListIngame[] = { "km%02d.86" };
-#endif
-
- static const AudioDataStruct soundData_PC[] = {
- { _musicFileListIntro, _musicFileListIntroSize, 0, 0 },
- { _musicFileListIngame, _musicFileListIngameSize, 0, 0},
- { _musicFileListFinale, _musicFileListIntroSize, 0, 0 }
- };
- static const AudioDataStruct soundData_TOWNS[] = {
- { fmtMusicFileListIntro, 1, _cdaTrackTableIntro, _cdaTrackTableIntroSize >> 1 },
- { fmtMusicFileListIngame, 1, _cdaTrackTableIngame, _cdaTrackTableIngameSize >> 1 },
- { fmtMusicFileListFinale, 1, _cdaTrackTableFinale, _cdaTrackTableFinaleSize >> 1 }
- };
-
-#if 0
- static const AudioDataStruct soundData_PC98[] = {
- { pc98MusicFileListIntro, 1, 0, 0 },
- { pc98MusicFileListIngame, 1, 0, 0 },
- { pc98MusicFileListFinale, 1, 0, 0 }
- };
-#endif
-
- if (_flags.platform == Common::kPlatformPC)
- _soundData = soundData_PC;
- else if (_flags.platform == Common::kPlatformFMTowns)
- _soundData = soundData_TOWNS;
- else if (_flags.platform == Common::kPlatformPC98)
- _soundData = soundData_TOWNS/*soundData_PC98*/;
+ if (_flags.platform == Common::kPlatformPC) {
+ _soundData[0]._fileList = _musicFileListIntro;
+ _soundData[0]._fileListLen = _musicFileListIntroSize;
+ _soundData[0]._cdaTracks = 0;
+ _soundData[0]._cdaNumTracks = 0;
+ _soundData[1]._fileList = _musicFileListIngame;
+ _soundData[1]._fileListLen = _musicFileListIngameSize;
+ _soundData[1]._cdaTracks = 0;
+ _soundData[1]._cdaNumTracks = 0;
+ _soundData[2]._fileList = _musicFileListFinale;
+ _soundData[2]._fileListLen = _musicFileListIntroSize;
+ _soundData[2]._cdaTracks = 0;
+ _soundData[2]._cdaNumTracks = 0;
+ } else if (_flags.platform == Common::kPlatformFMTowns) {
+ _soundData[0]._fileList = fmtMusicFileListIntro;
+ _soundData[0]._fileListLen = 1;
+ _soundData[0]._cdaTracks = _cdaTrackTableIntro;
+ _soundData[0]._cdaNumTracks = _cdaTrackTableIntroSize >> 1;
+ _soundData[1]._fileList = fmtMusicFileListIngame;
+ _soundData[1]._fileListLen = 1;
+ _soundData[1]._cdaTracks = _cdaTrackTableIngame;
+ _soundData[1]._cdaNumTracks = _cdaTrackTableIngameSize >> 1;
+ _soundData[2]._fileList = fmtMusicFileListFinale;
+ _soundData[2]._fileListLen = 1;
+ _soundData[2]._cdaTracks = _cdaTrackTableFinale;
+ _soundData[2]._cdaNumTracks = _cdaTrackTableFinaleSize >> 1;
+ } else if (_flags.platform == Common::kPlatformPC98) {
+ _soundData[0]._fileList = pc98MusicFileListIntro;
+ _soundData[0]._fileListLen = 1;
+ _soundData[0]._cdaTracks = 0;
+ _soundData[0]._cdaNumTracks = 0;
+ _soundData[1]._fileList = pc98MusicFileListIngame;
+ _soundData[1]._fileListLen = 1;
+ _soundData[1]._cdaTracks = 0;
+ _soundData[1]._cdaNumTracks = 0;
+ _soundData[2]._fileList = pc98MusicFileListFinale;
+ _soundData[2]._fileListLen = 1;
+ _soundData[2]._cdaTracks = 0;
+ _soundData[2]._cdaNumTracks = 0;
+ }
// setup sequence data
_sequences = _staticres->loadHofSequenceData(k2SeqplaySeqData, tmpSize);
@@ -1336,8 +1376,17 @@ void KyraEngine_HoF::initStaticResource() {
&KyraEngine_HoF::seq_demoDig, 0
};
- _callbackS = (_flags.isDemo && !_flags.isTalkie) ? hofDemoSequenceCallbacks : hofSequenceCallbacks;
- _callbackN = (_flags.isDemo && !_flags.isTalkie) ? hofDemoNestedSequenceCallbacks : hofNestedSequenceCallbacks;
+ static const SeqProc lolDemoSequenceCallbacks[] = {
+ &KyraEngine_HoF::seq_lolDemoScene1, 0, &KyraEngine_HoF::seq_lolDemoScene2, 0,
+ &KyraEngine_HoF::seq_lolDemoScene3, 0, &KyraEngine_HoF::seq_lolDemoScene4, 0,
+ &KyraEngine_HoF::seq_lolDemoScene5, &KyraEngine_HoF::seq_lolDemoText5,
+ &KyraEngine_HoF::seq_lolDemoScene6, 0
+ };
+
+ static const SeqProc lolDemoNestedSequenceCallbacks[] = { 0 };
+
+ _callbackS = _flags.gameID == GI_LOL ? lolDemoSequenceCallbacks : ((_flags.isDemo && !_flags.isTalkie) ? hofDemoSequenceCallbacks : hofSequenceCallbacks);
+ _callbackN = _flags.gameID == GI_LOL ? lolDemoNestedSequenceCallbacks : ((_flags.isDemo && !_flags.isTalkie) ? hofDemoNestedSequenceCallbacks : hofNestedSequenceCallbacks);
}
void KyraEngine_MR::initStaticResource() {
diff --git a/engines/parallaction/balloons.cpp b/engines/parallaction/balloons.cpp
index 81b32adb15..222954ec3a 100644
--- a/engines/parallaction/balloons.cpp
+++ b/engines/parallaction/balloons.cpp
@@ -30,6 +30,183 @@
namespace Parallaction {
+class WrappedLineFormatter {
+
+protected:
+ Common::String _line;
+ Font *_font;
+ uint16 _lines, _lineWidth;
+
+ virtual void setup() = 0;
+ virtual void action() = 0;
+ virtual void end() = 0;
+ virtual Common::String expand(const Common::String &token) { return token; }
+
+ void textAccum(const Common::String &token, uint16 width) {
+ if (token.empty()) {
+ return;
+ }
+
+ _lineWidth += width;
+ _line += token;
+ }
+
+ void textNewLine() {
+ _lines++;
+ _lineWidth = 0;
+ _line.clear();
+ }
+
+public:
+ WrappedLineFormatter(Font *font) : _font(font) { }
+ virtual ~WrappedLineFormatter() { }
+
+ virtual void calc(const char *text, uint16 maxwidth) {
+ setup();
+
+ _lineWidth = 0;
+ _line.clear();
+ _lines = 0;
+
+ Common::StringTokenizer tokenizer(text, " ");
+ Common::String token;
+ Common::String blank(" ");
+
+ uint16 blankWidth = _font->getStringWidth(" ");
+ uint16 tokenWidth = 0;
+
+ while (!tokenizer.empty()) {
+ token = tokenizer.nextToken();
+ token = expand(token);
+
+ if (token == '/') {
+ tokenWidth = 0;
+ action();
+ textNewLine();
+ } else {
+ // todo: expand '%'
+ tokenWidth = _font->getStringWidth(token.c_str());
+
+ if (_lineWidth == 0) {
+ textAccum(token, tokenWidth);
+ } else {
+ if (_lineWidth + blankWidth + tokenWidth <= maxwidth) {
+ textAccum(blank, blankWidth);
+ textAccum(token, tokenWidth);
+ } else {
+ action();
+ textNewLine();
+ textAccum(token, tokenWidth);
+ }
+ }
+ }
+ }
+
+ end();
+ }
+};
+
+class StringExtent_NS : public WrappedLineFormatter {
+
+ uint _width, _height;
+
+protected:
+ virtual Common::String expand(const Common::String &token) {
+ if (token.compareToIgnoreCase("%p") == 0) {
+ return Common::String("/");
+ }
+
+ return token;
+ }
+
+ virtual void setup() {
+ _width = _height = 0;
+
+ _line.clear();
+ _lines = 0;
+ _width = 0;
+ }
+
+ virtual void action() {
+ if (_lineWidth > _width) {
+ _width = _lineWidth;
+ }
+ _height = _lines * _font->height();
+ }
+
+ virtual void end() {
+ action();
+ }
+
+public:
+ StringExtent_NS(Font *font) : WrappedLineFormatter(font) { }
+
+ uint width() const { return _width; }
+ uint height() const { return _height; }
+};
+
+
+class StringWriter_NS : public WrappedLineFormatter {
+
+ uint _width, _height;
+ byte _color;
+
+ Graphics::Surface *_surf;
+
+protected:
+ virtual Common::String expand(const Common::String& token) {
+ if (token.compareToIgnoreCase("%p") == 0) {
+ Common::String t(".......");
+ for (int i = 0; _password[i]; i++) {
+ t.setChar(_password[i], i);
+ }
+ return Common::String("> ") + t;
+ } else
+ if (token.compareToIgnoreCase("%s") == 0) {
+ char buf[20];
+ sprintf(buf, "%i", _score);
+ return Common::String(buf);
+ }
+
+ return token;
+ }
+
+ virtual void setup() {
+ }
+
+ virtual void action() {
+ if (_line.empty()) {
+ return;
+ }
+ uint16 rx = 10;
+ uint16 ry = 4 + _lines * _font->height(); // y
+
+ byte *dst = (byte*)_surf->getBasePtr(rx, ry);
+ _font->setColor(_color);
+ _font->drawString(dst, _surf->w, _line.c_str());
+ }
+
+ virtual void end() {
+ action();
+ }
+
+public:
+ StringWriter_NS(Font *font) : WrappedLineFormatter(font) { }
+
+ void write(const char *text, uint maxWidth, byte color, Graphics::Surface *surf) {
+ StringExtent_NS se(_font);
+ se.calc(text, maxWidth);
+ _width = se.width() + 10;
+ _height = se.height() + 20;
+ _color = color;
+ _surf = surf;
+
+ calc(text, maxWidth);
+ }
+
+};
+
+
#define BALLOON_TRANSPARENT_COLOR_NS 2
#define BALLOON_TRANSPARENT_COLOR_BR 0
@@ -78,8 +255,6 @@ class BalloonManager_ns : public BalloonManager {
uint _numBalloons;
- void getStringExtent(Font *font, char *text, uint16 maxwidth, int16* width, int16* height);
- void drawWrappedText(Font *font, Graphics::Surface* surf, char *text, byte color, int16 wrapwidth);
int createBalloon(int16 w, int16 h, int16 winding, uint16 borderThickness);
Balloon *getBalloon(uint id);
@@ -152,12 +327,16 @@ int BalloonManager_ns::setSingleBalloon(char *text, uint16 x, uint16 y, uint16 w
int16 w, h;
- getStringExtent(_vm->_dialogueFont, text, MAX_BALLOON_WIDTH, &w, &h);
+ StringExtent_NS se(_vm->_dialogueFont);
+ se.calc(text, MAX_BALLOON_WIDTH);
+ w = se.width() + 14;
+ h = se.height() + 20;
int id = createBalloon(w+5, h, winding, 1);
Balloon *balloon = &_intBalloons[id];
- drawWrappedText(_vm->_dialogueFont, balloon->surface, text, textColor, MAX_BALLOON_WIDTH);
+ StringWriter_NS sw(_vm->_dialogueFont);
+ sw.write(text, MAX_BALLOON_WIDTH, textColor, balloon->surface);
// TODO: extract some text to make a name for obj
balloon->obj = _gfx->registerBalloon(new SurfaceToFrames(balloon->surface), 0);
@@ -172,12 +351,17 @@ int BalloonManager_ns::setDialogueBalloon(char *text, uint16 winding, byte textC
int16 w, h;
- getStringExtent(_vm->_dialogueFont, text, MAX_BALLOON_WIDTH, &w, &h);
+ StringExtent_NS se(_vm->_dialogueFont);
+ se.calc(text, MAX_BALLOON_WIDTH);
+ w = se.width() + 14;
+ h = se.height() + 20;
+
int id = createBalloon(w+5, h, winding, 1);
Balloon *balloon = &_intBalloons[id];
- drawWrappedText(_vm->_dialogueFont, balloon->surface, text, textColor, MAX_BALLOON_WIDTH);
+ StringWriter_NS sw(_vm->_dialogueFont);
+ sw.write(text, MAX_BALLOON_WIDTH, textColor, balloon->surface);
// TODO: extract some text to make a name for obj
balloon->obj = _gfx->registerBalloon(new SurfaceToFrames(balloon->surface), 0);
@@ -196,7 +380,9 @@ int BalloonManager_ns::setDialogueBalloon(char *text, uint16 winding, byte textC
void BalloonManager_ns::setBalloonText(uint id, char *text, byte textColor) {
Balloon *balloon = getBalloon(id);
balloon->surface->fillRect(balloon->innerBox, 1);
- drawWrappedText(_vm->_dialogueFont, balloon->surface, text, textColor, MAX_BALLOON_WIDTH);
+
+ StringWriter_NS sw(_vm->_dialogueFont);
+ sw.write(text, MAX_BALLOON_WIDTH, textColor, balloon->surface);
}
@@ -204,11 +390,15 @@ int BalloonManager_ns::setLocationBalloon(char *text, bool endGame) {
int16 w, h;
- getStringExtent(_vm->_dialogueFont, text, MAX_BALLOON_WIDTH, &w, &h);
+ StringExtent_NS se(_vm->_dialogueFont);
+ se.calc(text, MAX_BALLOON_WIDTH);
+ w = se.width() + 14;
+ h = se.height() + 20;
int id = createBalloon(w+(endGame ? 5 : 10), h+5, -1, BALLOON_TRANSPARENT_COLOR_NS);
Balloon *balloon = &_intBalloons[id];
- drawWrappedText(_vm->_dialogueFont, balloon->surface, text, 0, MAX_BALLOON_WIDTH);
+ StringWriter_NS sw(_vm->_dialogueFont);
+ sw.write(text, MAX_BALLOON_WIDTH, 0, balloon->surface);
// TODO: extract some text to make a name for obj
balloon->obj = _gfx->registerBalloon(new SurfaceToFrames(balloon->surface), 0);
@@ -245,106 +435,99 @@ void BalloonManager_ns::freeBalloons() {
_numBalloons = 0;
}
-void BalloonManager_ns::drawWrappedText(Font *font, Graphics::Surface* surf, char *text, byte color, int16 wrapwidth) {
- uint16 lines = 0;
- uint16 linewidth = 0;
- uint16 rx = 10;
- uint16 ry = 4;
- uint16 blankWidth = font->getStringWidth(" ");
- uint16 tokenWidth = 0;
- char token[MAX_TOKEN_LEN];
- if (wrapwidth == -1)
- wrapwidth = _vm->_screenWidth;
- while (strlen(text) > 0) {
- text = parseNextToken(text, token, MAX_TOKEN_LEN, " ", true);
- if (!scumm_stricmp(token, "%p")) {
- lines++;
- rx = 10;
- ry = 4 + lines*10; // y
- strcpy(token, "> .......");
- strncpy(token+2, _password, strlen(_password));
- tokenWidth = font->getStringWidth(token);
- } else {
- tokenWidth = font->getStringWidth(token);
- linewidth += tokenWidth;
+class StringExtent_BR : public WrappedLineFormatter {
- if (linewidth > wrapwidth) {
- // wrap line
- lines++;
- rx = 10; // x
- ry = 4 + lines*10; // y
- linewidth = tokenWidth;
- }
+ uint _width, _height;
- if (!scumm_stricmp(token, "%s")) {
- sprintf(token, "%d", _score);
- }
+protected:
+ virtual void setup() {
+ _width = _height = 0;
- }
-
- _gfx->drawText(font, surf, rx, ry, token, color);
+ _line.clear();
+ _lines = 0;
+ _width = 0;
+ }
- rx += tokenWidth + blankWidth;
- linewidth += blankWidth;
+ virtual void action() {
+ if (_lineWidth > _width) {
+ _width = _lineWidth;
+ }
+ _height = _lines * _font->height();
+ }
- text = Common::ltrim(text);
+ virtual void end() {
+ action();
}
-}
+public:
+ StringExtent_BR(Font *font) : WrappedLineFormatter(font) { }
-void BalloonManager_ns::getStringExtent(Font *font, char *text, uint16 maxwidth, int16* width, int16* height) {
+ uint width() const { return _width; }
+ uint height() const { return _height; }
+};
- uint16 lines = 0;
- uint16 w = 0;
- *width = 0;
- uint16 blankWidth = font->getStringWidth(" ");
- uint16 tokenWidth = 0;
+class StringWriter_BR : public WrappedLineFormatter {
- char token[MAX_TOKEN_LEN];
+ uint _width, _height;
+ byte _color;
+ uint _x, _y;
- while (strlen(text) != 0) {
+ Graphics::Surface *_surf;
- text = parseNextToken(text, token, MAX_TOKEN_LEN, " ", true);
- tokenWidth = font->getStringWidth(token);
+protected:
+ StringWriter_BR(Font *font, byte color) : WrappedLineFormatter(font) {
- w += tokenWidth;
+ }
- if (!scumm_stricmp(token, "%p")) {
- lines++;
- } else {
- if (w > maxwidth) {
- w -= tokenWidth;
- lines++;
- if (w > *width)
- *width = w;
+ virtual void setup() {
+ }
- w = tokenWidth;
- }
+ virtual void action() {
+ if (_line.empty()) {
+ return;
}
+ uint16 rx = _x + (_surf->w - _lineWidth) / 2;
+ uint16 ry = _y + _lines * _font->height(); // y
- w += blankWidth;
- text = Common::ltrim(text);
+ byte *dst = (byte*)_surf->getBasePtr(rx, ry);
+ _font->setColor(_color);
+ _font->drawString(dst, _surf->w, _line.c_str());
}
- if (*width < w) *width = w;
- *width += 10;
+ virtual void end() {
+ action();
+ }
- *height = lines * 10 + 20;
+public:
+ StringWriter_BR(Font *font) : WrappedLineFormatter(font) { }
- return;
-}
+ void write(const char *text, uint maxWidth, byte color, Graphics::Surface *surf) {
+ maxWidth = 216;
+
+ StringExtent_BR se(_font);
+ se.calc(text, maxWidth);
+ _width = se.width() + 10;
+ _height = se.height() + 12;
+ _color = color;
+ _surf = surf;
+
+ _x = 0;
+ _y = (_surf->h - _height) / 2;
+ calc(text, maxWidth);
+ }
+};
@@ -366,29 +549,12 @@ class BalloonManager_br : public BalloonManager {
Frames *_rightBalloon;
void cacheAnims();
- void getStringExtent(Font *font, const char *text, uint16 maxwidth, int16* width, int16* height);
void drawWrappedText(Font *font, Graphics::Surface* surf, char *text, byte color, int16 wrapwidth);
int createBalloon(int16 w, int16 h, int16 winding, uint16 borderThickness);
Balloon *getBalloon(uint id);
Graphics::Surface *expandBalloon(Frames *data, int frameNum);
- void textSetupRendering(const Common::String &text, Graphics::Surface *dest, Font *font, byte color);
- void textEmitCenteredLine();
- void textAccum(const Common::String &token, uint16 width);
- void textNewLine();
-
- Common::String _textLine;
- Graphics::Surface *_textSurf;
- Font *_textFont;
- uint16 _textX, _textY;
- byte _textColor;
- uint16 _textLines, _textWidth;
-
- void extentSetup(Font *font, int16 *width, int16 *height);
- void extentAction();
-
- int16 *_extentWidth, *_extentHeight;
-
+ StringWriter_BR _writer;
public:
BalloonManager_br(Disk *disk, Gfx *gfx);
@@ -447,7 +613,7 @@ int BalloonManager_br::setSingleBalloon(char *text, uint16 x, uint16 y, uint16 w
balloon->surface = expandBalloon(src, srcFrame);
src->getRect(srcFrame, balloon->box);
- drawWrappedText(_vm->_dialogueFont, balloon->surface, text, textColor, MAX_BALLOON_WIDTH);
+ _writer.write(text, MAX_BALLOON_WIDTH, textColor, balloon->surface);
// TODO: extract some text to make a name for obj
balloon->obj = _gfx->registerBalloon(new SurfaceToFrames(balloon->surface), 0);
@@ -455,8 +621,6 @@ int BalloonManager_br::setSingleBalloon(char *text, uint16 x, uint16 y, uint16 w
balloon->obj->y = y + balloon->box.top;
balloon->obj->transparentKey = BALLOON_TRANSPARENT_COLOR_BR;
- printf("balloon (%i, %i)\n", balloon->obj->x, balloon->obj->y);
-
_numBalloons++;
return id;
@@ -485,7 +649,7 @@ int BalloonManager_br::setDialogueBalloon(char *text, uint16 winding, byte textC
balloon->surface = expandBalloon(src, srcFrame);
src->getRect(srcFrame, balloon->box);
- drawWrappedText(_vm->_dialogueFont, balloon->surface, text, textColor, MAX_BALLOON_WIDTH);
+ _writer.write(text, MAX_BALLOON_WIDTH, textColor, balloon->surface);
// TODO: extract some text to make a name for obj
balloon->obj = _gfx->registerBalloon(new SurfaceToFrames(balloon->surface), 0);
@@ -556,155 +720,9 @@ void BalloonManager_br::cacheAnims() {
}
-void BalloonManager_br::extentSetup(Font *font, int16 *width, int16 *height) {
- _extentWidth = width;
- _extentHeight = height;
-
- _textLine.clear();
- _textLines = 0;
- _textWidth = 0;
- _textFont = font;
-}
-
-void BalloonManager_br::extentAction() {
- if (_textWidth > *_extentWidth) {
- *_extentWidth = _textWidth;
- }
- *_extentHeight = _textLines * _textFont->height();
-}
-
-void BalloonManager_br::textSetupRendering(const Common::String &text, Graphics::Surface *dest, Font *font, byte color) {
- uint16 maxWidth = 216;
-
- int16 w, h;
- getStringExtent(font, text.c_str(), maxWidth, &w, &h);
-
- w += 10;
- h += 12;
-
- _textLine.clear();
- _textSurf = dest;
- _textFont = font;
- _textX = 0;
- _textY = (_textSurf->h - h) / 2;
- _textColor = color;
- _textLines = 0;
- _textWidth = 0;
-}
-
-void BalloonManager_br::textEmitCenteredLine() {
- if (_textLine.empty()) {
- return;
- }
- uint16 rx = _textX + (_textSurf->w - _textWidth) / 2;
- uint16 ry = _textY + _textLines * _textFont->height(); // y
- _gfx->drawText(_textFont, _textSurf, rx, ry, _textLine.c_str(), _textColor);
-}
-
-void BalloonManager_br::textAccum(const Common::String &token, uint16 width) {
- if (token.empty()) {
- return;
- }
-
- _textWidth += width;
- _textLine += token;
-}
-
-void BalloonManager_br::textNewLine() {
- _textLines++;
- _textWidth = 0;
- _textLine.clear();
-}
-
-
-// TODO: really, base this and getStringExtent on some kind of LineTokenizer, instead of
-// repeating the algorithm and changing a couple of lines.
-void BalloonManager_br::drawWrappedText(Font *font, Graphics::Surface* surf, char *text, byte color, int16 wrapWidth) {
- textSetupRendering(text, surf, font, color);
-
- wrapWidth = 216;
-
- Common::StringTokenizer tokenizer(text, " ");
- Common::String token;
- Common::String blank(" ");
-
- uint16 blankWidth = font->getStringWidth(" ");
- uint16 tokenWidth = 0;
-
- while (!tokenizer.empty()) {
- token = tokenizer.nextToken();
-
- if (token == '/') {
- tokenWidth = 0;
- textEmitCenteredLine();
- textNewLine();
- } else {
- // todo: expand '%'
- tokenWidth = font->getStringWidth(token.c_str());
-
- if (_textWidth == 0) {
- textAccum(token, tokenWidth);
- } else {
- if (_textWidth + blankWidth + tokenWidth <= wrapWidth) {
- textAccum(blank, blankWidth);
- textAccum(token, tokenWidth);
- } else {
- textEmitCenteredLine();
- textNewLine();
- textAccum(token, tokenWidth);
- }
- }
- }
- }
-
- textEmitCenteredLine();
-}
-
-
-
-void BalloonManager_br::getStringExtent(Font *font, const char *text, uint16 maxwidth, int16* width, int16* height) {
- extentSetup(font, width, height);
-
- Common::StringTokenizer tokenizer(text, " ");
- Common::String token;
- Common::String blank(" ");
-
- uint16 blankWidth = font->getStringWidth(" ");
- uint16 tokenWidth = 0;
-
- while (!tokenizer.empty()) {
- token = tokenizer.nextToken();
-
- if (token == '/') {
- tokenWidth = 0;
- extentAction();
- textNewLine();
- } else {
- // todo: expand '%'
- tokenWidth = font->getStringWidth(token.c_str());
-
- if (_textWidth == 0) {
- textAccum(token, tokenWidth);
- } else {
- if (_textWidth + blankWidth + tokenWidth <= maxwidth) {
- textAccum(blank, blankWidth);
- textAccum(token, tokenWidth);
- } else {
- extentAction();
- textNewLine();
- textAccum(token, tokenWidth);
- }
- }
- }
- }
-
- extentAction();
-}
-
-
-
-BalloonManager_br::BalloonManager_br(Disk *disk, Gfx *gfx) : _numBalloons(0), _disk(disk), _gfx(gfx), _leftBalloon(0), _rightBalloon(0) {
+BalloonManager_br::BalloonManager_br(Disk *disk, Gfx *gfx) : _numBalloons(0), _disk(disk), _gfx(gfx),
+ _leftBalloon(0), _rightBalloon(0), _writer(_vm->_dialogueFont) {
}
BalloonManager_br::~BalloonManager_br() {
diff --git a/engines/parallaction/callables_br.cpp b/engines/parallaction/callables_br.cpp
index 628ba0c1f1..a4aec8e93e 100644
--- a/engines/parallaction/callables_br.cpp
+++ b/engines/parallaction/callables_br.cpp
@@ -41,7 +41,7 @@ void Parallaction_br::_c_ferrcycle(void*) {
}
void Parallaction_br::_c_lipsinc(void*) {
- warning("Parallaction_br::_c_lipsinc() not yet implemented");
+ warning("Unexpected lipsinc routine call! Please notify the team!");
}
void Parallaction_br::_c_albcycle(void*) {
diff --git a/engines/parallaction/callables_ns.cpp b/engines/parallaction/callables_ns.cpp
index 761e11dc7d..0f89ca22d1 100644
--- a/engines/parallaction/callables_ns.cpp
+++ b/engines/parallaction/callables_ns.cpp
@@ -211,21 +211,17 @@ void Parallaction_ns::_c_moveSarc(void *parm) {
}
}
- _introSarcData1 = _introSarcData3 - _moveSarcZone1->_left;
- a->_z = _introSarcData3;
- a->_frame = _moveSarcZone1->_top - (_introSarcData1 / 20);
- _introSarcData3 = _moveSarcZone1->_left;
+ _introSarcData1 = _introSarcData3 - _moveSarcZone1->getX();
+ a->setZ(_introSarcData3);
+ a->setF(_moveSarcZone1->getY() - (_introSarcData1 / 20));
+ _introSarcData3 = _moveSarcZone1->getX();
if (_introSarcData1 > 0) {
- a->_left = _introSarcData1 / 2;
+ a->setX(_introSarcData1 / 2);
+ a->setY(2);
} else {
- a->_left = -_introSarcData1 / 2;
- }
-
- if (_introSarcData1 > 0) {
- a->_top = 2;
- } else {
- a->_top = -2;
+ a->setX(-_introSarcData1 / 2);
+ a->setY(-2);
}
return;
@@ -236,11 +232,11 @@ void Parallaction_ns::_c_moveSarc(void *parm) {
_moveSarcZone1->translate(_introSarcData1, -_introSarcData1 / 20);
_moveSarcZone0->translate(_introSarcData1, -_introSarcData1 / 20);
- if (_moveSarcZones[0]->_left == 35 &&
- _moveSarcZones[1]->_left == 68 &&
- _moveSarcZones[2]->_left == 101 &&
- _moveSarcZones[3]->_left == 134 &&
- _moveSarcZones[4]->_left == 167) {
+ if (_moveSarcZones[0]->getX() == 35 &&
+ _moveSarcZones[1]->getX() == 68 &&
+ _moveSarcZones[2]->getX() == 101 &&
+ _moveSarcZones[3]->getX() == 134 &&
+ _moveSarcZones[4]->getX() == 167) {
a = findAnimation("finito");
@@ -261,7 +257,7 @@ void Parallaction_ns::_c_contaFoglie(void *parm) {
if (num_foglie != 6)
return;
- _commandFlags |= 0x1000;
+ _globalFlags |= 0x1000;
return;
}
@@ -482,8 +478,8 @@ void Parallaction_ns::_c_sketch(void *parm) {
Graphics::drawLine(oldx, oldy, newx, newy, 0, zeroMask, &_gfx->_backgroundInfo);
- _rightHandAnim->_left = newx;
- _rightHandAnim->_top = newy - 20;
+ _rightHandAnim->setX(newx);
+ _rightHandAnim->setY(newy - 20);
index++;
@@ -496,10 +492,10 @@ void Parallaction_ns::_c_sketch(void *parm) {
void Parallaction_ns::_c_shade(void *parm) {
Common::Rect r(
- _rightHandAnim->_left - 36,
- _rightHandAnim->_top - 36,
- _rightHandAnim->_left,
- _rightHandAnim->_top
+ _rightHandAnim->getX() - 36,
+ _rightHandAnim->getY() - 36,
+ _rightHandAnim->getX(),
+ _rightHandAnim->getY()
);
uint16 _di = r.left/4 + r.top * _gfx->_backgroundInfo->mask.internalWidth;
diff --git a/engines/parallaction/debug.cpp b/engines/parallaction/debug.cpp
index f57976594e..0ff38913f7 100644
--- a/engines/parallaction/debug.cpp
+++ b/engines/parallaction/debug.cpp
@@ -101,14 +101,14 @@ bool Debugger::Cmd_Locations(int argc, const char **argv) {
bool Debugger::Cmd_GlobalFlags(int argc, const char **argv) {
- uint32 flags = _commandFlags;
+ uint32 flags = _globalFlags;
DebugPrintf("+------------------------------+---------+\n"
"| flag name | value |\n"
"+------------------------------+---------+\n");
- for (uint i = 0; i < _vm->_globalTable->count(); i++) {
+ for (uint i = 0; i < _vm->_globalFlagsNames->count(); i++) {
const char *value = ((flags & (1 << i)) == 0) ? "OFF" : "ON";
- DebugPrintf("|%-30s| %-6s|\n", _vm->_globalTable->item(i), value);
+ DebugPrintf("|%-30s| %-6s|\n", _vm->_globalFlagsNames->item(i), value);
}
DebugPrintf("+------------------------------+---------+\n");
@@ -157,7 +157,7 @@ bool Debugger::Cmd_Zones(int argc, const char **argv) {
"+--------------------+---+---+---+---+--------+--------+\n");
for ( ; b != e; b++) {
ZonePtr z = *b;
- DebugPrintf("|%-20s|%3i|%3i|%3i|%3i|%8x|%8x|\n", z->_name, z->_left, z->_top, z->_right, z->_bottom, z->_type, z->_flags );
+ DebugPrintf("|%-20s|%3i|%3i|%3i|%3i|%8x|%8x|\n", z->_name, z->getX(), z->getY(), z->getX() + z->width(), z->getY() + z->height(), z->_type, z->_flags );
}
DebugPrintf("+--------------------+---+---+---+---+--------+--------+\n");
@@ -175,7 +175,7 @@ bool Debugger::Cmd_Animations(int argc, const char **argv) {
"+--------------------+---+---+---+---+--------+--------+\n");
for ( ; b != e; b++) {
AnimationPtr a = *b;
- DebugPrintf("|%-20s|%3i|%3i|%3i|%3i|%8x|%8x|\n", a->_name, a->_left, a->_top, a->_z, a->_frame, a->_type, a->_flags );
+ DebugPrintf("|%-20s|%3i|%3i|%3i|%3i|%8x|%8x|\n", a->_name, a->getX(), a->getY(), a->getZ(), a->getF(), a->_type, a->_flags );
}
DebugPrintf("+--------------------+---+---+---+---+--------+--------+\n");
@@ -187,19 +187,19 @@ bool Debugger::Cmd_GfxObjects(int argc, const char **argv) {
const char *objType[] = { "DOOR", "GET", "ANIM" };
- DebugPrintf("+--------------------+-----+-----+-----+-----+--------+--------+\n"
- "| name | x | y | z | f | type | visi |\n"
- "+--------------------+-----+-----+-----+-----+--------+--------+\n");
+ DebugPrintf("+--------------------+-----+-----+-----+-------+-----+--------+--------+\n"
+ "| name | x | y | z | layer | f | type | visi |\n"
+ "+--------------------+-----+-----+-----+-------+-----+--------+--------+\n");
GfxObjList::iterator b = _vm->_gfx->_gfxobjList.begin();
GfxObjList::iterator e = _vm->_gfx->_gfxobjList.end();
for ( ; b != e; b++) {
GfxObj *obj = *b;
- DebugPrintf("|%-20s|%5i|%5i|%5i|%5i|%8s|%8x|\n", obj->getName(), obj->x, obj->y, obj->z, obj->frame, objType[obj->type], obj->isVisible() );
+ DebugPrintf("|%-20s|%5i|%5i|%5i|%7i|%5i|%8s|%8x|\n", obj->getName(), obj->x, obj->y, obj->z, obj->layer, obj->frame, objType[obj->type], obj->isVisible() );
}
- DebugPrintf("+--------------------+-----+-----+-----+-----+--------+--------+\n");
+ DebugPrintf("+--------------------+-----+-----+-----+-------+-----+--------+--------+\n");
return true;
}
diff --git a/engines/parallaction/dialogue.cpp b/engines/parallaction/dialogue.cpp
index 21584a0525..205d702aa0 100644
--- a/engines/parallaction/dialogue.cpp
+++ b/engines/parallaction/dialogue.cpp
@@ -168,7 +168,7 @@ bool DialogueManager::displayAnswer(uint16 i) {
uint32 flags = _vm->getLocationFlags();
if (a->_yesFlags & kFlagsGlobal)
- flags = _commandFlags | kFlagsGlobal;
+ flags = _globalFlags | kFlagsGlobal;
// display suitable answers
if (((a->_yesFlags & flags) == a->_yesFlags) && ((a->_noFlags & ~flags) == a->_noFlags)) {
diff --git a/engines/parallaction/disk.h b/engines/parallaction/disk.h
index 341229a649..2923f239d4 100644
--- a/engines/parallaction/disk.h
+++ b/engines/parallaction/disk.h
@@ -69,6 +69,7 @@ public:
virtual Table* loadTable(const char* name) = 0;
virtual Common::SeekableReadStream* loadMusic(const char* name) = 0;
virtual Common::ReadStream* loadSound(const char* name) = 0;
+ virtual void loadMask(const char *name, MaskBuffer &buffer) { }
};
@@ -248,6 +249,7 @@ public:
Table* loadTable(const char* name);
Common::SeekableReadStream* loadMusic(const char* name);
Common::ReadStream* loadSound(const char* name);
+ void loadMask(const char *name, MaskBuffer &buffer);
};
class DosDemo_br : public DosDisk_br {
diff --git a/engines/parallaction/disk_br.cpp b/engines/parallaction/disk_br.cpp
index cd57ec8822..f52567bea2 100644
--- a/engines/parallaction/disk_br.cpp
+++ b/engines/parallaction/disk_br.cpp
@@ -357,6 +357,29 @@ void DosDisk_br::loadSlide(BackgroundInfo& info, const char *name) {
return;
}
+void DosDisk_br::loadMask(const char *name, MaskBuffer &buffer) {
+ if (!name) {
+ return;
+ }
+
+ Common::String filepath;
+ FilesystemNode node;
+ Common::File stream;
+
+ filepath = Common::String(name) + ".msk";
+ node = _mskDir.getChild(filepath);
+ if (!node.exists()) {
+ errorFileNotFound(_mskDir, filepath);
+ }
+ stream.open(node);
+
+ // NOTE: info.width and info.height are only valid if the background graphics
+ // have already been loaded
+ buffer.bigEndian = false;
+ stream.read(buffer.data, buffer.size);
+ stream.close();
+}
+
void DosDisk_br::loadScenery(BackgroundInfo& info, const char *name, const char *mask, const char* path) {
debugC(5, kDebugDisk, "DosDisk_br::loadScenery");
@@ -733,8 +756,6 @@ Font* AmigaDisk_br::loadFont(const char* name) {
while ((ch = stream.readByte()) != 0) fontFile += ch;
stream.close();
- printf("fontDir = %s, fontFile = %s\n", fontDir.c_str(), fontFile.c_str());
-
node = _fntDir.getChild(fontDir);
if (!node.exists()) {
errorFileNotFound(_fntDir, fontDir);
diff --git a/engines/parallaction/exec.h b/engines/parallaction/exec.h
index 22e75744f1..4239857ec0 100644
--- a/engines/parallaction/exec.h
+++ b/engines/parallaction/exec.h
@@ -84,8 +84,6 @@ class CommandExec_ns : public CommandExec {
Parallaction_ns *_vm;
protected:
- void updateGetZone(ZonePtr z, bool visible);
-
DECLARE_UNQUALIFIED_COMMAND_OPCODE(invalid);
DECLARE_UNQUALIFIED_COMMAND_OPCODE(set);
DECLARE_UNQUALIFIED_COMMAND_OPCODE(clear);
diff --git a/engines/parallaction/exec_br.cpp b/engines/parallaction/exec_br.cpp
index 0b7400f0f7..fe7b1b2903 100644
--- a/engines/parallaction/exec_br.cpp
+++ b/engines/parallaction/exec_br.cpp
@@ -97,8 +97,9 @@ void Parallaction_br::setupSubtitles(char *s, char *s2, int y) {
} else {
_subtitle[1] = -1;
}
-
+#if 0 // disabled because no references to lip sync has been found in the scripts
_subtitleLipSync = 0;
+#endif
}
void Parallaction_br::clearSubtitles() {
@@ -122,48 +123,23 @@ DECLARE_COMMAND_OPCODE(location) {
DECLARE_COMMAND_OPCODE(open) {
warning("Parallaction_br::cmdOp_open command not yet implemented");
- _ctxt.cmd->u._zone->_flags &= ~kFlagsClosed;
- if (_ctxt.cmd->u._zone->u.door->gfxobj) {
- _vm->updateDoor(_ctxt.cmd->u._zone);
- }
+ _vm->updateDoor(_ctxt.cmd->u._zone, false);
}
DECLARE_COMMAND_OPCODE(close) {
warning("Parallaction_br::cmdOp_close not yet implemented");
- _ctxt.cmd->u._zone->_flags |= kFlagsClosed;
- if (_ctxt.cmd->u._zone->u.door->gfxobj) {
- _vm->updateDoor(_ctxt.cmd->u._zone);
- }
+ _vm->updateDoor(_ctxt.cmd->u._zone, true);
}
DECLARE_COMMAND_OPCODE(on) {
- CommandData *data = &_ctxt.cmd->u;
- ZonePtr z = data->_zone;
-
- if (z) {
- z->_flags |= kFlagsActive;
- z->_flags &= ~kFlagsRemove;
-
- if ((z->_type & 0xFFFF) & kZoneGet) {
- _vm->_gfx->showGfxObj(z->u.get->gfxobj, true);
- }
- }
+ _vm->showZone(_ctxt.cmd->u._zone, true);
}
DECLARE_COMMAND_OPCODE(off) {
- CommandData *data = &_ctxt.cmd->u;
- ZonePtr z = data->_zone;
-
- if (z) {
- z->_flags |= kFlagsRemove;
-
- if ((z->_type & 0xFFFF) & kZoneGet) {
- _vm->_gfx->showGfxObj(z->u.get->gfxobj, false);
- }
- }
+ _vm->showZone(_ctxt.cmd->u._zone, false);
}
@@ -335,42 +311,18 @@ DECLARE_COMMAND_OPCODE(offsave) {
DECLARE_INSTRUCTION_OPCODE(on) {
- InstructionPtr inst = *_ctxt.inst;
- ZonePtr z = inst->_z;
-
- if (z) {
- z->_flags |= kFlagsActive;
- z->_flags &= ~kFlagsRemove;
-
- if ((z->_type & 0xFFFF) & kZoneGet) {
- _vm->_gfx->showGfxObj(z->u.get->gfxobj, true);
- }
- }
+ _vm->showZone((*_ctxt.inst)->_z, true);
}
DECLARE_INSTRUCTION_OPCODE(off) {
- InstructionPtr inst = *_ctxt.inst;
- ZonePtr z = inst->_z;
-
- if (z) {
- z->_flags |= kFlagsRemove;
-
- if ((z->_type & 0xFFFF) & kZoneGet) {
- _vm->_gfx->showGfxObj(z->u.get->gfxobj, false);
- }
- }
+ _vm->showZone((*_ctxt.inst)->_z, false);
}
DECLARE_INSTRUCTION_OPCODE(set) {
InstructionPtr inst = *_ctxt.inst;
-
- int16 rvalue = inst->_opB.getRValue();
- int16* lvalue = inst->_opA.getLValue();
-
- *lvalue = rvalue;
-
+ inst->_opA.setValue(inst->_opB.getValue());
}
@@ -378,7 +330,7 @@ DECLARE_INSTRUCTION_OPCODE(set) {
DECLARE_INSTRUCTION_OPCODE(inc) {
InstructionPtr inst = *_ctxt.inst;
- int16 rvalue = inst->_opB.getRValue();
+ int16 rvalue = inst->_opB.getValue();
if (inst->_flags & kInstMod) { // mod
int16 _bx = (rvalue > 0 ? rvalue : -rvalue);
@@ -387,32 +339,30 @@ DECLARE_INSTRUCTION_OPCODE(inc) {
rvalue = (rvalue > 0 ? 1 : -1);
}
- int16 *lvalue = inst->_opA.getLValue();
+ int16 lvalue = inst->_opA.getValue();
switch (inst->_index) {
case INST_INC:
- *lvalue += rvalue;
+ lvalue += rvalue;
break;
case INST_DEC:
- *lvalue -= rvalue;
+ lvalue -= rvalue;
break;
case INST_MUL:
- *lvalue *= rvalue;
+ lvalue *= rvalue;
break;
case INST_DIV:
- *lvalue /= rvalue;
+ lvalue /= rvalue;
break;
default:
error("This should never happen. Report immediately");
}
- if (inst->_opA._flags & kParaLocal) {
- inst->_opA._local->wrap();
- }
+ inst->_opA.setValue(lvalue);
}
@@ -445,11 +395,7 @@ DECLARE_INSTRUCTION_OPCODE(move) {
DECLARE_INSTRUCTION_OPCODE(color) {
InstructionPtr inst = *_ctxt.inst;
-
- int16 entry = inst->_opB.getRValue();
-
- _vm->_gfx->_palette.setEntry(entry, inst->_colors[0], inst->_colors[1], inst->_colors[2]);
-
+ _vm->_gfx->_palette.setEntry(inst->_opB.getValue(), inst->_colors[0], inst->_colors[1], inst->_colors[2]);
}
diff --git a/engines/parallaction/exec_ns.cpp b/engines/parallaction/exec_ns.cpp
index 99a492863b..4ea7d601e0 100644
--- a/engines/parallaction/exec_ns.cpp
+++ b/engines/parallaction/exec_ns.cpp
@@ -80,7 +80,7 @@ DECLARE_INSTRUCTION_OPCODE(off) {
DECLARE_INSTRUCTION_OPCODE(loop) {
InstructionPtr inst = *_ctxt.inst;
- _ctxt.program->_loopCounter = inst->_opB.getRValue();
+ _ctxt.program->_loopCounter = inst->_opB.getValue();
_ctxt.program->_loopStart = _ctxt.ip;
}
@@ -93,7 +93,7 @@ DECLARE_INSTRUCTION_OPCODE(endloop) {
DECLARE_INSTRUCTION_OPCODE(inc) {
InstructionPtr inst = *_ctxt.inst;
- int16 _si = inst->_opB.getRValue();
+ int16 _si = inst->_opB.getValue();
if (inst->_flags & kInstMod) { // mod
int16 _bx = (_si > 0 ? _si : -_si);
@@ -102,29 +102,22 @@ DECLARE_INSTRUCTION_OPCODE(inc) {
_si = (_si > 0 ? 1 : -1);
}
- int16* lvalue = inst->_opA.getLValue();
+ int16 lvalue = inst->_opA.getValue();
if (inst->_index == INST_INC) {
- *lvalue += _si;
+ lvalue += _si;
} else {
- *lvalue -= _si;
+ lvalue -= _si;
}
- if (inst->_opA._flags & kParaLocal) {
- inst->_opA._local->wrap();
- }
+ inst->_opA.setValue(lvalue);
}
DECLARE_INSTRUCTION_OPCODE(set) {
InstructionPtr inst = *_ctxt.inst;
-
- int16 _si = inst->_opB.getRValue();
- int16 *lvalue = inst->_opA.getLValue();
-
- *lvalue = _si;
-
+ inst->_opA.setValue(inst->_opB.getValue());
}
@@ -133,10 +126,10 @@ DECLARE_INSTRUCTION_OPCODE(put) {
Graphics::Surface v18;
v18.w = inst->_a->width();
v18.h = inst->_a->height();
- v18.pixels = inst->_a->getFrameData(inst->_a->_frame);
+ v18.pixels = inst->_a->getFrameData(inst->_a->getF());
- int16 x = inst->_opA.getRValue();
- int16 y = inst->_opB.getRValue();
+ int16 x = inst->_opA.getValue();
+ int16 y = inst->_opB.getValue();
bool mask = (inst->_flags & kInstMaskedPut) == kInstMaskedPut;
_vm->_gfx->patchBackground(v18, x, y, mask);
@@ -176,8 +169,8 @@ DECLARE_INSTRUCTION_OPCODE(sound) {
DECLARE_INSTRUCTION_OPCODE(move) {
InstructionPtr inst = (*_ctxt.inst);
- int16 x = inst->_opA.getRValue();
- int16 y = inst->_opB.getRValue();
+ int16 x = inst->_opA.getValue();
+ int16 y = inst->_opB.getValue();
_vm->_char.scheduleWalk(x, y);
}
@@ -203,7 +196,7 @@ DECLARE_COMMAND_OPCODE(invalid) {
DECLARE_COMMAND_OPCODE(set) {
if (_ctxt.cmd->u._flags & kFlagsGlobal) {
_ctxt.cmd->u._flags &= ~kFlagsGlobal;
- _commandFlags |= _ctxt.cmd->u._flags;
+ _globalFlags |= _ctxt.cmd->u._flags;
} else {
_vm->setLocationFlags(_ctxt.cmd->u._flags);
}
@@ -213,7 +206,7 @@ DECLARE_COMMAND_OPCODE(set) {
DECLARE_COMMAND_OPCODE(clear) {
if (_ctxt.cmd->u._flags & kFlagsGlobal) {
_ctxt.cmd->u._flags &= ~kFlagsGlobal;
- _commandFlags &= ~_ctxt.cmd->u._flags;
+ _globalFlags &= ~_ctxt.cmd->u._flags;
} else {
_vm->clearLocationFlags(_ctxt.cmd->u._flags);
}
@@ -246,48 +239,47 @@ DECLARE_COMMAND_OPCODE(location) {
DECLARE_COMMAND_OPCODE(open) {
- _ctxt.cmd->u._zone->_flags &= ~kFlagsClosed;
- if (_ctxt.cmd->u._zone->u.door->gfxobj) {
- _vm->updateDoor(_ctxt.cmd->u._zone);
- }
+ _vm->updateDoor(_ctxt.cmd->u._zone, false);
}
DECLARE_COMMAND_OPCODE(close) {
- _ctxt.cmd->u._zone->_flags |= kFlagsClosed;
- if (_ctxt.cmd->u._zone->u.door->gfxobj) {
- _vm->updateDoor(_ctxt.cmd->u._zone);
- }
+ _vm->updateDoor(_ctxt.cmd->u._zone, true);
}
-void CommandExec_ns::updateGetZone(ZonePtr z, bool visible) {
+void Parallaction::showZone(ZonePtr z, bool visible) {
if (!z) {
return;
}
+ if (visible) {
+ z->_flags &= ~kFlagsRemove;
+ z->_flags |= kFlagsActive;
+ } else {
+ z->_flags |= kFlagsRemove;
+ }
+
if ((z->_type & 0xFFFF) == kZoneGet) {
- _vm->_gfx->showGfxObj(z->u.get->gfxobj, visible);
+ _gfx->showGfxObj(z->u.get->gfxobj, visible);
+
+ GetData *data = z->u.get;
+ if (data->hasMask && _gfx->_backgroundInfo->hasMask) {
+ if (visible) {
+ _gfx->_backgroundInfo->mask.bltOr(data->gfxobj->x, data->gfxobj->y, data->_mask[0], 0, 0, data->_mask->w, data->_mask->h);
+ } else {
+ _gfx->_backgroundInfo->mask.bltCopy(data->gfxobj->x, data->gfxobj->y, data->_mask[1], 0, 0, data->_mask->w, data->_mask->h);
+ }
+ }
}
}
DECLARE_COMMAND_OPCODE(on) {
- ZonePtr z = _ctxt.cmd->u._zone;
-
- if (z) {
- z->_flags &= ~kFlagsRemove;
- z->_flags |= kFlagsActive;
- updateGetZone(z, true);
- }
+ _vm->showZone(_ctxt.cmd->u._zone, true);
}
DECLARE_COMMAND_OPCODE(off) {
- ZonePtr z = _ctxt.cmd->u._zone;
-
- if (z) {
- _ctxt.cmd->u._zone->_flags |= kFlagsRemove;
- updateGetZone(z, false);
- }
+ _vm->showZone(_ctxt.cmd->u._zone, false);
}
@@ -299,7 +291,7 @@ DECLARE_COMMAND_OPCODE(call) {
DECLARE_COMMAND_OPCODE(toggle) {
if (_ctxt.cmd->u._flags & kFlagsGlobal) {
_ctxt.cmd->u._flags &= ~kFlagsGlobal;
- _commandFlags ^= _ctxt.cmd->u._flags;
+ _globalFlags ^= _ctxt.cmd->u._flags;
} else {
_vm->toggleLocationFlags(_ctxt.cmd->u._flags);
}
@@ -345,23 +337,31 @@ void Parallaction_ns::drawAnimations() {
if ((anim->_flags & kFlagsActive) && ((anim->_flags & kFlagsRemove) == 0)) {
if (anim->_flags & kFlagsNoMasked)
- layer = 3;
- else
- layer = _gfx->_backgroundInfo->getLayer(anim->_top + anim->height());
+ layer = LAYER_FOREGROUND;
+ else {
+ if (getGameType() == GType_Nippon) {
+ // Layer in NS depends on where the animation is on the screen, for each animation.
+ layer = _gfx->_backgroundInfo->getLayer(anim->getFrameY() + anim->height());
+ } else {
+ // Layer in BRA is calculated from Z value. For characters it is the same as NS,
+ // but other animations can have Z set from scripts independently from their
+ // position on the screen.
+ layer = _gfx->_backgroundInfo->getLayer(anim->getZ());
+ }
+ }
if (obj) {
_gfx->showGfxObj(obj, true);
- obj->frame = anim->_frame;
- obj->x = anim->_left;
- obj->y = anim->_top;
- obj->z = anim->_z;
+ obj->frame = anim->getF();
+ obj->x = anim->getX();
+ obj->y = anim->getY();
+ obj->z = anim->getZ();
obj->layer = layer;
}
}
if (((anim->_flags & kFlagsActive) == 0) && (anim->_flags & kFlagsRemove)) {
anim->_flags &= ~kFlagsRemove;
- anim->_oldPos.x = -1000;
}
if ((anim->_flags & kFlagsActive) && (anim->_flags & kFlagsRemove)) {
@@ -418,7 +418,7 @@ void ProgramExec::runScripts(ProgramList::iterator first, ProgramList::iterator
AnimationPtr a = (*it)->_anim;
if (a->_flags & kFlagsCharacter)
- a->_z = a->_top + a->height();
+ a->setZ(a->getFrameY() + a->height());
if ((a->_flags & kFlagsActing) == 0)
continue;
@@ -426,7 +426,7 @@ void ProgramExec::runScripts(ProgramList::iterator first, ProgramList::iterator
runScript(*it, a);
if (a->_flags & kFlagsCharacter)
- a->_z = a->_top + a->height();
+ a->setZ(a->getFrameY() + a->height());
}
_modCounter++;
@@ -448,7 +448,7 @@ void CommandExec::runList(CommandList::iterator first, CommandList::iterator las
CommandPtr cmd = *first;
if (cmd->_flagsOn & kFlagsGlobal) {
- useFlags = _commandFlags | kFlagsGlobal;
+ useFlags = _globalFlags | kFlagsGlobal;
useLocalFlags = false;
} else {
useFlags = _vm->getLocationFlags();
@@ -601,7 +601,7 @@ void Parallaction::runCommentFrame() {
}
-uint16 Parallaction::runZone(ZonePtr z) {
+void Parallaction::runZone(ZonePtr z) {
debugC(3, kDebugExec, "runZone (%s)", z->_name);
uint16 subtype = z->_type & 0xFFFF;
@@ -611,20 +611,15 @@ uint16 Parallaction::runZone(ZonePtr z) {
case kZoneExamine:
enterCommentMode(z);
- return 0;
+ return;
case kZoneGet:
- if (z->_flags & kFlagsFixed) break;
- if (pickupItem(z) != 0) {
- return 1;
- }
- z->_flags |= kFlagsRemove;
+ pickupItem(z);
break;
case kZoneDoor:
if (z->_flags & kFlagsLocked) break;
- z->_flags ^= kFlagsClosed;
- updateDoor(z);
+ updateDoor(z, !(z->_flags & kFlagsClosed));
break;
case kZoneHear:
@@ -633,23 +628,24 @@ uint16 Parallaction::runZone(ZonePtr z) {
case kZoneSpeak:
enterDialogueMode(z);
- return 0;
+ return;
}
debugC(3, kDebugExec, "runZone completed");
_cmdExec->run(z->_commands, z);
- return 0;
+ return;
}
//
// ZONE TYPE: DOOR
//
-void Parallaction::updateDoor(ZonePtr z) {
+void Parallaction::updateDoor(ZonePtr z, bool close) {
+ z->_flags = close ? (z->_flags |= kFlagsClosed) : (z->_flags &= ~kFlagsClosed);
if (z->u.door->gfxobj) {
- uint frame = (z->_flags & kFlagsClosed ? 0 : 1);
+ uint frame = (close ? 0 : 1);
// z->u.door->gfxobj->setFrame(frame);
z->u.door->gfxobj->frame = frame;
}
@@ -663,13 +659,17 @@ void Parallaction::updateDoor(ZonePtr z) {
// ZONE TYPE: GET
//
-int16 Parallaction::pickupItem(ZonePtr z) {
- int r = addInventoryItem(z->u.get->_icon);
- if (r != -1) {
- _gfx->showGfxObj(z->u.get->gfxobj, false);
+bool Parallaction::pickupItem(ZonePtr z) {
+ if (z->_flags & kFlagsFixed) {
+ return false;
+ }
+
+ int slot = addInventoryItem(z->u.get->_icon);
+ if (slot != -1) {
+ showZone(z, false);
}
- return (r == -1);
+ return (slot != -1);
}
@@ -688,7 +688,7 @@ ZonePtr Parallaction::hitZone(uint32 type, uint16 x, uint16 y) {
if (z->_flags & kFlagsRemove) continue;
Common::Rect r;
- z->getRect(r);
+ z->getBox(r);
r.right++; // adjust border because Common::Rect doesn't include bottom-right edge
r.bottom++;
@@ -697,7 +697,7 @@ ZonePtr Parallaction::hitZone(uint32 type, uint16 x, uint16 y) {
if (!r.contains(_si, _di)) {
// out of Zone, so look for special values
- if ((z->_left == -2) || (z->_left == -3)) {
+ if ((z->getX() == -2) || (z->getX() == -3)) {
// WORKAROUND: this huge condition is needed because we made TypeData a collection of structs
// instead of an union. So, merge->_obj1 and get->_icon were just aliases in the original engine,
@@ -716,15 +716,15 @@ ZonePtr Parallaction::hitZone(uint32 type, uint16 x, uint16 y) {
}
}
- if (z->_left != -1)
+ if (z->getX() != -1)
continue;
- if (_si < _char._ani->_left)
+ if (_si < _char._ani->getFrameX())
continue;
- if (_si > (_char._ani->_left + _char._ani->width()))
+ if (_si > (_char._ani->getFrameX() + _char._ani->width()))
continue;
- if (_di < _char._ani->_top)
+ if (_di < _char._ani->getFrameY())
continue;
- if (_di > (_char._ani->_top + _char._ani->height()))
+ if (_di > (_char._ani->getFrameY() + _char._ani->height()))
continue;
}
@@ -746,8 +746,8 @@ ZonePtr Parallaction::hitZone(uint32 type, uint16 x, uint16 y) {
AnimationPtr a = *ait;
_a = (a->_flags & kFlagsActive) ? 1 : 0; // _a: active Animation
- _e = ((_si >= a->_left + a->width()) || (_si <= a->_left)) ? 0 : 1; // _e: horizontal range
- _f = ((_di >= a->_top + a->height()) || (_di <= a->_top)) ? 0 : 1; // _f: vertical range
+ _e = ((_si >= a->getFrameX() + a->width()) || (_si <= a->getFrameX())) ? 0 : 1; // _e: horizontal range
+ _f = ((_di >= a->getFrameY() + a->height()) || (_di <= a->getFrameY())) ? 0 : 1; // _f: vertical range
_b = ((type != 0) || (a->_type == kZoneYou)) ? 0 : 1; // _b: (no type specified) AND (Animation is not the character)
_c = (a->_type & 0xFFFF0000) ? 0 : 1; // _c: Animation is not an object
diff --git a/engines/parallaction/graphics.cpp b/engines/parallaction/graphics.cpp
index c19d6ae5e5..1c2cb58b5b 100644
--- a/engines/parallaction/graphics.cpp
+++ b/engines/parallaction/graphics.cpp
@@ -377,6 +377,12 @@ void Gfx::beginFrame() {
*data++ = _backgroundInfo->mask.getValue(x, y);
}
}
+#if 1
+ Common::DumpFile dump;
+ dump.open("maskdump.bin");
+ dump.write(_bitmapMask.pixels, _bitmapMask.w * _bitmapMask.h);
+ dump.close();
+#endif
break;
}
}
@@ -441,7 +447,7 @@ void Gfx::updateScreen() {
for (; b != e; b++) {
ZonePtr z = *b;
if (z->_type & kZonePath) {
- surf->frameRect(Common::Rect(z->_left, z->_top, z->_right, z->_bottom), 2);
+ surf->frameRect(Common::Rect(z->getX(), z->getY(), z->getX() + z->width(), z->getY() + z->height()), 2);
}
}
g_system->unlockScreen();
diff --git a/engines/parallaction/graphics.h b/engines/parallaction/graphics.h
index 23b4569c6a..471f71dfa8 100644
--- a/engines/parallaction/graphics.h
+++ b/engines/parallaction/graphics.h
@@ -209,6 +209,42 @@ public:
return (m >> n) & 3;
}
+ inline byte* getPtr(uint16 x, uint16 y) const {
+ return data + (x >> 2) + y * internalWidth;
+ }
+
+ void bltOr(uint16 dx, uint16 dy, const MaskBuffer &src, uint16 sx, uint16 sy, uint width, uint height) {
+ assert((width <= w) && (width <= src.w) && (height <= h) && (height <= src.h));
+
+ byte *s = src.getPtr(sx, sy);
+ byte *d = getPtr(dx, dy);
+
+ // this code assumes buffers are aligned on 4-pixels boundaries, as the original does
+ uint16 linewidth = width >> 2;
+ for (uint16 i = 0; i < height; i++) {
+ for (uint16 j = 0; j < linewidth; j++) {
+ *d++ |= *s++;
+ }
+ d += internalWidth - linewidth;
+ s += src.internalWidth - linewidth;
+ }
+ }
+
+ void bltCopy(uint16 dx, uint16 dy, const MaskBuffer &src, uint16 sx, uint16 sy, uint width, uint height) {
+ assert((width <= w) && (width <= src.w) && (height <= h) && (height <= src.h));
+
+ byte *s = src.getPtr(sx, sy);
+ byte *d = getPtr(dx, dy);
+
+ // this code assumes buffers are aligned on 4-pixels boundaries, as the original does
+ for (uint16 i = 0; i < height; i++) {
+ memcpy(d, s, (width >> 2));
+ d += internalWidth;
+ s += src.internalWidth;
+ }
+ }
+
+
};
@@ -419,7 +455,9 @@ struct BackgroundInfo {
int layers[4];
PaletteFxRange ranges[6];
- BackgroundInfo() : x(0), y(0), width(0), height(0) {
+ bool hasMask;
+
+ BackgroundInfo() : x(0), y(0), width(0), height(0), hasMask(false) {
layers[0] = layers[1] = layers[2] = layers[3] = 0;
memset(ranges, 0, sizeof(ranges));
}
diff --git a/engines/parallaction/objects.cpp b/engines/parallaction/objects.cpp
index c387484de7..d2332643ed 100644
--- a/engines/parallaction/objects.cpp
+++ b/engines/parallaction/objects.cpp
@@ -71,6 +71,20 @@ uint16 Animation::height() const {
return r.height();
}
+int16 Animation::getFrameX() const {
+ if (!gfxobj) return _left;
+ Common::Rect r;
+ gfxobj->getRect(_frame, r);
+ return r.left + _left;
+}
+
+int16 Animation::getFrameY() const {
+ if (!gfxobj) return _top;
+ Common::Rect r;
+ gfxobj->getRect(_frame, r);
+ return r.top + _top;
+}
+
uint16 Animation::getFrameNum() const {
if (!gfxobj) return 0;
return gfxobj->getNum();
@@ -115,24 +129,29 @@ int16 Program::addLocal(const char *name, int16 value, int16 min, int16 max) {
assert(_numLocals < NUM_LOCALS);
strcpy(_localNames[_numLocals], name);
- _locals[_numLocals]._value = value;
-
- _locals[_numLocals]._min = min;
- _locals[_numLocals]._max = max;
+ _locals[_numLocals].setRange(min, max);
+ _locals[_numLocals].setValue(value);
return _numLocals++;
}
-void LocalVariable::wrap() {
+void LocalVariable::setValue(int16 value) {
+ if (value >= _max)
+ value = _min;
+ if (value < _min)
+ value = _max - 1;
- if (_value >= _max)
- _value = _min;
- if (_value < _min)
- _value = _max - 1;
+ _value = value;
+}
- return;
+void LocalVariable::setRange(int16 min, int16 max) {
+ _max = max;
+ _min = min;
}
+int16 LocalVariable::getValue() const {
+ return _value;
+}
Zone::Zone() {
@@ -193,13 +212,6 @@ Zone::~Zone() {
free(_linkedName);
}
-void Zone::getRect(Common::Rect& r) const {
- r.left = _left;
- r.right = _right;
- r.top = _top;
- r.bottom = _bottom;
-}
-
void Zone::translate(int16 x, int16 y) {
_left += x;
_right += x;
@@ -273,18 +285,18 @@ Instruction::~Instruction() {
free(_text2);
}
-int16 ScriptVar::getRValue() {
+int16 ScriptVar::getValue() {
if (_flags & kParaImmediate) {
return _value;
}
if (_flags & kParaLocal) {
- return _local->_value;
+ return _local->getValue();
}
if (_flags & kParaField) {
- return *_pvalue;
+ return _field->getValue();
}
if (_flags & kParaRandom) {
@@ -296,27 +308,33 @@ int16 ScriptVar::getRValue() {
return 0;
}
-int16* ScriptVar::getLValue() {
+void ScriptVar::setValue(int16 value) {
+ if ((_flags & kParaLValue) == 0) {
+ error("Only l-value can be set");
+ }
if (_flags & kParaLocal) {
- return &_local->_value;
+ _local->setValue(value);
}
if (_flags & kParaField) {
- return _pvalue;
+ _field->setValue(value);
}
- error("Parameter is not an l-value");
-
}
void ScriptVar::setLocal(LocalVariable *local) {
_local = local;
- _flags |= kParaLocal;
+ _flags |= (kParaLocal | kParaLValue);
}
-void ScriptVar::setField(int16 *field) {
- _pvalue = field;
+void ScriptVar::setField(Animation *anim, AnimationField::AccessorFunc accessor, AnimationField::MutatorFunc mutator) {
+ _field = new AnimationField(anim, accessor, mutator);
+ _flags |= (kParaField | kParaLValue);
+}
+
+void ScriptVar::setField(Animation *anim, AnimationField::AccessorFunc accessor) {
+ _field = new AnimationField(anim, accessor);
_flags |= kParaField;
}
@@ -335,9 +353,14 @@ ScriptVar::ScriptVar() {
_flags = 0;
_local = 0;
_value = 0;
- _pvalue = 0;
+ _field = 0;
}
+ScriptVar::~ScriptVar() {
+ delete _field;
+}
+
+
Table::Table(uint32 size) : _size(size), _used(0), _disposeMemory(true) {
_data = (char**)calloc(size, sizeof(char*));
}
diff --git a/engines/parallaction/objects.h b/engines/parallaction/objects.h
index a3bf757bdb..15550c65c6 100644
--- a/engines/parallaction/objects.h
+++ b/engines/parallaction/objects.h
@@ -198,11 +198,14 @@ struct GetData { // size = 24
byte *_backup;
uint16 field_14; // unused
uint16 field_16; // unused
+ MaskBuffer _mask[2];
+ bool hasMask;
GetData() {
_icon = 0;
_backup = NULL;
gfxobj = NULL;
+ hasMask = false;
}
};
struct SpeakData { // size = 36
@@ -297,17 +300,19 @@ struct TypeData {
#define ZONENAME_LENGTH 32
struct Zone {
- char _name[ZONENAME_LENGTH];
-
+protected:
int16 _left;
int16 _top;
int16 _right;
int16 _bottom;
+
+public:
+ char _name[ZONENAME_LENGTH];
+
uint32 _type;
uint32 _flags;
uint _label;
- uint16 field_2C; // unused
- uint16 field_2E; // unused
+
TypeData u;
CommandList _commands;
Common::Point _moveTo;
@@ -320,32 +325,101 @@ struct Zone {
Zone();
virtual ~Zone();
- void getRect(Common::Rect& r) const;
void translate(int16 x, int16 y);
virtual uint16 width() const;
virtual uint16 height() const;
+
+ void setBox(int16 left, int16 top, int16 right, int16 bottom) {
+ setX(left);
+ setY(top);
+ _right = right;
+ _bottom = bottom;
+ }
+
+ void getBox(Common::Rect& r) {
+ r.left = getX();
+ r.right = getX() + width();
+ r.top = getY();
+ r.bottom = getY() + height();
+ }
+
+
+ // getters/setters
+ virtual int16 getX() { return _left; }
+ virtual void setX(int16 value) { _left = value; }
+
+ virtual int16 getY() { return _top; }
+ virtual void setY(int16 value) { _top = value; }
};
struct LocalVariable {
+protected:
int16 _value;
int16 _min;
int16 _max;
+public:
+
LocalVariable() {
_value = 0;
_min = -10000;
_max = 10000;
}
- void wrap();
+ void setRange(int16 min, int16 max);
+
+ int16 getValue() const;
+ void setValue(int16 value);
};
+
enum ParaFlags {
kParaImmediate = 1, // instruction is using an immediate parameter
kParaLocal = 2, // instruction is using a local variable
kParaField = 0x10, // instruction is using an animation's field
- kParaRandom = 0x100
+ kParaRandom = 0x100,
+
+ kParaLValue = 0x20
+};
+
+
+struct AnimationField {
+ typedef Common::Functor0Mem<int16, Animation> Accessor;
+ typedef Common::Functor1Mem<int16, void, Animation> Mutator;
+
+ typedef Accessor::FuncType AccessorFunc;
+ typedef Mutator::FuncType MutatorFunc;
+
+protected:
+ Accessor *_accessor;
+ Mutator *_mutator;
+
+public:
+ AnimationField(Animation* instance, AccessorFunc accessor, MutatorFunc mutator) {
+ _accessor = new Accessor(instance, accessor);
+ _mutator = new Mutator(instance, mutator);
+ }
+
+ AnimationField(Animation* instance, AccessorFunc accessor) {
+ _accessor = new Accessor(instance, accessor);
+ _mutator = 0;
+ }
+
+ ~AnimationField() {
+ delete _accessor;
+ delete _mutator;
+ }
+
+ int16 getValue() const {
+ assert(_accessor);
+ return _accessor->operator()();
+ }
+
+ void setValue(int16 value) {
+ assert(_mutator);
+ _mutator->operator()(value);
+ }
};
@@ -353,16 +427,18 @@ struct ScriptVar {
uint32 _flags;
int16 _value;
- int16* _pvalue;
LocalVariable* _local;
+ AnimationField* _field;
ScriptVar();
+ ~ScriptVar();
- int16 getRValue();
- int16* getLValue();
+ int16 getValue();
+ void setValue(int16 value);
void setLocal(LocalVariable *local);
- void setField(int16 *field);
+ void setField(Animation *anim, AnimationField::AccessorFunc accessor, AnimationField::MutatorFunc mutator);
+ void setField(Animation *anim, AnimationField::AccessorFunc accessor);
void setImmediate(int16 value);
void setRandom(int16 seed);
};
@@ -430,19 +506,13 @@ typedef Common::SharedPtr<Program> ProgramPtr;
typedef Common::List<ProgramPtr> ProgramList;
struct Animation : public Zone {
+protected:
+ int16 _frame;
+ int16 _z;
+public:
- Common::Point _oldPos;
GfxObj *gfxobj;
char *_scriptName;
- int16 _frame;
- uint16 field_50; // unused
- int16 _z;
- uint16 field_54; // unused
- uint16 field_56; // unused
- uint16 field_58; // unused
- uint16 field_5A; // unused
- uint16 field_5C; // unused
- uint16 field_5E; // unused
Animation();
virtual ~Animation();
@@ -452,6 +522,22 @@ struct Animation : public Zone {
byte* getFrameData(uint32 index) const;
void validateScriptVars();
+
+ int16 getFrameX() const;
+ int16 getFrameY() const;
+
+ // getters/setters used by scripts
+ int16 getX() { return _left; }
+ void setX(int16 value) { _left = value; }
+
+ int16 getY() { return _top; }
+ void setY(int16 value) { _top = value; }
+
+ int16 getZ() { return _z; }
+ void setZ(int16 value) { _z = value; }
+
+ int16 getF() { return _frame; }
+ void setF(int16 value) { _frame = value; }
};
class Table {
diff --git a/engines/parallaction/parallaction.cpp b/engines/parallaction/parallaction.cpp
index bb306c3299..c810d22b33 100644
--- a/engines/parallaction/parallaction.cpp
+++ b/engines/parallaction/parallaction.cpp
@@ -53,7 +53,7 @@ uint32 _engineFlags = 0;
uint16 _score = 1;
char _password[8];
-uint32 _commandFlags = 0;
+uint32 _globalFlags = 0;
// private stuff
@@ -85,7 +85,7 @@ Parallaction::Parallaction(OSystem *syst, const PARALLACTIONGameDescription *gam
Parallaction::~Parallaction() {
delete _debugger;
- delete _globalTable;
+ delete _globalFlagsNames;
delete _callableNames;
delete _cmdExec;
delete _programExec;
@@ -114,7 +114,7 @@ int Parallaction::init() {
_engineFlags = 0;
_objectsNames = NULL;
- _globalTable = NULL;
+ _globalFlagsNames = NULL;
_location._hasSound = false;
_baseTime = 0;
_numLocations = 0;
@@ -352,9 +352,9 @@ void Parallaction::runGame() {
if (_input->_inputMode == Input::kInputModeGame) {
_programExec->runScripts(_location._programs.begin(), _location._programs.end());
- _char._ani->_z = _char._ani->height() + _char._ani->_top;
+ _char._ani->setZ(_char._ani->height() + _char._ani->getFrameY());
if (_char._ani->gfxobj) {
- _char._ani->gfxobj->z = _char._ani->_z;
+ _char._ani->gfxobj->z = _char._ani->getZ();
}
_char._walker->walk();
drawAnimations();
@@ -452,7 +452,7 @@ void Parallaction::freeZones() {
// NOTE : this condition has been relaxed compared to the original, to allow the engine
// to retain special - needed - zones that were lost across location switches.
ZonePtr z = *it;
- if (((z->_top == -1) || (z->_left == -2)) && ((_engineFlags & kEngineQuit) == 0)) {
+ if (((z->getY() == -1) || (z->getX() == -2)) && ((_engineFlags & kEngineQuit) == 0)) {
debugC(2, kDebugExec, "freeZones preserving zone '%s'", z->_name);
it++;
} else {
@@ -510,12 +510,10 @@ Character::Character(Parallaction *vm) : _vm(vm), _ani(new Animation) {
_dummy = false;
- _ani->_left = 150;
- _ani->_top = 100;
- _ani->_z = 10;
- _ani->_oldPos.x = -1000;
- _ani->_oldPos.y = -1000;
- _ani->_frame = 0;
+ _ani->setX(150);
+ _ani->setY(100);
+ _ani->setZ(10);
+ _ani->setF(0);
_ani->_flags = kFlagsActive | kFlagsNoName;
_ani->_type = kZoneYou;
strncpy(_ani->_name, "yourself", ZONENAME_LENGTH);
@@ -542,18 +540,18 @@ Character::~Character() {
void Character::getFoot(Common::Point &foot) {
Common::Rect rect;
- _ani->gfxobj->getRect(_ani->_frame, rect);
+ _ani->gfxobj->getRect(_ani->getF(), rect);
- foot.x = _ani->_left + (rect.left + rect.width() / 2);
- foot.y = _ani->_top + (rect.top + rect.height());
+ foot.x = _ani->getX() + (rect.left + rect.width() / 2);
+ foot.y = _ani->getY() + (rect.top + rect.height());
}
void Character::setFoot(const Common::Point &foot) {
Common::Rect rect;
- _ani->gfxobj->getRect(_ani->_frame, rect);
+ _ani->gfxobj->getRect(_ani->getF(), rect);
- _ani->_left = foot.x - (rect.left + rect.width() / 2);
- _ani->_top = foot.y - (rect.top + rect.height());
+ _ani->setX(foot.x - (rect.left + rect.width() / 2));
+ _ani->setY(foot.y - (rect.top + rect.height()));
}
#if 0
@@ -677,7 +675,7 @@ void Character::updateDirection(const Common::Point& pos, const Common::Point& t
_step++;
if (dist.x == 0 && dist.y == 0) {
- _ani->_frame = frames->stillFrame[_direction];
+ _ani->setF(frames->stillFrame[_direction]);
return;
}
@@ -687,7 +685,7 @@ void Character::updateDirection(const Common::Point& pos, const Common::Point& t
dist.y = -dist.y;
_direction = (dist.x > dist.y) ? ((to.x > pos.x) ? WALK_LEFT : WALK_RIGHT) : ((to.y > pos.y) ? WALK_DOWN : WALK_UP);
- _ani->_frame = frames->firstWalkFrame[_direction] + (_step / frames->frameRepeat[_direction]) % frames->numWalkFrames[_direction];
+ _ani->setF(frames->firstWalkFrame[_direction] + (_step / frames->frameRepeat[_direction]) % frames->numWalkFrames[_direction]);
}
diff --git a/engines/parallaction/parallaction.h b/engines/parallaction/parallaction.h
index e5c5221414..fb004a25b7 100644
--- a/engines/parallaction/parallaction.h
+++ b/engines/parallaction/parallaction.h
@@ -136,7 +136,7 @@ extern uint16 _score;
extern uint16 _language;
extern uint32 _engineFlags;
extern char _saveData1[];
-extern uint32 _commandFlags;
+extern uint32 _globalFlags;
extern const char *_dinoName;
extern const char *_donnaName;
extern const char *_doughName;
@@ -263,7 +263,7 @@ public:
ZonePtr findZone(const char *name);
ZonePtr hitZone(uint32 type, uint16 x, uint16 y);
- uint16 runZone(ZonePtr z);
+ void runZone(ZonePtr z);
void freeZones();
AnimationPtr findAnimation(const char *name);
@@ -272,7 +272,7 @@ public:
void setBackground(const char *background, const char *mask, const char *path);
void freeBackground();
- Table *_globalTable;
+ Table *_globalFlagsNames;
Table *_objectsNames;
Table *_callableNames;
Table *_localFlagNames;
@@ -354,7 +354,7 @@ protected: // members
void freeCharacter();
- int16 pickupItem(ZonePtr z);
+ bool pickupItem(ZonePtr z);
void clearSet(OpcodeSet &opcodes);
@@ -370,7 +370,7 @@ public:
virtual void parseLocation(const char* name) = 0;
- void updateDoor(ZonePtr z);
+ void updateDoor(ZonePtr z, bool close);
virtual void drawAnimations() = 0;
@@ -424,6 +424,8 @@ public:
void setInternLanguage(uint id);
uint getInternLanguage();
+
+ void showZone(ZonePtr z, bool visible);
};
@@ -623,8 +625,10 @@ public:
int _part;
int _progress;
+#if 0 // disabled since I couldn't find any references to lip sync in the scripts
int16 _lipSyncVal;
uint _subtitleLipSync;
+#endif
int _subtitleY;
int _subtitle[2];
diff --git a/engines/parallaction/parallaction_br.cpp b/engines/parallaction/parallaction_br.cpp
index 761c8d1b74..bb8ddc5654 100644
--- a/engines/parallaction/parallaction_br.cpp
+++ b/engines/parallaction/parallaction_br.cpp
@@ -193,7 +193,7 @@ void Parallaction_br::initPart() {
memset(_counters, 0, ARRAYSIZE(_counters));
- _globalTable = _disk->loadTable("global");
+ _globalFlagsNames = _disk->loadTable("global");
_objectsNames = _disk->loadTable("objects");
_countersNames = _disk->loadTable("counters");
@@ -208,11 +208,11 @@ void Parallaction_br::initPart() {
void Parallaction_br::freePart() {
- delete _globalTable;
+ delete _globalFlagsNames;
delete _objectsNames;
delete _countersNames;
- _globalTable = 0;
+ _globalFlagsNames = 0;
_objectsNames = 0;
_countersNames = 0;
}
diff --git a/engines/parallaction/parallaction_ns.cpp b/engines/parallaction/parallaction_ns.cpp
index e81492e655..61f2859e8a 100644
--- a/engines/parallaction/parallaction_ns.cpp
+++ b/engines/parallaction/parallaction_ns.cpp
@@ -218,7 +218,7 @@ void Parallaction_ns::callFunction(uint index, void* parm) {
int Parallaction_ns::go() {
renameOldSavefiles();
- _globalTable = _disk->loadTable("global");
+ _globalFlagsNames = _disk->loadTable("global");
startGui();
@@ -318,14 +318,10 @@ void Parallaction_ns::changeLocation(char *location) {
strcpy(_saveData1, locname.location());
parseLocation(_saveData1);
- _char._ani->_oldPos.x = -1000;
- _char._ani->_oldPos.y = -1000;
-
- _char._ani->field_50 = 0;
if (_location._startPosition.x != -1000) {
- _char._ani->_left = _location._startPosition.x;
- _char._ani->_top = _location._startPosition.y;
- _char._ani->_frame = _location._startFrame;
+ _char._ani->setX(_location._startPosition.x);
+ _char._ani->setY(_location._startPosition.y);
+ _char._ani->setF(_location._startFrame);
_location._startPosition.y = -1000;
_location._startPosition.x = -1000;
}
@@ -438,7 +434,7 @@ void Parallaction_ns::cleanupGame() {
// this code saves main character animation from being removed from the following code
_location._animations.remove(_char._ani);
_numLocations = 0;
- _commandFlags = 0;
+ _globalFlags = 0;
memset(_localFlags, 0, sizeof(_localFlags));
memset(_locationNames, 0, sizeof(_locationNames));
diff --git a/engines/parallaction/parser.cpp b/engines/parallaction/parser.cpp
index 6de0a7d7f5..8e30a631e4 100644
--- a/engines/parallaction/parser.cpp
+++ b/engines/parallaction/parser.cpp
@@ -28,7 +28,10 @@
namespace Parallaction {
-char _tokens[20][MAX_TOKEN_LEN];
+#define MAX_TOKENS 50
+
+int _numTokens;
+char _tokens[MAX_TOKENS][MAX_TOKEN_LEN];
Script::Script(Common::ReadStream *input, bool disposeSource) : _input(input), _disposeSource(disposeSource), _line(0) {}
@@ -65,9 +68,11 @@ char *Script::readLine(char *buf, size_t bufSize) {
void Script::clearTokens() {
- for (uint16 i = 0; i < 20; i++)
+ for (uint16 i = 0; i < MAX_TOKENS; i++)
_tokens[i][0] = '\0';
+ _numTokens = 0;
+
return;
}
@@ -88,7 +93,7 @@ void Script::skip(const char* endToken) {
//
// The routine returns the unparsed portion of the input string 's'.
//
-char *parseNextToken(char *s, char *tok, uint16 count, const char *brk, bool ignoreQuotes) {
+char *Script::parseNextToken(char *s, char *tok, uint16 count, const char *brk, bool ignoreQuotes) {
enum STATES { NORMAL, QUOTED };
@@ -151,12 +156,14 @@ char *parseNextToken(char *s, char *tok, uint16 count, const char *brk, bool ign
uint16 Script::fillTokens(char* line) {
uint16 i = 0;
- while (strlen(line) > 0 && i < 20) {
+ while (strlen(line) > 0 && i < MAX_TOKENS) {
line = parseNextToken(line, _tokens[i], MAX_TOKEN_LEN, " \t\n");
line = Common::ltrim(line);
i++;
}
+ _numTokens = i;
+
return i;
}
@@ -243,4 +250,185 @@ void Parser::parseStatement() {
}
+#define BLOCK_BASE 100
+
+class StatementDef {
+protected:
+ Common::String makeLineFromTokens() {
+ Common::String space(" ");
+ Common::String newLine("\n");
+ Common::String text;
+ for (int i = 0; i < _numTokens; i++)
+ text += (Common::String(_tokens[i]) + space);
+ text.deleteLastChar();
+ text += newLine;
+ return text;
+ }
+
+public:
+ uint _score;
+ const char* _name;
+
+
+ StatementDef(uint score, const char *name) : _score(score), _name(name) { }
+ virtual ~StatementDef() { }
+
+ virtual Common::String makeLine(Script &script) = 0;
+
+};
+
+
+class SimpleStatementDef : public StatementDef {
+
+public:
+ SimpleStatementDef(uint score, const char *name) : StatementDef(score, name) { }
+
+ Common::String makeLine(Script &script) {
+ return makeLineFromTokens();
+ }
+
+};
+
+
+
+class BlockStatementDef : public StatementDef {
+
+ const char* _ending1;
+ const char* _ending2;
+
+public:
+ BlockStatementDef(uint score, const char *name, const char *ending1, const char *ending2 = 0) : StatementDef(score, name), _ending1(ending1),
+ _ending2(ending2) { }
+
+ Common::String makeLine(Script &script) {
+ Common::String text = makeLineFromTokens();
+ bool end;
+ do {
+ script.readLineToken(true);
+ text += makeLineFromTokens();
+ end = !scumm_stricmp(_ending1, _tokens[0]) || (_ending2 && !scumm_stricmp(_ending2, _tokens[0]));
+ } while (!end);
+ return text;
+ }
+
+};
+
+class CommentStatementDef : public StatementDef {
+
+ Common::String parseComment(Script &script) {
+ Common::String result;
+ char buf[401];
+
+ do {
+ script.readLine(buf, 400);
+ buf[strlen(buf)-1] = '\0';
+ if (!scumm_stricmp(buf, "endtext"))
+ break;
+ result += Common::String(buf) + "\n";
+ } while (true);
+ result += "endtext\n";
+ return result;
+ }
+
+public:
+ CommentStatementDef(uint score, const char *name) : StatementDef(score, name) { }
+
+ Common::String makeLine(Script &script) {
+ Common::String text = makeLineFromTokens();
+ text += parseComment(script);
+ return text;
+ }
+
+};
+
+
+
+
+PreProcessor::PreProcessor() {
+ _defs.push_back(new SimpleStatementDef(1, "disk" ));
+ _defs.push_back(new SimpleStatementDef(2, "location" ));
+ _defs.push_back(new SimpleStatementDef(3, "localflags" ));
+ _defs.push_back(new SimpleStatementDef(4, "flags" ));
+ _defs.push_back(new SimpleStatementDef(5, "zeta" ));
+ _defs.push_back(new SimpleStatementDef(6, "music" ));
+ _defs.push_back(new SimpleStatementDef(7, "sound" ));
+ _defs.push_back(new SimpleStatementDef(8, "mask" ));
+ _defs.push_back(new SimpleStatementDef(9, "path" ));
+ _defs.push_back(new SimpleStatementDef(10, "character" ));
+ _defs.push_back(new CommentStatementDef(11, "comment" ));
+ _defs.push_back(new CommentStatementDef(12, "endcomment" ));
+ _defs.push_back(new BlockStatementDef(13, "ifchar", "endif" ));
+ _defs.push_back(new BlockStatementDef(BLOCK_BASE, "zone", "endanimation", "endzone" ));
+ _defs.push_back(new BlockStatementDef(BLOCK_BASE, "animation", "endanimation", "endzone" ));
+ _defs.push_back(new BlockStatementDef(1000, "commands", "endcommands" ));
+ _defs.push_back(new BlockStatementDef(1001, "acommands", "endcommands" ));
+ _defs.push_back(new BlockStatementDef(1002, "escape", "endcommands" ));
+ _defs.push_back(new SimpleStatementDef(2000, "endlocation"));
+}
+
+PreProcessor::~PreProcessor() {
+ DefList::iterator it = _defs.begin();
+ for (; it != _defs.end(); it++) {
+ delete *it;
+ }
+}
+
+StatementDef* PreProcessor::findDef(const char* name) {
+ DefList::iterator it = _defs.begin();
+ for (; it != _defs.end(); it++) {
+ if (!scumm_stricmp((*it)->_name, name)) {
+ return *it;
+ }
+ }
+ return 0;
+}
+
+
+
+uint PreProcessor::getDefScore(StatementDef* def) {
+ if (def->_score == BLOCK_BASE) {
+ _numZones++;
+ return (_numZones + BLOCK_BASE);
+ }
+ return def->_score;
+}
+
+
+void PreProcessor::preprocessScript(Script &script, StatementList &list) {
+ _numZones = 0;
+ Common::String text;
+ do {
+ script.readLineToken(false);
+ if (_numTokens == 0)
+ break;
+
+ StatementDef *def = findDef(_tokens[0]);
+ assert(def);
+
+ text = def->makeLine(script);
+ int score = getDefScore(def);
+ list.push_back(StatementListNode(score, def->_name, text));
+ } while (true);
+ Common::sort(list.begin(), list.end());
+}
+
+
+
+
+void testPreprocessing(Parallaction *vm, const char *filename) {
+ Script *script = vm->_disk->loadLocation(filename);
+ StatementList list;
+ PreProcessor pp;
+ pp.preprocessScript(*script, list);
+ delete script;
+ Common::DumpFile dump;
+ dump.open(filename);
+ StatementList::iterator it = list.begin();
+ for ( ; it != list.end(); it++) {
+ dump.write((*it)._text.c_str(), (*it)._text.size());
+ }
+ dump.close();
+}
+
+
} // namespace Parallaction
diff --git a/engines/parallaction/parser.h b/engines/parallaction/parser.h
index 79e6cf6640..e622bfd81f 100644
--- a/engines/parallaction/parser.h
+++ b/engines/parallaction/parser.h
@@ -32,9 +32,8 @@
namespace Parallaction {
-char *parseNextToken(char *s, char *tok, uint16 count, const char *brk, bool ignoreQuotes = false);
-
#define MAX_TOKEN_LEN 50
+extern int _numTokens;
extern char _tokens[][MAX_TOKEN_LEN];
class Script {
@@ -45,6 +44,7 @@ class Script {
void clearTokens();
uint16 fillTokens(char* line);
+ char *parseNextToken(char *s, char *tok, uint16 count, const char *brk, bool ignoreQuotes = false);
public:
Script(Common::ReadStream *, bool _disposeSource = false);
@@ -63,6 +63,7 @@ typedef Common::Functor0<void> Opcode;
typedef Common::Array<const Opcode*> OpcodeSet;
+
class Parser {
public:
@@ -95,6 +96,7 @@ public:
class Parallaction_ns;
class Parallaction_br;
+
class LocationParser_ns {
protected:
@@ -129,9 +131,6 @@ protected:
// BRA specific
int numZones;
BackgroundInfo *info;
- char *bgName;
- char *maskName;
- char *pathName;
char *characterName;
} ctxt;
@@ -240,6 +239,23 @@ public:
};
+/*
+ TODO: adapt the parser to effectively use the
+ statement list provided by preprocessor as its
+ input, instead of relying on the current Script
+ class.
+
+ This would need a major rewrite of the parsing
+ system!
+
+ parseNextToken could then be sealed into the
+ PreProcessor class forever, together with the
+ _tokens[] and _numTokens stuff, now dangling as
+ global objects.
+
+ NS balloons code should be dealt with before,
+ though.
+*/
class LocationParser_br : public LocationParser_ns {
protected:
@@ -287,6 +303,7 @@ protected:
virtual void parseZoneTypeBlock(ZonePtr z);
void parsePathData(ZonePtr z);
+ void parseGetData(ZonePtr z);
public:
LocationParser_br(Parallaction_br *vm) : LocationParser_ns((Parallaction_ns*)vm), _vm(vm) {
@@ -402,6 +419,117 @@ public:
};
+
+/*
+ This simple stream is temporarily needed to hook the
+ preprocessor output to the parser. It will go away
+ when the parser is rewritten to fully exploit the
+ statement list provided by the preprocessor.
+*/
+
+class ReadStringStream : public Common::ReadStream {
+
+ char *_text;
+ uint32 _pos;
+ uint32 _size;
+
+public:
+ ReadStringStream(const Common::String &text) {
+ _text = new char[text.size() + 1];
+ strcpy(_text, text.c_str());
+ _size = text.size();
+ _pos = 0;
+ }
+
+ ~ReadStringStream() {
+ delete []_text;
+ }
+
+ uint32 read(void *buffer, uint32 size) {
+ if (_pos + size > _size) {
+ size = _size - _pos;
+ }
+ memcpy(buffer, _text + _pos, size);
+ _pos += size;
+ return size;
+ }
+
+ bool eos() const {
+ return _pos == _size;
+ }
+
+};
+
+
+/*
+ Demented as it may sound, the success of a parsing operation in the
+ original BRA depends on what has been parsed before. The game features
+ an innovative chaos system that involves the parser and the very game
+ engine, in order to inflict the user an unforgettable game experience.
+
+ Ok, now for the serious stuff.
+
+ The PreProcessor implemented here fixes the location scripts before
+ they are fed to the parser. It tries to do so by a preliminary scan
+ of the text file, during which a score is assigned to each statement
+ (more on this later). When the whole file has been analyzed, the
+ statements are sorted according to their score, to create a parsable
+ sequence.
+
+ For parsing, the statements in location scripts can be conveniently
+ divided into 3 groups:
+
+ * location definitions
+ * element definitions
+ * start-up commands
+
+ Since the parsing of element definitions requires location parameters
+ to be set, location definitions should be encountered first in the
+ script. Start-up commands in turn may reference elements, so they can
+ be parsed last. The first goal is to make sure the parser gets these
+ three sets in this order.
+
+ Location definitions must also be presented in a certain sequence,
+ because resource files are not fully self-describing. In short, some
+ critical game data in contained in certain files, that must obviously
+ be read before any other can be analyzed. This is the second goal.
+
+ TODO: some words about actual implementation.
+*/
+
+class StatementDef;
+
+struct StatementListNode {
+ int _score;
+ Common::String _name;
+ Common::String _text;
+
+ StatementListNode(int score, const Common::String &name, const Common::String &text) : _score(score), _name(name), _text(text) { }
+
+ bool operator<(const StatementListNode& node) const {
+ return _score < node._score;
+ }
+};
+typedef Common::List<StatementListNode> StatementList;
+
+
+class PreProcessor {
+ typedef Common::List<StatementDef*> DefList;
+
+ int _numZones;
+ DefList _defs;
+
+ StatementDef* findDef(const char* name);
+ uint getDefScore(StatementDef*);
+
+public:
+ PreProcessor();
+ ~PreProcessor();
+ void preprocessScript(Script &script, StatementList &list);
+};
+
+
+
} // namespace Parallaction
#endif
diff --git a/engines/parallaction/parser_br.cpp b/engines/parallaction/parser_br.cpp
index 3b446805d7..20800abc33 100644
--- a/engines/parallaction/parser_br.cpp
+++ b/engines/parallaction/parser_br.cpp
@@ -25,6 +25,7 @@
#include "parallaction/parallaction.h"
+
#include "parallaction/sound.h"
namespace Parallaction {
@@ -104,6 +105,7 @@ namespace Parallaction {
#define INST_ENDIF 30
#define INST_STOP 31
+
const char *_zoneTypeNamesRes_br[] = {
"examine",
"door",
@@ -314,7 +316,6 @@ DECLARE_LOCATION_PARSER(location) {
debugC(7, kDebugParser, "LOCATION_PARSER(location) ");
strcpy(_vm->_location._name, _tokens[1]);
- ctxt.bgName = strdup(_tokens[1]);
bool flip = false;
int nextToken;
@@ -329,15 +330,17 @@ DECLARE_LOCATION_PARSER(location) {
// TODO: handle background horizontal flip (via a context parameter)
if (_tokens[nextToken][0] != '\0') {
- _vm->_char._ani->_left = atoi(_tokens[nextToken]);
+ _vm->_char._ani->setX(atoi(_tokens[nextToken]));
nextToken++;
- _vm->_char._ani->_top = atoi(_tokens[nextToken]);
+ _vm->_char._ani->setY(atoi(_tokens[nextToken]));
nextToken++;
}
if (_tokens[nextToken][0] != '\0') {
- _vm->_char._ani->_frame = atoi(_tokens[nextToken]);
+ _vm->_char._ani->setF(atoi(_tokens[nextToken]));
}
+
+ _vm->_disk->loadScenery(*ctxt.info, _tokens[1], 0, 0);
}
@@ -463,17 +466,20 @@ DECLARE_LOCATION_PARSER(null) {
DECLARE_LOCATION_PARSER(mask) {
debugC(7, kDebugParser, "LOCATION_PARSER(mask) ");
- ctxt.maskName = strdup(_tokens[1]);
- ctxt.info->layers[0] = atoi(_tokens[2]);
- ctxt.info->layers[1] = atoi(_tokens[3]);
- ctxt.info->layers[2] = atoi(_tokens[4]);
+ ctxt.info->layers[0] = 0;
+ ctxt.info->layers[1] = atoi(_tokens[2]);
+ ctxt.info->layers[2] = atoi(_tokens[3]);
+ ctxt.info->layers[3] = atoi(_tokens[4]);
+
+ _vm->_disk->loadScenery(*ctxt.info, 0, _tokens[1], 0);
+ ctxt.info->hasMask = true;
}
DECLARE_LOCATION_PARSER(path) {
debugC(7, kDebugParser, "LOCATION_PARSER(path) ");
- ctxt.pathName = strdup(_tokens[1]);
+ _vm->_disk->loadScenery(*ctxt.info, 0, 0, _tokens[1]);
}
@@ -714,10 +720,7 @@ DECLARE_ZONE_PARSER(limits) {
ctxt.z->_linkedAnim = _vm->findAnimation(_tokens[1]);
ctxt.z->_linkedName = strdup(_tokens[1]);
} else {
- ctxt.z->_left = atoi(_tokens[1]);
- ctxt.z->_top = atoi(_tokens[2]);
- ctxt.z->_right = atoi(_tokens[3]);
- ctxt.z->_bottom = atoi(_tokens[4]);
+ ctxt.z->setBox(atoi(_tokens[1]), atoi(_tokens[2]), atoi(_tokens[3]), atoi(_tokens[4]));
}
}
@@ -768,6 +771,49 @@ void LocationParser_br::parsePathData(ZonePtr z) {
z->u.path = data;
}
+void LocationParser_br::parseGetData(ZonePtr z) {
+
+ GetData *data = new GetData;
+
+ do {
+
+ if (!scumm_stricmp(_tokens[0], "file")) {
+
+ GfxObj *obj = _vm->_gfx->loadGet(_tokens[1]);
+ obj->frame = 0;
+ obj->x = z->getX();
+ obj->y = z->getY();
+ data->gfxobj = obj;
+ }
+
+ if (!scumm_stricmp(_tokens[0], "mask")) {
+ if (ctxt.info->hasMask) {
+ Common::Rect rect;
+ data->gfxobj->getRect(0, rect);
+ data->_mask[0].create(rect.width(), rect.height());
+ _vm->_disk->loadMask(_tokens[1], data->_mask[0]);
+ data->_mask[1].create(rect.width(), rect.height());
+ data->_mask[1].bltCopy(0, 0, ctxt.info->mask, data->gfxobj->x, data->gfxobj->y, data->_mask->w, data->_mask->h);
+ data->hasMask = true;
+ } else {
+ warning("Mask for zone '%s' ignored, since background doesn't have one", z->_name);
+ }
+ }
+
+ if (!scumm_stricmp(_tokens[0], "path")) {
+
+ }
+
+ if (!scumm_stricmp(_tokens[0], "icon")) {
+ data->_icon = 4 + _vm->_objectsNames->lookup(_tokens[1]);
+ }
+
+ _script->readLineToken(true);
+ } while (scumm_stricmp(_tokens[0], "endzone"));
+
+ z->u.get = data;
+}
+
void LocationParser_br::parseZoneTypeBlock(ZonePtr z) {
debugC(7, kDebugParser, "parseZoneTypeBlock(name: %s, type: %x)", z->_name, z->_type);
@@ -822,10 +868,10 @@ DECLARE_ANIM_PARSER(file) {
DECLARE_ANIM_PARSER(position) {
debugC(7, kDebugParser, "ANIM_PARSER(position) ");
- ctxt.a->_left = atoi(_tokens[1]);
- ctxt.a->_top = atoi(_tokens[2]);
- ctxt.a->_z = atoi(_tokens[3]);
- ctxt.a->_frame = atoi(_tokens[4]);
+ ctxt.a->setX(atoi(_tokens[1]));
+ ctxt.a->setY(atoi(_tokens[2]));
+ ctxt.a->setZ(atoi(_tokens[3]));
+ ctxt.a->setF(atoi(_tokens[4]));
}
@@ -841,15 +887,14 @@ DECLARE_ANIM_PARSER(moveto) {
DECLARE_ANIM_PARSER(endanimation) {
debugC(7, kDebugParser, "ANIM_PARSER(endanimation) ");
-
+#if 0
+ // I have disabled the following code since it seems useless.
+ // I will remove it after mask processing is done.
if (ctxt.a->gfxobj) {
ctxt.a->_right = ctxt.a->width();
ctxt.a->_bottom = ctxt.a->height();
}
-
- ctxt.a->_oldPos.x = -1000;
- ctxt.a->_oldPos.y = -1000;
-
+#endif
ctxt.a->_flags |= 0x1000000;
_parser->popTables();
@@ -992,16 +1037,16 @@ void ProgramParser_br::parseRValue(ScriptVar &v, const char *str) {
a = AnimationPtr(ctxt.a);
if (str[0] == 'X') {
- v.setField(&a->_left);
+ v.setField(a.get(), &Animation::getX);
} else
if (str[0] == 'Y') {
- v.setField(&a->_top);
+ v.setField(a.get(), &Animation::getY);
} else
if (str[0] == 'Z') {
- v.setField(&a->_z);
+ v.setField(a.get(), &Animation::getZ);
} else
if (str[0] == 'F') {
- v.setField(&a->_frame);
+ v.setField(a.get(), &Animation::getF);
} else
if (str[0] == 'N') {
v.setImmediate(a->getFrameNum());
@@ -1010,7 +1055,10 @@ void ProgramParser_br::parseRValue(ScriptVar &v, const char *str) {
v.setRandom(atoi(&str[1]));
} else
if (str[0] == 'L') {
+#if 0 // disabled because no references to lip sync has been found in the scripts
v.setField(&_vm->_lipSyncVal);
+#endif
+ warning("Lip sync instruction encountered! Please notify the team!");
}
}
@@ -1168,31 +1216,50 @@ void ProgramParser_br::init() {
INSTRUCTION_PARSER(endscript);
}
+
+/*
+ Ancillary routine to support hooking preprocessor and
+ parser.
+*/
+Common::ReadStream *getStream(StatementList &list) {
+ Common::String text;
+ StatementList::iterator it = list.begin();
+ for ( ; it != list.end(); it++) {
+ text += (*it)._text;
+ }
+ return new ReadStringStream(text);
+}
+
void LocationParser_br::parse(Script *script) {
+ PreProcessor pp;
+ StatementList list;
+ pp.preprocessScript(*script, list);
+ Script *script2 = new Script(getStream(list), true);
+
ctxt.numZones = 0;
- ctxt.bgName = 0;
- ctxt.maskName = 0;
- ctxt.pathName = 0;
ctxt.characterName = 0;
ctxt.info = new BackgroundInfo;
- LocationParser_ns::parse(script);
+ LocationParser_ns::parse(script2);
- _vm->_disk->loadScenery(*ctxt.info, ctxt.bgName, ctxt.maskName, ctxt.pathName);
_vm->_gfx->setBackground(kBackgroundLocation, ctxt.info);
_vm->_pathBuffer = &ctxt.info->path;
+ ZoneList::iterator it = _vm->_location._zones.begin();
+ for ( ; it != _vm->_location._zones.end(); it++) {
+ bool visible = ((*it)->_flags & kFlagsRemove) == 0;
+ _vm->showZone((*it), visible);
+ }
+
if (ctxt.characterName) {
_vm->changeCharacter(ctxt.characterName);
}
- free(ctxt.bgName);
- free(ctxt.maskName);
- free(ctxt.pathName);
free(ctxt.characterName);
+ delete script2;
}
} // namespace Parallaction
diff --git a/engines/parallaction/parser_ns.cpp b/engines/parallaction/parser_ns.cpp
index ad0f714fdc..4b90e2364f 100644
--- a/engines/parallaction/parser_ns.cpp
+++ b/engines/parallaction/parser_ns.cpp
@@ -221,9 +221,6 @@ DECLARE_ANIM_PARSER(type) {
}
}
- ctxt.a->_oldPos.x = -1000;
- ctxt.a->_oldPos.y = -1000;
-
ctxt.a->_flags |= 0x1000000;
_parser->popTables();
@@ -267,9 +264,9 @@ DECLARE_ANIM_PARSER(file) {
DECLARE_ANIM_PARSER(position) {
debugC(7, kDebugParser, "ANIM_PARSER(position) ");
- ctxt.a->_left = atoi(_tokens[1]);
- ctxt.a->_top = atoi(_tokens[2]);
- ctxt.a->_z = atoi(_tokens[3]);
+ ctxt.a->setX(atoi(_tokens[1]));
+ ctxt.a->setY(atoi(_tokens[2]));
+ ctxt.a->setZ(atoi(_tokens[3]));
}
@@ -284,10 +281,6 @@ DECLARE_ANIM_PARSER(moveto) {
DECLARE_ANIM_PARSER(endanimation) {
debugC(7, kDebugParser, "ANIM_PARSER(endanimation) ");
-
- ctxt.a->_oldPos.x = -1000;
- ctxt.a->_oldPos.y = -1000;
-
ctxt.a->_flags |= 0x1000000;
_parser->popTables();
@@ -523,7 +516,7 @@ DECLARE_INSTRUCTION_PARSER(defLocal) {
}
ctxt.inst->_opA.setLocal(&ctxt.locals[index]);
- ctxt.inst->_opB.setImmediate(ctxt.locals[index]._value);
+ ctxt.inst->_opB.setImmediate(ctxt.locals[index].getValue());
ctxt.inst->_index = INST_SET;
}
@@ -558,16 +551,16 @@ void ProgramParser_ns::parseRValue(ScriptVar &v, const char *str) {
}
if (str[0] == 'X') {
- v.setField(&a->_left);
+ v.setField(a.get(), &Animation::getX);
} else
if (str[0] == 'Y') {
- v.setField(&a->_top);
+ v.setField(a.get(), &Animation::getY);
} else
if (str[0] == 'Z') {
- v.setField(&a->_z);
+ v.setField(a.get(), &Animation::getZ);
} else
if (str[0] == 'F') {
- v.setField(&a->_frame);
+ v.setField(a.get(), &Animation::getF);
}
}
@@ -588,16 +581,16 @@ void ProgramParser_ns::parseLValue(ScriptVar &v, const char *str) {
}
if (str[0] == 'X') {
- v.setField(&a->_left);
+ v.setField(a.get(), &Animation::getX, &Animation::setX);
} else
if (str[0] == 'Y') {
- v.setField(&a->_top);
+ v.setField(a.get(), &Animation::getY, &Animation::setY);
} else
if (str[0] == 'Z') {
- v.setField(&a->_z);
+ v.setField(a.get(), &Animation::getZ, &Animation::setZ);
} else
if (str[0] == 'F') {
- v.setField(&a->_frame);
+ v.setField(a.get(), &Animation::getF, &Animation::setF);
}
}
@@ -608,7 +601,7 @@ DECLARE_COMMAND_PARSER(flags) {
createCommand(_parser->_lookup);
- if (_vm->_globalTable->lookup(_tokens[1]) == Table::notFound) {
+ if (_vm->_globalFlagsNames->lookup(_tokens[1]) == Table::notFound) {
do {
char _al = _vm->_localFlagNames->lookup(_tokens[ctxt.nextToken]);
ctxt.nextToken++;
@@ -618,7 +611,7 @@ DECLARE_COMMAND_PARSER(flags) {
} else {
ctxt.cmd->u._flags |= kFlagsGlobal;
do {
- char _al = _vm->_globalTable->lookup(_tokens[1]);
+ char _al = _vm->_globalFlagsNames->lookup(_tokens[1]);
ctxt.nextToken++;
ctxt.cmd->u._flags |= 1 << (_al - 1);
} while (!scumm_stricmp(_tokens[ctxt.nextToken++], "|"));
@@ -759,11 +752,11 @@ void LocationParser_ns::parseCommandFlags() {
cmd->_flagsOn |= kFlagsEnter;
} else
if (!scumm_strnicmp(_tokens[_si], "no", 2)) {
- byte _al = _vm->_globalTable->lookup(&_tokens[_si][2]);
+ byte _al = _vm->_globalFlagsNames->lookup(&_tokens[_si][2]);
assert(_al != Table::notFound);
cmd->_flagsOff |= 1 << (_al - 1);
} else {
- byte _al = _vm->_globalTable->lookup(_tokens[_si]);
+ byte _al = _vm->_globalFlagsNames->lookup(_tokens[_si]);
assert(_al != Table::notFound);
cmd->_flagsOn |= 1 << (_al - 1);
}
@@ -880,7 +873,7 @@ Answer *LocationParser_ns::parseAnswer() {
if (!scumm_stricmp(_tokens[1], "global")) {
token = 2;
- flagNames = _vm->_globalTable;
+ flagNames = _vm->_globalFlagsNames;
answer->_yesFlags |= kFlagsGlobal;
} else {
token = 1;
@@ -992,12 +985,12 @@ DECLARE_LOCATION_PARSER(location) {
_vm->switchBackground(_vm->_location._name, mask);
if (_tokens[2][0] != '\0') {
- _vm->_char._ani->_left = atoi(_tokens[2]);
- _vm->_char._ani->_top = atoi(_tokens[3]);
+ _vm->_char._ani->setX(atoi(_tokens[2]));
+ _vm->_char._ani->setY(atoi(_tokens[3]));
}
if (_tokens[4][0] != '\0') {
- _vm->_char._ani->_frame = atoi(_tokens[4]);
+ _vm->_char._ani->setF(atoi(_tokens[4]));
}
}
@@ -1316,11 +1309,7 @@ DECLARE_ZONE_PARSER(endzone) {
DECLARE_ZONE_PARSER(limits) {
debugC(7, kDebugParser, "ZONE_PARSER(limits) ");
-
- ctxt.z->_left = atoi(_tokens[1]);
- ctxt.z->_top = atoi(_tokens[2]);
- ctxt.z->_right = atoi(_tokens[3]);
- ctxt.z->_bottom = atoi(_tokens[4]);
+ ctxt.z->setBox(atoi(_tokens[1]), atoi(_tokens[2]), atoi(_tokens[3]), atoi(_tokens[4]));
}
@@ -1411,8 +1400,8 @@ void LocationParser_ns::parseGetData(ZonePtr z) {
GfxObj *obj = _vm->_gfx->loadGet(_tokens[1]);
obj->frame = 0;
- obj->x = z->_left;
- obj->y = z->_top;
+ obj->x = z->getX();
+ obj->y = z->getY();
_vm->_gfx->showGfxObj(obj, visible);
data->gfxobj = obj;
@@ -1474,8 +1463,8 @@ void LocationParser_ns::parseDoorData(ZonePtr z) {
GfxObj *obj = _vm->_gfx->loadDoor(_tokens[1]);
obj->frame = frame;
- obj->x = z->_left;
- obj->y = z->_top;
+ obj->x = z->getX();
+ obj->y = z->getY();
_vm->_gfx->showGfxObj(obj, true);
data->gfxobj = obj;
diff --git a/engines/parallaction/saveload.cpp b/engines/parallaction/saveload.cpp
index 87a556182e..44613c970c 100644
--- a/engines/parallaction/saveload.cpp
+++ b/engines/parallaction/saveload.cpp
@@ -125,7 +125,7 @@ void Parallaction_ns::doLoadGame(uint16 slot) {
_score = atoi(s);
f->readLine(s, 15);
- _commandFlags = atoi(s);
+ _globalFlags = atoi(s);
f->readLine(s, 15);
@@ -207,13 +207,13 @@ void Parallaction_ns::doSaveGame(uint16 slot, const char* name) {
sprintf(s, "%s\n", _saveData1);
f->writeString(s);
- sprintf(s, "%d\n", _char._ani->_left);
+ sprintf(s, "%d\n", _char._ani->getX());
f->writeString(s);
- sprintf(s, "%d\n", _char._ani->_top);
+ sprintf(s, "%d\n", _char._ani->getY());
f->writeString(s);
sprintf(s, "%d\n", _score);
f->writeString(s);
- sprintf(s, "%u\n", _commandFlags);
+ sprintf(s, "%u\n", _globalFlags);
f->writeString(s);
sprintf(s, "%d\n", _numLocations);
diff --git a/engines/parallaction/walk.cpp b/engines/parallaction/walk.cpp
index bf8f423fd5..5fd67d26dc 100644
--- a/engines/parallaction/walk.cpp
+++ b/engines/parallaction/walk.cpp
@@ -479,7 +479,7 @@ void PathWalker_BR::finalizeWalk() {
_char.setFoot(foot);
#endif
- _ch->_ani->_frame = _dirFrame; // temporary solution
+ _ch->_ani->setF(_dirFrame); // temporary solution
#if 0
// TODO: support scrolling ;)
@@ -515,7 +515,7 @@ void PathWalker_BR::walk() {
GfxObj *obj = _ch->_ani->gfxobj;
Common::Rect rect;
- obj->getRect(_ch->_ani->_frame, rect);
+ obj->getRect(_ch->_ani->getF(), rect);
uint scale;
if (rect.bottom > _vm->_location._zeta0) {
@@ -609,11 +609,11 @@ void PathWalker_BR::walk() {
if (_fieldC) {
debugC(9, kDebugWalk, "PathWalker_BR::walk, foot moved from (%i, %i) to (%i, %i)\n", _startFoot.x, _startFoot.y, newpos.x, newpos.y);
- _ch->_ani->_frame = walkFrame + _dirFrame + 1;
+ _ch->_ani->setF(walkFrame + _dirFrame + 1);
_startFoot.x = newpos.x;
_startFoot.y = newpos.y;
_ch->setFoot(_startFoot);
- _ch->_ani->_z = newpos.y;
+ _ch->_ani->setZ(newpos.y);
}
if (_fieldC || !_ch->_walkPath.empty()) {
diff --git a/engines/queen/queen.h b/engines/queen/queen.h
index 0beb557b36..1eea43e882 100644
--- a/engines/queen/queen.h
+++ b/engines/queen/queen.h
@@ -29,7 +29,7 @@
#include "engines/engine.h"
namespace Common {
- class InSaveFile;
+ class SeekableReadStream;
}
#if defined(_WIN32_WCE) && (_WIN32_WCE <= 300)
@@ -112,7 +112,7 @@ public:
void makeGameStateName(int slot, char *buf) const;
int getGameStateSlot(const char *filename) const;
void findGameStateDescriptions(char descriptions[100][32]);
- Common::InSaveFile *readGameStateHeader(int slot, GameStateHeader *gsh);
+ Common::SeekableReadStream *readGameStateHeader(int slot, GameStateHeader *gsh);
enum {
SAVESTATE_CUR_VER = 1,
diff --git a/engines/saga/displayinfo.h b/engines/saga/displayinfo.h
index 7fee5fbee1..3775e904ff 100644
--- a/engines/saga/displayinfo.h
+++ b/engines/saga/displayinfo.h
@@ -348,9 +348,9 @@ static PanelButton IHNM_QuitPanelButtons[] = {
};
static PanelButton IHNM_LoadPanelButtons[] = {
- // TODO
- {kPanelButtonLoad, 101,19, 60,16, kTextOK,'o',0, 0,0,0},
- {kPanelButtonLoadText, -1,5, 0,0, kTextLoadSuccessful,'-',0, 0,0,0},
+ {kPanelButtonLoad, 26,80, 80,25, kTextOK,'o',0, 0,0,0},
+ {kPanelButtonLoad, 156,80, 80,25, kTextCancel,'c',0, 0,0,0},
+ {kPanelButtonLoadText, -1,30, 0,0, kTextLoadSavedGame,'-',0, 0,0,0},
};
static PanelButton IHNM_SavePanelButtons[] = {
diff --git a/engines/saga/interface.cpp b/engines/saga/interface.cpp
index 1d048baaad..256e231f57 100644
--- a/engines/saga/interface.cpp
+++ b/engines/saga/interface.cpp
@@ -94,7 +94,7 @@ static int IHNMTextStringIdsLUT[56] = {
8, // Give
10, // Options
11, // Test
- 12, //
+ 12, // Demo
13, // Help
14, // Quit Game
16, // Fast
@@ -905,10 +905,13 @@ void Interface::drawPanelText(Surface *ds, InterfacePanel *panel, PanelButton *p
textFont = kKnownFontMedium;
textShadowKnownColor = kKnownColorVerbTextShadow;
} else {
- if (panelButton->id < 39 || panelButton->id > 50) {
+ if ((panelButton->id < 39 || panelButton->id > 50) && panelButton->id != kTextLoadSavedGame) {
// Read non-hardcoded strings from the LUT string table, loaded from the game
// data files
text = _vm->_script->_mainStrings.getString(IHNMTextStringIdsLUT[panelButton->id]);
+ } else if (panelButton->id == kTextLoadSavedGame) {
+ // a bit of a kludge, but it will do
+ text = _vm->getTextString(52);
} else {
// Hardcoded strings in IHNM are read from the ITE hardcoded strings
text = _vm->getTextString(panelButton->id);
@@ -1142,7 +1145,21 @@ void Interface::setLoad(PanelButton *panelButton) {
_loadPanel.currentButton = NULL;
switch (panelButton->id) {
case kTextOK:
- setMode(kPanelMain);
+ if (_vm->getGameType() == GType_ITE) {
+ setMode(kPanelMain);
+ } else {
+ if (_vm->getSaveFilesCount() > 0) {
+ if (_vm->isSaveListFull() || (_optionSaveFileTitleNumber > 0)) {
+ debug(1, "Loading save game %d", _vm->getSaveFile(_optionSaveFileTitleNumber)->slotNumber);
+ setMode(kPanelMain);
+ _vm->load(_vm->calcSaveFileName(_vm->getSaveFile(_optionSaveFileTitleNumber)->slotNumber));
+ }
+ }
+ }
+ break;
+ case kTextCancel:
+ // IHNM only
+ setMode(kPanelOption);
break;
}
}
@@ -1573,7 +1590,6 @@ void Interface::handleChapterSelectionClick(const Point& mousePoint) {
}
void Interface::setOption(PanelButton *panelButton) {
- char * fileName;
_optionPanel.currentButton = NULL;
switch (panelButton->id) {
case kTextContinuePlaying:
@@ -1594,13 +1610,16 @@ void Interface::setOption(PanelButton *panelButton) {
setMode(kPanelQuit);
break;
case kTextLoad:
- if (_vm->getSaveFilesCount() > 0) {
- if (_vm->isSaveListFull() || (_optionSaveFileTitleNumber > 0)) {
- debug(1, "Loading save game %d", _vm->getSaveFile(_optionSaveFileTitleNumber)->slotNumber);
- fileName = _vm->calcSaveFileName(_vm->getSaveFile(_optionSaveFileTitleNumber)->slotNumber);
- setMode(kPanelMain);
- _vm->load(fileName);
+ if (_vm->getGameType() == GType_ITE) {
+ if (_vm->getSaveFilesCount() > 0) {
+ if (_vm->isSaveListFull() || (_optionSaveFileTitleNumber > 0)) {
+ debug(1, "Loading save game %d", _vm->getSaveFile(_optionSaveFileTitleNumber)->slotNumber);
+ setMode(kPanelMain);
+ _vm->load(_vm->calcSaveFileName(_vm->getSaveFile(_optionSaveFileTitleNumber)->slotNumber));
+ }
}
+ } else {
+ setMode(kPanelLoad);
}
break;
case kTextSave:
diff --git a/engines/saga/itedata.cpp b/engines/saga/itedata.cpp
index 43c3d21012..bbd5cbb615 100644
--- a/engines/saga/itedata.cpp
+++ b/engines/saga/itedata.cpp
@@ -339,7 +339,7 @@ FxTable ITE_SfxTable[ITE_SFXCOUNT] = {
{ 73, 64 }
};
-const char *ITEinterfaceTextStrings[][52] = {
+const char *ITEinterfaceTextStrings[][53] = {
{
// Note that the "Load Successful!" string is never used in ScummVM
"Walk to", "Look At", "Pick Up", "Talk to", "Open",
@@ -358,7 +358,8 @@ const char *ITEinterfaceTextStrings[][52] = {
"There's no opening to close.",
"I don't know how to do that.",
"Show Dialog",
- "What is Rif's reply?"
+ "What is Rif's reply?",
+ "Loading a saved game"
},
// German
{
@@ -378,7 +379,8 @@ const char *ITEinterfaceTextStrings[][52] = {
"Hier ist keine \231ffnung zum Schlie$en.",
"Ich wei$ nicht, wie ich das machen soll.",
"Text zeigen",
- "Wie lautet die Antwort?"
+ "Wie lautet die Antwort?",
+ "Spielstand wird geladen"
},
// Italian fan translation
{
@@ -398,7 +400,8 @@ const char *ITEinterfaceTextStrings[][52] = {
"Nessuna apertura da chiudere.",
"Non saprei come farlo.",
"Dialoghi",
- "Come risponderebbe Rif?"
+ "Come risponderebbe Rif?",
+ "Vuoi davvero caricare il gioco?"
},
// Spanish IHNM
{
@@ -420,7 +423,8 @@ const char *ITEinterfaceTextStrings[][52] = {
NULL,
NULL,
NULL,
- NULL
+ NULL,
+ "Cardango una partida guardada"
}
};
diff --git a/engines/saga/itedata.h b/engines/saga/itedata.h
index 00efd070c1..6d0f5a9d70 100644
--- a/engines/saga/itedata.h
+++ b/engines/saga/itedata.h
@@ -88,7 +88,7 @@ struct FxTable {
extern ObjectTableData ITE_ObjectTable[ITE_OBJECTCOUNT];
extern FxTable ITE_SfxTable[ITE_SFXCOUNT];
-extern const char *ITEinterfaceTextStrings[][52];
+extern const char *ITEinterfaceTextStrings[][53];
#define PUZZLE_PIECES 15
diff --git a/engines/saga/saga.h b/engines/saga/saga.h
index 6b6eb6b3fb..eeb8f7a61b 100644
--- a/engines/saga/saga.h
+++ b/engines/saga/saga.h
@@ -295,7 +295,8 @@ enum TextStringIds {
kTextVoices,
kTextText,
kTextAudio,
- kTextBoth
+ kTextBoth,
+ kTextLoadSavedGame
};
struct GameResourceDescription {
diff --git a/engines/saga/sprite.cpp b/engines/saga/sprite.cpp
index be4f2a423d..d9c7b446ba 100644
--- a/engines/saga/sprite.cpp
+++ b/engines/saga/sprite.cpp
@@ -74,9 +74,11 @@ Sprite::Sprite(SagaEngine *vm) : _vm(vm) {
Sprite::~Sprite(void) {
debug(8, "Shutting down sprite subsystem...");
_mainSprites.freeMem();
- _inventorySprites.freeMem();
- _arrowSprites.freeMem();
- _saveReminderSprites.freeMem();
+ if (_vm->getGameType() == GType_IHNM) {
+ _inventorySprites.freeMem();
+ _arrowSprites.freeMem();
+ _saveReminderSprites.freeMem();
+ }
free(_decodeBuf);
}
diff --git a/engines/scumm/boxes.cpp b/engines/scumm/boxes.cpp
index 0fa8b579ca..1e632a034e 100644
--- a/engines/scumm/boxes.cpp
+++ b/engines/scumm/boxes.cpp
@@ -544,8 +544,8 @@ bool ScummEngine::checkXYInBoxBounds(int boxnum, int x, int y) {
// Corner case: If the box is a simple line segment, we consider the
// point to be contained "in" (or rather, lying on) the line if it
// is very close to its projection to the line segment.
- if (box.ul == box.ur && box.lr == box.ll ||
- box.ul == box.ll && box.ur == box.lr) {
+ if ((box.ul == box.ur && box.lr == box.ll) ||
+ (box.ul == box.ll && box.ur == box.lr)) {
Common::Point tmp;
tmp = closestPtOnLine(box.ul, box.lr, p);
@@ -803,8 +803,8 @@ bool Actor::findPathTowards(byte box1nr, byte box2nr, byte box3nr, Common::Point
}
if (box1.ul.y > box2.ur.y || box2.ul.y > box1.ur.y ||
- (box1.ur.y == box2.ul.y || box2.ur.y == box1.ul.y) &&
- box1.ul.y != box1.ur.y && box2.ul.y != box2.ur.y) {
+ ((box1.ur.y == box2.ul.y || box2.ur.y == box1.ul.y) &&
+ box1.ul.y != box1.ur.y && box2.ul.y != box2.ur.y)) {
if (flag & 1)
SWAP(box1.ul.y, box1.ur.y);
if (flag & 2)
@@ -858,8 +858,8 @@ bool Actor::findPathTowards(byte box1nr, byte box2nr, byte box3nr, Common::Point
}
if (box1.ul.x > box2.ur.x || box2.ul.x > box1.ur.x ||
- (box1.ur.x == box2.ul.x || box2.ur.x == box1.ul.x) &&
- box1.ul.x != box1.ur.x && box2.ul.x != box2.ur.x) {
+ ((box1.ur.x == box2.ul.x || box2.ur.x == box1.ul.x) &&
+ box1.ul.x != box1.ur.x && box2.ul.x != box2.ur.x)) {
if (flag & 1)
SWAP(box1.ul.x, box1.ur.x);
if (flag & 2)
@@ -1074,8 +1074,8 @@ bool ScummEngine::areBoxesNeighbours(int box1nr, int box2nr) {
}
if (box.ur.y < box2.ul.y ||
box.ul.y > box2.ur.y ||
- (box.ul.y == box2.ur.y ||
- box.ur.y == box2.ul.y) && box2.ur.y != box2.ul.y && box.ul.y != box.ur.y) {
+ ((box.ul.y == box2.ur.y ||
+ box.ur.y == box2.ul.y) && box2.ur.y != box2.ul.y && box.ul.y != box.ur.y)) {
} else {
return true;
}
@@ -1103,8 +1103,8 @@ bool ScummEngine::areBoxesNeighbours(int box1nr, int box2nr) {
}
if (box.ur.x < box2.ul.x ||
box.ul.x > box2.ur.x ||
- (box.ul.x == box2.ur.x ||
- box.ur.x == box2.ul.x) && box2.ur.x != box2.ul.x && box.ul.x != box.ur.x) {
+ ((box.ul.x == box2.ur.x ||
+ box.ur.x == box2.ul.x) && box2.ur.x != box2.ul.x && box.ul.x != box.ur.x)) {
} else {
return true;
diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index 5a45fb7da9..609aca996d 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -49,7 +49,7 @@ void ScummEngine::loadCJKFont() {
Common::File fp;
_useCJKMode = false;
_textSurfaceMultiplier = 1;
- _newLineCharacter = 0xfe;
+ _newLineCharacter = 0;
if (_game.version <= 5 && _game.platform == Common::kPlatformFMTowns && _language == Common::JA_JPN) { // FM-TOWNS v3 / v5 Kanji
int numChar = 256 * 32;
@@ -277,7 +277,7 @@ CharsetRenderer::CharsetRenderer(ScummEngine *vm) {
_disableOffsX = false;
_vm = vm;
- _curId = 0;
+ _curId = -1;
}
CharsetRenderer::~CharsetRenderer() {
@@ -289,7 +289,10 @@ CharsetRendererCommon::CharsetRendererCommon(ScummEngine *vm)
_shadowColor = 0;
}
-void CharsetRendererCommon::setCurID(byte id) {
+void CharsetRendererCommon::setCurID(int32 id) {
+ if (id == -1)
+ return;
+
assertRange(0, id, _vm->_numCharsets - 1, "charset");
_curId = id;
@@ -308,7 +311,10 @@ void CharsetRendererCommon::setCurID(byte id) {
_numChars = READ_LE_UINT16(_fontPtr + 2);
}
-void CharsetRendererV3::setCurID(byte id) {
+void CharsetRendererV3::setCurID(int32 id) {
+ if (id == -1)
+ return;
+
assertRange(0, id, _vm->_numCharsets - 1, "charset");
_curId = id;
@@ -668,7 +674,8 @@ void CharsetRenderer::translateColor() {
void CharsetRenderer::saveLoadWithSerializer(Serializer *ser) {
static const SaveLoadEntry charsetRendererEntries[] = {
- MKLINE(CharsetRenderer, _curId, sleByte, VER(73)),
+ MKLINE_OLD(CharsetRenderer, _curId, sleByte, VER(73), VER(73)),
+ MKLINE(CharsetRenderer, _curId, sleInt32, VER(74)),
MKLINE(CharsetRenderer, _color, sleByte, VER(73)),
MKEND()
};
@@ -988,7 +995,10 @@ CharsetRendererNut::~CharsetRendererNut() {
}
}
-void CharsetRendererNut::setCurID(byte id) {
+void CharsetRendererNut::setCurID(int32 id) {
+ if (id == -1)
+ return;
+
int numFonts = ((_vm->_game.id == GID_CMI) && (_vm->_game.features & GF_DEMO)) ? 4 : 5;
assert(id < numFonts);
_curId = id;
diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h
index b62dbc6006..dbe02fc8fc 100644
--- a/engines/scumm/charset.h
+++ b/engines/scumm/charset.h
@@ -67,7 +67,7 @@ public:
protected:
ScummEngine *_vm;
- byte _curId;
+ int32 _curId;
public:
CharsetRenderer(ScummEngine *vm);
@@ -80,7 +80,7 @@ public:
void addLinebreaks(int a, byte *str, int pos, int maxwidth);
void translateColor();
- virtual void setCurID(byte id) = 0;
+ virtual void setCurID(int32 id) = 0;
int getCurID() { return _curId; }
virtual int getFontHeight() = 0;
@@ -113,7 +113,7 @@ protected:
public:
CharsetRendererCommon(ScummEngine *vm);
- void setCurID(byte id);
+ void setCurID(int32 id);
int getFontHeight();
};
@@ -142,7 +142,7 @@ protected:
public:
CharsetRendererNES(ScummEngine *vm) : CharsetRendererCommon(vm) {}
- void setCurID(byte id) {}
+ void setCurID(int32 id) {}
void printChar(int chr, bool ignoreCharsetMask);
void drawChar(int chr, const Graphics::Surface &s, int x, int y);
@@ -159,7 +159,7 @@ public:
void printChar(int chr, bool ignoreCharsetMask);
void drawChar(int chr, const Graphics::Surface &s, int x, int y);
- void setCurID(byte id);
+ void setCurID(int32 id);
void setColor(byte color);
int getCharWidth(byte chr);
};
@@ -168,7 +168,7 @@ class CharsetRendererV2 : public CharsetRendererV3 {
public:
CharsetRendererV2(ScummEngine *vm, Common::Language language);
- void setCurID(byte id) {}
+ void setCurID(int32 id) {}
int getCharWidth(byte chr) { return 8; }
};
@@ -184,7 +184,7 @@ public:
void printChar(int chr, bool ignoreCharsetMask);
- void setCurID(byte id);
+ void setCurID(int32 id);
int getFontHeight();
int getCharHeight(byte chr);
diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp
index 68d3010199..ca5dba7865 100644
--- a/engines/scumm/detection.cpp
+++ b/engines/scumm/detection.cpp
@@ -820,9 +820,10 @@ PluginError ScummMetaEngine::createInstance(OSystem *syst, Engine **engine) cons
// unknown MD5, or with a medium debug level in case of a known MD5 (for
// debugging purposes).
if (!findInMD5Table(res.md5.c_str())) {
- printf("Your game version appears to be unknown. Please, report the following\n");
- printf("data to the ScummVM team along with name of the game you tried to add\n");
- printf("and its version/language/etc.:\n");
+ printf("Your game version appears to be unknown. If this is *NOT* a fan-modified\n");
+ printf("version (in particular, not a fan-made translation), please, report the\n");
+ printf("following data to the ScummVM team along with name of the game you tried\n");
+ printf("to add and its version/language/etc.:\n");
printf(" SCUMM gameid '%s', file '%s', MD5 '%s'\n\n",
res.game.gameid,
diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp
index 8d977719da..8886214910 100644
--- a/engines/scumm/dialogs.cpp
+++ b/engines/scumm/dialogs.cpp
@@ -367,37 +367,32 @@ void SaveLoadChooser::reflowLayout() {
void SaveLoadChooser::updateInfos(bool redraw) {
int selItem = _list->getSelected();
- Graphics::Surface *thumb;
- thumb = _vm->loadThumbnailFromSlot(_saveMode ? selItem + 1 : selItem);
+ Graphics::Surface *thumb = 0;
+ if (selItem >= 0 && !_list->getSelectedString().empty())
+ thumb = _vm->loadThumbnailFromSlot(_saveMode ? selItem + 1 : selItem);
if (thumb) {
_gfxWidget->setGfx(thumb);
_gfxWidget->useAlpha(256);
thumb->free();
+ delete thumb;
} else {
_gfxWidget->setGfx(-1, -1, _fillR, _fillG, _fillB);
}
- delete thumb;
- if (redraw)
- _gfxWidget->draw();
-
InfoStuff infos;
memset(&infos, 0, sizeof(InfoStuff));
- char buffer[32];
- if (_vm->loadInfosFromSlot(_saveMode ? selItem + 1 : selItem, &infos)) {
+ if (selItem >= 0 && !_list->getSelectedString().empty()
+ && _vm->loadInfosFromSlot(_saveMode ? selItem + 1 : selItem, &infos)) {
+ char buffer[32];
snprintf(buffer, 32, "Date: %.2d.%.2d.%.4d",
(infos.date >> 24) & 0xFF, (infos.date >> 16) & 0xFF,
infos.date & 0xFFFF);
_date->setLabel(buffer);
- if (redraw)
- _date->draw();
snprintf(buffer, 32, "Time: %.2d:%.2d",
(infos.time >> 8) & 0xFF, infos.time & 0xFF);
_time->setLabel(buffer);
- if (redraw)
- _time->draw();
int minutes = infos.playtime / 60;
int hours = minutes / 60;
@@ -406,23 +401,17 @@ void SaveLoadChooser::updateInfos(bool redraw) {
snprintf(buffer, 32, "Playtime: %.2d:%.2d",
hours & 0xFF, minutes & 0xFF);
_playtime->setLabel(buffer);
- if (redraw)
- _playtime->draw();
} else {
- snprintf(buffer, 32, "No date saved");
- _date->setLabel(buffer);
- if (redraw)
- _date->draw();
-
- snprintf(buffer, 32, "No time saved");
- _time->setLabel(buffer);
- if (redraw)
- _time->draw();
+ _date->setLabel("No date saved");
+ _time->setLabel("No time saved");
+ _playtime->setLabel("No playtime saved");
+ }
- snprintf(buffer, 32, "No playtime saved");
- _playtime->setLabel(buffer);
- if (redraw)
- _playtime->draw();
+ if (redraw) {
+ _gfxWidget->draw();
+ _date->draw();
+ _time->draw();
+ _playtime->draw();
}
}
diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index 6c8d24d25a..d09accafb0 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -669,7 +669,7 @@ void ScummEngine::drawStripToScreen(VirtScreen *vs, int x, int width, int top, i
x += 16;
while (x + width >= _screenWidth)
width -= 16;
- if (width < 0)
+ if (width <= 0)
return;
}
diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp
index f9e4eb415c..54dfca9eea 100644
--- a/engines/scumm/saveload.cpp
+++ b/engines/scumm/saveload.cpp
@@ -479,6 +479,9 @@ Graphics::Surface *ScummEngine::loadThumbnailFromSlot(int slot) {
Common::SeekableReadStream *in;
SaveGameHeader hdr;
+ if (slot < 0)
+ return 0;
+
makeSavegameName(filename, slot, false);
if (!(in = _saveFileMan->openForLoading(filename))) {
return 0;
@@ -507,6 +510,9 @@ bool ScummEngine::loadInfosFromSlot(int slot, InfoStuff *stuff) {
Common::SeekableReadStream *in;
SaveGameHeader hdr;
+ if (slot < 0)
+ return 0;
+
makeSavegameName(filename, slot, false);
if (!(in = _saveFileMan->openForLoading(filename))) {
return false;
@@ -595,7 +601,7 @@ bool ScummEngine::loadInfos(Common::SeekableReadStream *file, InfoStuff *stuff)
return true;
}
-void ScummEngine::saveInfos(Common::OutSaveFile* file) {
+void ScummEngine::saveInfos(Common::WriteStream* file) {
SaveInfoSection section;
section.type = MKID_BE('INFO');
section.version = INFOSECTION_VERSION;
diff --git a/engines/scumm/saveload.h b/engines/scumm/saveload.h
index 0ddb4e5d2a..2d7ee64680 100644
--- a/engines/scumm/saveload.h
+++ b/engines/scumm/saveload.h
@@ -31,7 +31,7 @@
namespace Common {
class SeekableReadStream;
- class OutSaveFile;
+ class WriteStream;
}
namespace Scumm {
@@ -50,7 +50,7 @@ namespace Scumm {
* only saves/loads those which are valid for the version of the savegame
* which is being loaded/saved currently.
*/
-#define CURRENT_VER 73
+#define CURRENT_VER 74
/**
* An auxillary macro, used to specify savegame versions. We use this instead
@@ -125,7 +125,7 @@ struct SaveLoadEntry {
class Serializer {
public:
- Serializer(Common::SeekableReadStream *in, Common::OutSaveFile *out, uint32 savegameVersion)
+ Serializer(Common::SeekableReadStream *in, Common::WriteStream *out, uint32 savegameVersion)
: _loadStream(in), _saveStream(out),
_savegameVersion(savegameVersion)
{ }
@@ -151,7 +151,7 @@ public:
protected:
Common::SeekableReadStream *_loadStream;
- Common::OutSaveFile *_saveStream;
+ Common::WriteStream *_saveStream;
uint32 _savegameVersion;
void saveArrayOf(void *b, int len, int datasize, byte filetype);
diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp
index 9268768f2e..c727b59c64 100644
--- a/engines/scumm/script.cpp
+++ b/engines/scumm/script.cpp
@@ -769,14 +769,14 @@ void ScummEngine::runInventoryScript(int i) {
args[0] = i;
if (VAR(VAR_INVENTORY_SCRIPT)) {
if (_game.id == GID_INDY3 && _game.platform == Common::kPlatformMacintosh) {
- inventoryScript();
+ inventoryScriptIndy3Mac();
} else {
runScript(VAR(VAR_INVENTORY_SCRIPT), 0, 0, args);
}
}
}
-void ScummEngine::inventoryScript() {
+void ScummEngine::inventoryScriptIndy3Mac() {
VerbSlot *vs;
int args[24];
int j, slot;
@@ -1201,11 +1201,11 @@ void ScummEngine::runInputScript(int clickArea, int val, int mode) {
if (clickArea == kVerbClickArea && (val >= 101 && val <= 108)) {
if (val == 107) {
VAR(67) -= 2;
- inventoryScript();
+ inventoryScriptIndy3Mac();
return;
} else if (val == 108) {
VAR(67) += 2;
- inventoryScript();
+ inventoryScriptIndy3Mac();
return;
} else {
args[0] = 3;
diff --git a/engines/scumm/script_v5.cpp b/engines/scumm/script_v5.cpp
index 431321f459..3cbb2b8266 100644
--- a/engines/scumm/script_v5.cpp
+++ b/engines/scumm/script_v5.cpp
@@ -1402,7 +1402,7 @@ void ScummEngine_v5::o5_ifClassOfIs() {
while ((_opcode = fetchScriptByte()) != 0xFF) {
cls = getVarOrDirectWord(PARAM_1);
b = getClass(act, cls);
- if (cls & 0x80 && !b || !(cls & 0x80) && b)
+ if (((cls & 0x80) && !b) || (!(cls & 0x80) && b))
cond = false;
}
if (cond)
diff --git a/engines/scumm/script_v6.cpp b/engines/scumm/script_v6.cpp
index 04ea53137b..b9a00e6d38 100644
--- a/engines/scumm/script_v6.cpp
+++ b/engines/scumm/script_v6.cpp
@@ -2390,6 +2390,15 @@ void ScummEngine_v6::o6_talkActor() {
_actorToPrintStrFor = pop();
+ // WORKAROUND for bug #2016521: "DOTT: Bernard impersonating LaVerne"
+ // Original script did not check for VAR_EGO == 2 before executing
+ // a talkActor opcode.
+ if (_game.id == GID_TENTACLE && vm.slot[_currentScript].number == 307
+ && VAR(VAR_EGO) != 2 && _actorToPrintStrFor == 2) {
+ _scriptPointer += resStrLen(_scriptPointer) + 1;
+ return;
+ }
+
_string[0].loadDefault();
actorTalk(_scriptPointer);
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 20824ffe74..2763331420 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -46,7 +46,7 @@ namespace GUI {
using GUI::Dialog;
namespace Common {
class SeekableReadStream;
- class OutSaveFile;
+ class WriteStream;
}
namespace Scumm {
@@ -560,7 +560,7 @@ protected:
public:
int _numLocalScripts, _numImages, _numRooms, _numScripts, _numSounds; // Used by HE games
int _numCostumes; // FIXME - should be protected, used by Actor::remapActorPalette
- int _numCharsets; // FIXME - should be protected, used by CharsetRenderer
+ int32 _numCharsets; // FIXME - should be protected, used by CharsetRenderer
BaseCostumeLoader *_costumeLoader;
BaseCostumeRenderer *_costumeRenderer;
@@ -637,8 +637,8 @@ public:
protected:
Graphics::Surface *loadThumbnail(Common::SeekableReadStream *file);
bool loadInfos(Common::SeekableReadStream *file, InfoStuff *stuff);
- void saveThumbnail(Common::OutSaveFile *file);
- void saveInfos(Common::OutSaveFile* file);
+ void saveThumbnail(Common::WriteStream *file);
+ void saveInfos(Common::WriteStream* file);
int32 _engineStartTime;
int32 _pauseStartTime;
@@ -673,7 +673,7 @@ protected:
void executeScript();
void updateScriptPtr();
virtual void runInventoryScript(int i);
- void inventoryScript();
+ void inventoryScriptIndy3Mac();
void checkAndRunSentenceScript();
void runExitScript();
void runEntryScript();
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index f039e2ca23..700632e4b3 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -279,7 +279,7 @@ bool ScummEngine::handleNextCharsetCode(Actor *a, int *code) {
}
c = *buffer++;
- if (c == _newLineCharacter) {
+ if (_newLineCharacter != 0 && c == _newLineCharacter) {
c = 13;
break;
}
diff --git a/engines/sky/control.cpp b/engines/sky/control.cpp
index 53169402e1..9d6b58704d 100644
--- a/engines/sky/control.cpp
+++ b/engines/sky/control.cpp
@@ -986,7 +986,7 @@ void Control::handleKeyPress(Common::KeyState kbd, Common::String &textBuf) {
if (kbd.keycode == Common::KEYCODE_BACKSPACE) { // backspace
if (textBuf.size() > 0)
textBuf.deleteLastChar();
- } else {
+ } else if (kbd.ascii) {
// Cannot enter text wider than the save/load panel
if (_enteredTextWidth >= PAN_LINE_WIDTH - 10)
return;
diff --git a/engines/sky/logic.cpp b/engines/sky/logic.cpp
index ed7e419f64..c6c6c34c4d 100644
--- a/engines/sky/logic.cpp
+++ b/engines/sky/logic.cpp
@@ -1774,6 +1774,7 @@ bool Logic::fnChooser(uint32 a, uint32 b, uint32 c) {
uint32 size = ((dataFileHeader *)data)->s_height * ((dataFileHeader *)data)->s_width;
uint32 index = 0;
uint32 width = ((dataFileHeader *)data)->s_width;
+ uint32 height = ((dataFileHeader *)data)->s_height;
data += sizeof(dataFileHeader);
@@ -1794,7 +1795,7 @@ bool Logic::fnChooser(uint32 a, uint32 b, uint32 c) {
textCompact->xcood = TOP_LEFT_X; // set coordinates
textCompact->ycood = ycood;
- ycood += 12;
+ ycood += height;
}
if (p == _scriptVariables + TEXT1)
diff --git a/engines/sky/sound.cpp b/engines/sky/sound.cpp
index 928221a9a5..f15038c0b6 100644
--- a/engines/sky/sound.cpp
+++ b/engines/sky/sound.cpp
@@ -1025,6 +1025,7 @@ Sound::Sound(Audio::Mixer *mixer, Disk *pDisk, uint8 pVolume) {
_mixer = mixer;
_saveSounds[0] = _saveSounds[1] = 0xFFFF;
_mainSfxVolume = pVolume;
+ _isPaused = false;
}
Sound::~Sound(void) {
@@ -1254,14 +1255,20 @@ bool Sound::startSpeech(uint16 textNum) {
void Sound::fnPauseFx(void) {
- _mixer->pauseID(SOUND_CH0, true);
- _mixer->pauseID(SOUND_CH1, true);
+ if (!_isPaused) {
+ _isPaused = true;
+ _mixer->pauseID(SOUND_CH0, true);
+ _mixer->pauseID(SOUND_CH1, true);
+ }
}
void Sound::fnUnPauseFx(void) {
- _mixer->pauseID(SOUND_CH0, false);
- _mixer->pauseID(SOUND_CH1, false);
+ if (_isPaused) {
+ _isPaused = false;
+ _mixer->pauseID(SOUND_CH0, false);
+ _mixer->pauseID(SOUND_CH1, false);
+ }
}
} // End of namespace Sky
diff --git a/engines/sky/sound.h b/engines/sky/sound.h
index 28e2e8c88a..0ad509700e 100644
--- a/engines/sky/sound.h
+++ b/engines/sky/sound.h
@@ -89,6 +89,8 @@ private:
uint8 *_sampleRates, *_sfxInfo;
uint8 _mainSfxVolume;
+ bool _isPaused;
+
static uint16 _speechConvertTable[8];
static SfxQueue _sfxQueue[MAX_QUEUED_FX];
};
diff --git a/engines/tinsel/detection.cpp b/engines/tinsel/detection.cpp
index a638dde2c5..7da4192456 100644
--- a/engines/tinsel/detection.cpp
+++ b/engines/tinsel/detection.cpp
@@ -76,13 +76,28 @@ namespace Tinsel {
static const TinselGameDescription gameDescriptions[] = {
- // Note: versions with *.gra files use tinsel v1 (28/2/1995), whereas
- // versions with *.scn files tinsel v2 (7/5/1995)
- // Update: this is not entirely true, there were some versions released
- // with *.gra files and used tinsel v2
+ // The DW1 demo was based on an older revision of the Tinsel engine
+ // than the one used in the released game. We call it Tinsel v0 as
+ // opposed to v1 which was used in the full retail version of DW.
+
+ { // Demo from http://www.adventure-treff.de/specials/dl_demos.php
+ {
+ "dw",
+ "Demo",
+ AD_ENTRY1s("dw.gra", "ce1b57761ba705221bcf70955b827b97", 441192),
+ //AD_ENTRY1s("dw.scn", "ccd72f02183d0e96b6e7d8df9492cda8", 23308),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ Common::ADGF_DEMO
+ },
+ GID_DW1,
+ 0,
+ GF_DEMO,
+ TINSEL_V0,
+ },
{
- { // This version has *.gra files but uses tinsel v2
+ { // This version has *.gra files
"dw",
"Floppy",
AD_ENTRY1s("dw.gra", "c8808ccd988d603dd35dff42013ae7fd", 781656),
@@ -93,10 +108,10 @@ static const TinselGameDescription gameDescriptions[] = {
GID_DW1,
0,
GF_FLOPPY,
- TINSEL_V2,
+ TINSEL_V1,
},
- { // English CD v1. This version has *.gra files but uses tinsel v2
+ { // English CD. This version has *.gra files
{
"dw",
"CD",
@@ -112,10 +127,10 @@ static const TinselGameDescription gameDescriptions[] = {
GID_DW1,
0,
GF_CD,
- TINSEL_V2,
+ TINSEL_V1,
},
- { // English CD v2
+ { // English CD with SCN files
{
"dw",
"CD",
@@ -131,11 +146,11 @@ static const TinselGameDescription gameDescriptions[] = {
GID_DW1,
0,
GF_CD | GF_SCNFILES,
- TINSEL_V2,
+ TINSEL_V1,
},
#if 0
- { // English Saturn CD
+ { // English Saturn CD. Not (yet?) supported
{
"dw",
"CD",
@@ -151,25 +166,9 @@ static const TinselGameDescription gameDescriptions[] = {
GID_DW1,
0,
GF_CD,
- TINSEL_V2,
- },
-#endif
-
- { // Demo from http://www.adventure-treff.de/specials/dl_demos.php
- {
- "dw",
- "Demo",
- AD_ENTRY1s("dw.gra", "ce1b57761ba705221bcf70955b827b97", 441192),
- //AD_ENTRY1s("dw.scn", "ccd72f02183d0e96b6e7d8df9492cda8", 23308),
- Common::EN_ANY,
- Common::kPlatformPC,
- Common::ADGF_DEMO
- },
- GID_DW1,
- 0,
- GF_DEMO,
TINSEL_V1,
},
+#endif
{ // German CD re-release "Neon Edition"
// Note: This release has ENGLISH.TXT (with german content) instead of GERMAN.TXT
@@ -184,9 +183,9 @@ static const TinselGameDescription gameDescriptions[] = {
GID_DW1,
0,
GF_CD | GF_SCNFILES,
- TINSEL_V2,
+ TINSEL_V1,
},
-
+
{ AD_TABLE_END_MARKER, 0, 0, 0, 0 }
};
diff --git a/engines/tinsel/scn.cpp b/engines/tinsel/scn.cpp
index b14b1c5962..8639979b41 100644
--- a/engines/tinsel/scn.cpp
+++ b/engines/tinsel/scn.cpp
@@ -50,7 +50,7 @@ byte *FindChunk(SCNHANDLE handle, uint32 chunk) {
// V1 chunk types can be found by substracting 2 from the
// chunk type. Note that CHUNK_STRING and CHUNK_BITMAP are
// the same in V1 and V2
- if (_vm->getVersion() == TINSEL_V1 &&
+ if (_vm->getVersion() == TINSEL_V0 &&
chunk != CHUNK_STRING && chunk != CHUNK_BITMAP)
chunk -= 0x2L;
diff --git a/engines/tinsel/tinsel.h b/engines/tinsel/tinsel.h
index 9ffadfe8c1..44cc83af9b 100644
--- a/engines/tinsel/tinsel.h
+++ b/engines/tinsel/tinsel.h
@@ -59,8 +59,8 @@ enum TinselGameFeatures {
};
enum TinselEngineVersion {
- TINSEL_V1 = 1 << 0,
- TINSEL_V2 = 1 << 1
+ TINSEL_V0 = 1 << 0, // Used in the DW1 demo only
+ TINSEL_V1 = 1 << 1
};
struct TinselGameDescription;
diff --git a/engines/touche/graphics.cpp b/engines/touche/graphics.cpp
index 999aa8005c..ab711beba0 100644
--- a/engines/touche/graphics.cpp
+++ b/engines/touche/graphics.cpp
@@ -76,10 +76,13 @@ int Graphics::getCharWidth16(uint8 chr) {
return chrData[2];
}
-void Graphics::drawString16(uint8 *dst, int dstPitch, uint16 color, int x, int y, const char *str) {
+void Graphics::drawString16(uint8 *dst, int dstPitch, uint16 color, int x, int y, const char *str, int xmax) {
while (*str) {
uint8 chr = (uint8)*str++;
x += drawChar16(dst, dstPitch, chr, x, y, color);
+ if (xmax != 0 && x > xmax) {
+ break;
+ }
}
}
diff --git a/engines/touche/graphics.h b/engines/touche/graphics.h
index 6b4072d896..9c928f983c 100644
--- a/engines/touche/graphics.h
+++ b/engines/touche/graphics.h
@@ -40,7 +40,7 @@ public:
static void setupFont(Common::Language language);
static int getStringWidth16(const char *str);
static int getCharWidth16(uint8 chr);
- static void drawString16(uint8 *dst, int dstPitch, uint16 color, int x, int y, const char *str);
+ static void drawString16(uint8 *dst, int dstPitch, uint16 color, int x, int y, const char *str, int xmax = 0);
static int drawChar16(uint8 *dst, int dstPitch, uint8 chr, int x, int y, uint16 color);
static void fillRect(uint8 *dst, int dstPitch, int x, int y, int w, int h, uint8 color);
static void drawRect(uint8 *dst, int dstPitch, int x, int y, int w, int h, uint8 color1, uint8 color2);
diff --git a/engines/touche/staticres.cpp b/engines/touche/staticres.cpp
index ec9a0ea903..a377a6e45f 100644
--- a/engines/touche/staticres.cpp
+++ b/engines/touche/staticres.cpp
@@ -889,6 +889,7 @@ const uint8 Graphics::_freGerFontData[] = {
0xC0, 0x00, 0x00,
};
+// spanish charset differs from original executable, see tracker item #2040311.
const uint16 Graphics::_spaFontOffs[] = {
0x0000, 0x0007, 0x0024, 0x0043, 0x0072, 0x00AD, 0x00E0, 0x0113, 0x0124, 0x0141,
0x015E, 0x0191, 0x01C4, 0x01E3, 0x01F8, 0x0215, 0x0232, 0x0269, 0x0286, 0x02BD,
@@ -904,11 +905,11 @@ const uint16 Graphics::_spaFontOffs[] = {
0x1462, 0x1499, 0x14A4, 0x14DB, 0x1512, 0x1549, 0x1580, 0x15B7, 0x15EE, 0x1625,
0x165C, 0x1667, 0x169E, 0x16D5, 0x16E0, 0x16EB, 0x1722, 0x172D, 0x1738, 0x176F,
0x178C, 0x17C3, 0x17FA, 0x1831, 0x1868, 0x1873, 0x187E, 0x18B5, 0x18C0, 0x18CB,
- 0x18D6, 0x18E1, 0x18FE, 0x1929, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x18D6, 0x18E1, 0x18FE, 0x1929, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x054B,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0703, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x1954
};
diff --git a/engines/touche/touche.cpp b/engines/touche/touche.cpp
index ac8e8a786a..a39517fe32 100644
--- a/engines/touche/touche.cpp
+++ b/engines/touche/touche.cpp
@@ -1248,10 +1248,11 @@ int ToucheEngine::getStringWidth(int num) const {
return Graphics::getStringWidth16(str);
}
-void ToucheEngine::drawString(uint16 color, int x, int y, int16 num) {
+void ToucheEngine::drawString(uint16 color, int x, int y, int16 num, StringType strType) {
+ const int xmax = (_language == Common::ES_ESP && strType == kStringTypeConversation) ? kScreenWidth - 20 : 0;
if (num) {
const char *str = getString(num);
- Graphics::drawString16(_offscreenBuffer, kScreenWidth, color, x, y, str);
+ Graphics::drawString16(_offscreenBuffer, kScreenWidth, color, x, y, str, xmax);
}
}
@@ -2414,7 +2415,7 @@ void ToucheEngine::drawCharacterConversation() {
}
drawConversationPanel();
for (int i = 0; i < 4; ++i) {
- drawString(214, 42, 328 + i * kTextHeight, _conversationChoicesTable[_scrollConversationChoiceOffset + i].msg);
+ drawString(214, 42, 328 + i * kTextHeight, _conversationChoicesTable[_scrollConversationChoiceOffset + i].msg, kStringTypeConversation);
}
updateScreenArea(0, 320, kScreenWidth, kScreenHeight - 320);
_conversationAreaCleared = false;
@@ -2422,7 +2423,7 @@ void ToucheEngine::drawCharacterConversation() {
void ToucheEngine::drawConversationString(int num, uint16 color) {
const int y = 328 + num * kTextHeight;
- drawString(color, 42, y, _conversationChoicesTable[num + _scrollConversationChoiceOffset].msg);
+ drawString(color, 42, y, _conversationChoicesTable[num + _scrollConversationChoiceOffset].msg, kStringTypeConversation);
updateScreenArea(0, y, kScreenWidth, kTextHeight);
}
diff --git a/engines/touche/touche.h b/engines/touche/touche.h
index c1bc12655f..41f5c832c5 100644
--- a/engines/touche/touche.h
+++ b/engines/touche/touche.h
@@ -331,6 +331,11 @@ enum {
kMaxSaveStates = 100
};
+enum StringType {
+ kStringTypeDefault,
+ kStringTypeConversation
+};
+
class MidiPlayer;
class ToucheEngine: public Engine {
@@ -399,7 +404,7 @@ protected:
void setKeyCharMoney();
const char *getString(int num) const;
int getStringWidth(int num) const;
- void drawString(uint16 color, int x, int y, int16 num);
+ void drawString(uint16 color, int x, int y, int16 num, StringType strType = kStringTypeDefault);
void drawGameString(uint16 color, int x1, int y, const char *str);
int restartKeyCharScriptOnAction(int action, int obj1, int obj2);
void buildSpriteScalingTable(int z1, int z2);