aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWillem Jan Palenstijn2015-05-07 23:35:36 +0200
committerWillem Jan Palenstijn2015-05-07 23:35:36 +0200
commit57109ef0a8d30f15aa1fcef7d9d229fc9701f630 (patch)
tree230c0a7822875e9c41a4606fce54166d4dc37db0
parentd9e93f8e015ce27a95090e854494c4b3f7d1c0d4 (diff)
parent04931d040085d77d031290fda57ca2c5dc486f54 (diff)
downloadscummvm-rg350-57109ef0a8d30f15aa1fcef7d9d229fc9701f630.tar.gz
scummvm-rg350-57109ef0a8d30f15aa1fcef7d9d229fc9701f630.tar.bz2
scummvm-rg350-57109ef0a8d30f15aa1fcef7d9d229fc9701f630.zip
Merge branch 'master' into sherlock
-rw-r--r--NEWS8
-rw-r--r--README8
-rw-r--r--audio/decoders/mp3.cpp17
-rw-r--r--backends/graphics/maemosdl/maemosdl-graphics.cpp2
-rw-r--r--backends/log/log.cpp4
-rw-r--r--backends/platform/maemo/maemo.cpp4
-rw-r--r--devtools/scumm-md5.txt7
-rw-r--r--engines/gob/surface.cpp7
-rw-r--r--engines/gob/surface.h2
-rw-r--r--engines/mads/detection_tables.h4
-rw-r--r--engines/mads/dialogs.cpp1
-rw-r--r--engines/mads/events.cpp2
-rw-r--r--engines/mads/game.cpp3
-rw-r--r--engines/mads/mads.cpp7
-rw-r--r--engines/mads/menu_views.cpp5
-rw-r--r--engines/mads/messages.cpp8
-rw-r--r--engines/mads/nebular/dialogs_nebular.cpp56
-rw-r--r--engines/mads/nebular/dialogs_nebular.h3
-rw-r--r--engines/mads/nebular/game_nebular.cpp65
-rw-r--r--engines/mads/nebular/nebular_scenes1.cpp11
-rw-r--r--engines/mads/nebular/nebular_scenes6.cpp10
-rw-r--r--engines/mads/nebular/nebular_scenes8.cpp2
-rw-r--r--engines/mads/nebular/sound_nebular.cpp37
-rw-r--r--engines/mads/nebular/sound_nebular.h8
-rw-r--r--engines/mads/scene_data.cpp1
-rw-r--r--engines/mads/sound.cpp15
-rw-r--r--engines/mads/sound.h6
-rw-r--r--engines/mads/sprites.cpp6
-rw-r--r--engines/mads/user_interface.cpp1
-rw-r--r--engines/mohawk/configure.engine2
-rw-r--r--engines/mohawk/cursors.cpp6
-rw-r--r--engines/mohawk/myst.cpp12
-rw-r--r--engines/mohawk/myst_graphics.cpp165
-rw-r--r--engines/mohawk/myst_graphics.h27
-rw-r--r--engines/mohawk/myst_stacks/myst.cpp20
-rw-r--r--engines/mohawk/video.cpp42
-rw-r--r--engines/mohawk/video.h4
-rw-r--r--engines/sci/console.cpp311
-rw-r--r--engines/sci/console.h3
-rw-r--r--engines/sci/detection.cpp27
-rw-r--r--engines/sci/detection_tables.h11
-rw-r--r--engines/sci/engine/file.cpp3
-rw-r--r--engines/sci/engine/kernel.cpp45
-rw-r--r--engines/sci/engine/kernel.h2
-rw-r--r--engines/sci/engine/kevent.cpp13
-rw-r--r--engines/sci/engine/kgraphics.cpp10
-rw-r--r--engines/sci/engine/kmovement.cpp27
-rw-r--r--engines/sci/engine/kparse.cpp3
-rw-r--r--engines/sci/engine/kvideo.cpp2
-rw-r--r--engines/sci/engine/savegame.cpp58
-rw-r--r--engines/sci/engine/savegame.h4
-rw-r--r--engines/sci/engine/script.cpp193
-rw-r--r--engines/sci/engine/script.h19
-rw-r--r--engines/sci/engine/script_patches.cpp628
-rw-r--r--engines/sci/engine/script_patches.h9
-rw-r--r--engines/sci/engine/state.cpp4
-rw-r--r--engines/sci/engine/state.h4
-rw-r--r--engines/sci/engine/vm.cpp18
-rw-r--r--engines/sci/engine/workarounds.cpp955
-rw-r--r--engines/sci/engine/workarounds.h2
-rw-r--r--engines/sci/graphics/paint16.cpp19
-rw-r--r--engines/sci/graphics/portrait.cpp3
-rw-r--r--engines/sci/graphics/screen.cpp12
-rw-r--r--engines/sci/graphics/screen.h1
-rw-r--r--engines/sci/parser/vocabulary.cpp77
-rw-r--r--engines/sci/parser/vocabulary.h12
-rw-r--r--engines/sci/sci.cpp17
-rw-r--r--engines/sci/sci.h3
-rw-r--r--engines/scumm/debugger.cpp2
-rw-r--r--engines/scumm/detection_tables.h3
-rw-r--r--engines/scumm/scumm-md5.h9
-rw-r--r--engines/sword25/util/double_serialization.cpp2
-rw-r--r--engines/tony/mpal/mpal.cpp4
-rw-r--r--engines/toon/anim.cpp28
-rw-r--r--engines/toon/character.cpp5
-rw-r--r--engines/toon/font.cpp6
-rw-r--r--engines/toon/toon.cpp362
-rw-r--r--engines/toon/toon.h4
-rw-r--r--engines/zvision/graphics/effects/fog.cpp6
-rw-r--r--engines/zvision/graphics/effects/wave.cpp2
-rw-r--r--engines/zvision/graphics/render_manager.cpp4
-rw-r--r--engines/zvision/scripting/actions.cpp2
-rw-r--r--engines/zvision/scripting/effects/distort_effect.cpp2
-rw-r--r--engines/zvision/scripting/effects/music_effect.cpp2
-rw-r--r--engines/zvision/scripting/menu.cpp26
-rw-r--r--engines/zvision/text/truetype_font.cpp8
-rw-r--r--graphics/fonts/ttf.cpp6
-rw-r--r--gui/Tooltip.cpp2
-rw-r--r--gui/Tooltip.h33
-rw-r--r--gui/dialog.cpp2
-rw-r--r--image/codecs/cinepak.cpp557
-rw-r--r--image/codecs/cinepak.h18
-rw-r--r--image/codecs/cinepak_tables.h780
-rw-r--r--image/codecs/codec.cpp148
-rw-r--r--image/codecs/codec.h29
-rw-r--r--image/codecs/qtrle.cpp164
-rw-r--r--image/codecs/qtrle.h15
-rw-r--r--image/codecs/rpza.cpp270
-rw-r--r--image/codecs/rpza.h14
-rw-r--r--video/avi_decoder.cpp24
-rw-r--r--video/avi_decoder.h7
-rw-r--r--video/qt_decoder.cpp120
-rw-r--r--video/qt_decoder.h10
-rw-r--r--video/video_decoder.cpp20
-rw-r--r--video/video_decoder.h32
105 files changed, 4798 insertions, 1013 deletions
diff --git a/NEWS b/NEWS
index 45b0afd2d7..a7e7d20ded 100644
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,7 @@ For a more comprehensive changelog of the latest experimental code, see:
1.8.0 (????-??-??)
New Games:
+ - Added support for Rex Nebular and the Cosmic Gender Bender.
- Added support for Sfinx.
- Added support for Zork Nemesis: The Forbidden Lands.
- Added support for Zork: Grand Inquisitor.
@@ -17,7 +18,12 @@ For a more comprehensive changelog of the latest experimental code, see:
head scene (bug #6728). It may have been happening in other scenes as
well.
-SCUMM:
+ SCI:
+ - Handling of music priority has been greatly improved.
+ - A lot of fixes for original game script bugs, which are too numerous to
+ mention here.
+
+ SCUMM:
- It is now possible to play Maniac Mansion from within Day of the
Tentacle, with a few caveats. See README for details.
diff --git a/README b/README
index 3e2cacc05b..56b0a8423c 100644
--- a/README
+++ b/README
@@ -2456,9 +2456,11 @@ debug messages (see http://www.sysinternals.com/ntw2k/freeware/debugview.shtml).
* Type "./configure" in the ScummVM directory.
* You can now type 'make' to create a command line binary.
* To get a version you can run from Finder, type 'make bundle' which
- will create ScummVM.app (this only works out of the box if you
- installed SDL into /sw (as happens if you are using Fink). If you
- have installed SDL in another way, you will have to edit ports.mk).
+ will create ScummVM.app (this tries to detect where the static libraries
+ are installed, which should work in most cases, but if it doesn't you
+ will need to specify this path with --with-staticlib-prefix= when calling
+ configure - for example "./configure --with-staticlib-prefix=/Users/foo"
+ if the libraries are in /Users/foo/lib).
* For more information refer to:
http://wiki.scummvm.org/index.php/Compiling_ScummVM/MacOS_X_Crosscompiling
diff --git a/audio/decoders/mp3.cpp b/audio/decoders/mp3.cpp
index c1b3faaeb1..feb531f5ae 100644
--- a/audio/decoders/mp3.cpp
+++ b/audio/decoders/mp3.cpp
@@ -246,6 +246,23 @@ void MP3Stream::initStream() {
_inStream->seek(0, SEEK_SET);
_curTime = mad_timer_zero;
_posInFrame = 0;
+
+ // Skip ID3 TAG if any
+ // ID3v1 (beginning with with 'TAG') is located at the end of files. So we can ignore those.
+ // ID3v2 can be located at the start of files and begins with a 10 bytes header, the first 3 bytes being 'ID3'.
+ // The tag size is coded on the last 4 bytes of the 10 bytes header as a 32 bit synchsafe integer.
+ // See http://id3.org/id3v2.4.0-structure for details.
+ char data[10];
+ _inStream->read(data, 10);
+ if (data[0] == 'I' && data[1] == 'D' && data[2] == '3') {
+ uint32 size = data[9] + 128 * (data[8] + 128 * (data[7] + 128 * data[6]));
+ // This size does not include an optional 10 bytes footer. Check if it is present.
+ if (data[5] & 0x10)
+ size += 10;
+ debug("Skipping ID3 TAG (%d bytes)", size + 10);
+ _inStream->seek(size, SEEK_CUR);
+ } else
+ _inStream->seek(0, SEEK_SET);
// Update state
_state = MP3_STATE_READY;
diff --git a/backends/graphics/maemosdl/maemosdl-graphics.cpp b/backends/graphics/maemosdl/maemosdl-graphics.cpp
index 478d022aa0..d954333537 100644
--- a/backends/graphics/maemosdl/maemosdl-graphics.cpp
+++ b/backends/graphics/maemosdl/maemosdl-graphics.cpp
@@ -21,8 +21,6 @@
*/
#if defined(MAEMO)
-#include "SDL_syswm.h"
-
#include "common/scummsys.h"
#include "backends/platform/maemo/maemo.h"
diff --git a/backends/log/log.cpp b/backends/log/log.cpp
index 693399bae5..e37296aada 100644
--- a/backends/log/log.cpp
+++ b/backends/log/log.cpp
@@ -93,10 +93,12 @@ void Log::print(const char *message, const bool printTime) {
void Log::printTimeStamp() {
TimeDate date;
+ int curMonth;
_system->getTimeAndDate(date);
+ curMonth = date.tm_mon + 1; // month is base 0, we need base 1 (1 = january and so on)
_stream->writeString(Common::String::format("[%d-%02d-%02d %02d:%02d:%02d] ",
- date.tm_year + 1900, date.tm_mon, date.tm_mday,
+ date.tm_year + 1900, curMonth, date.tm_mday,
date.tm_hour, date.tm_min, date.tm_sec));
}
diff --git a/backends/platform/maemo/maemo.cpp b/backends/platform/maemo/maemo.cpp
index 5e1ecfe335..5fdcddac43 100644
--- a/backends/platform/maemo/maemo.cpp
+++ b/backends/platform/maemo/maemo.cpp
@@ -35,10 +35,6 @@
#include "common/textconsole.h"
#include "common/translation.h"
-
-#include <SDL/SDL_syswm.h>
-#include <X11/Xutil.h>
-
namespace Maemo {
OSystem_SDL_Maemo::OSystem_SDL_Maemo()
diff --git a/devtools/scumm-md5.txt b/devtools/scumm-md5.txt
index 62925e98fa..e56f1a6a99 100644
--- a/devtools/scumm-md5.txt
+++ b/devtools/scumm-md5.txt
@@ -127,6 +127,7 @@ indy3 Indiana Jones and the Last Crusade
66236cd1aec24e1d4aff4c4cc93b7e18 -1 fr DOS EGA EGA ?? v1.3, 25 Aug 89 Andrea Petrucci, Peter Eckerlein
89cfc425566003ff74b7dc7b3e6fd469 -1 fr DOS EGA EGA ?? v1.3, 25 Aug 89 Jorpho
69d70269fafc4445adbb0d223e4f9a3f 5361 en DOS EGA EGA v1.4, 11/07/89 (5.25\") Petr Maruska
+ 56e8c37a0a08c3a7076f82417461a877 -1 en DOS EGA EGA v1.4, 7 Nov 89 (3.5\") Paulo Vicente
6f6ef668c608c7f534fea6e6d3878dde -1 de DOS EGA EGA v1.4 from 19 Oct 89 dhewg, Peter Eckerlein
eb700bb73ca1cc44a1ad5e4b1a4bdeaf 5361 de DOS EGA EGA PC-Spiele a.borque
d62d248c3df6ec177405e2cb23d923b2 -1 it DOS EGA EGA v1.4 from 25 Nov 89 Andrea Petrucci, Peter Eckerlein
@@ -293,12 +294,14 @@ atlantis Indiana Jones and the Fate of Atlantis
035deab53b47bc43abc763560d0f8d4b -1 en DOS Floppy Demo -
98744fe66ff730e8c2b3b1f58803ab0b -1 en DOS Floppy Demo - Simon Krumrein, sev
+ 12cdc256eae5a461bcc9a49975999841 -1 en DOS Floppy Demo - Paulo Vicente
99b6f822b0b2612415407865438697d6 -1 en DOS - Demo non-interactive
28d24a33448fab6795850bc9f159a4a2 11170 jp FM-TOWNS FM-TOWNS Demo non-interactive khalek, Fingolfin
tentacle Day of the Tentacle
acad97ab1c6fc2a5b2d98abf6db4a190 -1 en All? Floppy Floppy Version A ?
2723fea3dae0cb47768c424b145ae0e7 7932 en DOS Floppy Floppy Version B ? Andrej Sinicyn, Andrea Petrucci, Fingolfin
+ f0ccc12a8704bf57706b42a37f877128 -1 en DOS Floppy Floppy 1.6 Paulo Vicente
92b078d9d6d9d751da9c26b8b3075779 -1 fr DOS Floppy Floppy - Nicolas Sauz&egrave;de, Andrea Petrucci
94aaedbb8f26d71ed3ad6dd34490e29e -1 fr DOS Floppy Floppy alt? Nicolas Joly
57b0d89af79befe1cabce3bece869e7f -1 de DOS Floppy Floppy - Andrej Sinicyn, Andrea Petrucci
@@ -666,6 +669,7 @@ pajama2 Pajama Sam 2: Thunder and Lightning Aren't so Frightening
d4e79c3d8645b8266cd78c325bc35154 60557 us All - - - Kirben
6a60d395b78b205c93a956100b1bf5ae -1 de All HE 98.5 - - EdDammer
513f91a9dbe8d5490b39e56a3ac5bbdf -1 nl All HE 98.5 - - daniel9
+ 2328be0317008ef047eed7912a4b0850 -1 gb Windows HE 98.5 - - Saleck
55f4e9402bec2bded383843123f37c5c -1 de Windows HE 98.5 - - WindlePoons
e5563c8358443c4352fcddf7402a5e0a -1 fr Windows HE 98.5 - - gist974
c6907d44f1166941d982864cd42cdc89 -1 de All HE 99 - - nachbarnebenan
@@ -682,6 +686,7 @@ pajama2 Pajama Sam 2: Thunder and Lightning Aren't so Frightening
pajama3 Pajama Sam 3: You Are What You Eat From Your Head to Your Feet
f7711f9264d4d43c2a1518ec7c10a607 79382 us All - - - Kirben
2e8a1f76ea33bc5e04347646feee173d -1 de All - - - Joachim Eberhard
+ 83e7a9205567dceb456ee35eeaf26ffa -1 it All - - - Saleck
aefa244ea034b7cd2041f0a44be7d9ba -1 en Mac - - - pix_climber
06c3cf4f31daad8b1cd93153491db9e6 79382 nl All - - - daniel9
7410a8ba9795020cd42f171c4320659e -1 fr Windows - - - gist974
@@ -724,6 +729,7 @@ socks Pajama Sam's Sock Works
puttrace Putt-Putt Enters the Race
981e1e1891f2be7e25a01f50ae55a5af -1 us All HE 98 - - Kirben
+ 05d3143827ab4f5d2521a1a47dab8ff2 -1 it All HE 98 - - Saleck
1ed22f601f8b3695804a6583cc3083f1 -1 nl All HE 98.5 - - daniel9
33e989f85da700e2014d00f345cab3d7 -1 fr Windows HE 98.5 - - gist974
b47be81e39a9710f6f595f7b527b60f8 -1 gb Windows HE 99 - - Reckless
@@ -887,6 +893,7 @@ spyfox2 SPY Fox 2: Some Assembly Required
f79e60c17cca601e411f1f75e8ee9b5a 51286 All All - - - Kirben
90e2f0af4f779629695c6394a65bb702 -1 fr All - - - gist974, ThierryFR
bc4700bc0e12879f6d25d14d6be6cfdd -1 de All - - - Joachim Eberhard
+ 3785fd25f7e02b5782bfc5072d8f77c8 -1 it All - - - Saleck
cea91e3dd47f2518ea418e41611aa77f -1 ru All - - - sev
9fd66fb3b04703bd50da4356e4202558 51295 en Mac - - - pix_climber
71fe97c3108678cf604f14abe342341b 51286 nl All - - - adutchguy
diff --git a/engines/gob/surface.cpp b/engines/gob/surface.cpp
index 42ac2b0d74..ed83e8255c 100644
--- a/engines/gob/surface.cpp
+++ b/engines/gob/surface.cpp
@@ -470,7 +470,7 @@ void Surface::blitScaled(const Surface &from, Common::Rational scale, int32 tran
blitScaled(from, 0, 0, from._width - 1, from._height - 1, 0, 0, scale, transp);
}
-void Surface::fillRect(uint16 left, uint16 top, uint16 right, uint16 bottom, uint32 color) {
+void Surface::fillRect(int16 left, int16 top, int16 right, int16 bottom, uint32 color) {
// Just in case those are swapped
if (left > right)
SWAP(left, right);
@@ -481,6 +481,11 @@ void Surface::fillRect(uint16 left, uint16 top, uint16 right, uint16 bottom, uin
// Nothing to do
return;
+ left = CLIP<int32>(left , 0, _width - 1);
+ top = CLIP<int32>(top , 0, _height - 1);
+ right = CLIP<int32>(right , 0, _width - 1);
+ bottom = CLIP<int32>(bottom, 0, _height - 1);
+
// Area to actually fill
uint16 width = CLIP<int32>(right - left + 1, 0, _width - left);
uint16 height = CLIP<int32>(bottom - top + 1, 0, _height - top);
diff --git a/engines/gob/surface.h b/engines/gob/surface.h
index c931731908..eb0a5bf1a4 100644
--- a/engines/gob/surface.h
+++ b/engines/gob/surface.h
@@ -121,7 +121,7 @@ public:
void blitScaled(const Surface &from, int16 x, int16 y, Common::Rational scale, int32 transp = -1);
void blitScaled(const Surface &from, Common::Rational scale, int32 transp = -1);
- void fillRect(uint16 left, uint16 top, uint16 right, uint16 bottom, uint32 color);
+ void fillRect(int16 left, int16 top, int16 right, int16 bottom, uint32 color);
void fill(uint32 color);
void clear();
diff --git a/engines/mads/detection_tables.h b/engines/mads/detection_tables.h
index 0a8e98bb31..faf73996ac 100644
--- a/engines/mads/detection_tables.h
+++ b/engines/mads/detection_tables.h
@@ -55,7 +55,7 @@ static const MADSGameDescription gameDescriptions[] = {
},
Common::EN_ANY,
Common::kPlatformDOS,
- ADGF_NO_FLAGS,
+ ADGF_TESTING,
GUIO5(GUIO_NOSPEECH, GAMEOPTION_EASY_MOUSE, GAMEOPTION_ANIMATED_INVENTORY, GAMEOPTION_ANIMATED_INTERFACE, GAMEOPTION_NAUGHTY_MODE)
},
GType_RexNebular,
@@ -73,7 +73,7 @@ static const MADSGameDescription gameDescriptions[] = {
},
Common::EN_ANY,
Common::kPlatformDOS,
- ADGF_NO_FLAGS,
+ ADGF_TESTING,
GUIO5(GUIO_NOSPEECH, GAMEOPTION_EASY_MOUSE, GAMEOPTION_ANIMATED_INVENTORY, GAMEOPTION_ANIMATED_INTERFACE, GAMEOPTION_NAUGHTY_MODE)
},
GType_RexNebular,
diff --git a/engines/mads/dialogs.cpp b/engines/mads/dialogs.cpp
index 158d9693ad..7f0b02bc65 100644
--- a/engines/mads/dialogs.cpp
+++ b/engines/mads/dialogs.cpp
@@ -43,6 +43,7 @@ Dialog::Dialog(MADSEngine *vm)
}
Dialog::~Dialog() {
+ delete _savedSurface;
}
void Dialog::save() {
diff --git a/engines/mads/events.cpp b/engines/mads/events.cpp
index 767f998d81..52569af375 100644
--- a/engines/mads/events.cpp
+++ b/engines/mads/events.cpp
@@ -84,8 +84,8 @@ void EventsManager::waitCursor() {
CursorType cursorId = (CursorType)MIN(_cursorSprites->getCount(), (int)CURSOR_WAIT);
_newCursorId = cursorId;
if (_cursorId != _newCursorId) {
- changeCursor();
_cursorId = _newCursorId;
+ changeCursor();
}
}
diff --git a/engines/mads/game.cpp b/engines/mads/game.cpp
index b601a12c82..3d1c194612 100644
--- a/engines/mads/game.cpp
+++ b/engines/mads/game.cpp
@@ -100,6 +100,7 @@ Game::~Game() {
}
delete _saveFile;
+ _surface->free();
delete _surface;
delete _sectionHandler;
}
@@ -115,8 +116,6 @@ void Game::run() {
_statusFlag = true;
while (!_vm->shouldQuit()) {
- initializeGlobals();
-
if (_loadGameSlot == -1) {
startGame();
}
diff --git a/engines/mads/mads.cpp b/engines/mads/mads.cpp
index 59d600fbfb..374e373035 100644
--- a/engines/mads/mads.cpp
+++ b/engines/mads/mads.cpp
@@ -126,6 +126,13 @@ void MADSEngine::loadOptions() {
if (ConfMan.hasKey("NaughtyMode"))
_game->setNaughtyMode(ConfMan.getBool("NaughtyMode"));
}
+
+ // Note: MADS is weird in that sfx and music are handled by the same driver,
+ // and the game scripts themselves check for music being enabled before playing
+ // a "music" sound. Which means we can independantly mute music in ScummVM, but
+ // otherwise all sound, music and sfx, is controlled by the SFX volume slider.
+ int soundVolume = MIN(255, ConfMan.getInt("sfx_volume"));
+ _sound->setVolume(soundVolume);
}
void MADSEngine::saveOptions() {
diff --git a/engines/mads/menu_views.cpp b/engines/mads/menu_views.cpp
index 5a0c7ee92e..319f5b0f87 100644
--- a/engines/mads/menu_views.cpp
+++ b/engines/mads/menu_views.cpp
@@ -544,6 +544,7 @@ void AnimationView::doFrame() {
scriptDone();
} else {
scene._frameStartTime = 0;
+ scene._spriteSlots.clear();
loadNextResource();
}
} else if (_currentAnimation->getCurrentFrame() == 1) {
@@ -559,6 +560,10 @@ void AnimationView::doFrame() {
++scene._frameStartTime;
_currentAnimation->update();
_redrawFlag = true;
+
+ if (_currentAnimation->freeFlag())
+ // We don't want the sprites removed after the last animation frame
+ scene._spriteSlots.clear();
}
}
diff --git a/engines/mads/messages.cpp b/engines/mads/messages.cpp
index 4b105630d6..304c79aa46 100644
--- a/engines/mads/messages.cpp
+++ b/engines/mads/messages.cpp
@@ -91,7 +91,7 @@ int KernelMessages::add(const Common::Point &pt, uint fontColor, uint8 flags,
rec._position = pt;
rec._textDisplayIndex = -1;
rec._timeout = timeout;
- rec._frameTimer = _vm->_game->_priorFrameTimer;
+ rec._frameTimer = scene._frameStartTime;
rec._trigger = endTrigger;
rec._abortMode = _vm->_game->_triggerSetupMode;
@@ -162,8 +162,10 @@ void KernelMessages::update() {
uint32 currentTimer = _vm->_game->_scene._frameStartTime;
for (uint i = 0; i < _entries.size() && !_vm->_game->_trigger; ++i) {
- KernelMessage &msg = _entries[i];
+ if (_vm->_game->_trigger)
+ break;
+ KernelMessage &msg = _entries[i];
if (((msg._flags & KMSG_ACTIVE) != 0) && (currentTimer >= msg._frameTimer))
processText(i);
}
@@ -172,7 +174,7 @@ void KernelMessages::update() {
void KernelMessages::processText(int msgIndex) {
Scene &scene = _vm->_game->_scene;
KernelMessage &msg = _entries[msgIndex];
- uint32 currentTimer = _vm->_game->_priorFrameTimer;
+ uint32 currentTimer = scene._frameStartTime;
bool flag = false;
if ((msg._flags & KMSG_EXPIRE) != 0) {
diff --git a/engines/mads/nebular/dialogs_nebular.cpp b/engines/mads/nebular/dialogs_nebular.cpp
index 4ba5366a60..5b9942db07 100644
--- a/engines/mads/nebular/dialogs_nebular.cpp
+++ b/engines/mads/nebular/dialogs_nebular.cpp
@@ -417,7 +417,7 @@ TextDialog(vm, FONT_INTERFACE, Common::Point(-1, -1), 32) {
_hogEntry._pageNum, _hogEntry._lineNum, _hogEntry._wordNum);
wordWrap(line);
- wordWrap("and type it on the line below (we',27h,'ve even given you");
+ wordWrap("and type it on the line below (we've even given you");
wordWrap("first letter as a hint). As soon as you do that, we can get");
wordWrap("right into this really COOL adventure game!\n");
wordWrap("\n");
@@ -428,17 +428,58 @@ TextDialog(vm, FONT_INTERFACE, Common::Point(-1, -1), 32) {
void CopyProtectionDialog::show() {
draw();
- _vm->_events->showCursor();
- // TODO: Replace with text input
- while (!_vm->shouldQuit() && !_vm->_events->isKeyPressed() &&
- !_vm->_events->_mouseClicked) {
- _vm->_events->delay(1);
+ Common::KeyState curKey;
+ const Common::Rect inputArea(110, 165, 210, 175);
+ MSurface *origInput = new MSurface(inputArea.width(), inputArea.height());
+ _vm->_screen.frameRect(inputArea, TEXTDIALOG_BLACK);
+ _vm->_screen.copyTo(origInput, inputArea, Common::Point(0, 0));
+ _font->setColors(TEXTDIALOG_FE, TEXTDIALOG_FE, TEXTDIALOG_FE, TEXTDIALOG_FE);
+ _vm->_screen.copyRectToScreen(inputArea);
+ _vm->_screen.updateScreen();
+
+ bool firstTime = true;
+
+ while (!_vm->shouldQuit()) {
+ if (!firstTime) {
+ while (!_vm->shouldQuit() && !_vm->_events->isKeyPressed()) {
+ _vm->_events->delay(1);
+ }
+
+ if (_vm->shouldQuit())
+ break;
+
+ curKey = _vm->_events->getKey();
+
+ if (curKey.keycode == Common::KEYCODE_RETURN || curKey.keycode == Common::KEYCODE_KP_ENTER)
+ break;
+ else if (curKey.keycode == Common::KEYCODE_BACKSPACE)
+ _textInput.deleteLastChar();
+ else if (_textInput.size() < 14)
+ _textInput += curKey.ascii;
+
+ _vm->_events->_pendingKeys.clear();
+ } else {
+ firstTime = false;
+ _textInput = _hogEntry._word[0];
+ }
+
+ _vm->_screen.copyFrom(origInput, Common::Rect(0, 0, inputArea.width(), inputArea.height()), Common::Point(inputArea.left, inputArea.top));
+ _font->writeString(&_vm->_screen, _textInput,
+ Common::Point(inputArea.left + 2, inputArea.top + 1), 1);
+ _vm->_screen.copyRectToScreen(inputArea);
+ _vm->_screen.updateScreen();
}
- _vm->_events->_pendingKeys.clear();
+ origInput->free();
+ delete origInput;
}
+bool CopyProtectionDialog::isCorrectAnswer() {
+ return _hogEntry._word == _textInput;
+}
+
+
bool CopyProtectionDialog::getHogAnusEntry(HOGANUS &entry) {
File f;
f.open("*HOGANUS.DAT");
@@ -552,6 +593,7 @@ void PictureDialog::save() {
void PictureDialog::restore() {
if (_savedSurface) {
_savedSurface->copyTo(&_vm->_screen);
+ _savedSurface->free();
delete _savedSurface;
_savedSurface = nullptr;
diff --git a/engines/mads/nebular/dialogs_nebular.h b/engines/mads/nebular/dialogs_nebular.h
index 5dbe4da6f0..0f086f6ec1 100644
--- a/engines/mads/nebular/dialogs_nebular.h
+++ b/engines/mads/nebular/dialogs_nebular.h
@@ -69,6 +69,7 @@ struct HOGANUS {
class CopyProtectionDialog : public TextDialog {
private:
HOGANUS _hogEntry;
+ Common::String _textInput;
/**
* Get a random copy protection entry from the HOGANUS resource
@@ -84,6 +85,8 @@ public:
* Show the dialog
*/
virtual void show();
+
+ bool isCorrectAnswer();
};
class PictureDialog : public TextDialog {
diff --git a/engines/mads/nebular/game_nebular.cpp b/engines/mads/nebular/game_nebular.cpp
index cde998e66a..e9a3d0b716 100644
--- a/engines/mads/nebular/game_nebular.cpp
+++ b/engines/mads/nebular/game_nebular.cpp
@@ -45,19 +45,25 @@ GameNebular::GameNebular(MADSEngine *vm)
}
ProtectionResult GameNebular::checkCopyProtection() {
- /*
- // DEBUG: Flag copy protection failure
- _globals[kCopyProtectFailed] = -1;
+ //if (!ConfMan.getBool("copy_protection"))
+ // return PROTECTION_SUCCEED;
- if (!ConfMan.getBool("copy_protection"))
- return true;
+ CopyProtectionDialog *dlg;
+ bool correctAnswer;
- * DEBUG: Disabled for now
- CopyProtectionDialog *dlg = new CopyProtectionDialog(_vm, false);
+ dlg = new CopyProtectionDialog(_vm, false);
dlg->show();
+ correctAnswer = dlg->isCorrectAnswer();
delete dlg;
- */
- return PROTECTION_SUCCEED;
+
+ if (!correctAnswer && !_vm->shouldQuit()) {
+ dlg = new CopyProtectionDialog(_vm, true);
+ dlg->show();
+ correctAnswer = dlg->isCorrectAnswer();
+ delete dlg;
+ }
+
+ return correctAnswer ? PROTECTION_SUCCEED : PROTECTION_FAIL;
}
void GameNebular::startGame() {
@@ -91,26 +97,6 @@ void GameNebular::startGame() {
checkShowDialog();
_winStatus = 0;
- /*
- // Check copy protection
- ProtectionResult protectionResult = checkCopyProtection();
- switch (protectionResult) {
- case PROTECTION_FAIL:
- // Copy protection failed
- _scene._nextSceneId = 804;
- initializeGlobals();
- _globals[kCopyProtectFailed] = true;
- return;
- case PROTECTION_ESCAPE:
- // User escaped out of copy protection dialog
- _vm->quitGame();
- return;
- default:
- // Copy protection check succeeded
- break;
- }
- */
-
_sectionNumber = 1;
initSection(_sectionNumber);
_vm->_events->setCursor(CURSOR_ARROW);
@@ -121,6 +107,9 @@ void GameNebular::startGame() {
_vm->_dialogs->showDialog();
} while (!_vm->shouldQuit() && _vm->_dialogs->_pendingDialog != DIALOG_NONE);
+ if (_vm->shouldQuit())
+ return;
+
_priorSectionNumber = 0;
_priorSectionNumber = -1;
_scene._priorSceneId = 0;
@@ -128,6 +117,24 @@ void GameNebular::startGame() {
_scene._nextSceneId = 101;
initializeGlobals();
+
+ // Check copy protection
+ ProtectionResult protectionResult = checkCopyProtection();
+
+ switch (protectionResult) {
+ case PROTECTION_FAIL:
+ // Copy protection failed
+ _scene._nextSceneId = 804;
+ _globals[kCopyProtectFailed] = true;
+ return;
+ case PROTECTION_ESCAPE:
+ // User escaped out of copy protection dialog
+ _vm->quitGame();
+ return;
+ default:
+ // Copy protection check succeeded
+ break;
+ }
}
void GameNebular::initializeGlobals() {
diff --git a/engines/mads/nebular/nebular_scenes1.cpp b/engines/mads/nebular/nebular_scenes1.cpp
index 0c5888b6ec..c9eda08859 100644
--- a/engines/mads/nebular/nebular_scenes1.cpp
+++ b/engines/mads/nebular/nebular_scenes1.cpp
@@ -1462,7 +1462,6 @@ void Scene103::actions() {
} else if (_action.isAction(VERB_TAKE, NOUN_REBREATHER, 0) && _game._objects.isInRoom(OBJ_REBREATHER)) {
switch (_vm->_game->_trigger) {
case 0:
- _scene->changeVariant(1);
_globals._sequenceIndexes[12] = _scene->_sequences.startPingPongCycle(_globals._spriteIndexes[12], false, 3, 2);
_scene->_sequences.setMsgLayout(_globals._sequenceIndexes[12]);
_scene->_sequences.addSubEntry(_globals._sequenceIndexes[12], SEQUENCE_TRIGGER_SPRITE, 6, 1);
@@ -2113,9 +2112,9 @@ void Scene106::step() {
}
if (msgId >= 0) {
- int nextAbortVal = _game._trigger + 1;
+ int nextTrigger = _game._trigger + 1;
_scene->_kernelMessages.add(Common::Point(15, _positionY), 0x1110, 0, 0, 360, _game.getQuote(msgId));
- _scene->_sequences.addTimer(150, nextAbortVal);
+ _scene->_sequences.addTimer(150, nextTrigger);
_positionY += 14;
}
}
@@ -2590,8 +2589,8 @@ void Scene109::preActions() {
_game._player._walkOffScreenSceneId = 108;
if ((_action.isAction(VERB_THROW) || _action.isAction(VERB_GIVE) || _action.isAction(VERB_PUT))
- && (_action.isObject(NOUN_SMALL_HOLE) || _action.isObject(NOUN_TUNNEL))
- && (_action.isObject(NOUN_DEAD_FISH) || _action.isObject(NOUN_STUFFED_FISH) || _action.isObject(NOUN_BURGER))) {
+ && (_action.isTarget(NOUN_SMALL_HOLE) || _action.isTarget(NOUN_TUNNEL))
+ && (_action.isObject(NOUN_DEAD_FISH) || _action.isObject(NOUN_STUFFED_FISH) || _action.isObject(NOUN_BURGER))) {
int idx = _game._objects.getIdFromDesc(_action._activeAction._objectNameId);
if ((idx >= 0) && _game._objects.isInInventory(idx)) {
_game._player._prepareWalkPos = Common::Point(106, 38);
@@ -2638,7 +2637,7 @@ void Scene109::actions() {
break;
case OBJ_BURGER:
- _hoovicDifficultFl = (_game._difficulty == DIFFICULTY_EASY);
+ _hoovicDifficultFl = (_game._difficulty == DIFFICULTY_HARD);
_globals._spriteIndexes[8] = _scene->_sprites.addSprites(formAnimName('H', (_hoovicDifficultFl ? 3 : 1)));
break;
}
diff --git a/engines/mads/nebular/nebular_scenes6.cpp b/engines/mads/nebular/nebular_scenes6.cpp
index 046782b772..d94fb17fd4 100644
--- a/engines/mads/nebular/nebular_scenes6.cpp
+++ b/engines/mads/nebular/nebular_scenes6.cpp
@@ -948,8 +948,14 @@ void Scene604::actions() {
_bombMode = 1;
if ((_game._difficulty == DIFFICULTY_HARD) || _globals[kWarnedFloodCity])
handleBombActions();
- else if ((_game._objects.isInInventory(OBJ_POLYCEMENT) && _game._objects.isInInventory(OBJ_CHICKEN))
- && ((_globals[kLineStatus] == LINE_TIED) || ((_game._difficulty == DIFFICULTY_EASY) && (!_globals[kBoatRaised]))))
+ else if (
+ (_game._objects.isInInventory(OBJ_POLYCEMENT) && (_game._objects.isInInventory(OBJ_CHICKEN) || _game._objects.isInInventory(OBJ_CHICKEN_BOMB)))
+ && (_globals[kLineStatus] == LINE_TIED || (_game._difficulty == DIFFICULTY_EASY && !_globals[kBoatRaised]))
+ )
+ // The original can get in an impossible state at this point, if the player has
+ // combined the chicken with the bomb before placing the timer bomb on the ledge.
+ // Therefore, we also allow the player to place the bomb if the chicken bomb is
+ // in the inventory.
handleBombActions();
else if (_game._difficulty == DIFFICULTY_EASY)
_vm->_dialogs->show(60424);
diff --git a/engines/mads/nebular/nebular_scenes8.cpp b/engines/mads/nebular/nebular_scenes8.cpp
index 8ce559b82b..5f8417cee1 100644
--- a/engines/mads/nebular/nebular_scenes8.cpp
+++ b/engines/mads/nebular/nebular_scenes8.cpp
@@ -957,7 +957,7 @@ void Scene804::step() {
_globals[kInSpace] = false;
_globals[kBeamIsUp] = true;
- assert(!_globals[kCopyProtectFailed]);
+ //assert(!_globals[kCopyProtectFailed]);
_game._winStatus = 4;
return;
}
diff --git a/engines/mads/nebular/sound_nebular.cpp b/engines/mads/nebular/sound_nebular.cpp
index 6412654fd6..9716e6d522 100644
--- a/engines/mads/nebular/sound_nebular.cpp
+++ b/engines/mads/nebular/sound_nebular.cpp
@@ -44,6 +44,7 @@ AdlibChannel::AdlibChannel() {
_field4 = 0;
_sampleIndex = 0;
_volume = 0;
+ _volumeOffset = 0;
_field7 = 0;
_field8 = 0;
_field9 = 0;
@@ -61,7 +62,6 @@ AdlibChannel::AdlibChannel() {
_field19 = 0;
_soundData = nullptr;
_field1D = 0;
- _field1E = 0;
_field1F = 0;
_field20 = 0;
@@ -97,6 +97,7 @@ void AdlibChannel::setPtr2(byte *pData) {
void AdlibChannel::load(byte *pData) {
_ptr1 = _pSrc = _ptr3 = pData;
_ptr4 = _soundData = pData;
+ _volumeOffset = 0;
_fieldA = 0xFF;
_activeCount = 1;
_fieldD = 64;
@@ -104,7 +105,7 @@ void AdlibChannel::load(byte *pData) {
_field1F = 0;
_field2 = _field3 = 0;
_volume = _field7 = 0;
- _field1D = _field1E = 0;
+ _field1D = 0;
_fieldE = 0;
_field9 = 0;
_fieldB = 0;
@@ -117,7 +118,7 @@ void AdlibChannel::load(byte *pData) {
void AdlibChannel::check(byte *nullPtr) {
if (_activeCount && _fieldE) {
- if (!_field1E) {
+ if (!_volumeOffset) {
_pSrc = nullPtr;
_fieldE = 0;
} else {
@@ -166,6 +167,7 @@ ASound::ASound(Audio::Mixer *mixer, FM_OPL *opl, const Common::String &filename,
_samplePtr = nullptr;
_frameCounter = 0;
_isDisabled = false;
+ _masterVolume = 255;
_v1 = 0;
_v2 = 0;
_activeChannelNumber = 0;
@@ -540,7 +542,7 @@ void ASound::pollActiveChannel() {
chan->_field1 = 0;
chan->_field2 = chan->_field3 = 0;
chan->_volume = chan->_field7 = 0;
- chan->_field1D = chan->_field1E = 0;
+ chan->_field1D = chan->_volumeOffset = 0;
chan->_field8 = 0;
chan->_field9 = 0;
chan->_fieldB = 0;
@@ -615,7 +617,7 @@ void ASound::pollActiveChannel() {
if (chan->_fieldE) {
chan->_pSrc += 2;
} else {
- chan->_field1E = *pSrc >> 1;
+ chan->_volumeOffset = *pSrc >> 1;
updateFlag = true;
chan->_pSrc += 2;
}
@@ -659,7 +661,7 @@ void ASound::pollActiveChannel() {
if (!--chan->_field9) {
chan->_field9 = chan->_fieldA;
if (chan->_field2) {
- int8 newVal = (int8)chan->_field2 + (int8)chan->_field1E;
+ int8 newVal = (int8)chan->_field2 + (int8)chan->_volumeOffset;
if (newVal < 0) {
chan->_field9 = 0;
newVal = 0;
@@ -668,7 +670,7 @@ void ASound::pollActiveChannel() {
newVal = 63;
}
- chan->_field1E = newVal;
+ chan->_volumeOffset = newVal;
updateFlag = true;
}
}
@@ -755,7 +757,8 @@ static const int outputChannels[] = {
void ASound::updateActiveChannel() {
int reg = 0x40 + outputChannels[outputIndexes[_activeChannelNumber * 2 + 1]];
int portVal = _ports[reg] & 0xFFC0;
- int newVolume = CLIP(_activeChannelPtr->_volume + _activeChannelPtr->_field1E, 0, 63);
+ int newVolume = CLIP(_activeChannelPtr->_volume + _activeChannelPtr->_volumeOffset, 0, 63);
+ newVolume = newVolume * _masterVolume / 255;
// Note: Original had a whole block not seeming to be used, since the initialisation
// sets a variable to 5660h, and doesn't change it, so the branch is never taken
@@ -857,6 +860,12 @@ int ASound::readBuffer(int16 *buffer, const int numSamples) {
return numSamples;
}
+void ASound::setVolume(int volume) {
+ _masterVolume = volume;
+ if (!volume)
+ command0();
+}
+
int ASound::command0() {
bool isDisabled = _isDisabled;
_isDisabled = true;
@@ -1014,22 +1023,22 @@ int ASound1::command10() {
int ASound1::command11() {
command111213();
- _channels[0]._field1E = 0;
- _channels[1]._field1E = 0;
+ _channels[0]._volumeOffset = 0;
+ _channels[1]._volumeOffset = 0;
return 0;
}
int ASound1::command12() {
command111213();
- _channels[0]._field1E = 40;
- _channels[1]._field1E = 0;
+ _channels[0]._volumeOffset = 40;
+ _channels[1]._volumeOffset = 0;
return 0;
}
int ASound1::command13() {
command111213();
- _channels[0]._field1E = 40;
- _channels[1]._field1E = 50;
+ _channels[0]._volumeOffset = 40;
+ _channels[1]._volumeOffset = 50;
return 0;
}
diff --git a/engines/mads/nebular/sound_nebular.h b/engines/mads/nebular/sound_nebular.h
index cfacb211a4..d2fc552eec 100644
--- a/engines/mads/nebular/sound_nebular.h
+++ b/engines/mads/nebular/sound_nebular.h
@@ -70,7 +70,7 @@ public:
int _field19;
byte *_soundData;
int _field1D;
- int _field1E;
+ int _volumeOffset;
int _field1F;
// TODO: Only used by asound.003. Figure out usage
@@ -146,6 +146,7 @@ class ASound : public Audio::AudioStream {
private:
Common::List<CachedDataEntry> _dataCache;
uint16 _randomSeed;
+ int _masterVolume;
/**
* Does the initial Adlib initialisation
@@ -382,6 +383,11 @@ public:
* Return sample rate
*/
virtual int getRate() const { return 11025; }
+
+ /**
+ * Set the volume
+ */
+ void setVolume(int volume);
};
class ASound1 : public ASound {
diff --git a/engines/mads/scene_data.cpp b/engines/mads/scene_data.cpp
index d0e96be1d7..e48bcd8c6f 100644
--- a/engines/mads/scene_data.cpp
+++ b/engines/mads/scene_data.cpp
@@ -469,6 +469,7 @@ void SceneInfo::loadMadsV2Background(int sceneId, const Common::String &resName,
for (int i = 0; i < tileIndex; i++)
++tile;
((*tile).get())->copyTo(&bgSurface, Common::Point(x * tileWidth, y * tileHeight));
+ ((*tile).get())->free();
}
}
tileSet.clear();
diff --git a/engines/mads/sound.cpp b/engines/mads/sound.cpp
index 1baa169c55..4036ee8112 100644
--- a/engines/mads/sound.cpp
+++ b/engines/mads/sound.cpp
@@ -36,6 +36,7 @@ SoundManager::SoundManager(MADSEngine *vm, Audio::Mixer *mixer) {
_pollSoundEnabled = false;
_soundPollFlag = false;
_newSoundsPaused = false;
+ _masterVolume = 255;
_opl = OPL::Config::create();
_opl->init(11025);
@@ -97,15 +98,18 @@ void SoundManager::init(int sectionNumber) {
break;
default:
_driver = nullptr;
- break;
+ return;
}
break;
default:
warning("SoundManager: Unknown game");
_driver = nullptr;
- break;
+ return;
}
+
+ // Set volume for newly loaded driver
+ _driver->setVolume(_masterVolume);
}
void SoundManager::closeDriver() {
@@ -141,6 +145,13 @@ void SoundManager::startQueuedCommands() {
}
}
+void SoundManager::setVolume(int volume) {
+ _masterVolume = volume;
+
+ if (_driver)
+ _driver->setVolume(volume);
+}
+
void SoundManager::command(int commandId, int param) {
if (_newSoundsPaused) {
if (_queuedCommands.size() < 8)
diff --git a/engines/mads/sound.h b/engines/mads/sound.h
index 72bb21a812..5884323474 100644
--- a/engines/mads/sound.h
+++ b/engines/mads/sound.h
@@ -43,6 +43,7 @@ private:
bool _soundPollFlag;
bool _newSoundsPaused;
Common::Queue<int> _queuedCommands;
+ int _masterVolume;
public:
SoundManager(MADSEngine *vm, Audio::Mixer *mixer);
~SoundManager();
@@ -78,6 +79,11 @@ public:
*/
void startQueuedCommands();
+ /**
+ * Set the master volume
+ */
+ void setVolume(int volume);
+
//@{
/**
* Executes a command on the sound driver
diff --git a/engines/mads/sprites.cpp b/engines/mads/sprites.cpp
index 67d5e20fe5..f15d6456d3 100644
--- a/engines/mads/sprites.cpp
+++ b/engines/mads/sprites.cpp
@@ -347,8 +347,10 @@ void SpriteSlots::drawSprites(MSurface *s) {
spr->copyTo(s, Common::Point(xp, yp), sprite->getTransparencyIndex());
// Free sprite if it was a flipped one
- if (flipped)
+ if (flipped) {
+ spr->free();
delete spr;
+ }
}
}
}
@@ -399,7 +401,7 @@ void SpriteSets::remove(int idx) {
if (idx == SPRITE_SLOTS_MAX_SIZE) {
delete _uiSprites;
_uiSprites = nullptr;
- } else if (idx >= 0) {
+ } else if (idx >= 0 && idx < (int)size()) {
delete (*this)[idx];
if (idx < ((int)size() - 1)) {
diff --git a/engines/mads/user_interface.cpp b/engines/mads/user_interface.cpp
index 93a555d9c7..1e5a1d80d2 100644
--- a/engines/mads/user_interface.cpp
+++ b/engines/mads/user_interface.cpp
@@ -164,6 +164,7 @@ void UISlots::draw(bool updateFlag, bool delFlag) {
MSurface *spr = sprite->flipHorizontal();
userInterface.mergeFrom(spr, spr->getBounds(), slot._position,
sprite->getTransparencyIndex());
+ spr->free();
delete spr;
} else {
userInterface.mergeFrom(sprite, sprite->getBounds(), slot._position,
diff --git a/engines/mohawk/configure.engine b/engines/mohawk/configure.engine
index fa9d15cffc..47402c4560 100644
--- a/engines/mohawk/configure.engine
+++ b/engines/mohawk/configure.engine
@@ -3,4 +3,4 @@
add_engine mohawk "Mohawk" yes "cstime myst riven" "Living Books"
add_engine cstime "Where in Time is Carmen Sandiego?" no
add_engine riven "Riven: The Sequel to Myst" no "" "" "16bit"
-add_engine myst "Myst" no "" "" "16bit"
+add_engine myst "Myst" no
diff --git a/engines/mohawk/cursors.cpp b/engines/mohawk/cursors.cpp
index f1baac02e2..4b66829e6a 100644
--- a/engines/mohawk/cursors.cpp
+++ b/engines/mohawk/cursors.cpp
@@ -122,7 +122,11 @@ void MystCursorManager::setCursor(uint16 id) {
// Myst ME stores some cursors as 24bpp images instead of 8bpp
if (surface->format.bytesPerPixel == 1) {
CursorMan.replaceCursor(surface->getPixels(), surface->w, surface->h, hotspotX, hotspotY, 0);
- CursorMan.replaceCursorPalette(mhkSurface->getPalette(), 0, 256);
+
+ // We're using the screen palette for the original game, but we need
+ // to use this for any 8bpp cursor in ME.
+ if (_vm->getFeatures() & GF_ME)
+ CursorMan.replaceCursorPalette(mhkSurface->getPalette(), 0, 256);
} else {
Graphics::PixelFormat pixelFormat = g_system->getScreenFormat();
CursorMan.replaceCursor(surface->getPixels(), surface->w, surface->h, hotspotX, hotspotY, pixelFormat.RGBToColor(255, 255, 255), false, &pixelFormat);
diff --git a/engines/mohawk/myst.cpp b/engines/mohawk/myst.cpp
index 7634e8d88a..b6a6c27329 100644
--- a/engines/mohawk/myst.cpp
+++ b/engines/mohawk/myst.cpp
@@ -413,7 +413,12 @@ void MohawkEngine_Myst::changeToStack(uint16 stack, uint16 card, uint16 linkSrcS
// Fill screen with black and empty cursor
_cursor->setCursor(0);
- _system->fillScreen(_system->getScreenFormat().RGBToColor(0, 0, 0));
+
+ if (getFeatures() & GF_ME)
+ _system->fillScreen(_system->getScreenFormat().RGBToColor(0, 0, 0));
+ else
+ _gfx->clearScreenPalette();
+
_system->updateScreen();
_sound->stopSound();
@@ -495,9 +500,10 @@ void MohawkEngine_Myst::changeToStack(uint16 stack, uint16 card, uint16 linkSrcS
_cache.clear();
_gfx->clearCache();
- // Play Flyby Entry Movie on Masterpiece Edition.
- const char *flyby = 0;
if (getFeatures() & GF_ME) {
+ // Play Flyby Entry Movie on Masterpiece Edition.
+ const char *flyby = 0;
+
switch (_curStack) {
case kSeleniticStack:
flyby = "selenitic flyby";
diff --git a/engines/mohawk/myst_graphics.cpp b/engines/mohawk/myst_graphics.cpp
index 9ea9f15444..49f97cca63 100644
--- a/engines/mohawk/myst_graphics.cpp
+++ b/engines/mohawk/myst_graphics.cpp
@@ -28,6 +28,7 @@
#include "common/system.h"
#include "common/textconsole.h"
#include "engines/util.h"
+#include "graphics/palette.h"
#include "image/pict.h"
namespace Mohawk {
@@ -37,16 +38,20 @@ MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : GraphicsManager(), _vm(vm) {
_viewport = Common::Rect(544, 332);
- // The original version of Myst could run in 8bpp color too.
- // However, it dithered videos to 8bpp and they looked considerably
- // worse (than they already did :P). So we're not even going to
- // support 8bpp mode in Myst (Myst ME required >8bpp anyway).
- initGraphics(_viewport.width(), _viewport.height(), true, NULL); // What an odd screen size!
+ if (_vm->getFeatures() & GF_ME) {
+ // High color
+ initGraphics(_viewport.width(), _viewport.height(), true, NULL);
- _pixelFormat = _vm->_system->getScreenFormat();
+ if (_vm->_system->getScreenFormat().bytesPerPixel == 1)
+ error("Myst ME requires greater than 256 colors to run");
+ } else {
+ // Paletted
+ initGraphics(_viewport.width(), _viewport.height(), true);
+ setBasePalette();
+ setPaletteToScreen();
+ }
- if (_pixelFormat.bytesPerPixel == 1)
- error("Myst requires greater than 256 colors to run");
+ _pixelFormat = _vm->_system->getScreenFormat();
// Initialize our buffer
_backBuffer = new Graphics::Surface();
@@ -101,7 +106,9 @@ MohawkSurface *MystGraphics::decodeImage(uint16 id) {
mhkSurface = new MohawkSurface(pict.getSurface()->convertTo(_pixelFormat));
} else {
mhkSurface = _bmpDecoder->decodeImage(dataStream);
- mhkSurface->convertToTrueColor();
+
+ if (_vm->getFeatures() & GF_ME)
+ mhkSurface->convertToTrueColor();
}
assert(mhkSurface);
@@ -151,7 +158,8 @@ void MystGraphics::copyImageSectionToScreen(uint16 image, Common::Rect src, Comm
}
void MystGraphics::copyImageSectionToBackBuffer(uint16 image, Common::Rect src, Common::Rect dest) {
- Graphics::Surface *surface = findImage(image)->getSurface();
+ MohawkSurface *mhkSurface = findImage(image);
+ Graphics::Surface *surface = mhkSurface->getSurface();
// Make sure the image is bottom aligned in the dest rect
dest.top = dest.bottom - MIN<int>(surface->h, dest.height());
@@ -190,6 +198,13 @@ void MystGraphics::copyImageSectionToBackBuffer(uint16 image, Common::Rect src,
for (uint16 i = 0; i < height; i++)
memcpy(_backBuffer->getBasePtr(dest.left, i + dest.top), surface->getBasePtr(src.left, top + i), width * surface->format.bytesPerPixel);
+
+ if (!(_vm->getFeatures() & GF_ME)) {
+ // Make sure the palette is set
+ assert(mhkSurface->getPalette());
+ memcpy(_palette + 10 * 3, mhkSurface->getPalette() + 10 * 3, (256 - 10 * 2) * 3);
+ setPaletteToScreen();
+ }
}
void MystGraphics::copyImageToScreen(uint16 image, Common::Rect dest) {
@@ -419,12 +434,16 @@ void MystGraphics::transitionDissolve(Common::Rect rect, uint step) {
for (uint16 x = rect.left; x < rect.right; x++) {
if (linePattern[x % 4]) {
- if (_pixelFormat.bytesPerPixel == 2) {
- uint16 *dst = (uint16 *)screen->getBasePtr(x, y);
- *dst = *(const uint16 *)_backBuffer->getBasePtr(x, y);
- } else {
- uint32 *dst = (uint32 *)screen->getBasePtr(x, y);
- *dst = *(const uint32 *)_backBuffer->getBasePtr(x, y);
+ switch (_pixelFormat.bytesPerPixel) {
+ case 1:
+ *((byte *)screen->getBasePtr(x, y)) = *((const byte *)_backBuffer->getBasePtr(x, y));
+ break;
+ case 2:
+ *((uint16 *)screen->getBasePtr(x, y)) = *((const uint16 *)_backBuffer->getBasePtr(x, y));
+ break;
+ case 4:
+ *((uint32 *)screen->getBasePtr(x, y)) = *((const uint32 *)_backBuffer->getBasePtr(x, y));
+ break;
}
}
}
@@ -588,11 +607,11 @@ void MystGraphics::drawRect(Common::Rect rect, RectState state) {
Graphics::Surface *screen = _vm->_system->lockScreen();
if (state == kRectEnabled)
- screen->frameRect(rect, _pixelFormat.RGBToColor(0, 255, 0));
+ screen->frameRect(rect, (_vm->getFeatures() & GF_ME) ? _pixelFormat.RGBToColor(0, 255, 0) : 250);
else if (state == kRectUnreachable)
- screen->frameRect(rect, _pixelFormat.RGBToColor(0, 0, 255));
+ screen->frameRect(rect, (_vm->getFeatures() & GF_ME) ? _pixelFormat.RGBToColor(0, 0, 255) : 252);
else
- screen->frameRect(rect, _pixelFormat.RGBToColor(255, 0, 0));
+ screen->frameRect(rect, (_vm->getFeatures() & GF_ME) ? _pixelFormat.RGBToColor(255, 0, 0) : 249);
_vm->_system->unlockScreen();
}
@@ -629,50 +648,94 @@ void MystGraphics::simulatePreviousDrawDelay(const Common::Rect &dest) {
_nextAllowedDrawTime = time + _constantDrawDelay + dest.height() * dest.width() / _proportionalDrawDelay;
}
-void MystGraphics::copyBackBufferToScreenWithSaturation(int16 saturation) {
- Graphics::Surface *screen = _vm->_system->lockScreen();
+void MystGraphics::fadeToBlack() {
+ // This is only for the demo
+ assert(!(_vm->getFeatures() & GF_ME));
- for (uint16 y = 0; y < _viewport.height(); y++)
- for (uint16 x = 0; x < _viewport.width(); x++) {
- uint32 color;
- uint8 r, g, b;
+ // Linear fade in 64 steps
+ for (int i = 63; i >= 0; i--) {
+ byte palette[256 * 3];
+ byte *src = _palette;
+ byte *dst = palette;
- if (_pixelFormat.bytesPerPixel == 2)
- color = *(const uint16 *)_backBuffer->getBasePtr(x, y);
- else
- color = *(const uint32 *)_backBuffer->getBasePtr(x, y);
+ for (uint j = 0; j < sizeof(palette); j++)
+ *dst++ = *src++ * i / 64;
- _pixelFormat.colorToRGB(color, r, g, b);
+ _vm->_system->getPaletteManager()->setPalette(palette, 0, 256);
+ _vm->_system->updateScreen();
+ }
+}
- r = CLIP<int16>((int16)r - saturation, 0, 255);
- g = CLIP<int16>((int16)g - saturation, 0, 255);
- b = CLIP<int16>((int16)b - saturation, 0, 255);
+void MystGraphics::fadeFromBlack() {
+ // This is only for the demo
+ assert(!(_vm->getFeatures() & GF_ME));
- color = _pixelFormat.RGBToColor(r, g, b);
+ copyBackBufferToScreen(_viewport);
- if (_pixelFormat.bytesPerPixel == 2) {
- uint16 *dst = (uint16 *)screen->getBasePtr(x, y);
- *dst = color;
- } else {
- uint32 *dst = (uint32 *)screen->getBasePtr(x, y);
- *dst = color;
- }
- }
+ // Linear fade in 64 steps
+ for (int i = 0; i < 64; i++) {
+ byte palette[256 * 3];
+ byte *src = _palette;
+ byte *dst = palette;
- _vm->_system->unlockScreen();
+ for (uint j = 0; j < sizeof(palette); j++)
+ *dst++ = *src++ * i / 64;
+
+ _vm->_system->getPaletteManager()->setPalette(palette, 0, 256);
+ _vm->_system->updateScreen();
+ }
+
+ // Set the full palette
+ _vm->_system->getPaletteManager()->setPalette(_palette, 0, 256);
_vm->_system->updateScreen();
}
-void MystGraphics::fadeToBlack() {
- for (int16 i = 0; i < 256; i += 32) {
- copyBackBufferToScreenWithSaturation(i);
- }
+void MystGraphics::clearScreenPalette() {
+ // Set the palette to all black
+ byte palette[256 * 3];
+ memset(palette, 0, sizeof(palette));
+ _vm->_system->getPaletteManager()->setPalette(palette, 0, 256);
}
-void MystGraphics::fadeFromBlack() {
- for (int16 i = 256; i >= 0; i -= 32) {
- copyBackBufferToScreenWithSaturation(i);
- }
+void MystGraphics::setBasePalette() {
+ // Entries [0, 9] of the palette
+ static const byte lowPalette[] = {
+ 0xFF, 0xFF, 0xFF,
+ 0x80, 0x00, 0x00,
+ 0x00, 0x80, 0x00,
+ 0x80, 0x80, 0x00,
+ 0x00, 0x00, 0x80,
+ 0x80, 0x00, 0x80,
+ 0x00, 0x80, 0x80,
+ 0xC0, 0xC0, 0xC0,
+ 0xC0, 0xDC, 0xC0,
+ 0xA6, 0xCA, 0xF0
+ };
+
+ // Entries [246, 255] of the palette
+ static const byte highPalette[] = {
+ 0xFF, 0xFB, 0xF0,
+ 0xA0, 0xA0, 0xA4,
+ 0x80, 0x80, 0x80,
+ 0xFF, 0x00, 0x00,
+ 0x00, 0xFF, 0x00,
+ 0xFF, 0xFF, 0x00,
+ 0x00, 0x00, 0xFF,
+ 0xFF, 0x00, 0xFF,
+ 0x00, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00
+ };
+
+ // Note that 0 and 255 are different from normal Windows.
+ // Myst seems to hack that to white, resp. black (probably for Mac compat).
+
+ memcpy(_palette, lowPalette, sizeof(lowPalette));
+ memset(_palette + sizeof(lowPalette), 0, sizeof(_palette) - sizeof(lowPalette) - sizeof(highPalette));
+ memcpy(_palette + sizeof(_palette) - sizeof(highPalette), highPalette, sizeof(highPalette));
+}
+
+void MystGraphics::setPaletteToScreen() {
+ _vm->_system->getPaletteManager()->setPalette(_palette, 0, 256);
}
} // End of namespace Mohawk
diff --git a/engines/mohawk/myst_graphics.h b/engines/mohawk/myst_graphics.h
index 1f70320bf6..6281c94cc8 100644
--- a/engines/mohawk/myst_graphics.h
+++ b/engines/mohawk/myst_graphics.h
@@ -40,7 +40,7 @@ enum RectState {
class MystGraphics : public GraphicsManager {
public:
- MystGraphics(MohawkEngine_Myst*);
+ MystGraphics(MohawkEngine_Myst *vm);
~MystGraphics();
void copyImageSectionToScreen(uint16 image, Common::Rect src, Common::Rect dest);
@@ -55,18 +55,15 @@ public:
void fadeToBlack();
void fadeFromBlack();
+ void clearScreenPalette();
+ void setBasePalette();
+ void setPaletteToScreen();
+ const byte *getPalette() const { return _palette; }
+
protected:
MohawkSurface *decodeImage(uint16 id);
MohawkEngine *getVM() { return (MohawkEngine *)_vm; }
- void simulatePreviousDrawDelay(const Common::Rect &dest);
- void copyBackBufferToScreenWithSaturation(int16 saturation);
- void transitionDissolve(Common::Rect rect, uint step);
- void transitionSlideToLeft(Common::Rect rect, uint16 steps, uint16 delay);
- void transitionSlideToRight(Common::Rect rect, uint16 steps, uint16 delay);
- void transitionSlideToTop(Common::Rect rect, uint16 steps, uint16 delay);
- void transitionSlideToBottom(Common::Rect rect, uint16 steps, uint16 delay);
- void transitionPartialToRight(Common::Rect rect, uint32 width, uint32 steps);
- void transitionPartialToLeft(Common::Rect rect, uint32 width, uint32 steps);
+
private:
MohawkEngine_Myst *_vm;
MystBitmap *_bmpDecoder;
@@ -74,11 +71,21 @@ private:
Graphics::Surface *_backBuffer;
Graphics::PixelFormat _pixelFormat;
Common::Rect _viewport;
+ byte _palette[256 * 3];
int _enableDrawingTimeSimulation;
uint32 _nextAllowedDrawTime;
static const uint _constantDrawDelay = 10; // ms
static const uint _proportionalDrawDelay = 500; // pixels per ms
+
+ void simulatePreviousDrawDelay(const Common::Rect &dest);
+ void transitionDissolve(Common::Rect rect, uint step);
+ void transitionSlideToLeft(Common::Rect rect, uint16 steps, uint16 delay);
+ void transitionSlideToRight(Common::Rect rect, uint16 steps, uint16 delay);
+ void transitionSlideToTop(Common::Rect rect, uint16 steps, uint16 delay);
+ void transitionSlideToBottom(Common::Rect rect, uint16 steps, uint16 delay);
+ void transitionPartialToRight(Common::Rect rect, uint32 width, uint32 steps);
+ void transitionPartialToLeft(Common::Rect rect, uint32 width, uint32 steps);
};
} // End of namespace Mohawk
diff --git a/engines/mohawk/myst_stacks/myst.cpp b/engines/mohawk/myst_stacks/myst.cpp
index c500df5ad3..ca6e7c0ee5 100644
--- a/engines/mohawk/myst_stacks/myst.cpp
+++ b/engines/mohawk/myst_stacks/myst.cpp
@@ -3201,13 +3201,21 @@ Common::Point Myst::towerRotationMapComputeCoords(const Common::Point &center, u
}
void Myst::towerRotationMapDrawLine(const Common::Point &center, const Common::Point &end) {
- Graphics::PixelFormat pf = _vm->_system->getScreenFormat();
- uint32 color = 0;
+ uint32 color;
- if (!_towerRotationOverSpot)
- color = pf.RGBToColor(0xFF, 0xFF, 0xFF); // White
- else
- color = pf.RGBToColor(0xFF, 0, 0); // Red
+ if (_vm->getFeatures() & GF_ME) {
+ Graphics::PixelFormat pf = _vm->_system->getScreenFormat();
+
+ if (!_towerRotationOverSpot)
+ color = pf.RGBToColor(0xFF, 0xFF, 0xFF); // White
+ else
+ color = pf.RGBToColor(0xFF, 0, 0); // Red
+ } else {
+ if (!_towerRotationOverSpot)
+ color = 0x00; // White
+ else
+ color = 0xF9; // Red
+ }
const Common::Rect rect = Common::Rect(106, 42, 459, 273);
diff --git a/engines/mohawk/video.cpp b/engines/mohawk/video.cpp
index cebb72e24f..3f27e4a1a4 100644
--- a/engines/mohawk/video.cpp
+++ b/engines/mohawk/video.cpp
@@ -53,6 +53,8 @@ bool VideoEntry::endOfVideo() {
}
VideoManager::VideoManager(MohawkEngine* vm) : _vm(vm) {
+ // Set dithering enabled, if required
+ _enableDither = _vm->getGameType() == GType_MYST && !(_vm->getFeatures() & GF_ME);
}
VideoManager::~VideoManager() {
@@ -230,16 +232,23 @@ bool VideoManager::updateMovies() {
Graphics::PixelFormat pixelFormat = _vm->_system->getScreenFormat();
if (frame->format != pixelFormat) {
- // We don't support downconverting to 8bpp
- if (pixelFormat.bytesPerPixel == 1)
- error("Cannot convert high color video frame to 8bpp");
+ // We don't support downconverting to 8bpp without having
+ // support in the codec. Set _enableDither if shows up.
+ if (pixelFormat.bytesPerPixel == 1) {
+ warning("Cannot convert high color video frame to 8bpp");
+ delete _videoStreams[i].video;
+ _videoStreams[i].clear();
+ continue;
+ }
// Convert to the current screen format
convertedFrame = frame->convertTo(pixelFormat, _videoStreams[i]->getPalette());
frame = convertedFrame;
} else if (pixelFormat.bytesPerPixel == 1 && _videoStreams[i]->hasDirtyPalette()) {
// Set the palette when running in 8bpp mode only
- _vm->_system->getPaletteManager()->setPalette(_videoStreams[i]->getPalette(), 0, 256);
+ // Don't do this for Myst, which has its own per-stack handling
+ if (_vm->getGameType() != GType_MYST)
+ _vm->_system->getPaletteManager()->setPalette(_videoStreams[i]->getPalette(), 0, 256);
}
// Clip the width/height to make sure we stay on the screen (Myst does this a few times)
@@ -394,6 +403,9 @@ VideoHandle VideoManager::createVideoHandle(uint16 id, uint16 x, uint16 y, bool
entry.loop = loop;
entry.enabled = true;
+ // Enable dither if necessary
+ checkEnableDither(entry);
+
entry->start();
// Search for any deleted videos so we can take a formerly used slot
@@ -431,6 +443,10 @@ VideoHandle VideoManager::createVideoHandle(const Common::String &filename, uint
}
entry->loadStream(file);
+
+ // Enable dither if necessary
+ checkEnableDither(entry);
+
entry->setVolume(volume);
entry->start();
@@ -551,4 +567,22 @@ void VideoManager::pauseMovie(VideoHandle handle, bool pause) {
_videoStreams[handle]->pauseVideo(pause);
}
+void VideoManager::checkEnableDither(VideoEntry &entry) {
+ // If we're not dithering, bail out
+ if (!_enableDither)
+ return;
+
+ // Set the palette
+ byte palette[256 * 3];
+ g_system->getPaletteManager()->grabPalette(palette, 0, 256);
+ entry->setDitheringPalette(palette);
+
+ if (entry->getPixelFormat().bytesPerPixel != 1) {
+ if (entry.filename.empty())
+ error("Failed to set dither for video %d", entry.id);
+ else
+ error("Failed to set dither for video %s", entry.filename.c_str());
+ }
+}
+
} // End of namespace Mohawk
diff --git a/engines/mohawk/video.h b/engines/mohawk/video.h
index 43181e3e6c..deb09afe6b 100644
--- a/engines/mohawk/video.h
+++ b/engines/mohawk/video.h
@@ -124,6 +124,10 @@ private:
VideoHandle createVideoHandle(uint16 id, uint16 x, uint16 y, bool loop, uint16 volume = 0xff);
VideoHandle createVideoHandle(const Common::String &filename, uint16 x, uint16 y, bool loop, byte volume = 0xff);
+
+ // Dithering control
+ bool _enableDither;
+ void checkEnableDither(VideoEntry &entry);
};
} // End of namespace Mohawk
diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp
index a3abf606d0..bc9ad362ab 100644
--- a/engines/sci/console.cpp
+++ b/engines/sci/console.cpp
@@ -209,6 +209,8 @@ Console::Console(SciEngine *engine) : GUI::Debugger(),
registerCmd("bpe", WRAP_METHOD(Console, cmdBreakpointFunction)); // alias
// VM
registerCmd("script_steps", WRAP_METHOD(Console, cmdScriptSteps));
+ registerCmd("script_strings", WRAP_METHOD(Console, cmdScriptStrings));
+ registerCmd("scrs", WRAP_METHOD(Console, cmdScriptStrings));
registerCmd("vm_varlist", WRAP_METHOD(Console, cmdVMVarlist));
registerCmd("vmvarlist", WRAP_METHOD(Console, cmdVMVarlist)); // alias
registerCmd("vl", WRAP_METHOD(Console, cmdVMVarlist)); // alias
@@ -660,10 +662,33 @@ bool Console::cmdRegisters(int argc, const char **argv) {
return true;
}
+bool Console::parseResourceNumber36(const char *userParameter, uint16 &resourceNumber, uint32 &resourceTuple) {
+ int userParameterLen = strlen(userParameter);
+
+ if (userParameterLen != 10) {
+ debugPrintf("Audio36/Sync36 resource numbers must be specified as RRRNNVVCCS\n");
+ debugPrintf("where RRR is the resource number/map\n");
+ debugPrintf(" NN is the noun\n");
+ debugPrintf(" VV is the verb\n");
+ debugPrintf(" CC is the cond\n");
+ debugPrintf(" S is the seq\n");
+ return false;
+ }
+
+ // input: RRRNNVVCCS
+ resourceNumber = strtol(Common::String(userParameter, 3).c_str(), 0, 36);
+ uint16 noun = strtol(Common::String(userParameter + 3, 2).c_str(), 0, 36);
+ uint16 verb = strtol(Common::String(userParameter + 5, 2).c_str(), 0, 36);
+ uint16 cond = strtol(Common::String(userParameter + 7, 2).c_str(), 0, 36);
+ uint16 seq = strtol(Common::String(userParameter + 9, 1).c_str(), 0, 36);
+ resourceTuple = ((noun & 0xff) << 24) | ((verb & 0xff) << 16) | ((cond & 0xff) << 8) | (seq & 0xff);
+ return true;
+}
+
bool Console::cmdDiskDump(int argc, const char **argv) {
- int resNumFrom = 0;
- int resNumTo = 0;
- int resNumCur = 0;
+ bool resourceAll = false;
+ uint16 resourceNumber = 0;
+ uint32 resourceTuple = 0;
if (argc != 3) {
debugPrintf("Dumps the specified resource to disk as a patch file\n");
@@ -673,42 +698,90 @@ bool Console::cmdDiskDump(int argc, const char **argv) {
return true;
}
+ ResourceType resourceType = parseResourceType(argv[1]);
+ if (resourceType == kResourceTypeInvalid) {
+ debugPrintf("Resource type '%s' is not valid\n", argv[1]);
+ return true;
+ }
+
if (strcmp(argv[2], "*") == 0) {
- resNumFrom = 0;
- resNumTo = 65535;
+ resourceAll = true;
} else {
- resNumFrom = atoi(argv[2]);
- resNumTo = resNumFrom;
+ switch (resourceType) {
+ case kResourceTypeAudio36:
+ case kResourceTypeSync36:
+ if (!parseResourceNumber36(argv[2], resourceNumber, resourceTuple)) {
+ return true;
+ }
+ break;
+ default:
+ resourceNumber = atoi(argv[2]);
+ break;
+ }
}
- ResourceType res = parseResourceType(argv[1]);
-
- if (res == kResourceTypeInvalid)
+ if (resourceType == kResourceTypeInvalid) {
debugPrintf("Resource type '%s' is not valid\n", argv[1]);
- else {
- for (resNumCur = resNumFrom; resNumCur <= resNumTo; resNumCur++) {
- Resource *resource = _engine->getResMan()->findResource(ResourceId(res, resNumCur), 0);
- if (resource) {
- char outFileName[50];
- sprintf(outFileName, "%s.%03d", getResourceTypeName(res), resNumCur);
- Common::DumpFile *outFile = new Common::DumpFile();
- outFile->open(outFileName);
- resource->writeToStream(outFile);
- outFile->finalize();
- outFile->close();
- delete outFile;
- debugPrintf("Resource %s.%03d (located in %s) has been dumped to disk\n", argv[1], resNumCur, resource->getResourceLocation().c_str());
- } else {
- if (resNumFrom == resNumTo) {
- debugPrintf("Resource %s.%03d not found\n", argv[1], resNumCur);
- }
- }
+ return true;
+ }
+
+ if (resourceAll) {
+ // "*" used, dump everything of that type
+ Common::List<ResourceId> resources = _engine->getResMan()->listResources(resourceType, -1);
+ Common::sort(resources.begin(), resources.end());
+
+ Common::List<ResourceId>::iterator itr;
+ for (itr = resources.begin(); itr != resources.end(); ++itr) {
+ resourceNumber = itr->getNumber();
+ resourceTuple = itr->getTuple();
+ cmdDiskDumpWorker(resourceType, resourceNumber, resourceTuple);
}
+ } else {
+ // id was given, dump only this resource
+ cmdDiskDumpWorker(resourceType, resourceNumber, resourceTuple);
}
return true;
}
+void Console::cmdDiskDumpWorker(ResourceType resourceType, int resourceNumber, uint32 resourceTuple) {
+ const char *resourceTypeName = getResourceTypeName(resourceType);
+ ResourceId resourceId;
+ Resource *resource = NULL;
+ char outFileName[50];
+
+ switch (resourceType) {
+ case kResourceTypeAudio36:
+ case kResourceTypeSync36: {
+ resourceId = ResourceId(resourceType, resourceNumber, resourceTuple);
+ resource = _engine->getResMan()->findResource(resourceId, 0);
+ sprintf(outFileName, "%s", resourceId.toPatchNameBase36().c_str());
+ // patch filename is: [type:1 char] [map:3 chars] [noun:2 chars] [verb:2 chars] "." [cond: 2 chars] [seq:1 char]
+ // e.g. "@5EG0000.014"
+ break;
+ }
+ default:
+ resourceId = ResourceId(resourceType, resourceNumber);
+ resource = _engine->getResMan()->findResource(resourceId, 0);
+ sprintf(outFileName, "%s.%03d", resourceTypeName, resourceNumber);
+ // patch filename is: [resourcetype].[resourcenumber]
+ // e.g. "Script.0"
+ break;
+ }
+
+ if (resource) {
+ Common::DumpFile *outFile = new Common::DumpFile();
+ outFile->open(outFileName);
+ resource->writeToStream(outFile);
+ outFile->finalize();
+ outFile->close();
+ delete outFile;
+ debugPrintf("Resource %s (located in %s) has been dumped to disk\n", outFileName, resource->getResourceLocation().c_str());
+ } else {
+ debugPrintf("Resource %s not found\n", outFileName);
+ }
+}
+
bool Console::cmdHexDump(int argc, const char **argv) {
if (argc != 3) {
debugPrintf("Dumps the specified resource to standard output\n");
@@ -748,6 +821,77 @@ bool Console::cmdResourceId(int argc, const char **argv) {
return true;
}
+bool Console::cmdList(int argc, const char **argv) {
+ int selectedMapNumber = -1;
+ Common::List<ResourceId> resources;
+ Common::List<ResourceId>::iterator itr;
+ int displayCount = 0;
+ int currentMap = -1;
+
+ if (argc < 2) {
+ debugPrintf("Lists all the resources of a given type\n");
+ cmdResourceTypes(argc, argv);
+ return true;
+ }
+
+ ResourceType resourceType = parseResourceType(argv[1]);
+ if (resourceType == kResourceTypeInvalid) {
+ debugPrintf("Unknown resource type: '%s'\n", argv[1]);
+ return true;
+ }
+
+ switch (resourceType) {
+ case kResourceTypeAudio36:
+ case kResourceTypeSync36:
+ if (argc != 3) {
+ debugPrintf("Please specify map number (-1: all maps)\n");
+ return true;
+ }
+ selectedMapNumber = atoi(argv[2]);
+ resources = _engine->getResMan()->listResources(resourceType, selectedMapNumber);
+ Common::sort(resources.begin(), resources.end());
+
+ for (itr = resources.begin(); itr != resources.end(); ++itr) {
+ const uint16 map = itr->getNumber();
+ const uint32 resourceTuple = itr->getTuple();
+ const uint16 noun = (resourceTuple >> 24) & 0xff;
+ const uint16 verb = (resourceTuple >> 16) & 0xff;
+ const uint16 cond = (resourceTuple >> 8) & 0xff;
+ const uint16 seq = resourceTuple & 0xff;
+
+ if (currentMap != map) {
+ if (displayCount % 3)
+ debugPrintf("\n");
+ debugPrintf("Map %04x (%i):\n", map, map);
+ currentMap = map;
+ displayCount = 0;
+ }
+
+ if (displayCount % 3 == 0)
+ debugPrintf(" ");
+
+ debugPrintf("%02x %02x %02x %02x (%3i %3i %3i %3i) ", noun, verb, cond, seq, noun, verb, cond, seq);
+
+ if (++displayCount % 3 == 0)
+ debugPrintf("\n");
+ }
+ break;
+ default:
+ resources = _engine->getResMan()->listResources(resourceType);
+ Common::sort(resources.begin(), resources.end());
+
+ for (itr = resources.begin(); itr != resources.end(); ++itr) {
+ debugPrintf("%8i", itr->getNumber());
+ if (++displayCount % 10 == 0)
+ debugPrintf("\n");
+ }
+ break;
+ }
+
+ debugPrintf("\n");
+ return true;
+}
+
bool Console::cmdDissectScript(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Examines a script\n");
@@ -1124,52 +1268,6 @@ bool Console::cmdMapInstrument(int argc, const char **argv) {
return true;
}
-bool Console::cmdList(int argc, const char **argv) {
- if (argc < 2) {
- debugPrintf("Lists all the resources of a given type\n");
- cmdResourceTypes(argc, argv);
- return true;
- }
-
-
- ResourceType res = parseResourceType(argv[1]);
- if (res == kResourceTypeInvalid)
- debugPrintf("Unknown resource type: '%s'\n", argv[1]);
- else {
- int number = -1;
-
- if ((res == kResourceTypeAudio36) || (res == kResourceTypeSync36)) {
- if (argc != 3) {
- debugPrintf("Please specify map number (-1: all maps)\n");
- return true;
- }
- number = atoi(argv[2]);
- }
-
- Common::List<ResourceId> resources = _engine->getResMan()->listResources(res, number);
- Common::sort(resources.begin(), resources.end());
-
- int cnt = 0;
- Common::List<ResourceId>::iterator itr;
- for (itr = resources.begin(); itr != resources.end(); ++itr) {
- if (number == -1) {
- debugPrintf("%8i", itr->getNumber());
- if (++cnt % 10 == 0)
- debugPrintf("\n");
- } else if (number == (int)itr->getNumber()) {
- const uint32 tuple = itr->getTuple();
- debugPrintf("(%3i, %3i, %3i, %3i) ", (tuple >> 24) & 0xff, (tuple >> 16) & 0xff,
- (tuple >> 8) & 0xff, tuple & 0xff);
- if (++cnt % 4 == 0)
- debugPrintf("\n");
- }
- }
- debugPrintf("\n");
- }
-
- return true;
-}
-
bool Console::cmdSaveGame(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Saves the current game state to the hard disk\n");
@@ -2732,6 +2830,71 @@ bool Console::cmdScriptSteps(int argc, const char **argv) {
return true;
}
+bool Console::cmdScriptStrings(int argc, const char **argv) {
+ SegManager *segMan = _engine->_gamestate->_segMan;
+ int curScriptNr = -1;
+ SegmentId curSegmentNr;
+ Common::List<SegmentId> segmentNrList;
+
+ SegmentType curSegmentType = SEG_TYPE_INVALID;
+ SegmentObj *curSegmentObj = NULL;
+ Script *curScriptObj = NULL;
+
+ if (argc < 2) {
+ debugPrintf("Shows the strings inside a specified script.\n");
+ debugPrintf("Usage: %s <script number>\n", argv[0]);
+ debugPrintf("Example: %s 999\n", argv[0]);
+ debugPrintf("<script number> may be * to show strings inside all loaded scripts\n");
+ return true;
+ }
+
+ segmentNrList.clear();
+
+ if (strcmp(argv[1], "*") == 0) {
+ // get strings of all currently loaded scripts
+ for (curSegmentNr = 0; curSegmentNr < segMan->_heap.size(); curSegmentNr++) {
+ curSegmentObj = segMan->_heap[curSegmentNr];
+ if (curSegmentObj && curSegmentObj->getType() == SEG_TYPE_SCRIPT) {
+ segmentNrList.push_back(curSegmentNr);
+ }
+ }
+
+ } else {
+ curScriptNr = atoi(argv[1]);
+ curSegmentNr = segMan->getScriptSegment(curScriptNr);
+ if (!curSegmentNr) {
+ debugPrintf("Script %d is currently not loaded/available\n", curScriptNr);
+ return true;
+ }
+ segmentNrList.push_back(curSegmentNr);
+ }
+
+ Common::List<SegmentId>::iterator it;
+ const Common::List<SegmentId>::iterator end = segmentNrList.end();
+
+ for (it = segmentNrList.begin(); it != end; it++) {
+ curSegmentNr = *it;
+ // get object of this segment
+ curSegmentObj = segMan->getSegmentObj(curSegmentNr);
+ if (!curSegmentObj)
+ continue;
+
+ curSegmentType = curSegmentObj->getType();
+ if (curSegmentType != SEG_TYPE_SCRIPT) // safety check
+ continue;
+
+ curScriptObj = (Script *)curSegmentObj;
+ debugPrintf("=== SCRIPT %d from Segment %d ===\n", curScriptObj->getScriptNumber(), curSegmentNr);
+ debugN("=== SCRIPT %d from Segment %d ===\n", curScriptObj->getScriptNumber(), curSegmentNr);
+
+ // now print the string list
+ curScriptObj->debugPrintStrings(this);
+ debugPrintf("\n");
+ debugN("\n");
+ }
+ return true;
+}
+
bool Console::cmdBacktrace(int argc, const char **argv) {
debugPrintf("Call stack (current base: 0x%x):\n", _engine->_gamestate->executionStackBase);
Common::List<ExecStack>::const_iterator iter;
diff --git a/engines/sci/console.h b/engines/sci/console.h
index c8e99f78f7..00d95b511f 100644
--- a/engines/sci/console.h
+++ b/engines/sci/console.h
@@ -68,6 +68,7 @@ private:
bool cmdSaid(int argc, const char **argv);
// Resources
bool cmdDiskDump(int argc, const char **argv);
+ void cmdDiskDumpWorker(ResourceType resourceType, int resourceNumber, uint32 resourceTuple);
bool cmdHexDump(int argc, const char **argv);
bool cmdResourceId(int argc, const char **argv);
bool cmdResourceInfo(int argc, const char **argv);
@@ -146,6 +147,7 @@ private:
bool cmdBreakpointFunction(int argc, const char **argv);
// VM
bool cmdScriptSteps(int argc, const char **argv);
+ bool cmdScriptStrings(int argc, const char **argv);
bool cmdVMVarlist(int argc, const char **argv);
bool cmdVMVars(int argc, const char **argv);
bool cmdStack(int argc, const char **argv);
@@ -157,6 +159,7 @@ private:
bool cmdViewAccumulatorObject(int argc, const char **argv);
bool parseInteger(const char *argument, int &result);
+ bool parseResourceNumber36(const char *userParameter, uint16 &resourceNumber, uint32 &resourceTuple);
void printBasicVarInfo(reg_t variable);
diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp
index 85ff1c0062..9a41127f6d 100644
--- a/engines/sci/detection.cpp
+++ b/engines/sci/detection.cpp
@@ -791,22 +791,9 @@ void SciMetaEngine::removeSaveState(const char *target, int slot) const {
}
Common::Error SciEngine::loadGameState(int slot) {
- Common::String fileName = Common::String::format("%s.%03d", _targetName.c_str(), slot);
- Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
- Common::SeekableReadStream *in = saveFileMan->openForLoading(fileName);
-
- if (in) {
- // found a savegame file
- gamestate_restore(_gamestate, in);
- delete in;
- }
-
- if (_gamestate->r_acc != make_reg(0, 1)) {
- return Common::kNoError;
- } else {
- warning("Restoring gamestate '%s' failed", fileName.c_str());
- return Common::kUnknownError;
- }
+ _gamestate->_delayedRestoreGameId = slot;
+ _gamestate->_delayedRestoreGame = true;
+ return Common::kNoError;
}
Common::Error SciEngine::saveGameState(int slot, const Common::String &desc) {
@@ -834,16 +821,12 @@ Common::Error SciEngine::saveGameState(int slot, const Common::String &desc) {
return Common::kNoError;
}
-// Before enabling the load option in the ScummVM menu, the main game loop must
-// have run at least once. When the game loop runs, kGameIsRestarting is invoked,
-// thus the speed throttler is initialized. Hopefully fixes bug #3565505.
-
bool SciEngine::canLoadGameStateCurrently() {
- return !_gamestate->executionStackBase && (_gamestate->_throttleLastTime > 0 || _gamestate->_throttleTrigger);
+ return !_gamestate->executionStackBase;
}
bool SciEngine::canSaveGameStateCurrently() {
- return !_gamestate->executionStackBase && (_gamestate->_throttleLastTime > 0 || _gamestate->_throttleTrigger);
+ return !_gamestate->executionStackBase;
}
} // End of namespace Sci
diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h
index 32d1a58765..312069025b 100644
--- a/engines/sci/detection_tables.h
+++ b/engines/sci/detection_tables.h
@@ -1552,6 +1552,17 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ // King's Quest 6 - English DOS Playable CD "Sneak Peaks" Demo (first island fully playable)
+ // (supplied by KQ5 G5 in bug report #6824)
+ // Executable scanning reports "1.cfs.158 Not a release version", VERSION file reports "1.000.000"
+ // SCI interpreter version ???
+ {"kq6", "Demo/CD", {
+ {"resource.000", 0, "233394a5f33b475ae5975e7e9a420865", 8345598},
+ {"resource.map", 0, "eb9e177281b7cde188dc0d83194cd365", 8960},
+ {"resource.msg", 0, "3cf5de44de36191f109d425b8450efc8", 259510},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_CD | ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+
// King's Quest 6 - English DOS Floppy
// SCI interpreter version 1.001.054
{"kq6", "", {
diff --git a/engines/sci/engine/file.cpp b/engines/sci/engine/file.cpp
index 2ba7d15ac0..623caec856 100644
--- a/engines/sci/engine/file.cpp
+++ b/engines/sci/engine/file.cpp
@@ -252,6 +252,9 @@ void DirSeeker::addAsVirtualFiles(Common::String title, Common::String fileMask)
Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
Common::StringArray foundFiles = saveFileMan->listSavefiles(fileMask);
if (!foundFiles.empty()) {
+ // Sort all filenames alphabetically
+ Common::sort(foundFiles.begin(), foundFiles.end());
+
_files.push_back(title);
_virtualFiles.push_back("");
Common::StringArray::iterator it;
diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp
index 2b16bb3d99..bfb7bfcd08 100644
--- a/engines/sci/engine/kernel.cpp
+++ b/engines/sci/engine/kernel.cpp
@@ -432,57 +432,66 @@ static const SignatureDebugType signatureDebugTypeList[] = {
{ 0, NULL }
};
-static void kernelSignatureDebugType(const uint16 type) {
+static void kernelSignatureDebugType(Common::String &signatureDetailsStr, const uint16 type) {
bool firstPrint = true;
const SignatureDebugType *list = signatureDebugTypeList;
while (list->typeCheck) {
if (type & list->typeCheck) {
if (!firstPrint)
- debugN(", ");
- debugN("%s", list->text);
+// debugN(", ");
+ signatureDetailsStr += ", ";
+// debugN("%s", list->text);
+// signatureDetailsStr += signatureDetailsStr.format("%s", list->text);
+ signatureDetailsStr += list->text;
firstPrint = false;
}
list++;
}
}
-// Shows kernel call signature and current arguments for debugging purposes
-void Kernel::signatureDebug(const uint16 *sig, int argc, const reg_t *argv) {
+// Create string, that holds the details of a kernel call signature and current arguments
+// For debugging purposes
+void Kernel::signatureDebug(Common::String &signatureDetailsStr, const uint16 *sig, int argc, const reg_t *argv) {
int argnr = 0;
+
+ // add ERROR: to debug output
+ debugN("ERROR:");
+
while (*sig || argc) {
- debugN("parameter %d: ", argnr++);
+ // add leading spaces for additional parameters
+ signatureDetailsStr += signatureDetailsStr.format("parameter %d: ", argnr++);
if (argc) {
reg_t parameter = *argv;
- debugN("%04x:%04x (", PRINT_REG(parameter));
+ signatureDetailsStr += signatureDetailsStr.format("%04x:%04x (", PRINT_REG(parameter));
int regType = findRegType(parameter);
if (regType)
- kernelSignatureDebugType(regType);
+ kernelSignatureDebugType(signatureDetailsStr, regType);
else
- debugN("unknown type of %04x:%04x", PRINT_REG(parameter));
- debugN(")");
+ signatureDetailsStr += signatureDetailsStr.format("unknown type of %04x:%04x", PRINT_REG(parameter));
+ signatureDetailsStr += ")";
argv++;
argc--;
} else {
- debugN("not passed");
+ signatureDetailsStr += "not passed";
}
if (*sig) {
const uint16 signature = *sig;
if ((signature & SIG_MAYBE_ANY) == SIG_MAYBE_ANY) {
- debugN(", may be any");
+ signatureDetailsStr += ", may be any";
} else {
- debugN(", should be ");
- kernelSignatureDebugType(signature);
+ signatureDetailsStr += ", should be ";
+ kernelSignatureDebugType(signatureDetailsStr, signature);
}
if (signature & SIG_IS_OPTIONAL)
- debugN(" (optional)");
+ signatureDetailsStr += " (optional)";
if (signature & SIG_NEEDS_MORE)
- debugN(" (needs more)");
+ signatureDetailsStr += " (needs more)";
if (signature & SIG_MORE_MAY_FOLLOW)
- debugN(" (more may follow)");
+ signatureDetailsStr += " (more may follow)";
sig++;
}
- debugN("\n");
+ signatureDetailsStr += "\n";
}
}
diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h
index a65bcb7df5..57b4d9455b 100644
--- a/engines/sci/engine/kernel.h
+++ b/engines/sci/engine/kernel.h
@@ -186,7 +186,7 @@ public:
bool signatureMatch(const uint16 *sig, int argc, const reg_t *argv);
// Prints out debug information in case a signature check fails
- void signatureDebug(const uint16 *sig, int argc, const reg_t *argv);
+ void signatureDebug(Common::String &signatureDetails, const uint16 *sig, int argc, const reg_t *argv);
/**
* Determines the type of the object indicated by reg.
diff --git a/engines/sci/engine/kevent.cpp b/engines/sci/engine/kevent.cpp
index cb81da2279..8e16e0a07a 100644
--- a/engines/sci/engine/kevent.cpp
+++ b/engines/sci/engine/kevent.cpp
@@ -24,9 +24,10 @@
#include "sci/sci.h"
#include "sci/engine/features.h"
-#include "sci/engine/state.h"
-#include "sci/engine/selector.h"
#include "sci/engine/kernel.h"
+#include "sci/engine/savegame.h"
+#include "sci/engine/selector.h"
+#include "sci/engine/state.h"
#include "sci/console.h"
#include "sci/debug.h" // for g_debug_simulated_key
#include "sci/event.h"
@@ -71,9 +72,15 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
g_debug_simulated_key = 0;
return make_reg(0, 1);
}
-
+
curEvent = g_sci->getEventManager()->getSciEvent(mask);
+ if (s->_delayedRestoreGame) {
+ // delayed restore game from ScummVM menu got triggered
+ gamestate_delayedrestore(s);
+ return NULL_REG;
+ }
+
// For a real event we use its associated mouse position
mousePos = curEvent.mousePos;
#ifdef ENABLE_SCI32
diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp
index ee2249bd9d..8b790e6a58 100644
--- a/engines/sci/engine/kgraphics.cpp
+++ b/engines/sci/engine/kgraphics.cpp
@@ -32,6 +32,7 @@
#include "sci/event.h"
#include "sci/resource.h"
#include "sci/engine/features.h"
+#include "sci/engine/savegame.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
#include "sci/engine/kernel.h"
@@ -400,6 +401,12 @@ reg_t kWait(EngineState *s, int argc, reg_t *argv) {
s->wait(sleep_time);
+ if (s->_delayedRestoreGame) {
+ // delayed restore game from ScummVM menu got triggered
+ gamestate_delayedrestore(s);
+ return NULL_REG;
+ }
+
return s->r_acc;
}
@@ -955,8 +962,9 @@ reg_t kDrawControl(EngineState *s, int argc, reg_t *argv) {
reg_t textReference = readSelector(s->_segMan, controlObject, SELECTOR(text));
if (!textReference.isNull()) {
Common::String text = s->_segMan->getString(textReference);
- if ((text == "a:hq1_hero.sav") || (text == "a:glory1.sav") || (text == "a:glory2.sav") || (text == "a:glory3.sav")) {
+ if ((text == "a:hq1_hero.sav") || (text == "a:glory1.sav") || (text == "a:glory2.sav") || (text == "a:glory3.sav") || (text == "a:gloire3.sauv")) {
// Remove "a:" from hero quest / quest for glory export default filenames
+ // The french version of Quest For Glory 3 uses "gloire3.sauv". It seems a translator translated the filename.
text.deleteChar(0);
text.deleteChar(0);
s->_segMan->strcpy(textReference, text.c_str());
diff --git a/engines/sci/engine/kmovement.cpp b/engines/sci/engine/kmovement.cpp
index 51d49eea9f..9b83dbc52d 100644
--- a/engines/sci/engine/kmovement.cpp
+++ b/engines/sci/engine/kmovement.cpp
@@ -305,12 +305,23 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) {
for (uint i = 0; i < clientVarNum; ++i)
clientBackup[i] = clientObject->getVariable(i);
- if (mover_xAxis) {
- if (ABS(mover_x - client_x) < ABS(mover_dx))
- completed = true;
+ if ((getSciVersion() <= SCI_VERSION_1_EGA_ONLY)) {
+ if (mover_xAxis) {
+ if (ABS(mover_x - client_x) < ABS(mover_dx))
+ completed = true;
+ } else {
+ if (ABS(mover_y - client_y) < ABS(mover_dy))
+ completed = true;
+ }
} else {
- if (ABS(mover_y - client_y) < ABS(mover_dy))
- completed = true;
+ // SCI1EARLY+ code
+ if (mover_xAxis) {
+ if (ABS(mover_x - client_x) <= ABS(mover_dx))
+ completed = true;
+ } else {
+ if (ABS(mover_y - client_y) <= ABS(mover_dy))
+ completed = true;
+ }
}
if (completed) {
client_x = mover_x;
@@ -336,10 +347,10 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) {
bool collision = false;
reg_t cantBeHere = NULL_REG;
+ // adding this here for hoyle 3 to get happy. CantBeHere is a dummy in hoyle 3 and acc is != 0 so we would
+ // get a collision otherwise. Resetting the result was always done in SSCI
+ s->r_acc = NULL_REG;
if (SELECTOR(cantBeHere) != -1) {
- // adding this here for hoyle 3 to get happy. CantBeHere is a dummy in hoyle 3 and acc is != 0 so we would
- // get a collision otherwise
- s->r_acc = NULL_REG;
invokeSelector(s, client, SELECTOR(cantBeHere), argc, argv);
if (!s->r_acc.isNull())
collision = true;
diff --git a/engines/sci/engine/kparse.cpp b/engines/sci/engine/kparse.cpp
index aa89b963cc..f85f33e3e8 100644
--- a/engines/sci/engine/kparse.cpp
+++ b/engines/sci/engine/kparse.cpp
@@ -117,6 +117,8 @@ reg_t kParse(EngineState *s, int argc, reg_t *argv) {
}
#endif
+ voc->replacePronouns(words);
+
int syntax_fail = voc->parseGNF(words);
if (syntax_fail) {
@@ -130,6 +132,7 @@ reg_t kParse(EngineState *s, int argc, reg_t *argv) {
} else {
voc->parserIsValid = true;
+ voc->storePronounReference();
writeSelectorValue(segMan, event, SELECTOR(claimed), 0);
#ifdef DEBUG_PARSER
diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp
index 319469cb08..f925111fc9 100644
--- a/engines/sci/engine/kvideo.cpp
+++ b/engines/sci/engine/kvideo.cpp
@@ -123,6 +123,8 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) {
if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP)
skipVideo = true;
}
+ if (g_sci->getEngineState()->_delayedRestoreGame)
+ skipVideo = true;
g_system->delayMillis(10);
}
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index 61f8058e45..93b3a997cc 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -20,6 +20,7 @@
*
*/
+#include "common/savefile.h"
#include "common/stream.h"
#include "common/system.h"
#include "common/func.h"
@@ -40,6 +41,7 @@
#include "sci/graphics/helpers.h"
#include "sci/graphics/palette.h"
#include "sci/graphics/ports.h"
+#include "sci/graphics/screen.h"
#include "sci/parser/vocabulary.h"
#include "sci/sound/audio.h"
#include "sci/sound/music.h"
@@ -132,13 +134,6 @@ void SegManager::saveLoadWithSerializer(Common::Serializer &s) {
// Reset _scriptSegMap, to be restored below
_scriptSegMap.clear();
-
-#ifdef ENABLE_SCI32
- // Clear any planes/screen items currently showing so they
- // don't show up after the load.
- if (getSciVersion() >= SCI_VERSION_2)
- g_sci->_gfxFrameout->clear();
-#endif
}
s.skip(4, VER(14), VER(18)); // OBSOLETE: Used to be _exportsAreWide
@@ -727,9 +722,7 @@ void GfxPalette::saveLoadWithSerializer(Common::Serializer &s) {
}
void GfxPorts::saveLoadWithSerializer(Common::Serializer &s) {
- if (s.isLoading())
- reset(); // remove all script generated windows
-
+ // reset() is called directly way earlier in gamestate_restore()
if (s.getVersion() >= 27) {
uint windowCount = 0;
uint id = PORTS_FIRSTSCRIPTWINDOWID;
@@ -772,10 +765,17 @@ void GfxPorts::saveLoadWithSerializer(Common::Serializer &s) {
if (window->counterTillFree) {
_freeCounter++;
} else {
- if (window->wndStyle & SCI_WINDOWMGR_STYLE_TOPMOST)
- _windowList.push_front(window);
- else
- _windowList.push_back(window);
+ // we don't put the saved script windows into _windowList[], so that they aren't used
+ // by kernel functions. This is important and would cause issues otherwise.
+ // see Conquests of Camelot - bug #6744 - when saving on the map screen (room 103),
+ // restoring would result in a black window in place
+ // where the area name was displayed before
+ // In Sierra's SCI the behaviour is identical to us
+ // Sierra's SCI won't show those windows after restoring
+ // If this should cause issues in another game, we would have to add a flag to simply
+ // avoid any drawing operations for such windows
+ // We still have to restore script windows, because for example Conquests of Camelot
+ // will immediately delete windows, that were created before saving the game.
}
windowCount--;
@@ -865,6 +865,22 @@ bool gamestate_save(EngineState *s, Common::WriteStream *fh, const Common::Strin
extern void showScummVMDialog(const Common::String &message);
+void gamestate_delayedrestore(EngineState *s) {
+ Common::String fileName = g_sci->getSavegameName(s->_delayedRestoreGameId);
+ Common::SeekableReadStream *in = g_sci->getSaveFileManager()->openForLoading(fileName);
+
+ if (in) {
+ // found a savegame file
+ gamestate_restore(s, in);
+ delete in;
+ if (s->r_acc != make_reg(0, 1)) {
+ return;
+ }
+ }
+
+ error("Restoring gamestate '%s' failed", fileName.c_str());
+}
+
void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
SavegameMetadata meta;
@@ -901,6 +917,20 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
// We don't need the thumbnail here, so just read it and discard it
Graphics::skipThumbnail(*fh);
+ // reset ports is one of the first things we do, because that may free() some hunk memory
+ // and we don't want to do that after we read in the saved game hunk memory
+ if (g_sci->_gfxPorts)
+ g_sci->_gfxPorts->reset();
+ // clear screen
+ if (g_sci->_gfxScreen)
+ g_sci->_gfxScreen->clearForRestoreGame();
+#ifdef ENABLE_SCI32
+ // Also clear any SCI32 planes/screen items currently showing so they
+ // don't show up after the load.
+ if (getSciVersion() >= SCI_VERSION_2)
+ g_sci->_gfxFrameout->clear();
+#endif
+
s->reset(true);
s->saveLoadWithSerializer(ser); // FIXME: Error handling?
diff --git a/engines/sci/engine/savegame.h b/engines/sci/engine/savegame.h
index 7f482ed0a1..5512b90fa8 100644
--- a/engines/sci/engine/savegame.h
+++ b/engines/sci/engine/savegame.h
@@ -74,7 +74,6 @@ struct SavegameMetadata {
uint16 script0Size;
};
-
/**
* Saves a game state to the hard disk in a portable way.
* @param s The state to save
@@ -84,6 +83,9 @@ struct SavegameMetadata {
*/
bool gamestate_save(EngineState *s, Common::WriteStream *save, const Common::String &savename, const Common::String &version);
+// does a delayed saved game restore, used by ScummVM game menu - see detection.cpp / SciEngine::loadGameState()
+void gamestate_delayedrestore(EngineState *s);
+
/**
* Restores a game state from a directory.
* @param s An older state from the same game
diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp
index 2fe1aba975..69f52495e0 100644
--- a/engines/sci/engine/script.cpp
+++ b/engines/sci/engine/script.cpp
@@ -20,6 +20,7 @@
*
*/
+#include "sci/console.h"
#include "sci/sci.h"
#include "sci/resource.h"
#include "sci/util.h"
@@ -64,6 +65,7 @@ void Script::freeScript() {
_lockers = 1;
_markedAsDeleted = false;
_objects.clear();
+ _stringLookupList.clear();
}
void Script::load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptPatcher) {
@@ -136,9 +138,6 @@ void Script::load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptP
assert(_bufSize >= script->size);
memcpy(_buf, script->data, script->size);
- // Check scripts for matching signatures and patch those, if found
- scriptPatcher->processScript(_nr, _buf, script->size);
-
if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) {
Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, _nr), 0);
assert(heap != 0);
@@ -149,6 +148,9 @@ void Script::load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptP
memcpy(_heapStart, heap->data, heap->size);
}
+ // Check scripts (+ possibly SCI 1.1 heap) for matching signatures and patch those, if found
+ scriptPatcher->processScript(_nr, _buf, _bufSize);
+
if (getSciVersion() <= SCI_VERSION_1_LATE) {
_exportTable = (const uint16 *)findBlockSCI0(SCI_OBJ_EXPORTS);
if (_exportTable) {
@@ -204,6 +206,191 @@ void Script::load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptP
//_localsCount = (_bufSize - _localsOffset) >> 1;
}
}
+
+ // find all strings of this script
+ identifyStrings();
+}
+
+void Script::identifyStrings() {
+ stringLookupListEntry listEntry;
+ byte *scriptDataPtr = NULL;
+ byte *stringStartPtr = NULL;
+ byte *stringDataPtr = NULL;
+ int scriptDataLeft = 0;
+ int stringDataLeft = 0;
+ byte stringDataByte = 0;
+
+ _stringLookupList.clear();
+ //stringLookupListType _stringLookupList; // Table of string data, that is inside the currently loaded script
+
+ if (getSciVersion() < SCI_VERSION_1_1) {
+ scriptDataPtr = _buf;
+ scriptDataLeft = _bufSize;
+
+ // Go through all SCI_OBJ_STRINGS blocks
+ if (getSciVersion() == SCI_VERSION_0_EARLY) {
+ if (scriptDataLeft < 2)
+ error("Script::identifyStrings(): unexpected end of script");
+ scriptDataPtr += 2;
+ scriptDataLeft -= 2;
+ }
+
+ uint16 blockType;
+ uint16 blockSize;
+
+ do {
+ if (scriptDataLeft < 2)
+ error("Script::identifyStrings(): unexpected end of script");
+
+ blockType = READ_LE_UINT16(scriptDataPtr);
+ scriptDataPtr += 2;
+ scriptDataLeft -= 2;
+ if (blockType == 0) // end of blocks detected
+ break;
+
+ if (scriptDataLeft < 2)
+ error("Script::identifyStrings(): unexpected end of script");
+
+ blockSize = READ_LE_UINT16(scriptDataPtr);
+ if (blockSize < 4)
+ error("Script::identifyStrings(): invalid block size");
+ blockSize -= 4; // block size includes block-type UINT16 and block-size UINT16
+ scriptDataPtr += 2;
+ scriptDataLeft -= 2;
+
+ if (scriptDataLeft < blockSize)
+ error("Script::identifyStrings(): invalid block size");
+
+ if (blockType == SCI_OBJ_STRINGS) {
+ // string block detected, we now grab all NUL terminated strings out of this block
+ stringDataPtr = scriptDataPtr;
+ stringDataLeft = blockSize;
+
+ do {
+ if (stringDataLeft < 1) // no more bytes left
+ break;
+
+ stringStartPtr = stringDataPtr;
+ listEntry.ptrOffset = stringStartPtr - _buf; // Calculate offset inside script data
+ // now look for terminating [NUL]
+ do {
+ stringDataByte = *stringDataPtr;
+ stringDataPtr++;
+ stringDataLeft--;
+ if (!stringDataByte) // NUL found, exit this loop
+ break;
+ if (stringDataLeft < 1) // no more bytes left
+ error("Script::identifyStrings(): string without terminating NUL");
+ } while (1);
+
+ listEntry.stringSize = stringDataPtr - stringStartPtr;
+ _stringLookupList.push_back(listEntry);
+ } while (1);
+ }
+
+ scriptDataPtr += blockSize;
+ scriptDataLeft -= blockSize;
+ } while (1);
+
+ } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) {
+ // Strings in SCI1.1 come after the object instances
+ scriptDataPtr = _heapStart;
+ scriptDataLeft = _heapSize;
+
+ if (scriptDataLeft < 4)
+ error("Script::identifyStrings(): unexpected end of script");
+
+ uint16 endOfStringOffset = READ_SCI11ENDIAN_UINT16(scriptDataPtr);
+ uint16 objectStartOffset = READ_SCI11ENDIAN_UINT16(scriptDataPtr + 2) * 2 + 4;
+
+ if (scriptDataLeft < objectStartOffset)
+ error("Script::identifyStrings(): object start is beyond heap size");
+ if (scriptDataLeft < endOfStringOffset)
+ error("Script::identifyStrings(): end of string is beyond heap size");
+
+ byte *endOfStringPtr = scriptDataPtr + endOfStringOffset;
+
+ scriptDataPtr += objectStartOffset;
+ scriptDataLeft -= objectStartOffset;
+
+ uint16 blockType;
+ uint16 blockSize;
+
+ // skip all objects
+ do {
+ if (scriptDataLeft < 2)
+ error("Script::identifyStrings(): unexpected end of script");
+
+ blockType = READ_SCI11ENDIAN_UINT16(scriptDataPtr);
+ scriptDataPtr += 2;
+ scriptDataLeft -= 2;
+ if (blockType != SCRIPT_OBJECT_MAGIC_NUMBER)
+ break;
+
+ if (scriptDataLeft < 2)
+ error("Script::identifyStrings(): unexpected end of script");
+
+ blockSize = READ_SCI11ENDIAN_UINT16(scriptDataPtr) * 2;
+ scriptDataPtr += 2;
+ scriptDataLeft -= 2;
+ blockSize -= 4; // blocksize contains UINT16 type and UINT16 size
+ if (scriptDataLeft < blockSize)
+ error("Script::identifyStrings(): invalid block size");
+
+ scriptDataPtr += blockSize;
+ scriptDataLeft -= blockSize;
+ } while (1);
+
+ // now scriptDataPtr points to right at the start of the strings
+ if (scriptDataPtr > endOfStringPtr)
+ error("Script::identifyStrings(): string block / end-of-string block mismatch");
+
+ stringDataPtr = scriptDataPtr;
+ stringDataLeft = endOfStringPtr - scriptDataPtr; // Calculate byte count within string-block
+
+ do {
+ if (stringDataLeft < 1) // no more bytes left
+ break;
+
+ stringStartPtr = stringDataPtr;
+ listEntry.ptrOffset = stringStartPtr - _buf; // Calculate offset inside script data
+ // now look for terminating [NUL]
+ do {
+ stringDataByte = *stringDataPtr;
+ stringDataPtr++;
+ stringDataLeft--;
+ if (!stringDataByte) // NUL found, exit this loop
+ break;
+ if (stringDataLeft < 1) // no more bytes left
+ error("Script::identifyStrings(): string without terminating NUL");
+ } while (1);
+
+ listEntry.stringSize = stringDataPtr - stringStartPtr;
+ _stringLookupList.push_back(listEntry);
+ } while (1);
+
+ } else if (getSciVersion() == SCI_VERSION_3) {
+ warning("TODO: identifyStrings(): Implement SCI3 variant");
+ return;
+ }
+}
+
+void Script::debugPrintStrings(Console *con) {
+ stringLookupListType::iterator it;
+ const stringLookupListType::iterator end = _stringLookupList.end();
+ byte *stringPtr;
+
+ if (!_stringLookupList.size()) {
+ con->debugPrintf(" no strings\n");
+ debugN(" no strings\n");
+ return;
+ }
+
+ for (it = _stringLookupList.begin(); it != end; ++it) {
+ stringPtr = _buf + it->ptrOffset;
+ con->debugPrintf(" %04x: '%s' (size %d)\n", it->ptrOffset, stringPtr, it->stringSize);
+ debugN(" %04x: '%s' (size %d)\n", it->ptrOffset, stringPtr, it->stringSize);
+ }
}
const byte *Script::getSci3ObjectsPointer() {
diff --git a/engines/sci/engine/script.h b/engines/sci/engine/script.h
index 46d6ace917..fe774aeab5 100644
--- a/engines/sci/engine/script.h
+++ b/engines/sci/engine/script.h
@@ -49,6 +49,13 @@ enum ScriptObjectTypes {
typedef Common::HashMap<uint16, Object> ObjMap;
+struct stringLookupListEntry {
+ uint16 ptrOffset; // offset of the string
+ uint16 stringSize; // size of string, including terminating [NUL]
+};
+
+typedef Common::List<stringLookupListEntry> stringLookupListType;
+
class Script : public SegmentObj {
private:
int _nr; /**< Script number */
@@ -75,6 +82,8 @@ private:
ObjMap _objects; /**< Table for objects, contains property variables */
+ stringLookupListType _stringLookupList; // Table of string data, that is inside the currently loaded script
+
public:
int getLocalsOffset() const { return _localsOffset; }
uint16 getLocalsCount() const { return _localsCount; }
@@ -248,6 +257,11 @@ public:
*/
int getCodeBlockOffsetSci3() { return READ_SCI11ENDIAN_UINT32(_buf); }
+ /**
+ * Print string lookup table (list) to debug console
+ */
+ void debugPrintStrings(Console *con);
+
private:
/**
* Processes a relocation block within a SCI0-SCI2.1 script
@@ -294,6 +308,11 @@ private:
void initializeObjectsSci3(SegManager *segMan, SegmentId segmentId);
LocalVariables *allocLocalsSegment(SegManager *segMan);
+
+ /**
+ * Identifies strings within script data and set up lookup-table
+ */
+ void identifyStrings();
};
} // End of namespace Sci
diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp
index 45e0155950..4011273ca3 100644
--- a/engines/sci/engine/script_patches.cpp
+++ b/engines/sci/engine/script_patches.cpp
@@ -94,6 +94,8 @@ static const char *const selectorNameTable[] = {
"deskSarg", // Gabriel Knight
"localize", // Freddy Pharkas
"put", // Police Quest 1 VGA
+ "say", // Quest For Glory 1 VGA
+ "contains", // Quest For Glory 2
"solvePuzzle", // Quest For Glory 3
"timesShownID", // Space Quest 1 VGA
"startText", // King's Quest 6 CD / Laura Bow 2 CD for audio+text support
@@ -119,6 +121,8 @@ enum ScriptPatcherSelectors {
SELECTOR_deskSarg,
SELECTOR_localize,
SELECTOR_put,
+ SELECTOR_say,
+ SELECTOR_contains,
SELECTOR_solvePuzzle,
SELECTOR_timesShownID,
SELECTOR_startText,
@@ -1421,6 +1425,37 @@ static const SciScriptPatcherEntry larry2Signatures[] = {
// ===========================================================================
// Leisure Suit Larry 5
+// In Miami the player can call the green card telephone number and get
+// green card including limo at the same time in the English 1.000 PC release.
+// This results later in a broken game in case the player doesn't read
+// the second telephone number for the actual limousine service, because
+// in that case it's impossible for the player to get back to the airport.
+//
+// We disable the code, that is responsible to make the limo arrive.
+//
+// This bug was fixed in the European (dual language) versions of the game.
+//
+// Applies to at least: English PC floppy (1.000)
+// Responsible method: sPhone::changeState(40)
+static const uint16 larry5SignatureGreenCardLimoBug[] = {
+ 0x7a, // push2
+ SIG_MAGICDWORD,
+ 0x39, 0x07, // pushi 07
+ 0x39, 0x0c, // pushi 0Ch
+ 0x45, 0x0a, 0x04, // call export 10 of script 0
+ 0x78, // push1
+ 0x39, 0x26, // pushi 26h (limo arrived flag)
+ 0x45, 0x07, 0x02, // call export 7 of script 0 (sets flag)
+ SIG_END
+};
+
+static const uint16 larry5PatchGreenCardLimoBug[] = {
+ PATCH_ADDTOOFFSET(+8),
+ 0x34, PATCH_UINT16(0), // ldi 0000 (dummy)
+ 0x34, PATCH_UINT16(0), // ldi 0000 (dummy)
+ PATCH_END
+};
+
// In one of the conversations near the end (to be exact - room 380 and the text
// about using champagne on Reverse Biaz - only used when you actually did that
// in the game), the German text is too large, causing the textbox to get too large.
@@ -1444,6 +1479,7 @@ static const uint16 larry5PatchGermanEndingPattiTalker[] = {
// script, description, signature patch
static const SciScriptPatcherEntry larry5Signatures[] = {
+ { true, 280, "English-only: fix green card limo bug", 1, larry5SignatureGreenCardLimoBug, larry5PatchGreenCardLimoBug },
{ true, 380, "German-only: Enlarge Patti Textbox", 1, larry5SignatureGermanEndingPattiTalker, larry5PatchGermanEndingPattiTalker },
SCI_SIGNATUREENTRY_TERMINATOR
};
@@ -2116,7 +2152,7 @@ static const uint16 qfg1vgaSignatureCheetaurDescription[] = {
0x34, SIG_UINT16(0x01b8), // ldi 01b8
0x1a, // eq?
0x31, 0x16, // bnt 16
- 0x38, SIG_UINT16(0x0127), // pushi 0127
+ 0x38, SIG_SELECTOR16(say), // pushi 0127h (selector "say")
0x39, 0x06, // pushi 06
0x39, 0x03, // pushi 03
0x78, // push1
@@ -2140,6 +2176,7 @@ static const uint16 qfg1vgaPatchCheetaurDescription[] = {
// Local 5 of that room is a timer, that closes the door (object door11).
// Setting it to 1 during happyFace::changeState(0) stops door11::doit from
// calling goTo6::init, so the whole issue is stopped from happening.
+//
// Applies to at least: English floppy
// Responsible method: happyFace::changeState, door11::doit
// Fixes bug #6181
@@ -2166,20 +2203,164 @@ static const uint16 qfg1vgaPatchFunnyRoomFix[] = {
PATCH_END
};
+// The player is able to buy (and also steal) potions in the healer's hut
+// Strangely Sierra delays the actual buy/get potion code for 60 ticks
+// Why they did that is unknown. The code is triggered anyway only after
+// the relevant dialog boxes are closed.
+//
+// This delay causes problems in case the user quickly enters the inventory.
+// That's why we change the amount of ticks to 1, so that the remaining states
+// are executed right after the dialog boxes are closed.
+//
+// Applies to at least: English floppy
+// Responsible method: cueItScript::changeState
+// Fixes bug #6706
+static const uint16 qfg1vgaSignatureHealerHutNoDelay[] = {
+ 0x65, 0x14, // aTop 14 (state)
+ 0x36, // push
+ 0x3c, // dup
+ 0x35, 0x00, // ldi 00
+ 0x1a, // eq?
+ 0x31, 0x07, // bnt 07 [-> next state]
+ SIG_MAGICDWORD,
+ 0x35, 0x3c, // ldi 3c (60 ticks)
+ 0x65, 0x20, // aTop ticks
+ 0x32, // jmp [-> end of method]
+ SIG_END
+};
+
+static const uint16 qfg1vgaPatchHealerHutNoDelay[] = {
+ PATCH_ADDTOOFFSET(+9),
+ 0x35, 0x01, // ldi 01 (1 tick only, so that execution will resume as soon as dialog box is closed)
+ PATCH_END
+};
+
+// When following the white stag, you can actually enter the 2nd room from the mushroom/fairy location,
+// which results in ego entering from the top. When you then throw a dagger at the stag, one animation
+// frame will stay on screen, because of a script bug.
+//
+// Applies to at least: English floppy, Mac floppy
+// Responsible method: stagHurt::changeState
+// Fixes bug #6135
+static const uint16 qfg1vgaSignatureWhiteStagDagger[] = {
+ 0x87, 0x01, // lap param[1]
+ 0x65, 0x14, // aTop state
+ 0x36, // push
+ 0x3c, // dup
+ 0x35, 0x00, // ldi 0
+ 0x1a, // eq?
+ 0x31, 0x16, // bnt [next parameter check]
+ 0x76, // push0
+ 0x45, 0x02, 0x00, // callb export 2 from script 0, 0
+ SIG_MAGICDWORD,
+ 0x38, SIG_SELECTOR16(say), // pushi 0127h (selector "say")
+ 0x39, 0x05, // pushi 05
+ 0x39, 0x03, // pushi 03
+ 0x39, 0x51, // pushi 51h
+ 0x76, // push0
+ 0x76, // push0
+ 0x7c, // pushSelf
+ 0x81, 0x5b, // lag global[5Bh] -> qg1Messager
+ 0x4a, 0x0e, // send 0Eh -> qg1Messager::say(3, 51h, 0, 0, stagHurt)
+ 0x33, 0x12, // jmp -> [ret]
+ 0x3c, // dup
+ 0x35, 0x01, // ldi 1
+ 0x1a, // eq?
+ 0x31, 0x0c, // bnt [ret]
+ 0x38, // pushi...
+ SIG_ADDTOOFFSET(+11),
+ 0x3a, // toss
+ 0x48, // ret
+ SIG_END
+};
+
+static const uint16 qfg1vgaPatchWhiteStagDagger[] = {
+ PATCH_ADDTOOFFSET(+4),
+ 0x2f, 0x05, // bt [next check] (state != 0)
+ // state = 0 code
+ 0x35, 0x01, // ldi 1
+ 0x65, 0x1a, // aTop cycles
+ 0x48, // ret
+ 0x36, // push
+ 0x35, 0x01, // ldi 1
+ 0x1a, // eq?
+ 0x31, 0x16, // bnt [state = 2 code]
+ // state = 1 code
+ 0x76, // push0
+ 0x45, 0x02, 0x00, // callb export 2 from script 0, 0
+ 0x38, PATCH_SELECTOR16(say), // pushi 0127h (selector "say")
+ 0x39, 0x05, // pushi 05
+ 0x39, 0x03, // pushi 03
+ 0x39, 0x51, // pushi 51h
+ 0x76, // push0
+ 0x76, // push0
+ 0x7c, // pushSelf
+ 0x81, 0x5b, // lag global[5Bh] -> qg1Messager
+ 0x4a, 0x0e, // send 0Eh -> qg1Messager::say(3, 51h, 0, 0, stagHurt)
+ 0x48, // ret
+ // state = 2 code
+ PATCH_ADDTOOFFSET(+13),
+ 0x48, // ret (remove toss)
+ PATCH_END
+};
+
// script, description, signature patch
static const SciScriptPatcherEntry qfg1vgaSignatures[] = {
+ { true, 41, "moving to castle gate", 1, qfg1vgaSignatureMoveToCastleGate, qfg1vgaPatchMoveToCastleGate },
+ { true, 55, "healer's hut, no delay for buy/steal", 1, qfg1vgaSignatureHealerHutNoDelay, qfg1vgaPatchHealerHutNoDelay },
+ { true, 77, "white stag dagger throw animation glitch", 1, qfg1vgaSignatureWhiteStagDagger, qfg1vgaPatchWhiteStagDagger },
+ { true, 96, "funny room script bug fixed", 1, qfg1vgaSignatureFunnyRoomFix, qfg1vgaPatchFunnyRoomFix },
+ { true, 210, "cheetaur description fixed", 1, qfg1vgaSignatureCheetaurDescription, qfg1vgaPatchCheetaurDescription },
{ true, 215, "fight event issue", 1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents },
{ true, 216, "weapon master event issue", 1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents },
+ { true, 331, "moving to crusher", 1, qfg1vgaSignatureMoveToCrusher, qfg1vgaPatchMoveToCrusher },
{ true, 814, "window text temp space", 1, qfg1vgaSignatureTempSpace, qfg1vgaPatchTempSpace },
{ true, 814, "dialog header offset", 3, qfg1vgaSignatureDialogHeader, qfg1vgaPatchDialogHeader },
- { true, 331, "moving to crusher", 1, qfg1vgaSignatureMoveToCrusher, qfg1vgaPatchMoveToCrusher },
- { true, 41, "moving to castle gate", 1, qfg1vgaSignatureMoveToCastleGate, qfg1vgaPatchMoveToCastleGate },
- { true, 210, "cheetaur description fixed", 1, qfg1vgaSignatureCheetaurDescription, qfg1vgaPatchCheetaurDescription },
- { true, 96, "funny room script bug fixed", 1, qfg1vgaSignatureFunnyRoomFix, qfg1vgaPatchFunnyRoomFix },
SCI_SIGNATUREENTRY_TERMINATOR
};
// ===========================================================================
+
+// This is a very complicated bug.
+// When the player encounters an enemy in the desert while riding a saurus and later
+// tries to get back on it by entering "ride", the game will not give control back
+// to the player.
+//
+// This is caused by script mountSaurus getting triggered twice.
+// Once by entering the command "ride" and then a second time by a proximity check.
+//
+// Both are calling mountSaurus::init() in script 20, this one disables controls
+// then mountSaurus::changeState() from script 660 is triggered
+// mountSaurus::changeState(5) finally calls mountSaurus::dispose(), which is also in script 20
+// which finally re-enables controls
+//
+// A fix is difficult to implement. The code in script 20 is generic and used by multiple objects
+// That's why I have decided to change the responsible globals (66h and A1h) during mountSaurus::changeState(5)
+//
+// This fix could cause issues in case there is a cutscene, that contains ego getting on a saurus and
+// requires controls not getting re-enabled after getting back up on the saurus.
+//
+// Applies to at least: English PC Floppy, English Amiga Floppy
+// Responsible method: mountSaurus::changeState(), mountSaurus::init(), mountSaurus::dispose()
+// Fixes bug: #5156
+static const uint16 qfg2SignatureSaurusFreeze[] = {
+ 0x3c, // dup
+ 0x35, 0x05, // ldi 5
+ SIG_MAGICDWORD,
+ 0x1a, // eq?
+ 0x30, SIG_UINT16(0x004e), // bnt [ret]
+ 0x39, SIG_SELECTOR8(contains), // pushi [selector contains]
+ 0x78, // push1
+ SIG_END
+};
+
+static const uint16 qfg2PatchSaurusFreeze[] = {
+ 0x35, 0x01, // ldi 1
+ 0xa1, 0x66, // sag 66h
+ 0xa0, SIG_UINT16(0x00a1), // sag 00A1h
+ PATCH_END
+};
+
// Script 944 in QFG2 contains the FileSelector system class, used in the
// character import screen. This gets incorrectly called constantly, whenever
// the user clicks on a button in order to refresh the file list. This was
@@ -2211,9 +2392,58 @@ static const uint16 qfg2PatchImportDialog[] = {
PATCH_END
};
-// script, description, signature patch
+// Quest For Glory 2 character import doesn't properly set the character type
+// in versions 1.102 and below, which makes all importerted characters a fighter.
+//
+// Sierra released an official patch. However the fix is really easy to
+// implement on our side, so we also patch the flaw in here in case we find it.
+//
+// The version released on GOG is 1.102 without this patch applied, so us
+// patching it is quite useful.
+//
+// Applies to at least: English Floppy
+// Responsible method: importHero::changeState
+// Fixes bug: inside versions 1.102 and below
+static const uint16 qfg2SignatureImportCharType[] = {
+ 0x35, 0x04, // ldi 04
+ 0x90, SIG_UINT16(0x023b), // lagi global[23Bh]
+ 0x02, // add
+ 0x36, // push
+ 0x35, 0x04, // ldi 04
+ 0x08, // div
+ 0x36, // push
+ 0x35, 0x0d, // ldi 0D
+ 0xb0, SIG_UINT16(0x023b), // sagi global[023Bh]
+ 0x8b, 0x1f, // lsl local[1Fh]
+ 0x35, 0x05, // ldi 05
+ SIG_MAGICDWORD,
+ 0xb0, SIG_UINT16(0x0150), // sagi global[0150h]
+ 0x8b, 0x02, // lsl local[02h]
+ SIG_END
+};
+
+static const uint16 qfg2PatchImportCharType[] = {
+ 0x80, PATCH_UINT16(0x023f), // lag global[23Fh] <-- patched to save 2 bytes
+ 0x02, // add
+ 0x36, // push
+ 0x35, 0x04, // ldi 04
+ 0x08, // div
+ 0x36, // push
+ 0xa8, SIG_UINT16(0x0248), // ssg global[0248h] <-- patched to save 2 bytes
+ 0x8b, 0x1f, // lsl local[1Fh]
+ 0xa8, SIG_UINT16(0x0155), // ssg global[0155h] <-- patched to save 2 bytes
+ // new code, directly from the official sierra patch file
+ 0x83, 0x01, // lal local[01h]
+ 0xa1, 0xbb, // sag global[BBh]
+ 0xa1, 0x73, // sag global[73h]
+ PATCH_END
+};
+
+// script, description, signature patch
static const SciScriptPatcherEntry qfg2Signatures[] = {
- { true, 944, "import dialog continuous calls", 1, qfg2SignatureImportDialog, qfg2PatchImportDialog },
+ { true, 660, "getting back on saurus freeze fix", 1, qfg2SignatureSaurusFreeze, qfg2PatchSaurusFreeze },
+ { true, 805, "import character type fix", 1, qfg2SignatureImportCharType, qfg2PatchImportCharType },
+ { true, 944, "import dialog continuous calls", 1, qfg2SignatureImportDialog, qfg2PatchImportDialog },
SCI_SIGNATUREENTRY_TERMINATOR
};
@@ -2310,12 +2540,130 @@ static const uint16 qfg3PatchWooDialogAlt[] = {
PATCH_END
};
+// When exporting characters at the end of Quest for Glory 3, the underlying
+// code has issues with values, that are above 9999.
+// For further study: https://github.com/Blazingstix/QFGImporter/blob/master/QFGImporter/QFGImporter/QFG3.txt
+//
+// If a value is above 9999, parts or even the whole character file will get corrupted.
+//
+// We are fixing the code because of that. We are patching code, that is calculating the checksum
+// and add extra code to lower such values to 9999.
+//
+// Applies to at least: English, French, German, Italian, Spanish floppy
+// Responsible method: saveHero::changeState
+// Fixes bug #6807
+static const uint16 qfg3SignatureExportChar[] = {
+ 0x35, SIG_ADDTOOFFSET(+1), // ldi 00 / ldi 01 (2 loops, we patch both)
+ 0xa5, 0x00, // sat temp[0] [contains index to data]
+ 0x8d, 0x00, // lst temp[0]
+ SIG_MAGICDWORD,
+ 0x35, 0x2c, // ldi 2c
+ 0x22, // lt? [index above or equal 2Ch (44d)?
+ 0x31, 0x23, // bnt [exit loop]
+ // from this point it's actually useless code, maybe a sci compiler bug
+ 0x8d, 0x00, // lst temp[0]
+ 0x35, 0x01, // ldi 01
+ 0x02, // add
+ 0x9b, 0x00, // lsli local[0] ---------- load local[0 + ACC] onto stack
+ 0x8d, 0x00, // lst temp[0]
+ 0x35, 0x01, // ldi 01
+ 0x02, // add
+ 0xb3, 0x00, // sali local[0] ---------- save stack to local[0 + ACC]
+ // end of useless code
+ 0x8b, SIG_ADDTOOFFSET(+1), // lsl local[36h/37h] ---- load local[36h/37h] onto stack
+ 0x8d, 0x00, // lst temp[0]
+ 0x35, 0x01, // ldi 01
+ 0x02, // add
+ 0x93, 0x00, // lali local[0] ---------- load local[0 + ACC] into ACC
+ 0x02, // add -------------------- add ACC + stack and put into ACC
+ 0xa3, SIG_ADDTOOFFSET(+1), // sal local[36h/37h] ---- save ACC to local[36h/37h]
+ 0x8d, 0x00, // lst temp[0] ------------ temp[0] to stack
+ 0x35, 0x02, // ldi 02
+ 0x02, // add -------------------- add 2 to stack
+ 0xa5, 0x00, // sat temp[0] ------------ save ACC to temp[0]
+ 0x33, 0xd6, // jmp [loop]
+ SIG_END
+};
+
+static const uint16 qfg3PatchExportChar[] = {
+ PATCH_ADDTOOFFSET(+11),
+ 0x85, 0x00, // lat temp[0]
+ 0x9b, 0x01, // lsli local[0] + 1 ------ load local[ ACC + 1] onto stack
+ 0x3c, // dup
+ 0x34, PATCH_UINT16(0x2710), // ldi 2710h (10000d)
+ 0x2c, // ult? ------------------- is value smaller than 10000?
+ 0x2f, 0x0a, // bt [jump over]
+ 0x3a, // toss
+ 0x38, PATCH_UINT16(0x270f), // pushi 270fh (9999d)
+ 0x3c, // dup
+ 0x85, 0x00, // lat temp[0]
+ 0xba, PATCH_UINT16(0x0001), // ssli local[0] + 1 ------ save stack to local[ ACC + 1] (UINT16 to waste 1 byte)
+ // jump offset
+ 0x83, PATCH_GETORIGINALBYTE(+26), // lal local[37h/36h] ---- load local[37h/36h] into ACC
+ 0x02, // add -------------------- add local[37h/36h] + data value
+ PATCH_END
+};
+
+// Quest for Glory 3 doesn't properly import the character type of Quest for Glory 1 character files.
+// This issue was never addressed. It's caused by Sierra reading data directly from the local
+// area, which is only set by Quest For Glory 2 import data, instead of reading the properly set global variable.
+//
+// We fix it, by also directly setting the local variable.
+//
+// Applies to at least: English, French, German, Italian, Spanish floppy
+// Responsible method: importHero::changeState(4)
+static const uint16 qfg3SignatureImportQfG1Char[] = {
+ SIG_MAGICDWORD,
+ 0x82, SIG_UINT16(0x0238), // lal local[0x0238]
+ 0xa0, SIG_UINT16(0x016a), // sag global[0x016a]
+ 0xa1, 0x7d, // sag global[0x7d]
+ 0x35, 0x01, // ldi 01
+ 0x99, 0xfb, // lsgi global[0xfb]
+ SIG_END
+};
+
+static const uint16 qfg3PatchImportQfG1Char[] = {
+ PATCH_ADDTOOFFSET(+8),
+ 0xa3, 0x01, // sal 01 -> also set local[01]
+ 0x89, 0xfc, // lsg global[0xFD] -> save 2 bytes
+ PATCH_END
+};
+
+// The chief in his hut (room 640) is not drawn using the correct priority,
+// which results in a graphical glitch. This is a game bug and also happens
+// in Sierra's SCI. We adjust priority accordingly to fix it.
+//
+// Applies to at least: English, French, German, Italian, Spanish floppy
+// Responsible method: heap in script 640
+// Fixes bug #5173
+static const uint16 qfg3SignatureChiefPriority[] = {
+ SIG_MAGICDWORD,
+ SIG_UINT16(0x0002), // yStep 0x0002
+ SIG_UINT16(0x0281), // view 0x0281
+ SIG_UINT16(0x0000), // loop 0x0000
+ SIG_UINT16(0x0000), // cel 0x0000
+ SIG_UINT16(0x0000), // priority 0x0000
+ SIG_UINT16(0x0000), // underbits 0x0000
+ SIG_UINT16(0x1000), // signal 0x1000
+ SIG_END
+};
-// script, description, signature patch
+static const uint16 qfg3PatchChiefPriority[] = {
+ PATCH_ADDTOOFFSET(+8),
+ PATCH_UINT16(0x000A), // new priority 0x000A (10d)
+ PATCH_ADDTOOFFSET(+2),
+ PATCH_UINT16(0x1010), // signal 0x1010 (set fixed priority flag)
+ PATCH_END
+};
+
+// script, description, signature patch
static const SciScriptPatcherEntry qfg3Signatures[] = {
- { true, 944, "import dialog continuous calls", 1, qfg3SignatureImportDialog, qfg3PatchImportDialog },
- { true, 440, "dialog crash when asking about Woo", 1, qfg3SignatureWooDialog, qfg3PatchWooDialog },
- { true, 440, "dialog crash when asking about Woo", 1, qfg3SignatureWooDialogAlt, qfg3PatchWooDialogAlt },
+ { true, 944, "import dialog continuous calls", 1, qfg3SignatureImportDialog, qfg3PatchImportDialog },
+ { true, 440, "dialog crash when asking about Woo", 1, qfg3SignatureWooDialog, qfg3PatchWooDialog },
+ { true, 440, "dialog crash when asking about Woo", 1, qfg3SignatureWooDialogAlt, qfg3PatchWooDialogAlt },
+ { true, 52, "export character save bug", 2, qfg3SignatureExportChar, qfg3PatchExportChar },
+ { true, 54, "import character from QfG1 bug", 1, qfg3SignatureImportQfG1Char, qfg3PatchImportQfG1Char },
+ { true, 640, "chief in hut priority fix", 1, qfg3SignatureChiefPriority, qfg3PatchChiefPriority },
SCI_SIGNATUREENTRY_TERMINATOR
};
@@ -2375,6 +2723,47 @@ static const uint16 sq4FloppyPatchThrowStuffAtSequelPoliceBug[] = {
PATCH_END
};
+// Right at the start of Space Quest 4 CD, when walking up in the first room, ego will
+// immediately walk down just after entering the upper room.
+//
+// This is caused by the scripts setting ego's vertical coordinate to 189 (BDh), which is the
+// trigger in rooms to walk to the room below it. Sometimes this isn't triggered, because
+// the scripts also initiate a motion to vertical coordinate 188 (BCh). When you lower the game's speed,
+// this bug normally always triggers. And it triggers of course also in the original interpreter.
+//
+// It doesn't happen in PC floppy, because nsRect is not the same as in CD.
+//
+// We fix it by setting ego's vertical coordinate to 188 and we also initiate a motion to 187.
+//
+// Applies to at least: English PC CD
+// Responsible method: rm045::doit
+// Fixes bug: #5468
+static const uint16 sq4CdSignatureWalkInFromBelowRoom45[] = {
+ 0x76, // push0
+ SIG_MAGICDWORD,
+ 0x78, // push1
+ 0x38, SIG_UINT16(0x00bd), // pushi 00BDh
+ 0x38, SIG_ADDTOOFFSET(+2), // pushi [setMotion selector]
+ 0x39, 0x03, // pushi 3
+ 0x51, SIG_ADDTOOFFSET(+1), // class [MoveTo]
+ 0x36, // push
+ 0x78, // push1
+ 0x76, // push0
+ 0x81, 0x00, // lag global[0]
+ 0x4a, 0x04, // send 04 -> get ego::x
+ 0x36, // push
+ 0x38, SIG_UINT16(0x00bc), // pushi 00BCh
+ SIG_END
+};
+
+static const uint16 sq4CdPatchWalkInFromBelowRoom45[] = {
+ PATCH_ADDTOOFFSET(+2),
+ 0x38, PATCH_UINT16(0x00bc), // pushi 00BCh
+ PATCH_ADDTOOFFSET(+15),
+ 0x38, PATCH_UINT16(0x00bb), // pushi 00BBh
+ PATCH_END
+};
+
// The scripts in SQ4CD support simultaneous playing of speech and subtitles,
// but this was not available as an option. The following two patches enable
// this functionality in the game's GUI options dialog.
@@ -2469,8 +2858,9 @@ static const uint16 sq4CdPatchTextOptions[] = {
static const SciScriptPatcherEntry sq4Signatures[] = {
{ true, 298, "Floppy: endless flight", 1, sq4FloppySignatureEndlessFlight, sq4FloppyPatchEndlessFlight },
{ true, 700, "Floppy: throw stuff at sequel police bug", 1, sq4FloppySignatureThrowStuffAtSequelPoliceBug, sq4FloppyPatchThrowStuffAtSequelPoliceBug },
- { true, 818, "CD: Speech and subtitles option", 1, sq4CdSignatureTextOptions, sq4CdPatchTextOptions },
+ { true, 45, "CD: walk in from below for room 45 fix", 1, sq4CdSignatureWalkInFromBelowRoom45, sq4CdPatchWalkInFromBelowRoom45 },
{ true, 0, "CD: Babble icon speech and subtitles fix", 1, sq4CdSignatureBabbleIcon, sq4CdPatchBabbleIcon },
+ { true, 818, "CD: Speech and subtitles option", 1, sq4CdSignatureTextOptions, sq4CdPatchTextOptions },
{ true, 818, "CD: Speech and subtitles option button", 1, sq4CdSignatureTextOptionsButton, sq4CdPatchTextOptionsButton },
SCI_SIGNATUREENTRY_TERMINATOR
};
@@ -2501,6 +2891,29 @@ static const uint16 sq1vgaPatchUlenceFlatsTimepodGfxGlitch[] = {
PATCH_END
};
+// In Ulence Flats, there is a space ship, that you will use at some point.
+// Near that space ship are 2 force field generators.
+// When you look at the top of those generators, the game will crash.
+// This happens also in Sierra SCI. It's caused by a jump, that goes out of bounds.
+// We currently do not know if this was caused by a compiler glitch or if it was a developer error.
+// Anyway we patch this glitchy code, so that the game won't crash anymore.
+//
+// Applies to at least: English Floppy
+// Responsible method: radar1::doVerb
+// Fixes bug: #6816
+static const uint16 sq1vgaSignatureUlenceFlatsGeneratorGlitch[] = {
+ SIG_MAGICDWORD, 0x1a, // eq?
+ 0x30, SIG_UINT16(0xcdf4), // bnt absolute 0xf000
+ SIG_END
+};
+
+static const uint16 sq1vgaPatchUlenceFlatsGeneratorGlitch[] = {
+ PATCH_ADDTOOFFSET(+1),
+ 0x32, PATCH_UINT16(0x0000), // jmp 0x0000 (waste bytes)
+ PATCH_END
+};
+
+// No documentation for this patch (TODO)
static const uint16 sq1vgaSignatureEgoShowsCard[] = {
SIG_MAGICDWORD,
0x38, SIG_SELECTOR16(timesShownID), // push "timesShownID"
@@ -2625,6 +3038,7 @@ static const uint16 sq1vgaPatchSpiderDroidTiming[] = {
// script, description, signature patch
static const SciScriptPatcherEntry sq1vgaSignatures[] = {
{ true, 45, "Ulence Flats: timepod graphic glitch", 1, sq1vgaSignatureUlenceFlatsTimepodGfxGlitch, sq1vgaPatchUlenceFlatsTimepodGfxGlitch },
+ { true, 45, "Ulence Flats: force field generator glitch", 1, sq1vgaSignatureUlenceFlatsGeneratorGlitch, sq1vgaPatchUlenceFlatsGeneratorGlitch },
{ true, 58, "Sarien armory droid zapping ego first time", 1, sq1vgaSignatureEgoShowsCard, sq1vgaPatchEgoShowsCard },
{ true, 704, "spider droid timing issue", 1, sq1vgaSignatureSpiderDroidTiming, sq1vgaPatchSpiderDroidTiming },
SCI_SIGNATUREENTRY_TERMINATOR
@@ -2700,6 +3114,7 @@ ScriptPatcher::ScriptPatcher() {
_selectorIdTable[selectorNr] = -1;
_runtimeTable = NULL;
+ _isMacSci11 = false;
}
ScriptPatcher::~ScriptPatcher() {
@@ -2708,7 +3123,7 @@ ScriptPatcher::~ScriptPatcher() {
}
// will actually patch previously found signature area
-void ScriptPatcher::applyPatch(const SciScriptPatcherEntry *patchEntry, byte *scriptData, const uint32 scriptSize, int32 signatureOffset, const bool isMacSci11) {
+void ScriptPatcher::applyPatch(const SciScriptPatcherEntry *patchEntry, byte *scriptData, const uint32 scriptSize, int32 signatureOffset) {
const uint16 *patchData = patchEntry->patchData;
byte orgData[PATCH_VALUELIMIT];
int32 offset = signatureOffset;
@@ -2772,7 +3187,7 @@ void ScriptPatcher::applyPatch(const SciScriptPatcherEntry *patchEntry, byte *sc
default:
byte1 = 0; byte2 = 0;
}
- if (!isMacSci11) {
+ if (!_isMacSci11) {
scriptData[offset++] = byte1;
scriptData[offset++] = byte2;
} else {
@@ -2799,8 +3214,94 @@ void ScriptPatcher::applyPatch(const SciScriptPatcherEntry *patchEntry, byte *sc
}
}
+bool ScriptPatcher::verifySignature(uint32 byteOffset, const uint16 *signatureData, const char *signatureDescription, const byte *scriptData, const uint32 scriptSize) {
+ uint16 sigSelector = 0;
+
+ uint16 sigWord = *signatureData;
+ while (sigWord != SIG_END) {
+ uint16 sigCommand = sigWord & SIG_COMMANDMASK;
+ uint16 sigValue = sigWord & SIG_VALUEMASK;
+ switch (sigCommand) {
+ case SIG_CODE_ADDTOOFFSET: {
+ // add value to offset
+ byteOffset += sigValue;
+ break;
+ }
+ case SIG_CODE_UINT16:
+ case SIG_CODE_SELECTOR16: {
+ if ((byteOffset + 1) < scriptSize) {
+ byte byte1;
+ byte byte2;
+
+ switch (sigCommand) {
+ case SIG_CODE_UINT16: {
+ byte1 = sigValue & SIG_BYTEMASK;
+ signatureData++; sigWord = *signatureData;
+ if (sigWord & SIG_COMMANDMASK)
+ error("Script-Patcher: signature inconsistent\nFaulty signature: '%s'", signatureDescription);
+ byte2 = sigWord & SIG_BYTEMASK;
+ break;
+ }
+ case SIG_CODE_SELECTOR16: {
+ sigSelector = _selectorIdTable[sigValue];
+ byte1 = sigSelector & 0xFF;
+ byte2 = sigSelector >> 8;
+ break;
+ }
+ default:
+ byte1 = 0; byte2 = 0;
+ }
+ if (!_isMacSci11) {
+ if ((scriptData[byteOffset] != byte1) || (scriptData[byteOffset + 1] != byte2))
+ sigWord = SIG_MISMATCH;
+ } else {
+ // SCI1.1+ on macintosh had uint16s in script in BE-order
+ if ((scriptData[byteOffset] != byte2) || (scriptData[byteOffset + 1] != byte1))
+ sigWord = SIG_MISMATCH;
+ }
+ byteOffset += 2;
+ } else {
+ sigWord = SIG_MISMATCH;
+ }
+ break;
+ }
+ case SIG_CODE_SELECTOR8: {
+ if (byteOffset < scriptSize) {
+ sigSelector = _selectorIdTable[sigValue];
+ if (sigSelector & 0xFF00)
+ error("Script-Patcher: 8 bit selector required, game uses 16 bit selector\nFaulty signature: '%s'", signatureDescription);
+ if (scriptData[byteOffset] != (sigSelector & 0xFF))
+ sigWord = SIG_MISMATCH;
+ byteOffset++;
+ } else {
+ sigWord = SIG_MISMATCH; // out of bounds
+ }
+ break;
+ }
+ case SIG_CODE_BYTE:
+ if (byteOffset < scriptSize) {
+ if (scriptData[byteOffset] != sigWord)
+ sigWord = SIG_MISMATCH;
+ byteOffset++;
+ } else {
+ sigWord = SIG_MISMATCH; // out of bounds
+ }
+ }
+
+ if (sigWord == SIG_MISMATCH)
+ break;
+
+ signatureData++;
+ sigWord = *signatureData;
+ }
+
+ if (sigWord == SIG_END) // signature fully matched?
+ return true;
+ return false;
+}
+
// will return -1 if no match was found, otherwise an offset to the start of the signature match
-int32 ScriptPatcher::findSignature(const SciScriptPatcherEntry *patchEntry, SciScriptPatcherRuntimeEntry *runtimeEntry, const byte *scriptData, const uint32 scriptSize, const bool isMacSci11) {
+int32 ScriptPatcher::findSignature(const SciScriptPatcherEntry *patchEntry, SciScriptPatcherRuntimeEntry *runtimeEntry, const byte *scriptData, const uint32 scriptSize) {
if (scriptSize < 4) // we need to find a DWORD, so less than 4 bytes is not okay
return -1;
@@ -2812,89 +3313,8 @@ int32 ScriptPatcher::findSignature(const SciScriptPatcherEntry *patchEntry, SciS
if (magicDWord == READ_UINT32(scriptData + DWordOffset)) {
// magic DWORD found, check if actual signature matches
uint32 offset = DWordOffset + runtimeEntry->magicOffset;
- uint32 byteOffset = offset;
- const uint16 *signatureData = patchEntry->signatureData;
- uint16 sigSelector = 0;
-
- uint16 sigWord = *signatureData;
- while (sigWord != SIG_END) {
- uint16 sigCommand = sigWord & SIG_COMMANDMASK;
- uint16 sigValue = sigWord & SIG_VALUEMASK;
- switch (sigCommand) {
- case SIG_CODE_ADDTOOFFSET: {
- // add value to offset
- byteOffset += sigValue;
- break;
- }
- case SIG_CODE_UINT16:
- case SIG_CODE_SELECTOR16: {
- if ((byteOffset + 1) < scriptSize) {
- byte byte1;
- byte byte2;
-
- switch (sigCommand) {
- case SIG_CODE_UINT16: {
- byte1 = sigValue & SIG_BYTEMASK;
- signatureData++; sigWord = *signatureData;
- if (sigWord & SIG_COMMANDMASK)
- error("Script-Patcher: signature inconsistent\nFaulty patch: '%s'", patchEntry->description);
- byte2 = sigWord & SIG_BYTEMASK;
- break;
- }
- case SIG_CODE_SELECTOR16: {
- sigSelector = _selectorIdTable[sigValue];
- byte1 = sigSelector & 0xFF;
- byte2 = sigSelector >> 8;
- break;
- }
- default:
- byte1 = 0; byte2 = 0;
- }
- if (!isMacSci11) {
- if ((scriptData[byteOffset] != byte1) || (scriptData[byteOffset + 1] != byte2))
- sigWord = SIG_MISMATCH;
- } else {
- // SCI1.1+ on macintosh had uint16s in script in BE-order
- if ((scriptData[byteOffset] != byte2) || (scriptData[byteOffset + 1] != byte1))
- sigWord = SIG_MISMATCH;
- }
- byteOffset += 2;
- } else {
- sigWord = SIG_MISMATCH;
- }
- break;
- }
- case SIG_CODE_SELECTOR8: {
- if (byteOffset < scriptSize) {
- sigSelector = _selectorIdTable[sigValue];
- if (sigSelector & 0xFF00)
- error("Script-Patcher: 8 bit selector required, game uses 16 bit selector\nFaulty patch: '%s'", patchEntry->description);
- if (scriptData[byteOffset] != (sigSelector & 0xFF))
- sigWord = SIG_MISMATCH;
- byteOffset++;
- } else {
- sigWord = SIG_MISMATCH; // out of bounds
- }
- break;
- }
- case SIG_CODE_BYTE:
- if (byteOffset < scriptSize) {
- if (scriptData[byteOffset] != sigWord)
- sigWord = SIG_MISMATCH;
- byteOffset++;
- } else {
- sigWord = SIG_MISMATCH; // out of bounds
- }
- }
-
- if (sigWord == SIG_MISMATCH)
- break;
-
- signatureData++;
- sigWord = *signatureData;
- }
- if (sigWord == SIG_END) // signature fully matched?
+ if (verifySignature(offset, patchEntry->signatureData, patchEntry->description, scriptData, scriptSize))
return offset;
}
DWordOffset++;
@@ -2905,7 +3325,7 @@ int32 ScriptPatcher::findSignature(const SciScriptPatcherEntry *patchEntry, SciS
// This method calculates the magic DWORD for each entry in the signature table
// and it also initializes the selector table for selectors used in the signatures/patches of the current game
-void ScriptPatcher::initSignature(const SciScriptPatcherEntry *patchTable, bool isMacSci11) {
+void ScriptPatcher::initSignature(const SciScriptPatcherEntry *patchTable) {
const SciScriptPatcherEntry *curEntry = patchTable;
SciScriptPatcherRuntimeEntry *curRuntimeEntry;
Selector curSelector = -1;
@@ -2973,7 +3393,7 @@ void ScriptPatcher::initSignature(const SciScriptPatcherEntry *patchTable, bool
curData++; curWord = *curData;
if (curWord & SIG_COMMANDMASK)
error("Script-Patcher: signature entry inconsistent\nFaulty patch: '%s'", curEntry->description);
- if (!isMacSci11) {
+ if (!_isMacSci11) {
byte1 = curValue;
byte2 = curWord & SIG_BYTEMASK;
} else {
@@ -2988,7 +3408,7 @@ void ScriptPatcher::initSignature(const SciScriptPatcherEntry *patchTable, bool
curSelector = g_sci->getKernel()->findSelector(selectorNameTable[curValue]);
_selectorIdTable[curValue] = curSelector;
}
- if (!isMacSci11) {
+ if (!_isMacSci11) {
byte1 = curSelector & 0x00FF;
byte2 = curSelector >> 8;
} else {
@@ -3150,7 +3570,7 @@ void ScriptPatcher::processScript(uint16 scriptNr, byte *scriptData, const uint3
}
if (signatureTable) {
- bool isMacSci11 = (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_1_1);
+ _isMacSci11 = (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_1_1);
if (!_runtimeTable) {
// Abort, in case selectors are not yet initialized (happens for games w/o selector-dictionary)
@@ -3158,7 +3578,7 @@ void ScriptPatcher::processScript(uint16 scriptNr, byte *scriptData, const uint3
return;
// signature table needs to get initialized (Magic DWORD set, selector table set)
- initSignature(signatureTable, isMacSci11);
+ initSignature(signatureTable);
// Do additional game-specific initialization
switch (gameId) {
@@ -3193,11 +3613,11 @@ void ScriptPatcher::processScript(uint16 scriptNr, byte *scriptData, const uint3
int32 foundOffset = 0;
int16 applyCount = curEntry->applyCount;
do {
- foundOffset = findSignature(curEntry, curRuntimeEntry, scriptData, scriptSize, isMacSci11);
+ foundOffset = findSignature(curEntry, curRuntimeEntry, scriptData, scriptSize);
if (foundOffset != -1) {
// found, so apply the patch
debugC(kDebugLevelScriptPatcher, "Script-Patcher: '%s' on script %d offset %d", curEntry->description, scriptNr, foundOffset);
- applyPatch(curEntry, scriptData, scriptSize, foundOffset, isMacSci11);
+ applyPatch(curEntry, scriptData, scriptSize, foundOffset);
}
applyCount--;
} while ((foundOffset != -1) && (applyCount));
diff --git a/engines/sci/engine/script_patches.h b/engines/sci/engine/script_patches.h
index 7023ef327e..d15fce321b 100644
--- a/engines/sci/engine/script_patches.h
+++ b/engines/sci/engine/script_patches.h
@@ -74,7 +74,6 @@ struct SciScriptPatcherEntry {
const uint16 *patchData;
};
-//#define SCI_SIGNATUREENTRY_TERMINATOR { false, 0, NULL, 0, 0, 0, NULL, NULL }
#define SCI_SIGNATUREENTRY_TERMINATOR { false, 0, NULL, 0, NULL, NULL }
struct SciScriptPatcherRuntimeEntry {
@@ -92,15 +91,17 @@ public:
~ScriptPatcher();
void processScript(uint16 scriptNr, byte *scriptData, const uint32 scriptSize);
+ bool verifySignature(uint32 byteOffset, const uint16 *signatureData, const char *signatureDescription, const byte *scriptData, const uint32 scriptSize);
private:
- void initSignature(const SciScriptPatcherEntry *patchTable, bool isMacSci11);
+ void initSignature(const SciScriptPatcherEntry *patchTable);
void enablePatch(const SciScriptPatcherEntry *patchTable, const char *searchDescription);
- int32 findSignature(const SciScriptPatcherEntry *patchEntry, SciScriptPatcherRuntimeEntry *runtimeEntry, const byte *scriptData, const uint32 scriptSize, bool isMacSci11);
- void applyPatch(const SciScriptPatcherEntry *patchEntry, byte *scriptData, const uint32 scriptSize, int32 signatureOffset, bool isMacSci11);
+ int32 findSignature(const SciScriptPatcherEntry *patchEntry, SciScriptPatcherRuntimeEntry *runtimeEntry, const byte *scriptData, const uint32 scriptSize);
+ void applyPatch(const SciScriptPatcherEntry *patchEntry, byte *scriptData, const uint32 scriptSize, int32 signatureOffset);
Selector *_selectorIdTable;
SciScriptPatcherRuntimeEntry *_runtimeTable;
+ bool _isMacSci11;
};
} // End of namespace Sci
diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp
index 527c8f0ae0..417d98e2e2 100644
--- a/engines/sci/engine/state.cpp
+++ b/engines/sci/engine/state.cpp
@@ -92,6 +92,10 @@ void EngineState::reset(bool isRestoring) {
abortScriptProcessing = kAbortNone;
}
+ // reset delayed restore game functionality
+ _delayedRestoreGame = false;
+ _delayedRestoreGameId = 0;
+
executionStackBase = 0;
_executionStackPosChanged = false;
stack_base = 0;
diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h
index ecc8cb7dfe..e7499e66c9 100644
--- a/engines/sci/engine/state.h
+++ b/engines/sci/engine/state.h
@@ -131,6 +131,10 @@ public:
VirtualIndexFile *_virtualIndexFile;
#endif
+ // see detection.cpp / SciEngine::loadGameState()
+ bool _delayedRestoreGame; // boolean, that triggers delayed restore (triggered by ScummVM menu)
+ int _delayedRestoreGameId; // the saved game id, that it supposed to get restored (triggered by ScummVM menu)
+
uint _chosenQfGImportItem; // Remembers the item selected in QfG import rooms
bool _cursorWorkaroundActive; // Refer to GfxCursor::setPosition()
diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp
index 06858540ec..6f02c96de8 100644
--- a/engines/sci/engine/vm.cpp
+++ b/engines/sci/engine/vm.cpp
@@ -358,12 +358,15 @@ static void callKernelFunc(EngineState *s, int kernelCallNr, int argc) {
SciTrackOriginReply originReply;
SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, kernelCall.workarounds, &originReply);
switch (solution.type) {
- case WORKAROUND_NONE:
- kernel->signatureDebug(kernelCall.signature, argc, argv);
- error("[VM] k%s[%x]: signature mismatch via method %s::%s (room %d, script %d, localCall 0x%x)",
+ case WORKAROUND_NONE: {
+ Common::String signatureDetailsStr;
+ kernel->signatureDebug(signatureDetailsStr, kernelCall.signature, argc, argv);
+ error("\n%s[VM] k%s[%x]: signature mismatch in method %s::%s (room %d, script %d, localCall 0x%x)",
+ signatureDetailsStr.c_str(),
kernelCall.name, kernelCallNr, originReply.objectName.c_str(), originReply.methodName.c_str(),
s->currentRoomNumber(), originReply.scriptNr, originReply.localCallOffset);
break;
+ }
case WORKAROUND_IGNORE: // don't do kernel call, leave acc alone
return;
case WORKAROUND_STILLCALL: // call kernel anyway
@@ -408,15 +411,18 @@ static void callKernelFunc(EngineState *s, int kernelCallNr, int argc) {
SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, kernelSubCall.workarounds, &originReply);
switch (solution.type) {
case WORKAROUND_NONE: {
- kernel->signatureDebug(kernelSubCall.signature, argc, argv);
+ Common::String signatureDetailsStr;
+ kernel->signatureDebug(signatureDetailsStr, kernelSubCall.signature, argc, argv);
int callNameLen = strlen(kernelCall.name);
if (strncmp(kernelCall.name, kernelSubCall.name, callNameLen) == 0) {
const char *subCallName = kernelSubCall.name + callNameLen;
- error("[VM] k%s(%s): signature mismatch via method %s::%s (room %d, script %d, localCall %x)",
+ error("\n%s[VM] k%s(%s): signature mismatch in method %s::%s (room %d, script %d, localCall %x)",
+ signatureDetailsStr.c_str(),
kernelCall.name, subCallName, originReply.objectName.c_str(), originReply.methodName.c_str(),
s->currentRoomNumber(), originReply.scriptNr, originReply.localCallOffset);
}
- error("[VM] k%s: signature mismatch via method %s::%s (room %d, script %d, localCall %x)",
+ error("\n%s[VM] k%s: signature mismatch in method %s::%s (room %d, script %d, localCall %x)",
+ signatureDetailsStr.c_str(),
kernelSubCall.name, originReply.objectName.c_str(), originReply.methodName.c_str(),
s->currentRoomNumber(), originReply.scriptNr, originReply.localCallOffset);
break;
diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp
index c5730b5345..ea3443e551 100644
--- a/engines/sci/engine/workarounds.cpp
+++ b/engines/sci/engine/workarounds.cpp
@@ -24,426 +24,716 @@
#include "sci/engine/object.h"
#include "sci/engine/state.h"
#include "sci/engine/vm.h"
+#include "sci/engine/script_patches.h"
#include "sci/engine/workarounds.h"
-#define SCI_WORKAROUNDENTRY_TERMINATOR { (SciGameId)0, -1, -1, 0, NULL, NULL, -1, 0, { WORKAROUND_NONE, 0 } }
+#define SCI_WORKAROUNDENTRY_TERMINATOR { (SciGameId)0, -1, -1, 0, NULL, NULL, NULL, 0, { WORKAROUND_NONE, 0 } }
namespace Sci {
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+// Attention:
+// To identify local-call-subroutines code signatures are used.
+// Offsets change a lot between different versions of games, especially between different language releases.
+// That's why it isn't good to hardcode the offsets of those subroutines.
+//
+// Those signatures are just like the script patcher signatures (for further study: engine\script_patches.cpp)
+// However you may NOT use command SIG_SELECTOR8 nor SIG_SELECTOR16 atm. Proper support for those may be added later.
+
+// Game: Conquests of Camelot
+// Calling method: endingCartoon2::changeState
+// Subroutine offset: English 0x020d (script 92)
+// Applies to at least: English PC floppy
+static const uint16 sig_arithmetic_camelot_1[] = {
+ 0x83, 0x32, // lal local[32h]
+ 0x30, SIG_UINT16(0x001d), // bnt [...]
+ 0x7a, // push2
+ 0x39, 0x08, // pushi 08
+ 0x36, // push
+ 0x43, // callk Graph
+ SIG_END
+};
+
+// Game: Eco Quest 2
+// Calling method: Rain::points
+// Subroutine offset: English 0x0cc6, French/Spanish 0x0ce0 (script 0)
+// Applies to at least: English/French/Spanish PC floppy
+static const uint16 sig_arithmetic_ecoq2_1[] = {
+ 0x8f, 0x01, // lsp param[1]
+ 0x35, 0x10, // ldi 10h
+ 0x08, // div
+ 0x99, 0x6e, // lsgi global[6Eh]
+ 0x38, SIG_UINT16(0x8000), // pushi 8000h
+ 0x8f, 0x01, // lsp param[1]
+ 0x35, 0x10, // ldi 10h
+ 0x0a, // mod
+ 0x0c, // shr
+ 0x14, // or
+ 0x36, // push
+ SIG_END
+};
+
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry arithmeticWorkarounds[] = {
- { GID_CAMELOT, 92, 92, 0, "endingCartoon2", "changeState", 0x20d, 0, { WORKAROUND_FAKE, 0 } }, // op_lai: during the ending, sub gets called with no parameters, uses parameter 1 which is theGrail in this case - bug #5237
- { GID_ECOQUEST2, 100, 0, 0, "Rain", "points", 0xcc6, 0, { WORKAROUND_FAKE, 0 } }, // op_or: when giving the papers to the customs officer, gets called against a pointer instead of a number - bug #4939
- { GID_ECOQUEST2, 100, 0, 0, "Rain", "points", 0xce0, 0, { WORKAROUND_FAKE, 0 } }, // Same as above, for the Spanish version - bug #5750
- { GID_FANMADE, 516, 983, 0, "Wander", "setTarget", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_mul: The Legend of the Lost Jewel Demo (fan made): called with object as second parameter when attacked by insects - bug #5124
- { GID_GK1, 800,64992, 0, "Fwd", "doit", -1, 0, { WORKAROUND_FAKE, 1 } }, // op_gt: when Mosely finds Gabriel and Grace near the end of the game, compares the Grooper object with 7
- { GID_HOYLE4, 700, -1, 1, "Code", "doit", -1, 0, { WORKAROUND_FAKE, 1 } }, // op_add: while bidding in Bridge, an object ("Bid") is added to an object in another segment ("hand3")
- { GID_ICEMAN, 199, 977, 0, "Grooper", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_add: While dancing with the girl
- { GID_MOTHERGOOSE256, -1, 999, 0, "Event", "new", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_and: constantly during the game (SCI1 version)
- { GID_MOTHERGOOSE256, -1, 4, 0, "rm004", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_or: when going north and reaching the castle (rooms 4 and 37) - bug #5101
- { GID_MOTHERGOOSEHIRES,90, 90, 0, "newGameButton", "select", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_ge: MUMG Deluxe, when selecting "New Game" in the main menu. It tries to compare an integer with a list. Needs to return false for the game to continue.
- { GID_PHANTASMAGORIA, 902, 0, 0, "", "export 7", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_shr: when starting a chapter in Phantasmagoria
- { GID_QFG1VGA, 301, 928, 0, "Blink", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_div: when entering the inn, gets called with 1 parameter, but 2nd parameter is used for div which happens to be an object
- { GID_QFG2, 200, 200, 0, "astro", "messages", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_lsi: when getting asked for your name by the astrologer - bug #5152
- { GID_QFG3, 780, 999, 0, "", "export 6", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_add: trying to talk to yourself at the top of the giant tree - bug #6692
- { GID_QFG4, 710,64941, 0, "RandCycle", "doit", -1, 0, { WORKAROUND_FAKE, 1 } }, // op_gt: when the tentacle appears in the third room of the caves
- SCI_WORKAROUNDENTRY_TERMINATOR
-};
-
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+ { GID_CAMELOT, 92, 92, 0, "endingCartoon2", "changeState", sig_arithmetic_camelot_1, 0, { WORKAROUND_FAKE, 0 } }, // op_lai: during the ending, sub gets called with no parameters, uses parameter 1 which is theGrail in this case - bug #5237
+ { GID_ECOQUEST2, 100, 0, 0, "Rain", "points", sig_arithmetic_ecoq2_1, 0, { WORKAROUND_FAKE, 0 } }, // op_or: when giving the papers to the customs officer, gets called against a pointer instead of a number - bug #4939, Spanish version - bug #5750
+ { GID_FANMADE, 516, 983, 0, "Wander", "setTarget", NULL, 0, { WORKAROUND_FAKE, 0 } }, // op_mul: The Legend of the Lost Jewel Demo (fan made): called with object as second parameter when attacked by insects - bug #5124
+ { GID_GK1, 800,64992, 0, "Fwd", "doit", NULL, 0, { WORKAROUND_FAKE, 1 } }, // op_gt: when Mosely finds Gabriel and Grace near the end of the game, compares the Grooper object with 7
+ { GID_HOYLE4, 700, -1, 1, "Code", "doit", NULL, 0, { WORKAROUND_FAKE, 1 } }, // op_add: while bidding in Bridge, an object ("Bid") is added to an object in another segment ("hand3")
+ { GID_ICEMAN, 199, 977, 0, "Grooper", "doit", NULL, 0, { WORKAROUND_FAKE, 0 } }, // op_add: While dancing with the girl
+ { GID_MOTHERGOOSE256, -1, 999, 0, "Event", "new", NULL, 0, { WORKAROUND_FAKE, 0 } }, // op_and: constantly during the game (SCI1 version)
+ { GID_MOTHERGOOSE256, -1, 4, 0, "rm004", "doit", NULL, 0, { WORKAROUND_FAKE, 0 } }, // op_or: when going north and reaching the castle (rooms 4 and 37) - bug #5101
+ { GID_MOTHERGOOSEHIRES,90, 90, 0, "newGameButton", "select", NULL, 0, { WORKAROUND_FAKE, 0 } }, // op_ge: MUMG Deluxe, when selecting "New Game" in the main menu. It tries to compare an integer with a list. Needs to return false for the game to continue.
+ { GID_PHANTASMAGORIA, 902, 0, 0, "", "export 7", NULL, 0, { WORKAROUND_FAKE, 0 } }, // op_shr: when starting a chapter in Phantasmagoria
+ { GID_QFG1VGA, 301, 928, 0, "Blink", "init", NULL, 0, { WORKAROUND_FAKE, 0 } }, // op_div: when entering the inn, gets called with 1 parameter, but 2nd parameter is used for div which happens to be an object
+ { GID_QFG2, 200, 200, 0, "astro", "messages", NULL, 0, { WORKAROUND_FAKE, 0 } }, // op_lsi: when getting asked for your name by the astrologer - bug #5152
+ { GID_QFG3, 780, 999, 0, "", "export 6", NULL, 0, { WORKAROUND_FAKE, 0 } }, // op_add: trying to talk to yourself at the top of the giant tree - bug #6692
+ { GID_QFG4, 710,64941, 0, "RandCycle", "doit", NULL, 0, { WORKAROUND_FAKE, 1 } }, // op_gt: when the tentacle appears in the third room of the caves
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// Game: Fan-Made "Ocean Battle"
+// Calling method: RoomScript::doit
+// Subroutine offset: 0x1f17
+// Applies to at least: Ocean Battle
+static const uint16 sig_uninitread_fanmade_1[] = {
+ 0x3f, 0x04, // link 04
+ 0x88, SIG_UINT16(0x023b), // lsg global[23Bh]
+ SIG_END
+};
+
+// Game: Hoyle 1
+// Calling method: export 0
+// Subroutine offset: 0x037c (script 16)
+// Applies to at least: English PC floppy
+static const uint16 sig_uninitread_hoyle1_1[] = {
+ 0x3f, 0x05, // link 05
+ 0x78, // push1
+ 0x76, // push0
+ 0x40, // call [...]
+ SIG_END
+};
+
+// Game: Hoyle 4
+// Calling method: export 2
+// Subroutine offset: 0x1d4d (script 300)
+// Applies to at least: English PC floppy
+static const uint16 sig_uninitread_hoyle4_1[] = {
+ 0x3f, 0x01, // link 01
+ 0x39, 0x40, // pushi 40h
+ 0x78, // push1
+ SIG_END
+};
+
+// Game: Jones in the fast lane
+// Calling method: weekendText::draw
+// Subroutine offset: 0x03d3 (script 232)
+// Applies to at least: English PC CD
+static const uint16 sig_uninitread_jones_1[] = {
+ 0x3f, 0x02, // link 02
+ 0x8d, 0x00, // lst temp[0]
+ 0x35, 0x01, // ldi 01
+ 0x22, // lt?
+ SIG_END
+};
+
+// Game: Conquests of the Longbow
+// Calling method: letter::handleEvent
+// Subroutine offset: English PC/Amiga 0x00a8 (script 213)
+// Applies to at least: English PC floppy, English Amiga floppy
+static const uint16 sig_uninitread_longbow_1[] = {
+ 0x3f, 0x02, // link 02
+ 0x35, 0x00, // ldi 00
+ 0xa5, 0x00, // sat temp[0]
+ 0x8d, 0x00, // lst temp[0]
+ SIG_END
+};
+
+// Game: Quest for Glory 1 / Hero's Quest 1
+// Calling method: Encounter::init
+// Subroutine offset: English Hero's Quest 0x0bd0, English Quest for Glory 1 0x0be4 (script 210)
+// Applies to at least: English PC floppy (Hero's Quest, Quest For Glory 1), Japanese PC-9801 floppy
+static const uint16 sig_uninitread_qfg1_1[] = {
+ 0x3f, 0x02, // link 02
+ 0x87, 0x00, // lap param[0]
+ 0x30, SIG_UINT16(0x000c), // bnt [...]
+ 0x87, 0x01, // lap param[1]
+ 0x30, SIG_UINT16(0x0007), // bnt [...]
+ 0x87, 0x01, // lap param[1]
+ 0xa5, 0x01, // sat temp[1]
+ SIG_END
+};
+
+// Game: Quest for Glory 1 VGA
+// Calling method: Encounter::init
+// Subroutine offset: English w/o patch 0x0cee, w/ patch 0x0ce7 (script 210)
+// Applies to at least: English PC floppy
+static const uint16 sig_uninitread_qfg1vga_1[] = {
+ 0x3f, 0x02, // link 02
+ 0x87, 0x00, // lap param[0]
+ 0x31, 0x0b, // bnt [...]
+ 0x87, 0x01, // lap param[1]
+ 0x31, 0x07, // bnt [...]
+ 0x87, 0x01, // lap param[1]
+ 0xa5, 0x01, // sat temp[1]
+ // following jump is different for patched and unpatched game
+ SIG_END
+};
+
+// Game: Quest for Glory 2
+// Calling method: abdulS::changeState, jabbarS::changeState
+// Subroutine offset: English 0x2d22 (script 260)
+// Applies to at least: English PC floppy
+static const uint16 sig_uninitread_qfg2_1[] = {
+ 0x3f, 0x03, // link 03
+ 0x39, 0x3b, // pushi 3Bh
+ 0x76, // push0
+ 0x81, 0x00, // lag global[0]
+ 0x4a, 0x04, // send 04
+ SIG_END
+};
+
+// Game: Quest for Glory 3
+// Calling method: rm140::init
+// Subroutine offset: English 0x1008 (script 140)
+// Applies to at least: English, French, German, Italian, Spanish PC floppy
+static const uint16 sig_uninitread_qfg3_1[] = {
+ 0x3f, 0x01, // link 01
+ 0x89, 0x7d, // lsg global[7Dh]
+ 0x35, 0x03, // ldi 03
+ SIG_END
+};
+
+// Game: Quest for Glory 3
+// Calling method: computersMove::changeState
+// Subroutine offset: English/etc. 0x0f53 (script 490)
+// Applies to at least: English, French, German, Italian, Spanish PC floppy
+static const uint16 sig_uninitread_qfg3_2[] = {
+ 0x3f, 0x1d, // link 1Dh
+ 0x35, 0x01, // ldi 01
+ 0xa5, 0x18, // sat temp[18h]
+ 0x35, 0xce, // ldi CEh
+ 0xa5, 0x19, // sat temp[19h]
+ 0x35, 0x00, // ldi 00
+ 0xa5, 0x00, // sat temp[0]
+ SIG_END
+};
+
+// Game: Space Quest 1
+// Calling method: firePulsar::changeState
+// Subroutine offset: English 0x018a (script 703)
+// Applies to at least: English PC floppy, English Amiga floppy
+static const uint16 sig_uninitread_sq1_1[] = {
+ 0x3f, 0x01, // link 01
+ 0x38, SIG_ADDTOOFFSET(+2), // pushi 0242 (selector egoStatus)
+ 0x76, // push0
+ 0x72, SIG_ADDTOOFFSET(+2), // lofsa DeltaurRegion
+ 0x4a, 0x04, // send 04
+ SIG_END
+};
+
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
- { GID_CAMELOT, 40, 40, 0, "Rm40", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // when looking at the ground at the pool of Siloam - bug #6401
- { GID_CASTLEBRAIN, 280, 280, 0, "programmer", "dispatchEvent", -1, 0, { WORKAROUND_FAKE, 0xf } }, // pressing 'q' on the computer screen in the robot room, and closing the help dialog that pops up (bug #5143). Moves the cursor to the view with the ID returned (in this case, the robot hand)
- { GID_CNICK_KQ, -1, 0, 1, "Character", "say", -1, -1, { WORKAROUND_FAKE, 0 } }, // checkers/backgammon, like in hoyle 3 - temps 504 and 505 - bug #6255
- { GID_CNICK_KQ, -1, 700, 0, "gcWindow", "open", -1, -1, { WORKAROUND_FAKE, 0 } }, // when entering the control menu, like in hoyle 3
- { GID_CNICK_KQ, 300, 303, 0, "theDoubleCube", "<noname520>", -1, 5, { WORKAROUND_FAKE, 0 } }, // while playing backgammon with doubling enabled - bug #6426 (same as the theDoubleCube::make workaround for Hoyle 3)
- { GID_CNICK_KQ, 300, 303, 0, "theDoubleCube", "<noname519>", -1, 9, { WORKAROUND_FAKE, 0 } }, // when accepting a double, while playing backgammon with doubling enabled (same as the theDoubleCube::accept workaround for Hoyle 3)
- { GID_CNICK_LAURABOW, -1, 0, 1, "Character", "say", -1, -1, { WORKAROUND_FAKE, 0 } }, // Yatch, like in hoyle 3 - temps 504 and 505 - bug #6424
- { GID_CNICK_LAURABOW, -1, 700, 0, NULL, "open", -1, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu - bug #6423 (same as the gcWindow workaround for Hoyle 3)
- { GID_CNICK_LAURABOW,100, 100, 0, NULL, "<noname144>", -1, 1, { WORKAROUND_FAKE, 0 } }, // while playing domino - bug #6429 (same as the dominoHand2 workaround for Hoyle 3)
- { GID_CNICK_LAURABOW,100, 110, 0, NULL, "doit", -1, -1, { WORKAROUND_FAKE, 0 } }, // when changing the "Dominoes per hand" setting - bug #6430
- { GID_CNICK_LONGBOW, 0, 0, 0, "RH Budget", "init", -1, 1, { WORKAROUND_FAKE, 0 } }, // when starting the game
- { GID_ECOQUEST, -1, -1, 0, NULL, "doVerb", -1, 0, { WORKAROUND_FAKE, 0 } }, // almost clicking anywhere triggers this in almost all rooms
- { GID_FANMADE, 516, 979, 0, "", "export 0", -1, 20, { WORKAROUND_FAKE, 0 } }, // Happens in Grotesteing after the logos
- { GID_FANMADE, 528, 990, 0, "GDialog", "doit", -1, 4, { WORKAROUND_FAKE, 0 } }, // Happens in Cascade Quest when closing the glossary - bug #5116
- { GID_FANMADE, 488, 1, 0, "RoomScript", "doit", 0x1f17, 1, { WORKAROUND_FAKE, 0 } }, // Happens in Ocean Battle while playing - bug #5335
- { GID_FREDDYPHARKAS, -1, 24, 0, "gcWin", "open", -1, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu
- { GID_FREDDYPHARKAS, -1, 31, 0, "quitWin", "open", -1, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu
- { GID_FREDDYPHARKAS, 540, 540, 0, "WaverCode", "init", -1, -1, { WORKAROUND_FAKE, 0 } }, // Gun pratice mini-game - bug #5232
- { GID_GK1, -1, 64950, -1, "Feature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // sometimes when walk-clicking
- { GID_GK2, -1, 11, 0, "", "export 10", -1, 3, { WORKAROUND_FAKE, 0 } }, // called when the game starts
- { GID_GK2, -1, 11, 0, "", "export 10", -1, 4, { WORKAROUND_FAKE, 0 } }, // called during the game
- { GID_HOYLE1, 4, 104, 0, "GinRummyCardList", "calcRuns", -1, 4, { WORKAROUND_FAKE, 0 } }, // Gin Rummy / right when the game starts
- { GID_HOYLE1, 5, 204, 0, "tableau", "checkRuns", -1, 2, { WORKAROUND_FAKE, 0 } }, // Cribbage / during the game
- { GID_HOYLE1, 3, 16, 0, "", "export 0", 0x37c, 3, { WORKAROUND_FAKE, 0 } }, // Hearts / during the game - bug #5299
- { GID_HOYLE1, -1, 997, 0, "MenuBar", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // When changing game speed settings - bug #5512
- { GID_HOYLE3, -1, 0, 1, "Character", "say", -1, -1, { WORKAROUND_FAKE, 0 } }, // when starting checkers or dominoes, first time a character says something - temps 504 and 505
- { GID_HOYLE3, -1, 700, 0, "gcWindow", "open", -1, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu
- { GID_HOYLE3, 100, 100, 0, "dominoHand2", "cue", -1, 1, { WORKAROUND_FAKE, 0 } }, // while playing domino - bug #5042
- { GID_HOYLE3, 100, 110, 0, "OKButton", "doit", -1, -1, { WORKAROUND_FAKE, 0 } }, // when changing the "Dominoes per hand" setting - bug #6430
- { GID_HOYLE3, 300, 303, 0, "theDoubleCube", "make", -1, 5, { WORKAROUND_FAKE, 0 } }, // while playing backgammon with doubling enabled
- { GID_HOYLE3, 300, 303, 0, "theDoubleCube", "accept", -1, 9, { WORKAROUND_FAKE, 0 } }, // when accepting a double, while playing backgammon with doubling enabled
- { GID_HOYLE4, -1, 0, 0, NULL, "open", -1, -1, { WORKAROUND_FAKE, 0 } }, // when selecting "Control" from the menu (temp vars 0-3) - bug #5132
- { GID_HOYLE4, 910, 18, 0, NULL, "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // during tutorial - bug #5213
- { GID_HOYLE4, 910, 910, 0, NULL, "setup", -1, 3, { WORKAROUND_FAKE, 0 } }, // when selecting "Tutorial" from the main menu - bug #5132
- { GID_HOYLE4, 700, 700, 1, "BridgeHand", "calcQTS", -1, 3, { WORKAROUND_FAKE, 0 } }, // when placing a bid in bridge (always)
- { GID_HOYLE4, 700, 710, 1, "BridgeStrategyPlay", "checkSplitTops", -1, 10, { WORKAROUND_FAKE, 0 } }, // while playing bridge, objects LeadReturn_Trump, SecondSeat_Trump, ThirdSeat_Trump and others - bug #5794
- { GID_HOYLE4, 700, -1, 1, "BridgeDefense", "think", -1, -1, { WORKAROUND_FAKE, 0 } }, // sometimes while playing bridge, temp var 3, 17 and others, objects LeadReturn_Trump, ThirdSeat_Trump and others
- { GID_HOYLE4, 700, 730, 1, "BridgeDefense", "beatTheirBest", -1, 3, { WORKAROUND_FAKE, 0 } }, // rarely while playing bridge
- { GID_HOYLE4, 700, -1, 1, "Code", "doit", -1, -1, { WORKAROUND_FAKE, 0 } }, // when placing a bid in bridge (always), temp var 11, 24, 27, 46, 75, objects compete_tree, compwe_tree, other1_tree, b1 - bugs #5663 and #5794
- { GID_HOYLE4, 700, 921, 0, "Print", "addEdit", -1, 0, { WORKAROUND_FAKE, 118 } }, // when saving the game (may also occur in other situations) - bug #6601, bug #6614
- { GID_HOYLE4, 700, 921, 0, "Print", "addEdit", -1, 1, { WORKAROUND_FAKE, 1 } }, // see above, Text-control saves its coordinates to temp[0] and temp[1], Edit-control adjusts to those uninitialized temps, who by accident were left over from the Text-control
- { GID_HOYLE4, 300, 300, 0, "", "export 2", 0x1d4d, 0, { WORKAROUND_FAKE, 0 } }, // after passing around cards in hearts
- { GID_HOYLE4, 400, 400, 1, "GinHand", "calcRuns", -1, 4, { WORKAROUND_FAKE, 0 } }, // sometimes while playing Gin Rummy (e.g. when knocking and placing a card) - bug #5665
- { GID_HOYLE4, 500, 17, 1, "Character", "say", -1, 504, { WORKAROUND_FAKE, 0 } }, // sometimes while playing Cribbage (e.g. when the opponent says "Last Card") - bug #5662
- { GID_HOYLE4, -1, 937, 0, "IconBar", "dispatchEvent", -1, 408, { WORKAROUND_FAKE, 0 } }, // pressing ENTER on scoreboard while mouse is not on OK button, may not happen all the time - bug #6603
- { GID_ISLANDBRAIN, 100, 937, 0, "IconBar", "dispatchEvent", -1, 58, { WORKAROUND_FAKE, 0 } }, // when using ENTER at the startup menu - bug #5241
- { GID_ISLANDBRAIN, 140, 140, 0, "piece", "init", -1, 3, { WORKAROUND_FAKE, 1 } }, // first puzzle right at the start, some initialization variable. bnt is done on it, and it should be non-0
- { GID_ISLANDBRAIN, 200, 268, 0, "anElement", "select", -1, 0, { WORKAROUND_FAKE, 0 } }, // elements puzzle, gets used before super TextIcon
- { GID_JONES, 1, 232, 0, "weekendText", "draw", 0x3d3, 0, { WORKAROUND_FAKE, 0 } }, // jones/cd only - gets called during the game
- { GID_JONES, 1, 255, 0, "", "export 0", -1, -1, { WORKAROUND_FAKE, 0 } }, // jones/cd only - called when a game ends, temps 13 and 14
- { GID_JONES, 764, 255, 0, "", "export 0", -1, -1, { WORKAROUND_FAKE, 0 } }, // jones/ega&vga only - called when the game starts, temps 13 and 14
- //{ GID_KQ5, -1, 0, 0, "", "export 29", -1, 3, { WORKAROUND_FAKE, 0xf } }, // called when playing harp for the harpies or when aborting dialog in toy shop, is used for kDoAudio - bug #4961
+ { GID_CAMELOT, 40, 40, 0, "Rm40", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when looking at the ground at the pool of Siloam - bug #6401
+ { GID_CASTLEBRAIN, 280, 280, 0, "programmer", "dispatchEvent", NULL, 0, { WORKAROUND_FAKE, 0xf } }, // pressing 'q' on the computer screen in the robot room, and closing the help dialog that pops up (bug #5143). Moves the cursor to the view with the ID returned (in this case, the robot hand)
+ { GID_CNICK_KQ, -1, 0, 1, "Character", "say", NULL, -1, { WORKAROUND_FAKE, 0 } }, // checkers/backgammon, like in hoyle 3 - temps 504 and 505 - bug #6255
+ { GID_CNICK_KQ, -1, 700, 0, "gcWindow", "open", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when entering the control menu, like in hoyle 3
+ { GID_CNICK_KQ, 300, 303, 0, "theDoubleCube", "<noname520>", NULL, 5, { WORKAROUND_FAKE, 0 } }, // while playing backgammon with doubling enabled - bug #6426 (same as the theDoubleCube::make workaround for Hoyle 3)
+ { GID_CNICK_KQ, 300, 303, 0, "theDoubleCube", "<noname519>", NULL, 9, { WORKAROUND_FAKE, 0 } }, // when accepting a double, while playing backgammon with doubling enabled (same as the theDoubleCube::accept workaround for Hoyle 3)
+ { GID_CNICK_LAURABOW, -1, 0, 1, "Character", "say", NULL, -1, { WORKAROUND_FAKE, 0 } }, // Yatch, like in hoyle 3 - temps 504 and 505 - bug #6424
+ { GID_CNICK_LAURABOW, -1, 700, 0, NULL, "open", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu - bug #6423 (same as the gcWindow workaround for Hoyle 3)
+ { GID_CNICK_LAURABOW,100, 100, 0, NULL, "<noname144>", NULL, 1, { WORKAROUND_FAKE, 0 } }, // while playing domino - bug #6429 (same as the dominoHand2 workaround for Hoyle 3)
+ { GID_CNICK_LAURABOW,100, 110, 0, NULL, "doit", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when changing the "Dominoes per hand" setting - bug #6430
+ { GID_CNICK_LONGBOW, 0, 0, 0, "RH Budget", "init", NULL, 1, { WORKAROUND_FAKE, 0 } }, // when starting the game
+ { GID_ECOQUEST, -1, -1, 0, NULL, "doVerb", NULL, 0, { WORKAROUND_FAKE, 0 } }, // almost clicking anywhere triggers this in almost all rooms
+ { GID_FANMADE, 516, 979, 0, "", "export 0", NULL, 20, { WORKAROUND_FAKE, 0 } }, // Happens in Grotesteing after the logos
+ { GID_FANMADE, 528, 990, 0, "GDialog", "doit", NULL, 4, { WORKAROUND_FAKE, 0 } }, // Happens in Cascade Quest when closing the glossary - bug #5116
+ { GID_FANMADE, 488, 1, 0, "RoomScript", "doit", sig_uninitread_fanmade_1, 1, { WORKAROUND_FAKE, 0 } }, // Happens in Ocean Battle while playing - bug #5335
+ { GID_FREDDYPHARKAS, -1, 24, 0, "gcWin", "open", NULL, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu
+ { GID_FREDDYPHARKAS, -1, 31, 0, "quitWin", "open", NULL, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu
+ { GID_FREDDYPHARKAS, 540, 540, 0, "WaverCode", "init", NULL, -1, { WORKAROUND_FAKE, 0 } }, // Gun pratice mini-game - bug #5232
+ { GID_GK1, -1, 64950, -1, "Feature", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // sometimes when walk-clicking
+ { GID_GK2, -1, 11, 0, "", "export 10", NULL, 3, { WORKAROUND_FAKE, 0 } }, // called when the game starts
+ { GID_GK2, -1, 11, 0, "", "export 10", NULL, 4, { WORKAROUND_FAKE, 0 } }, // called during the game
+ { GID_HOYLE1, 4, 104, 0, "GinRummyCardList", "calcRuns", NULL, 4, { WORKAROUND_FAKE, 0 } }, // Gin Rummy / right when the game starts
+ { GID_HOYLE1, 5, 204, 0, "tableau", "checkRuns", NULL, 2, { WORKAROUND_FAKE, 0 } }, // Cribbage / during the game
+ { GID_HOYLE1, 3, 16, 0, "", "export 0", sig_uninitread_hoyle1_1, 3, { WORKAROUND_FAKE, 0 } }, // Hearts / during the game - bug #5299
+ { GID_HOYLE1, -1, 997, 0, "MenuBar", "doit", NULL, 0, { WORKAROUND_FAKE, 0 } }, // When changing game speed settings - bug #5512
+ { GID_HOYLE3, -1, 0, 1, "Character", "say", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when starting checkers or dominoes, first time a character says something - temps 504 and 505
+ { GID_HOYLE3, -1, 700, 0, "gcWindow", "open", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu
+ { GID_HOYLE3, 100, 100, 0, "dominoHand2", "cue", NULL, 1, { WORKAROUND_FAKE, 0 } }, // while playing domino - bug #5042
+ { GID_HOYLE3, 100, 110, 0, "OKButton", "doit", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when changing the "Dominoes per hand" setting - bug #6430
+ { GID_HOYLE3, 300, 303, 0, "theDoubleCube", "make", NULL, 5, { WORKAROUND_FAKE, 0 } }, // while playing backgammon with doubling enabled
+ { GID_HOYLE3, 300, 303, 0, "theDoubleCube", "accept", NULL, 9, { WORKAROUND_FAKE, 0 } }, // when accepting a double, while playing backgammon with doubling enabled
+ { GID_HOYLE4, -1, 0, 0, NULL, "open", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when selecting "Control" from the menu (temp vars 0-3) - bug #5132
+ { GID_HOYLE4, 910, 18, 0, NULL, "init", NULL, 0, { WORKAROUND_FAKE, 0 } }, // during tutorial - bug #5213
+ { GID_HOYLE4, 910, 910, 0, NULL, "setup", NULL, 3, { WORKAROUND_FAKE, 0 } }, // when selecting "Tutorial" from the main menu - bug #5132
+ { GID_HOYLE4, 700, 700, 1, "BridgeHand", "calcQTS", NULL, 3, { WORKAROUND_FAKE, 0 } }, // when placing a bid in bridge (always)
+ { GID_HOYLE4, 700, 710, 1, "BridgeStrategyPlay", "checkSplitTops", NULL, 10, { WORKAROUND_FAKE, 0 } }, // while playing bridge, objects LeadReturn_Trump, SecondSeat_Trump, ThirdSeat_Trump and others - bug #5794
+ { GID_HOYLE4, 700, -1, 1, "BridgeDefense", "think", NULL, -1, { WORKAROUND_FAKE, 0 } }, // sometimes while playing bridge, temp var 3, 17 and others, objects LeadReturn_Trump, ThirdSeat_Trump and others
+ { GID_HOYLE4, 700, 730, 1, "BridgeDefense", "beatTheirBest", NULL, 3, { WORKAROUND_FAKE, 0 } }, // rarely while playing bridge
+ { GID_HOYLE4, 700, -1, 1, "Code", "doit", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when placing a bid in bridge (always), temp var 11, 24, 27, 46, 75, objects compete_tree, compwe_tree, other1_tree, b1 - bugs #5663 and #5794
+ { GID_HOYLE4, 700, 921, 0, "Print", "addEdit", NULL, 0, { WORKAROUND_FAKE, 118 } }, // when saving the game (may also occur in other situations) - bug #6601, bug #6614
+ { GID_HOYLE4, 700, 921, 0, "Print", "addEdit", NULL, 1, { WORKAROUND_FAKE, 1 } }, // see above, Text-control saves its coordinates to temp[0] and temp[1], Edit-control adjusts to those uninitialized temps, who by accident were left over from the Text-control
+ { GID_HOYLE4, 300, 300, 0, "", "export 2", sig_uninitread_hoyle4_1, 0, { WORKAROUND_FAKE, 0 } }, // after passing around cards in hearts
+ { GID_HOYLE4, 400, 400, 1, "GinHand", "calcRuns", NULL, 4, { WORKAROUND_FAKE, 0 } }, // sometimes while playing Gin Rummy (e.g. when knocking and placing a card) - bug #5665
+ { GID_HOYLE4, 500, 17, 1, "Character", "say", NULL, 504, { WORKAROUND_FAKE, 0 } }, // sometimes while playing Cribbage (e.g. when the opponent says "Last Card") - bug #5662
+ { GID_HOYLE4, 800, 870, 0, "EuchreStrategy", "thinkLead", NULL, 0, { WORKAROUND_FAKE, 0 } }, // while playing Euchre, happens at least on 2nd or 3rd turn - bug #6602
+ { GID_HOYLE4, -1, 937, 0, "IconBar", "dispatchEvent", NULL, 408, { WORKAROUND_FAKE, 0 } }, // pressing ENTER on scoreboard while mouse is not on OK button, may not happen all the time - bug #6603
+ { GID_ISLANDBRAIN, 100, 937, 0, "IconBar", "dispatchEvent", NULL, 58, { WORKAROUND_FAKE, 0 } }, // when using ENTER at the startup menu - bug #5241
+ { GID_ISLANDBRAIN, 140, 140, 0, "piece", "init", NULL, 3, { WORKAROUND_FAKE, 1 } }, // first puzzle right at the start, some initialization variable. bnt is done on it, and it should be non-0
+ { GID_ISLANDBRAIN, 200, 268, 0, "anElement", "select", NULL, 0, { WORKAROUND_FAKE, 0 } }, // elements puzzle, gets used before super TextIcon
+ { GID_JONES, 1, 232, 0, "weekendText", "draw", sig_uninitread_jones_1, 0, { WORKAROUND_FAKE, 0 } }, // jones/cd only - gets called during the game
+ { GID_JONES, 1, 255, 0, "", "export 0", NULL, -1, { WORKAROUND_FAKE, 0 } }, // jones/cd only - called when a game ends, temps 13 and 14
+ { GID_JONES, 764, 255, 0, "", "export 0", NULL, -1, { WORKAROUND_FAKE, 0 } }, // jones/ega&vga only - called when the game starts, temps 13 and 14
+ //{ GID_KQ5, -1, 0, 0, "", "export 29", NULL, 3, { WORKAROUND_FAKE, 0xf } }, // called when playing harp for the harpies or when aborting dialog in toy shop, is used for kDoAudio - bug #4961
// ^^ shouldn't be needed anymore, we got a script patch instead (kq5PatchCdHarpyVolume)
- { GID_KQ5, 25, 25, 0, "rm025", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // inside witch forest, when going to the room where the walking rock is
- { GID_KQ5, 55, 55, 0, "helpScript", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // when giving the tambourine to the monster in the labyrinth (only happens at one of the locations) - bug #5198
- { GID_KQ5, -1, 755, 0, "gcWin", "open", -1, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu in the FM-Towns version
- { GID_KQ6, -1, 30, 0, "rats", "changeState", -1, -1, { WORKAROUND_FAKE, 0 } }, // rats in the catacombs (temps 1 - 5) - bugs #4958, #4998, #5017
- { GID_KQ6, 210, 210, 0, "rm210", "scriptCheck", -1, 0, { WORKAROUND_FAKE, 1 } }, // using inventory in that room - bug #4953
- { GID_KQ6, 500, 500, 0, "rm500", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // going to island of the beast
- { GID_KQ6, 520, 520, 0, "rm520", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // going to boiling water trap on beast isle
- { GID_KQ6, -1, 903, 0, "controlWin", "open", -1, 4, { WORKAROUND_FAKE, 0 } }, // when opening the controls window (save, load etc)
- { GID_KQ6, -1, 907, 0, "tomato", "doVerb", -1, 2, { WORKAROUND_FAKE, 0 } }, // when looking at the rotten tomato in the inventory - bug #5331
- { GID_KQ6, -1, 928, 0, NULL, "startText", -1, 0, { WORKAROUND_FAKE, 0 } }, // gets caused by Text+Audio support (see script patcher)
- { GID_KQ7, -1, 64996, 0, "User", "handleEvent", -1, 1, { WORKAROUND_FAKE, 0 } }, // called when pushing a keyboard key
- { GID_LAURABOW, 37, 0, 0, "CB1", "doit", -1, 1, { WORKAROUND_FAKE, 0 } }, // when going up the stairs - bug #5084
- { GID_LAURABOW, -1, 967, 0, "myIcon", "cycle", -1, 1, { WORKAROUND_FAKE, 0 } }, // having any portrait conversation coming up - initial bug #4971
- { GID_LAURABOW2, -1, 24, 0, "gcWin", "open", -1, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu
- { GID_LAURABOW2, -1, 21, 0, "dropCluesCode", "doit", -1, 1, { WORKAROUND_FAKE, 0x7fff } }, // when asking some questions (e.g. the reporter about the burglary, or the policeman about Ziggy). Must be big, as the game scripts perform lt on it and start deleting journal entries - bugs #4979, #5026
- { GID_LAURABOW2, -1, 90, 1, "MuseumActor", "init", -1, 6, { WORKAROUND_FAKE, 0 } }, // Random actors in museum - bug #5197
- { GID_LAURABOW2, 240, 240, 0, "sSteveAnimates", "changeState", -1, 0, { WORKAROUND_FAKE, 0 } }, // Steve Dorian's idle animation at the docks - bug #5028
- { GID_LAURABOW2, -1, 928, 0, NULL, "startText", -1, 0, { WORKAROUND_FAKE, 0 } }, // gets caused by Text+Audio support (see script patcher)
- { GID_LONGBOW, -1, 0, 0, "Longbow", "restart", -1, 0, { WORKAROUND_FAKE, 0 } }, // When canceling a restart game - bug #5244
- { GID_LONGBOW, -1, 213, 0, "clear", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // When giving an answer using the druid hand sign code in any room
- { GID_LONGBOW, -1, 213, 0, "letter", "handleEvent", 0xa8, 1, { WORKAROUND_FAKE, 0 } }, // When using the druid hand sign code in any room - bug #5035
- { GID_LSL1, 250, 250, 0, "increase", "handleEvent", -1, 2, { WORKAROUND_FAKE, 0 } }, // casino, playing game, increasing bet
- { GID_LSL1, 720, 720, 0, "rm720", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // age check room
- { GID_LSL2, 38, 38, 0, "cloudScript", "changeState", -1, 1, { WORKAROUND_FAKE, 0 } }, // entering the room in the middle deck of the ship - bug #5034
- { GID_LSL3, 340, 340, 0, "ComicScript", "changeState", -1, -1, { WORKAROUND_FAKE, 0 } }, // right after entering the 3 ethnic groups inside comedy club (temps 200, 201, 202, 203)
- { GID_LSL3, -1, 997, 0, "TheMenuBar", "handleEvent", -1, 1, { WORKAROUND_FAKE, 0xf } }, // when setting volume the first time, this temp is used to set volume on entry (normally it would have been initialized to 's')
- { GID_LSL6, 820, 82, 0, "", "export 0", -1, -1, { WORKAROUND_FAKE, 0 } }, // when touching the electric fence - bug #5103
- { GID_LSL6, -1, 85, 0, "washcloth", "doVerb", -1, 0, { WORKAROUND_FAKE, 0 } }, // washcloth in inventory
- { GID_LSL6, -1, 928, -1, "Narrator", "startText", -1, 0, { WORKAROUND_FAKE, 0 } }, // used by various objects that are even translated in foreign versions, that's why we use the base-class
- { GID_LSL6HIRES, 0, 85, 0, "LL6Inv", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // on startup
- { GID_LSL6HIRES, -1, 64950, 1, "Feature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // at least when entering swimming pool area
- { GID_LSL6HIRES, -1, 64964, 0, "DPath", "init", -1, 1, { WORKAROUND_FAKE, 0 } }, // during the game
- { GID_MOTHERGOOSE256, -1, 0, 0, "MG", "doit", -1, 5, { WORKAROUND_FAKE, 0 } }, // SCI1.1: When moving the cursor all the way to the left during the game - bug #5224
- { GID_MOTHERGOOSE256, -1, 992, 0, "AIPath", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // Happens in the demo and full version. In the demo, it happens when walking two screens from mother goose's house to the north. In the full version, it happens in rooms 7 and 23 - bug #5269
- { GID_MOTHERGOOSE256, 90, 90, 0, "introScript", "changeState", -1, 65, { WORKAROUND_FAKE, 0 } }, // SCI1(CD): At the very end, after the game is completed and restarted - bug #5626
- { GID_MOTHERGOOSE256, 94, 94, 0, "sunrise", "changeState", -1, 367, { WORKAROUND_FAKE, 0 } }, // At the very end, after the game is completed - bug #5294
- { GID_MOTHERGOOSEHIRES,-1,64950, 1, "Feature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // right when clicking on a child at the start and probably also later
- { GID_MOTHERGOOSEHIRES,-1,64950, 1, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // see above
- { GID_PEPPER, -1, 894, 0, "Package", "doVerb", -1, 3, { WORKAROUND_FAKE, 0 } }, // using the hand on the book in the inventory - bug #5154
- { GID_PEPPER, 150, 928, 0, "Narrator", "startText", -1, 0, { WORKAROUND_FAKE, 0 } }, // happens during the non-interactive demo of Pepper
- { GID_PQ4, -1, 25, 0, "iconToggle", "select", -1, 1, { WORKAROUND_FAKE, 0 } }, // when toggling the icon bar to auto-hide or not
- { GID_PQSWAT, -1, 64950, 0, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // Using the menu in the beginning
- { GID_QFG1, -1, 210, 0, "Encounter", "init", 0xbd0, 0, { WORKAROUND_FAKE, 0 } }, // hq1: going to the brigands hideout
- { GID_QFG1, -1, 210, 0, "Encounter", "init", 0xbe4, 0, { WORKAROUND_FAKE, 0 } }, // qfg1: going to the brigands hideout
- { GID_QFG1VGA, 16, 16, 0, "lassoFailed", "changeState", -1, -1, { WORKAROUND_FAKE, 0 } }, // qfg1vga: casting the "fetch" spell in the screen with the flowers, temps 0 and 1 - bug #5309
- { GID_QFG1VGA, -1, 210, 0, "Encounter", "init", 0xcee, 0, { WORKAROUND_FAKE, 0 } }, // qfg1vga: going to the brigands hideout - bug #5515
- { GID_QFG1VGA, -1, 210, 0, "Encounter", "init", 0xce7, 0, { WORKAROUND_FAKE, 0 } }, // qfg1vga: going to room 92
- { GID_QFG2, -1, 71, 0, "theInvSheet", "doit", -1, 1, { WORKAROUND_FAKE, 0 } }, // accessing the inventory
- { GID_QFG2, -1, 701, -1, "Alley", "at", -1, 0, { WORKAROUND_FAKE, 0 } }, // when walking inside the alleys in the town - bug #5019 & #5106
- { GID_QFG2, -1, 990, 0, "Restore", "doit", -1, 364, { WORKAROUND_FAKE, 0 } }, // when pressing enter in restore dialog w/o any saved games present
- { GID_QFG2, 260, 260, 0, "abdulS", "changeState",0x2d22, -1, { WORKAROUND_FAKE, 0 } }, // During the thief's first mission (in the house), just before Abdul is about to enter the house (where you have to hide in the wardrobe), bug #5153, temps 1 and 2
- { GID_QFG2, 260, 260, 0, "jabbarS", "changeState",0x2d22, -1, { WORKAROUND_FAKE, 0 } }, // During the thief's first mission (in the house), just before Jabbar is about to enter the house (where you have to hide in the wardrobe), bug #5164, temps 1 and 2
- { GID_QFG2, 500, 500, 0, "lightNextCandleS", "changeState", -1, -1, { WORKAROUND_FAKE, 0 } }, // Inside the last room, while Ad Avis performs the ritual to summon the genie - bug #5566
- { GID_QFG2, -1, 700, 0, NULL, "showSign", -1, 10, { WORKAROUND_FAKE, 0 } }, // Occurs sometimes when reading a sign in Raseir, Shapeir et al - bugs #5627, #5635
- { GID_QFG3, 510, 510, 0, "awardPrize", "changeState", -1, 0, { WORKAROUND_FAKE, 1 } }, // Simbani warrior challenge, after throwing the spears and retrieving the ring - bug #5277. Must be non-zero, otherwise the prize is awarded twice - bug #6160
- { GID_QFG3, 140, 140, 0, "rm140", "init", 0x1008, 0, { WORKAROUND_FAKE, 0 } }, // when importing a character and selecting the previous profession - bug #5163
- { GID_QFG3, 330, 330, -1, "Teller", "doChild", -1, -1, { WORKAROUND_FAKE, 0 } }, // when talking to King Rajah about "Rajah" (bug #5033, temp 1) or "Tarna" (temp 0), or when clicking on yourself and saying "Greet" (bug #5148, temp 1)
- { GID_QFG3, 700, 700, -1, "monsterIsDead", "changeState", -1, 0, { WORKAROUND_FAKE, 0 } }, // in the jungle, after winning any fight, bug #5169
- { GID_QFG3, 470, 470, -1, "rm470", "notify", -1, 0, { WORKAROUND_FAKE, 0 } }, // closing the character screen in the Simbani village in the room with the bridge, bug #5165
- { GID_QFG3, 490, 490, -1, "computersMove", "changeState", -1, 0, { WORKAROUND_FAKE, 0 } }, // when finishing awari game, bug #5167
- { GID_QFG3, 490, 490, -1, "computersMove", "changeState", 0xf53, 4, { WORKAROUND_FAKE, 0 } }, // also when finishing awari game
- { GID_QFG3, 851, 32, -1, "ProjObj", "doit", -1, 1, { WORKAROUND_FAKE, 0 } }, // near the end, when throwing the spear of death, bug #5282
- { GID_QFG4, -1, 15, -1, "charInitScreen", "dispatchEvent", -1, 5, { WORKAROUND_FAKE, 0 } }, // floppy version, when viewing the character screen
- { GID_QFG4, -1, 64917, -1, "controlPlane", "setBitmap", -1, 3, { WORKAROUND_FAKE, 0 } }, // floppy version, when entering the game menu
- { GID_QFG4, -1, 64917, -1, "Plane", "setBitmap", -1, 3, { WORKAROUND_FAKE, 0 } }, // floppy version, happens sometimes in fight scenes
- { GID_QFG4, 520, 64950, 0, "fLake2", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // CD version, at the lake, when meeting the Rusalka and attempting to leave
- { GID_QFG4, 800, 64950, 0, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // CD version, in the room with the spider pillar, when climbing on the pillar
- { GID_RAMA, 12, 64950, -1, "InterfaceFeature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // Demo, right when it starts
- { GID_RAMA, 12, 64950, -1, "hiliteOptText", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // Demo, right when it starts
- { GID_RAMA, 12, 64950, -1, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // Demo, right when it starts
- { GID_SHIVERS, -1, 952, 0, "SoundManager", "stop", -1, 2, { WORKAROUND_FAKE, 0 } }, // Just after Sierra logo
- { GID_SHIVERS, -1, 64950, 0, "Feature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // When clicking on the locked door at the beginning
- { GID_SHIVERS, -1, 64950, 0, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // When clicking on the gargoyle eye at the beginning
- { GID_SHIVERS, 20311, 64964, 0, "DPath", "init", -1, 1, { WORKAROUND_FAKE, 0 } }, // Just after door puzzle is solved and the metal balls start to roll
- { GID_SHIVERS, 29260, 29260, 0, "spMars", "handleEvent", -1, 4, { WORKAROUND_FAKE, 0 } }, // When clicking mars after seeing fortune to align earth etc...
- { GID_SHIVERS, 29260, 29260, 0, "spVenus", "handleEvent", -1, 4, { WORKAROUND_FAKE, 0 } }, // When clicking venus after seeing fortune to align earth etc...
- { GID_SQ1, 103, 103, 0, "hand", "internalEvent", -1, -1, { WORKAROUND_FAKE, 0 } }, // Spanish (and maybe early versions?) only: when moving cursor over input pad, temps 1 and 2
- { GID_SQ1, -1, 703, 0, "", "export 1", -1, 0, { WORKAROUND_FAKE, 0 } }, // sub that's called from several objects while on sarien battle cruiser
- { GID_SQ1, -1, 703, 0, "firePulsar", "changeState", 0x18a, 0, { WORKAROUND_FAKE, 0 } }, // export 1, but called locally (when shooting at aliens)
- { GID_SQ4, -1, 398, 0, "showBox", "changeState", -1, 0, { WORKAROUND_FAKE, 0 } }, // CD: called when rummaging in Software Excess bargain bin
- { GID_SQ4, -1, 928, -1, "Narrator", "startText", -1, 1000, { WORKAROUND_FAKE, 1 } }, // CD: happens in the options dialog and in-game when speech and subtitles are used simultaneously
- { GID_SQ4, -1, 708, -1, "exitBut", "doVerb", -1, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "close" button in the sq4 hintbook - bug #6447
- { GID_SQ4, -1, 708, -1, "prevBut", "doVerb", -1, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "previous" button in the sq4 hintbook - bug #6447
- { GID_SQ4, -1, 708, -1, "nextBut", "doVerb", -1, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "next" button in the sq4 hintbook - bug #6447
- { GID_SQ5, 201, 201, 0, "buttonPanel", "doVerb", -1, 0, { WORKAROUND_FAKE, 1 } }, // when looking at the orange or red button - bug #5112
- { GID_SQ6, -1, 0, 0, "SQ6", "init", -1, 2, { WORKAROUND_FAKE, 0 } }, // Demo and full version: called when the game starts (demo: room 0, full: room 100)
- { GID_SQ6, -1, 64950, -1, "Feature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // called when pressing "Start game" in the main menu, when entering the Orion's Belt bar (room 300), and perhaps other places
- { GID_SQ6, -1, 64964, 0, "DPath", "init", -1, 1, { WORKAROUND_FAKE, 0 } }, // during the game
- { GID_TORIN, -1, 64017, 0, "oFlags", "clear", -1, 0, { WORKAROUND_FAKE, 0 } }, // entering Torin's home in the French version
- SCI_WORKAROUNDENTRY_TERMINATOR
-};
-
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+ { GID_KQ5, 25, 25, 0, "rm025", "doit", NULL, 0, { WORKAROUND_FAKE, 0 } }, // inside witch forest, when going to the room where the walking rock is
+ { GID_KQ5, 55, 55, 0, "helpScript", "doit", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when giving the tambourine to the monster in the labyrinth (only happens at one of the locations) - bug #5198
+ { GID_KQ5, -1, 755, 0, "gcWin", "open", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu in the FM-Towns version
+ { GID_KQ6, -1, 30, 0, "rats", "changeState", NULL, -1, { WORKAROUND_FAKE, 0 } }, // rats in the catacombs (temps 1 - 5) - bugs #4958, #4998, #5017
+ { GID_KQ6, 210, 210, 0, "rm210", "scriptCheck", NULL, 0, { WORKAROUND_FAKE, 1 } }, // using inventory in that room - bug #4953
+ { GID_KQ6, 500, 500, 0, "rm500", "init", NULL, 0, { WORKAROUND_FAKE, 0 } }, // going to island of the beast
+ { GID_KQ6, 520, 520, 0, "rm520", "init", NULL, 0, { WORKAROUND_FAKE, 0 } }, // going to boiling water trap on beast isle
+ { GID_KQ6, -1, 903, 0, "controlWin", "open", NULL, 4, { WORKAROUND_FAKE, 0 } }, // when opening the controls window (save, load etc)
+ { GID_KQ6, -1, 907, 0, "tomato", "doVerb", NULL, 2, { WORKAROUND_FAKE, 0 } }, // when looking at the rotten tomato in the inventory - bug #5331
+ { GID_KQ6, -1, 928, 0, NULL, "startText", NULL, 0, { WORKAROUND_FAKE, 0 } }, // gets caused by Text+Audio support (see script patcher)
+ { GID_KQ7, -1, 64996, 0, "User", "handleEvent", NULL, 1, { WORKAROUND_FAKE, 0 } }, // called when pushing a keyboard key
+ { GID_LAURABOW, 37, 0, 0, "CB1", "doit", NULL, 1, { WORKAROUND_FAKE, 0 } }, // when going up the stairs - bug #5084
+ { GID_LAURABOW, -1, 967, 0, "myIcon", "cycle", NULL, 1, { WORKAROUND_FAKE, 0 } }, // having any portrait conversation coming up - initial bug #4971
+ { GID_LAURABOW2, -1, 24, 0, "gcWin", "open", NULL, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu
+ { GID_LAURABOW2, -1, 21, 0, "dropCluesCode", "doit", NULL, 1, { WORKAROUND_FAKE, 0x7fff } }, // when asking some questions (e.g. the reporter about the burglary, or the policeman about Ziggy). Must be big, as the game scripts perform lt on it and start deleting journal entries - bugs #4979, #5026
+ { GID_LAURABOW2, -1, 90, 1, "MuseumActor", "init", NULL, 6, { WORKAROUND_FAKE, 0 } }, // Random actors in museum - bug #5197
+ { GID_LAURABOW2, 240, 240, 0, "sSteveAnimates", "changeState", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Steve Dorian's idle animation at the docks - bug #5028
+ { GID_LAURABOW2, -1, 928, 0, NULL, "startText", NULL, 0, { WORKAROUND_FAKE, 0 } }, // gets caused by Text+Audio support (see script patcher)
+ { GID_LONGBOW, -1, 0, 0, "Longbow", "restart", NULL, 0, { WORKAROUND_FAKE, 0 } }, // When canceling a restart game - bug #5244
+ { GID_LONGBOW, -1, 213, 0, "clear", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // When giving an answer using the druid hand sign code in any room
+ { GID_LONGBOW, -1, 213, 0, "letter", "handleEvent", sig_uninitread_longbow_1, 1, { WORKAROUND_FAKE, 0 } }, // When using the druid hand sign code in any room - bug #5035
+ { GID_LSL1, 250, 250, 0, "increase", "handleEvent", NULL, 2, { WORKAROUND_FAKE, 0 } }, // casino, playing game, increasing bet
+ { GID_LSL1, 720, 720, 0, "rm720", "init", NULL, 0, { WORKAROUND_FAKE, 0 } }, // age check room
+ { GID_LSL2, 38, 38, 0, "cloudScript", "changeState", NULL, 1, { WORKAROUND_FAKE, 0 } }, // entering the room in the middle deck of the ship - bug #5034
+ { GID_LSL3, 340, 340, 0, "ComicScript", "changeState", NULL, -1, { WORKAROUND_FAKE, 0 } }, // right after entering the 3 ethnic groups inside comedy club (temps 200, 201, 202, 203)
+ { GID_LSL3, -1, 997, 0, "TheMenuBar", "handleEvent", NULL, 1, { WORKAROUND_FAKE, 0xf } }, // when setting volume the first time, this temp is used to set volume on entry (normally it would have been initialized to 's')
+ { GID_LSL6, 820, 82, 0, "", "export 0", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when touching the electric fence - bug #5103
+ { GID_LSL6, -1, 85, 0, "washcloth", "doVerb", NULL, 0, { WORKAROUND_FAKE, 0 } }, // washcloth in inventory
+ { GID_LSL6, -1, 928, -1, "Narrator", "startText", NULL, 0, { WORKAROUND_FAKE, 0 } }, // used by various objects that are even translated in foreign versions, that's why we use the base-class
+ { GID_LSL6HIRES, 0, 85, 0, "LL6Inv", "init", NULL, 0, { WORKAROUND_FAKE, 0 } }, // on startup
+ { GID_LSL6HIRES, -1, 64950, 1, "Feature", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // at least when entering swimming pool area
+ { GID_LSL6HIRES, -1, 64964, 0, "DPath", "init", NULL, 1, { WORKAROUND_FAKE, 0 } }, // during the game
+ { GID_MOTHERGOOSE256, -1, 0, 0, "MG", "doit", NULL, 5, { WORKAROUND_FAKE, 0 } }, // SCI1.1: When moving the cursor all the way to the left during the game - bug #5224
+ { GID_MOTHERGOOSE256, -1, 992, 0, "AIPath", "init", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Happens in the demo and full version. In the demo, it happens when walking two screens from mother goose's house to the north. In the full version, it happens in rooms 7 and 23 - bug #5269
+ { GID_MOTHERGOOSE256, 90, 90, 0, "introScript", "changeState", NULL, 65, { WORKAROUND_FAKE, 0 } }, // SCI1(CD): At the very end, after the game is completed and restarted - bug #5626
+ { GID_MOTHERGOOSE256, 94, 94, 0, "sunrise", "changeState", NULL, 367, { WORKAROUND_FAKE, 0 } }, // At the very end, after the game is completed - bug #5294
+ { GID_MOTHERGOOSEHIRES,-1,64950, 1, "Feature", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // right when clicking on a child at the start and probably also later
+ { GID_MOTHERGOOSEHIRES,-1,64950, 1, "View", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // see above
+ { GID_PEPPER, -1, 894, 0, "Package", "doVerb", NULL, 3, { WORKAROUND_FAKE, 0 } }, // using the hand on the book in the inventory - bug #5154
+ { GID_PEPPER, 150, 928, 0, "Narrator", "startText", NULL, 0, { WORKAROUND_FAKE, 0 } }, // happens during the non-interactive demo of Pepper
+ { GID_PQ4, -1, 25, 0, "iconToggle", "select", NULL, 1, { WORKAROUND_FAKE, 0 } }, // when toggling the icon bar to auto-hide or not
+ { GID_PQSWAT, -1, 64950, 0, "View", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Using the menu in the beginning
+ { GID_QFG1, -1, 210, 0, "Encounter", "init", sig_uninitread_qfg1_1, 0, { WORKAROUND_FAKE, 0 } }, // qfg1/hq1: going to the brigands hideout
+ { GID_QFG1VGA, 16, 16, 0, "lassoFailed", "changeState", NULL, -1, { WORKAROUND_FAKE, 0 } }, // qfg1vga: casting the "fetch" spell in the screen with the flowers, temps 0 and 1 - bug #5309
+ { GID_QFG1VGA, -1, 210, 0, "Encounter", "init", sig_uninitread_qfg1vga_1, 0, { WORKAROUND_FAKE, 0 } }, // qfg1vga: going to the brigands hideout - bug #5515
+ { GID_QFG2, -1, 71, 0, "theInvSheet", "doit", NULL, 1, { WORKAROUND_FAKE, 0 } }, // accessing the inventory
+ { GID_QFG2, -1, 79, 0, "TryToMoveTo", "onTarget", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when throwing pot at air elemental, happens when client coordinates are the same as airElemental coordinates. happened to me right after room change - bug #6859
+ { GID_QFG2, -1, 701, -1, "Alley", "at", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when walking inside the alleys in the town - bug #5019 & #5106
+ { GID_QFG2, -1, 990, 0, "Restore", "doit", NULL, 364, { WORKAROUND_FAKE, 0 } }, // when pressing enter in restore dialog w/o any saved games present
+ { GID_QFG2, 260, 260, 0, "abdulS", "changeState", sig_uninitread_qfg2_1, -1, { WORKAROUND_FAKE, 0 } }, // During the thief's first mission (in the house), just before Abdul is about to enter the house (where you have to hide in the wardrobe), bug #5153, temps 1 and 2
+ { GID_QFG2, 260, 260, 0, "jabbarS", "changeState", sig_uninitread_qfg2_1, -1, { WORKAROUND_FAKE, 0 } }, // During the thief's first mission (in the house), just before Jabbar is about to enter the house (where you have to hide in the wardrobe), bug #5164, temps 1 and 2
+ { GID_QFG2, 500, 500, 0, "lightNextCandleS", "changeState", NULL, -1, { WORKAROUND_FAKE, 0 } }, // Inside the last room, while Ad Avis performs the ritual to summon the genie - bug #5566
+ { GID_QFG2, -1, 700, 0, NULL, "showSign", NULL, 10, { WORKAROUND_FAKE, 0 } }, // Occurs sometimes when reading a sign in Raseir, Shapeir et al - bugs #5627, #5635
+ { GID_QFG3, 510, 510, 0, "awardPrize", "changeState", NULL, 0, { WORKAROUND_FAKE, 1 } }, // Simbani warrior challenge, after throwing the spears and retrieving the ring - bug #5277. Must be non-zero, otherwise the prize is awarded twice - bug #6160
+ { GID_QFG3, 140, 140, 0, "rm140", "init", sig_uninitread_qfg3_1, 0, { WORKAROUND_FAKE, 0 } }, // when importing a character and selecting the previous profession - bug #5163
+ { GID_QFG3, 330, 330, -1, "Teller", "doChild", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when talking to King Rajah about "Rajah" (bug #5033, temp 1) or "Tarna" (temp 0), or when clicking on yourself and saying "Greet" (bug #5148, temp 1)
+ { GID_QFG3, 700, 700, -1, "monsterIsDead", "changeState", NULL, 0, { WORKAROUND_FAKE, 0 } }, // in the jungle, after winning any fight, bug #5169
+ { GID_QFG3, 470, 470, -1, "rm470", "notify", NULL, 0, { WORKAROUND_FAKE, 0 } }, // closing the character screen in the Simbani village in the room with the bridge, bug #5165
+ { GID_QFG3, 490, 490, -1, "computersMove", "changeState", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when finishing awari game, bug #5167
+ { GID_QFG3, 490, 490, -1, "computersMove", "changeState", sig_uninitread_qfg3_2, 4, { WORKAROUND_FAKE, 0 } }, // also when finishing awari game
+ { GID_QFG3, 851, 32, -1, "ProjObj", "doit", NULL, 1, { WORKAROUND_FAKE, 0 } }, // near the end, when throwing the spear of death, bug #5282
+ { GID_QFG4, -1, 15, -1, "charInitScreen", "dispatchEvent", NULL, 5, { WORKAROUND_FAKE, 0 } }, // floppy version, when viewing the character screen
+ { GID_QFG4, -1, 64917, -1, "controlPlane", "setBitmap", NULL, 3, { WORKAROUND_FAKE, 0 } }, // floppy version, when entering the game menu
+ { GID_QFG4, -1, 64917, -1, "Plane", "setBitmap", NULL, 3, { WORKAROUND_FAKE, 0 } }, // floppy version, happens sometimes in fight scenes
+ { GID_QFG4, 520, 64950, 0, "fLake2", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // CD version, at the lake, when meeting the Rusalka and attempting to leave
+ { GID_QFG4, 800, 64950, 0, "View", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // CD version, in the room with the spider pillar, when climbing on the pillar
+ { GID_RAMA, 12, 64950, -1, "InterfaceFeature", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Demo, right when it starts
+ { GID_RAMA, 12, 64950, -1, "hiliteOptText", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Demo, right when it starts
+ { GID_RAMA, 12, 64950, -1, "View", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Demo, right when it starts
+ { GID_SHIVERS, -1, 952, 0, "SoundManager", "stop", NULL, 2, { WORKAROUND_FAKE, 0 } }, // Just after Sierra logo
+ { GID_SHIVERS, -1, 64950, 0, "Feature", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // When clicking on the locked door at the beginning
+ { GID_SHIVERS, -1, 64950, 0, "View", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // When clicking on the gargoyle eye at the beginning
+ { GID_SHIVERS, 20311, 64964, 0, "DPath", "init", NULL, 1, { WORKAROUND_FAKE, 0 } }, // Just after door puzzle is solved and the metal balls start to roll
+ { GID_SHIVERS, 29260, 29260, 0, "spMars", "handleEvent", NULL, 4, { WORKAROUND_FAKE, 0 } }, // When clicking mars after seeing fortune to align earth etc...
+ { GID_SHIVERS, 29260, 29260, 0, "spVenus", "handleEvent", NULL, 4, { WORKAROUND_FAKE, 0 } }, // When clicking venus after seeing fortune to align earth etc...
+ { GID_SQ1, 103, 103, 0, "hand", "internalEvent", NULL, -1, { WORKAROUND_FAKE, 0 } }, // Spanish (and maybe early versions?) only: when moving cursor over input pad, temps 1 and 2
+ { GID_SQ1, -1, 703, 0, "", "export 1", NULL, 0, { WORKAROUND_FAKE, 0 } }, // sub that's called from several objects while on sarien battle cruiser
+ { GID_SQ1, -1, 703, 0, "firePulsar", "changeState", sig_uninitread_sq1_1, 0, { WORKAROUND_FAKE, 0 } }, // export 1, but called locally (when shooting at aliens)
+ { GID_SQ4, -1, 398, 0, "showBox", "changeState", NULL, 0, { WORKAROUND_FAKE, 0 } }, // CD: called when rummaging in Software Excess bargain bin
+ { GID_SQ4, -1, 928, -1, "Narrator", "startText", NULL, 1000, { WORKAROUND_FAKE, 1 } }, // CD: happens in the options dialog and in-game when speech and subtitles are used simultaneously
+ { GID_SQ4, -1, 708, -1, "exitBut", "doVerb", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "close" button in the sq4 hintbook - bug #6447
+ { GID_SQ4, -1, 708, -1, "", "doVerb", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "close" button... in Russian version - bug #5573
+ { GID_SQ4, -1, 708, -1, "prevBut", "doVerb", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "previous" button in the sq4 hintbook - bug #6447
+ { GID_SQ4, -1, 708, -1, "\xA8\xE6\xE3 \xAD\xA0\xA7\xA0\xA4.", "doVerb", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "previous" button... in Russian version - bug #5573
+ { GID_SQ4, -1, 708, -1, "nextBut", "doVerb", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "next" button in the sq4 hintbook - bug #6447
+ { GID_SQ4, -1, 708, -1, ".", "doVerb", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "next" button... in Russian version - bug #5573
+ { GID_SQ5, 201, 201, 0, "buttonPanel", "doVerb", NULL, 0, { WORKAROUND_FAKE, 1 } }, // when looking at the orange or red button - bug #5112
+ { GID_SQ6, -1, 0, 0, "SQ6", "init", NULL, 2, { WORKAROUND_FAKE, 0 } }, // Demo and full version: called when the game starts (demo: room 0, full: room 100)
+ { GID_SQ6, -1, 64950, -1, "Feature", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // called when pressing "Start game" in the main menu, when entering the Orion's Belt bar (room 300), and perhaps other places
+ { GID_SQ6, -1, 64964, 0, "DPath", "init", NULL, 1, { WORKAROUND_FAKE, 0 } }, // during the game
+ { GID_TORIN, -1, 64017, 0, "oFlags", "clear", NULL, 0, { WORKAROUND_FAKE, 0 } }, // entering Torin's home in the French version
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kAbs_workarounds[] = {
- { GID_HOYLE1, 1, 1, 0, "room1", "doit", -1, 0, { WORKAROUND_FAKE, 0x3e9 } }, // crazy eights - called with objects instead of integers
- { GID_HOYLE1, 2, 2, 0, "room2", "doit", -1, 0, { WORKAROUND_FAKE, 0x3e9 } }, // old maid - called with objects instead of integers
- { GID_HOYLE1, 3, 3, 0, "room3", "doit", -1, 0, { WORKAROUND_FAKE, 0x3e9 } }, // hearts - called with objects instead of integers
- { GID_QFG1VGA, -1, -1, 0, NULL, "doit", -1, 0, { WORKAROUND_FAKE, 0x3e9 } }, // when the game is patched with the NRS patch
- { GID_QFG3 , -1, -1, 0, NULL, "doit", -1, 0, { WORKAROUND_FAKE, 0x3e9 } }, // when the game is patched with the NRS patch - bugs #6042, #6043
+ { GID_HOYLE1, 1, 1, 0, "room1", "doit", NULL, 0, { WORKAROUND_FAKE, 0x3e9 } }, // crazy eights - called with objects instead of integers
+ { GID_HOYLE1, 2, 2, 0, "room2", "doit", NULL, 0, { WORKAROUND_FAKE, 0x3e9 } }, // old maid - called with objects instead of integers
+ { GID_HOYLE1, 3, 3, 0, "room3", "doit", NULL, 0, { WORKAROUND_FAKE, 0x3e9 } }, // hearts - called with objects instead of integers
+ { GID_QFG1VGA, -1, -1, 0, NULL, "doit", NULL, 0, { WORKAROUND_FAKE, 0x3e9 } }, // when the game is patched with the NRS patch
+ { GID_QFG3 , -1, -1, 0, NULL, "doit", NULL, 0, { WORKAROUND_FAKE, 0x3e9 } }, // when the game is patched with the NRS patch - bugs #6042, #6043
SCI_WORKAROUNDENTRY_TERMINATOR
};
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kCelHigh_workarounds[] = {
- { GID_KQ5, -1, 255, 0, "deathIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // english floppy: when getting beaten up in the inn and probably more, called with 2nd parameter as object - bug #5049
- { GID_PQ2, -1, 255, 0, "DIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when showing picture within windows, called with 2nd/3rd parameters as objects
- { GID_SQ1, 1, 255, 0, "DIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // DEMO: Called with 2nd/3rd parameters as objects when clicking on the menu - bug #5012
- { GID_FANMADE, -1, 979, 0, "DIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // In The Gem Scenario and perhaps other fanmade games, this is called with 2nd/3rd parameters as objects - bug #5144
+ { GID_KQ5, -1, 255, 0, "deathIcon", "setSize", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // english floppy: when getting beaten up in the inn and probably more, called with 2nd parameter as object - bug #5049
+ { GID_PQ2, -1, 255, 0, "DIcon", "setSize", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when showing picture within windows, called with 2nd/3rd parameters as objects
+ { GID_SQ1, 1, 255, 0, "DIcon", "setSize", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // DEMO: Called with 2nd/3rd parameters as objects when clicking on the menu - bug #5012
+ { GID_FANMADE, -1, 979, 0, "DIcon", "setSize", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // In The Gem Scenario and perhaps other fanmade games, this is called with 2nd/3rd parameters as objects - bug #5144
SCI_WORKAROUNDENTRY_TERMINATOR
};
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kCelWide_workarounds[] = {
- { GID_KQ5, -1, 255, 0, "deathIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // english floppy: when getting beaten up in the inn and probably more, called with 2nd parameter as object - bug #5049
- { GID_PQ2, -1, 255, 0, "DIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when showing picture within windows, called with 2nd/3rd parameters as objects
- { GID_SQ1, 1, 255, 0, "DIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // DEMO: Called with 2nd/3rd parameters as objects when clicking on the menu - bug #5012
- { GID_FANMADE, -1, 979, 0, "DIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // In The Gem Scenario and perhaps other fanmade games, this is called with 2nd/3rd parameters as objects - bug #5144
+ { GID_KQ5, -1, 255, 0, "deathIcon", "setSize", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // english floppy: when getting beaten up in the inn and probably more, called with 2nd parameter as object - bug #5049
+ { GID_PQ2, -1, 255, 0, "DIcon", "setSize", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when showing picture within windows, called with 2nd/3rd parameters as objects
+ { GID_SQ1, 1, 255, 0, "DIcon", "setSize", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // DEMO: Called with 2nd/3rd parameters as objects when clicking on the menu - bug #5012
+ { GID_FANMADE, -1, 979, 0, "DIcon", "setSize", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // In The Gem Scenario and perhaps other fanmade games, this is called with 2nd/3rd parameters as objects - bug #5144
SCI_WORKAROUNDENTRY_TERMINATOR
};
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kDeleteKey_workarounds[] = {
- { GID_HOYLE4, 300, 999, 0, "handleEventList", "delete", -1, 0, { WORKAROUND_IGNORE, 0 } }, // restarting hearts, while tray is shown - bug #6604
- { GID_HOYLE4, 500, 999, 0, "handleEventList", "delete", -1, 0, { WORKAROUND_IGNORE, 0 } }, // restarting cribbage, while tray is shown - bug #6604
- { GID_HOYLE4, 975, 999, 0, "handleEventList", "delete", -1, 0, { WORKAROUND_IGNORE, 0 } }, // going back to gamelist from hearts/cribbage, while tray is shown - bug #6604
- SCI_WORKAROUNDENTRY_TERMINATOR
-};
-
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+ { GID_HOYLE4, 300, 999, 0, "handleEventList", "delete", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // restarting hearts, while tray is shown - bug #6604
+ { GID_HOYLE4, 500, 999, 0, "handleEventList", "delete", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // restarting cribbage, while tray is shown - bug #6604
+ { GID_HOYLE4, 975, 999, 0, "handleEventList", "delete", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // going back to gamelist from hearts/cribbage, while tray is shown - bug #6604
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// Game: Fan-Made games (SCI Studio)
+// Calling method: Game::save, Game::restore
+// Subroutine offset: Al Pond 2: 0x0e5c (ldi 0001)
+// Black Cauldron: 0x000a (ldi 01)
+// Cascade Quest: 0x0d1c (ldi 0001)
+// Demo Quest: 0x0e55 (ldi 0001)
+// I want my C64 back: 0x0e57 (ldi 0001)
+// Applies to at least: games listed above
+static const uint16 sig_kDeviceInfo_Fanmade_1[] = {
+ 0x3f, 0x79, // link 79h
+ 0x34, SIG_UINT16(0x0001), // ldi 0001
+ 0xa5, 0x00, // sat temp[0]
+ SIG_END
+};
+static const uint16 sig_kDeviceInfo_Fanmade_2[] = {
+ 0x3f, 0x79, // link 79h
+ 0x35, 0x01, // ldi 01
+ 0xa5, 0x00, // sat temp[0]
+ SIG_END
+};
+
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kDeviceInfo_workarounds[] = {
- { GID_FANMADE, -1, 994, 1, "Game", "save", 0xd1c, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games, this is called with one parameter for CurDevice (Cascade Quest)
- { GID_FANMADE, -1, 994, 1, "Game", "save", 0xe55, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games, this is called with one parameter for CurDevice (Demo Quest)
- { GID_FANMADE, -1, 994, 1, "Game", "save", 0xe57, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games, this is called with one parameter for CurDevice (I Want My C64 Back)
- { GID_FANMADE, -1, 994, 0, "Black", "save", 0xa, 0, { WORKAROUND_IGNORE, 0 } }, // In fanmade games, this is called with one parameter for CurDevice (Black Cauldron Remake)
- { GID_FANMADE, -1, 994, 1, "Game", "save", 0xe5c, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games, this is called with one parameter for CurDevice (Most of them)
- { GID_FANMADE, -1, 994, 1, "Game", "restore", 0xd1c, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games, this is called with one parameter for CurDevice (Cascade Quest)
- { GID_FANMADE, -1, 994, 1, "Game", "restore", 0xe55, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games, this is called with one parameter for CurDevice (Demo Quest)
- { GID_FANMADE, -1, 994, 1, "Game", "restore", 0xe57, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games, this is called with one parameter for CurDevice (I Want My C64 Back)
- { GID_FANMADE, -1, 994, 1, "Game", "restore", 0xe5c, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games, this is called with one parameter for CurDevice (Most of them)
- SCI_WORKAROUNDENTRY_TERMINATOR
-};
-
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+ { GID_FANMADE, -1, 994, 1, "Game", "save", sig_kDeviceInfo_Fanmade_1, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games (SCI Studio), this is called with one parameter for CurDevice LDI 01 variant
+ { GID_FANMADE, -1, 994, 1, "Game", "save", sig_kDeviceInfo_Fanmade_2, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games (SCI Studio), this is called with one parameter for CurDevice LDI 0001 variant
+ { GID_FANMADE, -1, 994, 0, "Black", "save", sig_kDeviceInfo_Fanmade_1, 0, { WORKAROUND_IGNORE, 0 } }, // In fanmade games (SCI Studio), this is called with one parameter for CurDevice (Black Cauldron Remake)
+ { GID_FANMADE, -1, 994, 1, "Game", "restore", sig_kDeviceInfo_Fanmade_1, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games (SCI Studio), this is called with one parameter for CurDevice LDI 01 variant
+ { GID_FANMADE, -1, 994, 1, "Game", "restore", sig_kDeviceInfo_Fanmade_2, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games (SCI Studio), this is called with one parameter for CurDevice LDI 0001 variant
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// Game: Police Quest 2
+// Calling method: rm23Script::elements
+// Subroutine offset: English 1.001.000: 0x04ae, English 1.002.011: 0x04ca, Japanese: 0x04eb (script 23)
+// Applies to at least: English PC floppy, Japanese PC-9801
+static const uint16 sig_kDisplay_pq2_1[] = {
+ 0x35, 0x00, // ldi 00
+ 0xa3, 0x09, // sal local[9]
+ 0x35, 0x01, // ldi 01
+ 0xa3, 0x0a, // sal local[0Ah]
+ 0x38, SIG_ADDTOOFFSET(+2), // pushi selector[drawPic] TODO: implement selectors
+ 0x7a, // push2
+ 0x39, 0x5a, // pushi 5Ah
+ 0x7a, // push2
+ 0x81, 0x02, // lag global[2]
+ 0x4a, 0x08, // send 08
+ SIG_END
+};
+
+// Game: Space Quest 4
+// Calling method: doCatalog::mode
+// Subroutine offset: English PC CD: 0x0084 (script 391)
+// Applies to at least: English PC CD
+static const uint16 sig_kDisplay_sq4_1[] = {
+ 0x38, SIG_UINT16(0x0187), // pushi 0187h (drawPic)
+ 0x78, // push1
+ 0x38, SIG_UINT16(0x0189), // pushi 0189h (reflectPosn)
+ 0x81, 0x02, // lag global[2]
+ 0x4a, 0x06, // send 06
+ SIG_END
+};
+
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kDisplay_workarounds[] = {
- { GID_ISLANDBRAIN, 300, 300, 0, "geneDude", "show", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when looking at the gene explanation chart - a parameter is an object
- { GID_PQ2, 23, 23, 0, "rm23Script", "elements", 0x4ae, 0, { WORKAROUND_IGNORE, 0 } }, // when looking at the 2nd page of pate's file - 0x75 as id
- { GID_PQ2, 23, 23, 0, "rm23Script", "elements", 0x4c1, 0, { WORKAROUND_IGNORE, 0 } }, // when looking at the 2nd page of pate's file - 0x75 as id (another pq2 version, bug #5223)
- { GID_QFG1, 11, 11, 0, "battle", "<noname90>", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: When entering battle, 0x75 as id
- { GID_SQ4, 397, 0, 0, "", "export 12", -1, 0, { WORKAROUND_IGNORE, 0 } }, // FLOPPY: when going into the computer store - bug #5227
- { GID_SQ4, 391, 391, 0, "doCatalog", "mode", 0x84, 0, { WORKAROUND_IGNORE, 0 } }, // CD: clicking on catalog in roboter sale - a parameter is an object
- { GID_SQ4, 391, 391, 0, "choosePlug", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // CD: ordering connector in roboter sale - a parameter is an object
+ { GID_ISLANDBRAIN, 300, 300, 0, "geneDude", "show", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // when looking at the gene explanation chart - a parameter is an object
+ { GID_LONGBOW, 95, 95, 0, "countDown", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during title screen "Robin Hood! Your bow is needed"
+ { GID_LONGBOW, 220, 220, 0, "moveOn", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during second room "Outwit and outfight..."
+ { GID_LONGBOW, 210, 210, 0, "mama", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during third room "Fall under the spell..."
+ { GID_LONGBOW, 320, 320, 0, "flyin", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during fourth room "Conspiracies, love..."
+ { GID_PQ2, 23, 23, 0, "rm23Script", "elements", sig_kDisplay_pq2_1, 0, { WORKAROUND_IGNORE, 0 } }, // when looking at the 2nd page of pate's file - 0x75 as id - bug #5223
+ { GID_QFG1, 11, 11, 0, "battle", "init", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: When entering battle, 0x75 as id
+ { GID_SQ4, 397, 0, 0, "", "export 12", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // FLOPPY: when going into the computer store - bug #5227
+ { GID_SQ4, 391, 391, 0, "doCatalog", "mode", sig_kDisplay_sq4_1, 0, { WORKAROUND_IGNORE, 0 } }, // CD: clicking on catalog in roboter sale - a parameter is an object
+ { GID_SQ4, 391, 391, 0, "choosePlug", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // CD: ordering connector in roboter sale - a parameter is an object
SCI_WORKAROUNDENTRY_TERMINATOR
};
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kDirLoop_workarounds[] = {
- { GID_KQ4, 4, 992, 0, "Avoid", "doit", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when the ogre catches you in front of his house, second parameter points to the same object as the first parameter, instead of being an integer (the angle) - bug #5217
+ { GID_KQ4, 4, 992, 0, "Avoid", "doit", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // when the ogre catches you in front of his house, second parameter points to the same object as the first parameter, instead of being an integer (the angle) - bug #5217
SCI_WORKAROUNDENTRY_TERMINATOR
};
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kDisposeScript_workarounds[] = {
- { GID_LAURABOW, 777, 777, 0, "myStab", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: after the will is signed, parameter 0 is an object - bug #4967
- { GID_QFG1, -1, 64, 0, "rm64", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when leaving graveyard, parameter 0 is an object
- { GID_SQ4, 150, 151, 0, "fightScript", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during fight with Vohaul, parameter 0 is an object
- { GID_SQ4, 150, 152, 0, "driveCloseUp", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when choosing "beam download", parameter 0 is an object
+ { GID_LAURABOW, 777, 777, 0, "myStab", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: after the will is signed, parameter 0 is an object - bug #4967
+ { GID_LSL2, -1, 54, 0, "rm54", "dispose", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // Amiga: room 55, script tries to kDisposeScript an object (does not happen for DOS) - bug #6818
+ { GID_QFG1, -1, 64, 0, "rm64", "dispose", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // when leaving graveyard, parameter 0 is an object
+ { GID_SQ4, 150, 151, 0, "fightScript", "dispose", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // during fight with Vohaul, parameter 0 is an object
+ { GID_SQ4, 150, 152, 0, "driveCloseUp", "dispose", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // when choosing "beam download", parameter 0 is an object
+ { GID_SQ4, 150, 152, 0, "", "dispose", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // when choosing "beam download"... in Russian version - bug #5573
SCI_WORKAROUNDENTRY_TERMINATOR
};
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kDoSoundFade_workarounds[] = {
- { GID_KQ5, 213, 989, 0, "globalSound3", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // english floppy: when bandits leave the secret temple, parameter 4 is an object - bug #5078
- { GID_KQ6, 105, 989, 0, "globalSound", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // floppy: during intro, parameter 4 is an object
- { GID_KQ6, 460, 989, 0, "globalSound2", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // after pulling the black widow's web on the isle of wonder, parameter 4 is an object - bug #4954
- { GID_QFG4, -1, 64989, 0, "longSong", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // CD version: many places, parameter 4 is an object (longSong)
- { GID_SQ5, 800, 989, 0, "sq5Music1", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when cutting the wrong part of Goliath with the laser - bug #6341
+ { GID_KQ5, 213, 989, 0, "globalSound3", "fade", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // english floppy: when bandits leave the secret temple, parameter 4 is an object - bug #5078
+ { GID_KQ6, 105, 989, 0, "globalSound", "fade", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // floppy: during intro, parameter 4 is an object
+ { GID_KQ6, 460, 989, 0, "globalSound2", "fade", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // after pulling the black widow's web on the isle of wonder, parameter 4 is an object - bug #4954
+ { GID_QFG4, -1, 64989, 0, "longSong", "fade", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // CD version: many places, parameter 4 is an object (longSong)
+ { GID_SQ5, 800, 989, 0, "sq5Music1", "fade", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when cutting the wrong part of Goliath with the laser - bug #6341
SCI_WORKAROUNDENTRY_TERMINATOR
};
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kGetAngle_workarounds[] = {
- { GID_FANMADE, 516, 992, 0, "Motion", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // The Legend of the Lost Jewel Demo (fan made): called with third/fourth parameters as objects
- { GID_KQ6, -1, 752, 0, "throwDazzle", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // room 740/790 after the Genie is exposed in the Palace (short and long ending), it starts shooting lightning bolts around. An extra 5th parameter is passed - bug #4959 & #5203
- { GID_SQ1, -1, 927, 0, "PAvoider", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // all rooms in Ulence Flats after getting the Pilot Droid: called with a single parameter when the droid is in Roger's path - bug #6016
+ { GID_FANMADE, 516, 992, 0, "Motion", "init", NULL, 0, { WORKAROUND_FAKE, 0 } }, // The Legend of the Lost Jewel Demo (fan made): called with third/fourth parameters as objects
+ { GID_KQ6, -1, 752, 0, "throwDazzle", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // room 740/790 after the Genie is exposed in the Palace (short and long ending), it starts shooting lightning bolts around. An extra 5th parameter is passed - bug #4959 & #5203
+ { GID_SQ1, -1, 927, 0, "PAvoider", "doit", NULL, 0, { WORKAROUND_FAKE, 0 } }, // all rooms in Ulence Flats after getting the Pilot Droid: called with a single parameter when the droid is in Roger's path - bug #6016
SCI_WORKAROUNDENTRY_TERMINATOR
};
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kFindKey_workarounds[] = {
- { GID_ECOQUEST2, 100, 999, 0, "myList", "contains", -1, 0, { WORKAROUND_FAKE, 0 } }, // When Noah Greene gives Adam the Ecorder, and just before the game gives a demonstration, a null reference to a list is passed - bug #4987
- { GID_HOYLE4, 300, 999, 0, "Piles", "contains", -1, 0, { WORKAROUND_FAKE, 0 } }, // When passing the three cards in Hearts, a null reference to a list is passed - bug #5664
+ { GID_ECOQUEST2, 100, 999, 0, "myList", "contains", NULL, 0, { WORKAROUND_FAKE, 0 } }, // When Noah Greene gives Adam the Ecorder, and just before the game gives a demonstration, a null reference to a list is passed - bug #4987
+ { GID_HOYLE4, 300, 999, 0, "Piles", "contains", NULL, 0, { WORKAROUND_FAKE, 0 } }, // When passing the three cards in Hearts, a null reference to a list is passed - bug #5664
SCI_WORKAROUNDENTRY_TERMINATOR
};
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kGraphDrawLine_workarounds[] = {
- { GID_ISLANDBRAIN, 300, 300, 0, "dudeViewer", "show", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when looking at the gene explanation chart, gets called with 1 extra parameter
- { GID_SQ1, 43, 43, 0, "someoneDied", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when ordering beer, gets called with 1 extra parameter
- { GID_SQ1, 71, 71, 0, "destroyXenon", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // during the Xenon destruction cutscene (which results in death), gets called with 1 extra parameter - bug #5176
- { GID_SQ1, 53, 53, 0, "blastEgo", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when Roger is found and zapped by the cleaning robot, gets called with 1 extra parameter - bug #5177
+ { GID_ISLANDBRAIN, 300, 300, 0, "dudeViewer", "show", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when looking at the gene explanation chart, gets called with 1 extra parameter
+ { GID_SQ1, 43, 43, 0, "someoneDied", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when ordering beer, gets called with 1 extra parameter
+ { GID_SQ1, 71, 71, 0, "destroyXenon", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // during the Xenon destruction cutscene (which results in death), gets called with 1 extra parameter - bug #5176
+ { GID_SQ1, 53, 53, 0, "blastEgo", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when Roger is found and zapped by the cleaning robot, gets called with 1 extra parameter - bug #5177
SCI_WORKAROUNDENTRY_TERMINATOR
};
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+// Game: Island of Dr. Brain
+// Calling method: upElevator::changeState, downElevator::changeState, correctElevator::changeState
+// Subroutine offset: 0x201f (script 291)
+// Applies to at least: English PC floppy
+static const uint16 sig_kGraphSaveBox_ibrain_1[] = {
+ 0x3f, 0x01, // link 01
+ 0x87, 0x01, // lap param[1]
+ 0x30, SIG_UINT16(0x0043), // bnt [...]
+ 0x76, // push0
+ SIG_END
+};
+
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kGraphSaveBox_workarounds[] = {
- { GID_CASTLEBRAIN, 420, 427, 0, "alienIcon", "select", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when selecting a card during the alien card game, gets called with 1 extra parameter
- { GID_ISLANDBRAIN, 290, 291, 0, "upElevator", "changeState",0x201f, 0, { WORKAROUND_STILLCALL, 0 } }, // when testing in the elevator puzzle, gets called with 1 argument less - 15 is on stack - bug #4943
- { GID_ISLANDBRAIN, 290, 291, 0, "downElevator", "changeState",0x201f, 0, { WORKAROUND_STILLCALL, 0 } }, // see above
- { GID_ISLANDBRAIN, 290, 291, 0, "correctElevator", "changeState",0x201f, 0, { WORKAROUND_STILLCALL, 0 } }, // see above (when testing the correct solution)
- { GID_PQ3, 202, 202, 0, "MapEdit", "movePt", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #5099
+ { GID_CASTLEBRAIN, 420, 427, 0, "alienIcon", "select", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when selecting a card during the alien card game, gets called with 1 extra parameter
+ { GID_ISLANDBRAIN, 290, 291, 0, "upElevator", "changeState", sig_kGraphSaveBox_ibrain_1, 0, { WORKAROUND_STILLCALL, 0 } }, // when testing in the elevator puzzle, gets called with 1 argument less - 15 is on stack - bug #4943
+ { GID_ISLANDBRAIN, 290, 291, 0, "downElevator", "changeState", sig_kGraphSaveBox_ibrain_1, 0, { WORKAROUND_STILLCALL, 0 } }, // see above
+ { GID_ISLANDBRAIN, 290, 291, 0, "correctElevator", "changeState", sig_kGraphSaveBox_ibrain_1, 0, { WORKAROUND_STILLCALL, 0 } }, // see above (when testing the correct solution)
+ { GID_PQ3, 202, 202, 0, "MapEdit", "movePt", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #5099
SCI_WORKAROUNDENTRY_TERMINATOR
};
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kGraphRestoreBox_workarounds[] = {
- { GID_LSL6, -1, 86, 0, "LL6Inv", "hide", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens during the game, gets called with 1 extra parameter
+ { GID_LSL6, -1, 86, 0, "LL6Inv", "hide", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // happens during the game, gets called with 1 extra parameter
SCI_WORKAROUNDENTRY_TERMINATOR
};
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kGraphFillBoxForeground_workarounds[] = {
- { GID_LSL6, -1, 0, 0, "LSL6", "hideControls", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens when giving the bungee key to merrily (room 240) and at least in room 650 too - gets called with additional 5th parameter
+ { GID_LSL6, -1, 0, 0, "LSL6", "hideControls", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // happens when giving the bungee key to merrily (room 240) and at least in room 650 too - gets called with additional 5th parameter
SCI_WORKAROUNDENTRY_TERMINATOR
};
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kGraphFillBoxAny_workarounds[] = {
- { GID_ECOQUEST2, 100, 333, 0, "showEcorder", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // necessary workaround for our ecorder script patch, because there isn't enough space to patch the function
- { GID_SQ4, -1, 818, 0, "iconTextSwitch", "show", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // CD: game menu "text/speech" display - parameter 5 is missing, but the right color number is on the stack
+ { GID_ECOQUEST2, 100, 333, 0, "showEcorder", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // necessary workaround for our ecorder script patch, because there isn't enough space to patch the function
+ { GID_SQ4, -1, 818, 0, "iconTextSwitch", "show", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // CD: game menu "text/speech" display - parameter 5 is missing, but the right color number is on the stack
SCI_WORKAROUNDENTRY_TERMINATOR
};
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+// Game: Space Quest 4
+// Calling method: laserScript::changeState
+// Subroutine offset: English/German/French/Russian PC floppy, Japanese PC-9801: 0x0016, English PC CD: 0x00b2 (script 150)
+// Applies to at least: English/German/French/Russian PC floppy, English PC CD, Japanese PC-9801
+static const uint16 sig_kGraphRedrawBox_sq4_1[] = {
+ 0x3f, 0x07, // link 07
+ 0x39, SIG_ADDTOOFFSET(+1), // pushi 2Ah for PC floppy, pushi 27h for PC CD
+ 0x76, // push0
+ 0x72, // lofsa laserSound
+ SIG_END
+};
+
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kGraphRedrawBox_workarounds[] = {
- { GID_SQ4, 405, 405, 0, "swimAfterEgo", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified
- { GID_SQ4, 406, 406, 0, "egoFollowed", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // FLOPPY: when getting shot by the police - accidental additional parameter specified
- { GID_SQ4, 406, 406, 0, "swimAndShoot", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified
- { GID_SQ4, 410, 410, 0, "swimAfterEgo", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified
- { GID_SQ4, 411, 411, 0, "swimAndShoot", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified
- { GID_SQ4, 150, 150, 0, "laserScript", "changeState", 0xb2, 0, { WORKAROUND_STILLCALL, 0 } }, // when visiting the pedestral where Roger Jr. is trapped, before trashing the brain icon in the programming chapter, accidental additional parameter specified - bug #5479
- { GID_SQ4, 150, 150, 0, "laserScript", "changeState", 0x16, 0, { WORKAROUND_STILLCALL, 0 } }, // same as above, for the German version - bug #5527
- { GID_SQ4, -1, 704, 0, "shootEgo", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // When shot by Droid in Super Computer Maze (Rooms 500, 505, 510...) - accidental additional parameter specified
- { GID_KQ5, -1, 981, 0, "myWindow", "dispose", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when closing any dialog box, accidental additional parameter specified - bug #5031
- { GID_KQ5, -1, 995, 0, "invW", "doit", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when closing the inventory window, accidental additional parameter specified
- { GID_KQ5, -1, 995, 0, "", "export 0", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when opening the gem pouch, accidental additional parameter specified - bug #5138
- { GID_KQ5, -1, 403, 0, "KQ5Window", "dispose", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the FM Towns version when closing any dialog box, accidental additional parameter specified
- SCI_WORKAROUNDENTRY_TERMINATOR
-};
-
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+ { GID_SQ4, 405, 405, 0, "swimAfterEgo", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified
+ { GID_SQ4, 405, 405, 0, "", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air... Russian version - bug #5573
+ { GID_SQ4, 406, 406, 0, "egoFollowed", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // FLOPPY: when getting shot by the police - accidental additional parameter specified
+ { GID_SQ4, 406, 406, 0, "swimAndShoot", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified
+ { GID_SQ4, 406, 406, 0, "", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air... Russian version - bug #5573 (is for both egoFollowed and swimAndShoot)
+ { GID_SQ4, 410, 410, 0, "swimAfterEgo", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified
+ { GID_SQ4, 410, 410, 0, "", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air... Russian version - bug #5573
+ { GID_SQ4, 411, 411, 0, "swimAndShoot", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified
+ { GID_SQ4, 411, 411, 0, "", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air... Russian version - bug #5573
+ { GID_SQ4, 150, 150, 0, "laserScript", "changeState", sig_kGraphRedrawBox_sq4_1, 0, { WORKAROUND_STILLCALL, 0 } }, // when visiting the pedestral where Roger Jr. is trapped, before trashing the brain icon in the programming chapter, accidental additional parameter specified - bug #5479, German - bug #5527
+ { GID_SQ4, 150, 150, 0, "", "changeState", sig_kGraphRedrawBox_sq4_1, 0, { WORKAROUND_STILLCALL, 0 } }, // same as above, for the Russian version - bug #5573
+ { GID_SQ4, -1, 704, 0, "shootEgo", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // When shot by Droid in Super Computer Maze (Rooms 500, 505, 510...) - accidental additional parameter specified
+ { GID_KQ5, -1, 981, 0, "myWindow", "dispose", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when closing any dialog box, accidental additional parameter specified - bug #5031
+ { GID_KQ5, -1, 995, 0, "invW", "doit", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when closing the inventory window, accidental additional parameter specified
+ { GID_KQ5, -1, 995, 0, "", "export 0", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when opening the gem pouch, accidental additional parameter specified - bug #5138
+ { GID_KQ5, -1, 403, 0, "KQ5Window", "dispose", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the FM Towns version when closing any dialog box, accidental additional parameter specified
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kGraphUpdateBox_workarounds[] = {
- { GID_ECOQUEST2, 100, 333, 0, "showEcorder", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // necessary workaround for our ecorder script patch, because there isn't enough space to patch the function
- { GID_PQ3, 202, 202, 0, "MapEdit", "addPt", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #5099
- { GID_PQ3, 202, 202, 0, "MapEdit", "movePt", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #5099
- { GID_PQ3, 202, 202, 0, "MapEdit", "dispose", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters
+ { GID_ECOQUEST2, 100, 333, 0, "showEcorder", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // necessary workaround for our ecorder script patch, because there isn't enough space to patch the function
+ { GID_PQ3, 202, 202, 0, "MapEdit", "addPt", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #5099
+ { GID_PQ3, 202, 202, 0, "MapEdit", "movePt", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #5099
+ { GID_PQ3, 202, 202, 0, "MapEdit", "dispose", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters
SCI_WORKAROUNDENTRY_TERMINATOR
};
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kIsObject_workarounds[] = {
- { GID_GK1, 50, 999, 0, "List", "eachElementDo", -1, 0, { WORKAROUND_FAKE, 0 } }, // GK1 demo, when asking Grace for messages it gets called with an invalid parameter (type "error") - bug #4950
- { GID_ISLANDBRAIN, -1, 999, 0, "List", "eachElementDo", -1, 0, { WORKAROUND_FAKE, 0 } }, // when going to the game options, choosing "Info" and selecting anything from the list, gets called with an invalid parameter (type "error") - bug #4989
- { GID_QFG3, -1, 999, 0, "List", "eachElementDo", -1, 0, { WORKAROUND_FAKE, 0 } }, // when asking for something, gets called with type error parameter
+ { GID_GK1, 50, 999, 0, "List", "eachElementDo", NULL, 0, { WORKAROUND_FAKE, 0 } }, // GK1 demo, when asking Grace for messages it gets called with an invalid parameter (type "error") - bug #4950
+ { GID_ISLANDBRAIN, -1, 999, 0, "List", "eachElementDo", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when going to the game options, choosing "Info" and selecting anything from the list, gets called with an invalid parameter (type "error") - bug #4989
+ { GID_QFG3, -1, 999, 0, "List", "eachElementDo", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when asking for something, gets called with type error parameter
SCI_WORKAROUNDENTRY_TERMINATOR
};
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kMemory_workarounds[] = {
- { GID_LAURABOW2, -1, 999, 0, "", "export 6", -1, 0, { WORKAROUND_FAKE, 0 } }, // during the intro, when exiting the train (room 160), talking to Mr. Augustini, etc. - bug #4944
- { GID_SQ1, -1, 999, 0, "", "export 6", -1, 0, { WORKAROUND_FAKE, 0 } }, // during walking Roger around Ulence Flats - bug #6017
+ { GID_LAURABOW2, -1, 999, 0, "", "export 6", NULL, 0, { WORKAROUND_FAKE, 0 } }, // during the intro, when exiting the train (room 160), talking to Mr. Augustini, etc. - bug #4944
+ { GID_SQ1, -1, 999, 0, "", "export 6", NULL, 0, { WORKAROUND_FAKE, 0 } }, // during walking Roger around Ulence Flats - bug #6017
SCI_WORKAROUNDENTRY_TERMINATOR
};
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kMoveCursor_workarounds[] = {
- { GID_KQ5, -1, 937, 0, "IconBar", "handleEvent", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when pressing escape to open the menu, gets called with one parameter instead of 2 - bug #5575
+ { GID_KQ5, -1, 937, 0, "IconBar", "handleEvent", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // when pressing escape to open the menu, gets called with one parameter instead of 2 - bug #5575
SCI_WORKAROUNDENTRY_TERMINATOR
};
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kNewWindow_workarounds[] = {
- { GID_ECOQUEST, -1, 981, 0, "SysWindow", "open", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // EcoQuest 1 demo uses an in-between interpreter from SCI1 to SCI1.1. It's SCI1.1, but uses the SCI1 semantics for this call - bug #4976
+ { GID_ECOQUEST, -1, 981, 0, "SysWindow", "open", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // EcoQuest 1 demo uses an in-between interpreter from SCI1 to SCI1.1. It's SCI1.1, but uses the SCI1 semantics for this call - bug #4976
SCI_WORKAROUNDENTRY_TERMINATOR
};
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kReadNumber_workarounds[] = {
- { GID_CNICK_LAURABOW,100, 101, 0, "dominoes.opt", "doit", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // When dominoes.opt is present, the game scripts call kReadNumber with an extra integer parameter - bug #6425
- { GID_HOYLE3, 100, 101, 0, "dominoes.opt", "doit", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // When dominoes.opt is present, the game scripts call kReadNumber with an extra integer parameter - bug #6425
+ { GID_CNICK_LAURABOW,100, 101, 0, "dominoes.opt", "doit", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // When dominoes.opt is present, the game scripts call kReadNumber with an extra integer parameter - bug #6425
+ { GID_HOYLE3, 100, 101, 0, "dominoes.opt", "doit", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // When dominoes.opt is present, the game scripts call kReadNumber with an extra integer parameter - bug #6425
SCI_WORKAROUNDENTRY_TERMINATOR
};
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kPaletteUnsetFlag_workarounds[] = {
- { GID_QFG4, 100, 100, 0, "doMovie", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // after the Sierra logo, no flags are passed, thus the call is meaningless - bug #4947
+ { GID_QFG4, 100, 100, 0, "doMovie", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // after the Sierra logo, no flags are passed, thus the call is meaningless - bug #4947
SCI_WORKAROUNDENTRY_TERMINATOR
};
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kSetCursor_workarounds[] = {
- { GID_KQ5, -1, 768, 0, "KQCursor", "init", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // CD: gets called with 4 additional "900d" parameters
+ { GID_KQ5, -1, 768, 0, "KQCursor", "init", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // CD: gets called with 4 additional "900d" parameters
SCI_WORKAROUNDENTRY_TERMINATOR
};
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kSetPort_workarounds[] = {
- { GID_LSL6, 740, 740, 0, "rm740", "drawPic", -1, 0, { WORKAROUND_IGNORE, 0 } }, // ending scene, is called with additional 3 (!) parameters
- { GID_QFG3, 830, 830, 0, "portalOpens", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when the portal appears during the end, gets called with 4 parameters - bug #5174
+ { GID_LSL6, 740, 740, 0, "rm740", "drawPic", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // ending scene, is called with additional 3 (!) parameters
+ { GID_QFG3, 830, 830, 0, "portalOpens", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // when the portal appears during the end, gets called with 4 parameters - bug #5174
SCI_WORKAROUNDENTRY_TERMINATOR
};
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+// Game: Island of Dr. Brain
+// Calling method: childBreed::changeState
+// Subroutine offset: 0x1c7c (script 310)
+// Applies to at least: English PC floppy
+static const uint16 sig_kStrAt_ibrain_1[] = {
+ 0x3f, 0x16, // link 16
+ 0x78, // push1
+ 0x8f, 0x01, // lsp param[1]
+ 0x43, // callk StrLen
+ SIG_END
+};
+
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kStrAt_workarounds[] = {
- { GID_CASTLEBRAIN, 220, 220, 0, "robotJokes", "animateOnce", -1, 0, { WORKAROUND_FAKE, 0 } }, // when trying to view the terminal at the end of the maze without having collected any robot jokes - bug #5127
- { GID_ISLANDBRAIN, 300, 310, 0, "childBreed", "changeState",0x1c7c, 0, { WORKAROUND_FAKE, 0 } }, // when clicking Breed to get the second-generation cyborg hybrid (Standard difficulty), the two parameters are swapped - bug #5088
+ { GID_CASTLEBRAIN, 220, 220, 0, "robotJokes", "animateOnce", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when trying to view the terminal at the end of the maze without having collected any robot jokes - bug #5127
+ { GID_ISLANDBRAIN, 300, 310, 0, "childBreed", "changeState", sig_kStrAt_ibrain_1, 0, { WORKAROUND_FAKE, 0 } }, // when clicking Breed to get the second-generation cyborg hybrid (Standard difficulty), the two parameters are swapped - bug #5088
SCI_WORKAROUNDENTRY_TERMINATOR
};
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kStrCpy_workarounds[] = {
- { GID_MOTHERGOOSE, 23, 23, 0, "talkScript", "changeState", -1, 0, { WORKAROUND_FAKE, 0 } }, // when talking to the girl in scene 23, there's no destination parameter (script bug - wrong instruction order). The original source is used directly afterwards in kDisplay, to show the girl's text - bug #6485
+ { GID_MOTHERGOOSE, 23, 23, 0, "talkScript", "changeState", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when talking to the girl in scene 23, there's no destination parameter (script bug - wrong instruction order). The original source is used directly afterwards in kDisplay, to show the girl's text - bug #6485
SCI_WORKAROUNDENTRY_TERMINATOR
};
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+// Game: Quest for Glory 2
+// Calling method: export 21 of script 2
+// Subroutine offset: English 0x0deb (script 2)
+// Applies to at least: English PC floppy
+static const uint16 sig_kStrLen_qfg2_1[] = {
+ 0x3f, 0x04, // link 04
+ 0x78, // push1
+ 0x8f, 0x02, // lsp param[2]
+ 0x43, // callk StrLen
+ SIG_END
+};
+
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kStrLen_workarounds[] = {
- { GID_QFG2, 210, 2, 0, "", "export 21", 0xdeb, 0, { WORKAROUND_FAKE, 0 } }, // When saying something incorrect at the WIT, an integer is passed instead of a reference - bug #5489
+ { GID_QFG2, 210, 2, 0, "", "export 21", sig_kStrLen_qfg2_1, 0, { WORKAROUND_FAKE, 0 } }, // When saying something incorrect at the WIT, an integer is passed instead of a reference - bug #5489
SCI_WORKAROUNDENTRY_TERMINATOR
};
-// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kUnLoad_workarounds[] = {
- { GID_ECOQUEST, 380, 61, 0, "gotIt", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // CD version: after talking to the dolphin the first time, a 3rd parameter is passed by accident
- { GID_ECOQUEST, 380, 69, 0, "lookAtBlackBoard", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // German version, when closing the blackboard closeup in the dolphin room, a 3rd parameter is passed by accident - bug #5483
- { GID_LAURABOW2, -1, -1, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during the intro, a 3rd parameter is passed by accident - bug #4966
- { GID_LSL6, 130, 130, 0, "recruitLarryScr", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during intro, a 3rd parameter is passed by accident
- { GID_LSL6, 740, 740, 0, "showCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during ending, 4 additional parameters are passed by accident
- { GID_LSL6HIRES, 130, 130, 0, "recruitLarryScr", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during intro, a 3rd parameter is passed by accident
- { GID_SQ1, 43, 303, 0, "slotGuy", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when leaving ulence flats bar, parameter 1 is not passed - script error
- { GID_QFG4, -1, 110, 0, "dreamer", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during the dream sequence, a 3rd parameter is passed by accident
+ { GID_ECOQUEST, 380, 61, 0, "gotIt", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // CD version: after talking to the dolphin the first time, a 3rd parameter is passed by accident
+ { GID_ECOQUEST, 380, 69, 0, "lookAtBlackBoard", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // German version, when closing the blackboard closeup in the dolphin room, a 3rd parameter is passed by accident - bug #5483
+ { GID_LAURABOW2, -1, -1, 0, "sCartoon", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during the intro, a 3rd parameter is passed by accident - bug #4966
+ { GID_LSL6, 130, 130, 0, "recruitLarryScr", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // during intro, a 3rd parameter is passed by accident
+ { GID_LSL6, 740, 740, 0, "showCartoon", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // during ending, 4 additional parameters are passed by accident
+ { GID_LSL6HIRES, 130, 130, 0, "recruitLarryScr", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // during intro, a 3rd parameter is passed by accident
+ { GID_SQ1, 43, 303, 0, "slotGuy", "dispose", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // when leaving ulence flats bar, parameter 1 is not passed - script error
+ { GID_QFG4, -1, 110, 0, "dreamer", "dispose", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // during the dream sequence, a 3rd parameter is passed by accident
SCI_WORKAROUNDENTRY_TERMINATOR
};
@@ -461,8 +751,9 @@ SciWorkaroundSolution trackOriginAndFindWorkaround(int index, const SciWorkaroun
ExecStack *lastCall = state->xs;
const Script *localScript = state->_segMan->getScriptIfLoaded(lastCall->local_segment);
int curScriptNr = localScript->getScriptNumber();
+ int curLocalCallOffset = lastCall->debugLocalCallOffset;
- if (lastCall->debugLocalCallOffset != -1) {
+ if (curLocalCallOffset != -1) {
// if lastcall was actually a local call search back for a real call
Common::List<ExecStack>::const_iterator callIterator = state->_executionStack.end();
while (callIterator != state->_executionStack.begin()) {
@@ -494,19 +785,17 @@ SciWorkaroundSolution trackOriginAndFindWorkaround(int index, const SciWorkaroun
// Search if there is a workaround for this one
const SciWorkaroundEntry *workaround;
int16 inheritanceLevel = 0;
- Common::String searchObjectName = curObjectName;
+ Common::String searchObjectName = g_sci->getSciLanguageString(curObjectName, K_LANG_ENGLISH);
reg_t searchObject = lastCall->sendp;
+ const byte *curScriptPtr = NULL;
+ uint32 curScriptSize = 0;
+ bool matched = false;
+
do {
workaround = workaroundList;
while (workaround->methodName) {
bool objectNameMatches = (workaround->objectName == NULL) ||
- (workaround->objectName == g_sci->getSciLanguageString(searchObjectName, K_LANG_ENGLISH));
-
- // Special case: in the fanmade Russian translation of SQ4, all
- // of the object names have been deleted or renamed to Russian,
- // thus we disable checking of the object name. Fixes bug #5573.
- if (g_sci->getLanguage() == Common::RU_RUS && g_sci->getGameId() == GID_SQ4)
- objectNameMatches = true;
+ (workaround->objectName == searchObjectName);
if (workaround->gameId == gameId
&& ((workaround->scriptNr == -1) || (workaround->scriptNr == curScriptNr))
@@ -514,10 +803,46 @@ SciWorkaroundSolution trackOriginAndFindWorkaround(int index, const SciWorkaroun
&& ((workaround->inheritanceLevel == -1) || (workaround->inheritanceLevel == inheritanceLevel))
&& objectNameMatches
&& workaround->methodName == g_sci->getSciLanguageString(curMethodName, K_LANG_ENGLISH)
- && workaround->localCallOffset == lastCall->debugLocalCallOffset
&& ((workaround->index == -1) || (workaround->index == index))) {
// Workaround found
- return workaround->newValue;
+ if ((workaround->localCallSignature) || (curLocalCallOffset >= 0)) {
+ // local call signature found and/or subcall was made
+ if ((workaround->localCallSignature) && (curLocalCallOffset >= 0)) {
+ // local call signature found and subcall was made -> check signature accordingly
+ if (!curScriptPtr) {
+ // get script data
+ int segmentId = g_sci->getEngineState()->_segMan->getScriptSegment(curScriptNr);
+ SegmentObj *segmentObj = NULL;
+ if (segmentId) {
+ segmentObj = g_sci->getEngineState()->_segMan->getScriptIfLoaded(segmentId);
+ }
+ if (!segmentObj) {
+ workaround++;
+ continue;
+ }
+ Script *scriptObj = (Script *)segmentObj;
+ curScriptPtr = scriptObj->getBuf();
+ curScriptSize = scriptObj->getScriptSize();
+ }
+
+ // now actually check for signature match
+ if (g_sci->getScriptPatcher()->verifySignature(curLocalCallOffset, workaround->localCallSignature, "workaround signature", curScriptPtr, curScriptSize)) {
+ matched = true;
+ }
+
+ } else {
+ // mismatch, so workaround doesn't match
+ workaround++;
+ continue;
+ }
+ } else {
+ // no localcalls involved -> workaround matches
+ matched = true;
+ }
+ if (matched) {
+ debugC(kDebugLevelWorkarounds, "Workaround: '%s:%s' in script %d, localcall %x", workaround->objectName, workaround->methodName, curScriptNr, curLocalCallOffset);
+ return workaround->newValue;
+ }
}
workaround++;
}
diff --git a/engines/sci/engine/workarounds.h b/engines/sci/engine/workarounds.h
index 9cad618481..46059a175c 100644
--- a/engines/sci/engine/workarounds.h
+++ b/engines/sci/engine/workarounds.h
@@ -60,7 +60,7 @@ struct SciWorkaroundEntry {
int16 inheritanceLevel;
const char *objectName;
const char *methodName;
- int localCallOffset;
+ const uint16 *localCallSignature;
int index;
SciWorkaroundSolution newValue;
};
diff --git a/engines/sci/graphics/paint16.cpp b/engines/sci/graphics/paint16.cpp
index f80703e14d..6004e9ce7a 100644
--- a/engines/sci/graphics/paint16.cpp
+++ b/engines/sci/graphics/paint16.cpp
@@ -471,9 +471,6 @@ void GfxPaint16::kernelGraphRedrawBox(Common::Rect rect) {
#define SCI_DISPLAY_WIDTH 106
#define SCI_DISPLAY_SAVEUNDER 107
#define SCI_DISPLAY_RESTOREUNDER 108
-#define SCI_DISPLAY_DUMMY1 114
-#define SCI_DISPLAY_DUMMY2 115
-#define SCI_DISPLAY_DUMMY3 117
#define SCI_DISPLAY_DONTSHOWBITS 121
reg_t GfxPaint16::kernelDisplay(const char *text, uint16 languageSplitter, int argc, reg_t *argv) {
@@ -543,22 +540,6 @@ reg_t GfxPaint16::kernelDisplay(const char *text, uint16 languageSplitter, int a
bRedraw = 0;
break;
- // The following three dummy calls are not supported by the Sierra SCI
- // interpreter, but are erroneously called in some game scripts.
- case SCI_DISPLAY_DUMMY1: // Longbow demo (all rooms) and QFG1 EGA demo (room 11)
- case SCI_DISPLAY_DUMMY2: // Longbow demo (all rooms)
- case SCI_DISPLAY_DUMMY3: // QFG1 EGA demo (room 11) and PQ2 (room 23)
- if (!(g_sci->getGameId() == GID_LONGBOW && g_sci->isDemo()) &&
- !(g_sci->getGameId() == GID_QFG1 && g_sci->isDemo()) &&
- !(g_sci->getGameId() == GID_PQ2))
- error("Unknown kDisplay argument %d", displayArg.getOffset());
-
- if (displayArg.getOffset() == SCI_DISPLAY_DUMMY2) {
- if (!argc)
- error("No parameter left for kDisplay(115)");
- argc--; argv++;
- }
- break;
default:
SciTrackOriginReply originReply;
SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, kDisplay_workarounds, &originReply);
diff --git a/engines/sci/graphics/portrait.cpp b/engines/sci/graphics/portrait.cpp
index 3311f47022..cb425f3be9 100644
--- a/engines/sci/graphics/portrait.cpp
+++ b/engines/sci/graphics/portrait.cpp
@@ -317,7 +317,8 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
curEvent = _event->getSciEvent(SCI_EVENT_ANY);
if (curEvent.type == SCI_EVENT_MOUSE_PRESS ||
(curEvent.type == SCI_EVENT_KEYBOARD && curEvent.data == SCI_KEY_ESC) ||
- g_sci->getEngineState()->abortScriptProcessing == kAbortQuitGame)
+ g_sci->getEngineState()->abortScriptProcessing == kAbortQuitGame ||
+ g_sci->getEngineState()->_delayedRestoreGame)
userAbort = true;
curPosition = _audio->getAudioPosition();
} while ((curPosition != -1) && (curPosition < timerPosition) && (!userAbort));
diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp
index 5a3b30f7ef..ca5b5b3b8c 100644
--- a/engines/sci/graphics/screen.cpp
+++ b/engines/sci/graphics/screen.cpp
@@ -251,6 +251,18 @@ GfxScreen::~GfxScreen() {
free(_displayScreen);
}
+// should not be used regularly; only meant for restore game
+void GfxScreen::clearForRestoreGame() {
+ // reset all screen data
+ memset(_visualScreen, 0, _pixels);
+ memset(_priorityScreen, 0, _pixels);
+ memset(_controlScreen, 0, _pixels);
+ memset(_displayScreen, 0, _displayPixels);
+ memset(&_ditheredPicColors, 0, sizeof(_ditheredPicColors));
+ _fontIsUpscaled = false;
+ copyToScreen();
+}
+
void GfxScreen::copyToScreen() {
g_system->copyRectToScreen(_activeScreen, _displayWidth, 0, 0, _displayWidth, _displayHeight);
}
diff --git a/engines/sci/graphics/screen.h b/engines/sci/graphics/screen.h
index 766e32614a..1c946ef02f 100644
--- a/engines/sci/graphics/screen.h
+++ b/engines/sci/graphics/screen.h
@@ -76,6 +76,7 @@ public:
byte getColorWhite() { return _colorWhite; }
byte getColorDefaultVectorData() { return _colorDefaultVectorData; }
+ void clearForRestoreGame();
void copyToScreen();
void copyFromScreen(byte *buffer);
void kernelSyncWithFramebuffer();
diff --git a/engines/sci/parser/vocabulary.cpp b/engines/sci/parser/vocabulary.cpp
index 000b037b44..828a57abeb 100644
--- a/engines/sci/parser/vocabulary.cpp
+++ b/engines/sci/parser/vocabulary.cpp
@@ -74,6 +74,8 @@ Vocabulary::Vocabulary(ResourceManager *resMan, bool foreign) : _resMan(resMan),
parser_event = NULL_REG;
parserIsValid = false;
+
+ _pronounReference = 0x1000; // Non-existent word
}
Vocabulary::~Vocabulary() {
@@ -738,4 +740,79 @@ int Vocabulary::parseNodes(int *i, int *pos, int type, int nr, int argc, const c
return oldPos;
}
+
+// FIXME: Duplicated from said.cpp
+static int node_major(ParseTreeNode* node) {
+ assert(node->type == kParseTreeBranchNode);
+ assert(node->left->type == kParseTreeLeafNode);
+ return node->left->value;
+}
+static bool node_is_terminal(ParseTreeNode* node) {
+ return (node->right->right &&
+ node->right->right->type != kParseTreeBranchNode);
+}
+static int node_terminal_value(ParseTreeNode* node) {
+ assert(node_is_terminal(node));
+ return node->right->right->value;
+}
+
+static ParseTreeNode* scanForMajor(ParseTreeNode *tree, int major) {
+ assert(tree);
+
+ if (node_is_terminal(tree)) {
+ if (node_major(tree) == major)
+ return tree;
+ else
+ return 0;
+ }
+
+ ParseTreeNode* ptr = tree->right;
+
+ // Scan children
+ while (ptr->right) {
+ ptr = ptr->right;
+
+ if (node_major(ptr->left) == major)
+ return ptr->left;
+ }
+
+ if (major == 0x141)
+ return 0;
+
+ // If not found, go into a 0x141 and try again
+ tree = scanForMajor(tree, 0x141);
+ if (!tree)
+ return 0;
+ return scanForMajor(tree, major);
+}
+
+bool Vocabulary::storePronounReference() {
+ assert(parserIsValid);
+
+ ParseTreeNode *ptr = scanForMajor(_parserNodes, 0x142); // 0x142 = object?
+
+ while (ptr && !node_is_terminal(ptr))
+ ptr = scanForMajor(ptr, 0x141);
+
+ if (!ptr)
+ return false;
+
+ _pronounReference = node_terminal_value(ptr);
+
+ debugC(kDebugLevelParser, "Stored pronoun reference: %x", _pronounReference);
+ return true;
+}
+
+void Vocabulary::replacePronouns(ResultWordListList &words) {
+ if (_pronounReference == 0x1000)
+ return;
+
+ for (ResultWordListList::iterator i = words.begin(); i != words.end(); ++i)
+ for (ResultWordList::iterator j = i->begin(); j != i->end(); ++j)
+ if (j->_class & (VOCAB_CLASS_PRONOUN << 4)) {
+ j->_class = VOCAB_CLASS_NOUN << 4;
+ j->_group = _pronounReference;
+ }
+}
+
} // End of namespace Sci
diff --git a/engines/sci/parser/vocabulary.h b/engines/sci/parser/vocabulary.h
index 09499946cb..f4adee6e55 100644
--- a/engines/sci/parser/vocabulary.h
+++ b/engines/sci/parser/vocabulary.h
@@ -233,6 +233,16 @@ public:
int parseGNF(const ResultWordListList &words, bool verbose = false);
/**
+ * Find and store reference for future pronouns
+ */
+ bool storePronounReference();
+
+ /**
+ * Replace pronouns by stored reference
+ */
+ void replacePronouns(ResultWordListList &words);
+
+ /**
* Constructs the Greibach Normal Form of the grammar supplied in 'branches'.
* @param verbose Set to true for debugging. If true, the list is
* freed before the function ends
@@ -360,6 +370,8 @@ private:
SynonymList _synonyms; /**< The list of synonyms */
Common::Array<Common::List<AltInput> > _altInputs;
+ int _pronounReference;
+
public:
// Accessed by said()
ParseTreeNode _parserNodes[VOCAB_TREE_NODES]; /**< The parse tree */
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index 60a1271b89..668ad053cc 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -114,6 +114,7 @@ SciEngine::SciEngine(OSystem *syst, const ADGameDescription *desc, SciGameId gam
DebugMan.addDebugChannel(kDebugLevelVM, "VM", "VM debugging");
DebugMan.addDebugChannel(kDebugLevelScripts, "Scripts", "Notifies when scripts are unloaded");
DebugMan.addDebugChannel(kDebugLevelScriptPatcher, "ScriptPatcher", "Notifies when scripts are patched");
+ DebugMan.addDebugChannel(kDebugLevelWorkarounds, "Workarounds", "Notifies when workarounds are triggered");
DebugMan.addDebugChannel(kDebugLevelGC, "GC", "Garbage Collector debugging");
DebugMan.addDebugChannel(kDebugLevelResMan, "ResMan", "Resource manager debugging");
DebugMan.addDebugChannel(kDebugLevelOnStartup, "OnStartup", "Enter debugger at start of game");
@@ -281,25 +282,13 @@ Common::Error SciEngine::run() {
// Check whether loading a savestate was requested
int directSaveSlotLoading = ConfMan.getInt("save_slot");
if (directSaveSlotLoading >= 0) {
- // call GameObject::play (like normally)
- initStackBaseWithSelector(SELECTOR(play));
- // We set this, so that the game automatically quit right after init
- _gamestate->variables[VAR_GLOBAL][4] = TRUE_REG;
+ _gamestate->_delayedRestoreGame = true;
+ _gamestate->_delayedRestoreGameId = directSaveSlotLoading;
// Jones only initializes its menus when restarting/restoring, thus set
// the gameIsRestarting flag here before initializing. Fixes bug #6536.
if (g_sci->getGameId() == GID_JONES)
_gamestate->gameIsRestarting = GAMEISRESTARTING_RESTORE;
-
- _gamestate->_executionStackPosChanged = false;
- run_vm(_gamestate);
-
- // As soon as we get control again, actually restore the game
- reg_t restoreArgv[2] = { NULL_REG, make_reg(0, directSaveSlotLoading) }; // special call (argv[0] is NULL)
- kRestoreGame(_gamestate, 2, restoreArgv);
-
- // this indirectly calls GameObject::init, which will setup menu, text font/color codes etc.
- // without this games would be pretty badly broken
}
// Show any special warnings for buggy scripts with severe game bugs,
diff --git a/engines/sci/sci.h b/engines/sci/sci.h
index 4928fd1b4e..c6813aa07c 100644
--- a/engines/sci/sci.h
+++ b/engines/sci/sci.h
@@ -105,7 +105,8 @@ enum kDebugLevels {
kDebugLevelResMan = 1 << 19,
kDebugLevelOnStartup = 1 << 20,
kDebugLevelDebugMode = 1 << 21,
- kDebugLevelScriptPatcher = 1 << 22
+ kDebugLevelScriptPatcher = 1 << 22,
+ kDebugLevelWorkarounds = 1 << 23
};
enum SciGameId {
diff --git a/engines/scumm/debugger.cpp b/engines/scumm/debugger.cpp
index 2b718b2cfe..0ebea94608 100644
--- a/engines/scumm/debugger.cpp
+++ b/engines/scumm/debugger.cpp
@@ -256,7 +256,7 @@ bool ScummDebugger::Cmd_Hide(int argc, const char **argv) {
bool ScummDebugger::Cmd_Script(int argc, const char** argv) {
int scriptnum;
- if (argc < 2) {
+ if (argc < 3) {
debugPrintf("Syntax: script <scriptnum> <command>\n");
return true;
}
diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h
index 6eab5c752f..d42a7251d9 100644
--- a/engines/scumm/detection_tables.h
+++ b/engines/scumm/detection_tables.h
@@ -812,6 +812,7 @@ static const GameFilenamePattern gameFilenamesTable[] = {
{ "pajama3", "PyjamaHG", kGenHEPC, Common::FR_FRA, UNK, 0 },
{ "pajama3", "PyjamaSKS", kGenHEPC, Common::DE_DEU, UNK, 0 },
{ "pajama3", "PyjamaSKS", kGenHEMac, Common::DE_DEU, Common::kPlatformMacintosh, 0 },
+ { "pajama3", "SamLDM", kGenHEPC, Common::IT_ITA, Common::kPlatformWindows, 0 },
{ "pajama3", "UKPajamaEAT", kGenHEPC, Common::RU_RUS, UNK, 0 },
{ "puttcircus", "puttcircus", kGenHEPC, UNK_LANG, UNK, 0 },
@@ -830,6 +831,7 @@ static const GameFilenamePattern gameFilenamesTable[] = {
{ "puttrace", "500demo", kGenHEPC, Common::NL_NLD, Common::kPlatformWindows, 0 },
{ "puttrace", "course", kGenHEPC, Common::FR_FRA, UNK, 0 },
{ "puttrace", "CourseDemo", kGenHEPC, Common::FR_FRA, UNK, 0 },
+ { "puttrace", "GasGasEG", kGenHEPC, Common::IT_ITA, Common::kPlatformWindows, 0 },
{ "puttrace", "racedemo", kGenHEPC, UNK_LANG, Common::kPlatformWindows, 0 },
{ "puttrace", "RaceDemo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 },
{ "puttrace", "Rennen", kGenHEPC, Common::DE_DEU, UNK, 0 },
@@ -926,6 +928,7 @@ static const GameFilenamePattern gameFilenamesTable[] = {
{ "spyfox2", "Sf2demo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 },
{ "spyfox2", "Spy Fox 2", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 },
{ "spyfox2", "Spy Fox 2 - Demo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 },
+ { "spyfox2", "SPyFoxMCR", kGenHEPC, Common::IT_ITA, Common::kPlatformWindows, 0 },
{ "spyfox2", "SpyFoxOR", kGenHEPC, Common::DE_DEU, UNK, 0 },
{ "spyfox2", "SpyFoxOR", kGenHEMac, Common::DE_DEU, Common::kPlatformMacintosh, 0 },
{ "spyfox2", "SPYFoxORE", kGenHEPC, Common::FR_FRA, UNK, 0 },
diff --git a/engines/scumm/scumm-md5.h b/engines/scumm/scumm-md5.h
index 5be18fb990..bac7a4665b 100644
--- a/engines/scumm/scumm-md5.h
+++ b/engines/scumm/scumm-md5.h
@@ -1,5 +1,5 @@
/*
- This file was generated by the md5table tool on Sun Dec 7 23:09:10 2014
+ This file was generated by the md5table tool on Thu May 7 20:24:51 2015
DO NOT EDIT MANUALLY!
*/
@@ -27,6 +27,7 @@ static const MD5Table md5table[] = {
{ "0557df19f046a84c2fdc63507c6616cb", "farm", "HE 72", "Demo", -1, Common::NL_NLD, Common::kPlatformWindows },
{ "055ffe4f47753e47594ac67823220c54", "puttrace", "HE 99", "", -1, Common::DE_DEU, Common::kPlatformUnknown },
{ "057c9b456dedcc4d71b991a3072a20b3", "monkey", "SEGA", "", 9465, Common::JA_JPN, Common::kPlatformSegaCD },
+ { "05d3143827ab4f5d2521a1a47dab8ff2", "puttrace", "HE 98", "", -1, Common::IT_ITA, Common::kPlatformUnknown },
{ "06b187468113f9ae5a400b148a847fac", "atlantis", "Floppy", "Floppy", 12075, Common::EN_ANY, Common::kPlatformMacintosh },
{ "06c3cf4f31daad8b1cd93153491db9e6", "pajama3", "", "", 79382, Common::NL_NLD, Common::kPlatformUnknown },
{ "07433205acdca3bc553d0e731588b35f", "airport", "", "", -1, Common::EN_ANY, Common::kPlatformWindows },
@@ -67,6 +68,7 @@ static const MD5Table md5table[] = {
{ "114acdc2659a273c220f86ee9edb24c1", "maniac", "V2", "V2", -1, Common::FR_FRA, Common::kPlatformDOS },
{ "11ddf1fde76e3156eb3a38da213f484e", "monkey2", "", "", -1, Common::IT_ITA, Common::kPlatformAmiga },
{ "11e6e244078ff09b0f3832e35420e0a7", "catalog", "", "Demo", -1, Common::EN_ANY, Common::kPlatformWindows },
+ { "12cdc256eae5a461bcc9a49975999841", "atlantis", "Floppy", "Demo", -1, Common::EN_ANY, Common::kPlatformDOS },
{ "132bff65e6367c09cc69318ce1b59333", "monkey2", "", "", 11155, Common::EN_ANY, Common::kPlatformAmiga },
{ "1387d16aa620dc1c2d1fd87f8a9e7a09", "puttcircus", "", "Demo", -1, Common::FR_FRA, Common::kPlatformWindows },
{ "13d2a86a7290813a1c386490447d72db", "fbear", "HE 62", "", -1, Common::EN_ANY, Common::kPlatform3DO },
@@ -119,6 +121,7 @@ static const MD5Table md5table[] = {
{ "22c9eb04455440131ffc157aeb8d40a8", "fbear", "HE 70", "Demo", -1, Common::EN_ANY, Common::kPlatformWindows },
{ "22de86b2f7ec6e5db745ed1123310b44", "spyfox2", "", "Demo", 15832, Common::FR_FRA, Common::kPlatformWindows },
{ "22f4ea88a09da12df9308ba30bcb7d0f", "loom", "EGA", "EGA", -1, Common::EN_ANY, Common::kPlatformDOS },
+ { "2328be0317008ef047eed7912a4b0850", "pajama2", "HE 98.5", "", -1, Common::EN_GRB, Common::kPlatformWindows },
{ "23394c8d29cc63c61313959431a12476", "spyfox", "HE 100", "Updated", -1, Common::EN_ANY, Common::kPlatformWindows },
{ "24942a4200d99bdb4bdb78f9c7e07027", "pajama3", "", "Mini Game", 13911, Common::NL_NLD, Common::kPlatformWindows },
{ "254fede2f15dbb32a23760d601b01816", "zak", "V1", "", -1, Common::EN_ANY, Common::kPlatformC64 },
@@ -164,6 +167,7 @@ static const MD5Table md5table[] = {
{ "3686cf8f89e102ececf4366e1d2c8126", "monkey2", "", "", 11135, Common::EN_ANY, Common::kPlatformDOS },
{ "36a6750e03fb505fc19fc2bf3e4dbe91", "pajama2", "", "Demo", 58749, Common::EN_ANY, Common::kPlatformUnknown },
{ "3769b56c9a22f5521d74525ee459f88d", "puttrace", "HE 99", "Demo", 13108, Common::DE_DEU, Common::kPlatformWindows },
+ { "3785fd25f7e02b5782bfc5072d8f77c8", "spyfox2", "", "", -1, Common::IT_ITA, Common::kPlatformUnknown },
{ "37aed3f91c1ef959e0bd265f9b13781f", "pajama", "HE 100", "Updated", -1, Common::EN_USA, Common::kPlatformUnknown },
{ "37f56ceb13e401a7ac7d9e6b37fecaf7", "loom", "EGA", "EGA", 5748, Common::EN_ANY, Common::kPlatformDOS },
{ "37ff1b308999c4cca7319edfcc1280a0", "puttputt", "HE 70", "Demo", 8269, Common::EN_ANY, Common::kPlatformWindows },
@@ -250,6 +254,7 @@ static const MD5Table md5table[] = {
{ "55f4e9402bec2bded383843123f37c5c", "pajama2", "HE 98.5", "", -1, Common::DE_DEU, Common::kPlatformWindows },
{ "566165a7338fa11029e7c14d94fa70d0", "freddi", "HE 73", "Demo", 9800, Common::EN_ANY, Common::kPlatformWindows },
{ "56b5922751be7ffd771b38dda56b028b", "freddi", "HE 100", "", 34837, Common::NL_NLD, Common::kPlatformWii },
+ { "56e8c37a0a08c3a7076f82417461a877", "indy3", "EGA", "EGA", -1, Common::EN_ANY, Common::kPlatformDOS },
{ "5719fc8a13b4638b78d9d8d12f091f94", "puttrace", "HE 99", "", -1, Common::FR_FRA, Common::kPlatformWindows },
{ "5798972220cd458be2626d54c80f71d7", "atlantis", "Floppy", "Floppy", -1, Common::IT_ITA, Common::kPlatformAmiga },
{ "57a17febe2183f521250e55d55b83e60", "PuttTime", "HE 99", "", -1, Common::FR_FRA, Common::kPlatformWindows },
@@ -376,6 +381,7 @@ static const MD5Table md5table[] = {
{ "8368f552b1e3eba559f8d559bcc4cadb", "freddi3", "", "", -1, Common::UNK_LANG, Common::kPlatformUnknown },
{ "839a658f7d22de00787ebc945348cdb6", "dog", "", "", 19681, Common::DE_DEU, Common::kPlatformWindows },
{ "83cedbe26aa8b58988e984e3d34cac8e", "freddi3", "HE 99", "", -1, Common::DE_DEU, Common::kPlatformUnknown },
+ { "83e7a9205567dceb456ee35eeaf26ffa", "pajama3", "", "", -1, Common::IT_ITA, Common::kPlatformUnknown },
{ "84e3c23a49ded8a6f9197735c8eb3de7", "PuttTime", "HE 85", "", -1, Common::DE_DEU, Common::kPlatformWindows },
{ "8539c0ff89868e55a08e652ac44daaae", "water", "HE 98.5", "", -1, Common::NL_NLD, Common::kPlatformUnknown },
{ "861e59ed72a1cd0e6d454f7ee7e2bf3d", "comi", "", "", -1, Common::RU_RUS, Common::kPlatformWindows },
@@ -651,6 +657,7 @@ static const MD5Table md5table[] = {
{ "f049e38c1f8302b5db6170f1872af89a", "monkey", "CD", "CD", 8955, Common::ES_ESP, Common::kPlatformDOS },
{ "f06e66fd45b2f8b0f4a2833ff4476050", "fbpack", "", "", -1, Common::HE_ISR, Common::kPlatformDOS },
{ "f08145577e4f13584cc90b3d6e9caa55", "pajama3", "", "Demo", -1, Common::NL_NLD, Common::kPlatformUnknown },
+ { "f0ccc12a8704bf57706b42a37f877128", "tentacle", "Floppy", "Floppy", -1, Common::EN_ANY, Common::kPlatformDOS },
{ "f1b0e0d587b85052de5534a3847e68fe", "water", "HE 99", "Updated", -1, Common::EN_ANY, Common::kPlatformUnknown },
{ "f237bf8a5ef9af78b2a6a4f3901da341", "pajama", "", "Demo", 18354, Common::EN_ANY, Common::kPlatformUnknown },
{ "f27b1ba0eadaf2a6617b2b58192d1dbf", "samnmax", "Floppy", "Floppy", -1, Common::DE_DEU, Common::kPlatformDOS },
diff --git a/engines/sword25/util/double_serialization.cpp b/engines/sword25/util/double_serialization.cpp
index 73da296e40..13fa42b6be 100644
--- a/engines/sword25/util/double_serialization.cpp
+++ b/engines/sword25/util/double_serialization.cpp
@@ -33,7 +33,7 @@ SerializedDouble encodeDouble(double value) {
double significand = frexp(value, &exponent);
// Shift the the first part of the significand into the integer range
- double shiftedsignificandPart = ldexp(abs(significand), 32);
+ double shiftedsignificandPart = ldexp(fabs(significand), 32);
uint32 significandOne = uint32(floor(shiftedsignificandPart));
// Shift the remainder of the significand into the integer range
diff --git a/engines/tony/mpal/mpal.cpp b/engines/tony/mpal/mpal.cpp
index c813b354b0..89cc28130d 100644
--- a/engines/tony/mpal/mpal.cpp
+++ b/engines/tony/mpal/mpal.cpp
@@ -709,6 +709,10 @@ void ActionThread(CORO_PARAM, const void *param) {
CORO_SLEEP(1);
}
+ // WORKAROUND: User interface sometimes remaining disabled after capturing guard on Ferris wheel
+ if (_ctx->item->_nObj == 3601 && _ctx->item->_dwRes == 9)
+ g_vm->getEngine()->enableInput();
+
globalDestroy(_ctx->item);
_ctx->item = NULL;
diff --git a/engines/toon/anim.cpp b/engines/toon/anim.cpp
index ec23fea186..4788ba61a9 100644
--- a/engines/toon/anim.cpp
+++ b/engines/toon/anim.cpp
@@ -150,10 +150,12 @@ void Animation::drawFrame(Graphics::Surface &surface, int32 frame, int16 xx, int
if (_numFrames == 0)
return;
+ int16 dataFrame = frame;
+
if (_frames[frame]._ref != -1)
- frame = _frames[frame]._ref;
+ dataFrame = _frames[frame]._ref;
- if (!_frames[frame]._data)
+ if (!_frames[dataFrame]._data)
return;
int16 rectX = _frames[frame]._x2 - _frames[frame]._x1;
@@ -194,7 +196,7 @@ void Animation::drawFrame(Graphics::Surface &surface, int32 frame, int16 xx, int
return;
int32 destPitch = surface.pitch;
- uint8 *srcRow = _frames[frame]._data + offsX + (_frames[frame]._x2 - _frames[frame]._x1) * offsY;
+ uint8 *srcRow = _frames[dataFrame]._data + offsX + (_frames[frame]._x2 - _frames[frame]._x1) * offsY;
uint8 *curRow = (uint8 *)surface.getBasePtr(xx + _x1 + _frames[frame]._x1 + offsX, yy + _frames[frame]._y1 + _y1 + offsY);
for (int16 y = 0; y < rectY; y++) {
uint8 *cur = curRow;
@@ -216,8 +218,12 @@ void Animation::drawFrameWithMask(Graphics::Surface &surface, int32 frame, int16
void Animation::drawFrameWithMaskAndScale(Graphics::Surface &surface, int32 frame, int16 xx, int16 yy, int32 zz, Picture *mask, int32 scale) {
debugC(5, kDebugAnim, "drawFrameWithMaskAndScale(surface, %d, %d, %d, %d, mask, %d)", frame, xx, yy, zz, scale);
+
+ int16 dataFrame = frame;
+
if (_frames[frame]._ref != -1)
- frame = _frames[frame]._ref;
+ dataFrame = _frames[frame]._ref;
+
int16 rectX = _frames[frame]._x2 - _frames[frame]._x1;
int16 rectY = _frames[frame]._y2 - _frames[frame]._y1;
@@ -235,7 +241,7 @@ void Animation::drawFrameWithMaskAndScale(Graphics::Surface &surface, int32 fram
int32 destPitch = surface.pitch;
int32 destPitchMask = mask->getWidth();
- uint8 *c = _frames[frame]._data;
+ uint8 *c = _frames[dataFrame]._data;
uint8 *curRow = (uint8 *)surface.getPixels();
uint8 *curRowMask = mask->getDataPtr();
@@ -287,9 +293,6 @@ int16 Animation::getFrameWidth(int32 frame) {
if ((frame < 0) || (frame >= _numFrames))
return 0;
- if (_frames[frame]._ref != -1)
- frame = _frames[frame]._ref;
-
return _frames[frame]._x2 - _frames[frame]._x1;
}
@@ -298,9 +301,6 @@ int16 Animation::getFrameHeight(int32 frame) {
if (frame < 0 || frame >= _numFrames)
return 0;
- if (_frames[frame]._ref != -1)
- frame = _frames[frame]._ref;
-
return _frames[frame]._y2 - _frames[frame]._y1;
}
@@ -323,8 +323,10 @@ void Animation::drawFontFrame(Graphics::Surface &surface, int32 frame, int16 xx,
if (_numFrames == 0)
return;
+ int16 dataFrame = frame;
+
if (_frames[frame]._ref != -1)
- frame = _frames[frame]._ref;
+ dataFrame = _frames[frame]._ref;
int16 rectX = _frames[frame]._x2 - _frames[frame]._x1;
int16 rectY = _frames[frame]._y2 - _frames[frame]._y1;
@@ -345,7 +347,7 @@ void Animation::drawFontFrame(Graphics::Surface &surface, int32 frame, int16 xx,
return;
int32 destPitch = surface.pitch;
- uint8 *c = _frames[frame]._data;
+ uint8 *c = _frames[dataFrame]._data;
uint8 *curRow = (uint8 *)surface.getBasePtr(xx + _x1 + _frames[frame]._x1, yy + _frames[frame]._y1 + _y1);
for (int16 y = 0; y < rectY; y++) {
unsigned char *cur = curRow;
diff --git a/engines/toon/character.cpp b/engines/toon/character.cpp
index 686fe99beb..3d7beeeafe 100644
--- a/engines/toon/character.cpp
+++ b/engines/toon/character.cpp
@@ -226,6 +226,11 @@ bool Character::walkTo(int16 newPosX, int16 newPosY) {
}
setFacing(getFacingFromDirection(smoothDx, smoothDy));
+ if (_currentWalkStamp != localWalkStamp) {
+ // another walkTo was started in setFacing, we need to cancel this one.
+ return false;
+ }
+
playWalkAnim(0, 0);
}
diff --git a/engines/toon/font.cpp b/engines/toon/font.cpp
index ab941e5de9..9b08e432fc 100644
--- a/engines/toon/font.cpp
+++ b/engines/toon/font.cpp
@@ -99,7 +99,7 @@ void FontRenderer::renderText(int16 x, int16 y, const Common::String &origText,
} else {
curChar = textToFont(curChar);
_currentFont->drawFontFrame(_vm->getMainSurface(), curChar, curX, curY, _currentFontColor);
- curX = curX + _currentFont->getFrameWidth(curChar) - 1;
+ curX = curX + MAX<int32>(_currentFont->getFrameWidth(curChar) - 2, 0);
height = MAX<int32>(height, _currentFont->getFrameHeight(curChar));
}
text++;
@@ -138,8 +138,8 @@ void FontRenderer::computeSize(const Common::String &origText, int16 *retX, int1
// really tell how far it will stick out. For now,
// assume we only need to take the lower bound into
// consideration.
- Common::Rect charRect = _currentFont->getFrameRect(curChar);
- lastLineHeight = MAX(lastLineHeight, charRect.bottom);
+ //Common::Rect charRect = _currentFont->getFrameRect(curChar);
+ lastLineHeight = MAX(lastLineHeight, _currentFont->getHeight());
}
text++;
}
diff --git a/engines/toon/toon.cpp b/engines/toon/toon.cpp
index 2f5051c157..09f865f798 100644
--- a/engines/toon/toon.cpp
+++ b/engines/toon/toon.cpp
@@ -210,6 +210,9 @@ void ToonEngine::parseInput() {
if (event.kbd.keycode == Common::KEYCODE_s && !hasModifier) {
_audioManager->muteSfx(!_audioManager->isSfxMuted());
}
+ if (event.kbd.keycode == Common::KEYCODE_F1 && !hasModifier && !_gameState->_inMenu) {
+ showOptions();
+ }
if (event.kbd.flags & Common::KBD_ALT) {
int slotNum = event.kbd.keycode - (event.kbd.keycode >= Common::KEYCODE_KP0 ? Common::KEYCODE_KP0 : Common::KEYCODE_0);
@@ -255,7 +258,7 @@ void ToonEngine::parseInput() {
}
}
- if (!_gameState->_inConversation && !_gameState->_mouseHidden && !_gameState->_inInventory) {
+ if (!_gameState->_inConversation && !_gameState->_mouseHidden && !_gameState->_inInventory && !_gameState->_inMenu) {
selectHotspot();
clickEvent();
}
@@ -576,7 +579,29 @@ enum MainMenuMasks {
MAINMENUMASK_EVERYWHERE = 3
};
-struct MainMenuFile {
+enum OptionMenuSelections {
+ OPTIONMENUHOTSPOT_NONE = 0,
+ OPTIONMENUHOTSPOT_PLAY = 1,
+ OPTIONMENUHOTSPOT_QUIT = 2,
+ OPTIONMENUHOTSPOT_TEXT = 3,
+ OPTIONMENUHOTSPOT_TEXTSPEED = 4,
+ OPTIONMENUHOTSPOT_VOLUMESFX = 5,
+ OPTIONMENUHOTSPOT_VOLUMESFXSLIDER = 6,
+ OPTIONMENUHOTSPOT_VOLUMEMUSIC = 7,
+ OPTIONMENUHOTSPOT_VOLUMEMUSICSLIDER = 8,
+ OPTIONMENUHOTSPOT_VOLUMEVOICE = 9,
+ OPTIONMENUHOTSPOT_VOLUMEVOICESLIDER = 10,
+ OPTIONMENUHOTSPOT_SPEAKERBUTTON = 11,
+ OPTIONMENUHOTSPOT_SPEAKERLEVER = 12,
+ OPTIONMENUHOTSPOT_VIDEO_MODE = 13
+};
+
+enum OptionMenuMasks {
+ OPTIONMENUMASK_EVERYWHERE = 1
+};
+
+
+struct MenuFile {
int menuMask;
int id;
const char *animationFile;
@@ -584,7 +609,7 @@ struct MainMenuFile {
};
#define MAINMENU_ENTRYCOUNT 12
-static const MainMenuFile mainMenuFiles[] = {
+static const MenuFile mainMenuFiles[] = {
{ MAINMENUMASK_BASE, MAINMENUHOTSPOT_START, "STARTBUT.CAF", 0 }, // "Start" button
{ MAINMENUMASK_BASE, MAINMENUHOTSPOT_INTRO, "INTROBUT.CAF", 0 }, // "Intro" button
{ MAINMENUMASK_BASE, MAINMENUHOTSPOT_LOADGAME, "LOADBUT.CAF", 0 }, // "Load Game" button
@@ -600,7 +625,38 @@ static const MainMenuFile mainMenuFiles[] = {
{ MAINMENUMASK_HOTKEYS, MAINMENUHOTSPOT_HOTKEYSCLOSE, "HOTKEYS.CAF", 0 } // Hotkeys display - clicking on it will close hotkeys
};
-struct MainMenuEntry {
+#define OPTIONMENU_ENTRYCOUNT 27
+static const MenuFile optionMenuFiles[] = {
+ { OPTIONMENUMASK_EVERYWHERE, OPTIONMENUHOTSPOT_PLAY, "PLAYBUTN.CAF", 0 }, // "Start" button
+ { OPTIONMENUMASK_EVERYWHERE, OPTIONMENUHOTSPOT_QUIT, "QUITBUTN.CAF", 0 }, // "Intro" button
+ { OPTIONMENUMASK_EVERYWHERE, OPTIONMENUHOTSPOT_VIDEO_MODE, "VIDMODE.CAF", 0 }, // "Start" button
+ { OPTIONMENUMASK_EVERYWHERE, OPTIONMENUHOTSPOT_TEXTSPEED, "TXTSPEED.CAF", 0 }, // "Intro" button
+ { OPTIONMENUMASK_EVERYWHERE, OPTIONMENUHOTSPOT_TEXT, "TEXTDIAL.CAF", 0}, // "Intro" button
+ { OPTIONMENUMASK_EVERYWHERE, OPTIONMENUHOTSPOT_VOLUMESFX, "SFXBUTN.CAF", 0 }, // "Start" button
+ { OPTIONMENUMASK_EVERYWHERE, OPTIONMENUHOTSPOT_VOLUMESFXSLIDER, "SFXSLDR.CAF", 0 }, // "Intro" button
+ { OPTIONMENUMASK_EVERYWHERE, OPTIONMENUHOTSPOT_VOLUMEVOICE, "VOICEBTN.CAF", 0 }, // "Start" button
+ { OPTIONMENUMASK_EVERYWHERE, OPTIONMENUHOTSPOT_VOLUMEVOICESLIDER, "VOICESLD.CAF", 0 }, // "Intro" button
+ { OPTIONMENUMASK_EVERYWHERE, OPTIONMENUHOTSPOT_VOLUMEMUSIC, "MUSICBTN.CAF", 0 }, // "Start" button
+ { OPTIONMENUMASK_EVERYWHERE, OPTIONMENUHOTSPOT_VOLUMEMUSICSLIDER, "MUSICSLD.CAF", 0 }, // "Intro" button
+ { OPTIONMENUMASK_EVERYWHERE, OPTIONMENUHOTSPOT_SPEAKERBUTTON, "XTRABUTN.CAF", 0 }, // "Start" button
+ { OPTIONMENUMASK_EVERYWHERE, OPTIONMENUHOTSPOT_SPEAKERLEVER, "XTRALEVR.CAF", 0}, // "Intro" button
+ { OPTIONMENUMASK_EVERYWHERE, OPTIONMENUHOTSPOT_NONE, "ANTENNAL.CAF", 6 }, // "Start" button
+ { OPTIONMENUMASK_EVERYWHERE, OPTIONMENUHOTSPOT_NONE, "ANTENNAR.CAF", 6 }, // "Intro" button
+ { OPTIONMENUMASK_EVERYWHERE, OPTIONMENUHOTSPOT_NONE, "BIGREDL.CAF", 6 }, // "Start" button
+ { OPTIONMENUMASK_EVERYWHERE, OPTIONMENUHOTSPOT_NONE, "BIGREDR.CAF", 6 }, // "Intro" button
+ { OPTIONMENUMASK_EVERYWHERE, OPTIONMENUHOTSPOT_NONE, "GRIDLTEL.CAF", 6 }, // "Start" button
+ { OPTIONMENUMASK_EVERYWHERE, OPTIONMENUHOTSPOT_NONE, "GRIDLTER.CAF", 6 }, // "Intro" button
+ { OPTIONMENUMASK_EVERYWHERE, OPTIONMENUHOTSPOT_NONE, "LSPEAKR.CAF", 0 }, // "Start" button
+ { OPTIONMENUMASK_EVERYWHERE, OPTIONMENUHOTSPOT_NONE, "RSPEAKR.CAF", 0 }, // "Intro" button
+ { OPTIONMENUMASK_EVERYWHERE, OPTIONMENUHOTSPOT_NONE, "STARLITL.CAF", 6 }, // "Start" button
+ { OPTIONMENUMASK_EVERYWHERE, OPTIONMENUHOTSPOT_NONE, "STARLITR.CAF", 6 }, // "Intro" button
+ { OPTIONMENUMASK_EVERYWHERE, OPTIONMENUHOTSPOT_NONE, "CHASE1.CAF", 6 }, // "Intro" button
+ { OPTIONMENUMASK_EVERYWHERE, OPTIONMENUHOTSPOT_NONE, "CHASE2.CAF", 6 }, // "Intro" button
+ { OPTIONMENUMASK_EVERYWHERE, OPTIONMENUHOTSPOT_NONE, "CHASE3.CAF", 6 }, // "Intro" button
+ { OPTIONMENUMASK_EVERYWHERE, OPTIONMENUHOTSPOT_NONE, "CHASE4.CAF", 6 } // "Intro" button
+};
+
+struct MenuEntry {
int menuMask;
int id;
Animation *animation;
@@ -608,15 +664,291 @@ struct MainMenuEntry {
int animateOnFrame;
int animateCurFrame;
int activeFrame;
+ bool playOnce;
};
+bool ToonEngine::showOptions() {
+
+ storePalette();
+ fadeOut(5);
+ Picture* optionPicture = new Picture(this);
+ optionPicture->loadPicture("OPTIONS.CPS");
+ optionPicture->setupPalette();
+ flushPalette(true);
+
+ int16 oldScrollValue = _gameState->_currentScrollValue;
+ _gameState->_currentScrollValue = 0;
+
+ bool oldMouseHidden = _gameState->_mouseHidden;
+ _gameState->_mouseHidden = false;
+
+ MenuEntry entries[OPTIONMENU_ENTRYCOUNT];
+
+ for (int entryNr = 0; entryNr < OPTIONMENU_ENTRYCOUNT; entryNr++) {
+ entries[entryNr].menuMask = optionMenuFiles[entryNr].menuMask;
+ entries[entryNr].id = optionMenuFiles[entryNr].id;
+ entries[entryNr].animation = new Animation(this);
+ entries[entryNr].animation->loadAnimation(optionMenuFiles[entryNr].animationFile);
+ if (entries[entryNr].id != OPTIONMENUHOTSPOT_NONE)
+ entries[entryNr].rect = entries[entryNr].animation->getRect();
+ entries[entryNr].animateOnFrame = optionMenuFiles[entryNr].animateOnFrame;
+ entries[entryNr].animateCurFrame = 0;
+ entries[entryNr].activeFrame = 0;
+ entries[entryNr].playOnce = false;
+ }
+
+ entries[10].activeFrame = _audioManager->_mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType) * (entries[10].animation->_numFrames - 1) / 256;
+ entries[8].activeFrame = _audioManager->_mixer->getVolumeForSoundType(Audio::Mixer::kSpeechSoundType) * (entries[8].animation->_numFrames - 1) / 256;
+ entries[6].activeFrame = _audioManager->_mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType) * (entries[6].animation->_numFrames - 1) / 256;
+
+ entries[9].activeFrame = _audioManager->isMusicMuted() ? 0 : 3;
+ entries[7].activeFrame = _audioManager->isVoiceMuted() ? 0 : 3;
+ entries[5].activeFrame = _audioManager->isSfxMuted() ? 0 : 3;
+
+ entries[2].activeFrame = entries[2].animation->_numFrames - 1;
+
+ if (!_showConversationText) {
+ entries[4].activeFrame = 4;
+ } else if (_useAlternativeFont) {
+ entries[4].activeFrame = 8;
+ } else {
+ entries[4].activeFrame = 0;
+ }
+
+ setCursor(0);
+
+ int menuMask = OPTIONMENUMASK_EVERYWHERE;
+ int ratioX = 0;
+ bool doExit = false;
+ bool exitGame = false;
+ _gameState->_inMenu = true;
+ dirtyAllScreen();
+ _firstFrame = true;
+
+ while (!doExit) {
+
+ int clickingOn = OPTIONMENUHOTSPOT_NONE;
+ int clickingOnSprite = 0;
+ int clickRelease = false;
+
+ while (!clickRelease) {
+
+ if (_dirtyAll) {
+ optionPicture->draw(*_mainSurface, 0, 0, 0, 0);
+ addDirtyRect(0, 0, TOON_SCREEN_WIDTH, TOON_SCREEN_HEIGHT);
+ } else {
+ optionPicture->drawWithRectList(*_mainSurface, 0, 0, 0, 0, _dirtyRects);
+ }
+ clearDirtyRects();
+
+ for (int entryNr = 0; entryNr < OPTIONMENU_ENTRYCOUNT; entryNr++) {
+ if (entries[entryNr].menuMask & menuMask) {
+ if (entries[entryNr].animateOnFrame) {
+ entries[entryNr].animateCurFrame++;
+ if (entries[entryNr].animateOnFrame <= entries[entryNr].animateCurFrame) {
+ entries[entryNr].activeFrame++;
+ if (entries[entryNr].activeFrame >= entries[entryNr].animation->_numFrames) {
+ entries[entryNr].activeFrame = 0;
+ if (entries[entryNr].playOnce) {
+ entries[entryNr].animateOnFrame = 0;
+ entries[entryNr].playOnce = false;
+ }
+ if (entryNr == 20 && entries[entryNr].animateOnFrame > 0) {
+ playSFX(-3, 128);
+ }
+ }
+ entries[entryNr].animateCurFrame = 0;
+ }
+ }
+ int32 frameNr = entries[entryNr].activeFrame;
+ entries[entryNr].animation->drawFrame(*_mainSurface, frameNr, 0, 0);
+ }
+ }
+
+ parseInput();
+
+ copyToVirtualScreen(true);
+ if (_firstFrame) {
+ _firstFrame = false;
+ fadeIn(5);
+ }
+ _system->delayMillis(17);
+
+ if (_mouseButton & 1) {
+ // left mouse button pushed down
+ clickingOn = OPTIONMENUHOTSPOT_NONE;
+ for (int entryNr = 0; entryNr < OPTIONMENU_ENTRYCOUNT; entryNr++) {
+ if (entries[entryNr].menuMask & menuMask) {
+ if (entries[entryNr].id != OPTIONMENUHOTSPOT_NONE) {
+ if (entries[entryNr].rect.contains(_mouseX, _mouseY)) {
+ clickingOn = entries[entryNr].id;
+ clickingOnSprite = entryNr;
+ ratioX = (_mouseX - entries[entryNr].rect.left) * 256 / entries[entryNr].rect.width();
+ }
+ }
+ }
+ }
+ } else {
+ // left mouse button released/not pushed down
+ if (clickingOn != OPTIONMENUHOTSPOT_NONE)
+ clickRelease = true;
+ }
+
+ // handle sliders
+ if (clickingOn == OPTIONMENUHOTSPOT_VOLUMEMUSICSLIDER) {
+ entries[clickingOnSprite].activeFrame = ratioX * (entries[clickingOnSprite].animation->_numFrames) / 256;
+ int vol = entries[clickingOnSprite].activeFrame * 256 / entries[clickingOnSprite].animation->_numFrames;
+ _audioManager->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, vol);
+ }
+
+ if (clickingOn == OPTIONMENUHOTSPOT_VOLUMEVOICESLIDER) {
+ entries[clickingOnSprite].activeFrame = ratioX * (entries[clickingOnSprite].animation->_numFrames) / 256;
+ int vol = entries[clickingOnSprite].activeFrame * 256 / entries[clickingOnSprite].animation->_numFrames;
+ _audioManager->_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, vol);
+ }
+
+ if (clickingOn == OPTIONMENUHOTSPOT_VOLUMESFXSLIDER) {
+ entries[clickingOnSprite].activeFrame = ratioX * (entries[clickingOnSprite].animation->_numFrames) / 256;
+ int vol = entries[clickingOnSprite].activeFrame * 256 / entries[clickingOnSprite].animation->_numFrames;
+ _audioManager->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, vol);
+ }
+
+ if (clickingOn == OPTIONMENUHOTSPOT_TEXTSPEED) {
+ entries[clickingOnSprite].activeFrame = ratioX * (entries[clickingOnSprite].animation->_numFrames) / 256;
+ }
+
+ if (clickingOn == OPTIONMENUHOTSPOT_PLAY) {
+ entries[0].activeFrame = entries[0].animation->_numFrames - 1;
+ } else {
+ entries[0].activeFrame = 0;
+ }
+
+ if (clickingOn == OPTIONMENUHOTSPOT_QUIT) {
+ entries[1].activeFrame = entries[1].animation->_numFrames - 1;
+ } else {
+ entries[1].activeFrame = 0;
+ }
+
+ if (_shouldQuit) {
+ clickingOn = OPTIONMENUHOTSPOT_NONE;
+ clickRelease = true;
+ doExit = true;
+ }
+ }
+
+ if (clickingOn == OPTIONMENUHOTSPOT_VOLUMEMUSIC) {
+ if (entries[9].activeFrame == 0) {
+ entries[9].activeFrame = 3;
+ _audioManager->muteMusic(false);
+ } else {
+ entries[9].activeFrame = 0;
+ _audioManager->muteMusic(true);
+ }
+ playSFX(-7, 128);
+ }
+
+ if (clickingOn == OPTIONMENUHOTSPOT_VOLUMEVOICE) {
+ if (entries[7].activeFrame == 0) {
+ entries[7].activeFrame = 3;
+ _audioManager->muteVoice(false);
+ } else {
+ entries[7].activeFrame = 0;
+ _audioManager->muteVoice(true);
+ }
+ playSFX(-7, 128);
+ }
+
+ if (clickingOn == OPTIONMENUHOTSPOT_VOLUMESFX) {
+ if (entries[5].activeFrame == 0) {
+ entries[5].activeFrame = 3;
+ _audioManager->muteSfx(false);
+ } else {
+ entries[5].activeFrame = 0;
+ _audioManager->muteSfx(true);
+ }
+ playSFX(-7, 128);
+ }
+
+ if (clickingOn == OPTIONMENUHOTSPOT_SPEAKERBUTTON) {
+ entries[11].animateOnFrame = 4;
+ entries[11].playOnce = true;
+
+ entries[19].animateOnFrame = 4;
+ entries[19].playOnce = true;
+
+ playSFX(-10, 128);
+ _audioManager->playVoice(316, true);
+ }
+
+ if (clickingOn == OPTIONMENUHOTSPOT_SPEAKERLEVER) {
+
+ entries[12].activeFrame = 1 - entries[12].activeFrame;
+ if(entries[12].activeFrame == 1) {
+ entries[20].animateOnFrame = 4;
+ entries[20].playOnce = false;
+ playSFX(-3, 128);
+ } else {
+ entries[20].playOnce = true;
+ }
+ playSFX(-9, 128);
+ }
+
+ if (clickingOn == OPTIONMENUHOTSPOT_TEXT) {
+
+ if (entries[4].activeFrame == 0) {
+ _showConversationText = false;
+ entries[4].activeFrame = 4;
+ } else if (entries[4].activeFrame == 4) {
+ _showConversationText = true;
+ setFont(true);
+ entries[4].activeFrame = 8;
+ } else if(entries[4].activeFrame == 8) {
+ _showConversationText = true;
+ setFont(false);
+ entries[4].activeFrame = 0;
+ }
+
+ playSFX(-9, 128);
+ }
+
+ // don't allow change to video mode
+ if (clickingOn == OPTIONMENUHOTSPOT_VIDEO_MODE) {
+ playSoundWrong();
+ }
+
+ if (clickingOn == OPTIONMENUHOTSPOT_PLAY) {
+ doExit = true;
+ exitGame = false;
+ _audioManager->playSFX(10, 128, true);
+ }
+
+ if (clickingOn == OPTIONMENUHOTSPOT_QUIT) {
+ doExit = true;
+ exitGame = true;
+ _shouldQuit = true;
+ _audioManager->playSFX(10, 128, true);
+ }
+ }
+
+ fadeOut(5);
+ _gameState->_mouseHidden = oldMouseHidden;
+ _gameState->_inMenu = false;
+ _firstFrame = true;
+ _gameState->_currentScrollValue = oldScrollValue;
+
+ restorePalette();
+ dirtyAllScreen();
+
+ return exitGame;
+}
+
bool ToonEngine::showMainmenu(bool &loadedGame) {
Picture *mainmenuPicture = new Picture(this);
mainmenuPicture->loadPicture("TITLESCR.CPS");
mainmenuPicture->setupPalette();
flushPalette(false);
- MainMenuEntry entries[MAINMENU_ENTRYCOUNT];
+ MenuEntry entries[MAINMENU_ENTRYCOUNT];
for (int entryNr = 0; entryNr < MAINMENU_ENTRYCOUNT; entryNr++) {
entries[entryNr].menuMask = mainMenuFiles[entryNr].menuMask;
@@ -630,7 +962,7 @@ bool ToonEngine::showMainmenu(bool &loadedGame) {
entries[entryNr].activeFrame = 0;
}
- setCursor(1);
+ setCursor(0);
bool doExit = false;
bool exitGame = false;
@@ -822,6 +1154,7 @@ ToonEngine::ToonEngine(OSystem *syst, const ADGameDescription *gameDescription)
_inventoryPicture = NULL;
_currentMask = NULL;
_showConversationText = true;
+ _useAlternativeFont = false;
_isDemo = _gameDescription->flags & ADGF_DEMO;
DebugMan.addDebugChannel(kDebugAnim, "Anim", "Animation debug level");
@@ -1853,6 +2186,17 @@ void ToonEngine::initFonts() {
_fontEZ = new Animation(this);
_fontEZ->loadAnimation("EZFONT.CAF");
+
+ setFont(false);
+}
+
+void ToonEngine::setFont(bool alternative) {
+ if (alternative) {
+ _currentFont = _fontEZ;
+ } else {
+ _currentFont = _fontToon;
+ }
+ _useAlternativeFont = alternative;
}
void ToonEngine::drawInfoLine() {
@@ -1870,7 +2214,7 @@ void ToonEngine::drawInfoLine() {
}
if (infoTool) {
_fontRenderer->setFontColor(0xc8, 0xdd, 0xe3);
- _fontRenderer->setFont(_fontToon);
+ _fontRenderer->setFont(_currentFont);
_fontRenderer->renderText(320 + _gameState->_currentScrollValue, 398, infoTool, 5);
}
}
@@ -1998,6 +2342,8 @@ int32 ToonEngine::simpleCharacterTalk(int32 dialogid) {
_audioManager->playVoice(myId, false);
} else {
myId = _genericTexts->getId(dialogid - 1000);
+ if (myId == -1)
+ return 0;
_audioManager->playVoice(myId, true);
}
@@ -2936,7 +3282,7 @@ Character *ToonEngine::getCharacterById(int32 charId) {
void ToonEngine::drawConversationLine() {
if (_currentTextLine && _showConversationText) {
_fontRenderer->setFontColorByCharacter(_currentTextLineCharacterId);
- _fontRenderer->setFont(_fontToon);
+ _fontRenderer->setFont(_currentFont);
_fontRenderer->renderMultiLineText(_currentTextLineX, _currentTextLineY, _currentTextLine, 0);
}
}
diff --git a/engines/toon/toon.h b/engines/toon/toon.h
index 6903e5de57..f419d491c6 100644
--- a/engines/toon/toon.h
+++ b/engines/toon/toon.h
@@ -111,6 +111,7 @@ public:
Common::Error run();
GUI::Debugger *getDebugger() { return _console; }
bool showMainmenu(bool &loadedGame);
+ bool showOptions();
void init();
bool loadToonDat();
char **loadTextsVariants(Common::File &in);
@@ -122,6 +123,7 @@ public:
void parseInput();
void initChapter();
void initFonts();
+ void setFont(bool alternative);
void loadScene(int32 SceneId, bool forGameLoad = false);
void exitScene();
void loadCursor();
@@ -421,6 +423,7 @@ protected:
FontRenderer *_fontRenderer;
Animation *_fontToon;
Animation *_fontEZ;
+ Animation *_currentFont;
AudioManager *_audioManager;
@@ -431,6 +434,7 @@ protected:
bool _firstFrame;
bool _isDemo;
bool _showConversationText;
+ bool _useAlternativeFont;
bool _needPaletteFlush;
private:
ToonConsole *_console;
diff --git a/engines/zvision/graphics/effects/fog.cpp b/engines/zvision/graphics/effects/fog.cpp
index 32a01915d3..7b65f60f24 100644
--- a/engines/zvision/graphics/effects/fog.cpp
+++ b/engines/zvision/graphics/effects/fog.cpp
@@ -142,9 +142,9 @@ void FogFx::update() {
for (uint8 i = 0; i < 31; i++) {
float perc = (float)i / 31.0;
- uint8 cr = (float)_r * perc;
- uint8 cg = (float)_g * perc;
- uint8 cb = (float)_b * perc;
+ uint8 cr = (uint8)((float)_r * perc);
+ uint8 cg = (uint8)((float)_g * perc);
+ uint8 cb = (uint8)((float)_b * perc);
_colorMap[i] = _engine->_resourcePixelFormat.RGBToColor(cr << 3, cg << 3, cb << 3);
}
}
diff --git a/engines/zvision/graphics/effects/wave.cpp b/engines/zvision/graphics/effects/wave.cpp
index cec631611b..d2887b3112 100644
--- a/engines/zvision/graphics/effects/wave.cpp
+++ b/engines/zvision/graphics/effects/wave.cpp
@@ -54,7 +54,7 @@ WaveFx::WaveFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, in
int16 dx = (x - quarterWidth);
int16 dy = (y - quarterHeight);
- _ampls[i][x + y * _halfWidth] = ampl * sin(sqrt(dx * dx / (float)centerX + dy * dy / (float)centerY) / (-waveln * 3.1415926) + phase);
+ _ampls[i][x + y * _halfWidth] = (int8)(ampl * sin(sqrt(dx * dx / (float)centerX + dy * dy / (float)centerY) / (-waveln * 3.1415926) + phase));
}
phase += spd;
}
diff --git a/engines/zvision/graphics/render_manager.cpp b/engines/zvision/graphics/render_manager.cpp
index 3772d5909e..ce0a02a1ad 100644
--- a/engines/zvision/graphics/render_manager.cpp
+++ b/engines/zvision/graphics/render_manager.cpp
@@ -1108,7 +1108,7 @@ void RenderManager::updateRotation() {
int16 newPosition = startPosition + _velocity;
int16 screenHeight = getBkgSize().y;
- int16 tiltGap = _renderTable.getTiltGap();
+ int16 tiltGap = (int16)_renderTable.getTiltGap();
if (newPosition >= (screenHeight - tiltGap))
newPosition = screenHeight - tiltGap;
@@ -1143,7 +1143,7 @@ void RenderManager::checkBorders() {
int16 newPosition = startPosition;
int16 screenHeight = getBkgSize().y;
- int16 tiltGap = _renderTable.getTiltGap();
+ int16 tiltGap = (int16)_renderTable.getTiltGap();
if (newPosition >= (screenHeight - tiltGap))
newPosition = screenHeight - tiltGap;
diff --git a/engines/zvision/scripting/actions.cpp b/engines/zvision/scripting/actions.cpp
index 21c97e766b..9a8b734e0c 100644
--- a/engines/zvision/scripting/actions.cpp
+++ b/engines/zvision/scripting/actions.cpp
@@ -1077,7 +1077,7 @@ ActionTtyText::ActionTtyText(ZVision *engine, int32 slotkey, const Common::Strin
char filename[64];
int32 x1 = 0, y1 = 0, x2 = 0, y2 = 0;
- sscanf(line.c_str(), "%d %d %d %d %64s %u", &x1, &y1, &x2, &y2, filename, &_delay);
+ sscanf(line.c_str(), "%d %d %d %d %63s %u", &x1, &y1, &x2, &y2, filename, &_delay);
_r = Common::Rect(x1, y1, x2, y2);
_filename = Common::String(filename);
}
diff --git a/engines/zvision/scripting/effects/distort_effect.cpp b/engines/zvision/scripting/effects/distort_effect.cpp
index 78c4a1b9a8..113b5d048d 100644
--- a/engines/zvision/scripting/effects/distort_effect.cpp
+++ b/engines/zvision/scripting/effects/distort_effect.cpp
@@ -52,7 +52,7 @@ DistortNode::DistortNode(ZVision *engine, uint32 key, int16 speed, float startAn
_diffLinScale = endLineScale - startLineScale;
_frmSpeed = (float)speed / 15.0;
- _frames = ceil((5.0 - _frmSpeed * 2.0) / _frmSpeed);
+ _frames = (int)ceil((5.0 - _frmSpeed * 2.0) / _frmSpeed);
if (_frames <= 0)
_frames = 1;
diff --git a/engines/zvision/scripting/effects/music_effect.cpp b/engines/zvision/scripting/effects/music_effect.cpp
index ad3c0f6d22..e3fdc96dba 100644
--- a/engines/zvision/scripting/effects/music_effect.cpp
+++ b/engines/zvision/scripting/effects/music_effect.cpp
@@ -140,7 +140,7 @@ bool MusicNode::process(uint32 deltaTimeInMillis) {
if (_crossfadeTime > 0) {
if ((int32)deltaTimeInMillis > _crossfadeTime)
deltaTimeInMillis = _crossfadeTime;
- _newvol += floor(((float)(_crossfadeTarget - _newvol) / (float)_crossfadeTime)) * (float)deltaTimeInMillis;
+ _newvol += (int)(floor(((float)(_crossfadeTarget - _newvol) / (float)_crossfadeTime)) * (float)deltaTimeInMillis);
_crossfadeTime -= deltaTimeInMillis;
} else {
_crossfade = false;
diff --git a/engines/zvision/scripting/menu.cpp b/engines/zvision/scripting/menu.cpp
index 16aa57e3ae..e7775cbe3f 100644
--- a/engines/zvision/scripting/menu.cpp
+++ b/engines/zvision/scripting/menu.cpp
@@ -50,9 +50,9 @@ MenuZGI::MenuZGI(ZVision *engine) :
scrolled[0] = false;
scrolled[1] = false;
scrolled[2] = false;
- scrollPos[0] = 0.0;
- scrollPos[1] = 0.0;
- scrollPos[2] = 0.0;
+ scrollPos[0] = 0;
+ scrollPos[1] = 0;
+ scrollPos[2] = 0;
mouseOnItem = -1;
redraw = false;
clean = false;
@@ -361,11 +361,11 @@ void MenuZGI::process(uint32 deltatime) {
if (scrl == 0)
scrl = 1.0;
- scrollPos [kMenuItem] += scrl;
+ scrollPos[kMenuItem] += (int)scrl;
if (scrollPos[kMenuItem] >= 0) {
scrolled[kMenuItem] = true;
- scrollPos [kMenuItem] = 0;
+ scrollPos[kMenuItem] = 0;
}
}
if (redraw) {
@@ -430,11 +430,11 @@ void MenuZGI::process(uint32 deltatime) {
if (scrl == 0)
scrl = 1.0;
- scrollPos [kMenuMagic] += scrl;
+ scrollPos[kMenuMagic] += (int)scrl;
if (scrollPos[kMenuMagic] >= 600) {
scrolled[kMenuMagic] = true;
- scrollPos [kMenuMagic] = 600;
+ scrollPos[kMenuMagic] = 600;
}
}
if (redraw) {
@@ -495,11 +495,11 @@ void MenuZGI::process(uint32 deltatime) {
if (scrl == 0)
scrl = 1.0;
- scrollPos [kMenuMain] += scrl;
+ scrollPos[kMenuMain] += (int)scrl;
if (scrollPos[kMenuMain] >= 0) {
scrolled[kMenuMain] = true;
- scrollPos [kMenuMain] = 0;
+ scrollPos[kMenuMain] = 0;
}
}
if (redraw) {
@@ -553,7 +553,7 @@ MenuNemesis::MenuNemesis(ZVision *engine) :
MenuHandler(engine) {
inmenu = false;
scrolled = false;
- scrollPos = 0.0;
+ scrollPos = 0;
mouseOnItem = -1;
redraw = false;
delay = 0;
@@ -696,7 +696,7 @@ void MenuNemesis::process(uint32 deltatime) {
if (scrl == 0)
scrl = 1.0;
- scrollPos += scrl;
+ scrollPos += (int)scrl;
redraw = true;
}
@@ -743,10 +743,10 @@ void MenuNemesis::process(uint32 deltatime) {
if (scrl == 0)
scrl = 1.0;
- Common::Rect cl(64, 32 + scrollPos - scrl, 64 + 512, 32 + scrollPos + 1);
+ Common::Rect cl(64, (int16)(32 + scrollPos - scrl), 64 + 512, 32 + scrollPos + 1);
_engine->getRenderManager()->clearMenuSurface(cl);
- scrollPos -= scrl;
+ scrollPos -= (int)scrl;
redraw = true;
} else
scrollPos = -32;
diff --git a/engines/zvision/text/truetype_font.cpp b/engines/zvision/text/truetype_font.cpp
index 7ad8d6db61..acb053ea8d 100644
--- a/engines/zvision/text/truetype_font.cpp
+++ b/engines/zvision/text/truetype_font.cpp
@@ -199,12 +199,12 @@ void StyledTTFont::drawChar(Graphics::Surface *dst, byte chr, int x, int y, uint
if (_font) {
_font->drawChar(dst, chr, x, y, color);
if (_style & TTF_STYLE_UNDERLINE) {
- int16 pos = floor(_font->getFontHeight() * 0.87);
+ int16 pos = (int16)floor(_font->getFontHeight() * 0.87);
int thk = MAX((int)(_font->getFontHeight() * 0.05), 1);
dst->fillRect(Common::Rect(x, y + pos, x + _font->getCharWidth(chr), y + pos + thk), color);
}
if (_style & TTF_STYLE_STRIKETHROUGH) {
- int16 pos = floor(_font->getFontHeight() * 0.60);
+ int16 pos = (int16)floor(_font->getFontHeight() * 0.60);
int thk = MAX((int)(_font->getFontHeight() * 0.05), 1);
dst->fillRect(Common::Rect(x, y + pos, x + _font->getCharWidth(chr), y + pos + thk), color);
}
@@ -216,7 +216,7 @@ void StyledTTFont::drawString(Graphics::Surface *dst, const Common::String &str,
Common::U32String u32str = convertUtf8ToUtf32(str);
_font->drawString(dst, u32str, x, y, w, color, align);
if (_style & TTF_STYLE_UNDERLINE) {
- int16 pos = floor(_font->getFontHeight() * 0.87);
+ int16 pos = (int16)floor(_font->getFontHeight() * 0.87);
int16 wd = MIN(_font->getStringWidth(u32str), w);
int16 stX = x;
if (align == Graphics::kTextAlignCenter)
@@ -229,7 +229,7 @@ void StyledTTFont::drawString(Graphics::Surface *dst, const Common::String &str,
dst->fillRect(Common::Rect(stX, y + pos, stX + wd, y + pos + thk), color);
}
if (_style & TTF_STYLE_STRIKETHROUGH) {
- int16 pos = floor(_font->getFontHeight() * 0.60);
+ int16 pos = (int16)floor(_font->getFontHeight() * 0.60);
int16 wd = MIN(_font->getStringWidth(u32str), w);
int16 stX = x;
if (align == Graphics::kTextAlignCenter)
diff --git a/graphics/fonts/ttf.cpp b/graphics/fonts/ttf.cpp
index 7373a965c8..dc7335f1c2 100644
--- a/graphics/fonts/ttf.cpp
+++ b/graphics/fonts/ttf.cpp
@@ -474,11 +474,11 @@ bool TTFFont::cacheGlyph(Glyph &glyph, uint32 chr) const {
switch (bitmap.pixel_mode) {
case FT_PIXEL_MODE_MONO:
- for (uint y = 0; y < bitmap.rows; ++y) {
+ for (int y = 0; y < (int)bitmap.rows; ++y) {
const uint8 *curSrc = src;
uint8 mask = 0;
- for (uint x = 0; x < bitmap.width; ++x) {
+ for (int x = 0; x < (int)bitmap.width; ++x) {
if ((x % 8) == 0)
mask = *curSrc++;
@@ -494,7 +494,7 @@ bool TTFFont::cacheGlyph(Glyph &glyph, uint32 chr) const {
break;
case FT_PIXEL_MODE_GRAY:
- for (uint y = 0; y < bitmap.rows; ++y) {
+ for (int y = 0; y < (int)bitmap.rows; ++y) {
memcpy(dst, src, bitmap.width);
dst += glyph.image.pitch;
src += srcPitch;
diff --git a/gui/Tooltip.cpp b/gui/Tooltip.cpp
index e5f06bcafe..ba313ee34f 100644
--- a/gui/Tooltip.cpp
+++ b/gui/Tooltip.cpp
@@ -40,6 +40,8 @@ Tooltip::Tooltip() :
void Tooltip::setup(Dialog *parent, Widget *widget, int x, int y) {
assert(widget->hasTooltip());
+ _parent = parent;
+
_maxWidth = g_gui.xmlEval()->getVar("Globals.Tooltip.MaxWidth", 100);
_xdelta = g_gui.xmlEval()->getVar("Globals.Tooltip.XDelta", 0);
_ydelta = g_gui.xmlEval()->getVar("Globals.Tooltip.YDelta", 0);
diff --git a/gui/Tooltip.h b/gui/Tooltip.h
index f83fc40966..58b6d8a429 100644
--- a/gui/Tooltip.h
+++ b/gui/Tooltip.h
@@ -32,6 +32,9 @@ namespace GUI {
class Widget;
class Tooltip : public Dialog {
+private:
+ Dialog *_parent;
+
public:
Tooltip();
@@ -39,12 +42,30 @@ public:
void drawDialog();
protected:
- virtual void handleMouseDown(int x, int y, int button, int clickCount) { close(); }
- virtual void handleMouseUp(int x, int y, int button, int clickCount) { close(); }
- virtual void handleMouseWheel(int x, int y, int direction) { close(); }
- virtual void handleKeyDown(Common::KeyState state) { close(); }
- virtual void handleKeyUp(Common::KeyState state) { close(); }
- virtual void handleMouseMoved(int x, int y, int button) { close(); }
+ virtual void handleMouseDown(int x, int y, int button, int clickCount) {
+ close();
+ _parent->handleMouseDown(x + (getAbsX() - _parent->getAbsX()), y + (getAbsY() - _parent->getAbsY()), button, clickCount);
+ }
+ virtual void handleMouseUp(int x, int y, int button, int clickCount) {
+ close();
+ _parent->handleMouseUp(x + (getAbsX() - _parent->getAbsX()), y + (getAbsY() - _parent->getAbsY()), button, clickCount);
+ }
+ virtual void handleMouseWheel(int x, int y, int direction) {
+ close();
+ _parent->handleMouseWheel(x + (getAbsX() - _parent->getAbsX()), y + (getAbsX() - _parent->getAbsX()), direction);
+ }
+ virtual void handleKeyDown(Common::KeyState state) {
+ close();
+ _parent->handleKeyDown(state);
+ }
+ virtual void handleKeyUp(Common::KeyState state) {
+ close();
+ _parent->handleKeyUp(state);
+ }
+ virtual void handleMouseMoved(int x, int y, int button) {
+ close();
+ _parent->handleMouseMoved(x + (getAbsX() - _parent->getAbsX()), y + (getAbsY() - _parent->getAbsY()), button);
+ }
int _maxWidth;
int _xdelta, _ydelta;
diff --git a/gui/dialog.cpp b/gui/dialog.cpp
index fa4e508494..315c24e9bf 100644
--- a/gui/dialog.cpp
+++ b/gui/dialog.cpp
@@ -219,7 +219,7 @@ void Dialog::handleMouseWheel(int x, int y, int direction) {
if (!w)
w = _focusedWidget;
if (w)
- w->handleMouseWheel(x, y, direction);
+ w->handleMouseWheel(x - (w->getAbsX() - _x), y - (w->getAbsY() - _y), direction);
}
void Dialog::handleKeyDown(Common::KeyState state) {
diff --git a/image/codecs/cinepak.cpp b/image/codecs/cinepak.cpp
index 8464aa3889..32f6be2cd5 100644
--- a/image/codecs/cinepak.cpp
+++ b/image/codecs/cinepak.cpp
@@ -21,6 +21,7 @@
*/
#include "image/codecs/cinepak.h"
+#include "image/codecs/cinepak_tables.h"
#include "common/debug.h"
#include "common/stream.h"
@@ -34,23 +35,328 @@
namespace Image {
-#define PUT_PIXEL(offset, lum, u, v) \
- if (_pixelFormat.bytesPerPixel != 1) { \
- byte r = _clipTable[lum + (v << 1)]; \
- byte g = _clipTable[lum - (u >> 1) - v]; \
- byte b = _clipTable[lum + (u << 1)]; \
- \
- if (_pixelFormat.bytesPerPixel == 2) \
- *((uint16 *)_curFrame.surface->getPixels() + offset) = _pixelFormat.RGBToColor(r, g, b); \
- else \
- *((uint32 *)_curFrame.surface->getPixels() + offset) = _pixelFormat.RGBToColor(r, g, b); \
- } else \
- *((byte *)_curFrame.surface->getPixels() + offset) = lum
-
-CinepakDecoder::CinepakDecoder(int bitsPerPixel) : Codec() {
- _curFrame.surface = NULL;
- _curFrame.strips = NULL;
+namespace {
+
+inline void convertYUVToRGB(const byte *clipTable, byte y, int8 u, int8 v, byte &r, byte &g, byte &b) {
+ r = clipTable[y + (v << 1)];
+ g = clipTable[y - (u >> 1) - v];
+ b = clipTable[y + (u << 1)];
+}
+
+inline uint32 convertYUVToColor(const byte *clipTable, const Graphics::PixelFormat &format, byte y, byte u, byte v) {
+ byte r, g, b;
+ convertYUVToRGB(clipTable, y, u, v, r, g, b);
+ return format.RGBToColor(r, g, b);
+}
+
+inline uint16 createDitherTableIndex(const byte *clipTable, byte y, int8 u, int8 v) {
+ byte r, g, b;
+ convertYUVToRGB(clipTable, y, u, v, r, g, b);
+ return ((r & 0xF8) << 6) |
+ ((g & 0xF8) << 1) |
+ ((b & 0xF0) >> 4);
+}
+
+/**
+ * Put a raw pixel to the destination surface
+ */
+template<typename PixelInt>
+inline void putPixelRaw(PixelInt *dst, const byte *clipTable, const Graphics::PixelFormat &format, byte y, byte u, byte v) {
+ *dst = convertYUVToColor(clipTable, format, y, u, v);
+}
+
+/**
+ * Specialized putPixelRaw for palettized 8bpp output
+ */
+template<>
+inline void putPixelRaw(byte *dst, const byte *clipTable, const Graphics::PixelFormat &format, byte y, byte u, byte v) {
+ *dst = y;
+}
+
+/**
+ * The default codebook converter: raw output.
+ */
+struct CodebookConverterRaw {
+ template<typename PixelInt>
+ static inline void decodeBlock1(byte codebookIndex, const CinepakStrip &strip, PixelInt *(&rows)[4], const byte *clipTable, const byte *colorMap, const Graphics::PixelFormat &format) {
+ const CinepakCodebook &codebook = strip.v1_codebook[codebookIndex];
+ putPixelRaw(rows[0] + 0, clipTable, format, codebook.y[0], codebook.u, codebook.v);
+ putPixelRaw(rows[0] + 1, clipTable, format, codebook.y[0], codebook.u, codebook.v);
+ putPixelRaw(rows[1] + 0, clipTable, format, codebook.y[0], codebook.u, codebook.v);
+ putPixelRaw(rows[1] + 1, clipTable, format, codebook.y[0], codebook.u, codebook.v);
+
+ putPixelRaw(rows[0] + 2, clipTable, format, codebook.y[1], codebook.u, codebook.v);
+ putPixelRaw(rows[0] + 3, clipTable, format, codebook.y[1], codebook.u, codebook.v);
+ putPixelRaw(rows[1] + 2, clipTable, format, codebook.y[1], codebook.u, codebook.v);
+ putPixelRaw(rows[1] + 3, clipTable, format, codebook.y[1], codebook.u, codebook.v);
+
+ putPixelRaw(rows[2] + 0, clipTable, format, codebook.y[2], codebook.u, codebook.v);
+ putPixelRaw(rows[2] + 1, clipTable, format, codebook.y[2], codebook.u, codebook.v);
+ putPixelRaw(rows[3] + 0, clipTable, format, codebook.y[2], codebook.u, codebook.v);
+ putPixelRaw(rows[3] + 1, clipTable, format, codebook.y[2], codebook.u, codebook.v);
+
+ putPixelRaw(rows[2] + 2, clipTable, format, codebook.y[3], codebook.u, codebook.v);
+ putPixelRaw(rows[2] + 3, clipTable, format, codebook.y[3], codebook.u, codebook.v);
+ putPixelRaw(rows[3] + 2, clipTable, format, codebook.y[3], codebook.u, codebook.v);
+ putPixelRaw(rows[3] + 3, clipTable, format, codebook.y[3], codebook.u, codebook.v);
+ }
+
+ template<typename PixelInt>
+ static inline void decodeBlock4(const byte (&codebookIndex)[4], const CinepakStrip &strip, PixelInt *(&rows)[4], const byte *clipTable, const byte *colorMap, const Graphics::PixelFormat &format) {
+ const CinepakCodebook &codebook1 = strip.v4_codebook[codebookIndex[0]];
+ putPixelRaw(rows[0] + 0, clipTable, format, codebook1.y[0], codebook1.u, codebook1.v);
+ putPixelRaw(rows[0] + 1, clipTable, format, codebook1.y[1], codebook1.u, codebook1.v);
+ putPixelRaw(rows[1] + 0, clipTable, format, codebook1.y[2], codebook1.u, codebook1.v);
+ putPixelRaw(rows[1] + 1, clipTable, format, codebook1.y[3], codebook1.u, codebook1.v);
+
+ const CinepakCodebook &codebook2 = strip.v4_codebook[codebookIndex[1]];
+ putPixelRaw(rows[0] + 2, clipTable, format, codebook2.y[0], codebook2.u, codebook2.v);
+ putPixelRaw(rows[0] + 3, clipTable, format, codebook2.y[1], codebook2.u, codebook2.v);
+ putPixelRaw(rows[1] + 2, clipTable, format, codebook2.y[2], codebook2.u, codebook2.v);
+ putPixelRaw(rows[1] + 3, clipTable, format, codebook2.y[3], codebook2.u, codebook2.v);
+
+ const CinepakCodebook &codebook3 = strip.v4_codebook[codebookIndex[2]];
+ putPixelRaw(rows[2] + 0, clipTable, format, codebook3.y[0], codebook3.u, codebook3.v);
+ putPixelRaw(rows[2] + 1, clipTable, format, codebook3.y[1], codebook3.u, codebook3.v);
+ putPixelRaw(rows[3] + 0, clipTable, format, codebook3.y[2], codebook3.u, codebook3.v);
+ putPixelRaw(rows[3] + 1, clipTable, format, codebook3.y[3], codebook3.u, codebook3.v);
+
+ const CinepakCodebook &codebook4 = strip.v4_codebook[codebookIndex[3]];
+ putPixelRaw(rows[2] + 2, clipTable, format, codebook4.y[0], codebook4.u, codebook4.v);
+ putPixelRaw(rows[2] + 3, clipTable, format, codebook4.y[1], codebook4.u, codebook4.v);
+ putPixelRaw(rows[3] + 2, clipTable, format, codebook4.y[2], codebook4.u, codebook4.v);
+ putPixelRaw(rows[3] + 3, clipTable, format, codebook4.y[3], codebook4.u, codebook4.v);
+ }
+};
+
+/**
+ * Codebook converter that dithers in VFW-style
+ */
+struct CodebookConverterDitherVFW {
+ static inline void decodeBlock1(byte codebookIndex, const CinepakStrip &strip, byte *(&rows)[4], const byte *clipTable, const byte *colorMap, const Graphics::PixelFormat &format) {
+ const CinepakCodebook &codebook = strip.v1_codebook[codebookIndex];
+ byte blockBuffer[16];
+ ditherCodebookSmooth(codebook, blockBuffer, colorMap);
+ rows[0][0] = blockBuffer[0];
+ rows[0][1] = blockBuffer[1];
+ rows[0][2] = blockBuffer[2];
+ rows[0][3] = blockBuffer[3];
+ rows[1][0] = blockBuffer[4];
+ rows[1][1] = blockBuffer[5];
+ rows[1][2] = blockBuffer[6];
+ rows[1][3] = blockBuffer[7];
+ rows[2][0] = blockBuffer[8];
+ rows[2][1] = blockBuffer[9];
+ rows[2][2] = blockBuffer[10];
+ rows[2][3] = blockBuffer[11];
+ rows[3][0] = blockBuffer[12];
+ rows[3][1] = blockBuffer[13];
+ rows[3][2] = blockBuffer[14];
+ rows[3][3] = blockBuffer[15];
+ }
+
+ static inline void decodeBlock4(const byte (&codebookIndex)[4], const CinepakStrip &strip, byte *(&rows)[4], const byte *clipTable, const byte *colorMap, const Graphics::PixelFormat &format) {
+ byte blockBuffer[16];
+ ditherCodebookDetail(strip.v4_codebook[codebookIndex[0]], blockBuffer, colorMap);
+ rows[0][0] = blockBuffer[0];
+ rows[0][1] = blockBuffer[1];
+ rows[1][0] = blockBuffer[4];
+ rows[1][1] = blockBuffer[5];
+
+ ditherCodebookDetail(strip.v4_codebook[codebookIndex[1]], blockBuffer, colorMap);
+ rows[0][2] = blockBuffer[2];
+ rows[0][3] = blockBuffer[3];
+ rows[1][2] = blockBuffer[6];
+ rows[1][3] = blockBuffer[7];
+
+ ditherCodebookDetail(strip.v4_codebook[codebookIndex[2]], blockBuffer, colorMap);
+ rows[2][0] = blockBuffer[8];
+ rows[2][1] = blockBuffer[9];
+ rows[3][0] = blockBuffer[12];
+ rows[3][1] = blockBuffer[13];
+
+ ditherCodebookDetail(strip.v4_codebook[codebookIndex[3]], blockBuffer, colorMap);
+ rows[2][2] = blockBuffer[10];
+ rows[2][3] = blockBuffer[11];
+ rows[3][2] = blockBuffer[14];
+ rows[3][3] = blockBuffer[15];
+ }
+
+private:
+ static inline void ditherCodebookDetail(const CinepakCodebook &codebook, byte *dst, const byte *colorMap) {
+ int uLookup = (byte)codebook.u * 2;
+ int vLookup = (byte)codebook.v * 2;
+ uint32 uv1 = s_uLookup[uLookup] | s_vLookup[vLookup];
+ uint32 uv2 = s_uLookup[uLookup + 1] | s_vLookup[vLookup + 1];
+
+ int yLookup1 = codebook.y[0] * 2;
+ int yLookup2 = codebook.y[1] * 2;
+ int yLookup3 = codebook.y[2] * 2;
+ int yLookup4 = codebook.y[3] * 2;
+
+ uint32 pixelGroup1 = uv2 | s_yLookup[yLookup1 + 1];
+ uint32 pixelGroup2 = uv2 | s_yLookup[yLookup2 + 1];
+ uint32 pixelGroup3 = uv1 | s_yLookup[yLookup3];
+ uint32 pixelGroup4 = uv1 | s_yLookup[yLookup4];
+ uint32 pixelGroup5 = uv1 | s_yLookup[yLookup1];
+ uint32 pixelGroup6 = uv1 | s_yLookup[yLookup2];
+ uint32 pixelGroup7 = uv2 | s_yLookup[yLookup3 + 1];
+ uint32 pixelGroup8 = uv2 | s_yLookup[yLookup4 + 1];
+
+ dst[0] = getRGBLookupEntry(colorMap, pixelGroup1 & 0xFFFF);
+ dst[1] = getRGBLookupEntry(colorMap, pixelGroup2 >> 16);
+ dst[2] = getRGBLookupEntry(colorMap, pixelGroup5 & 0xFFFF);
+ dst[3] = getRGBLookupEntry(colorMap, pixelGroup6 >> 16);
+ dst[4] = getRGBLookupEntry(colorMap, pixelGroup3 & 0xFFFF);
+ dst[5] = getRGBLookupEntry(colorMap, pixelGroup4 >> 16);
+ dst[6] = getRGBLookupEntry(colorMap, pixelGroup7 & 0xFFFF);
+ dst[7] = getRGBLookupEntry(colorMap, pixelGroup8 >> 16);
+ dst[8] = getRGBLookupEntry(colorMap, pixelGroup1 >> 16);
+ dst[9] = getRGBLookupEntry(colorMap, pixelGroup6 & 0xFFFF);
+ dst[10] = getRGBLookupEntry(colorMap, pixelGroup5 >> 16);
+ dst[11] = getRGBLookupEntry(colorMap, pixelGroup2 & 0xFFFF);
+ dst[12] = getRGBLookupEntry(colorMap, pixelGroup3 >> 16);
+ dst[13] = getRGBLookupEntry(colorMap, pixelGroup8 & 0xFFFF);
+ dst[14] = getRGBLookupEntry(colorMap, pixelGroup7 >> 16);
+ dst[15] = getRGBLookupEntry(colorMap, pixelGroup4 & 0xFFFF);
+ }
+
+ static inline void ditherCodebookSmooth(const CinepakCodebook &codebook, byte *dst, const byte *colorMap) {
+ int uLookup = (byte)codebook.u * 2;
+ int vLookup = (byte)codebook.v * 2;
+ uint32 uv1 = s_uLookup[uLookup] | s_vLookup[vLookup];
+ uint32 uv2 = s_uLookup[uLookup + 1] | s_vLookup[vLookup + 1];
+
+ int yLookup1 = codebook.y[0] * 2;
+ int yLookup2 = codebook.y[1] * 2;
+ int yLookup3 = codebook.y[2] * 2;
+ int yLookup4 = codebook.y[3] * 2;
+
+ uint32 pixelGroup1 = uv2 | s_yLookup[yLookup1 + 1];
+ uint32 pixelGroup2 = uv1 | s_yLookup[yLookup2];
+ uint32 pixelGroup3 = uv1 | s_yLookup[yLookup1];
+ uint32 pixelGroup4 = uv2 | s_yLookup[yLookup2 + 1];
+ uint32 pixelGroup5 = uv2 | s_yLookup[yLookup3 + 1];
+ uint32 pixelGroup6 = uv1 | s_yLookup[yLookup3];
+ uint32 pixelGroup7 = uv1 | s_yLookup[yLookup4];
+ uint32 pixelGroup8 = uv2 | s_yLookup[yLookup4 + 1];
+
+ dst[0] = getRGBLookupEntry(colorMap, pixelGroup1 & 0xFFFF);
+ dst[1] = getRGBLookupEntry(colorMap, pixelGroup1 >> 16);
+ dst[2] = getRGBLookupEntry(colorMap, pixelGroup2 & 0xFFFF);
+ dst[3] = getRGBLookupEntry(colorMap, pixelGroup2 >> 16);
+ dst[4] = getRGBLookupEntry(colorMap, pixelGroup3 & 0xFFFF);
+ dst[5] = getRGBLookupEntry(colorMap, pixelGroup3 >> 16);
+ dst[6] = getRGBLookupEntry(colorMap, pixelGroup4 & 0xFFFF);
+ dst[7] = getRGBLookupEntry(colorMap, pixelGroup4 >> 16);
+ dst[8] = getRGBLookupEntry(colorMap, pixelGroup5 >> 16);
+ dst[9] = getRGBLookupEntry(colorMap, pixelGroup6 & 0xFFFF);
+ dst[10] = getRGBLookupEntry(colorMap, pixelGroup7 >> 16);
+ dst[11] = getRGBLookupEntry(colorMap, pixelGroup8 & 0xFFFF);
+ dst[12] = getRGBLookupEntry(colorMap, pixelGroup6 >> 16);
+ dst[13] = getRGBLookupEntry(colorMap, pixelGroup5 & 0xFFFF);
+ dst[14] = getRGBLookupEntry(colorMap, pixelGroup8 >> 16);
+ dst[15] = getRGBLookupEntry(colorMap, pixelGroup7 & 0xFFFF);
+ }
+
+ static inline byte getRGBLookupEntry(const byte *colorMap, uint16 index) {
+ return colorMap[s_defaultPaletteLookup[CLIP<int>(index, 0, 1024)]];
+ }
+};
+
+/**
+ * Codebook converter that dithers in QT-style
+ */
+struct CodebookConverterDitherQT {
+ static inline void decodeBlock1(byte codebookIndex, const CinepakStrip &strip, byte *(&rows)[4], const byte *clipTable, const byte *colorMap, const Graphics::PixelFormat &format) {
+ const byte *colorPtr = strip.v1_dither + (codebookIndex << 2);
+ WRITE_UINT32(rows[0], READ_UINT32(colorPtr));
+ WRITE_UINT32(rows[1], READ_UINT32(colorPtr + 1024));
+ WRITE_UINT32(rows[2], READ_UINT32(colorPtr + 2048));
+ WRITE_UINT32(rows[3], READ_UINT32(colorPtr + 3072));
+ }
+
+ static inline void decodeBlock4(const byte (&codebookIndex)[4], const CinepakStrip &strip, byte *(&rows)[4], const byte *clipTable, const byte *colorMap, const Graphics::PixelFormat &format) {
+ const byte *colorPtr = strip.v4_dither + (codebookIndex[0] << 2);
+ WRITE_UINT16(rows[0] + 0, READ_UINT16(colorPtr + 0));
+ WRITE_UINT16(rows[1] + 0, READ_UINT16(colorPtr + 2));
+
+ colorPtr = strip.v4_dither + (codebookIndex[1] << 2);
+ WRITE_UINT16(rows[0] + 2, READ_UINT16(colorPtr + 1024));
+ WRITE_UINT16(rows[1] + 2, READ_UINT16(colorPtr + 1026));
+
+ colorPtr = strip.v4_dither + (codebookIndex[2] << 2);
+ WRITE_UINT16(rows[2] + 0, READ_UINT16(colorPtr + 2048));
+ WRITE_UINT16(rows[3] + 0, READ_UINT16(colorPtr + 2050));
+
+ colorPtr = strip.v4_dither + (codebookIndex[3] << 2);
+ WRITE_UINT16(rows[2] + 2, READ_UINT16(colorPtr + 3072));
+ WRITE_UINT16(rows[3] + 2, READ_UINT16(colorPtr + 3074));
+ }
+};
+
+template<typename PixelInt, typename CodebookConverter>
+void decodeVectorsTmpl(CinepakFrame &frame, const byte *clipTable, const byte *colorMap, Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize) {
+ uint32 flag = 0, mask = 0;
+ PixelInt *iy[4];
+ int32 startPos = stream.pos();
+
+ for (uint16 y = frame.strips[strip].rect.top; y < frame.strips[strip].rect.bottom; y += 4) {
+ iy[0] = (PixelInt *)frame.surface->getBasePtr(frame.strips[strip].rect.left, + y);
+ iy[1] = iy[0] + frame.width;
+ iy[2] = iy[1] + frame.width;
+ iy[3] = iy[2] + frame.width;
+
+ for (uint16 x = frame.strips[strip].rect.left; x < frame.strips[strip].rect.right; x += 4) {
+ if ((chunkID & 0x01) && !(mask >>= 1)) {
+ if ((stream.pos() - startPos + 4) > (int32)chunkSize)
+ return;
+
+ flag = stream.readUint32BE();
+ mask = 0x80000000;
+ }
+
+ if (!(chunkID & 0x01) || (flag & mask)) {
+ if (!(chunkID & 0x02) && !(mask >>= 1)) {
+ if ((stream.pos() - startPos + 4) > (int32)chunkSize)
+ return;
+
+ flag = stream.readUint32BE();
+ mask = 0x80000000;
+ }
+
+ if ((chunkID & 0x02) || (~flag & mask)) {
+ if ((stream.pos() - startPos + 1) > (int32)chunkSize)
+ return;
+
+ // Get the codebook
+ byte codebook = stream.readByte();
+ CodebookConverter::decodeBlock1(codebook, frame.strips[strip], iy, clipTable, colorMap, frame.surface->format);
+ } else if (flag & mask) {
+ if ((stream.pos() - startPos + 4) > (int32)chunkSize)
+ return;
+
+ byte codebook[4];
+ stream.read(codebook, 4);
+ CodebookConverter::decodeBlock4(codebook, frame.strips[strip], iy, clipTable, colorMap, frame.surface->format);
+ }
+ }
+
+ for (byte i = 0; i < 4; i++)
+ iy[i] += 4;
+ }
+ }
+}
+
+} // End of anonymous namespace
+
+CinepakDecoder::CinepakDecoder(int bitsPerPixel) : Codec(), _bitsPerPixel(bitsPerPixel) {
+ _curFrame.surface = 0;
+ _curFrame.strips = 0;
_y = 0;
+ _colorMap = 0;
+ _ditherPalette = 0;
+ _ditherType = kDitherTypeUnknown;
if (bitsPerPixel == 8) {
_pixelFormat = Graphics::PixelFormat::createFormatCLUT8();
@@ -86,6 +392,9 @@ CinepakDecoder::~CinepakDecoder() {
delete[] _curFrame.strips;
delete[] _clipTableBuf;
+
+ delete[] _colorMap;
+ delete[] _ditherPalette;
}
const Graphics::Surface *CinepakDecoder::decodeFrame(Common::SeekableReadStream &stream) {
@@ -96,7 +405,7 @@ const Graphics::Surface *CinepakDecoder::decodeFrame(Common::SeekableReadStream
_curFrame.height = stream.readUint16BE();
_curFrame.stripCount = stream.readUint16BE();
- if (_curFrame.strips == NULL)
+ if (!_curFrame.strips)
_curFrame.strips = new CinepakStrip[_curFrame.stripCount];
debug(4, "Cinepak Frame: Width = %d, Height = %d, Strip Count = %d", _curFrame.width, _curFrame.height, _curFrame.stripCount);
@@ -120,9 +429,12 @@ const Graphics::Surface *CinepakDecoder::decodeFrame(Common::SeekableReadStream
for (uint16 i = 0; i < _curFrame.stripCount; i++) {
if (i > 0 && !(_curFrame.flags & 1)) { // Use codebooks from last strip
+
for (uint16 j = 0; j < 256; j++) {
_curFrame.strips[i].v1_codebook[j] = _curFrame.strips[i - 1].v1_codebook[j];
_curFrame.strips[i].v4_codebook[j] = _curFrame.strips[i - 1].v4_codebook[j];
+ memcpy(_curFrame.strips[i].v1_dither, _curFrame.strips[i - 1].v1_dither, 256 * 4 * 4 * 4);
+ memcpy(_curFrame.strips[i].v4_dither, _curFrame.strips[i - 1].v4_dither, 256 * 4 * 4 * 4);
}
}
@@ -166,7 +478,10 @@ const Graphics::Surface *CinepakDecoder::decodeFrame(Common::SeekableReadStream
case 0x30:
case 0x31:
case 0x32:
- decodeVectors(stream, i, chunkID, chunkSize);
+ if (_ditherPalette)
+ ditherVectors(stream, i, chunkID, chunkSize);
+ else
+ decodeVectors(stream, i, chunkID, chunkSize);
break;
default:
warning("Unknown Cinepak chunk ID %02x", chunkID);
@@ -203,8 +518,7 @@ void CinepakDecoder::loadCodebook(Common::SeekableReadStream &stream, uint16 str
if ((stream.pos() - startPos + n) > (int32)chunkSize)
break;
- for (byte j = 0; j < 4; j++)
- codebook[i].y[j] = stream.readByte();
+ stream.read(codebook[i].y, 4);
if (n == 6) {
codebook[i].u = stream.readSByte();
@@ -216,99 +530,150 @@ void CinepakDecoder::loadCodebook(Common::SeekableReadStream &stream, uint16 str
codebook[i].u = 0;
codebook[i].v = 0;
}
+
+ // Dither the codebook if we're dithering for QuickTime
+ if (_ditherType == kDitherTypeQT)
+ ditherCodebookQT(strip, codebookType, i);
}
}
}
+void CinepakDecoder::ditherCodebookQT(uint16 strip, byte codebookType, uint16 codebookIndex) {
+ if (codebookType == 1) {
+ const CinepakCodebook &codebook = _curFrame.strips[strip].v1_codebook[codebookIndex];
+ byte *output = _curFrame.strips[strip].v1_dither + (codebookIndex << 2);
+
+ byte *ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[0], codebook.u, codebook.v);
+ output[0x000] = ditherEntry[0x0000];
+ output[0x001] = ditherEntry[0x4000];
+ output[0x400] = ditherEntry[0xC000];
+ output[0x401] = ditherEntry[0x0000];
+
+ ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[1], codebook.u, codebook.v);
+ output[0x002] = ditherEntry[0x8000];
+ output[0x003] = ditherEntry[0xC000];
+ output[0x402] = ditherEntry[0x4000];
+ output[0x403] = ditherEntry[0x8000];
+
+ ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[2], codebook.u, codebook.v);
+ output[0x800] = ditherEntry[0x4000];
+ output[0x801] = ditherEntry[0x8000];
+ output[0xC00] = ditherEntry[0x8000];
+ output[0xC01] = ditherEntry[0xC000];
+
+ ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[3], codebook.u, codebook.v);
+ output[0x802] = ditherEntry[0xC000];
+ output[0x803] = ditherEntry[0x0000];
+ output[0xC02] = ditherEntry[0x0000];
+ output[0xC03] = ditherEntry[0x4000];
+ } else {
+ const CinepakCodebook &codebook = _curFrame.strips[strip].v4_codebook[codebookIndex];
+ byte *output = _curFrame.strips[strip].v4_dither + (codebookIndex << 2);
+
+ byte *ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[0], codebook.u, codebook.v);
+ output[0x000] = ditherEntry[0x0000];
+ output[0x400] = ditherEntry[0x8000];
+ output[0x800] = ditherEntry[0x4000];
+ output[0xC00] = ditherEntry[0xC000];
+
+ ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[1], codebook.u, codebook.v);
+ output[0x001] = ditherEntry[0x4000];
+ output[0x401] = ditherEntry[0xC000];
+ output[0x801] = ditherEntry[0x8000];
+ output[0xC01] = ditherEntry[0x0000];
+
+ ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[2], codebook.u, codebook.v);
+ output[0x002] = ditherEntry[0xC000];
+ output[0x402] = ditherEntry[0x4000];
+ output[0x802] = ditherEntry[0x8000];
+ output[0xC02] = ditherEntry[0x0000];
+
+ ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[3], codebook.u, codebook.v);
+ output[0x003] = ditherEntry[0x0000];
+ output[0x403] = ditherEntry[0x8000];
+ output[0x803] = ditherEntry[0xC000];
+ output[0xC03] = ditherEntry[0x4000];
+ }
+}
+
void CinepakDecoder::decodeVectors(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize) {
- uint32 flag = 0, mask = 0;
- uint32 iy[4];
- int32 startPos = stream.pos();
+ if (_curFrame.surface->format.bytesPerPixel == 1) {
+ decodeVectorsTmpl<byte, CodebookConverterRaw>(_curFrame, _clipTable, _colorMap, stream, strip, chunkID, chunkSize);
+ } else if (_curFrame.surface->format.bytesPerPixel == 2) {
+ decodeVectorsTmpl<uint16, CodebookConverterRaw>(_curFrame, _clipTable, _colorMap, stream, strip, chunkID, chunkSize);
+ } else if (_curFrame.surface->format.bytesPerPixel == 4) {
+ decodeVectorsTmpl<uint32, CodebookConverterRaw>(_curFrame, _clipTable, _colorMap, stream, strip, chunkID, chunkSize);
+ }
+}
- for (uint16 y = _curFrame.strips[strip].rect.top; y < _curFrame.strips[strip].rect.bottom; y += 4) {
- iy[0] = _curFrame.strips[strip].rect.left + y * _curFrame.width;
- iy[1] = iy[0] + _curFrame.width;
- iy[2] = iy[1] + _curFrame.width;
- iy[3] = iy[2] + _curFrame.width;
+bool CinepakDecoder::canDither(DitherType type) const {
+ return (type == kDitherTypeVFW || type == kDitherTypeQT) && _bitsPerPixel == 24;
+}
- for (uint16 x = _curFrame.strips[strip].rect.left; x < _curFrame.strips[strip].rect.right; x += 4) {
- if ((chunkID & 0x01) && !(mask >>= 1)) {
- if ((stream.pos() - startPos + 4) > (int32)chunkSize)
- return;
+void CinepakDecoder::setDither(DitherType type, const byte *palette) {
+ assert(canDither(type));
- flag = stream.readUint32BE();
- mask = 0x80000000;
- }
+ delete[] _colorMap;
+ delete[] _ditherPalette;
- if (!(chunkID & 0x01) || (flag & mask)) {
- if (!(chunkID & 0x02) && !(mask >>= 1)) {
- if ((stream.pos() - startPos + 4) > (int32)chunkSize)
- return;
+ _ditherPalette = new byte[256 * 3];
+ memcpy(_ditherPalette, palette, 256 * 3);
- flag = stream.readUint32BE();
- mask = 0x80000000;
- }
+ _dirtyPalette = true;
+ _pixelFormat = Graphics::PixelFormat::createFormatCLUT8();
+ _ditherType = type;
- if ((chunkID & 0x02) || (~flag & mask)) {
- if ((stream.pos() - startPos + 1) > (int32)chunkSize)
- return;
+ if (type == kDitherTypeVFW) {
+ _colorMap = new byte[221];
- // Get the codebook
- CinepakCodebook codebook = _curFrame.strips[strip].v1_codebook[stream.readByte()];
-
- PUT_PIXEL(iy[0] + 0, codebook.y[0], codebook.u, codebook.v);
- PUT_PIXEL(iy[0] + 1, codebook.y[0], codebook.u, codebook.v);
- PUT_PIXEL(iy[1] + 0, codebook.y[0], codebook.u, codebook.v);
- PUT_PIXEL(iy[1] + 1, codebook.y[0], codebook.u, codebook.v);
-
- PUT_PIXEL(iy[0] + 2, codebook.y[1], codebook.u, codebook.v);
- PUT_PIXEL(iy[0] + 3, codebook.y[1], codebook.u, codebook.v);
- PUT_PIXEL(iy[1] + 2, codebook.y[1], codebook.u, codebook.v);
- PUT_PIXEL(iy[1] + 3, codebook.y[1], codebook.u, codebook.v);
-
- PUT_PIXEL(iy[2] + 0, codebook.y[2], codebook.u, codebook.v);
- PUT_PIXEL(iy[2] + 1, codebook.y[2], codebook.u, codebook.v);
- PUT_PIXEL(iy[3] + 0, codebook.y[2], codebook.u, codebook.v);
- PUT_PIXEL(iy[3] + 1, codebook.y[2], codebook.u, codebook.v);
-
- PUT_PIXEL(iy[2] + 2, codebook.y[3], codebook.u, codebook.v);
- PUT_PIXEL(iy[2] + 3, codebook.y[3], codebook.u, codebook.v);
- PUT_PIXEL(iy[3] + 2, codebook.y[3], codebook.u, codebook.v);
- PUT_PIXEL(iy[3] + 3, codebook.y[3], codebook.u, codebook.v);
- } else if (flag & mask) {
- if ((stream.pos() - startPos + 4) > (int32)chunkSize)
- return;
+ for (int i = 0; i < 221; i++)
+ _colorMap[i] = findNearestRGB(i);
+ } else {
+ // Generate QuickTime dither table
+ // 4 blocks of 0x4000 bytes (RGB554 lookup)
+ _colorMap = createQuickTimeDitherTable(palette, 256);
+ }
+}
+
+byte CinepakDecoder::findNearestRGB(int index) const {
+ int r = s_defaultPalette[index * 3];
+ int g = s_defaultPalette[index * 3 + 1];
+ int b = s_defaultPalette[index * 3 + 2];
+
+ byte result = 0;
+ int diff = 0x7FFFFFFF;
+
+ for (int i = 0; i < 256; i++) {
+ int bDiff = b - (int)_ditherPalette[i * 3 + 2];
+ int curDiffB = diff - (bDiff * bDiff);
+
+ if (curDiffB > 0) {
+ int gDiff = g - (int)_ditherPalette[i * 3 + 1];
+ int curDiffG = curDiffB - (gDiff * gDiff);
+
+ if (curDiffG > 0) {
+ int rDiff = r - (int)_ditherPalette[i * 3];
+ int curDiffR = curDiffG - (rDiff * rDiff);
- CinepakCodebook codebook = _curFrame.strips[strip].v4_codebook[stream.readByte()];
- PUT_PIXEL(iy[0] + 0, codebook.y[0], codebook.u, codebook.v);
- PUT_PIXEL(iy[0] + 1, codebook.y[1], codebook.u, codebook.v);
- PUT_PIXEL(iy[1] + 0, codebook.y[2], codebook.u, codebook.v);
- PUT_PIXEL(iy[1] + 1, codebook.y[3], codebook.u, codebook.v);
-
- codebook = _curFrame.strips[strip].v4_codebook[stream.readByte()];
- PUT_PIXEL(iy[0] + 2, codebook.y[0], codebook.u, codebook.v);
- PUT_PIXEL(iy[0] + 3, codebook.y[1], codebook.u, codebook.v);
- PUT_PIXEL(iy[1] + 2, codebook.y[2], codebook.u, codebook.v);
- PUT_PIXEL(iy[1] + 3, codebook.y[3], codebook.u, codebook.v);
-
- codebook = _curFrame.strips[strip].v4_codebook[stream.readByte()];
- PUT_PIXEL(iy[2] + 0, codebook.y[0], codebook.u, codebook.v);
- PUT_PIXEL(iy[2] + 1, codebook.y[1], codebook.u, codebook.v);
- PUT_PIXEL(iy[3] + 0, codebook.y[2], codebook.u, codebook.v);
- PUT_PIXEL(iy[3] + 1, codebook.y[3], codebook.u, codebook.v);
-
- codebook = _curFrame.strips[strip].v4_codebook[stream.readByte()];
- PUT_PIXEL(iy[2] + 2, codebook.y[0], codebook.u, codebook.v);
- PUT_PIXEL(iy[2] + 3, codebook.y[1], codebook.u, codebook.v);
- PUT_PIXEL(iy[3] + 2, codebook.y[2], codebook.u, codebook.v);
- PUT_PIXEL(iy[3] + 3, codebook.y[3], codebook.u, codebook.v);
+ if (curDiffR > 0) {
+ diff -= curDiffR;
+ result = i;
+
+ if (diff == 0)
+ break;
}
}
-
- for (byte i = 0; i < 4; i++)
- iy[i] += 4;
}
}
+
+ return result;
+}
+
+void CinepakDecoder::ditherVectors(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize) {
+ if (_ditherType == kDitherTypeVFW)
+ decodeVectorsTmpl<byte, CodebookConverterDitherVFW>(_curFrame, _clipTable, _colorMap, stream, strip, chunkID, chunkSize);
+ else
+ decodeVectorsTmpl<byte, CodebookConverterDitherQT>(_curFrame, _clipTable, _colorMap, stream, strip, chunkID, chunkSize);
}
} // End of namespace Image
diff --git a/image/codecs/cinepak.h b/image/codecs/cinepak.h
index e9cd437730..dc8172ea0f 100644
--- a/image/codecs/cinepak.h
+++ b/image/codecs/cinepak.h
@@ -46,6 +46,7 @@ struct CinepakStrip {
uint16 length;
Common::Rect rect;
CinepakCodebook v1_codebook[256], v4_codebook[256];
+ byte v1_dither[256 * 4 * 4 * 4], v4_dither[256 * 4 * 4 * 4];
};
struct CinepakFrame {
@@ -72,14 +73,31 @@ public:
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream);
Graphics::PixelFormat getPixelFormat() const { return _pixelFormat; }
+ bool containsPalette() const { return _ditherPalette != 0; }
+ const byte *getPalette() { _dirtyPalette = false; return _ditherPalette; }
+ bool hasDirtyPalette() const { return _dirtyPalette; }
+ bool canDither(DitherType type) const;
+ void setDither(DitherType type, const byte *palette);
+
private:
CinepakFrame _curFrame;
int32 _y;
+ int _bitsPerPixel;
Graphics::PixelFormat _pixelFormat;
byte *_clipTable, *_clipTableBuf;
+ byte *_ditherPalette;
+ bool _dirtyPalette;
+ byte *_rgbLookup;
+ byte *_colorMap;
+ DitherType _ditherType;
+
void loadCodebook(Common::SeekableReadStream &stream, uint16 strip, byte codebookType, byte chunkID, uint32 chunkSize);
void decodeVectors(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize);
+
+ byte findNearestRGB(int index) const;
+ void ditherVectors(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize);
+ void ditherCodebookQT(uint16 strip, byte codebookType, uint16 codebookIndex);
};
} // End of namespace Image
diff --git a/image/codecs/cinepak_tables.h b/image/codecs/cinepak_tables.h
new file mode 100644
index 0000000000..03131cec22
--- /dev/null
+++ b/image/codecs/cinepak_tables.h
@@ -0,0 +1,780 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef IMAGE_CODECS_CINEPAK_TABLES_H
+#define IMAGE_CODECS_CINEPAK_TABLES_H
+
+#include "common/scummsys.h"
+
+namespace Image {
+
+static const byte s_defaultPaletteLookup[1024] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
+ 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
+ 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
+ 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
+ 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
+ 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02,
+ 0x04, 0x04, 0x04, 0x05, 0x06, 0x07, 0x07, 0x07,
+ 0x04, 0x04, 0x04, 0x05, 0x06, 0x07, 0x07, 0x07,
+ 0x04, 0x04, 0x04, 0x05, 0x06, 0x07, 0x07, 0x07,
+ 0x04, 0x04, 0x04, 0x05, 0x06, 0x07, 0x07, 0x07,
+ 0x08, 0x08, 0x08, 0x09, 0x0A, 0x07, 0x07, 0x07,
+ 0x0B, 0x0B, 0x0B, 0x0C, 0x0D, 0x0D, 0x0D, 0x07,
+ 0x0E, 0x0E, 0x0E, 0x0F, 0x0D, 0x0D, 0x0D, 0x0D,
+ 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x0D, 0x0D,
+ 0x12, 0x12, 0x12, 0x13, 0x14, 0x15, 0x16, 0x16,
+ 0x12, 0x12, 0x12, 0x13, 0x14, 0x15, 0x16, 0x16,
+ 0x12, 0x12, 0x12, 0x13, 0x14, 0x15, 0x16, 0x16,
+ 0x12, 0x12, 0x12, 0x13, 0x14, 0x15, 0x16, 0x16,
+ 0x17, 0x17, 0x17, 0x18, 0x19, 0x1A, 0x16, 0x16,
+ 0x1B, 0x1B, 0x1B, 0x1C, 0x1D, 0x1E, 0x1E, 0x1E,
+ 0x1F, 0x1F, 0x1F, 0x20, 0x21, 0x1E, 0x1E, 0x1E,
+ 0x22, 0x22, 0x22, 0x23, 0x24, 0x24, 0x24, 0x1E,
+ 0x25, 0x25, 0x25, 0x26, 0x27, 0x28, 0x29, 0x29,
+ 0x25, 0x25, 0x25, 0x26, 0x27, 0x28, 0x29, 0x29,
+ 0x25, 0x25, 0x25, 0x26, 0x27, 0x28, 0x29, 0x29,
+ 0x25, 0x25, 0x25, 0x26, 0x27, 0x28, 0x29, 0x29,
+ 0x2A, 0x2A, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2E,
+ 0x2F, 0x2F, 0x2F, 0x30, 0x31, 0x32, 0x2E, 0x2E,
+ 0x33, 0x33, 0x33, 0x34, 0x35, 0x36, 0x36, 0x36,
+ 0x33, 0x33, 0x33, 0x34, 0x35, 0x36, 0x36, 0x36,
+ 0x37, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3B, 0x3B,
+ 0x37, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3B, 0x3B,
+ 0x37, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3B, 0x3B,
+ 0x3C, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x40, 0x40,
+ 0x41, 0x41, 0x42, 0x43, 0x44, 0x45, 0x45, 0x45,
+ 0x46, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4A, 0x4A,
+ 0x4B, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x4F, 0x4F,
+ 0x4B, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x4F, 0x4F,
+ 0x50, 0x50, 0x51, 0x52, 0x53, 0x54, 0x54, 0x54,
+ 0x50, 0x50, 0x51, 0x52, 0x53, 0x54, 0x54, 0x54,
+ 0x50, 0x50, 0x51, 0x52, 0x53, 0x54, 0x54, 0x54,
+ 0x55, 0x55, 0x56, 0x57, 0x58, 0x59, 0x59, 0x59,
+ 0x5A, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5E, 0x5E,
+ 0x5F, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x63, 0x63,
+ 0x64, 0x64, 0x65, 0x66, 0x67, 0x68, 0x68, 0x68,
+ 0x64, 0x64, 0x65, 0x66, 0x67, 0x68, 0x68, 0x68,
+ 0x69, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6D, 0x6D,
+ 0x69, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6D, 0x6D,
+ 0x69, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6D, 0x6D,
+ 0x6E, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x72, 0x72,
+ 0x73, 0x73, 0x74, 0x75, 0x76, 0x77, 0x77, 0x77,
+ 0x78, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7C, 0x7C,
+ 0x7D, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x81, 0x81,
+ 0x7D, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x81, 0x81,
+ 0x82, 0x82, 0x83, 0x84, 0x85, 0x86, 0x86, 0x86,
+ 0x82, 0x82, 0x83, 0x84, 0x85, 0x86, 0x86, 0x86,
+ 0x87, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8B, 0x8B,
+ 0x8C, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x90, 0x90,
+ 0x91, 0x91, 0x92, 0x93, 0x94, 0x95, 0x95, 0x95,
+ 0x96, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9A, 0x9A,
+ 0x96, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9A, 0x9A,
+ 0x96, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9A, 0x9A,
+ 0x9B, 0x9B, 0x9B, 0x9C, 0x9D, 0x9D, 0x9D, 0x9D,
+ 0x9E, 0x9B, 0x9B, 0x9C, 0x9D, 0x9D, 0x9D, 0x9D,
+ 0x9E, 0x9E, 0x9F, 0xA0, 0xA1, 0xA1, 0xA1, 0xA1,
+ 0xA2, 0xA2, 0xA3, 0xA4, 0xA5, 0xA5, 0xA5, 0xA5,
+ 0xA6, 0xA6, 0xA7, 0xA8, 0xA9, 0xA9, 0xA9, 0xA9,
+ 0xAA, 0xAA, 0xAB, 0xAC, 0xAD, 0xAD, 0xAD, 0xAD,
+ 0xAA, 0xAA, 0xAB, 0xAC, 0xAD, 0xAD, 0xAD, 0xAD,
+ 0xAA, 0xAA, 0xAB, 0xAC, 0xAD, 0xAD, 0xAD, 0xAD,
+ 0xAE, 0xAE, 0xAE, 0xAF, 0xB0, 0xB0, 0xB0, 0xB0,
+ 0xAE, 0xAE, 0xAE, 0xAF, 0xB0, 0xB0, 0xB0, 0xB0,
+ 0xB4, 0xB1, 0xB1, 0xB2, 0xB3, 0xB3, 0xB3, 0xB3,
+ 0xB4, 0xB4, 0xB5, 0xB6, 0xB7, 0xB7, 0xB7, 0xB7,
+ 0xB8, 0xB8, 0xB9, 0xBA, 0xBB, 0xBB, 0xBB, 0xBB,
+ 0xBC, 0xBC, 0xBD, 0xBE, 0xBF, 0xBF, 0xBF, 0xBF,
+ 0xBC, 0xBC, 0xBD, 0xBE, 0xBF, 0xBF, 0xBF, 0xBF,
+ 0xBC, 0xBC, 0xBD, 0xBE, 0xBF, 0xBF, 0xBF, 0xBF,
+ 0xC2, 0xC0, 0xC0, 0xC0, 0xC1, 0xC1, 0xC1, 0xC1,
+ 0xC2, 0xC2, 0xC0, 0xC0, 0xC1, 0xC1, 0xC1, 0xC1,
+ 0xC2, 0xC2, 0xC2, 0xC3, 0xC4, 0xC4, 0xC4, 0xC4,
+ 0xC8, 0xC5, 0xC5, 0xC6, 0xC7, 0xC7, 0xC7, 0xC7,
+ 0xC8, 0xC8, 0xC9, 0xCA, 0xCB, 0xCB, 0xCB, 0xCB,
+ 0xCC, 0xCC, 0xCD, 0xCE, 0xCF, 0xCF, 0xCF, 0xCF,
+ 0xCC, 0xCC, 0xCD, 0xCE, 0xCF, 0xCF, 0xCF, 0xCF,
+ 0xCC, 0xCC, 0xCD, 0xCE, 0xCF, 0xCF, 0xCF, 0xCF,
+ 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0,
+ 0xD2, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0,
+ 0xD2, 0xD2, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1,
+ 0xD2, 0xD2, 0xD2, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3,
+ 0xD4, 0xD4, 0xD4, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5,
+ 0xD4, 0xD4, 0xD4, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5,
+ 0xD4, 0xD4, 0xD4, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5,
+ 0xD4, 0xD4, 0xD4, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5,
+ 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6,
+ 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6,
+ 0xD8, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6,
+ 0xD8, 0xD8, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7,
+ 0xD8, 0xD8, 0xD8, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9,
+ 0xD8, 0xD8, 0xD8, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9,
+ 0xD8, 0xD8, 0xD8, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9,
+ 0xD8, 0xD8, 0xD8, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9,
+ 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA,
+ 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA,
+ 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA,
+ 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA,
+ 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB,
+ 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB,
+ 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB,
+ 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB,
+ 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
+ 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
+ 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
+ 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
+ 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
+ 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
+ 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
+ 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC
+};
+
+static const byte s_defaultPalette[221 * 3] = {
+ 0x02, 0x02, 0x02,
+ 0x19, 0x19, 0x19,
+ 0x47, 0x02, 0x19,
+ 0x19, 0x0D, 0x47,
+ 0x00, 0x51, 0x00,
+ 0x2E, 0x3A, 0x00,
+ 0x5C, 0x23, 0x00,
+ 0x8F, 0x0A, 0x00,
+ 0x00, 0x45, 0x2E,
+ 0x2E, 0x2E, 0x2E,
+ 0x5C, 0x17, 0x2E,
+ 0x00, 0x3A, 0x5C,
+ 0x2E, 0x23, 0x5C,
+ 0x5C, 0x0C, 0x5C,
+ 0x00, 0x2C, 0x95,
+ 0x2E, 0x15, 0x95,
+ 0x00, 0x1A, 0xDC,
+ 0x2E, 0x03, 0xDC,
+ 0x15, 0x66, 0x15,
+ 0x43, 0x4F, 0x15,
+ 0x71, 0x38, 0x15,
+ 0xA4, 0x1E, 0x15,
+ 0xDB, 0x02, 0x15,
+ 0x15, 0x5A, 0x43,
+ 0x43, 0x43, 0x43,
+ 0x71, 0x2C, 0x43,
+ 0xA4, 0x13, 0x43,
+ 0x15, 0x4F, 0x71,
+ 0x43, 0x38, 0x71,
+ 0x71, 0x21, 0x71,
+ 0xA4, 0x07, 0x71,
+ 0x15, 0x40, 0xAA,
+ 0x43, 0x29, 0xAA,
+ 0x71, 0x12, 0xAA,
+ 0x15, 0x2F, 0xF1,
+ 0x43, 0x18, 0xF1,
+ 0x71, 0x01, 0xF1,
+ 0x29, 0x79, 0x29,
+ 0x57, 0x62, 0x29,
+ 0x85, 0x4B, 0x29,
+ 0xB7, 0x32, 0x29,
+ 0xEF, 0x16, 0x29,
+ 0x29, 0x6E, 0x57,
+ 0x57, 0x57, 0x57,
+ 0x85, 0x40, 0x57,
+ 0xB7, 0x27, 0x57,
+ 0xEF, 0x0B, 0x57,
+ 0x29, 0x62, 0x85,
+ 0x57, 0x4B, 0x85,
+ 0x85, 0x34, 0x85,
+ 0xB7, 0x1B, 0x85,
+ 0x29, 0x54, 0xBE,
+ 0x57, 0x3D, 0xBE,
+ 0x85, 0x26, 0xBE,
+ 0xB7, 0x0D, 0xBE,
+ 0x03, 0xB5, 0x09,
+ 0x3C, 0x99, 0x09,
+ 0x6A, 0x82, 0x09,
+ 0x98, 0x6B, 0x09,
+ 0xCA, 0x51, 0x09,
+ 0x03, 0xA9, 0x3C,
+ 0x3C, 0x8C, 0x3C,
+ 0x6A, 0x75, 0x3C,
+ 0x98, 0x5E, 0x3C,
+ 0xCA, 0x45, 0x3C,
+ 0x03, 0x9D, 0x6A,
+ 0x3C, 0x81, 0x6A,
+ 0x6A, 0x6A, 0x6A,
+ 0x98, 0x53, 0x6A,
+ 0xCA, 0x39, 0x6A,
+ 0x03, 0x92, 0x98,
+ 0x3C, 0x75, 0x98,
+ 0x6A, 0x5E, 0x98,
+ 0x98, 0x47, 0x98,
+ 0xCA, 0x2E, 0x98,
+ 0x03, 0x83, 0xD1,
+ 0x3C, 0x67, 0xD1,
+ 0x6A, 0x50, 0xD1,
+ 0x98, 0x39, 0xD1,
+ 0xCA, 0x20, 0xD1,
+ 0x14, 0xC7, 0x1B,
+ 0x4D, 0xAB, 0x1B,
+ 0x7B, 0x94, 0x1B,
+ 0xA9, 0x7D, 0x1B,
+ 0xDC, 0x63, 0x1B,
+ 0x14, 0xBA, 0x4D,
+ 0x4D, 0x9E, 0x4D,
+ 0x7B, 0x87, 0x4D,
+ 0xA9, 0x70, 0x4D,
+ 0xDC, 0x57, 0x4D,
+ 0x14, 0xAF, 0x7B,
+ 0x4D, 0x92, 0x7B,
+ 0x7B, 0x7B, 0x7B,
+ 0xA9, 0x64, 0x7B,
+ 0xDC, 0x4B, 0x7B,
+ 0x14, 0xA3, 0xA9,
+ 0x4D, 0x87, 0xA9,
+ 0x7B, 0x70, 0xA9,
+ 0xA9, 0x59, 0xA9,
+ 0xDC, 0x40, 0xA9,
+ 0x14, 0x95, 0xE2,
+ 0x4D, 0x79, 0xE2,
+ 0x7B, 0x62, 0xE2,
+ 0xA9, 0x4B, 0xE2,
+ 0xDC, 0x31, 0xE2,
+ 0x25, 0xD8, 0x2C,
+ 0x5E, 0xBB, 0x2C,
+ 0x8C, 0xA4, 0x2C,
+ 0xBA, 0x8D, 0x2C,
+ 0xED, 0x74, 0x2C,
+ 0x25, 0xCB, 0x5E,
+ 0x5E, 0xAF, 0x5E,
+ 0x8C, 0x98, 0x5E,
+ 0xBA, 0x81, 0x5E,
+ 0xED, 0x67, 0x5E,
+ 0x25, 0xC0, 0x8C,
+ 0x5E, 0xA3, 0x8C,
+ 0x8C, 0x8C, 0x8C,
+ 0xBA, 0x75, 0x8C,
+ 0xED, 0x5C, 0x8C,
+ 0x25, 0xB4, 0xBA,
+ 0x5E, 0x98, 0xBA,
+ 0x8C, 0x81, 0xBA,
+ 0xBA, 0x6A, 0xBA,
+ 0xED, 0x50, 0xBA,
+ 0x25, 0xA6, 0xF3,
+ 0x5E, 0x8A, 0xF3,
+ 0x8C, 0x73, 0xF3,
+ 0xBA, 0x5C, 0xF3,
+ 0xED, 0x42, 0xF3,
+ 0x35, 0xF6, 0x04,
+ 0x6E, 0xD9, 0x04,
+ 0x9C, 0xC2, 0x04,
+ 0xCA, 0xAB, 0x04,
+ 0xFD, 0x92, 0x04,
+ 0x35, 0xE8, 0x3C,
+ 0x6E, 0xCB, 0x3C,
+ 0x9C, 0xB4, 0x3C,
+ 0xCA, 0x9D, 0x3C,
+ 0xFD, 0x84, 0x3C,
+ 0x35, 0xDB, 0x6E,
+ 0x6E, 0xBF, 0x6E,
+ 0x9C, 0xA8, 0x6E,
+ 0xCA, 0x91, 0x6E,
+ 0xFD, 0x78, 0x6E,
+ 0x35, 0xD0, 0x9C,
+ 0x6E, 0xB3, 0x9C,
+ 0x9C, 0x9C, 0x9C,
+ 0xCA, 0x85, 0x9C,
+ 0xFD, 0x6C, 0x9C,
+ 0x35, 0xC4, 0xCA,
+ 0x6E, 0xA8, 0xCA,
+ 0x9C, 0x91, 0xCA,
+ 0xCA, 0x7A, 0xCA,
+ 0xFD, 0x61, 0xCA,
+ 0x7E, 0xE9, 0x13,
+ 0xAC, 0xD2, 0x13,
+ 0xDA, 0xBB, 0x13,
+ 0x45, 0xF7, 0x4B,
+ 0x7E, 0xDB, 0x4B,
+ 0xAC, 0xC4, 0x4B,
+ 0xDA, 0xAD, 0x4B,
+ 0x45, 0xEB, 0x7E,
+ 0x7E, 0xCE, 0x7E,
+ 0xAC, 0xB7, 0x7E,
+ 0xDA, 0xA0, 0x7E,
+ 0x45, 0xDF, 0xAC,
+ 0x7E, 0xC3, 0xAC,
+ 0xAC, 0xAC, 0xAC,
+ 0xDA, 0x95, 0xAC,
+ 0x45, 0xD4, 0xDA,
+ 0x7E, 0xB7, 0xDA,
+ 0xAC, 0xA0, 0xDA,
+ 0xDA, 0x89, 0xDA,
+ 0x8C, 0xF7, 0x22,
+ 0xBA, 0xE0, 0x22,
+ 0xE8, 0xC9, 0x22,
+ 0x8C, 0xE9, 0x59,
+ 0xBA, 0xD2, 0x59,
+ 0xE8, 0xBB, 0x59,
+ 0x53, 0xF9, 0x8C,
+ 0x8C, 0xDD, 0x8C,
+ 0xBA, 0xC6, 0x8C,
+ 0xE8, 0xAF, 0x8C,
+ 0x53, 0xEE, 0xBA,
+ 0x8C, 0xD1, 0xBA,
+ 0xBA, 0xBA, 0xBA,
+ 0xE8, 0xA3, 0xBA,
+ 0x53, 0xE2, 0xE8,
+ 0x8C, 0xC6, 0xE8,
+ 0xBA, 0xAF, 0xE8,
+ 0xE8, 0x98, 0xE8,
+ 0xC8, 0xEE, 0x30,
+ 0xF6, 0xD7, 0x30,
+ 0x9A, 0xF7, 0x67,
+ 0xC8, 0xE0, 0x67,
+ 0xF6, 0xC9, 0x67,
+ 0x9A, 0xEA, 0x9A,
+ 0xC8, 0xD3, 0x9A,
+ 0xF6, 0xBC, 0x9A,
+ 0x61, 0xFB, 0xC8,
+ 0x9A, 0xDF, 0xC8,
+ 0xC8, 0xC8, 0xC8,
+ 0xF6, 0xB1, 0xC8,
+ 0x61, 0xF0, 0xF6,
+ 0x9A, 0xD3, 0xF6,
+ 0xC8, 0xBC, 0xF6,
+ 0xF6, 0xA5, 0xF6,
+ 0xD5, 0xFB, 0x3D,
+ 0xD5, 0xED, 0x74,
+ 0xA7, 0xF7, 0xA7,
+ 0xD5, 0xE0, 0xA7,
+ 0xA7, 0xEC, 0xD5,
+ 0xD5, 0xD5, 0xD5,
+ 0xE1, 0xFA, 0x81,
+ 0xE1, 0xED, 0xB3,
+ 0xB3, 0xF8, 0xE1,
+ 0xE1, 0xE1, 0xE1,
+ 0xED, 0xF9, 0xBF,
+ 0xED, 0xED, 0xED,
+ 0xF8, 0xF8, 0xF8
+};
+
+static const uint32 s_yLookup[512] = {
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000040, 0x00000000, 0x00000040, 0x00000000,
+ 0x00000040, 0x00000000, 0x00000040, 0x00000000,
+ 0x00000040, 0x00000000, 0x00000040, 0x00000040,
+ 0x00000040, 0x00000040, 0x00000040, 0x00000040,
+ 0x00000040, 0x00000040, 0x00400040, 0x00000040,
+ 0x00400040, 0x00000040, 0x00400040, 0x00000040,
+ 0x00400040, 0x00000040, 0x00400040, 0x00000040,
+ 0x00400040, 0x00400040, 0x00400040, 0x00400040,
+ 0x00400040, 0x00400040, 0x00400040, 0x00400040,
+ 0x00400040, 0x00400040, 0x00400040, 0x00400040,
+ 0x00400040, 0x00400040, 0x00400040, 0x00400040,
+ 0x00400040, 0x00400040, 0x00400080, 0x00400040,
+ 0x00400080, 0x00400040, 0x00400080, 0x00400040,
+ 0x00400080, 0x00400040, 0x00400080, 0x00400080,
+ 0x00400080, 0x00400080, 0x00400080, 0x00400080,
+ 0x00400080, 0x00400080, 0x00400080, 0x00400080,
+ 0x00800080, 0x00400080, 0x00800080, 0x00400080,
+ 0x00800080, 0x00400080, 0x00800080, 0x00400080,
+ 0x00800080, 0x00800080, 0x00800080, 0x00800080,
+ 0x00800080, 0x00800080, 0x00800080, 0x00800080,
+ 0x00800080, 0x00800080, 0x00800080, 0x00800080,
+ 0x00800080, 0x00800080, 0x00800080, 0x00800080,
+ 0x00800080, 0x00800080, 0x008000C0, 0x00800080,
+ 0x008000C0, 0x00800080, 0x008000C0, 0x00800080,
+ 0x008000C0, 0x00800080, 0x008000C0, 0x008000C0,
+ 0x008000C0, 0x008000C0, 0x008000C0, 0x008000C0,
+ 0x008000C0, 0x008000C0, 0x00C000C0, 0x008000C0,
+ 0x00C000C0, 0x008000C0, 0x00C000C0, 0x008000C0,
+ 0x00C000C0, 0x008000C0, 0x00C000C0, 0x00C000C0,
+ 0x00C000C0, 0x00C000C0, 0x00C000C0, 0x00C000C0,
+ 0x00C000C0, 0x00C000C0, 0x00C000C0, 0x00C000C0,
+ 0x00C000C0, 0x00C000C0, 0x00C000C0, 0x00C000C0,
+ 0x00C000C0, 0x00C000C0, 0x00C00100, 0x00C000C0,
+ 0x00C00100, 0x00C000C0, 0x00C00100, 0x00C000C0,
+ 0x00C00100, 0x00C000C0, 0x00C00100, 0x00C00100,
+ 0x00C00100, 0x00C00100, 0x00C00100, 0x00C00100,
+ 0x00C00100, 0x00C00100, 0x01000100, 0x00C00100,
+ 0x01000100, 0x00C00100, 0x01000100, 0x00C00100,
+ 0x01000100, 0x00C00100, 0x01000100, 0x01000100,
+ 0x01000100, 0x01000100, 0x01000100, 0x01000100,
+ 0x01000100, 0x01000100, 0x01000100, 0x01000100,
+ 0x01000100, 0x01000100, 0x01000100, 0x01000100,
+ 0x01000100, 0x01000100, 0x01000140, 0x01000100,
+ 0x01000140, 0x01000100, 0x01000140, 0x01000100,
+ 0x01000140, 0x01000140, 0x01000140, 0x01000140,
+ 0x01000140, 0x01000140, 0x01000140, 0x01000140,
+ 0x01400140, 0x01000140, 0x01400140, 0x01000140,
+ 0x01400140, 0x01000140, 0x01400140, 0x01000140,
+ 0x01400140, 0x01400140, 0x01400140, 0x01400140,
+ 0x01400140, 0x01400140, 0x01400140, 0x01400140,
+ 0x01400140, 0x01400140, 0x01400140, 0x01400140,
+ 0x01400140, 0x01400140, 0x01400180, 0x01400140,
+ 0x01400180, 0x01400140, 0x01400180, 0x01400140,
+ 0x01400180, 0x01400140, 0x01400180, 0x01400180,
+ 0x01400180, 0x01400180, 0x01400180, 0x01400180,
+ 0x01800180, 0x01400180, 0x01800180, 0x01400180,
+ 0x01800180, 0x01400180, 0x01800180, 0x01400180,
+ 0x01800180, 0x01800180, 0x01800180, 0x01800180,
+ 0x01800180, 0x01800180, 0x01800180, 0x01800180,
+ 0x01800180, 0x01800180, 0x01800180, 0x01800180,
+ 0x01800180, 0x01800180, 0x018001C0, 0x01800180,
+ 0x018001C0, 0x01800180, 0x018001C0, 0x01800180,
+ 0x018001C0, 0x018001C0, 0x018001C0, 0x018001C0,
+ 0x018001C0, 0x018001C0, 0x018001C0, 0x018001C0,
+ 0x01C001C0, 0x018001C0, 0x01C001C0, 0x018001C0,
+ 0x01C001C0, 0x018001C0, 0x01C001C0, 0x01C001C0,
+ 0x01C001C0, 0x01C001C0, 0x01C001C0, 0x01C001C0,
+ 0x01C001C0, 0x01C001C0, 0x01C001C0, 0x01C001C0,
+ 0x01C001C0, 0x01C001C0, 0x01C00200, 0x01C001C0,
+ 0x01C00200, 0x01C001C0, 0x01C00200, 0x01C001C0,
+ 0x01C00200, 0x01C001C0, 0x01C00200, 0x01C00200,
+ 0x01C00200, 0x01C00200, 0x01C00200, 0x01C00200,
+ 0x02000200, 0x01C00200, 0x02000200, 0x01C00200,
+ 0x02000200, 0x01C00200, 0x02000200, 0x02000200,
+ 0x02000200, 0x02000200, 0x02000200, 0x02000200,
+ 0x02000200, 0x02000200, 0x02000200, 0x02000200,
+ 0x02000200, 0x02000200, 0x02000240, 0x02000200,
+ 0x02000240, 0x02000200, 0x02000240, 0x02000200,
+ 0x02000240, 0x02000240, 0x02000240, 0x02000240,
+ 0x02000240, 0x02000240, 0x02400240, 0x02000240,
+ 0x02400240, 0x02000240, 0x02400240, 0x02000240,
+ 0x02400240, 0x02000240, 0x02400240, 0x02400240,
+ 0x02400240, 0x02400240, 0x02400240, 0x02400240,
+ 0x02400240, 0x02400240, 0x02400240, 0x02400240,
+ 0x02400280, 0x02400240, 0x02400280, 0x02400240,
+ 0x02400280, 0x02400240, 0x02400280, 0x02400280,
+ 0x02400280, 0x02400280, 0x02400280, 0x02400280,
+ 0x02800280, 0x02400280, 0x02800280, 0x02400280,
+ 0x02800280, 0x02400280, 0x02800280, 0x02800280,
+ 0x02800280, 0x02800280, 0x02800280, 0x02800280,
+ 0x02800280, 0x02800280, 0x02800280, 0x02800280,
+ 0x02800280, 0x02800280, 0x028002C0, 0x02800280,
+ 0x028002C0, 0x02800280, 0x028002C0, 0x02800280,
+ 0x028002C0, 0x028002C0, 0x028002C0, 0x028002C0,
+ 0x02C002C0, 0x028002C0, 0x02C002C0, 0x028002C0,
+ 0x02C002C0, 0x028002C0, 0x02C002C0, 0x02C002C0,
+ 0x02C002C0, 0x02C002C0, 0x02C002C0, 0x02C002C0,
+ 0x02C002C0, 0x02C002C0, 0x02C002C0, 0x02C002C0,
+ 0x02C00300, 0x02C002C0, 0x02C00300, 0x02C002C0,
+ 0x02C00300, 0x02C002C0, 0x02C00300, 0x02C00300,
+ 0x02C00300, 0x02C00300, 0x02C00300, 0x02C00300,
+ 0x03000300, 0x02C00300, 0x03000300, 0x02C00300,
+ 0x03000300, 0x03000300, 0x03000300, 0x03000300,
+ 0x03000300, 0x03000300, 0x03000300, 0x03000300,
+ 0x03000300, 0x03000300, 0x03000340, 0x03000300,
+ 0x03000340, 0x03000300, 0x03000340, 0x03000300,
+ 0x03000340, 0x03000340, 0x03000340, 0x03000340,
+ 0x03400340, 0x03000340, 0x03400340, 0x03000340,
+ 0x03400340, 0x03000340, 0x03400340, 0x03400340,
+ 0x03400340, 0x03400340, 0x03400340, 0x03400340,
+ 0x03400340, 0x03400340, 0x03400340, 0x03400340,
+ 0x03400380, 0x03400340, 0x03400380, 0x03400340,
+ 0x03400380, 0x03400380, 0x03400380, 0x03400380,
+ 0x03800380, 0x03400380, 0x03800380, 0x03400380,
+ 0x03800380, 0x03400380, 0x03800380, 0x03800380,
+ 0x03800380, 0x03800380, 0x03800380, 0x03800380,
+ 0x03800380, 0x03800380, 0x038003C0, 0x03800380,
+ 0x038003C0, 0x03800380, 0x038003C0, 0x03800380,
+ 0x038003C0, 0x038003C0, 0x038003C0, 0x038003C0,
+ 0x03C003C0, 0x038003C0, 0x03C003C0, 0x038003C0,
+ 0x03C003C0, 0x03C003C0, 0x03C003C0, 0x03C003C0,
+ 0x03C003C0, 0x03C003C0, 0x03C003C0, 0x03C003C0,
+ 0x03C003C0, 0x03C003C0, 0x03C003C0, 0x03C003C0,
+ 0x03C003C0, 0x03C003C0, 0x03C003C0, 0x03C003C0,
+ 0x03C003C0, 0x03C003C0, 0x03C003C0, 0x03C003C0
+};
+
+static const uint32 s_uLookup[512] = {
+ 0x00200020, 0x00200020, 0x00200020, 0x00200020,
+ 0x00200020, 0x00200020, 0x00200020, 0x00200020,
+ 0x00200020, 0x00200020, 0x00280020, 0x00200020,
+ 0x00280020, 0x00200020, 0x00280020, 0x00200020,
+ 0x00280020, 0x00200020, 0x00280020, 0x00280020,
+ 0x00280020, 0x00280020, 0x00280020, 0x00280020,
+ 0x00280020, 0x00280020, 0x00280020, 0x00280020,
+ 0x00280020, 0x00280028, 0x00280020, 0x00280028,
+ 0x00280020, 0x00280028, 0x00280020, 0x00280028,
+ 0x00280028, 0x00280028, 0x00280028, 0x00280028,
+ 0x00280028, 0x00280028, 0x00280028, 0x00280028,
+ 0x00280028, 0x00280028, 0x00280028, 0x00280028,
+ 0x00280028, 0x00280028, 0x00280028, 0x00280028,
+ 0x00280028, 0x00280028, 0x00280028, 0x00280028,
+ 0x00280028, 0x00280028, 0x00300028, 0x00280028,
+ 0x00300028, 0x00280028, 0x00300028, 0x00280028,
+ 0x00300028, 0x00280028, 0x00300028, 0x00280028,
+ 0x00300028, 0x00300028, 0x00300028, 0x00300028,
+ 0x00300028, 0x00300028, 0x00300028, 0x00300028,
+ 0x00300028, 0x00300028, 0x00300028, 0x00300028,
+ 0x00300028, 0x00300030, 0x00300028, 0x00300030,
+ 0x00300028, 0x00300030, 0x00300028, 0x00300030,
+ 0x00300028, 0x00300030, 0x00300028, 0x00300030,
+ 0x00300030, 0x00300030, 0x00300030, 0x00300030,
+ 0x00300030, 0x00300030, 0x00300030, 0x00300030,
+ 0x00300030, 0x00300030, 0x00300030, 0x00300030,
+ 0x00300030, 0x00300030, 0x00300030, 0x00300030,
+ 0x00300030, 0x00300030, 0x00300030, 0x00300030,
+ 0x00300030, 0x00300030, 0x00300030, 0x00300030,
+ 0x00300030, 0x00300030, 0x00380030, 0x00300030,
+ 0x00380030, 0x00300030, 0x00380030, 0x00300030,
+ 0x00380030, 0x00300030, 0x00380030, 0x00300030,
+ 0x00380030, 0x00300030, 0x00380030, 0x00300030,
+ 0x00380030, 0x00380030, 0x00380030, 0x00380030,
+ 0x00380030, 0x00380030, 0x00380030, 0x00380030,
+ 0x00380030, 0x00380030, 0x00380030, 0x00380030,
+ 0x00380030, 0x00380030, 0x00380030, 0x00380038,
+ 0x00380030, 0x00380038, 0x00380030, 0x00380038,
+ 0x00380030, 0x00380038, 0x00380030, 0x00380038,
+ 0x00380030, 0x00380038, 0x00380030, 0x00380038,
+ 0x00380038, 0x00380038, 0x00380038, 0x00380038,
+ 0x00380038, 0x00380038, 0x00380038, 0x00380038,
+ 0x00380038, 0x00380038, 0x00380038, 0x00380038,
+ 0x00380038, 0x00380038, 0x00380038, 0x00380038,
+ 0x00380038, 0x00380038, 0x00380038, 0x00380038,
+ 0x00380038, 0x00380038, 0x00380038, 0x00380038,
+ 0x00380038, 0x00380038, 0x00380038, 0x00380038,
+ 0x00380038, 0x00380038, 0x00380038, 0x00380038,
+ 0x00380038, 0x00380038, 0x00380038, 0x00380038,
+ 0x00380038, 0x00380038, 0x00380038, 0x00380038,
+ 0x00380038, 0x00380038, 0x00380038, 0x00380038,
+ 0x00380038, 0x00380038, 0x00380038, 0x00380038,
+ 0x00380038, 0x00380038, 0x00380038, 0x00380038,
+ 0x00380038, 0x00380038, 0x00380038, 0x00380038,
+ 0x00380038, 0x00380038, 0x00380038, 0x00380038,
+ 0x00380038, 0x00380038, 0x00380038, 0x00380038,
+ 0x00380038, 0x00380038, 0x00380038, 0x00380038,
+ 0x00380038, 0x00380038, 0x00380038, 0x00380038,
+ 0x00380038, 0x00380038, 0x00380038, 0x00380038,
+ 0x00380038, 0x00380038, 0x00380038, 0x00380038,
+ 0x00380038, 0x00380038, 0x00380038, 0x00380038,
+ 0x00380038, 0x00380038, 0x00380038, 0x00380038,
+ 0x00380038, 0x00380038, 0x00380038, 0x00380038,
+ 0x00380038, 0x00380038, 0x00380038, 0x00380038,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00080000, 0x00000000,
+ 0x00080000, 0x00000000, 0x00080000, 0x00000000,
+ 0x00080000, 0x00000000, 0x00080000, 0x00000000,
+ 0x00080000, 0x00000000, 0x00080000, 0x00000000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080000, 0x00080000, 0x00080000,
+ 0x00080000, 0x00080008, 0x00080000, 0x00080008,
+ 0x00080000, 0x00080008, 0x00080000, 0x00080008,
+ 0x00080000, 0x00080008, 0x00080000, 0x00080008,
+ 0x00080008, 0x00080008, 0x00080008, 0x00080008,
+ 0x00080008, 0x00080008, 0x00080008, 0x00080008,
+ 0x00080008, 0x00080008, 0x00080008, 0x00080008,
+ 0x00080008, 0x00080008, 0x00080008, 0x00080008,
+ 0x00080008, 0x00080008, 0x00080008, 0x00080008,
+ 0x00080008, 0x00080008, 0x00100008, 0x00080008,
+ 0x00100008, 0x00080008, 0x00100008, 0x00080008,
+ 0x00100008, 0x00080008, 0x00100008, 0x00080008,
+ 0x00100008, 0x00080008, 0x00100008, 0x00100008,
+ 0x00100008, 0x00100008, 0x00100008, 0x00100008,
+ 0x00100008, 0x00100008, 0x00100008, 0x00100008,
+ 0x00100008, 0x00100008, 0x00100008, 0x00100010,
+ 0x00100008, 0x00100010, 0x00100008, 0x00100010,
+ 0x00100008, 0x00100010, 0x00100008, 0x00100010,
+ 0x00100010, 0x00100010, 0x00100010, 0x00100010,
+ 0x00100010, 0x00100010, 0x00100010, 0x00100010,
+ 0x00100010, 0x00100010, 0x00100010, 0x00100010,
+ 0x00100010, 0x00100010, 0x00100010, 0x00100010,
+ 0x00100010, 0x00100010, 0x00100010, 0x00100010,
+ 0x00100010, 0x00100010, 0x00180010, 0x00100010,
+ 0x00180010, 0x00100010, 0x00180010, 0x00100010,
+ 0x00180010, 0x00100010, 0x00180010, 0x00100010,
+ 0x00180010, 0x00180010, 0x00180010, 0x00180010,
+ 0x00180010, 0x00180010, 0x00180010, 0x00180010,
+ 0x00180010, 0x00180010, 0x00180010, 0x00180018,
+ 0x00180010, 0x00180018, 0x00180010, 0x00180018,
+ 0x00180010, 0x00180018, 0x00180010, 0x00180018,
+ 0x00180018, 0x00180018, 0x00180018, 0x00180018,
+ 0x00180018, 0x00180018, 0x00180018, 0x00180018,
+ 0x00180018, 0x00180018, 0x00180018, 0x00180018,
+ 0x00180018, 0x00180018, 0x00180018, 0x00180018,
+ 0x00180018, 0x00180018, 0x00180018, 0x00180018,
+ 0x00200018, 0x00180018, 0x00200018, 0x00180018,
+ 0x00200018, 0x00180018, 0x00200018, 0x00180018,
+ 0x00200018, 0x00200018, 0x00200018, 0x00200018,
+ 0x00200018, 0x00200018, 0x00200018, 0x00200018,
+ 0x00200018, 0x00200018, 0x00200018, 0x00200020,
+ 0x00200018, 0x00200020, 0x00200018, 0x00200020,
+ 0x00200018, 0x00200020, 0x00200020, 0x00200020,
+ 0x00200020, 0x00200020, 0x00200020, 0x00200020,
+ 0x00200020, 0x00200020, 0x00200020, 0x00200020
+};
+
+static const uint32 s_vLookup[512] = {
+ 0x00030003, 0x00030003, 0x00030003, 0x00030003,
+ 0x00030003, 0x00030003, 0x00030003, 0x00030003,
+ 0x00030003, 0x00030003, 0x00030003, 0x00030004,
+ 0x00030003, 0x00030004, 0x00030003, 0x00030004,
+ 0x00030003, 0x00030004, 0x00030004, 0x00030004,
+ 0x00030004, 0x00030004, 0x00030004, 0x00030004,
+ 0x00030004, 0x00030004, 0x00030004, 0x00030004,
+ 0x00030004, 0x00040004, 0x00030004, 0x00040004,
+ 0x00030004, 0x00040004, 0x00030004, 0x00040004,
+ 0x00040004, 0x00040004, 0x00040004, 0x00040004,
+ 0x00040004, 0x00040004, 0x00040004, 0x00040004,
+ 0x00040004, 0x00040004, 0x00040004, 0x00040004,
+ 0x00040004, 0x00040004, 0x00040004, 0x00040004,
+ 0x00040004, 0x00040004, 0x00040004, 0x00040004,
+ 0x00040004, 0x00040005, 0x00040004, 0x00040005,
+ 0x00040004, 0x00040005, 0x00040004, 0x00040005,
+ 0x00040004, 0x00040005, 0x00040005, 0x00040005,
+ 0x00040005, 0x00040005, 0x00040005, 0x00040005,
+ 0x00040005, 0x00040005, 0x00040005, 0x00040005,
+ 0x00040005, 0x00050005, 0x00040005, 0x00050005,
+ 0x00040005, 0x00050005, 0x00040005, 0x00050005,
+ 0x00040005, 0x00050005, 0x00050005, 0x00050005,
+ 0x00050005, 0x00050005, 0x00050005, 0x00050005,
+ 0x00050005, 0x00050005, 0x00050005, 0x00050005,
+ 0x00050005, 0x00050005, 0x00050005, 0x00050005,
+ 0x00050005, 0x00050005, 0x00050005, 0x00050005,
+ 0x00050005, 0x00050005, 0x00050005, 0x00050005,
+ 0x00050005, 0x00050006, 0x00050005, 0x00050006,
+ 0x00050005, 0x00050006, 0x00050005, 0x00050006,
+ 0x00050005, 0x00050006, 0x00050006, 0x00050006,
+ 0x00050006, 0x00050006, 0x00050006, 0x00050006,
+ 0x00050006, 0x00050006, 0x00050006, 0x00050006,
+ 0x00050006, 0x00050006, 0x00050006, 0x00060006,
+ 0x00050006, 0x00060006, 0x00050006, 0x00060006,
+ 0x00050006, 0x00060006, 0x00050006, 0x00060006,
+ 0x00050006, 0x00060006, 0x00060006, 0x00060006,
+ 0x00060006, 0x00060006, 0x00060006, 0x00060006,
+ 0x00060006, 0x00060006, 0x00060006, 0x00060006,
+ 0x00060006, 0x00060006, 0x00060006, 0x00060006,
+ 0x00060006, 0x00060006, 0x00060006, 0x00060006,
+ 0x00060006, 0x00060006, 0x00060006, 0x00060006,
+ 0x00060006, 0x00060007, 0x00060006, 0x00060007,
+ 0x00060006, 0x00060007, 0x00060006, 0x00060007,
+ 0x00060006, 0x00060007, 0x00060006, 0x00060007,
+ 0x00060007, 0x00060007, 0x00060007, 0x00060007,
+ 0x00060007, 0x00060007, 0x00060007, 0x00060007,
+ 0x00060007, 0x00060007, 0x00060007, 0x00060007,
+ 0x00060007, 0x00070007, 0x00060007, 0x00070007,
+ 0x00060007, 0x00070007, 0x00060007, 0x00070007,
+ 0x00060007, 0x00070007, 0x00060007, 0x00070007,
+ 0x00060007, 0x00070007, 0x00070007, 0x00070007,
+ 0x00070007, 0x00070007, 0x00070007, 0x00070007,
+ 0x00070007, 0x00070007, 0x00070007, 0x00070007,
+ 0x00070007, 0x00070007, 0x00070007, 0x00070007,
+ 0x00070007, 0x00070007, 0x00070007, 0x00070007,
+ 0x00070007, 0x00070007, 0x00070007, 0x00070007,
+ 0x00070007, 0x00070007, 0x00070007, 0x00070007,
+ 0x00070007, 0x00070007, 0x00070007, 0x00070007,
+ 0x00070007, 0x00070007, 0x00070007, 0x00070007,
+ 0x00070007, 0x00070007, 0x00070007, 0x00070007,
+ 0x00070007, 0x00070007, 0x00070007, 0x00070007,
+ 0x00070007, 0x00070007, 0x00070007, 0x00070007,
+ 0x00070007, 0x00070007, 0x00070007, 0x00070007,
+ 0x00070007, 0x00070007, 0x00070007, 0x00070007,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000001, 0x00000000, 0x00000001,
+ 0x00000000, 0x00000001, 0x00000000, 0x00000001,
+ 0x00000000, 0x00000001, 0x00000000, 0x00000001,
+ 0x00000000, 0x00000001, 0x00000001, 0x00000001,
+ 0x00000001, 0x00000001, 0x00000001, 0x00000001,
+ 0x00000001, 0x00000001, 0x00000001, 0x00000001,
+ 0x00000001, 0x00000001, 0x00000001, 0x00000001,
+ 0x00000001, 0x00010001, 0x00000001, 0x00010001,
+ 0x00000001, 0x00010001, 0x00000001, 0x00010001,
+ 0x00000001, 0x00010001, 0x00000001, 0x00010001,
+ 0x00000001, 0x00010001, 0x00010001, 0x00010001,
+ 0x00010001, 0x00010001, 0x00010001, 0x00010001,
+ 0x00010001, 0x00010001, 0x00010001, 0x00010001,
+ 0x00010001, 0x00010001, 0x00010001, 0x00010001,
+ 0x00010001, 0x00010001, 0x00010001, 0x00010001,
+ 0x00010001, 0x00010001, 0x00010001, 0x00010001,
+ 0x00010001, 0x00010001, 0x00010001, 0x00010001,
+ 0x00010001, 0x00010002, 0x00010001, 0x00010002,
+ 0x00010001, 0x00010002, 0x00010001, 0x00010002,
+ 0x00010001, 0x00010002, 0x00010001, 0x00010002,
+ 0x00010002, 0x00010002, 0x00010002, 0x00010002,
+ 0x00010002, 0x00010002, 0x00010002, 0x00010002,
+ 0x00010002, 0x00010002, 0x00010002, 0x00010002,
+ 0x00010002, 0x00020002, 0x00010002, 0x00020002,
+ 0x00010002, 0x00020002, 0x00010002, 0x00020002,
+ 0x00010002, 0x00020002, 0x00020002, 0x00020002,
+ 0x00020002, 0x00020002, 0x00020002, 0x00020002,
+ 0x00020002, 0x00020002, 0x00020002, 0x00020002,
+ 0x00020002, 0x00020002, 0x00020002, 0x00020002,
+ 0x00020002, 0x00020002, 0x00020002, 0x00020002,
+ 0x00020002, 0x00020002, 0x00020002, 0x00020002,
+ 0x00020002, 0x00020003, 0x00020002, 0x00020003,
+ 0x00020002, 0x00020003, 0x00020002, 0x00020003,
+ 0x00020003, 0x00020003, 0x00020003, 0x00020003,
+ 0x00020003, 0x00020003, 0x00020003, 0x00020003,
+ 0x00020003, 0x00020003, 0x00020003, 0x00030003,
+ 0x00020003, 0x00030003, 0x00020003, 0x00030003,
+ 0x00020003, 0x00030003, 0x00030003, 0x00030003,
+ 0x00030003, 0x00030003, 0x00030003, 0x00030003,
+ 0x00030003, 0x00030003, 0x00030003, 0x00030003
+};
+
+} // End of namespace Image
+
+#endif
diff --git a/image/codecs/codec.cpp b/image/codecs/codec.cpp
index 6b0c7ebcfb..398e9c562c 100644
--- a/image/codecs/codec.cpp
+++ b/image/codecs/codec.cpp
@@ -20,6 +20,7 @@
*
*/
+#include "common/list.h"
#include "common/scummsys.h"
#include "image/codecs/codec.h"
@@ -44,6 +45,153 @@
namespace Image {
+namespace {
+
+/**
+ * Add a color to the QuickTime dither table check queue if it hasn't already been found.
+ */
+inline void addColorToQueue(uint16 color, uint16 index, byte *checkBuffer, Common::List<uint16> &checkQueue) {
+ if ((READ_UINT16(checkBuffer + color * 2) & 0xFF) == 0) {
+ // Previously unfound color
+ WRITE_UINT16(checkBuffer + color * 2, index);
+ checkQueue.push_back(color);
+ }
+}
+
+inline byte adjustColorRange(byte currentColor, byte correctColor, byte palColor) {
+ return CLIP<int>(currentColor - palColor + correctColor, 0, 255);
+}
+
+inline uint16 makeQuickTimeDitherColor(byte r, byte g, byte b) {
+ // RGB554
+ return ((r & 0xF8) << 6) | ((g & 0xF8) << 1) | (b >> 4);
+}
+
+} // End of anonymous namespace
+
+byte *Codec::createQuickTimeDitherTable(const byte *palette, uint colorCount) {
+ byte *buf = new byte[0x10000];
+ memset(buf, 0, 0x10000);
+
+ Common::List<uint16> checkQueue;
+
+ bool foundBlack = false;
+ bool foundWhite = false;
+
+ const byte *palPtr = palette;
+
+ // See what colors we have, and add them to the queue to check
+ for (uint i = 0; i < colorCount; i++) {
+ byte r = *palPtr++;
+ byte g = *palPtr++;
+ byte b = *palPtr++;
+ uint16 n = (i << 8) | 1;
+ uint16 col = makeQuickTimeDitherColor(r, g, b);
+
+ if (col == 0) {
+ // Special case for close-to-black
+ // The original did more here, but it effectively discarded the value
+ // due to a poor if-check (whole 16-bit value instead of lower 8-bits).
+ WRITE_UINT16(buf, n);
+ foundBlack = true;
+ } else if (col == 0x3FFF) {
+ // Special case for close-to-white
+ // The original did more here, but it effectively discarded the value
+ // due to a poor if-check (whole 16-bit value instead of lower 8-bits).
+ WRITE_UINT16(buf + 0x7FFE, n);
+ foundWhite = true;
+ } else {
+ // Previously unfound color
+ addColorToQueue(col, n, buf, checkQueue);
+ }
+ }
+
+ // More special handling for white
+ if (foundWhite)
+ checkQueue.push_front(0x3FFF);
+
+ // More special handling for black
+ if (foundBlack)
+ checkQueue.push_front(0);
+
+ // Go through the list of colors we have and match up similar colors
+ // to fill in the table as best as we can.
+ while (!checkQueue.empty()) {
+ uint16 col = checkQueue.front();
+ checkQueue.pop_front();
+ uint16 index = READ_UINT16(buf + col * 2);
+
+ uint32 x = col << 4;
+ if ((x & 0xFF) < 0xF0)
+ addColorToQueue((x + 0x10) >> 4, index, buf, checkQueue);
+ if ((x & 0xFF) >= 0x10)
+ addColorToQueue((x - 0x10) >> 4, index, buf, checkQueue);
+
+ uint32 y = col << 7;
+ if ((y & 0xFF00) < 0xF800)
+ addColorToQueue((y + 0x800) >> 7, index, buf, checkQueue);
+ if ((y & 0xFF00) >= 0x800)
+ addColorToQueue((y - 0x800) >> 7, index, buf, checkQueue);
+
+ uint32 z = col << 2;
+ if ((z & 0xFF00) < 0xF800)
+ addColorToQueue((z + 0x800) >> 2, index, buf, checkQueue);
+ if ((z & 0xFF00) >= 0x800)
+ addColorToQueue((z - 0x800) >> 2, index, buf, checkQueue);
+ }
+
+ // Contract the table back to just palette entries
+ for (int i = 0; i < 0x4000; i++)
+ buf[i] = READ_UINT16(buf + i * 2) >> 8;
+
+ // Now go through and distribute the error to three more pixels
+ byte *bufPtr = buf;
+ for (uint realR = 0; realR < 0x100; realR += 8) {
+ for (uint realG = 0; realG < 0x100; realG += 8) {
+ for (uint realB = 0; realB < 0x100; realB += 16) {
+ byte palIndex = *bufPtr;
+ byte r = realR;
+ byte g = realG;
+ byte b = realB;
+
+ byte palR = palette[palIndex * 3] & 0xF8;
+ byte palG = palette[palIndex * 3 + 1] & 0xF8;
+ byte palB = palette[palIndex * 3 + 2] & 0xF0;
+
+ r = adjustColorRange(r, realR, palR);
+ g = adjustColorRange(g, realG, palG);
+ b = adjustColorRange(b, realB, palB);
+ palIndex = buf[makeQuickTimeDitherColor(r, g, b)];
+ bufPtr[0x4000] = palIndex;
+
+ palR = palette[palIndex * 3] & 0xF8;
+ palG = palette[palIndex * 3 + 1] & 0xF8;
+ palB = palette[palIndex * 3 + 2] & 0xF0;
+
+ r = adjustColorRange(r, realR, palR);
+ g = adjustColorRange(g, realG, palG);
+ b = adjustColorRange(b, realB, palB);
+ palIndex = buf[makeQuickTimeDitherColor(r, g, b)];
+ bufPtr[0x8000] = palIndex;
+
+ palR = palette[palIndex * 3] & 0xF8;
+ palG = palette[palIndex * 3 + 1] & 0xF8;
+ palB = palette[palIndex * 3 + 2] & 0xF0;
+
+ r = adjustColorRange(r, realR, palR);
+ g = adjustColorRange(g, realG, palG);
+ b = adjustColorRange(b, realB, palB);
+ palIndex = buf[makeQuickTimeDitherColor(r, g, b)];
+ bufPtr[0xC000] = palIndex;
+
+ bufPtr++;
+ }
+ }
+ }
+
+ return buf;
+}
+
Codec *createBitmapCodec(uint32 tag, int width, int height, int bitsPerPixel) {
switch (tag) {
case SWAP_CONSTANT_32(0):
diff --git a/image/codecs/codec.h b/image/codecs/codec.h
index d87758e65e..5c072132d3 100644
--- a/image/codecs/codec.h
+++ b/image/codecs/codec.h
@@ -59,6 +59,20 @@ public:
virtual ~Codec() {}
/**
+ * A type of dithering.
+ */
+ enum DitherType {
+ /** Unknown */
+ kDitherTypeUnknown,
+
+ /** Video for Windows dithering */
+ kDitherTypeVFW,
+
+ /** QuickTime dithering */
+ kDitherTypeQT
+ };
+
+ /**
* Decode the frame for the given data and return a pointer to a surface
* containing the decoded frame.
*
@@ -86,6 +100,21 @@ public:
* Does the codec have a dirty palette?
*/
virtual bool hasDirtyPalette() const { return false; }
+
+ /**
+ * Can the codec dither down to 8bpp?
+ */
+ virtual bool canDither(DitherType type) const { return false; }
+
+ /**
+ * Activate dithering mode with a palette
+ */
+ virtual void setDither(DitherType type, const byte *palette) {}
+
+ /**
+ * Create a dither table, as used by QuickTime codecs.
+ */
+ static byte *createQuickTimeDitherTable(const byte *palette, uint colorCount);
};
/**
diff --git a/image/codecs/qtrle.cpp b/image/codecs/qtrle.cpp
index 94744efa5a..8a83cfa2bd 100644
--- a/image/codecs/qtrle.cpp
+++ b/image/codecs/qtrle.cpp
@@ -37,27 +37,45 @@ namespace Image {
QTRLEDecoder::QTRLEDecoder(uint16 width, uint16 height, byte bitsPerPixel) : Codec() {
_bitsPerPixel = bitsPerPixel;
+ _ditherPalette = 0;
+ _width = width;
+ _height = height;
+ _surface = 0;
+ _dirtyPalette = false;
+ _colorMap = 0;
// We need to ensure the width is a multiple of 4
+ _paddedWidth = width;
uint16 wMod = width % 4;
if (wMod != 0)
- width += 4 - wMod;
+ _paddedWidth += 4 - wMod;
+}
- _surface = new Graphics::Surface();
- _surface->create(width, height, getPixelFormat());
+QTRLEDecoder::~QTRLEDecoder() {
+ if (_surface) {
+ _surface->free();
+ delete _surface;
+ }
+
+ delete[] _colorMap;
+ delete[] _ditherPalette;
}
#define CHECK_STREAM_PTR(n) \
- if ((stream.pos() + n) > stream.size()) { \
- warning("QTRLE Problem: stream out of bounds (%d > %d)", stream.pos() + n, stream.size()); \
- return; \
- }
+ do { \
+ if ((stream.pos() + n) > stream.size()) { \
+ warning("QTRLE Problem: stream out of bounds (%d > %d)", stream.pos() + n, stream.size()); \
+ return; \
+ } \
+ } while (0)
#define CHECK_PIXEL_PTR(n) \
- if ((int32)pixelPtr + n > _surface->w * _surface->h) { \
- warning("QTRLE Problem: pixel ptr = %d, pixel limit = %d", pixelPtr + n, _surface->w * _surface->h); \
- return; \
- } \
+ do { \
+ if ((int32)pixelPtr + n > (int)_paddedWidth * _surface->h) { \
+ warning("QTRLE Problem: pixel ptr = %d, pixel limit = %d", pixelPtr + n, _paddedWidth * _surface->h); \
+ return; \
+ } \
+ } while (0)
void QTRLEDecoder::decode1(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
uint32 pixelPtr = 0;
@@ -73,7 +91,7 @@ void QTRLEDecoder::decode1(Common::SeekableReadStream &stream, uint32 rowPtr, ui
if (skip & 0x80) {
linesToChange--;
- rowPtr += _surface->w;
+ rowPtr += _paddedWidth;
pixelPtr = rowPtr + 2 * (skip & 0x7f);
} else
pixelPtr += 2 * skip;
@@ -159,7 +177,7 @@ void QTRLEDecoder::decode2_4(Common::SeekableReadStream &stream, uint32 rowPtr,
}
}
- rowPtr += _surface->w;
+ rowPtr += _paddedWidth;
}
}
@@ -204,7 +222,7 @@ void QTRLEDecoder::decode8(Common::SeekableReadStream &stream, uint32 rowPtr, ui
}
}
- rowPtr += _surface->w;
+ rowPtr += _paddedWidth;
}
}
@@ -242,7 +260,7 @@ void QTRLEDecoder::decode16(Common::SeekableReadStream &stream, uint32 rowPtr, u
}
}
- rowPtr += _surface->w;
+ rowPtr += _paddedWidth;
}
}
@@ -288,7 +306,72 @@ void QTRLEDecoder::decode24(Common::SeekableReadStream &stream, uint32 rowPtr, u
}
}
- rowPtr += _surface->w;
+ rowPtr += _paddedWidth;
+ }
+}
+
+namespace {
+
+inline uint16 readDitherColor24(Common::ReadStream &stream) {
+ uint16 color = (stream.readByte() & 0xF8) << 6;
+ color |= (stream.readByte() & 0xF8) << 1;
+ color |= stream.readByte() >> 4;
+ return color;
+}
+
+} // End of anonymous namespace
+
+void QTRLEDecoder::dither24(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
+ uint32 pixelPtr = 0;
+ byte *output = (byte *)_surface->getPixels();
+
+ static const uint16 colorTableOffsets[] = { 0x0000, 0xC000, 0x4000, 0x8000 };
+
+ // clone2727 thinks this should be startLine & 3, but the original definitely
+ // isn't doing this. Unless startLine & 3 is always 0? Kinda defeats the
+ // purpose of the compression then.
+ byte curColorTableOffset = 0;
+
+ while (linesToChange--) {
+ CHECK_STREAM_PTR(2);
+
+ byte rowOffset = stream.readByte() - 1;
+ pixelPtr = rowPtr + rowOffset;
+ uint16 colorTableOffset = colorTableOffsets[curColorTableOffset] + (rowOffset << 14);
+
+ for (int8 rleCode = stream.readSByte(); rleCode != -1; rleCode = stream.readSByte()) {
+ if (rleCode == 0) {
+ // there's another skip code in the stream
+ CHECK_STREAM_PTR(1);
+ pixelPtr += stream.readByte() - 1;
+ } else if (rleCode < 0) {
+ // decode the run length code
+ rleCode = -rleCode;
+
+ CHECK_STREAM_PTR(3);
+ CHECK_PIXEL_PTR(rleCode);
+
+ uint16 color = readDitherColor24(stream);
+
+ while (rleCode--) {
+ output[pixelPtr++] = _colorMap[colorTableOffset + color];
+ colorTableOffset += 0x4000;
+ }
+ } else {
+ CHECK_STREAM_PTR(rleCode * 3);
+ CHECK_PIXEL_PTR(rleCode);
+
+ // copy pixels directly to output
+ while (rleCode--) {
+ uint16 color = readDitherColor24(stream);
+ output[pixelPtr++] = _colorMap[colorTableOffset + color];
+ colorTableOffset += 0x4000;
+ }
+ }
+ }
+
+ rowPtr += _paddedWidth;
+ curColorTableOffset = (curColorTableOffset + 1) & 3;
}
}
@@ -336,13 +419,16 @@ void QTRLEDecoder::decode32(Common::SeekableReadStream &stream, uint32 rowPtr, u
}
}
- rowPtr += _surface->w;
+ rowPtr += _paddedWidth;
}
}
const Graphics::Surface *QTRLEDecoder::decodeFrame(Common::SeekableReadStream &stream) {
+ if (!_surface)
+ createSurface();
+
uint16 startLine = 0;
- uint16 height = _surface->h;
+ uint16 height = _height;
// check if this frame is even supposed to change
if (stream.size() < 8)
@@ -365,7 +451,7 @@ const Graphics::Surface *QTRLEDecoder::decodeFrame(Common::SeekableReadStream &s
stream.readUint16BE(); // Unknown
}
- uint32 rowPtr = _surface->w * startLine;
+ uint32 rowPtr = _paddedWidth * startLine;
switch (_bitsPerPixel) {
case 1:
@@ -388,7 +474,10 @@ const Graphics::Surface *QTRLEDecoder::decodeFrame(Common::SeekableReadStream &s
decode16(stream, rowPtr, height);
break;
case 24:
- decode24(stream, rowPtr, height);
+ if (_ditherPalette)
+ dither24(stream, rowPtr, height);
+ else
+ decode24(stream, rowPtr, height);
break;
case 32:
decode32(stream, rowPtr, height);
@@ -400,12 +489,10 @@ const Graphics::Surface *QTRLEDecoder::decodeFrame(Common::SeekableReadStream &s
return _surface;
}
-QTRLEDecoder::~QTRLEDecoder() {
- _surface->free();
- delete _surface;
-}
-
Graphics::PixelFormat QTRLEDecoder::getPixelFormat() const {
+ if (_ditherPalette)
+ return Graphics::PixelFormat::createFormatCLUT8();
+
switch (_bitsPerPixel) {
case 1:
case 33:
@@ -428,4 +515,31 @@ Graphics::PixelFormat QTRLEDecoder::getPixelFormat() const {
return Graphics::PixelFormat();
}
+bool QTRLEDecoder::canDither(DitherType type) const {
+ // Only 24-bit dithering is implemented at the moment
+ return type == kDitherTypeQT && _bitsPerPixel == 24;
+}
+
+void QTRLEDecoder::setDither(DitherType type, const byte *palette) {
+ assert(canDither(type));
+
+ _ditherPalette = new byte[256 * 3];
+ memcpy(_ditherPalette, palette, 256 * 3);
+ _dirtyPalette = true;
+
+ delete[] _colorMap;
+ _colorMap = createQuickTimeDitherTable(palette, 256);
+}
+
+void QTRLEDecoder::createSurface() {
+ if (_surface) {
+ _surface->free();
+ delete _surface;
+ }
+
+ _surface = new Graphics::Surface();
+ _surface->create(_paddedWidth, _height, getPixelFormat());
+ _surface->w = _width;
+}
+
} // End of namespace Image
diff --git a/image/codecs/qtrle.h b/image/codecs/qtrle.h
index b44a46c3e2..e345fbeedf 100644
--- a/image/codecs/qtrle.h
+++ b/image/codecs/qtrle.h
@@ -41,16 +41,29 @@ public:
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream);
Graphics::PixelFormat getPixelFormat() const;
+ bool containsPalette() const { return _ditherPalette != 0; }
+ const byte *getPalette() { _dirtyPalette = false; return _ditherPalette; }
+ bool hasDirtyPalette() const { return _dirtyPalette; }
+ bool canDither(DitherType type) const;
+ void setDither(DitherType type, const byte *palette);
+
private:
byte _bitsPerPixel;
-
Graphics::Surface *_surface;
+ uint16 _width, _height;
+ uint32 _paddedWidth;
+ byte *_ditherPalette;
+ bool _dirtyPalette;
+ byte *_colorMap;
+
+ void createSurface();
void decode1(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
void decode2_4(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange, byte bpp);
void decode8(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
void decode16(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
void decode24(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
+ void dither24(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
void decode32(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
};
diff --git a/image/codecs/rpza.cpp b/image/codecs/rpza.cpp
index 5aeee7c90b..8d648e1cc1 100644
--- a/image/codecs/rpza.cpp
+++ b/image/codecs/rpza.cpp
@@ -32,43 +32,185 @@
namespace Image {
RPZADecoder::RPZADecoder(uint16 width, uint16 height) : Codec() {
- // We need to increase the surface size to a multiple of 4
- uint16 wMod = width % 4;
- if (wMod != 0)
- width += 4 - wMod;
-
- _surface = new Graphics::Surface();
- _surface->create(width, height, getPixelFormat());
+ _format = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
+ _ditherPalette = 0;
+ _dirtyPalette = false;
+ _colorMap = 0;
+ _width = width;
+ _height = height;
+ _blockWidth = (width + 3) / 4;
+ _blockHeight = (height + 3) / 4;
+ _surface = 0;
}
RPZADecoder::~RPZADecoder() {
- _surface->free();
- delete _surface;
+ if (_surface) {
+ _surface->free();
+ delete _surface;
+ }
+
+ delete[] _ditherPalette;
}
#define ADVANCE_BLOCK() \
- pixelPtr += 4; \
- if (pixelPtr >= _surface->w) { \
- pixelPtr = 0; \
- rowPtr += _surface->w * 4; \
+ blockPtr += 4; \
+ if (blockPtr >= endPtr) { \
+ blockPtr += pitch * 3; \
+ endPtr = blockPtr + pitch; \
} \
totalBlocks--; \
if (totalBlocks < 0) \
error("rpza block counter just went negative (this should not happen)") \
-#define PUT_PIXEL(color) \
- if ((int32)blockPtr < _surface->w * _surface->h) \
- WRITE_UINT16((uint16 *)_surface->getPixels() + blockPtr, color); \
- blockPtr++
+struct BlockDecoderRaw {
+ static inline void drawFillBlock(uint16 *blockPtr, uint16 pitch, uint16 color, const byte *colorMap) {
+ blockPtr[0] = color;
+ blockPtr[1] = color;
+ blockPtr[2] = color;
+ blockPtr[3] = color;
+ blockPtr += pitch;
+ blockPtr[0] = color;
+ blockPtr[1] = color;
+ blockPtr[2] = color;
+ blockPtr[3] = color;
+ blockPtr += pitch;
+ blockPtr[0] = color;
+ blockPtr[1] = color;
+ blockPtr[2] = color;
+ blockPtr[3] = color;
+ blockPtr += pitch;
+ blockPtr[0] = color;
+ blockPtr[1] = color;
+ blockPtr[2] = color;
+ blockPtr[3] = color;
+ }
-const Graphics::Surface *RPZADecoder::decodeFrame(Common::SeekableReadStream &stream) {
+ static inline void drawRawBlock(uint16 *blockPtr, uint16 pitch, const uint16 (&colors)[16], const byte *colorMap) {
+ blockPtr[0] = colors[0];
+ blockPtr[1] = colors[1];
+ blockPtr[2] = colors[2];
+ blockPtr[3] = colors[3];
+ blockPtr += pitch;
+ blockPtr[0] = colors[4];
+ blockPtr[1] = colors[5];
+ blockPtr[2] = colors[6];
+ blockPtr[3] = colors[7];
+ blockPtr += pitch;
+ blockPtr[0] = colors[8];
+ blockPtr[1] = colors[9];
+ blockPtr[2] = colors[10];
+ blockPtr[3] = colors[11];
+ blockPtr += pitch;
+ blockPtr[0] = colors[12];
+ blockPtr[1] = colors[13];
+ blockPtr[2] = colors[14];
+ blockPtr[3] = colors[15];
+ }
+
+ static inline void drawBlendBlock(uint16 *blockPtr, uint16 pitch, const uint16 (&colors)[4], const byte (&indexes)[4], const byte *colorMap) {
+ blockPtr[0] = colors[(indexes[0] >> 6) & 0x03];
+ blockPtr[1] = colors[(indexes[0] >> 4) & 0x03];
+ blockPtr[2] = colors[(indexes[0] >> 2) & 0x03];
+ blockPtr[3] = colors[(indexes[0] >> 0) & 0x03];
+ blockPtr += pitch;
+ blockPtr[0] = colors[(indexes[1] >> 6) & 0x03];
+ blockPtr[1] = colors[(indexes[1] >> 4) & 0x03];
+ blockPtr[2] = colors[(indexes[1] >> 2) & 0x03];
+ blockPtr[3] = colors[(indexes[1] >> 0) & 0x03];
+ blockPtr += pitch;
+ blockPtr[0] = colors[(indexes[2] >> 6) & 0x03];
+ blockPtr[1] = colors[(indexes[2] >> 4) & 0x03];
+ blockPtr[2] = colors[(indexes[2] >> 2) & 0x03];
+ blockPtr[3] = colors[(indexes[2] >> 0) & 0x03];
+ blockPtr += pitch;
+ blockPtr[0] = colors[(indexes[3] >> 6) & 0x03];
+ blockPtr[1] = colors[(indexes[3] >> 4) & 0x03];
+ blockPtr[2] = colors[(indexes[3] >> 2) & 0x03];
+ blockPtr[3] = colors[(indexes[3] >> 0) & 0x03];
+ }
+};
+
+struct BlockDecoderDither {
+ static inline void drawFillBlock(byte *blockPtr, uint16 pitch, uint16 color, const byte *colorMap) {
+ const byte *mapOffset = colorMap + (color >> 1);
+ byte pixel1 = mapOffset[0x0000];
+ byte pixel2 = mapOffset[0x4000];
+ byte pixel3 = mapOffset[0x8000];
+ byte pixel4 = mapOffset[0xC000];
+
+ blockPtr[0] = pixel1;
+ blockPtr[1] = pixel2;
+ blockPtr[2] = pixel3;
+ blockPtr[3] = pixel4;
+ blockPtr += pitch;
+ blockPtr[0] = pixel4;
+ blockPtr[1] = pixel1;
+ blockPtr[2] = pixel2;
+ blockPtr[3] = pixel3;
+ blockPtr += pitch;
+ blockPtr[0] = pixel2;
+ blockPtr[1] = pixel3;
+ blockPtr[2] = pixel4;
+ blockPtr[3] = pixel1;
+ blockPtr += pitch;
+ blockPtr[0] = pixel3;
+ blockPtr[1] = pixel4;
+ blockPtr[2] = pixel1;
+ blockPtr[3] = pixel2;
+ }
+
+ static inline void drawRawBlock(byte *blockPtr, uint16 pitch, const uint16 (&colors)[16], const byte *colorMap) {
+ blockPtr[0] = colorMap[(colors[0] >> 1) + 0x0000];
+ blockPtr[1] = colorMap[(colors[1] >> 1) + 0x4000];
+ blockPtr[2] = colorMap[(colors[2] >> 1) + 0x8000];
+ blockPtr[3] = colorMap[(colors[3] >> 1) + 0xC000];
+ blockPtr += pitch;
+ blockPtr[0] = colorMap[(colors[4] >> 1) + 0xC000];
+ blockPtr[1] = colorMap[(colors[5] >> 1) + 0x0000];
+ blockPtr[2] = colorMap[(colors[6] >> 1) + 0x4000];
+ blockPtr[3] = colorMap[(colors[7] >> 1) + 0x8000];
+ blockPtr += pitch;
+ blockPtr[0] = colorMap[(colors[8] >> 1) + 0x4000];
+ blockPtr[1] = colorMap[(colors[9] >> 1) + 0x8000];
+ blockPtr[2] = colorMap[(colors[10] >> 1) + 0xC000];
+ blockPtr[3] = colorMap[(colors[11] >> 1) + 0x0000];
+ blockPtr += pitch;
+ blockPtr[0] = colorMap[(colors[12] >> 1) + 0x8000];
+ blockPtr[1] = colorMap[(colors[13] >> 1) + 0xC000];
+ blockPtr[2] = colorMap[(colors[14] >> 1) + 0x0000];
+ blockPtr[3] = colorMap[(colors[15] >> 1) + 0x4000];
+ }
+
+ static inline void drawBlendBlock(byte *blockPtr, uint16 pitch, const uint16 (&colors)[4], const byte (&indexes)[4], const byte *colorMap) {
+ blockPtr[0] = colorMap[(colors[(indexes[0] >> 6) & 0x03] >> 1) + 0x0000];
+ blockPtr[1] = colorMap[(colors[(indexes[0] >> 4) & 0x03] >> 1) + 0x4000];
+ blockPtr[2] = colorMap[(colors[(indexes[0] >> 2) & 0x03] >> 1) + 0x8000];
+ blockPtr[3] = colorMap[(colors[(indexes[0] >> 0) & 0x03] >> 1) + 0xC000];
+ blockPtr += pitch;
+ blockPtr[0] = colorMap[(colors[(indexes[1] >> 6) & 0x03] >> 1) + 0xC000];
+ blockPtr[1] = colorMap[(colors[(indexes[1] >> 4) & 0x03] >> 1) + 0x0000];
+ blockPtr[2] = colorMap[(colors[(indexes[1] >> 2) & 0x03] >> 1) + 0x4000];
+ blockPtr[3] = colorMap[(colors[(indexes[1] >> 0) & 0x03] >> 1) + 0x8000];
+ blockPtr += pitch;
+ blockPtr[0] = colorMap[(colors[(indexes[2] >> 6) & 0x03] >> 1) + 0x4000];
+ blockPtr[1] = colorMap[(colors[(indexes[2] >> 4) & 0x03] >> 1) + 0x8000];
+ blockPtr[2] = colorMap[(colors[(indexes[2] >> 2) & 0x03] >> 1) + 0xC000];
+ blockPtr[3] = colorMap[(colors[(indexes[2] >> 0) & 0x03] >> 1) + 0x0000];
+ blockPtr += pitch;
+ blockPtr[0] = colorMap[(colors[(indexes[3] >> 6) & 0x03] >> 1) + 0x8000];
+ blockPtr[1] = colorMap[(colors[(indexes[3] >> 4) & 0x03] >> 1) + 0xC000];
+ blockPtr[2] = colorMap[(colors[(indexes[3] >> 2) & 0x03] >> 1) + 0x0000];
+ blockPtr[3] = colorMap[(colors[(indexes[3] >> 0) & 0x03] >> 1) + 0x4000];
+ }
+};
+
+template<typename PixelInt, typename BlockDecoder>
+static inline void decodeFrameTmpl(Common::SeekableReadStream &stream, PixelInt *ptr, uint16 pitch, uint16 blockWidth, uint16 blockHeight, const byte *colorMap) {
uint16 colorA = 0, colorB = 0;
uint16 color4[4];
- uint32 rowPtr = 0;
- uint32 pixelPtr = 0;
- uint32 blockPtr = 0;
- uint32 rowInc = _surface->w - 4;
+ PixelInt *blockPtr = ptr;
+ PixelInt *endPtr = ptr + pitch;
uint16 ta;
uint16 tb;
@@ -88,7 +230,7 @@ const Graphics::Surface *RPZADecoder::decodeFrame(Common::SeekableReadStream &st
}
// Number of 4x4 blocks in frame
- int32 totalBlocks = ((_surface->w + 3) / 4) * ((_surface->h + 3) / 4);
+ int32 totalBlocks = blockWidth * blockHeight;
// Process chunk data
while ((uint32)stream.pos() < chunkSize) {
@@ -117,14 +259,9 @@ const Graphics::Surface *RPZADecoder::decodeFrame(Common::SeekableReadStream &st
break;
case 0xa0: // Fill blocks with one color
colorA = stream.readUint16BE();
+
while (numBlocks--) {
- blockPtr = rowPtr + pixelPtr;
- for (byte pixel_y = 0; pixel_y < 4; pixel_y++) {
- for (byte pixel_x = 0; pixel_x < 4; pixel_x++) {
- PUT_PIXEL(colorA);
- }
- blockPtr += rowInc;
- }
+ BlockDecoder::drawFillBlock(blockPtr, pitch, colorA, colorMap);
ADVANCE_BLOCK();
}
break;
@@ -136,10 +273,10 @@ const Graphics::Surface *RPZADecoder::decodeFrame(Common::SeekableReadStream &st
colorB = stream.readUint16BE();
// Sort out the colors
- color4[0] = colorB;
+ color4[0] = colorB & 0x7FFF;
color4[1] = 0;
color4[2] = 0;
- color4[3] = colorA;
+ color4[3] = colorA & 0x7FFF;
// Red components
ta = (colorA >> 10) & 0x1F;
@@ -160,42 +297,69 @@ const Graphics::Surface *RPZADecoder::decodeFrame(Common::SeekableReadStream &st
color4[2] |= ((21 * ta + 11 * tb) >> 5);
while (numBlocks--) {
- blockPtr = rowPtr + pixelPtr;
- for (byte pixel_y = 0; pixel_y < 4; pixel_y++) {
- byte index = stream.readByte();
- for (byte pixel_x = 0; pixel_x < 4; pixel_x++) {
- byte idx = (index >> (2 * (3 - pixel_x))) & 0x03;
- PUT_PIXEL(color4[idx]);
- }
- blockPtr += rowInc;
- }
+ byte indexes[4];
+ stream.read(indexes, 4);
+
+ BlockDecoder::drawBlendBlock(blockPtr, pitch, color4, indexes, colorMap);
ADVANCE_BLOCK();
}
break;
// Fill block with 16 colors
- case 0x00:
- blockPtr = rowPtr + pixelPtr;
- for (byte pixel_y = 0; pixel_y < 4; pixel_y++) {
- for (byte pixel_x = 0; pixel_x < 4; pixel_x++) {
- // We already have color of upper left pixel
- if (pixel_y != 0 || pixel_x != 0)
- colorA = stream.readUint16BE();
-
- PUT_PIXEL(colorA);
- }
- blockPtr += rowInc;
- }
+ case 0x00: {
+ uint16 colors[16];
+ colors[0] = colorA;
+
+ for (int i = 0; i < 15; i++)
+ colors[i + 1] = stream.readUint16BE();
+
+ BlockDecoder::drawRawBlock(blockPtr, pitch, colors, colorMap);
ADVANCE_BLOCK();
break;
+ }
// Unknown opcode
default:
error("Unknown opcode %02x in rpza chunk", opcode);
}
}
+}
+
+const Graphics::Surface *RPZADecoder::decodeFrame(Common::SeekableReadStream &stream) {
+ if (!_surface) {
+ _surface = new Graphics::Surface();
+
+ // Allocate enough space in the surface for the blocks
+ _surface->create(_blockWidth * 4, _blockHeight * 4, getPixelFormat());
+
+ // Adjust width/height to be the right ones
+ _surface->w = _width;
+ _surface->h = _height;
+ }
+
+ if (_colorMap)
+ decodeFrameTmpl<byte, BlockDecoderDither>(stream, (byte *)_surface->getPixels(), _surface->pitch, _blockWidth, _blockHeight, _colorMap);
+ else
+ decodeFrameTmpl<uint16, BlockDecoderRaw>(stream, (uint16 *)_surface->getPixels(), _surface->pitch / 2, _blockWidth, _blockHeight, _colorMap);
return _surface;
}
+bool RPZADecoder::canDither(DitherType type) const {
+ return type == kDitherTypeQT;
+}
+
+void RPZADecoder::setDither(DitherType type, const byte *palette) {
+ assert(canDither(type));
+
+ _ditherPalette = new byte[256 * 3];
+ memcpy(_ditherPalette, palette, 256 * 3);
+
+ _dirtyPalette = true;
+ _format = Graphics::PixelFormat::createFormatCLUT8();
+
+ delete[] _colorMap;
+ _colorMap = createQuickTimeDitherTable(palette, 256);
+}
+
} // End of namespace Image
diff --git a/image/codecs/rpza.h b/image/codecs/rpza.h
index d1dbbdb676..d62b385330 100644
--- a/image/codecs/rpza.h
+++ b/image/codecs/rpza.h
@@ -39,10 +39,22 @@ public:
~RPZADecoder();
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream);
- Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); }
+ Graphics::PixelFormat getPixelFormat() const { return _format; }
+
+ bool containsPalette() const { return _ditherPalette != 0; }
+ const byte *getPalette() { _dirtyPalette = false; return _ditherPalette; }
+ bool hasDirtyPalette() const { return _dirtyPalette; }
+ bool canDither(DitherType type) const;
+ void setDither(DitherType type, const byte *palette);
private:
+ Graphics::PixelFormat _format;
Graphics::Surface *_surface;
+ byte *_ditherPalette;
+ bool _dirtyPalette;
+ byte *_colorMap;
+ uint16 _width, _height;
+ uint16 _blockWidth, _blockHeight;
};
} // End of namespace Image
diff --git a/video/avi_decoder.cpp b/video/avi_decoder.cpp
index 7119c72f07..700975d9a2 100644
--- a/video/avi_decoder.cpp
+++ b/video/avi_decoder.cpp
@@ -815,6 +815,30 @@ void AVIDecoder::AVIVideoTrack::forceTrackEnd() {
_curFrame = _frameCount - 1;
}
+const byte *AVIDecoder::AVIVideoTrack::getPalette() const {
+ if (_videoCodec && _videoCodec->containsPalette())
+ return _videoCodec->getPalette();
+
+ _dirtyPalette = false;
+ return _palette;
+}
+
+bool AVIDecoder::AVIVideoTrack::hasDirtyPalette() const {
+ if (_videoCodec && _videoCodec->containsPalette())
+ return _videoCodec->hasDirtyPalette();
+
+ return _dirtyPalette;
+}
+
+bool AVIDecoder::AVIVideoTrack::canDither() const {
+ return _videoCodec && _videoCodec->canDither(Image::Codec::kDitherTypeVFW);
+}
+
+void AVIDecoder::AVIVideoTrack::setDither(const byte *palette) {
+ assert(_videoCodec);
+ _videoCodec->setDither(Image::Codec::kDitherTypeVFW, palette);
+}
+
AVIDecoder::AVIAudioTrack::AVIAudioTrack(const AVIStreamHeader &streamHeader, const PCMWaveFormat &waveFormat, Audio::Mixer::SoundType soundType)
: _audsHeader(streamHeader), _wvInfo(waveFormat), _soundType(soundType), _curChunk(0) {
_audStream = createAudioStream();
diff --git a/video/avi_decoder.h b/video/avi_decoder.h
index 8941ff4e75..6c1ce1a4b9 100644
--- a/video/avi_decoder.h
+++ b/video/avi_decoder.h
@@ -179,11 +179,14 @@ protected:
int getCurFrame() const { return _curFrame; }
int getFrameCount() const { return _frameCount; }
const Graphics::Surface *decodeNextFrame() { return _lastFrame; }
- const byte *getPalette() const { _dirtyPalette = false; return _palette; }
- bool hasDirtyPalette() const { return _dirtyPalette; }
+
+ const byte *getPalette() const;
+ bool hasDirtyPalette() const;
void setCurFrame(int frame) { _curFrame = frame; }
void loadPaletteFromChunk(Common::SeekableReadStream *chunk);
void useInitialPalette();
+ bool canDither() const;
+ void setDither(const byte *palette);
bool isTruemotion1() const;
void forceDimensions(uint16 width, uint16 height);
diff --git a/video/qt_decoder.cpp b/video/qt_decoder.cpp
index 9b77ef70c1..324bf65294 100644
--- a/video/qt_decoder.cpp
+++ b/video/qt_decoder.cpp
@@ -292,6 +292,9 @@ QuickTimeDecoder::VideoTrackHandler::VideoTrackHandler(QuickTimeDecoder *decoder
_curPalette = 0;
_dirtyPalette = false;
_reversed = false;
+ _forcedDitherPalette = 0;
+ _ditherTable = 0;
+ _ditherFrame = 0;
}
QuickTimeDecoder::VideoTrackHandler::~VideoTrackHandler() {
@@ -299,6 +302,14 @@ QuickTimeDecoder::VideoTrackHandler::~VideoTrackHandler() {
_scaledSurface->free();
delete _scaledSurface;
}
+
+ delete[] _forcedDitherPalette;
+ delete[] _ditherTable;
+
+ if (_ditherFrame) {
+ _ditherFrame->free();
+ delete _ditherFrame;
+ }
}
bool QuickTimeDecoder::VideoTrackHandler::endOfTrack() const {
@@ -382,6 +393,9 @@ uint16 QuickTimeDecoder::VideoTrackHandler::getHeight() const {
}
Graphics::PixelFormat QuickTimeDecoder::VideoTrackHandler::getPixelFormat() const {
+ if (_forcedDitherPalette)
+ return Graphics::PixelFormat::createFormatCLUT8();
+
return ((VideoSampleDesc *)_parent->sampleDescs[0])->_videoCodec->getPixelFormat();
}
@@ -463,6 +477,10 @@ const Graphics::Surface *QuickTimeDecoder::VideoTrackHandler::decodeNextFrame()
}
}
+ // Handle forced dithering
+ if (frame && _forcedDitherPalette)
+ frame = forceDither(*frame);
+
if (frame && (_parent->scaleFactorX != 1 || _parent->scaleFactorY != 1)) {
if (!_scaledSurface) {
_scaledSurface = new Graphics::Surface();
@@ -476,6 +494,11 @@ const Graphics::Surface *QuickTimeDecoder::VideoTrackHandler::decodeNextFrame()
return frame;
}
+const byte *QuickTimeDecoder::VideoTrackHandler::getPalette() const {
+ _dirtyPalette = false;
+ return _forcedDitherPalette ? _forcedDitherPalette : _curPalette;
+}
+
bool QuickTimeDecoder::VideoTrackHandler::setReverse(bool reverse) {
_reversed = reverse;
@@ -751,4 +774,101 @@ bool QuickTimeDecoder::VideoTrackHandler::endOfCurEdit() const {
return getRateAdjustedFrameTime() >= getCurEditTimeOffset() + getCurEditTrackDuration();
}
+bool QuickTimeDecoder::VideoTrackHandler::canDither() const {
+ for (uint i = 0; i < _parent->sampleDescs.size(); i++) {
+ VideoSampleDesc *desc = (VideoSampleDesc *)_parent->sampleDescs[i];
+
+ if (!desc || !desc->_videoCodec)
+ return false;
+ }
+
+ return true;
+}
+
+void QuickTimeDecoder::VideoTrackHandler::setDither(const byte *palette) {
+ assert(canDither());
+
+ for (uint i = 0; i < _parent->sampleDescs.size(); i++) {
+ VideoSampleDesc *desc = (VideoSampleDesc *)_parent->sampleDescs[i];
+
+ if (desc->_videoCodec->canDither(Image::Codec::kDitherTypeQT)) {
+ // Codec dither
+ desc->_videoCodec->setDither(Image::Codec::kDitherTypeQT, palette);
+ } else {
+ // Forced dither
+ _forcedDitherPalette = new byte[256 * 3];
+ memcpy(_forcedDitherPalette, palette, 256 * 3);
+ _ditherTable = Image::Codec::createQuickTimeDitherTable(_forcedDitherPalette, 256);
+ _dirtyPalette = true;
+ }
+ }
+}
+
+namespace {
+
+// Return a pixel in RGB554
+uint16 makeDitherColor(byte r, byte g, byte b) {
+ return ((r & 0xF8) << 6) | ((g & 0xF8) << 1) | (b >> 4);
+}
+
+// Default template to convert a dither color
+template<typename PixelInt>
+inline uint16 readDitherColor(PixelInt srcColor, const Graphics::PixelFormat& format, const byte *palette) {
+ byte r, g, b;
+ format.colorToRGB(srcColor, r, g, b);
+ return makeDitherColor(r, g, b);
+}
+
+// Specialized version for 8bpp
+template<>
+inline uint16 readDitherColor(byte srcColor, const Graphics::PixelFormat& format, const byte *palette) {
+ return makeDitherColor(palette[srcColor * 3], palette[srcColor * 3 + 1], palette[srcColor * 3 + 2]);
+}
+
+template<typename PixelInt>
+void ditherFrame(const Graphics::Surface &src, Graphics::Surface &dst, const byte *ditherTable, const byte *palette = 0) {
+ static const uint16 colorTableOffsets[] = { 0x0000, 0xC000, 0x4000, 0x8000 };
+
+ for (int y = 0; y < dst.h; y++) {
+ const PixelInt *srcPtr = (const PixelInt *)src.getBasePtr(0, y);
+ byte *dstPtr = (byte *)dst.getBasePtr(0, y);
+ uint16 colorTableOffset = colorTableOffsets[y & 3];
+
+ for (int x = 0; x < dst.w; x++) {
+ uint16 color = readDitherColor(*srcPtr++, src.format, palette);
+ *dstPtr++ = ditherTable[colorTableOffset + color];
+ colorTableOffset += 0x4000;
+ }
+ }
+}
+
+} // End of anonymous namespace
+
+const Graphics::Surface *QuickTimeDecoder::VideoTrackHandler::forceDither(const Graphics::Surface &frame) {
+ if (frame.format.bytesPerPixel == 1) {
+ // This should always be true, but this is for sanity
+ if (!_curPalette)
+ return &frame;
+
+ // If the palettes match, bail out
+ if (memcmp(_forcedDitherPalette, _curPalette, 256 * 3) == 0)
+ return &frame;
+ }
+
+ // Need to create a new one
+ if (!_ditherFrame) {
+ _ditherFrame = new Graphics::Surface();
+ _ditherFrame->create(frame.w, frame.h, Graphics::PixelFormat::createFormatCLUT8());
+ }
+
+ if (frame.format.bytesPerPixel == 1)
+ ditherFrame<byte>(frame, *_ditherFrame, _ditherTable, _curPalette);
+ else if (frame.format.bytesPerPixel == 2)
+ ditherFrame<uint16>(frame, *_ditherFrame, _ditherTable);
+ else if (frame.format.bytesPerPixel == 4)
+ ditherFrame<uint32>(frame, *_ditherFrame, _ditherTable);
+
+ return _ditherFrame;
+}
+
} // End of namespace Video
diff --git a/video/qt_decoder.h b/video/qt_decoder.h
index 99ac9ff5f7..5a6c5eebec 100644
--- a/video/qt_decoder.h
+++ b/video/qt_decoder.h
@@ -136,10 +136,12 @@ private:
int getFrameCount() const;
uint32 getNextFrameStartTime() const;
const Graphics::Surface *decodeNextFrame();
- const byte *getPalette() const { _dirtyPalette = false; return _curPalette; }
+ const byte *getPalette() const;
bool hasDirtyPalette() const { return _curPalette; }
bool setReverse(bool reverse);
bool isReversed() const { return _reversed; }
+ bool canDither() const;
+ void setDither(const byte *palette);
Common::Rational getScaledWidth() const;
Common::Rational getScaledHeight() const;
@@ -156,6 +158,12 @@ private:
mutable bool _dirtyPalette;
bool _reversed;
+ // Forced dithering of frames
+ byte *_forcedDitherPalette;
+ byte *_ditherTable;
+ Graphics::Surface *_ditherFrame;
+ const Graphics::Surface *forceDither(const Graphics::Surface &frame);
+
Common::SeekableReadStream *getNextFramePacket(uint32 &descId);
uint32 getFrameDuration();
uint32 findKeyFrame(uint32 frame) const;
diff --git a/video/video_decoder.cpp b/video/video_decoder.cpp
index a4bc5b81a2..217b4c8456 100644
--- a/video/video_decoder.cpp
+++ b/video/video_decoder.cpp
@@ -47,6 +47,7 @@ VideoDecoder::VideoDecoder() {
_endTimeSet = false;
_nextVideoTrack = 0;
_mainAudioTrack = 0;
+ _canSetDither = true;
// Find the best format for output
_defaultHighColorFormat = g_system->getScreenFormat();
@@ -77,6 +78,7 @@ void VideoDecoder::close() {
_endTimeSet = false;
_nextVideoTrack = 0;
_mainAudioTrack = 0;
+ _canSetDither = true;
}
bool VideoDecoder::loadFile(const Common::String &filename) {
@@ -171,6 +173,7 @@ Graphics::PixelFormat VideoDecoder::getPixelFormat() const {
const Graphics::Surface *VideoDecoder::decodeNextFrame() {
_needsUpdate = false;
+ _canSetDither = false;
readNextPacket();
@@ -488,6 +491,23 @@ bool VideoDecoder::seekIntern(const Audio::Timestamp &time) {
return true;
}
+bool VideoDecoder::setDitheringPalette(const byte *palette) {
+ // If a frame was already decoded, we can't set it now.
+ if (!_canSetDither)
+ return false;
+
+ bool result = false;
+
+ for (TrackList::iterator it = _tracks.begin(); it != _tracks.end(); it++) {
+ if ((*it)->getTrackType() == Track::kTrackTypeVideo && ((VideoTrack *)*it)->canDither()) {
+ ((VideoTrack *)*it)->setDither(palette);
+ result = true;
+ }
+ }
+
+ return result;
+}
+
VideoDecoder::Track::Track() {
_paused = false;
}
diff --git a/video/video_decoder.h b/video/video_decoder.h
index c3879e9144..eca15e7265 100644
--- a/video/video_decoder.h
+++ b/video/video_decoder.h
@@ -377,6 +377,25 @@ public:
*/
bool setReverse(bool reverse);
+ /**
+ * Tell the video to dither to a palette.
+ *
+ * By default, VideoDecoder will return surfaces in native, or in the case
+ * of YUV-based videos, the format set by setDefaultHighColorFormat().
+ * For video formats or codecs that support it, this will start outputting
+ * its surfaces in 8bpp with this palette.
+ *
+ * This should be called after loadStream(), but before a decodeNextFrame()
+ * call. This is enforced.
+ *
+ * The palette will be copied, so you do not need to worry about the pointer
+ * going out-of-scope.
+ *
+ * @param palette The palette to use for dithering
+ * @return true on success, false otherwise
+ */
+ bool setDitheringPalette(const byte *palette);
+
/////////////////////////////////////////
// Audio Control
/////////////////////////////////////////
@@ -604,6 +623,16 @@ protected:
* Is the video track set to play in reverse?
*/
virtual bool isReversed() const { return false; }
+
+ /**
+ * Can the video track dither?
+ */
+ virtual bool canDither() const { return false; }
+
+ /**
+ * Activate dithering mode with a palette
+ */
+ virtual void setDither(const byte *palette) {}
};
/**
@@ -901,6 +930,9 @@ private:
mutable bool _dirtyPalette;
const byte *_palette;
+ // Enforcement of not being able to set dither
+ bool _canSetDither;
+
// Default PixelFormat settings
Graphics::PixelFormat _defaultHighColorFormat;