aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore17
-rw-r--r--AUTHORS2
-rw-r--r--NEWS38
-rw-r--r--audio/mididrv.h11
-rw-r--r--audio/mixer.cpp26
-rw-r--r--audio/mixer_intern.h4
-rw-r--r--backends/base-backend.cpp3
-rw-r--r--backends/graphics/opengl/gltexture.cpp2
-rw-r--r--backends/graphics/opengl/opengl-graphics.cpp151
-rw-r--r--backends/graphics/openglsdl/openglsdl-graphics.cpp17
-rw-r--r--backends/graphics/sdl/sdl-graphics.cpp14
-rw-r--r--backends/platform/android/android.cpp1347
-rw-r--r--backends/platform/android/android.h210
-rw-r--r--backends/platform/android/asset-archive.cpp35
-rw-r--r--backends/platform/android/asset-archive.h5
-rw-r--r--backends/platform/android/gfx.cpp699
-rw-r--r--backends/platform/android/jni.cpp653
-rw-r--r--backends/platform/android/jni.h158
-rw-r--r--backends/platform/android/module.mk6
-rw-r--r--backends/platform/android/org/inodes/gus/scummvm/EditableSurfaceView.java15
-rw-r--r--backends/platform/android/org/inodes/gus/scummvm/PluginProvider.java14
-rw-r--r--backends/platform/android/org/inodes/gus/scummvm/ScummVM.java618
-rw-r--r--backends/platform/android/org/inodes/gus/scummvm/ScummVMActivity.java264
-rw-r--r--backends/platform/android/org/inodes/gus/scummvm/ScummVMApplication.java10
-rw-r--r--backends/platform/android/org/inodes/gus/scummvm/Unpacker.java1
-rw-r--r--backends/platform/android/texture.cpp (renamed from backends/platform/android/video.cpp)253
-rw-r--r--backends/platform/android/texture.h (renamed from backends/platform/android/video.h)148
-rw-r--r--backends/platform/sdl/sdl.cpp21
-rw-r--r--backends/platform/symbian/S60v3/ScummVM_A0000658_S60v3.mmp.in2
-rw-r--r--backends/platform/symbian/S60v3/ScummVM_S60v3.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_agi.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_agos.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_base.mmp.in10
-rw-r--r--backends/platform/symbian/mmp/scummvm_cine.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_cruise.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_draci.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_drascula.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_gob.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_groovie.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_hugo.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_kyra.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_lastexpress.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_lure.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_m4.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_made.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_mohawk.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_parallaction.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_queen.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_saga.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_sci.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_scumm.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_sky.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_sword1.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_sword2.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_teenagent.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_tinsel.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_toon.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_touche.mmp.in2
-rw-r--r--backends/platform/symbian/mmp/scummvm_tucker.mmp.in2
-rw-r--r--base/commandLine.cpp3
-rw-r--r--common/config-manager.cpp4
-rwxr-xr-xconfigure24
-rw-r--r--dists/android/AndroidManifest.xml113
-rw-r--r--dists/android/AndroidManifest.xml.in113
-rw-r--r--engines/agi/agi.cpp23
-rw-r--r--engines/agi/agi.h5
-rw-r--r--engines/agi/detection_tables.h1
-rw-r--r--engines/agi/preagi.cpp3
-rw-r--r--engines/agos/agos.h2
-rw-r--r--engines/agos/animation.cpp4
-rw-r--r--engines/agos/oracle.cpp6
-rw-r--r--engines/agos/script_ff.cpp3
-rw-r--r--engines/cruise/cruise_main.cpp5
-rw-r--r--engines/cruise/script.cpp4
-rw-r--r--engines/engine.cpp11
-rw-r--r--engines/gob/save/saveload.h4
-rw-r--r--engines/gob/save/saveload_inca2.cpp4
-rw-r--r--engines/gob/save/saveload_v3.cpp6
-rw-r--r--engines/gob/sound/bgatmosphere.cpp4
-rw-r--r--engines/gob/sound/bgatmosphere.h4
-rw-r--r--engines/gob/sound/sound.cpp10
-rw-r--r--engines/groovie/music.h18
-rw-r--r--engines/hugo/display.cpp69
-rw-r--r--engines/hugo/display.h19
-rw-r--r--engines/hugo/file.cpp2
-rw-r--r--engines/hugo/hugo.cpp21
-rw-r--r--engines/hugo/hugo.h9
-rw-r--r--engines/hugo/intro.cpp10
-rw-r--r--engines/hugo/object.cpp35
-rw-r--r--engines/hugo/schedule.cpp368
-rw-r--r--engines/hugo/schedule.h2
-rw-r--r--engines/mohawk/myst.cpp14
-rw-r--r--engines/mohawk/myst.h2
-rw-r--r--engines/mohawk/myst_areas.h1
-rw-r--r--engines/mohawk/myst_stacks/mechanical.cpp279
-rw-r--r--engines/mohawk/myst_stacks/mechanical.h31
-rw-r--r--engines/mohawk/myst_stacks/myst.cpp1
-rw-r--r--engines/sci/console.cpp48
-rw-r--r--engines/sci/detection_tables.h36
-rw-r--r--engines/sci/engine/features.cpp20
-rw-r--r--engines/sci/engine/gc.cpp16
-rw-r--r--engines/sci/engine/kernel_tables.h2
-rw-r--r--engines/sci/engine/kfile.cpp8
-rw-r--r--engines/sci/engine/kgraphics.cpp188
-rw-r--r--engines/sci/engine/kmenu.cpp3
-rw-r--r--engines/sci/engine/kmisc.cpp14
-rw-r--r--engines/sci/engine/kmovement.cpp27
-rw-r--r--engines/sci/engine/kparse.cpp2
-rw-r--r--engines/sci/engine/kpathing.cpp12
-rw-r--r--engines/sci/engine/kscripts.cpp8
-rw-r--r--engines/sci/engine/kstring.cpp14
-rw-r--r--engines/sci/engine/script.cpp16
-rw-r--r--engines/sci/engine/script_patches.cpp282
-rw-r--r--engines/sci/engine/seg_manager.cpp12
-rw-r--r--engines/sci/engine/segment.cpp5
-rw-r--r--engines/sci/engine/segment.h13
-rw-r--r--engines/sci/engine/static_selectors.cpp4
-rw-r--r--engines/sci/engine/vm.cpp70
-rw-r--r--engines/sci/engine/vm_types.cpp136
-rw-r--r--engines/sci/engine/vm_types.h64
-rw-r--r--engines/sci/engine/workarounds.cpp29
-rw-r--r--engines/sci/graphics/animate.cpp345
-rw-r--r--engines/sci/graphics/animate.h4
-rw-r--r--engines/sci/graphics/compare.cpp13
-rw-r--r--engines/sci/graphics/cursor.cpp12
-rw-r--r--engines/sci/graphics/frameout.cpp8
-rw-r--r--engines/sci/graphics/helpers.h18
-rw-r--r--engines/sci/graphics/maciconbar.cpp6
-rw-r--r--engines/sci/graphics/paint.cpp3
-rw-r--r--engines/sci/graphics/paint.h1
-rw-r--r--engines/sci/graphics/paint16.cpp22
-rw-r--r--engines/sci/graphics/paint32.cpp11
-rw-r--r--engines/sci/graphics/paint32.h1
-rw-r--r--engines/sci/graphics/palette.cpp71
-rw-r--r--engines/sci/graphics/palette.h2
-rw-r--r--engines/sci/graphics/picture.cpp175
-rw-r--r--engines/sci/graphics/ports.cpp26
-rw-r--r--engines/sci/graphics/ports.h18
-rw-r--r--engines/sci/graphics/screen.cpp26
-rw-r--r--engines/sci/graphics/screen.h2
-rw-r--r--engines/sci/graphics/text16.cpp2
-rw-r--r--engines/sci/graphics/transitions.cpp7
-rw-r--r--engines/sci/graphics/transitions.h3
-rw-r--r--engines/sci/graphics/view.cpp235
-rw-r--r--engines/sci/parser/vocabulary.cpp2
-rw-r--r--engines/sci/resource.cpp46
-rw-r--r--engines/sci/resource.h2
-rw-r--r--engines/sci/sci.cpp139
-rw-r--r--engines/sci/sci.h30
-rw-r--r--engines/sci/sound/drivers/adlib.cpp14
-rw-r--r--engines/sci/sound/drivers/amigamac.cpp72
-rw-r--r--engines/sci/sound/drivers/midi.cpp86
-rw-r--r--engines/sci/sound/music.cpp41
-rw-r--r--engines/sci/sound/soundcmd.cpp12
-rw-r--r--engines/sci/util.cpp26
-rw-r--r--engines/sci/util.h5
-rw-r--r--engines/sci/video/robot_decoder.cpp5
-rw-r--r--engines/sword2/animation.cpp40
-rw-r--r--engines/sword2/animation.h6
-rw-r--r--engines/testbed/graphics.cpp8
-rw-r--r--engines/tinsel/music.cpp28
-rw-r--r--engines/toon/movie.cpp6
-rw-r--r--engines/toon/movie.h2
-rw-r--r--gui/ThemeEngine.cpp5
-rw-r--r--gui/ThemeEngine.h4
-rw-r--r--gui/ThemeParser.cpp48
-rw-r--r--gui/credits.h2
-rw-r--r--gui/gui-manager.cpp7
-rw-r--r--gui/themes/default.inc10
-rw-r--r--gui/themes/scummclassic.zipbin91252 -> 91156 bytes
-rw-r--r--gui/themes/scummclassic/THEMERC2
-rw-r--r--gui/themes/scummclassic/classic_gfx.stx6
-rw-r--r--gui/themes/scummclassic/classic_layout.stx2
-rw-r--r--gui/themes/scummclassic/classic_layout_lowres.stx2
-rw-r--r--gui/themes/scummmodern.zipbin198584 -> 198479 bytes
-rw-r--r--gui/themes/scummmodern/THEMERC2
-rw-r--r--gui/themes/scummmodern/scummmodern_gfx.stx8
-rw-r--r--gui/themes/scummmodern/scummmodern_layout.stx2
-rw-r--r--gui/themes/scummmodern/scummmodern_layout_lowres.stx2
-rwxr-xr-xtools/credits.pl2
180 files changed, 5096 insertions, 3667 deletions
diff --git a/.gitignore b/.gitignore
index 6e3e61134c..29a14c5459 100644
--- a/.gitignore
+++ b/.gitignore
@@ -66,17 +66,12 @@ lib*.a
/dists/iphone/scummvm.xcodeproj/*.mode1v3
/dists/iphone/scummvm.xcodeproj/*.pbxuser
-/dists/msvc8/[Dd]ebug*/
-/dists/msvc8/[Rr]elease*/
-/dists/msvc8/*.lib
-
-/dists/msvc9/[Dd]ebug*/
-/dists/msvc9/[Rr]elease*/
-/dists/msvc9/*.lib
-
-/dists/msvc10/[Dd]ebug*/
-/dists/msvc10/[Rr]elease*/
-/dists/msvc10/*.lib
+/dists/msvc*/[Dd]ebug*/
+/dists/msvc*/[Rr]elease*/
+/dists/msvc*/*.lib
+/dists/msvc*/*.SAV
+/dists/msvc*/*.dat
+/dists/msvc*/*.dll
/doc/*.aux
/doc/*.dvi
diff --git a/AUTHORS b/AUTHORS
index 758d1934cf..9504f6d6cf 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -405,7 +405,7 @@ Other contributions
engine)
Paolo Costabel - PSP port contributions
Martin Doucha - CinE engine objectification
- Thomas Fach-Pedersen - ProTracker module player
+ Thomas Fach-Pedersen - ProTracker module player, Smacker video decoder
Tobias Gunkel - Sound support for C64 version of MM/Zak, Loom
PCE support
Janne Huttunen - V3 actor mask support, Dig/FT SMUSH audio
diff --git a/NEWS b/NEWS
index 09a99ff4c6..5b50b5e368 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,6 @@
-For a more comprehensive changelog for the latest experimental SVN code, see:
- http://scummvm.svn.sourceforge.net/viewvc/scummvm/?view=log
+For a more comprehensive changelog of the latest experimental code, see:
+ https://github.com/scummvm/scummvm/commits/
+
1.3.0 (????-??-??)
New Games:
- Added support for Backyard Baseball.
@@ -24,14 +25,14 @@ For a more comprehensive changelog for the latest experimental SVN code, see:
Cine:
- Corrected memory leaks and invalid memory accesses.
Future Wars should be more stable.
- - Operation Stealth is now completable, though significant
- graphical glitches remain so not official supported.
+ - Made Operation Stealth completable, though significant graphical
+ glitches remain so not official supported.
Drascula:
- Added German and French subtitles in the Von Braun cutscene (#3069981:
no subtitles in scene with "von Braun").
- Improved French translation of the game.
- - Return To Launcher now supported.
+ - Added support for "Return To Launcher".
Gob:
- Fixed "Goblin Stuck On Reload" bugs affecting Gobliiins.
@@ -41,7 +42,7 @@ For a more comprehensive changelog for the latest experimental SVN code, see:
Parallaction:
- Corrected issue which could cause crash at engine exit.
- - Leak fixes in Nippon Safes Amiga.
+ - Closed memory leaks in Nippon Safes Amiga.
SCI:
- Added a CMS music driver for SCI1 - SCI1.1 games.
@@ -78,14 +79,15 @@ For a more comprehensive changelog for the latest experimental SVN code, see:
- Improved support for FM-TOWNS versions of games.
Sky:
- - Fixed crashes on sequences for several backends
- (e.g. OpenGL, including Android)
+ - Fixed crashes on sequences for several ports (Android, OpenGL, ...)
Teenagent:
- Closed memory leaks.
Tinsel:
- Closed memory leaks in Coroutines.
+ - Added enhanced music support for the German CD "Neon Edition" re-release
+ of Discworld 1.
Touche:
- Corrected memory leaks and minor issues.
@@ -94,6 +96,14 @@ For a more comprehensive changelog for the latest experimental SVN code, see:
- Added support for OpenGL. (GSoC Task)
- Closed memory leaks in Mouse Surfaces.
+ Android port:
+ - Switched to the official NDK toolchain for building.
+ - Fixed GFX output for various devices.
+ - Fixed various crashes.
+
+ Nintendo DS port:
+ - Added support for loadable modules.
+
PSP port:
- Added support for loadable modules.
- Added image viewer.
@@ -101,22 +111,14 @@ For a more comprehensive changelog for the latest experimental SVN code, see:
PS2 port:
- Added support for loadable modules.
- Nintendo DS port:
- - Added support for loadable modules.
-
Wii/GameCube port:
- Added support for loadable modules.
- Fixed 16bit mouse cursors on HE games.
- Android port:
- - Switched to the official NDK toolchain for building.
- - Fixed GFX output for various devices.
- - Fixed various crashes.
-
1.2.1 (2010-12-19)
General
- - Add Hungarian translation.
- - Add Brazilian Portuguese translation.
+ - Added Hungarian translation.
+ - Added Brazilian Portuguese translation.
Cruise:
- Fixed a problem with Raoul appearing when examining the Book.
diff --git a/audio/mididrv.h b/audio/mididrv.h
index 9e649cba3d..eed8c419f3 100644
--- a/audio/mididrv.h
+++ b/audio/mididrv.h
@@ -71,9 +71,14 @@ enum MusicType {
* A set of flags to be passed to detectDevice() which can be used to
* specify what kind of music driver is preferred / accepted.
*
- * The flags (except for MDT_PREFER_MT32 and MDT_PREFER_GM) indicate whether a given driver
- * type is acceptable. E.g. the TOWNS music driver could be returned by
- * detectDevice if and only if MDT_TOWNS is specified.
+ * The flags (except for MDT_PREFER_MT32 and MDT_PREFER_GM) indicate whether a
+ * given driver type is acceptable. E.g. the TOWNS music driver could be
+ * returned by detectDevice if and only if MDT_TOWNS is specified.
+ *
+ * MDT_PREFER_MT32 and MDT_PREFER_GM indicate the MIDI device type to use when
+ * no device is selected in the music options, or when the MIDI device selected
+ * does not match the requirements of a game engine. With these flags, more
+ * priority is given to an MT-32 device, or a GM device respectively.
*
* @todo Rename MidiDriverFlags to MusicDriverFlags
*/
diff --git a/audio/mixer.cpp b/audio/mixer.cpp
index c2271b1059..dc0287e3fb 100644
--- a/audio/mixer.cpp
+++ b/audio/mixer.cpp
@@ -54,8 +54,9 @@ public:
* @param len number of sample *pairs*. So a value of
* 10 means that the buffer contains twice 10 sample, each
* 16 bits, for a total of 40 bytes.
+ * @return number of sample pairs processed (which can still be silence!)
*/
- void mix(int16 *data, uint len);
+ int mix(int16 *data, uint len);
/**
* Queries whether the channel is still playing or not.
@@ -257,7 +258,7 @@ void MixerImpl::playStream(
insertChannel(handle, chan);
}
-void MixerImpl::mixCallback(byte *samples, uint len) {
+int MixerImpl::mixCallback(byte *samples, uint len) {
assert(samples);
Common::StackLock lock(_mutex);
@@ -272,14 +273,21 @@ void MixerImpl::mixCallback(byte *samples, uint len) {
memset(buf, 0, 2 * len * sizeof(int16));
// mix all channels
+ int res = 0, tmp;
for (int i = 0; i != NUM_CHANNELS; i++)
if (_channels[i]) {
if (_channels[i]->isFinished()) {
delete _channels[i];
_channels[i] = 0;
- } else if (!_channels[i]->isPaused())
- _channels[i]->mix(buf, len);
+ } else if (!_channels[i]->isPaused()) {
+ tmp = _channels[i]->mix(buf, len);
+
+ if (tmp > res)
+ res = tmp;
+ }
}
+
+ return res;
}
void MixerImpl::stopAll() {
@@ -538,19 +546,23 @@ Timestamp Channel::getElapsedTime() {
return ts;
}
-void Channel::mix(int16 *data, uint len) {
+int Channel::mix(int16 *data, uint len) {
assert(_stream);
+ int res = 0;
+
if (_stream->endOfData()) {
// TODO: call drain method
} else {
assert(_converter);
-
_samplesConsumed = _samplesDecoded;
_mixerTimeStamp = g_system->getMillis();
_pauseTime = 0;
- _samplesDecoded += _converter->flow(*_stream, data, len, _volL, _volR);
+ res = _converter->flow(*_stream, data, len, _volL, _volR);
+ _samplesDecoded += res;
}
+
+ return res;
}
} // End of namespace Audio
diff --git a/audio/mixer_intern.h b/audio/mixer_intern.h
index c8df9a594d..dd2746e9ea 100644
--- a/audio/mixer_intern.h
+++ b/audio/mixer_intern.h
@@ -118,8 +118,10 @@ public:
* The mixer callback function, to be called at regular intervals by
* the backend (e.g. from an audio mixing thread). All the actual mixing
* work is done from here.
+ *
+ * @return number of sample pairs processed (which can still be silence!)
*/
- void mixCallback(byte *samples, uint len);
+ int mixCallback(byte *samples, uint len);
/**
* Set the internal 'is ready' flag of the mixer.
diff --git a/backends/base-backend.cpp b/backends/base-backend.cpp
index 42ab7b887a..f349cc8005 100644
--- a/backends/base-backend.cpp
+++ b/backends/base-backend.cpp
@@ -101,7 +101,4 @@ AudioCDManager *BaseBackend::getAudioCDManager() {
}
void BaseBackend::resetGraphicsScale() {
- // As a hack, we use 0 here. Backends should override this method
- // and provide their own.
- setGraphicsMode(0);
}
diff --git a/backends/graphics/opengl/gltexture.cpp b/backends/graphics/opengl/gltexture.cpp
index cd9e23cb71..7b7d40f174 100644
--- a/backends/graphics/opengl/gltexture.cpp
+++ b/backends/graphics/opengl/gltexture.cpp
@@ -149,7 +149,7 @@ void GLTexture::updateBuffer(const void *buf, int pitch, GLuint x, GLuint y, GLu
glBindTexture(GL_TEXTURE_2D, _textureName); CHECK_GL_ERROR();
// Check if the buffer has its data contiguously
- if (static_cast<int>(w) * _bytesPerPixel == pitch && w == _textureWidth) {
+ if (static_cast<int>(w) * _bytesPerPixel == pitch) {
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h,
_glFormat, _glType, buf); CHECK_GL_ERROR();
} else {
diff --git a/backends/graphics/opengl/opengl-graphics.cpp b/backends/graphics/opengl/opengl-graphics.cpp
index beac2f6d3e..9a2efe3eec 100644
--- a/backends/graphics/opengl/opengl-graphics.cpp
+++ b/backends/graphics/opengl/opengl-graphics.cpp
@@ -136,6 +136,8 @@ int OpenGLGraphicsManager::getDefaultGraphicsMode() const {
bool OpenGLGraphicsManager::setGraphicsMode(int mode) {
assert(_transactionMode == kTransactionActive);
+ setScale(2);
+
if (_oldVideoMode.setup && _oldVideoMode.mode == mode)
return true;
@@ -166,11 +168,9 @@ void OpenGLGraphicsManager::resetGraphicsScale() {
}
#ifdef USE_RGB_COLOR
-
Graphics::PixelFormat OpenGLGraphicsManager::getScreenFormat() const {
return _screenFormat;
}
-
#endif
void OpenGLGraphicsManager::initSize(uint width, uint height, const Graphics::PixelFormat *format) {
@@ -308,7 +308,7 @@ int16 OpenGLGraphicsManager::getWidth() {
void OpenGLGraphicsManager::setPalette(const byte *colors, uint start, uint num) {
assert(colors);
-
+
#ifdef USE_RGB_COLOR
assert(_screenFormat.bytesPerPixel == 1);
#endif
@@ -324,7 +324,7 @@ void OpenGLGraphicsManager::setPalette(const byte *colors, uint start, uint num)
void OpenGLGraphicsManager::grabPalette(byte *colors, uint start, uint num) {
assert(colors);
-
+
#ifdef USE_RGB_COLOR
assert(_screenFormat.bytesPerPixel == 1);
#endif
@@ -341,9 +341,9 @@ void OpenGLGraphicsManager::copyRectToScreen(const byte *buf, int pitch, int x,
// Copy buffer data to game screen internal buffer
const byte *src = buf;
- byte *dst = (byte *)_screenData.pixels + y * _screenData.pitch;
+ byte *dst = (byte *)_screenData.pixels + y * _screenData.pitch + x * _screenData.bytesPerPixel;
for (int i = 0; i < h; i++) {
- memcpy(dst + x * _screenData.bytesPerPixel, src, w * _screenData.bytesPerPixel);
+ memcpy(dst, src, w * _screenData.bytesPerPixel);
src += pitch;
dst += _screenData.pitch;
}
@@ -367,6 +367,7 @@ void OpenGLGraphicsManager::fillScreen(uint32 col) {
if (_gameTexture == NULL)
return;
+#ifdef USE_RGB_COLOR
if (_screenFormat.bytesPerPixel == 1) {
memset(_screenData.pixels, col, _screenData.h * _screenData.pitch);
} else if (_screenFormat.bytesPerPixel == 2) {
@@ -392,7 +393,9 @@ void OpenGLGraphicsManager::fillScreen(uint32 col) {
pixels[i] = col;
}
}
-
+#else
+ memset(_screenData.pixels, col, _screenData.h * _screenData.pitch);
+#endif
_screenNeedsRedraw = true;
}
@@ -558,7 +561,8 @@ void OpenGLGraphicsManager::setMouseCursor(const byte *buf, uint w, uint h, int
#endif
// Allocate space for cursor data
- if (_cursorData.w != w || _cursorData.h != h)
+ if (_cursorData.w != w || _cursorData.h != h ||
+ _cursorData.bytesPerPixel != _cursorFormat.bytesPerPixel)
_cursorData.create(w, h, _cursorFormat.bytesPerPixel);
// Save cursor data
@@ -754,11 +758,17 @@ void OpenGLGraphicsManager::refreshOverlay() {
void OpenGLGraphicsManager::refreshCursor() {
_cursorNeedsRedraw = false;
- if (_cursorFormat.bytesPerPixel == 1) {
- // Create a temporary RGBA8888 surface
- byte *surface = new byte[_cursorState.w * _cursorState.h * 4];
- memset(surface, 0, _cursorState.w * _cursorState.h * 4);
+ // Allocate a texture big enough for cursor
+ _cursorTexture->allocBuffer(_cursorState.w, _cursorState.h);
+ // Create a temporary RGBA8888 surface
+ byte *surface = new byte[_cursorState.w * _cursorState.h * 4];
+ memset(surface, 0, _cursorState.w * _cursorState.h * 4);
+
+ byte *dst = surface;
+
+ // Convert the paletted cursor to RGBA8888
+ if (_cursorFormat.bytesPerPixel == 1) {
// Select palette
byte *palette;
if (_cursorPaletteDisabled)
@@ -768,7 +778,6 @@ void OpenGLGraphicsManager::refreshCursor() {
// Convert the paletted cursor to RGBA8888
const byte *src = (byte *)_cursorData.pixels;
- byte *dst = surface;
for (int i = 0; i < _cursorState.w * _cursorState.h; i++) {
// Check for keycolor
if (src[i] != _cursorKeyColor) {
@@ -779,16 +788,42 @@ void OpenGLGraphicsManager::refreshCursor() {
}
dst += 4;
}
+ } else {
+ const bool gotNoAlpha = (_cursorFormat.aLoss == 8);
+
+ // Convert the RGB cursor to RGBA8888
+ if (_cursorFormat.bytesPerPixel == 2) {
+ const uint16 *src = (uint16 *)_cursorData.pixels;
+ for (int i = 0; i < _cursorState.w * _cursorState.h; i++) {
+ // Check for keycolor
+ if (src[i] != _cursorKeyColor) {
+ _cursorFormat.colorToARGB(src[i], dst[3], dst[0], dst[1], dst[2]);
+
+ if (gotNoAlpha)
+ dst[3] = 255;
+ }
+ dst += 4;
+ }
+ } else if (_cursorFormat.bytesPerPixel == 4) {
+ const uint32 *src = (uint32 *)_cursorData.pixels;
+ for (int i = 0; i < _cursorState.w * _cursorState.h; i++) {
+ // Check for keycolor
+ if (src[i] != _cursorKeyColor) {
+ _cursorFormat.colorToARGB(src[i], dst[3], dst[0], dst[1], dst[2]);
+
+ if (gotNoAlpha)
+ dst[3] = 255;
+ }
+ dst += 4;
+ }
+ }
+ }
- // Allocate a texture big enough for cursor
- _cursorTexture->allocBuffer(_cursorState.w, _cursorState.h);
-
- // Update the texture with new cursor
- _cursorTexture->updateBuffer(surface, _cursorState.w * 4, 0, 0, _cursorState.w, _cursorState.h);
+ // Update the texture with new cursor
+ _cursorTexture->updateBuffer(surface, _cursorState.w * 4, 0, 0, _cursorState.w, _cursorState.h);
- // Free the temp surface
- delete[] surface;
- }
+ // Free the temp surface
+ delete[] surface;
}
void OpenGLGraphicsManager::refreshCursorScale() {
@@ -811,7 +846,8 @@ void OpenGLGraphicsManager::refreshCursorScale() {
} else {
// Otherwise, scale the cursor for the overlay
int targetScaleFactor = MIN(_cursorTargetScale, _videoMode.scaleFactor);
- int actualFactor = screenScaleFactor - (targetScaleFactor - 1) * 10000;
+ // We limit the maximum scale to 3 here to avoid too big cursors, for large overlay resolutions
+ int actualFactor = MIN<uint>(3, screenScaleFactor - (targetScaleFactor - 1)) * 10000;
_cursorState.rW = (int16)(_cursorState.w * actualFactor / 10000);
_cursorState.rH = (int16)(_cursorState.h * actualFactor / 10000);
_cursorState.rHotX = (int16)(_cursorState.hotX * actualFactor / 10000);
@@ -873,6 +909,11 @@ void OpenGLGraphicsManager::getGLPixelFormat(Graphics::PixelFormat pixelFormat,
intFormat = GL_RGBA;
glFormat = GL_RGBA;
gltype = GL_UNSIGNED_SHORT_5_5_5_1;
+ } else if (pixelFormat == Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)) { // RGB555
+ bpp = 2;
+ intFormat = GL_RGB;
+ glFormat = GL_BGRA;
+ gltype = GL_UNSIGNED_SHORT_1_5_5_5_REV;
} else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0)) { // RGBA4444
bpp = 2;
intFormat = GL_RGBA;
@@ -963,7 +1004,7 @@ void OpenGLGraphicsManager::internUpdateScreen() {
refreshOverlay();
// Draw the overlay
- _overlayTexture->drawTexture(_displayX, _displayY, _displayWidth, _displayHeight);
+ _overlayTexture->drawTexture(0, 0, _videoMode.overlayWidth, _videoMode.overlayHeight);
}
if (_cursorVisible) {
@@ -1059,10 +1100,14 @@ void OpenGLGraphicsManager::initGL() {
void OpenGLGraphicsManager::loadTextures() {
#ifdef USE_RGB_COLOR
- if (_transactionDetails.formatChanged && _gameTexture)
+ if (_transactionDetails.formatChanged && _gameTexture) {
delete _gameTexture;
+ _gameTexture = 0;
+ }
#endif
+ uint gameScreenBPP = 0;
+
if (!_gameTexture) {
byte bpp;
GLenum intformat;
@@ -1073,6 +1118,7 @@ void OpenGLGraphicsManager::loadTextures() {
#else
getGLPixelFormat(Graphics::PixelFormat::createFormatCLUT8(), bpp, intformat, format, type);
#endif
+ gameScreenBPP = bpp;
_gameTexture = new GLTexture(bpp, intformat, format, type);
} else
_gameTexture->refresh();
@@ -1093,7 +1139,7 @@ void OpenGLGraphicsManager::loadTextures() {
_cursorTexture = new GLTexture(4, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
else
_cursorTexture->refresh();
-
+
GLint filter = _videoMode.antialiasing ? GL_LINEAR : GL_NEAREST;
_gameTexture->setFilter(filter);
_overlayTexture->setFilter(filter);
@@ -1104,21 +1150,38 @@ void OpenGLGraphicsManager::loadTextures() {
_overlayTexture->allocBuffer(_videoMode.overlayWidth, _videoMode.overlayHeight);
_cursorTexture->allocBuffer(_cursorState.w, _cursorState.h);
- if (_transactionDetails.formatChanged ||
+ if (
+#ifdef USE_RGB_COLOR
+ _transactionDetails.formatChanged ||
+#endif
_oldVideoMode.screenWidth != _videoMode.screenWidth ||
_oldVideoMode.screenHeight != _videoMode.screenHeight)
_screenData.create(_videoMode.screenWidth, _videoMode.screenHeight,
- _screenFormat.bytesPerPixel);
+#ifdef USE_RGB_COLOR
+ _screenFormat.bytesPerPixel
+#else
+ 1
+#endif
+ );
+
if (_oldVideoMode.overlayWidth != _videoMode.overlayWidth ||
_oldVideoMode.overlayHeight != _videoMode.overlayHeight)
_overlayData.create(_videoMode.overlayWidth, _videoMode.overlayHeight,
_overlayFormat.bytesPerPixel);
-
+
_screenNeedsRedraw = true;
_overlayNeedsRedraw = true;
_cursorNeedsRedraw = true;
+ // We need to setup a proper unpack alignment value here, else we will
+ // get problems with the texture updates, in case the surface data is
+ // not properly aligned.
+ // For now we use the gcd of the game screen format and 2, since 2 is
+ // the BPP value for the overlay and the OSD.
+ if (gameScreenBPP)
+ glPixelStorei(GL_UNPACK_ALIGNMENT, Common::gcd<uint>(gameScreenBPP, 2));
+
#ifdef USE_OSD
if (!_osdTexture)
_osdTexture = new GLTexture(2, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1);
@@ -1166,32 +1229,20 @@ uint OpenGLGraphicsManager::getAspectRatio() {
}
void OpenGLGraphicsManager::adjustMousePosition(int16 &x, int16 &y) {
- if (_videoMode.mode == OpenGL::GFX_NORMAL) {
- if (_videoMode.hardwareWidth != _videoMode.overlayWidth)
- x = x * _videoMode.overlayWidth / _videoMode.hardwareWidth;
- if (_videoMode.hardwareHeight != _videoMode.overlayHeight)
- y = y * _videoMode.overlayHeight / _videoMode.hardwareHeight;
-
- if (!_overlayVisible) {
- x /= _videoMode.scaleFactor;
- y /= _videoMode.scaleFactor;
- }
+ if (_overlayVisible)
+ return;
- } else {
+ if (_videoMode.mode == OpenGL::GFX_NORMAL) {
+ x /= _videoMode.scaleFactor;
+ y /= _videoMode.scaleFactor;
+ } else if (!_overlayVisible) {
x -= _displayX;
y -= _displayY;
- if (_overlayVisible) {
- if (_displayWidth != _videoMode.overlayWidth)
- x = x * _videoMode.overlayWidth / _displayWidth;
- if (_displayHeight != _videoMode.overlayHeight)
- y = y * _videoMode.overlayHeight / _displayHeight;
- } else {
- if (_displayWidth != _videoMode.screenWidth)
- x = x * _videoMode.screenWidth / _displayWidth;
- if (_displayHeight != _videoMode.screenHeight)
- y = y * _videoMode.screenHeight / _displayHeight;
- }
+ if (_displayWidth != _videoMode.screenWidth)
+ x = x * _videoMode.screenWidth / _displayWidth;
+ if (_displayHeight != _videoMode.screenHeight)
+ y = y * _videoMode.screenHeight / _displayHeight;
}
}
diff --git a/backends/graphics/openglsdl/openglsdl-graphics.cpp b/backends/graphics/openglsdl/openglsdl-graphics.cpp
index c7ce0aa7de..cbc152a4a3 100644
--- a/backends/graphics/openglsdl/openglsdl-graphics.cpp
+++ b/backends/graphics/openglsdl/openglsdl-graphics.cpp
@@ -121,6 +121,7 @@ void OpenGLSdlGraphicsManager::detectSupportedFormats() {
#endif
Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0), // RGB565
Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0), // RGB5551
+ Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0), // RGB555
Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0), // RGBA4444
#ifndef USE_GLES
Graphics::PixelFormat(2, 4, 4, 4, 4, 8, 4, 0, 12) // ARGB4444
@@ -344,13 +345,10 @@ bool OpenGLSdlGraphicsManager::loadGFXMode() {
if (_aspectRatioCorrection)
_videoMode.mode = OpenGL::GFX_4_3;
- _videoMode.overlayWidth = _videoMode.screenWidth * _videoMode.scaleFactor;
- _videoMode.overlayHeight = _videoMode.screenHeight * _videoMode.scaleFactor;
-
// If the screen was resized, do not change its size
if (!_screenResized) {
- _videoMode.hardwareWidth = _videoMode.overlayWidth;
- _videoMode.hardwareHeight = _videoMode.overlayHeight;
+ _videoMode.overlayWidth = _videoMode.hardwareWidth = _videoMode.screenWidth * _videoMode.scaleFactor;
+ _videoMode.overlayHeight = _videoMode.hardwareHeight = _videoMode.screenHeight * _videoMode.scaleFactor;
int screenAspectRatio = _videoMode.screenWidth * 10000 / _videoMode.screenHeight;
int desiredAspectRatio = getAspectRatio();
@@ -365,6 +363,9 @@ bool OpenGLSdlGraphicsManager::loadGFXMode() {
// the width is modified it can break the overlay.
if (_videoMode.hardwareHeight > _videoMode.overlayHeight)
_videoMode.overlayHeight = _videoMode.hardwareHeight;
+ } else {
+ _videoMode.overlayWidth = _videoMode.hardwareWidth;
+ _videoMode.overlayHeight = _videoMode.hardwareHeight;
}
_screenResized = false;
@@ -376,11 +377,15 @@ bool OpenGLSdlGraphicsManager::loadGFXMode() {
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
- if (_videoMode.fullscreen)
+ if (_videoMode.fullscreen) {
if (!setupFullscreenMode())
// Failed setuping a fullscreen mode
return false;
+ _videoMode.overlayWidth = _videoMode.hardwareWidth;
+ _videoMode.overlayHeight = _videoMode.hardwareHeight;
+ }
+
uint32 flags = SDL_OPENGL;
if (_videoMode.fullscreen)
diff --git a/backends/graphics/sdl/sdl-graphics.cpp b/backends/graphics/sdl/sdl-graphics.cpp
index 15d896c57a..d8b686e61f 100644
--- a/backends/graphics/sdl/sdl-graphics.cpp
+++ b/backends/graphics/sdl/sdl-graphics.cpp
@@ -1166,25 +1166,25 @@ void SdlGraphicsManager::copyRectToScreen(const byte *src, int pitch, int x, int
error("SDL_LockSurface failed: %s", SDL_GetError());
#ifdef USE_RGB_COLOR
- byte *dst = (byte *)_screen->pixels + y * _videoMode.screenWidth * _screenFormat.bytesPerPixel + x * _screenFormat.bytesPerPixel;
- if (_videoMode.screenWidth == w && pitch == w * _screenFormat.bytesPerPixel) {
- memcpy(dst, src, h*w*_screenFormat.bytesPerPixel);
+ byte *dst = (byte *)_screen->pixels + y * _screen->pitch + x * _screenFormat.bytesPerPixel;
+ if (_videoMode.screenWidth == w && pitch == _screen->pitch) {
+ memcpy(dst, src, h*pitch);
} else {
do {
memcpy(dst, src, w * _screenFormat.bytesPerPixel);
src += pitch;
- dst += _videoMode.screenWidth * _screenFormat.bytesPerPixel;
+ dst += _screen->pitch;
} while (--h);
}
#else
- byte *dst = (byte *)_screen->pixels + y * _videoMode.screenWidth + x;
- if (_videoMode.screenWidth == pitch && pitch == w) {
+ byte *dst = (byte *)_screen->pixels + y * _screen->pitch + x;
+ if (_screen->pitch == pitch && pitch == w) {
memcpy(dst, src, h*w);
} else {
do {
memcpy(dst, src, w);
src += pitch;
- dst += _videoMode.screenWidth;
+ dst += _screen->pitch;
} while (--h);
}
#endif
diff --git a/backends/platform/android/android.cpp b/backends/platform/android/android.cpp
index c49745f8bd..0cfe7c9a22 100644
--- a/backends/platform/android/android.cpp
+++ b/backends/platform/android/android.cpp
@@ -25,22 +25,11 @@
#if defined(__ANDROID__)
-#include "backends/base-backend.h"
-#include "base/main.h"
-#include "graphics/surface.h"
-
-#include "backends/platform/android/android.h"
-#include "backends/platform/android/video.h"
-
-#include <jni.h>
-
-#include <string.h>
-#include <unistd.h>
-#include <pthread.h>
#include <sys/time.h>
+#include <sys/resource.h>
#include <time.h>
+#include <unistd.h>
-#include "common/archive.h"
#include "common/util.h"
#include "common/rect.h"
#include "common/queue.h"
@@ -48,14 +37,12 @@
#include "common/events.h"
#include "common/config-manager.h"
-#include "backends/fs/posix/posix-fs-factory.h"
#include "backends/keymapper/keymapper.h"
#include "backends/saves/default/default-saves.h"
#include "backends/timer/default/default-timer.h"
-#include "backends/plugins/posix/posix-provider.h"
-#include "audio/mixer_intern.h"
-#include "backends/platform/android/asset-archive.h"
+#include "backends/platform/android/jni.h"
+#include "backends/platform/android/android.h"
const char *android_log_tag = "ScummVM";
@@ -68,7 +55,8 @@ extern "C" {
expr, file, line);
}
- void __assert2(const char *file, int line, const char *func, const char *expr) {
+ void __assert2(const char *file, int line, const char *func,
+ const char *expr) {
__android_log_assert(expr, android_log_tag,
"Assertion failure: '%s' in %s:%d (%s)",
expr, file, line, func);
@@ -106,234 +94,24 @@ void checkGlError(const char *expr, const char *file, int line) {
}
#endif
-static JavaVM *cached_jvm;
-static jfieldID FID_Event_type;
-static jfieldID FID_Event_synthetic;
-static jfieldID FID_Event_kbd_keycode;
-static jfieldID FID_Event_kbd_ascii;
-static jfieldID FID_Event_kbd_flags;
-static jfieldID FID_Event_mouse_x;
-static jfieldID FID_Event_mouse_y;
-static jfieldID FID_Event_mouse_relative;
-static jfieldID FID_ScummVM_nativeScummVM;
-static jmethodID MID_Object_wait;
-
-JNIEnv *JNU_GetEnv() {
- JNIEnv *env = 0;
-
- jint res = cached_jvm->GetEnv((void **)&env, JNI_VERSION_1_2);
-
- if (res != JNI_OK) {
- LOGE("GetEnv() failed: %d", res);
- abort();
- }
-
- return env;
-}
-
-static void JNU_ThrowByName(JNIEnv *env, const char *name, const char *msg) {
- jclass cls = env->FindClass(name);
-
- // if cls is 0, an exception has already been thrown
- if (cls != 0)
- env->ThrowNew(cls, msg);
-
- env->DeleteLocalRef(cls);
-}
-
// floating point. use sparingly
template <class T>
static inline T scalef(T in, float numerator, float denominator) {
return static_cast<float>(in) * numerator / denominator;
}
-static inline GLfixed xdiv(int numerator, int denominator) {
- assert(numerator < (1 << 16));
- return (numerator << 16) / denominator;
-}
-
-#ifdef DYNAMIC_MODULES
-class AndroidPluginProvider : public POSIXPluginProvider {
-protected:
- virtual void addCustomDirectories(Common::FSList &dirs) const;
-};
-#endif
-
-class OSystem_Android : public BaseBackend, public PaletteManager {
-private:
- // back pointer to (java) peer instance
- jobject _back_ptr;
-
- jmethodID MID_displayMessageOnOSD;
- jmethodID MID_setWindowCaption;
- jmethodID MID_initBackend;
- jmethodID MID_audioSampleRate;
- jmethodID MID_showVirtualKeyboard;
- jmethodID MID_getSysArchives;
- jmethodID MID_getPluginDirectories;
- jmethodID MID_setupScummVMSurface;
- jmethodID MID_destroyScummVMSurface;
- jmethodID MID_swapBuffers;
-
- int _screen_changeid;
- int _egl_surface_width;
- int _egl_surface_height;
-
- bool _force_redraw;
-
- // Game layer
- GLESPaletteTexture *_game_texture;
- int _shake_offset;
- Common::Rect _focus_rect;
-
- // Overlay layer
- GLES4444Texture *_overlay_texture;
- bool _show_overlay;
-
- // Mouse layer
- GLESPaletteATexture *_mouse_texture;
- Common::Point _mouse_hotspot;
- int _mouse_targetscale;
- bool _show_mouse;
- bool _use_mouse_palette;
-
- Common::Queue<Common::Event> _event_queue;
- MutexRef _event_queue_lock;
-
- bool _timer_thread_exit;
- pthread_t _timer_thread;
- static void *timerThreadFunc(void *arg);
-
- bool _enable_zoning;
- bool _virtkeybd_on;
-
- Common::SaveFileManager *_savefile;
- Audio::MixerImpl *_mixer;
- Common::TimerManager *_timer;
- FilesystemFactory *_fsFactory;
- Common::Archive *_asset_archive;
- timeval _startTime;
-
- void setupScummVMSurface();
- void destroyScummVMSurface();
- void setupKeymapper();
- void _setCursorPalette(const byte *colors, uint start, uint num);
-
-public:
- OSystem_Android(jobject am);
- virtual ~OSystem_Android();
- bool initJavaHooks(JNIEnv *env, jobject self);
-
- static OSystem_Android *fromJavaObject(JNIEnv *env, jobject obj);
- virtual void initBackend();
- void addPluginDirectories(Common::FSList &dirs) const;
- void enableZoning(bool enable) { _enable_zoning = enable; }
- void setSurfaceSize(int width, int height) {
- _egl_surface_width = width;
- _egl_surface_height = height;
- }
-
- virtual bool hasFeature(Feature f);
- virtual void setFeatureState(Feature f, bool enable);
- virtual bool getFeatureState(Feature f);
- virtual const GraphicsMode *getSupportedGraphicsModes() const;
- virtual int getDefaultGraphicsMode() const;
- bool setGraphicsMode(const char *name);
- virtual bool setGraphicsMode(int mode);
- virtual int getGraphicsMode() const;
- virtual void initSize(uint width, uint height,
- const Graphics::PixelFormat *format);
-
- virtual int getScreenChangeID() const {
- return _screen_changeid;
- }
-
- virtual int16 getHeight();
- virtual int16 getWidth();
-
- virtual PaletteManager *getPaletteManager() {
- return this;
- }
-
-protected:
- // PaletteManager API
- virtual void setPalette(const byte *colors, uint start, uint num);
- virtual void grabPalette(byte *colors, uint start, uint num);
-
-public:
- virtual void copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h);
- virtual void updateScreen();
- virtual Graphics::Surface *lockScreen();
- virtual void unlockScreen();
- virtual void setShakePos(int shakeOffset);
- virtual void fillScreen(uint32 col);
- virtual void setFocusRectangle(const Common::Rect& rect);
- virtual void clearFocusRectangle();
-
- virtual void showOverlay();
- virtual void hideOverlay();
- virtual void clearOverlay();
- virtual void grabOverlay(OverlayColor *buf, int pitch);
- virtual void copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h);
- virtual int16 getOverlayHeight();
- virtual int16 getOverlayWidth();
-
- // RGBA 4444
- virtual Graphics::PixelFormat getOverlayFormat() const {
- Graphics::PixelFormat format;
-
- format.bytesPerPixel = 2;
- format.rLoss = 8 - 4;
- format.gLoss = 8 - 4;
- format.bLoss = 8 - 4;
- format.aLoss = 8 - 4;
- format.rShift = 3 * 4;
- format.gShift = 2 * 4;
- format.bShift = 1 * 4;
- format.aShift = 0 * 4;
-
- return format;
- }
-
- virtual bool showMouse(bool visible);
-
- virtual void warpMouse(int x, int y);
- virtual void setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int cursorTargetScale, const Graphics::PixelFormat *format);
- virtual void setCursorPalette(const byte *colors, uint start, uint num);
- virtual void disableCursorPalette(bool disable);
-
- virtual bool pollEvent(Common::Event &event);
- void pushEvent(const Common::Event& event);
- virtual uint32 getMillis();
- virtual void delayMillis(uint msecs);
-
- virtual MutexRef createMutex(void);
- virtual void lockMutex(MutexRef mutex);
- virtual void unlockMutex(MutexRef mutex);
- virtual void deleteMutex(MutexRef mutex);
-
- virtual void quit();
-
- virtual void setWindowCaption(const char *caption);
- virtual void displayMessageOnOSD(const char *msg);
- virtual void showVirtualKeyboard(bool enable);
-
- virtual Common::SaveFileManager *getSavefileManager();
- virtual Audio::Mixer *getMixer();
- virtual void getTimeAndDate(TimeDate &t) const;
- virtual Common::TimerManager *getTimerManager();
- virtual FilesystemFactory *getFilesystemFactory();
- virtual void logMessage(LogMessageType::Type type, const char *message);
- virtual void addSysArchivesToSearchSet(Common::SearchSet &s, int priority = 0);
-};
-
-OSystem_Android::OSystem_Android(jobject am) :
- _back_ptr(0),
+OSystem_Android::OSystem_Android(int audio_sample_rate, int audio_buffer_size) :
+ _audio_sample_rate(audio_sample_rate),
+ _audio_buffer_size(audio_buffer_size),
_screen_changeid(0),
+ _egl_surface_width(0),
+ _egl_surface_height(0),
_force_redraw(false),
_game_texture(0),
_overlay_texture(0),
_mouse_texture(0),
+ _mouse_texture_palette(0),
+ _mouse_texture_rgb(0),
_use_mouse_palette(false),
_show_mouse(false),
_show_overlay(false),
@@ -342,7 +120,6 @@ OSystem_Android::OSystem_Android(jobject am) :
_mixer(0),
_timer(0),
_fsFactory(new POSIXFilesystemFactory()),
- _asset_archive(new AndroidAssetArchive(am)),
_shake_offset(0),
_event_queue_lock(createMutex()) {
}
@@ -350,172 +127,182 @@ OSystem_Android::OSystem_Android(jobject am) :
OSystem_Android::~OSystem_Android() {
ENTER();
- delete _game_texture;
- delete _overlay_texture;
- delete _mouse_texture;
-
- destroyScummVMSurface();
-
- JNIEnv *env = JNU_GetEnv();
- //env->DeleteWeakGlobalRef(_back_ptr);
- env->DeleteGlobalRef(_back_ptr);
-
delete _savefile;
- delete _mixer;
delete _timer;
+ delete _mixer;
delete _fsFactory;
- delete _asset_archive;
deleteMutex(_event_queue_lock);
}
-OSystem_Android *OSystem_Android::fromJavaObject(JNIEnv *env, jobject obj) {
- jlong peer = env->GetLongField(obj, FID_ScummVM_nativeScummVM);
- return (OSystem_Android *)peer;
-}
+void *OSystem_Android::timerThreadFunc(void *arg) {
+ OSystem_Android *system = (OSystem_Android *)arg;
+ DefaultTimerManager *timer = (DefaultTimerManager *)(system->_timer);
-bool OSystem_Android::initJavaHooks(JNIEnv *env, jobject self) {
- // weak global ref to allow class to be unloaded
- // ... except dalvik doesn't implement NewWeakGlobalRef (yet)
- //_back_ptr = env->NewWeakGlobalRef(self);
- _back_ptr = env->NewGlobalRef(self);
-
- jclass cls = env->GetObjectClass(_back_ptr);
-
-#define FIND_METHOD(name, signature) do { \
- MID_ ## name = env->GetMethodID(cls, #name, signature); \
- if (MID_ ## name == 0) \
- return false; \
- } while (0)
-
- FIND_METHOD(setWindowCaption, "(Ljava/lang/String;)V");
- FIND_METHOD(displayMessageOnOSD, "(Ljava/lang/String;)V");
- FIND_METHOD(initBackend, "()V");
- FIND_METHOD(audioSampleRate, "()I");
- FIND_METHOD(showVirtualKeyboard, "(Z)V");
- FIND_METHOD(getSysArchives, "()[Ljava/lang/String;");
- FIND_METHOD(getPluginDirectories, "()[Ljava/lang/String;");
- FIND_METHOD(setupScummVMSurface, "()V");
- FIND_METHOD(destroyScummVMSurface, "()V");
- FIND_METHOD(swapBuffers, "()Z");
-
-#undef FIND_METHOD
+ // renice this thread to boost the audio thread
+ if (setpriority(PRIO_PROCESS, 0, 19) < 0)
+ LOGW("couldn't renice the timer thread");
- return true;
-}
+ JNI::attachThread();
-static void ScummVM_create(JNIEnv *env, jobject self, jobject am) {
- OSystem_Android *cpp_obj = new OSystem_Android(am);
+ struct timespec tv;
+ tv.tv_sec = 0;
+ tv.tv_nsec = 100 * 1000 * 1000; // 100ms
- // Exception already thrown by initJavaHooks?
- if (!cpp_obj->initJavaHooks(env, self))
- return;
+ while (!system->_timer_thread_exit) {
+ if (JNI::pause) {
+ LOGD("timer thread going to sleep");
+ sem_wait(&JNI::pause_sem);
+ LOGD("timer thread woke up");
+ }
- env->SetLongField(self, FID_ScummVM_nativeScummVM, (jlong)cpp_obj);
+ timer->handler();
+ nanosleep(&tv, 0);
+ }
-#ifdef DYNAMIC_MODULES
- PluginManager::instance().addPluginProvider(new AndroidPluginProvider());
-#endif
-}
+ JNI::detachThread();
-static void ScummVM_nativeDestroy(JNIEnv *env, jobject self) {
- OSystem_Android *cpp_obj = OSystem_Android::fromJavaObject(env, self);
- delete cpp_obj;
+ return 0;
}
-static void ScummVM_audioMixCallback(JNIEnv *env, jobject self,
- jbyteArray jbuf) {
- OSystem_Android *cpp_obj = OSystem_Android::fromJavaObject(env, self);
- jsize len = env->GetArrayLength(jbuf);
- jbyte *buf = env->GetByteArrayElements(jbuf, 0);
+void *OSystem_Android::audioThreadFunc(void *arg) {
+ JNI::attachThread();
- if (buf == 0) {
- warning("Unable to get Java audio byte array. Skipping");
- return;
- }
+ OSystem_Android *system = (OSystem_Android *)arg;
+ Audio::MixerImpl *mixer = system->_mixer;
- Audio::MixerImpl *mixer =
- static_cast<Audio::MixerImpl *>(cpp_obj->getMixer());
- assert(mixer);
- mixer->mixCallback(reinterpret_cast<byte *>(buf), len);
+ uint buf_size = system->_audio_buffer_size;
- env->ReleaseByteArrayElements(jbuf, buf, 0);
-}
+ JNIEnv *env = JNI::getEnv();
-static void ScummVM_setConfManInt(JNIEnv *env, jclass cls,
- jstring key_obj, jint value) {
- ENTER("%p, %d", key_obj, (int)value);
+ jbyteArray bufa = env->NewByteArray(buf_size);
- const char *key = env->GetStringUTFChars(key_obj, 0);
+ bool paused = true;
- if (key == 0)
- return;
+ byte *buf;
+ int offset, left, written;
+ int samples, i;
- ConfMan.setInt(key, value);
+ struct timespec tv_delay;
+ tv_delay.tv_sec = 0;
+ tv_delay.tv_nsec = 20 * 1000 * 1000;
- env->ReleaseStringUTFChars(key_obj, key);
-}
+ uint msecs_full = buf_size * 1000 / (mixer->getOutputRate() * 2 * 2);
-static void ScummVM_setConfManString(JNIEnv *env, jclass cls, jstring key_obj,
- jstring value_obj) {
- ENTER("%p, %p", key_obj, value_obj);
+ struct timespec tv_full;
+ tv_full.tv_sec = 0;
+ tv_full.tv_nsec = msecs_full * 1000 * 1000;
- const char *key = env->GetStringUTFChars(key_obj, 0);
+ bool silence;
+ uint silence_count = 33;
- if (key == 0)
- return;
+ while (!system->_audio_thread_exit) {
+ if (JNI::pause) {
+ JNI::setAudioStop();
- const char *value = env->GetStringUTFChars(value_obj, 0);
+ paused = true;
+ silence_count = 33;
- if (value == 0) {
- env->ReleaseStringUTFChars(key_obj, key);
- return;
- }
+ LOGD("audio thread going to sleep");
+ sem_wait(&JNI::pause_sem);
+ LOGD("audio thread woke up");
+ }
- ConfMan.set(key, value);
+ buf = (byte *)env->GetPrimitiveArrayCritical(bufa, 0);
+ assert(buf);
- env->ReleaseStringUTFChars(value_obj, value);
- env->ReleaseStringUTFChars(key_obj, key);
-}
+ samples = mixer->mixCallback(buf, buf_size);
-void *OSystem_Android::timerThreadFunc(void *arg) {
- OSystem_Android *system = (OSystem_Android *)arg;
- DefaultTimerManager *timer = (DefaultTimerManager *)(system->_timer);
+ silence = samples < 1;
- JNIEnv *env = 0;
- jint res = cached_jvm->AttachCurrentThread(&env, 0);
+ // looks stupid, and it is, but currently there's no way to detect
+ // silence-only buffers from the mixer
+ if (!silence) {
+ silence = true;
- if (res != JNI_OK) {
- LOGE("AttachCurrentThread() failed: %d", res);
- abort();
- }
+ for (i = 0; i < samples; i += 2)
+ // SID streams constant crap
+ if (READ_UINT16(buf + i) > 32) {
+ silence = false;
+ break;
+ }
+ }
- struct timespec tv;
- tv.tv_sec = 0;
- tv.tv_nsec = 100 * 1000 * 1000; // 100ms
+ env->ReleasePrimitiveArrayCritical(bufa, buf, 0);
- while (!system->_timer_thread_exit) {
- timer->handler();
- nanosleep(&tv, 0);
- }
+ if (silence) {
+ if (!paused)
+ silence_count++;
+
+ // only pause after a while to prevent toggle mania
+ if (silence_count > 32) {
+ if (!paused) {
+ LOGD("AudioTrack pause");
+
+ JNI::setAudioPause();
+ paused = true;
+ }
+
+ nanosleep(&tv_full, 0);
+
+ continue;
+ }
+ }
+
+ if (paused) {
+ LOGD("AudioTrack play");
+
+ JNI::setAudioPlay();
+ paused = false;
+
+ silence_count = 0;
+ }
+
+ offset = 0;
+ left = buf_size;
+ written = 0;
- res = cached_jvm->DetachCurrentThread();
+ while (left > 0) {
+ written = JNI::writeAudio(env, bufa, offset, left);
- if (res != JNI_OK) {
- LOGE("DetachCurrentThread() failed: %d", res);
- abort();
+ if (written < 0) {
+ LOGE("AudioTrack error: %d", written);
+ break;
+ }
+
+ // buffer full
+ if (written < left)
+ nanosleep(&tv_delay, 0);
+
+ offset += written;
+ left -= written;
+ }
+
+ if (written < 0)
+ break;
+
+ // sleep a little, prepare the next buffer, and run into the
+ // blocking AudioTrack.write
+ nanosleep(&tv_delay, 0);
}
+ JNI::setAudioStop();
+
+ env->DeleteLocalRef(bufa);
+
+ JNI::detachThread();
+
return 0;
}
void OSystem_Android::initBackend() {
ENTER();
- JNIEnv *env = JNU_GetEnv();
+ _main_thread = pthread_self();
ConfMan.setInt("autosave_period", 0);
- ConfMan.setInt("FM_medium_quality", true);
+ ConfMan.setBool("FM_high_quality", false);
+ ConfMan.setBool("FM_medium_quality", true);
// must happen before creating TimerManager to avoid race in
// creating EventManager
@@ -529,74 +316,34 @@ void OSystem_Android::initBackend() {
gettimeofday(&_startTime, 0);
- jint sample_rate = env->CallIntMethod(_back_ptr, MID_audioSampleRate);
- if (env->ExceptionCheck()) {
- warning("Error finding audio sample rate - assuming 11025HZ");
-
- env->ExceptionDescribe();
- env->ExceptionClear();
-
- sample_rate = 11025;
- }
-
- _mixer = new Audio::MixerImpl(this, sample_rate);
+ _mixer = new Audio::MixerImpl(this, _audio_sample_rate);
_mixer->setReady(true);
- env->CallVoidMethod(_back_ptr, MID_initBackend);
+ _timer_thread_exit = false;
+ pthread_create(&_timer_thread, 0, timerThreadFunc, this);
- if (env->ExceptionCheck()) {
- error("Error in Java initBackend");
+ _audio_thread_exit = false;
+ pthread_create(&_audio_thread, 0, audioThreadFunc, this);
- env->ExceptionDescribe();
- env->ExceptionClear();
- }
+ initSurface();
+ initViewport();
- _timer_thread_exit = false;
- pthread_create(&_timer_thread, 0, timerThreadFunc, this);
+ _game_texture = new GLESPalette888Texture();
+ _overlay_texture = new GLES4444Texture();
+ _mouse_texture_palette = new GLESPalette8888Texture();
+ _mouse_texture = _mouse_texture_palette;
- OSystem::initBackend();
+ // renice this thread to boost the audio thread
+ if (setpriority(PRIO_PROCESS, 0, 19) < 0)
+ warning("couldn't renice the main thread");
- setupScummVMSurface();
+ JNI::setReadyForEvents(true);
}
void OSystem_Android::addPluginDirectories(Common::FSList &dirs) const {
ENTER();
- JNIEnv *env = JNU_GetEnv();
-
- jobjectArray array =
- (jobjectArray)env->CallObjectMethod(_back_ptr, MID_getPluginDirectories);
- if (env->ExceptionCheck()) {
- warning("Error finding plugin directories");
-
- env->ExceptionDescribe();
- env->ExceptionClear();
-
- return;
- }
-
- jsize size = env->GetArrayLength(array);
- for (jsize i = 0; i < size; ++i) {
- jstring path_obj = (jstring)env->GetObjectArrayElement(array, i);
-
- if (path_obj == 0)
- continue;
-
- const char *path = env->GetStringUTFChars(path_obj, 0);
- if (path == 0) {
- warning("Error getting string characters from plugin directory");
-
- env->ExceptionClear();
- env->DeleteLocalRef(path_obj);
-
- continue;
- }
-
- dirs.push_back(Common::FSNode(path));
-
- env->ReleaseStringUTFChars(path_obj, path);
- env->DeleteLocalRef(path_obj);
- }
+ JNI::getPluginDirectories(dirs);
}
bool OSystem_Android::hasFeature(Feature f) {
@@ -627,430 +374,6 @@ bool OSystem_Android::getFeatureState(Feature f) {
}
}
-const OSystem::GraphicsMode *OSystem_Android::getSupportedGraphicsModes() const {
- static const OSystem::GraphicsMode s_supportedGraphicsModes[] = {
- { "default", "Default", 1 },
- { 0, 0, 0 },
- };
-
- return s_supportedGraphicsModes;
-}
-
-
-int OSystem_Android::getDefaultGraphicsMode() const {
- return 1;
-}
-
-bool OSystem_Android::setGraphicsMode(const char *mode) {
- ENTER("%s", mode);
- return true;
-}
-
-bool OSystem_Android::setGraphicsMode(int mode) {
- ENTER("%d", mode);
- return true;
-}
-
-int OSystem_Android::getGraphicsMode() const {
- return 1;
-}
-
-void OSystem_Android::setupScummVMSurface() {
- ENTER();
-
- JNIEnv *env = JNU_GetEnv();
- env->CallVoidMethod(_back_ptr, MID_setupScummVMSurface);
-
- if (env->ExceptionCheck())
- return;
-
- // EGL set up with a new surface. Initialise OpenGLES context.
- GLESTexture::initGLExtensions();
-
- // Turn off anything that looks like 3D ;)
- GLCALL(glDisable(GL_CULL_FACE));
- GLCALL(glDisable(GL_DEPTH_TEST));
- GLCALL(glDisable(GL_LIGHTING));
- GLCALL(glDisable(GL_FOG));
- GLCALL(glDisable(GL_DITHER));
-
- GLCALL(glShadeModel(GL_FLAT));
- GLCALL(glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST));
-
- GLCALL(glEnable(GL_BLEND));
- GLCALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
-
- GLCALL(glEnableClientState(GL_VERTEX_ARRAY));
- GLCALL(glEnableClientState(GL_TEXTURE_COORD_ARRAY));
-
- GLCALL(glEnable(GL_TEXTURE_2D));
-
- if (!_game_texture)
- _game_texture = new GLESPaletteTexture();
- else
- _game_texture->reinitGL();
-
- if (!_overlay_texture)
- _overlay_texture = new GLES4444Texture();
- else
- _overlay_texture->reinitGL();
-
- if (!_mouse_texture)
- _mouse_texture = new GLESPaletteATexture();
- else
- _mouse_texture->reinitGL();
-
- GLCALL(glViewport(0, 0, _egl_surface_width, _egl_surface_height));
-
- GLCALL(glMatrixMode(GL_PROJECTION));
- GLCALL(glLoadIdentity());
- GLCALL(glOrthof(0, _egl_surface_width, _egl_surface_height, 0, -1, 1));
- GLCALL(glMatrixMode(GL_MODELVIEW));
- GLCALL(glLoadIdentity());
-
- clearFocusRectangle();
-}
-
-void OSystem_Android::destroyScummVMSurface() {
- JNIEnv *env = JNU_GetEnv();
- env->CallVoidMethod(_back_ptr, MID_destroyScummVMSurface);
- // Can't use OpenGLES functions after this
-}
-
-void OSystem_Android::initSize(uint width, uint height,
- const Graphics::PixelFormat *format) {
- ENTER("%d, %d, %p", width, height, format);
-
- _game_texture->allocBuffer(width, height);
-
- // Cap at 320x200 or the ScummVM themes abort :/
- GLuint overlay_width = MIN(_egl_surface_width, 320);
- GLuint overlay_height = MIN(_egl_surface_height, 200);
- _overlay_texture->allocBuffer(overlay_width, overlay_height);
-
- // Don't know mouse size yet - it gets reallocated in
- // setMouseCursor. We need the palette allocated before
- // setMouseCursor however, so just take a guess at the desired
- // size (it's small).
- _mouse_texture->allocBuffer(20, 20);
-}
-
-int16 OSystem_Android::getHeight() {
- return _game_texture->height();
-}
-
-int16 OSystem_Android::getWidth() {
- return _game_texture->width();
-}
-
-void OSystem_Android::setPalette(const byte *colors, uint start, uint num) {
- ENTER("%p, %u, %u", colors, start, num);
-
- if (!_use_mouse_palette)
- _setCursorPalette(colors, start, num);
-
- memcpy(_game_texture->palette() + start * 3, colors, num * 3);
-}
-
-void OSystem_Android::grabPalette(byte *colors, uint start, uint num) {
- ENTER("%p, %u, %u", colors, start, num);
- memcpy(colors, _game_texture->palette_const() + start * 3, num * 3);
-}
-
-void OSystem_Android::copyRectToScreen(const byte *buf, int pitch,
- int x, int y, int w, int h) {
- ENTER("%p, %d, %d, %d, %d, %d", buf, pitch, x, y, w, h);
-
- _game_texture->updateBuffer(x, y, w, h, buf, pitch);
-}
-
-void OSystem_Android::updateScreen() {
- //ENTER();
-
- if (!_force_redraw &&
- !_game_texture->dirty() &&
- !_overlay_texture->dirty() &&
- !_mouse_texture->dirty())
- return;
-
- _force_redraw = false;
-
- GLCALL(glPushMatrix());
-
- if (_shake_offset != 0 ||
- (!_focus_rect.isEmpty() &&
- !Common::Rect(_game_texture->width(),
- _game_texture->height()).contains(_focus_rect))) {
- // These are the only cases where _game_texture doesn't
- // cover the entire screen.
- GLCALL(glClearColorx(0, 0, 0, 1 << 16));
- GLCALL(glClear(GL_COLOR_BUFFER_BIT));
-
- // Move everything up by _shake_offset (game) pixels
- GLCALL(glTranslatex(0, -_shake_offset << 16, 0));
- }
-
- if (_focus_rect.isEmpty()) {
- _game_texture->drawTexture(0, 0,
- _egl_surface_width, _egl_surface_height);
- } else {
- GLCALL(glPushMatrix());
- GLCALL(glScalex(xdiv(_egl_surface_width, _focus_rect.width()),
- xdiv(_egl_surface_height, _focus_rect.height()),
- 1 << 16));
- GLCALL(glTranslatex(-_focus_rect.left << 16,
- -_focus_rect.top << 16, 0));
- GLCALL(glScalex(xdiv(_game_texture->width(), _egl_surface_width),
- xdiv(_game_texture->height(), _egl_surface_height),
- 1 << 16));
-
- _game_texture->drawTexture(0, 0,
- _egl_surface_width, _egl_surface_height);
- GLCALL(glPopMatrix());
- }
-
- int cs = _mouse_targetscale;
-
- if (_show_overlay) {
- // ugly, but the modern theme sets a wacko factor, only god knows why
- cs = 1;
-
- GLCALL(_overlay_texture->drawTexture(0, 0,
- _egl_surface_width,
- _egl_surface_height));
- }
-
- if (_show_mouse) {
- GLCALL(glPushMatrix());
-
- // Scale up ScummVM -> OpenGL (pixel) coordinates
- int texwidth, texheight;
-
- if (_show_overlay) {
- texwidth = getOverlayWidth();
- texheight = getOverlayHeight();
- } else {
- texwidth = getWidth();
- texheight = getHeight();
- }
-
- GLCALL(glScalex(xdiv(_egl_surface_width, texwidth),
- xdiv(_egl_surface_height, texheight),
- 1 << 16));
-
- GLCALL(glTranslatex((-_mouse_hotspot.x * cs) << 16,
- (-_mouse_hotspot.y * cs) << 16,
- 0));
-
- // Note the extra half texel to position the mouse in
- // the middle of the x,y square:
- const Common::Point& mouse = getEventManager()->getMousePos();
- GLCALL(glTranslatex((mouse.x << 16) | 1 << 15,
- (mouse.y << 16) | 1 << 15, 0));
-
- GLCALL(glScalex(cs << 16, cs << 16, 1 << 16));
-
- _mouse_texture->drawTexture();
-
- GLCALL(glPopMatrix());
- }
-
- GLCALL(glPopMatrix());
-
- JNIEnv *env = JNU_GetEnv();
- if (!env->CallBooleanMethod(_back_ptr, MID_swapBuffers)) {
- // Context lost -> need to reinit GL
- destroyScummVMSurface();
- setupScummVMSurface();
- }
-}
-
-Graphics::Surface *OSystem_Android::lockScreen() {
- ENTER();
-
- Graphics::Surface *surface = _game_texture->surface();
- assert(surface->pixels);
-
- return surface;
-}
-
-void OSystem_Android::unlockScreen() {
- ENTER();
-
- assert(_game_texture->dirty());
-}
-
-void OSystem_Android::setShakePos(int shake_offset) {
- ENTER("%d", shake_offset);
-
- if (_shake_offset != shake_offset) {
- _shake_offset = shake_offset;
- _force_redraw = true;
- }
-}
-
-void OSystem_Android::fillScreen(uint32 col) {
- ENTER("%u", col);
-
- assert(col < 256);
- _game_texture->fillBuffer(col);
-}
-
-void OSystem_Android::setFocusRectangle(const Common::Rect& rect) {
- ENTER("%d, %d, %d, %d", rect.left, rect.top, rect.right, rect.bottom);
-
- if (_enable_zoning) {
- _focus_rect = rect;
- _force_redraw = true;
- }
-}
-
-void OSystem_Android::clearFocusRectangle() {
- ENTER();
-
- if (_enable_zoning) {
- _focus_rect = Common::Rect();
- _force_redraw = true;
- }
-}
-
-void OSystem_Android::showOverlay() {
- ENTER();
-
- _show_overlay = true;
- _force_redraw = true;
-}
-
-void OSystem_Android::hideOverlay() {
- ENTER();
-
- _show_overlay = false;
- _force_redraw = true;
-}
-
-void OSystem_Android::clearOverlay() {
- ENTER();
-
- _overlay_texture->fillBuffer(0);
-
- // Shouldn't need this, but works around a 'blank screen' bug on Nexus1
- updateScreen();
-}
-
-void OSystem_Android::grabOverlay(OverlayColor *buf, int pitch) {
- ENTER("%p, %d", buf, pitch);
-
- // We support overlay alpha blending, so the pixel data here
- // shouldn't actually be used. Let's fill it with zeros, I'm sure
- // it will be fine...
- const Graphics::Surface *surface = _overlay_texture->surface_const();
- assert(surface->bytesPerPixel == sizeof(buf[0]));
-
- int h = surface->h;
-
- do {
- memset(buf, 0, surface->w * sizeof(buf[0]));
-
- // This 'pitch' is pixels not bytes
- buf += pitch;
- } while (--h);
-}
-
-void OSystem_Android::copyRectToOverlay(const OverlayColor *buf, int pitch,
- int x, int y, int w, int h) {
- ENTER("%p, %d, %d, %d, %d, %d", buf, pitch, x, y, w, h);
-
- const Graphics::Surface *surface = _overlay_texture->surface_const();
- assert(surface->bytesPerPixel == sizeof(buf[0]));
-
- // This 'pitch' is pixels not bytes
- _overlay_texture->updateBuffer(x, y, w, h, buf, pitch * sizeof(buf[0]));
-
- // Shouldn't need this, but works around a 'blank screen' bug on Nexus1?
- updateScreen();
-}
-
-int16 OSystem_Android::getOverlayHeight() {
- return _overlay_texture->height();
-}
-
-int16 OSystem_Android::getOverlayWidth() {
- return _overlay_texture->width();
-}
-
-bool OSystem_Android::showMouse(bool visible) {
- ENTER("%d", visible);
-
- _show_mouse = visible;
-
- return true;
-}
-
-void OSystem_Android::warpMouse(int x, int y) {
- ENTER("%d, %d", x, y);
-
- // We use only the eventmanager's idea of the current mouse
- // position, so there is nothing extra to do here.
-}
-
-void OSystem_Android::setMouseCursor(const byte *buf, uint w, uint h,
- int hotspotX, int hotspotY,
- uint32 keycolor, int cursorTargetScale,
- const Graphics::PixelFormat *format) {
- ENTER("%p, %u, %u, %d, %d, %u, %d, %p", buf, w, h, hotspotX, hotspotY,
- keycolor, cursorTargetScale, format);
-
- assert(keycolor < 256);
-
- _mouse_texture->allocBuffer(w, h);
-
- // Update palette alpha based on keycolor
- byte *palette = _mouse_texture->palette();
- int i = 256;
-
- do {
- palette[3] = 0xff;
- palette += 4;
- } while (--i);
-
- palette = _mouse_texture->palette();
- palette[keycolor * 4 + 3] = 0x00;
-
- _mouse_texture->updateBuffer(0, 0, w, h, buf, w);
-
- _mouse_hotspot = Common::Point(hotspotX, hotspotY);
- _mouse_targetscale = cursorTargetScale;
-}
-
-void OSystem_Android::_setCursorPalette(const byte *colors,
- uint start, uint num) {
- byte *palette = _mouse_texture->palette() + start * 4;
-
- do {
- for (int i = 0; i < 3; ++i)
- palette[i] = colors[i];
-
- // Leave alpha untouched to preserve keycolor
-
- palette += 4;
- colors += 3;
- } while (--num);
-}
-
-void OSystem_Android::setCursorPalette(const byte *colors,
- uint start, uint num) {
- ENTER("%p, %u, %u", colors, start, num);
-
- _setCursorPalette(colors, start, num);
- _use_mouse_palette = true;
-}
-
-void OSystem_Android::disableCursorPalette(bool disable) {
- ENTER("%d", disable);
-
- _use_mouse_palette = !disable;
-}
-
void OSystem_Android::setupKeymapper() {
#ifdef ENABLE_KEYMAPPER
using namespace Common;
@@ -1082,6 +405,48 @@ void OSystem_Android::setupKeymapper() {
bool OSystem_Android::pollEvent(Common::Event &event) {
//ENTER();
+ if (pthread_self() == _main_thread) {
+ if (_screen_changeid != JNI::surface_changeid) {
+ if (JNI::egl_surface_width > 0 && JNI::egl_surface_height > 0) {
+ if (_egl_surface_width > 0 && _egl_surface_height > 0) {
+ // surface still alive but changed
+ _screen_changeid = JNI::surface_changeid;
+ _egl_surface_width = JNI::egl_surface_width;
+ _egl_surface_height = JNI::egl_surface_height;
+
+ initViewport();
+ // double buffered, flip twice
+ _force_redraw = true;
+ updateScreen();
+ _force_redraw = true;
+
+ event.type = Common::EVENT_SCREEN_CHANGED;
+
+ return true;
+ } else {
+ // new surface
+ initSurface();
+ _force_redraw = true;
+
+ event.type = Common::EVENT_SCREEN_CHANGED;
+
+ return true;
+ }
+ } else {
+ // surface lost
+ deinitSurface();
+ }
+ }
+
+ if (JNI::pause) {
+ deinitSurface();
+
+ LOGD("main thread going to sleep");
+ sem_wait(&JNI::pause_sem);
+ LOGD("main thread woke up");
+ }
+ }
+
lockMutex(_event_queue_lock);
if (_event_queue.empty()) {
@@ -1119,9 +484,12 @@ bool OSystem_Android::pollEvent(Common::Event &event) {
} else {
// Touchscreen events need to be converted
// from device to game coords first.
- const GLESTexture *tex = _show_overlay
- ? static_cast<GLESTexture *>(_overlay_texture)
- : static_cast<GLESTexture *>(_game_texture);
+ const GLESTexture *tex;
+ if (_show_overlay)
+ tex = _overlay_texture;
+ else
+ tex = _game_texture;
+
event.mouse.x = scalef(event.mouse.x, tex->width(),
_egl_surface_width);
event.mouse.y = scalef(event.mouse.y, tex->height(),
@@ -1130,12 +498,6 @@ bool OSystem_Android::pollEvent(Common::Event &event) {
}
break;
}
- case Common::EVENT_SCREEN_CHANGED:
- debug("EVENT_SCREEN_CHANGED");
- _screen_changeid++;
- destroyScummVMSurface();
- setupScummVMSurface();
- break;
default:
break;
}
@@ -1168,52 +530,6 @@ void OSystem_Android::pushEvent(const Common::Event& event) {
unlockMutex(_event_queue_lock);
}
-static void ScummVM_pushEvent(JNIEnv *env, jobject self, jobject java_event) {
- OSystem_Android *cpp_obj = OSystem_Android::fromJavaObject(env, self);
-
- Common::Event event;
- event.type = (Common::EventType)env->GetIntField(java_event,
- FID_Event_type);
-
- event.synthetic =
- env->GetBooleanField(java_event, FID_Event_synthetic);
-
- switch (event.type) {
- case Common::EVENT_KEYDOWN:
- case Common::EVENT_KEYUP:
- event.kbd.keycode = (Common::KeyCode)env->GetIntField(
- java_event, FID_Event_kbd_keycode);
- event.kbd.ascii = static_cast<int>(env->GetIntField(
- java_event, FID_Event_kbd_ascii));
- event.kbd.flags = static_cast<int>(env->GetIntField(
- java_event, FID_Event_kbd_flags));
- break;
- case Common::EVENT_MOUSEMOVE:
- case Common::EVENT_LBUTTONDOWN:
- case Common::EVENT_LBUTTONUP:
- case Common::EVENT_RBUTTONDOWN:
- case Common::EVENT_RBUTTONUP:
- case Common::EVENT_WHEELUP:
- case Common::EVENT_WHEELDOWN:
- case Common::EVENT_MBUTTONDOWN:
- case Common::EVENT_MBUTTONUP:
- event.mouse.x =
- env->GetIntField(java_event, FID_Event_mouse_x);
- event.mouse.y =
- env->GetIntField(java_event, FID_Event_mouse_y);
- // This is a terrible hack. We stash "relativeness"
- // in the kbd.flags field until pollEvent() can work
- // it out.
- event.kbd.flags = env->GetBooleanField(
- java_event, FID_Event_mouse_relative) ? 1 : 0;
- break;
- default:
- break;
- }
-
- cpp_obj->pushEvent(event);
-}
-
uint32 OSystem_Android::getMillis() {
timeval curTime;
@@ -1229,6 +545,7 @@ void OSystem_Android::delayMillis(uint msecs) {
OSystem::MutexRef OSystem_Android::createMutex() {
pthread_mutexattr_t attr;
+
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
@@ -1267,58 +584,38 @@ void OSystem_Android::deleteMutex(MutexRef mutex) {
void OSystem_Android::quit() {
ENTER();
+ JNI::setReadyForEvents(false);
+
+ _audio_thread_exit = true;
+ pthread_join(_audio_thread, 0);
+
_timer_thread_exit = true;
pthread_join(_timer_thread, 0);
+
+ delete _game_texture;
+ delete _overlay_texture;
+ delete _mouse_texture_palette;
+ delete _mouse_texture_rgb;
+
+ deinitSurface();
}
void OSystem_Android::setWindowCaption(const char *caption) {
ENTER("%s", caption);
- JNIEnv *env = JNU_GetEnv();
- jstring java_caption = env->NewStringUTF(caption);
- env->CallVoidMethod(_back_ptr, MID_setWindowCaption, java_caption);
-
- if (env->ExceptionCheck()) {
- warning("Failed to set window caption");
-
- env->ExceptionDescribe();
- env->ExceptionClear();
- }
-
- env->DeleteLocalRef(java_caption);
+ JNI::setWindowCaption(caption);
}
void OSystem_Android::displayMessageOnOSD(const char *msg) {
ENTER("%s", msg);
- JNIEnv *env = JNU_GetEnv();
- jstring java_msg = env->NewStringUTF(msg);
-
- env->CallVoidMethod(_back_ptr, MID_displayMessageOnOSD, java_msg);
-
- if (env->ExceptionCheck()) {
- warning("Failed to display OSD message");
-
- env->ExceptionDescribe();
- env->ExceptionClear();
- }
-
- env->DeleteLocalRef(java_msg);
+ JNI::displayMessageOnOSD(msg);
}
void OSystem_Android::showVirtualKeyboard(bool enable) {
ENTER("%d", enable);
- JNIEnv *env = JNU_GetEnv();
-
- env->CallVoidMethod(_back_ptr, MID_showVirtualKeyboard, enable);
-
- if (env->ExceptionCheck()) {
- error("Error trying to show virtual keyboard");
-
- env->ExceptionDescribe();
- env->ExceptionClear();
- }
+ JNI::showVirtualKeyboard(enable);
}
Common::SaveFileManager *OSystem_Android::getSavefileManager() {
@@ -1355,37 +652,13 @@ FilesystemFactory *OSystem_Android::getFilesystemFactory() {
void OSystem_Android::addSysArchivesToSearchSet(Common::SearchSet &s,
int priority) {
- s.add("ASSET", _asset_archive, priority, false);
-
- JNIEnv *env = JNU_GetEnv();
-
- jobjectArray array =
- (jobjectArray)env->CallObjectMethod(_back_ptr, MID_getSysArchives);
-
- if (env->ExceptionCheck()) {
- warning("Error finding system archive path");
-
- env->ExceptionDescribe();
- env->ExceptionClear();
+ ENTER("");
- return;
- }
-
- jsize size = env->GetArrayLength(array);
- for (jsize i = 0; i < size; ++i) {
- jstring path_obj = (jstring)env->GetObjectArrayElement(array, i);
- const char *path = env->GetStringUTFChars(path_obj, 0);
-
- if (path != 0) {
- s.addDirectory(path, path, priority);
- env->ReleaseStringUTFChars(path_obj, path);
- }
-
- env->DeleteLocalRef(path_obj);
- }
+ JNI::addSysArchivesToSearchSet(s, priority);
}
-void OSystem_Android::logMessage(LogMessageType::Type type, const char *message) {
+void OSystem_Android::logMessage(LogMessageType::Type type,
+ const char *message) {
switch (type) {
case LogMessageType::kDebug:
__android_log_write(ANDROID_LOG_DEBUG, android_log_tag, message);
@@ -1401,177 +674,11 @@ void OSystem_Android::logMessage(LogMessageType::Type type, const char *message)
}
}
-static jint ScummVM_scummVMMain(JNIEnv *env, jobject self, jobjectArray args) {
- OSystem_Android *cpp_obj = OSystem_Android::fromJavaObject(env, self);
-
- const int MAX_NARGS = 32;
- int res = -1;
-
- int argc = env->GetArrayLength(args);
- if (argc > MAX_NARGS) {
- JNU_ThrowByName(env, "java/lang/IllegalArgumentException",
- "too many arguments");
- return 0;
- }
-
- char *argv[MAX_NARGS];
-
- // note use in cleanup loop below
- int nargs;
-
- for (nargs = 0; nargs < argc; ++nargs) {
- jstring arg = (jstring)env->GetObjectArrayElement(args, nargs);
-
- if (arg == 0) {
- argv[nargs] = 0;
- } else {
- const char *cstr = env->GetStringUTFChars(arg, 0);
-
- argv[nargs] = const_cast<char *>(cstr);
-
- // exception already thrown?
- if (cstr == 0)
- goto cleanup;
- }
-
- env->DeleteLocalRef(arg);
- }
-
- g_system = cpp_obj;
- assert(g_system);
-
- LOGI("Entering scummvm_main with %d args", argc);
-
- res = scummvm_main(argc, argv);
-
- LOGI("Exiting scummvm_main");
-
- g_system->quit();
-
-cleanup:
- nargs--;
-
- for (int i = 0; i < nargs; ++i) {
- if (argv[i] == 0)
- continue;
-
- jstring arg = (jstring)env->GetObjectArrayElement(args, nargs);
-
- // Exception already thrown?
- if (arg == 0)
- return res;
-
- env->ReleaseStringUTFChars(arg, argv[i]);
- env->DeleteLocalRef(arg);
- }
-
- return res;
-}
-
#ifdef DYNAMIC_MODULES
void AndroidPluginProvider::addCustomDirectories(Common::FSList &dirs) const {
- OSystem_Android *g_system_android = (OSystem_Android *)g_system;
- g_system_android->addPluginDirectories(dirs);
+ ((OSystem_Android *)g_system)->addPluginDirectories(dirs);
}
#endif
-static void ScummVM_enableZoning(JNIEnv *env, jobject self, jboolean enable) {
- OSystem_Android *cpp_obj = OSystem_Android::fromJavaObject(env, self);
- cpp_obj->enableZoning(enable);
-}
-
-static void ScummVM_setSurfaceSize(JNIEnv *env, jobject self,
- jint width, jint height) {
- OSystem_Android *cpp_obj = OSystem_Android::fromJavaObject(env, self);
- cpp_obj->setSurfaceSize(width, height);
-}
-
-const static JNINativeMethod gMethods[] = {
- { "create", "(Landroid/content/res/AssetManager;)V",
- (void *)ScummVM_create },
- { "nativeDestroy", "()V",
- (void *)ScummVM_nativeDestroy },
- { "scummVMMain", "([Ljava/lang/String;)I",
- (void *)ScummVM_scummVMMain },
- { "pushEvent", "(Lorg/inodes/gus/scummvm/Event;)V",
- (void *)ScummVM_pushEvent },
- { "audioMixCallback", "([B)V",
- (void *)ScummVM_audioMixCallback },
- { "setConfMan", "(Ljava/lang/String;I)V",
- (void *)ScummVM_setConfManInt },
- { "setConfMan", "(Ljava/lang/String;Ljava/lang/String;)V",
- (void *)ScummVM_setConfManString },
- { "enableZoning", "(Z)V",
- (void *)ScummVM_enableZoning },
- { "setSurfaceSize", "(II)V",
- (void *)ScummVM_setSurfaceSize },
-};
-
-JNIEXPORT jint JNICALL
-JNI_OnLoad(JavaVM *jvm, void *reserved) {
- cached_jvm = jvm;
-
- JNIEnv *env;
-
- if (jvm->GetEnv((void **)&env, JNI_VERSION_1_2))
- return JNI_ERR;
-
- jclass cls = env->FindClass("org/inodes/gus/scummvm/ScummVM");
- if (cls == 0)
- return JNI_ERR;
-
- if (env->RegisterNatives(cls, gMethods, ARRAYSIZE(gMethods)) < 0)
- return JNI_ERR;
-
- FID_ScummVM_nativeScummVM = env->GetFieldID(cls, "nativeScummVM", "J");
- if (FID_ScummVM_nativeScummVM == 0)
- return JNI_ERR;
-
- jclass event = env->FindClass("org/inodes/gus/scummvm/Event");
- if (event == 0)
- return JNI_ERR;
-
- FID_Event_type = env->GetFieldID(event, "type", "I");
- if (FID_Event_type == 0)
- return JNI_ERR;
-
- FID_Event_synthetic = env->GetFieldID(event, "synthetic", "Z");
- if (FID_Event_synthetic == 0)
- return JNI_ERR;
-
- FID_Event_kbd_keycode = env->GetFieldID(event, "kbd_keycode", "I");
- if (FID_Event_kbd_keycode == 0)
- return JNI_ERR;
-
- FID_Event_kbd_ascii = env->GetFieldID(event, "kbd_ascii", "I");
- if (FID_Event_kbd_ascii == 0)
- return JNI_ERR;
-
- FID_Event_kbd_flags = env->GetFieldID(event, "kbd_flags", "I");
- if (FID_Event_kbd_flags == 0)
- return JNI_ERR;
-
- FID_Event_mouse_x = env->GetFieldID(event, "mouse_x", "I");
- if (FID_Event_mouse_x == 0)
- return JNI_ERR;
-
- FID_Event_mouse_y = env->GetFieldID(event, "mouse_y", "I");
- if (FID_Event_mouse_y == 0)
- return JNI_ERR;
-
- FID_Event_mouse_relative = env->GetFieldID(event, "mouse_relative", "Z");
- if (FID_Event_mouse_relative == 0)
- return JNI_ERR;
-
- cls = env->FindClass("java/lang/Object");
- if (cls == 0)
- return JNI_ERR;
-
- MID_Object_wait = env->GetMethodID(cls, "wait", "()V");
- if (MID_Object_wait == 0)
- return JNI_ERR;
-
- return JNI_VERSION_1_2;
-}
-
#endif
+
diff --git a/backends/platform/android/android.h b/backends/platform/android/android.h
index 855fb04b5d..5c7154a8e4 100644
--- a/backends/platform/android/android.h
+++ b/backends/platform/android/android.h
@@ -23,8 +23,23 @@
*
*/
+#ifndef _ANDROID_H_
+#define _ANDROID_H_
+
#if defined(__ANDROID__)
+#include "common/fs.h"
+#include "common/archive.h"
+#include "audio/mixer_intern.h"
+#include "graphics/surface.h"
+#include "backends/base-backend.h"
+#include "backends/plugins/posix/posix-provider.h"
+#include "backends/fs/posix/posix-fs-factory.h"
+
+#include "backends/platform/android/texture.h"
+
+#include <pthread.h>
+
#include <android/log.h>
#include <GLES/gl.h>
@@ -46,7 +61,7 @@ extern const char *android_log_tag;
#ifdef ANDROID_DEBUG_ENTER
#define ENTER(fmt, args...) LOGD("%s(" fmt ")", __FUNCTION__, ##args)
#else
-#define ENTER(fmt, args...) /**/
+#define ENTER(fmt, args...) do { } while (false)
#endif
#ifdef ANDROID_DEBUG_GL
@@ -58,13 +73,200 @@ extern void checkGlError(const char *expr, const char *file, int line);
checkGlError(#x, __FILE__, __LINE__); \
} while (false)
+#define GLTHREADCHECK \
+ do { \
+ assert(pthread_self() == _main_thread); \
+ } while (false)
+
#else
#define GLCALL(x) do { (x); } while (false)
+#define GLTHREADCHECK do { } while (false)
+#endif
+
+#ifdef DYNAMIC_MODULES
+class AndroidPluginProvider : public POSIXPluginProvider {
+protected:
+ virtual void addCustomDirectories(Common::FSList &dirs) const;
+};
+#endif
+
+class OSystem_Android : public BaseBackend, public PaletteManager {
+private:
+ // passed from the dark side
+ int _audio_sample_rate;
+ int _audio_buffer_size;
+
+ int _screen_changeid;
+ int _egl_surface_width;
+ int _egl_surface_height;
+
+ bool _force_redraw;
+
+ // Game layer
+ GLESTexture *_game_texture;
+ int _shake_offset;
+ Common::Rect _focus_rect;
+
+ // Overlay layer
+ GLES4444Texture *_overlay_texture;
+ bool _show_overlay;
+
+ // Mouse layer
+ GLESTexture *_mouse_texture;
+ GLESPaletteTexture *_mouse_texture_palette;
+ GLES5551Texture *_mouse_texture_rgb;
+ Common::Point _mouse_hotspot;
+ int _mouse_targetscale;
+ bool _show_mouse;
+ bool _use_mouse_palette;
+
+ Common::Queue<Common::Event> _event_queue;
+ MutexRef _event_queue_lock;
+
+ pthread_t _main_thread;
+
+ bool _timer_thread_exit;
+ pthread_t _timer_thread;
+ static void *timerThreadFunc(void *arg);
+
+ bool _audio_thread_exit;
+ pthread_t _audio_thread;
+ static void *audioThreadFunc(void *arg);
+
+ bool _enable_zoning;
+ bool _virtkeybd_on;
+
+ Common::SaveFileManager *_savefile;
+ Audio::MixerImpl *_mixer;
+ Common::TimerManager *_timer;
+ FilesystemFactory *_fsFactory;
+ timeval _startTime;
+
+ void initSurface();
+ void deinitSurface();
+ void initViewport();
+
+#ifdef USE_RGB_COLOR
+ Common::String getPixelFormatName(const Graphics::PixelFormat &format) const;
+ void initTexture(GLESTexture **texture, uint width, uint height,
+ const Graphics::PixelFormat *format, bool alphaPalette);
#endif
-// Fix JNIEXPORT declaration to actually do something useful
-#undef JNIEXPORT
-#define JNIEXPORT __attribute__ ((visibility("default")))
+ void setupKeymapper();
+ void setCursorPaletteInternal(const byte *colors, uint start, uint num);
+public:
+ OSystem_Android(int audio_sample_rate, int audio_buffer_size);
+ virtual ~OSystem_Android();
+
+ virtual void initBackend();
+ void addPluginDirectories(Common::FSList &dirs) const;
+ void enableZoning(bool enable) { _enable_zoning = enable; }
+
+ virtual bool hasFeature(Feature f);
+ virtual void setFeatureState(Feature f, bool enable);
+ virtual bool getFeatureState(Feature f);
+
+ virtual const GraphicsMode *getSupportedGraphicsModes() const;
+ virtual int getDefaultGraphicsMode() const;
+ bool setGraphicsMode(const char *name);
+ virtual bool setGraphicsMode(int mode);
+ virtual int getGraphicsMode() const;
+
+#ifdef USE_RGB_COLOR
+ virtual Graphics::PixelFormat getScreenFormat() const;
+ virtual Common::List<Graphics::PixelFormat> getSupportedFormats() const;
+#endif
+
+ virtual void initSize(uint width, uint height,
+ const Graphics::PixelFormat *format);
+ virtual int getScreenChangeID() const;
+
+ virtual int16 getHeight();
+ virtual int16 getWidth();
+
+ virtual PaletteManager *getPaletteManager() {
+ return this;
+ }
+
+protected:
+ // PaletteManager API
+ virtual void setPalette(const byte *colors, uint start, uint num);
+ virtual void grabPalette(byte *colors, uint start, uint num);
+
+public:
+ virtual void copyRectToScreen(const byte *buf, int pitch, int x, int y,
+ int w, int h);
+ virtual void updateScreen();
+ virtual Graphics::Surface *lockScreen();
+ virtual void unlockScreen();
+ virtual void setShakePos(int shakeOffset);
+ virtual void fillScreen(uint32 col);
+ virtual void setFocusRectangle(const Common::Rect& rect);
+ virtual void clearFocusRectangle();
+
+ virtual void showOverlay();
+ virtual void hideOverlay();
+ virtual void clearOverlay();
+ virtual void grabOverlay(OverlayColor *buf, int pitch);
+ virtual void copyRectToOverlay(const OverlayColor *buf, int pitch,
+ int x, int y, int w, int h);
+ virtual int16 getOverlayHeight();
+ virtual int16 getOverlayWidth();
+
+ // RGBA 4444
+ virtual Graphics::PixelFormat getOverlayFormat() const {
+ Graphics::PixelFormat format;
+
+ format.bytesPerPixel = 2;
+ format.rLoss = 8 - 4;
+ format.gLoss = 8 - 4;
+ format.bLoss = 8 - 4;
+ format.aLoss = 8 - 4;
+ format.rShift = 3 * 4;
+ format.gShift = 2 * 4;
+ format.bShift = 1 * 4;
+ format.aShift = 0 * 4;
+
+ return format;
+ }
+
+ virtual bool showMouse(bool visible);
+
+ virtual void warpMouse(int x, int y);
+ virtual void setMouseCursor(const byte *buf, uint w, uint h, int hotspotX,
+ int hotspotY, uint32 keycolor,
+ int cursorTargetScale,
+ const Graphics::PixelFormat *format);
+ virtual void setCursorPalette(const byte *colors, uint start, uint num);
+ virtual void disableCursorPalette(bool disable);
+
+ virtual bool pollEvent(Common::Event &event);
+ void pushEvent(const Common::Event& event);
+ virtual uint32 getMillis();
+ virtual void delayMillis(uint msecs);
+
+ virtual MutexRef createMutex(void);
+ virtual void lockMutex(MutexRef mutex);
+ virtual void unlockMutex(MutexRef mutex);
+ virtual void deleteMutex(MutexRef mutex);
+
+ virtual void quit();
+
+ virtual void setWindowCaption(const char *caption);
+ virtual void displayMessageOnOSD(const char *msg);
+ virtual void showVirtualKeyboard(bool enable);
+
+ virtual Common::SaveFileManager *getSavefileManager();
+ virtual Audio::Mixer *getMixer();
+ virtual void getTimeAndDate(TimeDate &t) const;
+ virtual Common::TimerManager *getTimerManager();
+ virtual FilesystemFactory *getFilesystemFactory();
+ virtual void logMessage(LogMessageType::Type type, const char *message);
+ virtual void addSysArchivesToSearchSet(Common::SearchSet &s,
+ int priority = 0);
+};
+
+#endif
#endif
diff --git a/backends/platform/android/asset-archive.cpp b/backends/platform/android/asset-archive.cpp
index 71ce25aa72..26b1a6ad39 100644
--- a/backends/platform/android/asset-archive.cpp
+++ b/backends/platform/android/asset-archive.cpp
@@ -36,10 +36,9 @@
#include "common/archive.h"
#include "common/debug.h"
+#include "backends/platform/android/jni.h"
#include "backends/platform/android/asset-archive.h"
-extern JNIEnv *JNU_GetEnv();
-
// Must match android.content.res.AssetManager.ACCESS_*
const jint ACCESS_UNKNOWN = 0;
const jint ACCESS_RANDOM = 1;
@@ -100,7 +99,7 @@ JavaInputStream::JavaInputStream(JNIEnv *env, jobject is) :
{
_input_stream = env->NewGlobalRef(is);
_buflen = 8192;
- _buf = static_cast<jbyteArray>(env->NewGlobalRef(env->NewByteArray(_buflen)));
+ _buf = (jbyteArray)env->NewGlobalRef(env->NewByteArray(_buflen));
jclass cls = env->GetObjectClass(_input_stream);
MID_mark = env->GetMethodID(cls, "mark", "(I)V");
@@ -124,7 +123,7 @@ JavaInputStream::JavaInputStream(JNIEnv *env, jobject is) :
}
JavaInputStream::~JavaInputStream() {
- JNIEnv *env = JNU_GetEnv();
+ JNIEnv *env = JNI::getEnv();
close(env);
env->DeleteGlobalRef(_buf);
@@ -139,11 +138,11 @@ void JavaInputStream::close(JNIEnv *env) {
}
uint32 JavaInputStream::read(void *dataPtr, uint32 dataSize) {
- JNIEnv *env = JNU_GetEnv();
+ JNIEnv *env = JNI::getEnv();
if (_buflen < jint(dataSize)) {
_buflen = dataSize;
-
+
env->DeleteGlobalRef(_buf);
_buf = static_cast<jbyteArray>(env->NewGlobalRef(env->NewByteArray(_buflen)));
}
@@ -171,7 +170,7 @@ uint32 JavaInputStream::read(void *dataPtr, uint32 dataSize) {
}
bool JavaInputStream::seek(int32 offset, int whence) {
- JNIEnv *env = JNU_GetEnv();
+ JNIEnv *env = JNI::getEnv();
uint32 newpos;
switch (whence) {
@@ -305,7 +304,8 @@ AssetFdReadStream::AssetFdReadStream(JNIEnv *env, jobject assetfd) :
_declared_len = env->CallLongMethod(_assetfd, MID_getDeclaredLength);
jmethodID MID_getFileDescriptor =
- env->GetMethodID(cls, "getFileDescriptor", "()Ljava/io/FileDescriptor;");
+ env->GetMethodID(cls, "getFileDescriptor",
+ "()Ljava/io/FileDescriptor;");
assert(MID_getFileDescriptor);
jobject javafd = env->CallObjectMethod(_assetfd, MID_getFileDescriptor);
assert(javafd);
@@ -318,7 +318,7 @@ AssetFdReadStream::AssetFdReadStream(JNIEnv *env, jobject assetfd) :
}
AssetFdReadStream::~AssetFdReadStream() {
- JNIEnv *env = JNU_GetEnv();
+ JNIEnv *env = JNI::getEnv();
env->CallVoidMethod(_assetfd, MID_close);
if (env->ExceptionCheck())
@@ -369,7 +369,7 @@ bool AssetFdReadStream::seek(int32 offset, int whence) {
}
AndroidAssetArchive::AndroidAssetArchive(jobject am) {
- JNIEnv *env = JNU_GetEnv();
+ JNIEnv *env = JNI::getEnv();
_am = env->NewGlobalRef(am);
jclass cls = env->GetObjectClass(_am);
@@ -377,8 +377,8 @@ AndroidAssetArchive::AndroidAssetArchive(jobject am) {
"(Ljava/lang/String;I)Ljava/io/InputStream;");
assert(MID_open);
- MID_openFd = env->GetMethodID(cls, "openFd",
- "(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;");
+ MID_openFd = env->GetMethodID(cls, "openFd", "(Ljava/lang/String;)"
+ "Landroid/content/res/AssetFileDescriptor;");
assert(MID_openFd);
MID_list = env->GetMethodID(cls, "list",
@@ -387,12 +387,12 @@ AndroidAssetArchive::AndroidAssetArchive(jobject am) {
}
AndroidAssetArchive::~AndroidAssetArchive() {
- JNIEnv *env = JNU_GetEnv();
+ JNIEnv *env = JNI::getEnv();
env->DeleteGlobalRef(_am);
}
bool AndroidAssetArchive::hasFile(const Common::String &name) {
- JNIEnv *env = JNU_GetEnv();
+ JNIEnv *env = JNI::getEnv();
jstring path = env->NewStringUTF(name.c_str());
jobject result = env->CallObjectMethod(_am, MID_open, path, ACCESS_UNKNOWN);
if (env->ExceptionCheck()) {
@@ -412,7 +412,7 @@ bool AndroidAssetArchive::hasFile(const Common::String &name) {
}
int AndroidAssetArchive::listMembers(Common::ArchiveMemberList &member_list) {
- JNIEnv *env = JNU_GetEnv();
+ JNIEnv *env = JNI::getEnv();
Common::List<Common::String> dirlist;
dirlist.push_back("");
@@ -422,7 +422,8 @@ int AndroidAssetArchive::listMembers(Common::ArchiveMemberList &member_list) {
dirlist.pop_back();
jstring jpath = env->NewStringUTF(dir.c_str());
- jobjectArray jpathlist = static_cast<jobjectArray>(env->CallObjectMethod(_am, MID_list, jpath));
+ jobjectArray jpathlist =
+ (jobjectArray)env->CallObjectMethod(_am, MID_list, jpath);
if (env->ExceptionCheck()) {
warning("Error while calling AssetManager->list(%s). Ignoring.",
@@ -469,7 +470,7 @@ Common::ArchiveMemberPtr AndroidAssetArchive::getMember(const Common::String &na
}
Common::SeekableReadStream *AndroidAssetArchive::createReadStreamForMember(const Common::String &path) const {
- JNIEnv *env = JNU_GetEnv();
+ JNIEnv *env = JNI::getEnv();
jstring jpath = env->NewStringUTF(path.c_str());
// Try openFd() first ...
diff --git a/backends/platform/android/asset-archive.h b/backends/platform/android/asset-archive.h
index 28e48426e9..6ec86e4cd0 100644
--- a/backends/platform/android/asset-archive.h
+++ b/backends/platform/android/asset-archive.h
@@ -23,6 +23,9 @@
*
*/
+#ifndef _ANDROID_ASSET_H_
+#define _ANDROID_ASSET_H_
+
#if defined(__ANDROID__)
#include <jni.h>
@@ -51,3 +54,5 @@ private:
};
#endif
+#endif
+
diff --git a/backends/platform/android/gfx.cpp b/backends/platform/android/gfx.cpp
new file mode 100644
index 0000000000..90b1c3ec57
--- /dev/null
+++ b/backends/platform/android/gfx.cpp
@@ -0,0 +1,699 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#if defined(__ANDROID__)
+
+#include "graphics/conversion.h"
+
+#include "backends/platform/android/android.h"
+#include "backends/platform/android/jni.h"
+
+static inline GLfixed xdiv(int numerator, int denominator) {
+ assert(numerator < (1 << 16));
+ return (numerator << 16) / denominator;
+}
+
+const OSystem::GraphicsMode *OSystem_Android::getSupportedGraphicsModes() const {
+ static const OSystem::GraphicsMode s_supportedGraphicsModes[] = {
+ { "default", "Default", 1 },
+ { 0, 0, 0 },
+ };
+
+ return s_supportedGraphicsModes;
+}
+
+int OSystem_Android::getDefaultGraphicsMode() const {
+ return 1;
+}
+
+bool OSystem_Android::setGraphicsMode(const char *mode) {
+ ENTER("%s", mode);
+
+ return true;
+}
+
+bool OSystem_Android::setGraphicsMode(int mode) {
+ ENTER("%d", mode);
+
+ return true;
+}
+
+int OSystem_Android::getGraphicsMode() const {
+ return 1;
+}
+
+#ifdef USE_RGB_COLOR
+Graphics::PixelFormat OSystem_Android::getScreenFormat() const {
+ return _game_texture->getPixelFormat();
+}
+
+Common::List<Graphics::PixelFormat> OSystem_Android::getSupportedFormats() const {
+ Common::List<Graphics::PixelFormat> res;
+ res.push_back(GLES565Texture::getPixelFormat());
+ res.push_back(GLES5551Texture::getPixelFormat());
+ res.push_back(GLES4444Texture::getPixelFormat());
+ res.push_back(Graphics::PixelFormat::createFormatCLUT8());
+
+ return res;
+}
+
+Common::String OSystem_Android::getPixelFormatName(const Graphics::PixelFormat &format) const {
+ if (format.bytesPerPixel == 1)
+ return "CLUT8";
+
+ if (format.aLoss == 8)
+ return Common::String::format("RGB%u%u%u",
+ 8 - format.rLoss,
+ 8 - format.gLoss,
+ 8 - format.bLoss);
+
+ return Common::String::format("RGBA%u%u%u%u",
+ 8 - format.rLoss,
+ 8 - format.gLoss,
+ 8 - format.bLoss,
+ 8 - format.aLoss);
+}
+
+void OSystem_Android::initTexture(GLESTexture **texture,
+ uint width, uint height,
+ const Graphics::PixelFormat *format,
+ bool alphaPalette) {
+ assert(texture);
+ Graphics::PixelFormat format_clut8 =
+ Graphics::PixelFormat::createFormatCLUT8();
+ Graphics::PixelFormat format_current;
+ Graphics::PixelFormat format_new;
+
+ if (*texture)
+ format_current = (*texture)->getPixelFormat();
+ else
+ format_current = Graphics::PixelFormat();
+
+ if (format)
+ format_new = *format;
+ else
+ format_new = format_clut8;
+
+ if (format_current != format_new) {
+ if (*texture)
+ LOGD("switching pixel format from: %s",
+ getPixelFormatName((*texture)->getPixelFormat()).c_str());
+
+ delete *texture;
+
+ if (format_new == GLES565Texture::getPixelFormat())
+ *texture = new GLES565Texture();
+ else if (format_new == GLES5551Texture::getPixelFormat())
+ *texture = new GLES5551Texture();
+ else if (format_new == GLES4444Texture::getPixelFormat())
+ *texture = new GLES4444Texture();
+ else {
+ // TODO what now?
+ if (format_new != format_clut8)
+ LOGE("unsupported pixel format: %s",
+ getPixelFormatName(format_new).c_str());
+
+ if (alphaPalette)
+ *texture = new GLESPalette8888Texture;
+ else
+ *texture = new GLESPalette888Texture;
+ }
+
+ LOGD("new pixel format: %s",
+ getPixelFormatName((*texture)->getPixelFormat()).c_str());
+ }
+
+ (*texture)->allocBuffer(width, height);
+ (*texture)->fillBuffer(0);
+}
+#endif
+
+void OSystem_Android::initSurface() {
+ LOGD("initializing surface");
+
+ assert(!JNI::haveSurface());
+
+ _screen_changeid = JNI::surface_changeid;
+ _egl_surface_width = JNI::egl_surface_width;
+ _egl_surface_height = JNI::egl_surface_height;
+
+ assert(_egl_surface_width > 0 && _egl_surface_height > 0);
+
+ JNI::initSurface();
+
+ // Initialise OpenGLES context.
+ GLESTexture::initGLExtensions();
+
+ if (_game_texture)
+ _game_texture->reinit();
+
+ if (_overlay_texture)
+ _overlay_texture->reinit();
+
+ if (_mouse_texture)
+ _mouse_texture->reinit();
+}
+
+void OSystem_Android::deinitSurface() {
+ if (!JNI::haveSurface())
+ return;
+
+ LOGD("deinitializing surface");
+
+ _screen_changeid = JNI::surface_changeid;
+ _egl_surface_width = 0;
+ _egl_surface_height = 0;
+
+ // release texture resources
+ if (_game_texture)
+ _game_texture->release();
+
+ if (_overlay_texture)
+ _overlay_texture->release();
+
+ if (_mouse_texture)
+ _mouse_texture->release();
+
+ JNI::deinitSurface();
+}
+
+void OSystem_Android::initViewport() {
+ LOGD("initializing viewport");
+
+ assert(JNI::haveSurface());
+
+ // Turn off anything that looks like 3D ;)
+ GLCALL(glDisable(GL_CULL_FACE));
+ GLCALL(glDisable(GL_DEPTH_TEST));
+ GLCALL(glDisable(GL_LIGHTING));
+ GLCALL(glDisable(GL_FOG));
+ GLCALL(glDisable(GL_DITHER));
+
+ GLCALL(glShadeModel(GL_FLAT));
+ GLCALL(glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST));
+
+ GLCALL(glEnable(GL_BLEND));
+ GLCALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
+
+ GLCALL(glEnableClientState(GL_VERTEX_ARRAY));
+ GLCALL(glEnableClientState(GL_TEXTURE_COORD_ARRAY));
+
+ GLCALL(glEnable(GL_TEXTURE_2D));
+
+ GLCALL(glViewport(0, 0, _egl_surface_width, _egl_surface_height));
+
+ GLCALL(glMatrixMode(GL_PROJECTION));
+ GLCALL(glLoadIdentity());
+ GLCALL(glOrthof(0, _egl_surface_width, _egl_surface_height, 0, -1, 1));
+ GLCALL(glMatrixMode(GL_MODELVIEW));
+ GLCALL(glLoadIdentity());
+
+ clearFocusRectangle();
+}
+
+void OSystem_Android::initSize(uint width, uint height,
+ const Graphics::PixelFormat *format) {
+ ENTER("%d, %d, %p", width, height, format);
+
+ GLTHREADCHECK;
+
+ int overlay_width = _egl_surface_width;
+ int overlay_height = _egl_surface_height;
+
+ // the 'normal' theme layout uses a max height of 400 pixels. if the
+ // surface is too big we use only a quarter of the size so that the widgets
+ // don't get too small. if the surface height has less than 800 pixels, this
+ // enforces the 'lowres' layout, which will be scaled back up by factor 2x,
+ // but this looks way better than the 'normal' layout scaled by some
+ // calculated factors
+ if (overlay_height > 480) {
+ overlay_width /= 2;
+ overlay_height /= 2;
+ }
+
+ LOGI("overlay size is %ux%u", overlay_width, overlay_height);
+
+ _overlay_texture->allocBuffer(overlay_width, overlay_height);
+
+#ifdef USE_RGB_COLOR
+ initTexture(&_game_texture, width, height, format, false);
+#else
+ _game_texture->allocBuffer(width, height);
+ _game_texture->fillBuffer(0);
+#endif
+ // Don't know mouse size yet - it gets reallocated in
+ // setMouseCursor. We need the palette allocated before
+ // setMouseCursor however, so just take a guess at the desired
+ // size (it's small).
+ _mouse_texture_palette->allocBuffer(20, 20);
+
+ // clear screen
+ GLCALL(glClearColorx(0, 0, 0, 1 << 16));
+ GLCALL(glClear(GL_COLOR_BUFFER_BIT));
+ JNI::swapBuffers();
+}
+
+int OSystem_Android::getScreenChangeID() const {
+ return _screen_changeid;
+}
+
+int16 OSystem_Android::getHeight() {
+ return _game_texture->height();
+}
+
+int16 OSystem_Android::getWidth() {
+ return _game_texture->width();
+}
+
+void OSystem_Android::setPalette(const byte *colors, uint start, uint num) {
+ ENTER("%p, %u, %u", colors, start, num);
+
+#ifdef USE_RGB_COLOR
+ assert(_game_texture->getPixelFormat().bytesPerPixel == 1);
+#endif
+
+ GLTHREADCHECK;
+
+ memcpy(((GLESPaletteTexture *)_game_texture)->palette() + start * 3,
+ colors, num * 3);
+
+ if (!_use_mouse_palette)
+ setCursorPaletteInternal(colors, start, num);
+}
+
+void OSystem_Android::grabPalette(byte *colors, uint start, uint num) {
+ ENTER("%p, %u, %u", colors, start, num);
+
+#ifdef USE_RGB_COLOR
+ assert(_game_texture->getPixelFormat().bytesPerPixel == 1);
+#endif
+
+ GLTHREADCHECK;
+
+ memcpy(colors, ((GLESPaletteTexture *)_game_texture)->palette() + start * 3,
+ num * 3);
+}
+
+void OSystem_Android::copyRectToScreen(const byte *buf, int pitch,
+ int x, int y, int w, int h) {
+ ENTER("%p, %d, %d, %d, %d, %d", buf, pitch, x, y, w, h);
+
+ GLTHREADCHECK;
+
+ _game_texture->updateBuffer(x, y, w, h, buf, pitch);
+}
+
+void OSystem_Android::updateScreen() {
+ //ENTER();
+
+ GLTHREADCHECK;
+
+ if (!JNI::haveSurface())
+ return;
+
+ if (!_force_redraw &&
+ !_game_texture->dirty() &&
+ !_overlay_texture->dirty() &&
+ !_mouse_texture->dirty())
+ return;
+
+ _force_redraw = false;
+
+ GLCALL(glPushMatrix());
+
+ if (_shake_offset != 0 ||
+ (!_focus_rect.isEmpty() &&
+ !Common::Rect(_game_texture->width(),
+ _game_texture->height()).contains(_focus_rect))) {
+ // These are the only cases where _game_texture doesn't
+ // cover the entire screen.
+ GLCALL(glClearColorx(0, 0, 0, 1 << 16));
+ GLCALL(glClear(GL_COLOR_BUFFER_BIT));
+
+ // Move everything up by _shake_offset (game) pixels
+ GLCALL(glTranslatex(0, -_shake_offset << 16, 0));
+ }
+
+ if (_focus_rect.isEmpty()) {
+ _game_texture->drawTexture(0, 0, _egl_surface_width,
+ _egl_surface_height);
+ } else {
+ GLCALL(glPushMatrix());
+ GLCALL(glScalex(xdiv(_egl_surface_width, _focus_rect.width()),
+ xdiv(_egl_surface_height, _focus_rect.height()),
+ 1 << 16));
+ GLCALL(glTranslatex(-_focus_rect.left << 16,
+ -_focus_rect.top << 16, 0));
+ GLCALL(glScalex(xdiv(_game_texture->width(), _egl_surface_width),
+ xdiv(_game_texture->height(), _egl_surface_height),
+ 1 << 16));
+
+ _game_texture->drawTexture(0, 0, _egl_surface_width,
+ _egl_surface_height);
+ GLCALL(glPopMatrix());
+ }
+
+ int cs = _mouse_targetscale;
+
+ if (_show_overlay) {
+ // ugly, but the modern theme sets a wacko factor, only god knows why
+ cs = 1;
+
+ GLCALL(_overlay_texture->drawTexture(0, 0, _egl_surface_width,
+ _egl_surface_height));
+ }
+
+ if (_show_mouse) {
+ GLCALL(glPushMatrix());
+
+ // Scale up ScummVM -> OpenGL (pixel) coordinates
+ int texwidth, texheight;
+
+ if (_show_overlay) {
+ texwidth = getOverlayWidth();
+ texheight = getOverlayHeight();
+ } else {
+ texwidth = getWidth();
+ texheight = getHeight();
+ }
+
+ GLCALL(glScalex(xdiv(_egl_surface_width, texwidth),
+ xdiv(_egl_surface_height, texheight),
+ 1 << 16));
+
+ GLCALL(glTranslatex((-_mouse_hotspot.x * cs) << 16,
+ (-_mouse_hotspot.y * cs) << 16,
+ 0));
+
+ // Note the extra half texel to position the mouse in
+ // the middle of the x,y square:
+ const Common::Point& mouse = getEventManager()->getMousePos();
+ GLCALL(glTranslatex((mouse.x << 16) | 1 << 15,
+ (mouse.y << 16) | 1 << 15, 0));
+
+ GLCALL(glScalex(cs << 16, cs << 16, 1 << 16));
+
+ _mouse_texture->drawTexture();
+
+ GLCALL(glPopMatrix());
+ }
+
+ GLCALL(glPopMatrix());
+
+ if (!JNI::swapBuffers())
+ LOGW("swapBuffers failed: 0x%x", glGetError());
+}
+
+Graphics::Surface *OSystem_Android::lockScreen() {
+ ENTER();
+
+ GLTHREADCHECK;
+
+ // TODO this doesn't return any pixel data for non CLUT8
+ Graphics::Surface *surface = _game_texture->surface();
+ assert(surface->pixels);
+
+ return surface;
+}
+
+void OSystem_Android::unlockScreen() {
+ ENTER();
+
+ GLTHREADCHECK;
+
+ assert(_game_texture->dirty());
+}
+
+void OSystem_Android::setShakePos(int shake_offset) {
+ ENTER("%d", shake_offset);
+
+ if (_shake_offset != shake_offset) {
+ _shake_offset = shake_offset;
+ _force_redraw = true;
+ }
+}
+
+void OSystem_Android::fillScreen(uint32 col) {
+ ENTER("%u", col);
+
+ GLTHREADCHECK;
+
+ _game_texture->fillBuffer(col);
+}
+
+void OSystem_Android::setFocusRectangle(const Common::Rect& rect) {
+ ENTER("%d, %d, %d, %d", rect.left, rect.top, rect.right, rect.bottom);
+
+ if (_enable_zoning) {
+ _focus_rect = rect;
+ _force_redraw = true;
+ }
+}
+
+void OSystem_Android::clearFocusRectangle() {
+ ENTER();
+
+ if (_enable_zoning) {
+ _focus_rect = Common::Rect();
+ _force_redraw = true;
+ }
+}
+
+void OSystem_Android::showOverlay() {
+ ENTER();
+
+ _show_overlay = true;
+ _force_redraw = true;
+}
+
+void OSystem_Android::hideOverlay() {
+ ENTER();
+
+ _show_overlay = false;
+ _force_redraw = true;
+}
+
+void OSystem_Android::clearOverlay() {
+ ENTER();
+
+ GLTHREADCHECK;
+
+ _overlay_texture->fillBuffer(0);
+
+ // breaks more than it fixes, disabled for now
+ // Shouldn't need this, but works around a 'blank screen' bug on Nexus1
+ //updateScreen();
+}
+
+void OSystem_Android::grabOverlay(OverlayColor *buf, int pitch) {
+ ENTER("%p, %d", buf, pitch);
+
+ GLTHREADCHECK;
+
+ // We support overlay alpha blending, so the pixel data here
+ // shouldn't actually be used. Let's fill it with zeros, I'm sure
+ // it will be fine...
+ const Graphics::Surface *surface = _overlay_texture->surface_const();
+ assert(surface->bytesPerPixel == sizeof(buf[0]));
+
+ uint h = surface->h;
+
+ do {
+ memset(buf, 0, surface->w * sizeof(buf[0]));
+
+ // This 'pitch' is pixels not bytes
+ buf += pitch;
+ } while (--h);
+}
+
+void OSystem_Android::copyRectToOverlay(const OverlayColor *buf, int pitch,
+ int x, int y, int w, int h) {
+ ENTER("%p, %d, %d, %d, %d, %d", buf, pitch, x, y, w, h);
+
+ GLTHREADCHECK;
+
+ // This 'pitch' is pixels not bytes
+ _overlay_texture->updateBuffer(x, y, w, h, buf, pitch * sizeof(buf[0]));
+
+ // Shouldn't need this, but works around a 'blank screen' bug on Nexus1?
+ //updateScreen();
+}
+
+int16 OSystem_Android::getOverlayHeight() {
+ return _overlay_texture->height();
+}
+
+int16 OSystem_Android::getOverlayWidth() {
+ return _overlay_texture->width();
+}
+
+bool OSystem_Android::showMouse(bool visible) {
+ ENTER("%d", visible);
+
+ _show_mouse = visible;
+
+ return true;
+}
+
+void OSystem_Android::warpMouse(int x, int y) {
+ ENTER("%d, %d", x, y);
+
+ // We use only the eventmanager's idea of the current mouse
+ // position, so there is nothing extra to do here.
+}
+
+void OSystem_Android::setMouseCursor(const byte *buf, uint w, uint h,
+ int hotspotX, int hotspotY,
+ uint32 keycolor, int cursorTargetScale,
+ const Graphics::PixelFormat *format) {
+ ENTER("%p, %u, %u, %d, %d, %u, %d, %p", buf, w, h, hotspotX, hotspotY,
+ keycolor, cursorTargetScale, format);
+
+ GLTHREADCHECK;
+
+ assert(keycolor < 256);
+
+#ifdef USE_RGB_COLOR
+ if (format && format->bytesPerPixel > 1) {
+ if (_mouse_texture != _mouse_texture_rgb)
+ LOGD("switching to rgb mouse cursor");
+
+ _mouse_texture_rgb = new GLES5551Texture();
+ _mouse_texture = _mouse_texture_rgb;
+ } else {
+ if (_mouse_texture != _mouse_texture_palette)
+ LOGD("switching to paletted mouse cursor");
+
+ _mouse_texture = _mouse_texture_palette;
+
+ delete _mouse_texture_rgb;
+ _mouse_texture_rgb = 0;
+ }
+#endif
+
+ _mouse_texture->allocBuffer(w, h);
+
+ if (_mouse_texture == _mouse_texture_palette) {
+ // Update palette alpha based on keycolor
+ byte *palette = _mouse_texture_palette->palette();
+
+ for (uint i = 0; i < 256; ++i, palette += 4)
+ palette[3] = 0xff;
+
+ _mouse_texture_palette->palette()[keycolor * 4 + 3] = 0;
+ }
+
+ if (w == 0 || h == 0)
+ return;
+
+ if (_mouse_texture == _mouse_texture_palette) {
+ _mouse_texture->updateBuffer(0, 0, w, h, buf, w);
+ } else {
+ uint16 pitch = _mouse_texture->pitch();
+
+ byte *tmp = new byte[pitch * h];
+
+ // meh, a 16bit cursor without alpha bits... this is so silly
+ if (!crossBlit(tmp, buf, pitch, w * 2, w, h,
+ _mouse_texture->getPixelFormat(),
+ *format)) {
+ LOGE("crossblit failed");
+
+ delete[] tmp;
+
+ _mouse_texture->fillBuffer(0);
+
+ return;
+ }
+
+ uint16 *s = (uint16 *)buf;
+ uint16 *d = (uint16 *)tmp;
+ for (uint16 y = 0; y < h; ++y, d += pitch / 2 - w)
+ for (uint16 x = 0; x < w; ++x, d++)
+ if (*s++ != (keycolor & 0xffff))
+ *d |= 1;
+
+ _mouse_texture->updateBuffer(0, 0, w, h, tmp, pitch);
+
+ delete[] tmp;
+ }
+
+ _mouse_hotspot = Common::Point(hotspotX, hotspotY);
+ _mouse_targetscale = cursorTargetScale;
+}
+
+void OSystem_Android::setCursorPaletteInternal(const byte *colors,
+ uint start, uint num) {
+ byte *palette = _mouse_texture_palette->palette() + start * 4;
+
+ for (uint i = 0; i < num; ++i, palette += 4, colors += 3) {
+ palette[0] = colors[0];
+ palette[1] = colors[1];
+ palette[2] = colors[2];
+ // Leave alpha untouched to preserve keycolor
+ }
+}
+
+void OSystem_Android::setCursorPalette(const byte *colors,
+ uint start, uint num) {
+ ENTER("%p, %u, %u", colors, start, num);
+
+ GLTHREADCHECK;
+
+ if (_mouse_texture->getPixelFormat().bytesPerPixel != 1) {
+ LOGD("switching to paletted mouse cursor");
+
+ _mouse_texture = _mouse_texture_palette;
+
+ delete _mouse_texture_rgb;
+ _mouse_texture_rgb = 0;
+ }
+
+ setCursorPaletteInternal(colors, start, num);
+ _use_mouse_palette = true;
+}
+
+void OSystem_Android::disableCursorPalette(bool disable) {
+ ENTER("%d", disable);
+
+ // when disabling the cursor palette, and we're running a clut8 game,
+ // it expects the game palette to be used for the cursor
+ if (disable && _game_texture->getPixelFormat().bytesPerPixel == 1) {
+ byte *src = ((GLESPaletteTexture *)_game_texture)->palette();
+ byte *dst = _mouse_texture_palette->palette();
+
+ for (uint i = 0; i < 256; ++i, src += 3, dst += 4) {
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ // Leave alpha untouched to preserve keycolor
+ }
+ }
+
+ _use_mouse_palette = !disable;
+}
+
+#endif
+
diff --git a/backends/platform/android/jni.cpp b/backends/platform/android/jni.cpp
new file mode 100644
index 0000000000..6bfe9c2095
--- /dev/null
+++ b/backends/platform/android/jni.cpp
@@ -0,0 +1,653 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#if defined(__ANDROID__)
+
+#include "base/main.h"
+#include "common/config-manager.h"
+#include "engines/engine.h"
+
+#include "backends/platform/android/android.h"
+#include "backends/platform/android/asset-archive.h"
+#include "backends/platform/android/jni.h"
+
+__attribute__ ((visibility("default")))
+jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
+ return JNI::onLoad(vm);
+}
+
+JavaVM *JNI::_vm = 0;
+jobject JNI::_jobj = 0;
+jobject JNI::_jobj_audio_track = 0;
+jobject JNI::_jobj_egl = 0;
+jobject JNI::_jobj_egl_display = 0;
+jobject JNI::_jobj_egl_surface = 0;
+
+Common::Archive *JNI::_asset_archive = 0;
+OSystem_Android *JNI::_system = 0;
+
+bool JNI::pause = false;
+sem_t JNI::pause_sem = { 0 };
+
+int JNI::surface_changeid = 0;
+int JNI::egl_surface_width = 0;
+int JNI::egl_surface_height = 0;
+bool JNI::_ready_for_events = 0;
+
+jfieldID JNI::_FID_Event_type = 0;
+jfieldID JNI::_FID_Event_synthetic = 0;
+jfieldID JNI::_FID_Event_kbd_keycode = 0;
+jfieldID JNI::_FID_Event_kbd_ascii = 0;
+jfieldID JNI::_FID_Event_kbd_flags = 0;
+jfieldID JNI::_FID_Event_mouse_x = 0;
+jfieldID JNI::_FID_Event_mouse_y = 0;
+jfieldID JNI::_FID_Event_mouse_relative = 0;
+
+jmethodID JNI::_MID_displayMessageOnOSD = 0;
+jmethodID JNI::_MID_setWindowCaption = 0;
+jmethodID JNI::_MID_showVirtualKeyboard = 0;
+jmethodID JNI::_MID_getSysArchives = 0;
+jmethodID JNI::_MID_getPluginDirectories = 0;
+jmethodID JNI::_MID_initSurface = 0;
+jmethodID JNI::_MID_deinitSurface = 0;
+
+jmethodID JNI::_MID_EGL10_eglSwapBuffers = 0;
+
+jmethodID JNI::_MID_AudioTrack_flush = 0;
+jmethodID JNI::_MID_AudioTrack_pause = 0;
+jmethodID JNI::_MID_AudioTrack_play = 0;
+jmethodID JNI::_MID_AudioTrack_stop = 0;
+jmethodID JNI::_MID_AudioTrack_write = 0;
+
+const JNINativeMethod JNI::_natives[] = {
+ { "create", "(Landroid/content/res/AssetManager;"
+ "Ljavax/microedition/khronos/egl/EGL10;"
+ "Ljavax/microedition/khronos/egl/EGLDisplay;"
+ "Landroid/media/AudioTrack;II)V",
+ (void *)JNI::create },
+ { "destroy", "()V",
+ (void *)JNI::destroy },
+ { "setSurface", "(II)V",
+ (void *)JNI::setSurface },
+ { "main", "([Ljava/lang/String;)I",
+ (void *)JNI::main },
+ { "pushEvent", "(Lorg/inodes/gus/scummvm/Event;)V",
+ (void *)JNI::pushEvent },
+ { "enableZoning", "(Z)V",
+ (void *)JNI::enableZoning },
+ { "setPause", "(Z)V",
+ (void *)JNI::setPause }
+};
+
+JNI::JNI() {
+}
+
+JNI::~JNI() {
+}
+
+jint JNI::onLoad(JavaVM *vm) {
+ _vm = vm;
+
+ JNIEnv *env;
+
+ if (_vm->GetEnv((void **)&env, JNI_VERSION_1_2))
+ return JNI_ERR;
+
+ jclass cls = env->FindClass("org/inodes/gus/scummvm/ScummVM");
+ if (cls == 0)
+ return JNI_ERR;
+
+ if (env->RegisterNatives(cls, _natives, ARRAYSIZE(_natives)) < 0)
+ return JNI_ERR;
+
+ jclass event = env->FindClass("org/inodes/gus/scummvm/Event");
+ if (event == 0)
+ return JNI_ERR;
+
+ _FID_Event_type = env->GetFieldID(event, "type", "I");
+ if (_FID_Event_type == 0)
+ return JNI_ERR;
+
+ _FID_Event_synthetic = env->GetFieldID(event, "synthetic", "Z");
+ if (_FID_Event_synthetic == 0)
+ return JNI_ERR;
+
+ _FID_Event_kbd_keycode = env->GetFieldID(event, "kbd_keycode", "I");
+ if (_FID_Event_kbd_keycode == 0)
+ return JNI_ERR;
+
+ _FID_Event_kbd_ascii = env->GetFieldID(event, "kbd_ascii", "I");
+ if (_FID_Event_kbd_ascii == 0)
+ return JNI_ERR;
+
+ _FID_Event_kbd_flags = env->GetFieldID(event, "kbd_flags", "I");
+ if (_FID_Event_kbd_flags == 0)
+ return JNI_ERR;
+
+ _FID_Event_mouse_x = env->GetFieldID(event, "mouse_x", "I");
+ if (_FID_Event_mouse_x == 0)
+ return JNI_ERR;
+
+ _FID_Event_mouse_y = env->GetFieldID(event, "mouse_y", "I");
+ if (_FID_Event_mouse_y == 0)
+ return JNI_ERR;
+
+ _FID_Event_mouse_relative = env->GetFieldID(event, "mouse_relative", "Z");
+ if (_FID_Event_mouse_relative == 0)
+ return JNI_ERR;
+
+ return JNI_VERSION_1_2;
+}
+
+JNIEnv *JNI::getEnv() {
+ JNIEnv *env = 0;
+
+ jint res = _vm->GetEnv((void **)&env, JNI_VERSION_1_2);
+
+ if (res != JNI_OK) {
+ LOGE("GetEnv() failed: %d", res);
+ abort();
+ }
+
+ return env;
+}
+
+void JNI::attachThread() {
+ JNIEnv *env = 0;
+
+ jint res = _vm->AttachCurrentThread(&env, 0);
+
+ if (res != JNI_OK) {
+ LOGE("AttachCurrentThread() failed: %d", res);
+ abort();
+ }
+}
+
+void JNI::detachThread() {
+ jint res = _vm->DetachCurrentThread();
+
+ if (res != JNI_OK) {
+ LOGE("DetachCurrentThread() failed: %d", res);
+ abort();
+ }
+}
+
+void JNI::setReadyForEvents(bool ready) {
+ _ready_for_events = ready;
+}
+
+void JNI::throwByName(JNIEnv *env, const char *name, const char *msg) {
+ jclass cls = env->FindClass(name);
+
+ // if cls is 0, an exception has already been thrown
+ if (cls != 0)
+ env->ThrowNew(cls, msg);
+
+ env->DeleteLocalRef(cls);
+}
+
+void JNI::throwRuntimeException(JNIEnv *env, const char *msg) {
+ throwByName(env, "java/lang/RuntimeException", msg);
+}
+
+// calls to the dark side
+
+void JNI::displayMessageOnOSD(const char *msg) {
+ JNIEnv *env = JNI::getEnv();
+ jstring java_msg = env->NewStringUTF(msg);
+
+ env->CallVoidMethod(_jobj, _MID_displayMessageOnOSD, java_msg);
+
+ if (env->ExceptionCheck()) {
+ LOGE("Failed to display OSD message");
+
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+
+ env->DeleteLocalRef(java_msg);
+}
+
+void JNI::setWindowCaption(const char *caption) {
+ JNIEnv *env = JNI::getEnv();
+ jstring java_caption = env->NewStringUTF(caption);
+
+ env->CallVoidMethod(_jobj, _MID_setWindowCaption, java_caption);
+
+ if (env->ExceptionCheck()) {
+ LOGE("Failed to set window caption");
+
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+
+ env->DeleteLocalRef(java_caption);
+}
+
+void JNI::showVirtualKeyboard(bool enable) {
+ JNIEnv *env = JNI::getEnv();
+
+ env->CallVoidMethod(_jobj, _MID_showVirtualKeyboard, enable);
+
+ if (env->ExceptionCheck()) {
+ LOGE("Error trying to show virtual keyboard");
+
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+}
+
+void JNI::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) {
+ JNIEnv *env = JNI::getEnv();
+
+ s.add("ASSET", _asset_archive, priority, false);
+
+ jobjectArray array =
+ (jobjectArray)env->CallObjectMethod(_jobj, _MID_getSysArchives);
+
+ if (env->ExceptionCheck()) {
+ LOGE("Error finding system archive path");
+
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+
+ return;
+ }
+
+ jsize size = env->GetArrayLength(array);
+ for (jsize i = 0; i < size; ++i) {
+ jstring path_obj = (jstring)env->GetObjectArrayElement(array, i);
+ const char *path = env->GetStringUTFChars(path_obj, 0);
+
+ if (path != 0) {
+ s.addDirectory(path, path, priority);
+ env->ReleaseStringUTFChars(path_obj, path);
+ }
+
+ env->DeleteLocalRef(path_obj);
+ }
+}
+
+void JNI::getPluginDirectories(Common::FSList &dirs) {
+ JNIEnv *env = JNI::getEnv();
+
+ jobjectArray array =
+ (jobjectArray)env->CallObjectMethod(_jobj, _MID_getPluginDirectories);
+
+ if (env->ExceptionCheck()) {
+ LOGE("Error finding plugin directories");
+
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+
+ return;
+ }
+
+ jsize size = env->GetArrayLength(array);
+ for (jsize i = 0; i < size; ++i) {
+ jstring path_obj = (jstring)env->GetObjectArrayElement(array, i);
+
+ if (path_obj == 0)
+ continue;
+
+ const char *path = env->GetStringUTFChars(path_obj, 0);
+
+ if (path == 0) {
+ LOGE("Error getting string characters from plugin directory");
+
+ env->ExceptionClear();
+ env->DeleteLocalRef(path_obj);
+
+ continue;
+ }
+
+ dirs.push_back(Common::FSNode(path));
+
+ env->ReleaseStringUTFChars(path_obj, path);
+ env->DeleteLocalRef(path_obj);
+ }
+}
+
+bool JNI::initSurface() {
+ JNIEnv *env = JNI::getEnv();
+
+ jobject obj = env->CallObjectMethod(_jobj, _MID_initSurface);
+
+ if (!obj || env->ExceptionCheck()) {
+ LOGE("initSurface failed");
+
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+
+ return false;
+ }
+
+ _jobj_egl_surface = env->NewGlobalRef(obj);
+
+ return true;
+}
+
+void JNI::deinitSurface() {
+ JNIEnv *env = JNI::getEnv();
+
+ env->CallVoidMethod(_jobj, _MID_deinitSurface);
+
+ if (env->ExceptionCheck()) {
+ LOGE("deinitSurface failed");
+
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+
+ env->DeleteGlobalRef(_jobj_egl_surface);
+ _jobj_egl_surface = 0;
+}
+
+void JNI::setAudioPause() {
+ JNIEnv *env = JNI::getEnv();
+
+ env->CallVoidMethod(_jobj_audio_track, _MID_AudioTrack_flush);
+
+ if (env->ExceptionCheck()) {
+ LOGE("Error flushing AudioTrack");
+
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+
+ env->CallVoidMethod(_jobj_audio_track, _MID_AudioTrack_pause);
+
+ if (env->ExceptionCheck()) {
+ LOGE("Error setting AudioTrack: pause");
+
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+}
+
+void JNI::setAudioPlay() {
+ JNIEnv *env = JNI::getEnv();
+
+ env->CallVoidMethod(_jobj_audio_track, _MID_AudioTrack_play);
+
+ if (env->ExceptionCheck()) {
+ LOGE("Error setting AudioTrack: play");
+
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+}
+
+void JNI::setAudioStop() {
+ JNIEnv *env = JNI::getEnv();
+
+ env->CallVoidMethod(_jobj_audio_track, _MID_AudioTrack_stop);
+
+ if (env->ExceptionCheck()) {
+ LOGE("Error setting AudioTrack: stop");
+
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+}
+
+// natives for the dark side
+
+void JNI::create(JNIEnv *env, jobject self, jobject asset_manager,
+ jobject egl, jobject egl_display,
+ jobject at, jint audio_sample_rate, jint audio_buffer_size) {
+ assert(!_system);
+
+ pause = false;
+ // initial value of zero!
+ sem_init(&pause_sem, 0, 0);
+
+ _asset_archive = new AndroidAssetArchive(asset_manager);
+ assert(_asset_archive);
+
+ _system = new OSystem_Android(audio_sample_rate, audio_buffer_size);
+ assert(_system);
+
+ // weak global ref to allow class to be unloaded
+ // ... except dalvik implements NewWeakGlobalRef only on froyo
+ //_jobj = env->NewWeakGlobalRef(self);
+
+ _jobj = env->NewGlobalRef(self);
+
+ jclass cls = env->GetObjectClass(_jobj);
+
+#define FIND_METHOD(prefix, name, signature) do { \
+ _MID_ ## prefix ## name = env->GetMethodID(cls, #name, signature); \
+ if (_MID_ ## prefix ## name == 0) \
+ return; \
+ } while (0)
+
+ FIND_METHOD(, setWindowCaption, "(Ljava/lang/String;)V");
+ FIND_METHOD(, displayMessageOnOSD, "(Ljava/lang/String;)V");
+ FIND_METHOD(, showVirtualKeyboard, "(Z)V");
+ FIND_METHOD(, getSysArchives, "()[Ljava/lang/String;");
+ FIND_METHOD(, getPluginDirectories, "()[Ljava/lang/String;");
+ FIND_METHOD(, initSurface, "()Ljavax/microedition/khronos/egl/EGLSurface;");
+ FIND_METHOD(, deinitSurface, "()V");
+
+ _jobj_egl = env->NewGlobalRef(egl);
+ _jobj_egl_display = env->NewGlobalRef(egl_display);
+
+ cls = env->GetObjectClass(_jobj_egl);
+
+ FIND_METHOD(EGL10_, eglSwapBuffers,
+ "(Ljavax/microedition/khronos/egl/EGLDisplay;"
+ "Ljavax/microedition/khronos/egl/EGLSurface;)Z");
+
+ _jobj_audio_track = env->NewGlobalRef(at);
+
+ cls = env->GetObjectClass(_jobj_audio_track);
+
+ FIND_METHOD(AudioTrack_, flush, "()V");
+ FIND_METHOD(AudioTrack_, pause, "()V");
+ FIND_METHOD(AudioTrack_, play, "()V");
+ FIND_METHOD(AudioTrack_, stop, "()V");
+ FIND_METHOD(AudioTrack_, write, "([BII)I");
+
+#undef FIND_METHOD
+
+ g_system = _system;
+}
+
+void JNI::destroy(JNIEnv *env, jobject self) {
+ delete _asset_archive;
+ _asset_archive = 0;
+
+ delete _system;
+ g_system = 0;
+ _system = 0;
+
+ sem_destroy(&pause_sem);
+
+ // see above
+ //JNI::getEnv()->DeleteWeakGlobalRef(_jobj);
+
+ JNI::getEnv()->DeleteGlobalRef(_jobj_egl_display);
+ JNI::getEnv()->DeleteGlobalRef(_jobj_egl);
+ JNI::getEnv()->DeleteGlobalRef(_jobj_audio_track);
+ JNI::getEnv()->DeleteGlobalRef(_jobj);
+}
+
+void JNI::setSurface(JNIEnv *env, jobject self, jint width, jint height) {
+ egl_surface_width = width;
+ egl_surface_height = height;
+ surface_changeid++;
+}
+
+jint JNI::main(JNIEnv *env, jobject self, jobjectArray args) {
+ assert(_system);
+
+ const int MAX_NARGS = 32;
+ int res = -1;
+
+ int argc = env->GetArrayLength(args);
+ if (argc > MAX_NARGS) {
+ throwByName(env, "java/lang/IllegalArgumentException",
+ "too many arguments");
+ return 0;
+ }
+
+ char *argv[MAX_NARGS];
+
+ // note use in cleanup loop below
+ int nargs;
+
+ for (nargs = 0; nargs < argc; ++nargs) {
+ jstring arg = (jstring)env->GetObjectArrayElement(args, nargs);
+
+ if (arg == 0) {
+ argv[nargs] = 0;
+ } else {
+ const char *cstr = env->GetStringUTFChars(arg, 0);
+
+ argv[nargs] = const_cast<char *>(cstr);
+
+ // exception already thrown?
+ if (cstr == 0)
+ goto cleanup;
+ }
+
+ env->DeleteLocalRef(arg);
+ }
+
+#ifdef DYNAMIC_MODULES
+ PluginManager::instance().addPluginProvider(new AndroidPluginProvider());
+#endif
+
+ LOGI("Entering scummvm_main with %d args", argc);
+
+ res = scummvm_main(argc, argv);
+
+ LOGI("scummvm_main exited with code %d", res);
+
+ _system->quit();
+
+cleanup:
+ nargs--;
+
+ for (int i = 0; i < nargs; ++i) {
+ if (argv[i] == 0)
+ continue;
+
+ jstring arg = (jstring)env->GetObjectArrayElement(args, nargs);
+
+ // Exception already thrown?
+ if (arg == 0)
+ return res;
+
+ env->ReleaseStringUTFChars(arg, argv[i]);
+ env->DeleteLocalRef(arg);
+ }
+
+ return res;
+}
+
+void JNI::pushEvent(JNIEnv *env, jobject self, jobject java_event) {
+ // drop events until we're ready and after we quit
+ if (!_ready_for_events)
+ return;
+
+ assert(_system);
+
+ Common::Event event;
+ event.type = (Common::EventType)env->GetIntField(java_event,
+ _FID_Event_type);
+
+ event.synthetic =
+ env->GetBooleanField(java_event, _FID_Event_synthetic);
+
+ switch (event.type) {
+ case Common::EVENT_KEYDOWN:
+ case Common::EVENT_KEYUP:
+ event.kbd.keycode = (Common::KeyCode)env->GetIntField(
+ java_event, _FID_Event_kbd_keycode);
+ event.kbd.ascii = static_cast<int>(env->GetIntField(
+ java_event, _FID_Event_kbd_ascii));
+ event.kbd.flags = static_cast<int>(env->GetIntField(
+ java_event, _FID_Event_kbd_flags));
+ break;
+ case Common::EVENT_MOUSEMOVE:
+ case Common::EVENT_LBUTTONDOWN:
+ case Common::EVENT_LBUTTONUP:
+ case Common::EVENT_RBUTTONDOWN:
+ case Common::EVENT_RBUTTONUP:
+ case Common::EVENT_WHEELUP:
+ case Common::EVENT_WHEELDOWN:
+ case Common::EVENT_MBUTTONDOWN:
+ case Common::EVENT_MBUTTONUP:
+ event.mouse.x =
+ env->GetIntField(java_event, _FID_Event_mouse_x);
+ event.mouse.y =
+ env->GetIntField(java_event, _FID_Event_mouse_y);
+ // This is a terrible hack. We stash "relativeness"
+ // in the kbd.flags field until pollEvent() can work
+ // it out.
+ event.kbd.flags = env->GetBooleanField(
+ java_event, _FID_Event_mouse_relative) ? 1 : 0;
+ break;
+ default:
+ break;
+ }
+
+ _system->pushEvent(event);
+}
+
+void JNI::enableZoning(JNIEnv *env, jobject self, jboolean enable) {
+ assert(_system);
+
+ _system->enableZoning(enable);
+}
+
+void JNI::setPause(JNIEnv *env, jobject self, jboolean value) {
+ if (!_system)
+ return;
+
+ if (g_engine) {
+ LOGD("pauseEngine: %d", value);
+
+ g_engine->pauseEngine(value);
+
+ if (value &&
+ g_engine->hasFeature(Engine::kSupportsSavingDuringRuntime) &&
+ g_engine->canSaveGameStateCurrently())
+ g_engine->saveGameState(0, "Android parachute");
+ }
+
+ pause = value;
+
+ if (!pause) {
+ // wake up all threads
+ for (uint i = 0; i < 3; ++i)
+ sem_post(&pause_sem);
+ }
+}
+
+#endif
+
diff --git a/backends/platform/android/jni.h b/backends/platform/android/jni.h
new file mode 100644
index 0000000000..146938636d
--- /dev/null
+++ b/backends/platform/android/jni.h
@@ -0,0 +1,158 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef _ANDROID_JNI_H_
+#define _ANDROID_JNI_H_
+
+#if defined(__ANDROID__)
+
+#include <jni.h>
+#include <semaphore.h>
+
+#include "common/fs.h"
+#include "common/archive.h"
+
+class OSystem_Android;
+
+class JNI {
+private:
+ JNI();
+ virtual ~JNI();
+
+public:
+ static bool pause;
+ static sem_t pause_sem;
+
+ static int surface_changeid;
+ static int egl_surface_width;
+ static int egl_surface_height;
+
+ static jint onLoad(JavaVM *vm);
+
+ static JNIEnv *getEnv();
+
+ static void attachThread();
+ static void detachThread();
+
+ static void setReadyForEvents(bool ready);
+
+ static void getPluginDirectories(Common::FSList &dirs);
+ static void setWindowCaption(const char *caption);
+ static void displayMessageOnOSD(const char *msg);
+ static void showVirtualKeyboard(bool enable);
+ static void addSysArchivesToSearchSet(Common::SearchSet &s, int priority);
+
+ static inline bool haveSurface();
+ static inline bool swapBuffers();
+ static bool initSurface();
+ static void deinitSurface();
+
+ static void setAudioPause();
+ static void setAudioPlay();
+ static void setAudioStop();
+
+ static inline int writeAudio(JNIEnv *env, jbyteArray &data, int offset,
+ int size);
+
+private:
+ static JavaVM *_vm;
+ // back pointer to (java) peer instance
+ static jobject _jobj;
+ static jobject _jobj_audio_track;
+ static jobject _jobj_egl;
+ static jobject _jobj_egl_display;
+ static jobject _jobj_egl_surface;
+
+ static Common::Archive *_asset_archive;
+ static OSystem_Android *_system;
+
+ static bool _ready_for_events;
+
+ static jfieldID _FID_Event_type;
+ static jfieldID _FID_Event_synthetic;
+ static jfieldID _FID_Event_kbd_keycode;
+ static jfieldID _FID_Event_kbd_ascii;
+ static jfieldID _FID_Event_kbd_flags;
+ static jfieldID _FID_Event_mouse_x;
+ static jfieldID _FID_Event_mouse_y;
+ static jfieldID _FID_Event_mouse_relative;
+ static jfieldID _FID_ScummVM_nativeScummVM;
+
+ static jmethodID _MID_displayMessageOnOSD;
+ static jmethodID _MID_setWindowCaption;
+ static jmethodID _MID_showVirtualKeyboard;
+ static jmethodID _MID_getSysArchives;
+ static jmethodID _MID_getPluginDirectories;
+ static jmethodID _MID_initSurface;
+ static jmethodID _MID_deinitSurface;
+
+ static jmethodID _MID_EGL10_eglSwapBuffers;
+
+ static jmethodID _MID_AudioTrack_flush;
+ static jmethodID _MID_AudioTrack_pause;
+ static jmethodID _MID_AudioTrack_play;
+ static jmethodID _MID_AudioTrack_stop;
+ static jmethodID _MID_AudioTrack_write;
+
+ static const JNINativeMethod _natives[];
+
+ static void throwByName(JNIEnv *env, const char *name, const char *msg);
+ static void throwRuntimeException(JNIEnv *env, const char *msg);
+
+ // natives for the dark side
+ static void create(JNIEnv *env, jobject self, jobject asset_manager,
+ jobject egl, jobject egl_display,
+ jobject at, jint audio_sample_rate,
+ jint audio_buffer_size);
+ static void destroy(JNIEnv *env, jobject self);
+
+ static void setSurface(JNIEnv *env, jobject self, jint width, jint height);
+ static jint main(JNIEnv *env, jobject self, jobjectArray args);
+
+ static void pushEvent(JNIEnv *env, jobject self, jobject java_event);
+ static void enableZoning(JNIEnv *env, jobject self, jboolean enable);
+
+ static void setPause(JNIEnv *env, jobject self, jboolean value);
+};
+
+inline bool JNI::haveSurface() {
+ return _jobj_egl_surface != 0;
+}
+
+inline bool JNI::swapBuffers() {
+ JNIEnv *env = JNI::getEnv();
+
+ return env->CallBooleanMethod(_jobj_egl, _MID_EGL10_eglSwapBuffers,
+ _jobj_egl_display, _jobj_egl_surface);
+}
+
+inline int JNI::writeAudio(JNIEnv *env, jbyteArray &data, int offset, int size) {
+ return env->CallIntMethod(_jobj_audio_track, _MID_AudioTrack_write, data,
+ offset, size);
+}
+
+#endif
+#endif
+
diff --git a/backends/platform/android/module.mk b/backends/platform/android/module.mk
index 8b120b21ff..3bcfa7ad87 100644
--- a/backends/platform/android/module.mk
+++ b/backends/platform/android/module.mk
@@ -1,9 +1,11 @@
MODULE := backends/platform/android
MODULE_OBJS := \
- android.o \
+ jni.o \
+ texture.o \
asset-archive.o \
- video.o
+ android.o \
+ gfx.o
# We don't use rules.mk but rather manually update OBJS and MODULE_DIRS.
MODULE_OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS))
diff --git a/backends/platform/android/org/inodes/gus/scummvm/EditableSurfaceView.java b/backends/platform/android/org/inodes/gus/scummvm/EditableSurfaceView.java
index 5b71d4a3a5..cede7eedd4 100644
--- a/backends/platform/android/org/inodes/gus/scummvm/EditableSurfaceView.java
+++ b/backends/platform/android/org/inodes/gus/scummvm/EditableSurfaceView.java
@@ -19,13 +19,13 @@ public class EditableSurfaceView extends SurfaceView {
}
public EditableSurfaceView(Context context, AttributeSet attrs,
- int defStyle) {
+ int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean onCheckIsTextEditor() {
- return true;
+ return false;
}
private class MyInputConnection extends BaseInputConnection {
@@ -40,7 +40,9 @@ public class EditableSurfaceView extends SurfaceView {
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getWindowToken(), 0);
}
- return super.performEditorAction(actionCode); // Sends enter key
+
+ // Sends enter key
+ return super.performEditorAction(actionCode);
}
}
@@ -49,11 +51,12 @@ public class EditableSurfaceView extends SurfaceView {
outAttrs.initialCapsMode = 0;
outAttrs.initialSelEnd = outAttrs.initialSelStart = -1;
outAttrs.inputType = (InputType.TYPE_CLASS_TEXT |
- InputType.TYPE_TEXT_VARIATION_NORMAL |
- InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE);
+ InputType.TYPE_TEXT_VARIATION_NORMAL |
+ InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE);
outAttrs.imeOptions = (EditorInfo.IME_ACTION_DONE |
- EditorInfo.IME_FLAG_NO_EXTRACT_UI);
+ EditorInfo.IME_FLAG_NO_EXTRACT_UI);
return new MyInputConnection();
}
}
+
diff --git a/backends/platform/android/org/inodes/gus/scummvm/PluginProvider.java b/backends/platform/android/org/inodes/gus/scummvm/PluginProvider.java
index c94ab0a3ff..3c91d9f5dc 100644
--- a/backends/platform/android/org/inodes/gus/scummvm/PluginProvider.java
+++ b/backends/platform/android/org/inodes/gus/scummvm/PluginProvider.java
@@ -28,7 +28,7 @@ public class PluginProvider extends BroadcastReceiver {
try {
info = context.getPackageManager()
.getReceiverInfo(new ComponentName(context, this.getClass()),
- PackageManager.GET_META_DATA);
+ PackageManager.GET_META_DATA);
} catch (PackageManager.NameNotFoundException e) {
Log.e(LOG_TAG, "Error finding my own info?", e);
return;
@@ -38,17 +38,17 @@ public class PluginProvider extends BroadcastReceiver {
if (mylib != null) {
ArrayList<String> all_libs =
extras.getStringArrayList(ScummVMApplication.EXTRA_UNPACK_LIBS);
-
all_libs.add(new Uri.Builder()
- .scheme("plugin")
- .authority(context.getPackageName())
- .path(mylib)
- .toString());
+ .scheme("plugin")
+ .authority(context.getPackageName())
+ .path(mylib)
+ .toString());
extras.putStringArrayList(ScummVMApplication.EXTRA_UNPACK_LIBS,
- all_libs);
+ all_libs);
}
setResultExtras(extras);
}
}
+
diff --git a/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java b/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java
index 0e905f43a5..f263b89015 100644
--- a/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java
+++ b/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java
@@ -1,18 +1,12 @@
package org.inodes.gus.scummvm;
-import android.content.Context;
+import android.util.Log;
import android.content.res.AssetManager;
+import android.view.SurfaceHolder;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Process;
-import android.util.Log;
-import android.view.Surface;
-import android.view.SurfaceHolder;
-import javax.microedition.khronos.opengles.GL;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGL11;
@@ -22,58 +16,269 @@ import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
import java.io.File;
-import java.util.concurrent.Semaphore;
import java.util.Map;
import java.util.LinkedHashMap;
+public abstract class ScummVM implements SurfaceHolder.Callback, Runnable {
+ final protected static String LOG_TAG = "ScummVM";
+ final private AssetManager _asset_manager;
+ final private Object _sem_surface;
+
+ private EGL10 _egl;
+ private EGLDisplay _egl_display = EGL10.EGL_NO_DISPLAY;
+ private EGLConfig _egl_config;
+ private EGLContext _egl_context = EGL10.EGL_NO_CONTEXT;
+ private EGLSurface _egl_surface = EGL10.EGL_NO_SURFACE;
+
+ private SurfaceHolder _surface_holder;
+ private AudioTrack _audio_track;
+ private int _sample_rate = 0;
+ private int _buffer_size = 0;
+
+ private String[] _args;
+
+ final private native void create(AssetManager _asset_manager,
+ EGL10 egl, EGLDisplay egl_display,
+ AudioTrack audio_track,
+ int sample_rate, int buffer_size);
+ final private native void destroy();
+ final private native void setSurface(int width, int height);
+ final private native int main(String[] args);
+
+ // pause the engine and all native threads
+ final public native void setPause(boolean pause);
+ final public native void enableZoning(boolean enable);
+ // Feed an event to ScummVM. Safe to call from other threads.
+ final public native void pushEvent(Event e);
+
+ // Callbacks from C++ peer instance
+ abstract protected void displayMessageOnOSD(String msg);
+ abstract protected void setWindowCaption(String caption);
+ abstract protected String[] getPluginDirectories();
+ abstract protected void showVirtualKeyboard(boolean enable);
+ abstract protected String[] getSysArchives();
-// At least in Android 2.1, eglCreateWindowSurface() requires an
-// EGLNativeWindowSurface object, which is hidden deep in the bowels
-// of libui. Until EGL is properly exposed, it's probably safer to
-// use the Java versions of most EGL functions :(
+ public ScummVM(AssetManager asset_manager, SurfaceHolder holder) {
+ _asset_manager = asset_manager;
+ _sem_surface = new Object();
-public class ScummVM implements SurfaceHolder.Callback {
- protected final static String LOG_TAG = "ScummVM";
+ holder.addCallback(this);
+ }
- private final int AUDIO_FRAME_SIZE = 2 * 2; // bytes. 16bit audio * stereo
- public static class AudioSetupException extends Exception {}
+ // SurfaceHolder callback
+ final public void surfaceCreated(SurfaceHolder holder) {
+ Log.d(LOG_TAG, "surfaceCreated");
- private long nativeScummVM; // native code hangs itself here
- boolean scummVMRunning = false;
+ // no need to do anything, surfaceChanged() will be called in any case
+ }
- private native void create(AssetManager am);
+ // SurfaceHolder callback
+ final public void surfaceChanged(SurfaceHolder holder, int format,
+ int width, int height) {
+ Log.d(LOG_TAG, String.format("surfaceChanged: %dx%d (%d)",
+ width, height, format));
- public ScummVM(Context context) {
- create(context.getAssets()); // Init C++ code, set nativeScummVM
+ synchronized(_sem_surface) {
+ _surface_holder = holder;
+ _sem_surface.notifyAll();
+ }
+
+ // store values for the native code
+ setSurface(width, height);
}
- private native void nativeDestroy();
+ // SurfaceHolder callback
+ final public void surfaceDestroyed(SurfaceHolder holder) {
+ Log.d(LOG_TAG, "surfaceDestroyed");
- public synchronized void destroy() {
- if (nativeScummVM != 0) {
- nativeDestroy();
- nativeScummVM = 0;
+ synchronized(_sem_surface) {
+ _surface_holder = null;
+ _sem_surface.notifyAll();
}
+
+ // clear values for the native code
+ setSurface(0, 0);
}
- protected void finalize() {
+
+ final public void setArgs(String[] args) {
+ _args = args;
+ }
+
+ final public void run() {
+ try {
+ initAudio();
+ initEGL();
+
+ // wait for the surfaceChanged callback
+ synchronized(_sem_surface) {
+ while (_surface_holder == null)
+ _sem_surface.wait();
+ }
+ } catch (Exception e) {
+ deinitEGL();
+ deinitAudio();
+
+ throw new RuntimeException("Error preparing the ScummVM thread", e);
+ }
+
+ create(_asset_manager, _egl, _egl_display,
+ _audio_track, _sample_rate, _buffer_size);
+
+ int res = main(_args);
+
destroy();
+
+ deinitEGL();
+ deinitAudio();
+
+ // On exit, tear everything down for a fresh restart next time.
+ System.exit(res);
+ }
+
+ final private void initEGL() throws Exception {
+ _egl = (EGL10)EGLContext.getEGL();
+ _egl_display = _egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+
+ int[] version = new int[2];
+ _egl.eglInitialize(_egl_display, version);
+
+ int[] num_config = new int[1];
+ _egl.eglChooseConfig(_egl_display, configSpec, null, 0, num_config);
+
+ final int numConfigs = num_config[0];
+
+ if (numConfigs <= 0)
+ throw new IllegalArgumentException("No configs match configSpec");
+
+ EGLConfig[] configs = new EGLConfig[numConfigs];
+ _egl.eglChooseConfig(_egl_display, configSpec, configs, numConfigs,
+ num_config);
+
+ if (false) {
+ Log.d(LOG_TAG, String.format("Found %d EGL configurations.",
+ numConfigs));
+ for (EGLConfig config : configs)
+ dumpEglConfig(config);
+ }
+
+ // Android's eglChooseConfig is busted in several versions and
+ // devices so we have to filter/rank the configs again ourselves.
+ _egl_config = chooseEglConfig(configs);
+
+ if (false) {
+ Log.d(LOG_TAG, String.format("Chose from %d EGL configs",
+ numConfigs));
+ dumpEglConfig(_egl_config);
+ }
+
+ _egl_context = _egl.eglCreateContext(_egl_display, _egl_config,
+ EGL10.EGL_NO_CONTEXT, null);
+
+ if (_egl_context == EGL10.EGL_NO_CONTEXT)
+ throw new Exception(String.format("Failed to create context: 0x%x",
+ _egl.eglGetError()));
+ }
+
+ // Callback from C++ peer instance
+ final protected EGLSurface initSurface() throws Exception {
+ _egl_surface = _egl.eglCreateWindowSurface(_egl_display, _egl_config,
+ _surface_holder, null);
+
+ if (_egl_surface == EGL10.EGL_NO_SURFACE)
+ throw new Exception(String.format(
+ "eglCreateWindowSurface failed: 0x%x", _egl.eglGetError()));
+
+ _egl.eglMakeCurrent(_egl_display, _egl_surface, _egl_surface,
+ _egl_context);
+
+ GL10 gl = (GL10)_egl_context.getGL();
+
+ Log.i(LOG_TAG, String.format("Using EGL %s (%s); GL %s/%s (%s)",
+ _egl.eglQueryString(_egl_display, EGL10.EGL_VERSION),
+ _egl.eglQueryString(_egl_display, EGL10.EGL_VENDOR),
+ gl.glGetString(GL10.GL_VERSION),
+ gl.glGetString(GL10.GL_RENDERER),
+ gl.glGetString(GL10.GL_VENDOR)));
+
+ return _egl_surface;
+ }
+
+ // Callback from C++ peer instance
+ final protected void deinitSurface() {
+ if (_egl_display != EGL10.EGL_NO_DISPLAY) {
+ _egl.eglMakeCurrent(_egl_display, EGL10.EGL_NO_SURFACE,
+ EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
+
+ if (_egl_surface != EGL10.EGL_NO_SURFACE)
+ _egl.eglDestroySurface(_egl_display, _egl_surface);
+ }
+
+ _egl_surface = EGL10.EGL_NO_SURFACE;
+ }
+
+ final private void deinitEGL() {
+ if (_egl_display != EGL10.EGL_NO_DISPLAY) {
+ _egl.eglMakeCurrent(_egl_display, EGL10.EGL_NO_SURFACE,
+ EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
+
+ if (_egl_surface != EGL10.EGL_NO_SURFACE)
+ _egl.eglDestroySurface(_egl_display, _egl_surface);
+
+ if (_egl_context != EGL10.EGL_NO_CONTEXT)
+ _egl.eglDestroyContext(_egl_display, _egl_context);
+
+ _egl.eglTerminate(_egl_display);
+ }
+
+ _egl_surface = EGL10.EGL_NO_SURFACE;
+ _egl_context = EGL10.EGL_NO_CONTEXT;
+ _egl_config = null;
+ _egl_display = EGL10.EGL_NO_DISPLAY;
+ _egl = null;
+ }
+
+ final private void initAudio() throws Exception {
+ _sample_rate = AudioTrack.getNativeOutputSampleRate(
+ AudioManager.STREAM_MUSIC);
+ _buffer_size = AudioTrack.getMinBufferSize(_sample_rate,
+ AudioFormat.CHANNEL_CONFIGURATION_STEREO,
+ AudioFormat.ENCODING_PCM_16BIT);
+
+ // ~100ms
+ int buffer_size_want = (_sample_rate * 2 * 2 / 10) & ~1023;
+
+ if (_buffer_size < buffer_size_want) {
+ Log.w(LOG_TAG, String.format(
+ "adjusting audio buffer size (was: %d)", _buffer_size));
+
+ _buffer_size = buffer_size_want;
+ }
+
+ Log.i(LOG_TAG, String.format("Using %d bytes buffer for %dHz audio",
+ _buffer_size, _sample_rate));
+
+ _audio_track = new AudioTrack(AudioManager.STREAM_MUSIC,
+ _sample_rate,
+ AudioFormat.CHANNEL_CONFIGURATION_STEREO,
+ AudioFormat.ENCODING_PCM_16BIT,
+ _buffer_size,
+ AudioTrack.MODE_STREAM);
+
+ if (_audio_track.getState() != AudioTrack.STATE_INITIALIZED)
+ throw new Exception(
+ String.format("Error initialising AudioTrack: %d",
+ _audio_track.getState()));
+ }
+
+ final private void deinitAudio() {
+ if (_audio_track != null)
+ _audio_track.stop();
+
+ _audio_track = null;
+ _buffer_size = 0;
+ _sample_rate = 0;
}
- // Surface creation:
- // GUI thread: create surface, release lock
- // ScummVM thread: acquire lock (block), read surface
- //
- // Surface deletion:
- // GUI thread: post event, acquire lock (block), return
- // ScummVM thread: read event, free surface, release lock
- //
- // In other words, ScummVM thread does this:
- // acquire lock
- // setup surface
- // when SCREEN_CHANGED arrives:
- // destroy surface
- // release lock
- // back to acquire lock
static final int configSpec[] = {
EGL10.EGL_RED_SIZE, 5,
EGL10.EGL_GREEN_SIZE, 5,
@@ -82,35 +287,10 @@ public class ScummVM implements SurfaceHolder.Callback {
EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT,
EGL10.EGL_NONE,
};
- EGL10 egl;
- EGLDisplay eglDisplay = EGL10.EGL_NO_DISPLAY;
- EGLConfig eglConfig;
- EGLContext eglContext = EGL10.EGL_NO_CONTEXT;
- EGLSurface eglSurface = EGL10.EGL_NO_SURFACE;
- Semaphore surfaceLock = new Semaphore(0, true);
- SurfaceHolder nativeSurface;
-
- public void surfaceCreated(SurfaceHolder holder) {
- nativeSurface = holder;
- surfaceLock.release();
- }
-
- public void surfaceChanged(SurfaceHolder holder, int format,
- int width, int height) {
- // Disabled while I debug GL problems
- pushEvent(new Event(Event.EVENT_SCREEN_CHANGED));
- }
-
- public void surfaceDestroyed(SurfaceHolder holder) {
- try {
- surfaceLock.acquire();
- } catch (InterruptedException e) {
- Log.e(LOG_TAG, "Interrupted while waiting for surface lock", e);
- }
- }
// For debugging
private static final Map<String, Integer> attribs;
+
static {
attribs = new LinkedHashMap<String, Integer>();
attribs.put("CONFIG_ID", EGL10.EGL_CONFIG_ID);
@@ -141,11 +321,14 @@ public class ScummVM implements SurfaceHolder.Callback {
attribs.put("TRANSPARENT_GREEN_VALUE", EGL10.EGL_TRANSPARENT_GREEN_VALUE);
attribs.put("TRANSPARENT_BLUE_VALUE", EGL10.EGL_TRANSPARENT_BLUE_VALUE);
}
- private void dumpEglConfig(EGLConfig config) {
+
+ final private void dumpEglConfig(EGLConfig config) {
int[] value = new int[1];
+
for (Map.Entry<String, Integer> entry : attribs.entrySet()) {
- egl.eglGetConfigAttrib(eglDisplay, config,
- entry.getValue(), value);
+ _egl.eglGetConfigAttrib(_egl_display, config,
+ entry.getValue(), value);
+
if (value[0] == EGL10.EGL_NONE)
Log.d(LOG_TAG, entry.getKey() + ": NONE");
else
@@ -153,44 +336,7 @@ public class ScummVM implements SurfaceHolder.Callback {
}
}
- // Called by ScummVM thread (from initBackend)
- private void createScummVMGLContext() {
- egl = (EGL10)EGLContext.getEGL();
- eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
- int[] version = new int[2];
- egl.eglInitialize(eglDisplay, version);
- int[] num_config = new int[1];
- egl.eglChooseConfig(eglDisplay, configSpec, null, 0, num_config);
-
- final int numConfigs = num_config[0];
- if (numConfigs <= 0)
- throw new IllegalArgumentException("No configs match configSpec");
-
- EGLConfig[] configs = new EGLConfig[numConfigs];
- egl.eglChooseConfig(eglDisplay, configSpec, configs, numConfigs,
- num_config);
-
- if (false) {
- Log.d(LOG_TAG, String.format("Found %d EGL configurations.", numConfigs));
- for (EGLConfig config : configs)
- dumpEglConfig(config);
- }
-
- // Android's eglChooseConfig is busted in several versions and
- // devices so we have to filter/rank the configs again ourselves.
- eglConfig = chooseEglConfig(configs);
- if (false) {
- Log.d(LOG_TAG, String.format("Chose EGL config from %d possibilities.", numConfigs));
- dumpEglConfig(eglConfig);
- }
-
- eglContext = egl.eglCreateContext(eglDisplay, eglConfig,
- EGL10.EGL_NO_CONTEXT, null);
- if (eglContext == EGL10.EGL_NO_CONTEXT)
- throw new RuntimeException("Failed to create context");
- }
-
- private EGLConfig chooseEglConfig(EGLConfig[] configs) {
+ final private EGLConfig chooseEglConfig(EGLConfig[] configs) {
int best = 0;
int bestScore = -1;
int[] value = new int[1];
@@ -198,32 +344,43 @@ public class ScummVM implements SurfaceHolder.Callback {
for (int i = 0; i < configs.length; i++) {
EGLConfig config = configs[i];
int score = 10000;
- egl.eglGetConfigAttrib(eglDisplay, config,
- EGL10.EGL_SURFACE_TYPE, value);
+
+ _egl.eglGetConfigAttrib(_egl_display, config,
+ EGL10.EGL_SURFACE_TYPE, value);
+
+ // must have
if ((value[0] & EGL10.EGL_WINDOW_BIT) == 0)
- continue; // must have
+ continue;
+
+ _egl.eglGetConfigAttrib(_egl_display, config,
+ EGL10.EGL_CONFIG_CAVEAT, value);
- egl.eglGetConfigAttrib(eglDisplay, config,
- EGL10.EGL_CONFIG_CAVEAT, value);
if (value[0] != EGL10.EGL_NONE)
score -= 1000;
// Must be at least 555, but then smaller is better
- final int[] colorBits = {EGL10.EGL_RED_SIZE,
- EGL10.EGL_GREEN_SIZE,
- EGL10.EGL_BLUE_SIZE,
- EGL10.EGL_ALPHA_SIZE};
+ final int[] colorBits = { EGL10.EGL_RED_SIZE,
+ EGL10.EGL_GREEN_SIZE,
+ EGL10.EGL_BLUE_SIZE,
+ EGL10.EGL_ALPHA_SIZE
+ };
+
for (int component : colorBits) {
- egl.eglGetConfigAttrib(eglDisplay, config,
- component, value);
+ _egl.eglGetConfigAttrib(_egl_display, config, component, value);
+
+ // boost if >5 bits accuracy
if (value[0] >= 5)
- score += 10; // boost if >5 bits accuracy
- score -= value[0]; // penalize for wasted bits
+ score += 10;
+
+ // penalize for wasted bits
+ score -= value[0];
}
- egl.eglGetConfigAttrib(eglDisplay, config,
- EGL10.EGL_DEPTH_SIZE, value);
- score -= value[0]; // penalize for wasted bits
+ _egl.eglGetConfigAttrib(_egl_display, config,
+ EGL10.EGL_DEPTH_SIZE, value);
+
+ // penalize for wasted bits
+ score -= value[0];
if (score > bestScore) {
best = i;
@@ -239,224 +396,21 @@ public class ScummVM implements SurfaceHolder.Callback {
return configs[best];
}
- // Called by ScummVM thread
- static private boolean _log_version = true;
- protected void setupScummVMSurface() {
- try {
- surfaceLock.acquire();
- } catch (InterruptedException e) {
- Log.e(LOG_TAG, "Interrupted while waiting for surface lock", e);
- return;
- }
- eglSurface = egl.eglCreateWindowSurface(eglDisplay, eglConfig,
- nativeSurface, null);
- if (eglSurface == EGL10.EGL_NO_SURFACE)
- Log.e(LOG_TAG, "CreateWindowSurface failed!");
- egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
-
- GL10 gl = (GL10)eglContext.getGL();
-
- if (_log_version) {
- Log.i(LOG_TAG, String.format("Using EGL %s (%s); GL %s/%s (%s)",
- egl.eglQueryString(eglDisplay, EGL10.EGL_VERSION),
- egl.eglQueryString(eglDisplay, EGL10.EGL_VENDOR),
- gl.glGetString(GL10.GL_VERSION),
- gl.glGetString(GL10.GL_RENDERER),
- gl.glGetString(GL10.GL_VENDOR)));
- _log_version = false; // only log this once
- }
-
- int[] value = new int[1];
- egl.eglQuerySurface(eglDisplay, eglSurface, EGL10.EGL_WIDTH, value);
- int width = value[0];
- egl.eglQuerySurface(eglDisplay, eglSurface, EGL10.EGL_HEIGHT, value);
- int height = value[0];
- Log.i(LOG_TAG, String.format("New surface is %dx%d", width, height));
- setSurfaceSize(width, height);
- }
-
- // Called by ScummVM thread
- protected void destroyScummVMSurface() {
- if (eglSurface != null) {
- egl.eglMakeCurrent(eglDisplay, EGL10.EGL_NO_SURFACE,
- EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
- egl.eglDestroySurface(eglDisplay, eglSurface);
- eglSurface = EGL10.EGL_NO_SURFACE;
- }
-
- surfaceLock.release();
- }
-
- public void setSurface(SurfaceHolder holder) {
- holder.addCallback(this);
- }
-
- final public boolean swapBuffers() {
- if (!egl.eglSwapBuffers(eglDisplay, eglSurface)) {
- int error = egl.eglGetError();
- Log.w(LOG_TAG, String.format("eglSwapBuffers exited with error 0x%x", error));
- if (error == EGL11.EGL_CONTEXT_LOST)
- return false;
- }
- return true;
- }
-
- // Set scummvm config options
- final public native static void loadConfigFile(String path);
- final public native static void setConfMan(String key, int value);
- final public native static void setConfMan(String key, String value);
- final public native void enableZoning(boolean enable);
- final public native void setSurfaceSize(int width, int height);
-
- // Feed an event to ScummVM. Safe to call from other threads.
- final public native void pushEvent(Event e);
-
- final private native void audioMixCallback(byte[] buf);
-
- // Runs the actual ScummVM program and returns when it does.
- // This should not be called from multiple threads simultaneously...
- final public native int scummVMMain(String[] argv);
-
- // Callbacks from C++ peer instance
- //protected GraphicsMode[] getSupportedGraphicsModes() {}
- protected void displayMessageOnOSD(String msg) {}
- protected void setWindowCaption(String caption) {}
- protected void showVirtualKeyboard(boolean enable) {}
- protected String[] getSysArchives() { return new String[0]; }
- protected String[] getPluginDirectories() { return new String[0]; }
- protected void initBackend() throws AudioSetupException {
- createScummVMGLContext();
- initAudio();
- }
-
- private static class AudioThread extends Thread {
- final private int buf_size;
- private boolean is_paused = false;
- final private ScummVM scummvm;
- final private AudioTrack audio_track;
-
- AudioThread(ScummVM scummvm, AudioTrack audio_track, int buf_size) {
- super("AudioThread");
- this.scummvm = scummvm;
- this.audio_track = audio_track;
- this.buf_size = buf_size;
- setPriority(Thread.MAX_PRIORITY);
- setDaemon(true);
- }
-
- public void pauseAudio() {
- synchronized (this) {
- is_paused = true;
- }
- audio_track.pause();
- }
-
- public void resumeAudio() {
- synchronized (this) {
- is_paused = false;
- notifyAll();
- }
- audio_track.play();
- }
-
- public void run() {
- byte[] buf = new byte[buf_size];
- audio_track.play();
- int offset = 0;
- try {
- while (true) {
- synchronized (this) {
- while (is_paused)
- wait();
- }
-
- if (offset == buf.length) {
- // Grab new audio data
- scummvm.audioMixCallback(buf);
- offset = 0;
- }
- int len = buf.length - offset;
- int ret = audio_track.write(buf, offset, len);
- if (ret < 0) {
- Log.w(LOG_TAG, String.format(
- "AudioTrack.write(%dB) returned error %d",
- buf.length, ret));
- break;
- } else if (ret != len) {
- Log.w(LOG_TAG, String.format(
- "Short audio write. Wrote %dB, not %dB",
- ret, buf.length));
- // Buffer is full, so yield cpu for a while
- Thread.sleep(100);
- }
- offset += ret;
- }
- } catch (InterruptedException e) {
- Log.e(LOG_TAG, "Audio thread interrupted", e);
- }
- }
- }
- private AudioThread audio_thread;
-
- final public int audioSampleRate() {
- return AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_MUSIC);
- }
-
- private void initAudio() throws AudioSetupException {
- int sample_rate = audioSampleRate();
- int buf_size =
- AudioTrack.getMinBufferSize(sample_rate,
- AudioFormat.CHANNEL_CONFIGURATION_STEREO,
- AudioFormat.ENCODING_PCM_16BIT);
- if (buf_size < 0) {
- int guess = AUDIO_FRAME_SIZE * sample_rate / 100; // 10ms of audio
- Log.w(LOG_TAG, String.format(
- "Unable to get min audio buffer size (error %d). Guessing %dB.",
- buf_size, guess));
- buf_size = guess;
- }
- Log.d(LOG_TAG, String.format("Using %dB buffer for %dHZ audio",
- buf_size, sample_rate));
- AudioTrack audio_track =
- new AudioTrack(AudioManager.STREAM_MUSIC,
- sample_rate,
- AudioFormat.CHANNEL_CONFIGURATION_STEREO,
- AudioFormat.ENCODING_PCM_16BIT,
- buf_size,
- AudioTrack.MODE_STREAM);
- if (audio_track.getState() != AudioTrack.STATE_INITIALIZED) {
- Log.e(LOG_TAG, "Error initialising Android audio system.");
- throw new AudioSetupException();
- }
-
- audio_thread = new AudioThread(this, audio_track, buf_size);
- audio_thread.start();
- }
-
- public void pause() {
- audio_thread.pauseAudio();
- // TODO: need to pause engine too
- }
-
- public void resume() {
- // TODO: need to resume engine too
- audio_thread.resumeAudio();
- }
-
static {
// For grabbing with gdb...
final boolean sleep_for_debugger = false;
if (sleep_for_debugger) {
try {
- Thread.sleep(20*1000);
+ Thread.sleep(20 * 1000);
} catch (InterruptedException e) {
}
}
- //System.loadLibrary("scummvm");
File cache_dir = ScummVMApplication.getLastCacheDir();
String libname = System.mapLibraryName("scummvm");
File libpath = new File(cache_dir, libname);
+
System.load(libpath.getPath());
}
}
+
diff --git a/backends/platform/android/org/inodes/gus/scummvm/ScummVMActivity.java b/backends/platform/android/org/inodes/gus/scummvm/ScummVMActivity.java
index fae35b6695..8cb3d80063 100644
--- a/backends/platform/android/org/inodes/gus/scummvm/ScummVMActivity.java
+++ b/backends/platform/android/org/inodes/gus/scummvm/ScummVMActivity.java
@@ -3,6 +3,7 @@ package org.inodes.gus.scummvm;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
+import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.media.AudioManager;
import android.os.Bundle;
@@ -14,6 +15,7 @@ import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceView;
+import android.view.SurfaceHolder;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.inputmethod.InputMethodManager;
@@ -30,14 +32,13 @@ public class ScummVMActivity extends Activity {
private final static int TRACKBALL_SCALE = 2;
private class MyScummVM extends ScummVM {
- private boolean scummvmRunning = false;
-
private boolean usingSmallScreen() {
// Multiple screen sizes came in with Android 1.6. Have
// to use reflection in order to continue supporting 1.5
// devices :(
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
+
try {
// This 'density' term is very confusing.
int DENSITY_LOW = metrics.getClass().getField("DENSITY_LOW").getInt(null);
@@ -48,27 +49,13 @@ public class ScummVMActivity extends Activity {
}
}
- public MyScummVM() {
- super(ScummVMActivity.this);
+ public MyScummVM(SurfaceHolder holder) {
+ super(ScummVMActivity.this.getAssets(), holder);
// Enable ScummVM zoning on 'small' screens.
- enableZoning(usingSmallScreen());
- }
-
- @Override
- protected void initBackend() throws ScummVM.AudioSetupException {
- synchronized (this) {
- scummvmRunning = true;
- notifyAll();
- }
- super.initBackend();
- }
-
- public void waitUntilRunning() throws InterruptedException {
- synchronized (this) {
- while (!scummvmRunning)
- wait();
- }
+ // FIXME make this optional for the user
+ // disabled for now since it crops too much
+ //enableZoning(usingSmallScreen());
}
@Override
@@ -95,18 +82,22 @@ public class ScummVMActivity extends Activity {
@Override
protected void showVirtualKeyboard(final boolean enable) {
- if (getResources().getConfiguration().keyboard ==
- Configuration.KEYBOARD_NOKEYS) {
- runOnUiThread(new Runnable() {
- public void run() {
- showKeyboard(enable);
- }
- });
- }
+ runOnUiThread(new Runnable() {
+ public void run() {
+ showKeyboard(enable);
+ }
+ });
+ }
+
+ @Override
+ protected String[] getSysArchives() {
+ return new String[0];
}
+
}
- private MyScummVM scummvm;
- private Thread scummvm_thread;
+
+ private MyScummVM _scummvm;
+ private Thread _scummvm_thread;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -126,113 +117,106 @@ public class ScummVMActivity extends Activity {
.setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(R.string.no_sdcard)
.setNegativeButton(R.string.quit,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog,
- int which) {
- finish();
- }
- })
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int which) {
+ finish();
+ }
+ })
.show();
+
return;
}
SurfaceView main_surface = (SurfaceView)findViewById(R.id.main_surface);
-
+
main_surface.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
return onTouchEvent(event);
}
});
+
main_surface.setOnKeyListener(new View.OnKeyListener() {
public boolean onKey(View v, int code, KeyEvent ev) {
return onKeyDown(code, ev);
}
});
+
main_surface.requestFocus();
- // Start ScummVM
- scummvm = new MyScummVM();
- scummvm_thread = new Thread(new Runnable() {
- public void run() {
- try {
- runScummVM();
- } catch (Exception e) {
- Log.e(ScummVM.LOG_TAG, "Fatal error in ScummVM thread", e);
- new AlertDialog.Builder(ScummVMActivity.this)
- .setTitle("Error")
- .setMessage(e.toString())
- .setIcon(android.R.drawable.ic_dialog_alert)
- .show();
- finish();
- }
- }
- }, "ScummVM");
- scummvm_thread.start();
-
- // Block UI thread until ScummVM has started. In particular,
- // this means that surface and event callbacks should be safe
- // after this point.
- try {
- scummvm.waitUntilRunning();
- } catch (InterruptedException e) {
- Log.e(ScummVM.LOG_TAG, "Interrupted while waiting for ScummVM.initBackend", e);
- finish();
- }
+ getFilesDir().mkdirs();
- scummvm.setSurface(main_surface.getHolder());
- }
+ // Start ScummVM
+ _scummvm = new MyScummVM(main_surface.getHolder());
- // Runs in another thread
- private void runScummVM() throws IOException {
- getFilesDir().mkdirs();
- String[] args = {
- "ScummVM-lib",
+ _scummvm.setArgs(new String[] {
+ "ScummVM",
"--config=" + getFileStreamPath("scummvmrc").getPath(),
"--path=" + Environment.getExternalStorageDirectory().getPath(),
"--gui-theme=scummmodern",
"--savepath=" + getDir("saves", 0).getPath()
- };
+ });
- int ret = scummvm.scummVMMain(args);
+ _scummvm_thread = new Thread(_scummvm, "ScummVM");
+ _scummvm_thread.start();
+ }
+
+ @Override
+ public void onStart() {
+ Log.d(ScummVM.LOG_TAG, "onStart");
- // On exit, tear everything down for a fresh
- // restart next time.
- System.exit(ret);
+ super.onStart();
}
- private boolean was_paused = false;
+ @Override
+ public void onResume() {
+ Log.d(ScummVM.LOG_TAG, "onResume");
+
+ super.onResume();
+
+ if (_scummvm != null)
+ _scummvm.setPause(false);
+ }
@Override
public void onPause() {
- if (scummvm != null) {
- was_paused = true;
- scummvm.pause();
- }
+ Log.d(ScummVM.LOG_TAG, "onPause");
+
super.onPause();
+
+ if (_scummvm != null)
+ _scummvm.setPause(true);
}
@Override
- public void onResume() {
- super.onResume();
- if (scummvm != null && was_paused)
- scummvm.resume();
- was_paused = false;
+ public void onStop() {
+ Log.d(ScummVM.LOG_TAG, "onStop");
+
+ super.onStop();
}
@Override
- public void onStop() {
- if (scummvm != null) {
- scummvm.pushEvent(new Event(Event.EVENT_QUIT));
+ public void onDestroy() {
+ Log.d(ScummVM.LOG_TAG, "onDestroy");
+
+ super.onDestroy();
+
+ if (_scummvm != null) {
+ _scummvm.pushEvent(new Event(Event.EVENT_QUIT));
+
try {
- scummvm_thread.join(1000); // 1s timeout
+ // 1s timeout
+ _scummvm_thread.join(1000);
} catch (InterruptedException e) {
Log.i(ScummVM.LOG_TAG, "Error while joining ScummVM thread", e);
}
+
+ _scummvm = null;
}
- super.onStop();
}
static final int MSG_MENU_LONG_PRESS = 1;
+
private final Handler keycodeMenuTimeoutHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -252,7 +236,7 @@ public class ScummVMActivity extends Activity {
@Override
public boolean onKeyMultiple(int keyCode, int repeatCount,
- KeyEvent kevent) {
+ KeyEvent kevent) {
return onKeyDown(keyCode, kevent);
}
@@ -262,35 +246,37 @@ public class ScummVMActivity extends Activity {
switch (keyCode) {
case KeyEvent.KEYCODE_MENU:
// Have to reimplement hold-down-menu-brings-up-softkeybd
- // ourselves, since we are otherwise hijacking the menu
- // key :(
+ // ourselves, since we are otherwise hijacking the menu key :(
// See com.android.internal.policy.impl.PhoneWindow.onKeyDownPanel()
// for the usual Android implementation of this feature.
+
+ // Ignore keyrepeat for menu
if (kevent.getRepeatCount() > 0)
- // Ignore keyrepeat for menu
return false;
- boolean timeout_fired = false;
- if (getResources().getConfiguration().keyboard ==
- Configuration.KEYBOARD_NOKEYS) {
- timeout_fired = !keycodeMenuTimeoutHandler.hasMessages(MSG_MENU_LONG_PRESS);
- keycodeMenuTimeoutHandler.removeMessages(MSG_MENU_LONG_PRESS);
- if (kevent.getAction() == KeyEvent.ACTION_DOWN) {
- keycodeMenuTimeoutHandler.sendMessageDelayed(
- keycodeMenuTimeoutHandler.obtainMessage(MSG_MENU_LONG_PRESS),
- ViewConfiguration.getLongPressTimeout());
- return true;
- }
+
+ boolean timeout_fired = !keycodeMenuTimeoutHandler.hasMessages(MSG_MENU_LONG_PRESS);
+ keycodeMenuTimeoutHandler.removeMessages(MSG_MENU_LONG_PRESS);
+
+ if (kevent.getAction() == KeyEvent.ACTION_DOWN) {
+ keycodeMenuTimeoutHandler.sendMessageDelayed(keycodeMenuTimeoutHandler.obtainMessage(MSG_MENU_LONG_PRESS),
+ ViewConfiguration.getLongPressTimeout());
+ return true;
}
+
if (kevent.getAction() == KeyEvent.ACTION_UP) {
if (!timeout_fired)
- scummvm.pushEvent(new Event(Event.EVENT_MAINMENU));
+ _scummvm.pushEvent(new Event(Event.EVENT_MAINMENU));
+
return true;
}
+
return false;
+
case KeyEvent.KEYCODE_CAMERA:
case KeyEvent.KEYCODE_SEARCH:
_do_right_click = (kevent.getAction() == KeyEvent.ACTION_DOWN);
return true;
+
case KeyEvent.KEYCODE_DPAD_CENTER:
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_DPAD_DOWN:
@@ -302,45 +288,59 @@ public class ScummVMActivity extends Activity {
// Some other handsets lack a trackball, so the DPAD is
// the only way of moving the cursor.
int motion_action;
+
// FIXME: this logic is a mess.
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
switch (kevent.getAction()) {
case KeyEvent.ACTION_DOWN:
motion_action = MotionEvent.ACTION_DOWN;
break;
+
case KeyEvent.ACTION_UP:
motion_action = MotionEvent.ACTION_UP;
break;
- default: // ACTION_MULTIPLE
+
+ // ACTION_MULTIPLE
+ default:
return false;
}
- } else
+ } else {
motion_action = MotionEvent.ACTION_MOVE;
+ }
Event e = new Event(getEventType(motion_action));
+
e.mouse_x = 0;
e.mouse_y = 0;
e.mouse_relative = true;
+
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_UP:
e.mouse_y = -TRACKBALL_SCALE;
break;
+
case KeyEvent.KEYCODE_DPAD_DOWN:
e.mouse_y = TRACKBALL_SCALE;
break;
+
case KeyEvent.KEYCODE_DPAD_LEFT:
e.mouse_x = -TRACKBALL_SCALE;
break;
+
case KeyEvent.KEYCODE_DPAD_RIGHT:
e.mouse_x = TRACKBALL_SCALE;
break;
}
- scummvm.pushEvent(e);
+
+ _scummvm.pushEvent(e);
+
return true;
}
+
case KeyEvent.KEYCODE_BACK:
// skip isSystem() check and fall through to main code
break;
+
default:
if (kevent.isSystem())
return false;
@@ -355,51 +355,63 @@ public class ScummVMActivity extends Activity {
e.type = Event.EVENT_KEYDOWN;
e.synthetic = false;
break;
+
case KeyEvent.ACTION_UP:
e.type = Event.EVENT_KEYUP;
e.synthetic = false;
break;
+
case KeyEvent.ACTION_MULTIPLE:
// e.type is handled below
e.synthetic = true;
break;
+
default:
return false;
}
e.kbd_keycode = Event.androidKeyMap.containsKey(keyCode) ?
Event.androidKeyMap.get(keyCode) : Event.KEYCODE_INVALID;
+
e.kbd_ascii = kevent.getUnicodeChar();
+
if (e.kbd_ascii == 0)
e.kbd_ascii = e.kbd_keycode; // scummvm keycodes are mostly ascii
-
e.kbd_flags = 0;
+
if (kevent.isAltPressed())
e.kbd_flags |= Event.KBD_ALT;
- if (kevent.isSymPressed()) // no ctrl key in android, so use sym (?)
+
+ // no ctrl key in android, so use sym (?)
+ if (kevent.isSymPressed())
e.kbd_flags |= Event.KBD_CTRL;
+
if (kevent.isShiftPressed()) {
if (keyCode >= KeyEvent.KEYCODE_0 &&
- keyCode <= KeyEvent.KEYCODE_9) {
+ keyCode <= KeyEvent.KEYCODE_9) {
// Shift+number -> convert to F* key
int offset = keyCode == KeyEvent.KEYCODE_0 ?
10 : keyCode - KeyEvent.KEYCODE_1; // turn 0 into 10
+
e.kbd_keycode = Event.KEYCODE_F1 + offset;
e.kbd_ascii = Event.ASCII_F1 + offset;
- } else
+ } else {
e.kbd_flags |= Event.KBD_SHIFT;
+ }
}
if (kevent.getAction() == KeyEvent.ACTION_MULTIPLE) {
for (int i = 0; i <= kevent.getRepeatCount(); i++) {
e.type = Event.EVENT_KEYDOWN;
- scummvm.pushEvent(e);
+ _scummvm.pushEvent(e);
+
e.type = Event.EVENT_KEYUP;
- scummvm.pushEvent(e);
+ _scummvm.pushEvent(e);
}
- } else
- scummvm.pushEvent(e);
+ } else {
+ _scummvm.pushEvent(e);
+ }
return true;
}
@@ -410,11 +422,14 @@ public class ScummVMActivity extends Activity {
_last_click_was_right = _do_right_click;
return _last_click_was_right ?
Event.EVENT_RBUTTONDOWN : Event.EVENT_LBUTTONDOWN;
+
case MotionEvent.ACTION_UP:
return _last_click_was_right ?
Event.EVENT_RBUTTONUP : Event.EVENT_LBUTTONUP;
+
case MotionEvent.ACTION_MOVE:
return Event.EVENT_MOUSEMOVE;
+
default:
return Event.EVENT_INVALID;
}
@@ -432,7 +447,8 @@ public class ScummVMActivity extends Activity {
e.mouse_y =
(int)(event.getY() * event.getYPrecision()) * TRACKBALL_SCALE;
e.mouse_relative = true;
- scummvm.pushEvent(e);
+
+ _scummvm.pushEvent(e);
return true;
}
@@ -440,6 +456,7 @@ public class ScummVMActivity extends Activity {
@Override
public boolean onTouchEvent(MotionEvent event) {
int type = getEventType(event.getAction());
+
if (type == Event.EVENT_INVALID)
return false;
@@ -447,7 +464,8 @@ public class ScummVMActivity extends Activity {
e.mouse_x = (int)event.getX();
e.mouse_y = (int)event.getY();
e.mouse_relative = false;
- scummvm.pushEvent(e);
+
+ _scummvm.pushEvent(e);
return true;
}
@@ -456,6 +474,7 @@ public class ScummVMActivity extends Activity {
SurfaceView main_surface = (SurfaceView)findViewById(R.id.main_surface);
InputMethodManager imm = (InputMethodManager)
getSystemService(INPUT_METHOD_SERVICE);
+
if (show)
imm.showSoftInput(main_surface, InputMethodManager.SHOW_IMPLICIT);
else
@@ -463,3 +482,4 @@ public class ScummVMActivity extends Activity {
InputMethodManager.HIDE_IMPLICIT_ONLY);
}
}
+
diff --git a/backends/platform/android/org/inodes/gus/scummvm/ScummVMApplication.java b/backends/platform/android/org/inodes/gus/scummvm/ScummVMApplication.java
index 37a9d09e1a..f9eec72eac 100644
--- a/backends/platform/android/org/inodes/gus/scummvm/ScummVMApplication.java
+++ b/backends/platform/android/org/inodes/gus/scummvm/ScummVMApplication.java
@@ -8,22 +8,24 @@ public class ScummVMApplication extends Application {
public final static String ACTION_PLUGIN_QUERY = "org.inodes.gus.scummvm.action.PLUGIN_QUERY";
public final static String EXTRA_UNPACK_LIBS = "org.inodes.gus.scummvm.extra.UNPACK_LIBS";
- private static File cache_dir;
+ private static File _cache_dir;
@Override
public void onCreate() {
super.onCreate();
+
// This is still on /data :(
- cache_dir = getCacheDir();
+ _cache_dir = getCacheDir();
// This is mounted noexec :(
//cache_dir = new File(Environment.getExternalStorageDirectory(),
- // "/.ScummVM.tmp");
+ // "/.ScummVM.tmp");
// This is owned by download manager and requires special
// permissions to access :(
//cache_dir = Environment.getDownloadCacheDirectory();
}
public static File getLastCacheDir() {
- return cache_dir;
+ return _cache_dir;
}
}
+
diff --git a/backends/platform/android/org/inodes/gus/scummvm/Unpacker.java b/backends/platform/android/org/inodes/gus/scummvm/Unpacker.java
index 8811b1f3ae..c4b2ad7f5d 100644
--- a/backends/platform/android/org/inodes/gus/scummvm/Unpacker.java
+++ b/backends/platform/android/org/inodes/gus/scummvm/Unpacker.java
@@ -370,3 +370,4 @@ public class Unpacker extends Activity {
}
}
}
+
diff --git a/backends/platform/android/video.cpp b/backends/platform/android/texture.cpp
index f8427c2ac8..24e6549b1a 100644
--- a/backends/platform/android/video.cpp
+++ b/backends/platform/android/texture.cpp
@@ -33,8 +33,8 @@
#include "common/util.h"
#include "common/tokenizer.h"
+#include "backends/platform/android/texture.h"
#include "backends/platform/android/android.h"
-#include "backends/platform/android/video.h"
// Unfortunately, Android devices are too varied to make broad assumptions :/
#define TEXSUBIMAGE_IS_EXPENSIVE 0
@@ -82,43 +82,67 @@ void GLESTexture::initGLExtensions() {
}
}
-GLESTexture::GLESTexture() :
+GLESTexture::GLESTexture(byte bytesPerPixel, GLenum glFormat, GLenum glType,
+ size_t paletteSize, Graphics::PixelFormat pixelFormat) :
+ _bytesPerPixel(bytesPerPixel),
+ _glFormat(glFormat),
+ _glType(glType),
+ _paletteSize(paletteSize),
+ _texture_name(0),
+ _surface(),
_texture_width(0),
_texture_height(0),
- _all_dirty(true)
+ _all_dirty(false),
+ _dirty_rect(),
+ _pixelFormat(pixelFormat)
{
GLCALL(glGenTextures(1, &_texture_name));
-
- // This all gets reset later in allocBuffer:
- _surface.w = 0;
- _surface.h = 0;
- _surface.pitch = _texture_width;
- _surface.pixels = 0;
- _surface.bytesPerPixel = 0;
}
GLESTexture::~GLESTexture() {
+ release();
+}
+
+void GLESTexture::release() {
debug("Destroying texture %u", _texture_name);
GLCALL(glDeleteTextures(1, &_texture_name));
}
-void GLESTexture::reinitGL() {
- GLCALL(glDeleteTextures(1, &_texture_name));
+void GLESTexture::reinit() {
GLCALL(glGenTextures(1, &_texture_name));
- // bypass allocBuffer() shortcut to reinit the texture properly
- _texture_width = 0;
- _texture_height = 0;
+ if (_paletteSize) {
+ // paletted textures are in a local buffer, don't wipe it
+ initSize();
+ } else {
+ // bypass allocBuffer() shortcut to reinit the texture properly
+ _texture_width = 0;
+ _texture_height = 0;
+
+ allocBuffer(_surface.w, _surface.h);
+ }
- allocBuffer(_surface.w, _surface.h);
setDirty();
}
+void GLESTexture::initSize() {
+ // Allocate room for the texture now, but pixel data gets uploaded
+ // later (perhaps with multiple TexSubImage2D operations).
+ GLCALL(glBindTexture(GL_TEXTURE_2D, _texture_name));
+ GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
+ GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
+ GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
+ GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
+ GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
+ GLCALL(glTexImage2D(GL_TEXTURE_2D, 0, _glFormat,
+ _texture_width, _texture_height,
+ 0, _glFormat, _glType, 0));
+}
+
void GLESTexture::allocBuffer(GLuint w, GLuint h) {
- int bpp = bytesPerPixel();
_surface.w = w;
_surface.h = h;
- _surface.bytesPerPixel = bpp;
+ _surface.bytesPerPixel = _bytesPerPixel;
// Already allocated a sufficiently large buffer?
if (w <= _texture_width && h <= _texture_height)
@@ -132,72 +156,81 @@ void GLESTexture::allocBuffer(GLuint w, GLuint h) {
_texture_height = nextHigher2(_surface.h);
}
- _surface.pitch = _texture_width * bpp;
+ _surface.pitch = _texture_width * _bytesPerPixel;
- // Allocate room for the texture now, but pixel data gets uploaded
- // later (perhaps with multiple TexSubImage2D operations).
- GLCALL(glBindTexture(GL_TEXTURE_2D, _texture_name));
- GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
- GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
- GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
- GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
- GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
- GLCALL(glTexImage2D(GL_TEXTURE_2D, 0, glFormat(),
- _texture_width, _texture_height,
- 0, glFormat(), glType(), 0));
+ initSize();
}
void GLESTexture::updateBuffer(GLuint x, GLuint y, GLuint w, GLuint h,
- const void *buf, int pitch) {
- ENTER("%u, %u, %u, %u, %p, %d", x, y, w, h, buf, pitch);
+ const void *buf, int pitch_buf) {
+ ENTER("%u, %u, %u, %u, %p, %d", x, y, w, h, buf, pitch_buf);
GLCALL(glBindTexture(GL_TEXTURE_2D, _texture_name));
GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
- setDirtyRect(Common::Rect(x, y, x+w, y+h));
+ setDirtyRect(Common::Rect(x, y, x + w, y + h));
- if (static_cast<int>(w) * bytesPerPixel() == pitch) {
+ if (static_cast<int>(w) * _bytesPerPixel == pitch_buf) {
GLCALL(glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h,
- glFormat(), glType(), buf));
+ _glFormat, _glType, buf));
} else {
// GLES removed the ability to specify pitch, so we
// have to do this ourselves.
- if (h == 0)
- return;
-
#if TEXSUBIMAGE_IS_EXPENSIVE
- byte tmpbuf[w * h * bytesPerPixel()];
+ byte *tmp = new byte[w * h * _bytesPerPixel];
+ assert(tmp);
+
const byte *src = static_cast<const byte *>(buf);
- byte *dst = tmpbuf;
+ byte *dst = tmp;
GLuint count = h;
do {
- memcpy(dst, src, w * bytesPerPixel());
- dst += w * bytesPerPixel();
- src += pitch;
+ memcpy(dst, src, w * _bytesPerPixel);
+ dst += w * _bytesPerPixel;
+ src += pitch_buf;
} while (--count);
GLCALL(glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h,
- glFormat(), glType(), tmpbuf));
+ _glFormat, _glType, tmp));
+
+ delete[] tmp;
#else
// This version avoids the intermediate copy at the expense of
// repeat glTexSubImage2D calls. On some devices this is worse.
const byte *src = static_cast<const byte *>(buf);
do {
GLCALL(glTexSubImage2D(GL_TEXTURE_2D, 0, x, y,
- w, 1, glFormat(), glType(), src));
+ w, 1, _glFormat, _glType, src));
++y;
- src += pitch;
+ src += pitch_buf;
} while (--h);
#endif
}
}
-void GLESTexture::fillBuffer(byte x) {
- int rowbytes = _surface.w * bytesPerPixel();
- byte tmpbuf[_surface.h * rowbytes];
- memset(tmpbuf, x, _surface.h * rowbytes);
- updateBuffer(0, 0, _surface.w, _surface.h, tmpbuf, rowbytes);
+void GLESTexture::fillBuffer(uint32 color) {
+ uint rowbytes = _surface.w * _bytesPerPixel;
+
+ byte *tmp = new byte[rowbytes];
+ assert(tmp);
+
+ if (_bytesPerPixel == 1 || ((color & 0xff) == ((color >> 8) & 0xff))) {
+ memset(tmp, color & 0xff, rowbytes);
+ } else {
+ uint16 *p = (uint16 *)tmp;
+ Common::set_to(p, p + _surface.w, (uint16)color);
+ }
+
+ GLCALL(glBindTexture(GL_TEXTURE_2D, _texture_name));
+ GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
+
+ for (GLuint y = 0; y < _surface.h; ++y)
+ GLCALL(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, y, _surface.w, 1,
+ _glFormat, _glType, tmp));
+
+ delete[] tmp;
+
+ setDirty();
}
void GLESTexture::drawTexture(GLshort x, GLshort y, GLshort w, GLshort h) {
@@ -206,7 +239,7 @@ void GLESTexture::drawTexture(GLshort x, GLshort y, GLshort w, GLshort h) {
#ifdef GL_OES_draw_texture
// Great extension, but only works under specific conditions.
// Still a work-in-progress - disabled for now.
- if (false && draw_tex_supported && paletteSize() == 0) {
+ if (false && draw_tex_supported && _paletteSize == 0) {
//GLCALL(glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE));
const GLint crop[4] = { 0, _surface.h, _surface.w, -_surface.h };
@@ -243,12 +276,34 @@ void GLESTexture::drawTexture(GLshort x, GLshort y, GLshort w, GLshort h) {
GLCALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, ARRAYSIZE(vertices) / 2));
}
- _all_dirty = false;
- _dirty_rect = Common::Rect();
+ clearDirty();
+}
+
+GLES4444Texture::GLES4444Texture() :
+ GLESTexture(2, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 0, getPixelFormat()) {
+}
+
+GLES4444Texture::~GLES4444Texture() {
+}
+
+GLES5551Texture::GLES5551Texture() :
+ GLESTexture(2, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, 0, getPixelFormat()) {
+}
+
+GLES5551Texture::~GLES5551Texture() {
+}
+
+GLES565Texture::GLES565Texture() :
+ GLESTexture(2, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0, getPixelFormat()) {
+}
+
+GLES565Texture::~GLES565Texture() {
}
-GLESPaletteTexture::GLESPaletteTexture() :
- GLESTexture(),
+GLESPaletteTexture::GLESPaletteTexture(byte bytesPerPixel, GLenum glFormat,
+ GLenum glType, size_t paletteSize) :
+ GLESTexture(bytesPerPixel, glFormat, glType, paletteSize,
+ Graphics::PixelFormat::createFormatCLUT8()),
_texture(0)
{
}
@@ -258,85 +313,73 @@ GLESPaletteTexture::~GLESPaletteTexture() {
}
void GLESPaletteTexture::allocBuffer(GLuint w, GLuint h) {
- int bpp = bytesPerPixel();
- _surface.w = w;
- _surface.h = h;
- _surface.bytesPerPixel = bpp;
-
- // Already allocated a sufficiently large buffer?
- if (w <= _texture_width && h <= _texture_height)
- return;
-
- if (npot_supported) {
- _texture_width = _surface.w;
- _texture_height = _surface.h;
- } else {
- _texture_width = nextHigher2(_surface.w);
- _texture_height = nextHigher2(_surface.h);
- }
- _surface.pitch = _texture_width * bpp;
+ GLESTexture::allocBuffer(w, h);
// Texture gets uploaded later (from drawTexture())
- byte *new_buffer = new byte[paletteSize() +
- _texture_width * _texture_height * bytesPerPixel()];
+ byte *new_buffer = new byte[_paletteSize +
+ _texture_width * _texture_height * _bytesPerPixel];
+ assert(new_buffer);
+
if (_texture) {
// preserve palette
- memcpy(new_buffer, _texture, paletteSize());
+ memcpy(new_buffer, _texture, _paletteSize);
delete[] _texture;
}
_texture = new_buffer;
- _surface.pixels = _texture + paletteSize();
+ _surface.pixels = _texture + _paletteSize;
}
-void GLESPaletteTexture::fillBuffer(byte x) {
+void GLESPaletteTexture::fillBuffer(uint32 color) {
assert(_surface.pixels);
- memset(_surface.pixels, x, _surface.pitch * _surface.h);
+ memset(_surface.pixels, color & 0xff, _surface.pitch * _surface.h);
setDirty();
}
-void GLESPaletteTexture::updateBuffer(GLuint x, GLuint y,
- GLuint w, GLuint h,
- const void *buf, int pitch) {
- _all_dirty = true;
+void GLESPaletteTexture::updateBuffer(GLuint x, GLuint y, GLuint w, GLuint h,
+ const void *buf, int pitch_buf) {
+ setDirtyRect(Common::Rect(x, y, x + w, y + h));
const byte * src = static_cast<const byte *>(buf);
byte *dst = static_cast<byte *>(_surface.getBasePtr(x, y));
do {
- memcpy(dst, src, w * bytesPerPixel());
+ memcpy(dst, src, w * _bytesPerPixel);
dst += _surface.pitch;
- src += pitch;
+ src += pitch_buf;
} while (--h);
}
-void GLESPaletteTexture::uploadTexture() const {
- const size_t texture_size =
- paletteSize() + _texture_width * _texture_height * bytesPerPixel();
+void GLESPaletteTexture::drawTexture(GLshort x, GLshort y, GLshort w,
+ GLshort h) {
+ if (dirty()) {
+ GLCALL(glBindTexture(GL_TEXTURE_2D, _texture_name));
- GLCALL(glCompressedTexImage2D(GL_TEXTURE_2D, 0, glType(),
- _texture_width, _texture_height,
- 0, texture_size, _texture));
-}
+ const size_t texture_size =
+ _paletteSize + _texture_width * _texture_height * _bytesPerPixel;
-void GLESPaletteTexture::drawTexture(GLshort x, GLshort y, GLshort w, GLshort h) {
- if (_all_dirty) {
- GLCALL(glBindTexture(GL_TEXTURE_2D, _texture_name));
- GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
- GL_NEAREST));
- GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
- GL_NEAREST));
- GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
- GL_CLAMP_TO_EDGE));
- GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
- GL_CLAMP_TO_EDGE));
- uploadTexture();
- _all_dirty = false;
+ GLCALL(glCompressedTexImage2D(GL_TEXTURE_2D, 0, _glType,
+ _texture_width, _texture_height,
+ 0, texture_size, _texture));
}
GLESTexture::drawTexture(x, y, w, h);
}
+GLESPalette888Texture::GLESPalette888Texture() :
+ GLESPaletteTexture(1, GL_RGB, GL_PALETTE8_RGB8_OES, 256 * 3) {
+}
+
+GLESPalette888Texture::~GLESPalette888Texture() {
+}
+
+GLESPalette8888Texture::GLESPalette8888Texture() :
+ GLESPaletteTexture(1, GL_RGBA, GL_PALETTE8_RGBA8_OES, 256 * 4) {
+}
+
+GLESPalette8888Texture::~GLESPalette8888Texture() {
+}
+
#endif
diff --git a/backends/platform/android/video.h b/backends/platform/android/texture.h
index da42ea876d..78df43aea9 100644
--- a/backends/platform/android/video.h
+++ b/backends/platform/android/texture.h
@@ -23,11 +23,15 @@
*
*/
+#ifndef _ANDROID_TEXTURE_H_
+#define _ANDROID_TEXTURE_H_
+
#if defined(__ANDROID__)
#include <GLES/gl.h>
#include "graphics/surface.h"
+#include "graphics/pixelformat.h"
#include "common/rect.h"
#include "common/array.h"
@@ -36,18 +40,29 @@ class GLESTexture {
public:
static void initGLExtensions();
- GLESTexture();
+protected:
+ GLESTexture(byte bytesPerPixel, GLenum glFormat, GLenum glType,
+ size_t paletteSize, Graphics::PixelFormat pixelFormat);
+
+public:
virtual ~GLESTexture();
- virtual void reinitGL();
+ void release();
+ void reinit();
+ void initSize();
+
virtual void allocBuffer(GLuint width, GLuint height);
virtual void updateBuffer(GLuint x, GLuint y, GLuint width, GLuint height,
- const void *buf, int pitch);
- virtual void fillBuffer(byte x);
+ const void *buf, int pitch_buf);
+ virtual void fillBuffer(uint32 color);
virtual void drawTexture(GLshort x, GLshort y, GLshort w, GLshort h);
+ inline void drawTexture() {
+ drawTexture(0, 0, _surface.w, _surface.h);
+ }
+
inline GLuint width() const {
return _surface.w;
}
@@ -56,8 +71,8 @@ public:
return _surface.h;
}
- inline GLuint texture_name() const {
- return _texture_name;
+ inline uint16 pitch() const {
+ return _surface.pitch;
}
inline const Graphics::Surface *surface_const() const {
@@ -73,22 +88,21 @@ public:
return _all_dirty || !_dirty_rect.isEmpty();
}
- inline void drawTexture() {
- drawTexture(0, 0, _surface.w, _surface.h);
+ inline const Graphics::PixelFormat &getPixelFormat() const {
+ return _pixelFormat;
}
protected:
- virtual byte bytesPerPixel() const = 0;
- virtual GLenum glFormat() const = 0;
- virtual GLenum glType() const = 0;
-
- virtual size_t paletteSize() const {
- return 0;
- }
-
inline void setDirty() {
_all_dirty = true;
- _dirty_rect = Common::Rect();
+ }
+
+ inline void clearDirty() {
+ _all_dirty = false;
+ _dirty_rect.top = 0;
+ _dirty_rect.left = 0;
+ _dirty_rect.bottom = 0;
+ _dirty_rect.right = 0;
}
inline void setDirtyRect(const Common::Rect& r) {
@@ -100,58 +114,68 @@ protected:
}
}
+ byte _bytesPerPixel;
+ GLenum _glFormat;
+ GLenum _glType;
+ size_t _paletteSize;
+
GLuint _texture_name;
Graphics::Surface _surface;
GLuint _texture_width;
GLuint _texture_height;
- bool _all_dirty;
// Covers dirty area
+ bool _all_dirty;
Common::Rect _dirty_rect;
+
+ Graphics::PixelFormat _pixelFormat;
};
// RGBA4444 texture
class GLES4444Texture : public GLESTexture {
-protected:
- virtual byte bytesPerPixel() const {
- return 2;
- }
+public:
+ GLES4444Texture();
+ virtual ~GLES4444Texture();
- virtual GLenum glFormat() const {
- return GL_RGBA;
+ static inline Graphics::PixelFormat getPixelFormat() {
+ return Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0);
}
+};
- virtual GLenum glType() const {
- return GL_UNSIGNED_SHORT_4_4_4_4;
+// RGBA5551 texture
+class GLES5551Texture : public GLESTexture {
+public:
+ GLES5551Texture();
+ virtual ~GLES5551Texture();
+
+ static inline Graphics::PixelFormat getPixelFormat() {
+ return Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0);
}
};
// RGB565 texture
class GLES565Texture : public GLESTexture {
-protected:
- virtual byte bytesPerPixel() const {
- return 2;
- }
-
- virtual GLenum glFormat() const {
- return GL_RGB;
- }
+public:
+ GLES565Texture();
+ virtual ~GLES565Texture();
- virtual GLenum glType() const {
- return GL_UNSIGNED_SHORT_5_6_5;
+ static inline Graphics::PixelFormat getPixelFormat() {
+ return Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
}
};
-// RGB888 256-entry paletted texture
class GLESPaletteTexture : public GLESTexture {
+protected:
+ GLESPaletteTexture(byte bytesPerPixel, GLenum glFormat, GLenum glType,
+ size_t paletteSize);
+
public:
- GLESPaletteTexture();
virtual ~GLESPaletteTexture();
virtual void allocBuffer(GLuint width, GLuint height);
virtual void updateBuffer(GLuint x, GLuint y, GLuint width, GLuint height,
- const void *buf, int pitch);
- virtual void fillBuffer(byte x);
+ const void *buf, int pitch_buf);
+ virtual void fillBuffer(uint32 color);
virtual void drawTexture(GLshort x, GLshort y, GLshort w, GLshort h);
@@ -169,41 +193,23 @@ public:
};
protected:
- virtual byte bytesPerPixel() const {
- return 1;
- }
-
- virtual GLenum glFormat() const {
- return GL_RGB;
- }
-
- virtual GLenum glType() const {
- return GL_PALETTE8_RGB8_OES;
- }
-
- virtual size_t paletteSize() const {
- return 256 * 3;
- }
-
- void uploadTexture() const;
-
byte *_texture;
};
-// RGBA8888 256-entry paletted texture
-class GLESPaletteATexture : public GLESPaletteTexture {
-protected:
- virtual GLenum glFormat() const {
- return GL_RGBA;
- }
-
- virtual GLenum glType() const {
- return GL_PALETTE8_RGBA8_OES;
- }
+// RGB888 256-entry paletted texture
+class GLESPalette888Texture : public GLESPaletteTexture {
+public:
+ GLESPalette888Texture();
+ virtual ~GLESPalette888Texture();
+};
- virtual size_t paletteSize() const {
- return 256 * 4;
- }
+// RGBA8888 256-entry paletted texture
+class GLESPalette8888Texture : public GLESPaletteTexture {
+public:
+ GLESPalette8888Texture();
+ virtual ~GLESPalette8888Texture();
};
#endif
+#endif
+
diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp
index dc91bd9fe7..bddb48ca95 100644
--- a/backends/platform/sdl/sdl.cpp
+++ b/backends/platform/sdl/sdl.cpp
@@ -125,10 +125,10 @@ void OSystem_SDL::init() {
if (_timerManager == 0)
_timerManager = new SdlTimerManager();
- #ifdef USE_OPENGL
- // Setup a list with both SDL and OpenGL graphics modes
- setupGraphicsModes();
- #endif
+#ifdef USE_OPENGL
+ // Setup a list with both SDL and OpenGL graphics modes
+ setupGraphicsModes();
+#endif
}
void OSystem_SDL::initBackend() {
@@ -148,11 +148,15 @@ void OSystem_SDL::initBackend() {
Common::String gfxMode(ConfMan.get("gfx_mode"));
bool use_opengl = false;
const OSystem::GraphicsMode *mode = OpenGLSdlGraphicsManager::supportedGraphicsModes();
+ int i = 0;
while (mode->name) {
- if (scumm_stricmp(mode->name, gfxMode.c_str()) == 0)
+ if (scumm_stricmp(mode->name, gfxMode.c_str()) == 0) {
+ _graphicsMode = i + _sdlModesCount;
use_opengl = true;
+ }
mode++;
+ ++i;
}
// If the gfx_mode is from OpenGL, create the OpenGL graphics manager
@@ -464,6 +468,7 @@ int OSystem_SDL::getDefaultGraphicsMode() const {
bool OSystem_SDL::setGraphicsMode(int mode) {
const OSystem::GraphicsMode *srcMode;
int i;
+
// Check if mode is from SDL or OpenGL
if (mode < _sdlModesCount) {
srcMode = SdlGraphicsManager::supportedGraphicsModes();
@@ -472,28 +477,34 @@ bool OSystem_SDL::setGraphicsMode(int mode) {
srcMode = OpenGLSdlGraphicsManager::supportedGraphicsModes();
i = _sdlModesCount;
}
+
// Loop through modes
while (srcMode->name) {
if (i == mode) {
// If the new mode and the current mode are not from the same graphics
// manager, delete and create the new mode graphics manager
if (_graphicsMode >= _sdlModesCount && mode < _sdlModesCount) {
+ debug(1, "switching to plain SDL graphics");
delete _graphicsManager;
_graphicsManager = new SdlGraphicsManager(_eventSource);
((SdlGraphicsManager *)_graphicsManager)->initEventObserver();
_graphicsManager->beginGFXTransaction();
} else if (_graphicsMode < _sdlModesCount && mode >= _sdlModesCount) {
+ debug(1, "switching to OpenGL graphics");
delete _graphicsManager;
_graphicsManager = new OpenGLSdlGraphicsManager();
((OpenGLSdlGraphicsManager *)_graphicsManager)->initEventObserver();
_graphicsManager->beginGFXTransaction();
}
+
_graphicsMode = mode;
return _graphicsManager->setGraphicsMode(srcMode->id);
}
+
i++;
srcMode++;
}
+
return false;
}
diff --git a/backends/platform/symbian/S60v3/ScummVM_A0000658_S60v3.mmp.in b/backends/platform/symbian/S60v3/ScummVM_A0000658_S60v3.mmp.in
index c7623d09a8..12b4cb3610 100644
--- a/backends/platform/symbian/S60v3/ScummVM_A0000658_S60v3.mmp.in
+++ b/backends/platform/symbian/S60v3/ScummVM_A0000658_S60v3.mmp.in
@@ -91,7 +91,7 @@ STATICLIBRARY esdl.lib
// *** Include paths
USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\engines
-USERINCLUDE ..\..\..\..\backends\fs ..\src ..\..\..\..\backends\platform\sdl ..\..\..\..\sound
+USERINCLUDE ..\..\..\..\backends\fs ..\src ..\..\..\..\backends\platform\sdl ..\..\..\..\audio
SYSTEMINCLUDE \epoc32\include\ESDL
SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version
diff --git a/backends/platform/symbian/S60v3/ScummVM_S60v3.mmp.in b/backends/platform/symbian/S60v3/ScummVM_S60v3.mmp.in
index 6f14e858b0..9af6535b22 100644
--- a/backends/platform/symbian/S60v3/ScummVM_S60v3.mmp.in
+++ b/backends/platform/symbian/S60v3/ScummVM_S60v3.mmp.in
@@ -91,7 +91,7 @@ STATICLIBRARY esdl.lib
// *** Include paths
USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\engines
-USERINCLUDE ..\..\..\..\backends\fs ..\src ..\..\..\..\backends\platform\sdl ..\..\..\..\sound
+USERINCLUDE ..\..\..\..\backends\fs ..\src ..\..\..\..\backends\platform\sdl ..\..\..\..\audio
SYSTEMINCLUDE \epoc32\include\ESDL
SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version
diff --git a/backends/platform/symbian/mmp/scummvm_agi.mmp.in b/backends/platform/symbian/mmp/scummvm_agi.mmp.in
index 044326405d..e2d98b9ae4 100644
--- a/backends/platform/symbian/mmp/scummvm_agi.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_agi.mmp.in
@@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\agi
// *** Include paths
USERINCLUDE ..\..\..\..\engines
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/backends/platform/symbian/mmp/scummvm_agos.mmp.in b/backends/platform/symbian/mmp/scummvm_agos.mmp.in
index e2d1e5eff4..077111d783 100644
--- a/backends/platform/symbian/mmp/scummvm_agos.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_agos.mmp.in
@@ -65,6 +65,6 @@ SOURCEPATH ..\..\..\..\engines\agos
// *** Include paths
USERINCLUDE ..\..\..\..\engines
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/backends/platform/symbian/mmp/scummvm_base.mmp.in b/backends/platform/symbian/mmp/scummvm_base.mmp.in
index d25cef4ffe..b3bfbab530 100644
--- a/backends/platform/symbian/mmp/scummvm_base.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_base.mmp.in
@@ -51,7 +51,7 @@ ALWAYS_BUILD_AS_ARM
// *** Include paths
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio
USERINCLUDE ..\..\..\..\backends\fs ..\src ..\..\..\..\backends\platform\sdl
SYSTEMINCLUDE \epoc32\include\ESDL
@@ -95,7 +95,7 @@ SOURCEPATH ..\..\..\..\gui
//SOURCE KeysDialog.cpp
//SOURCE Actions.cpp
-SOURCEPATH ..\..\..\..\sound
+SOURCEPATH ..\..\..\..\audio
//START_AUTO_OBJECTS_SOUND_//
// empty base file, will be updated by Perl build scripts
@@ -110,6 +110,12 @@ SOURCE rate_arm.cpp // ARM version: add ASM .cpp wrapper
SOURCE rate_arm_asm.s // ARM version: add ASM routines
#endif
+SOURCEPATH ..\..\..\..\video
+//START_AUTO_OBJECTS_VIDEO_//
+
+ // empty base file, will be updated by Perl build scripts
+
+//STOP_AUTO_OBJECTS_VIDEO_//
// add a few files manually, since they are not parsed from modules.mk files
SOURCEPATH ..\..\..\..
diff --git a/backends/platform/symbian/mmp/scummvm_cine.mmp.in b/backends/platform/symbian/mmp/scummvm_cine.mmp.in
index d114ec554b..2c8118ef13 100644
--- a/backends/platform/symbian/mmp/scummvm_cine.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_cine.mmp.in
@@ -59,5 +59,5 @@ SOURCEPATH ..\..\..\..\engines\cine
// *** Include paths
USERINCLUDE ..\..\..\..\engines
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/backends/platform/symbian/mmp/scummvm_cruise.mmp.in b/backends/platform/symbian/mmp/scummvm_cruise.mmp.in
index e4c836a68b..b43a867da3 100644
--- a/backends/platform/symbian/mmp/scummvm_cruise.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_cruise.mmp.in
@@ -59,5 +59,5 @@ SOURCEPATH ..\..\..\..\engines\cruise
// *** Include paths
USERINCLUDE ..\..\..\..\engines
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/backends/platform/symbian/mmp/scummvm_draci.mmp.in b/backends/platform/symbian/mmp/scummvm_draci.mmp.in
index b10eb0b5a8..9f24927f27 100644
--- a/backends/platform/symbian/mmp/scummvm_draci.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_draci.mmp.in
@@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\draci
// *** Include paths
USERINCLUDE ..\..\..\..\engines
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/backends/platform/symbian/mmp/scummvm_drascula.mmp.in b/backends/platform/symbian/mmp/scummvm_drascula.mmp.in
index 529eeb9e0b..d8cc6da6ae 100644
--- a/backends/platform/symbian/mmp/scummvm_drascula.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_drascula.mmp.in
@@ -59,5 +59,5 @@ SOURCEPATH ..\..\..\..\engines\drascula
// *** Include paths
USERINCLUDE ..\..\..\..\engines
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/backends/platform/symbian/mmp/scummvm_gob.mmp.in b/backends/platform/symbian/mmp/scummvm_gob.mmp.in
index fa2ce0dedf..ce94f85283 100644
--- a/backends/platform/symbian/mmp/scummvm_gob.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_gob.mmp.in
@@ -59,5 +59,5 @@ SOURCEPATH ..\..\..\..\engines\gob
// *** Include paths
USERINCLUDE ..\..\..\..\engines
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/backends/platform/symbian/mmp/scummvm_groovie.mmp.in b/backends/platform/symbian/mmp/scummvm_groovie.mmp.in
index 7a03cc1745..7229edf4aa 100644
--- a/backends/platform/symbian/mmp/scummvm_groovie.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_groovie.mmp.in
@@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\groovie
// *** Include paths
USERINCLUDE ..\..\..\..\engines
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/backends/platform/symbian/mmp/scummvm_hugo.mmp.in b/backends/platform/symbian/mmp/scummvm_hugo.mmp.in
index 301d23cab4..b3f9edd1e6 100644
--- a/backends/platform/symbian/mmp/scummvm_hugo.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_hugo.mmp.in
@@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\hugo
// *** Include paths
USERINCLUDE ..\..\..\..\engines
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/backends/platform/symbian/mmp/scummvm_kyra.mmp.in b/backends/platform/symbian/mmp/scummvm_kyra.mmp.in
index 5fa5fdac6a..654632c229 100644
--- a/backends/platform/symbian/mmp/scummvm_kyra.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_kyra.mmp.in
@@ -65,5 +65,5 @@ SOURCEPATH ..\..\..\..\engines\kyra
// *** Include paths
USERINCLUDE ..\..\..\..\engines
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/backends/platform/symbian/mmp/scummvm_lastexpress.mmp.in b/backends/platform/symbian/mmp/scummvm_lastexpress.mmp.in
index a297137f02..418730c064 100644
--- a/backends/platform/symbian/mmp/scummvm_lastexpress.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_lastexpress.mmp.in
@@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\lastexpress
// *** Include paths
USERINCLUDE ..\..\..\..\engines
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/backends/platform/symbian/mmp/scummvm_lure.mmp.in b/backends/platform/symbian/mmp/scummvm_lure.mmp.in
index c4a3900a05..e1a63b602b 100644
--- a/backends/platform/symbian/mmp/scummvm_lure.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_lure.mmp.in
@@ -59,5 +59,5 @@ SOURCEPATH ..\..\..\..\engines\lure
// *** Include paths
USERINCLUDE ..\..\..\..\engines
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/backends/platform/symbian/mmp/scummvm_m4.mmp.in b/backends/platform/symbian/mmp/scummvm_m4.mmp.in
index f4430f2e58..c2e1ce4e8b 100644
--- a/backends/platform/symbian/mmp/scummvm_m4.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_m4.mmp.in
@@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\m4
// *** Include paths
USERINCLUDE ..\..\..\..\engines
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/backends/platform/symbian/mmp/scummvm_made.mmp.in b/backends/platform/symbian/mmp/scummvm_made.mmp.in
index bf50157224..91b9ca756d 100644
--- a/backends/platform/symbian/mmp/scummvm_made.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_made.mmp.in
@@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\made
// *** Include paths
USERINCLUDE ..\..\..\..\engines
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/backends/platform/symbian/mmp/scummvm_mohawk.mmp.in b/backends/platform/symbian/mmp/scummvm_mohawk.mmp.in
index 78e931b06f..0edba5eb4d 100644
--- a/backends/platform/symbian/mmp/scummvm_mohawk.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_mohawk.mmp.in
@@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\mohawk
// *** Include paths
USERINCLUDE ..\..\..\..\engines
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/backends/platform/symbian/mmp/scummvm_parallaction.mmp.in b/backends/platform/symbian/mmp/scummvm_parallaction.mmp.in
index e96ab907cd..744a756f4e 100644
--- a/backends/platform/symbian/mmp/scummvm_parallaction.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_parallaction.mmp.in
@@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\parallaction
// *** Include paths
USERINCLUDE ..\..\..\..\engines
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/backends/platform/symbian/mmp/scummvm_queen.mmp.in b/backends/platform/symbian/mmp/scummvm_queen.mmp.in
index 4f9d9319e0..bf88744701 100644
--- a/backends/platform/symbian/mmp/scummvm_queen.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_queen.mmp.in
@@ -59,5 +59,5 @@ SOURCEPATH ..\..\..\..\engines\queen
// *** Include paths
USERINCLUDE ..\..\..\..\engines
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/backends/platform/symbian/mmp/scummvm_saga.mmp.in b/backends/platform/symbian/mmp/scummvm_saga.mmp.in
index e5beefc0c5..a95ee1e7fd 100644
--- a/backends/platform/symbian/mmp/scummvm_saga.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_saga.mmp.in
@@ -71,5 +71,5 @@ SOURCEPATH ..\..\..\..\engines\saga
// *** Include paths
USERINCLUDE ..\..\..\..\engines
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/backends/platform/symbian/mmp/scummvm_sci.mmp.in b/backends/platform/symbian/mmp/scummvm_sci.mmp.in
index eb0d1b020f..ca9e712f3f 100644
--- a/backends/platform/symbian/mmp/scummvm_sci.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_sci.mmp.in
@@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\sci
// *** Include paths
USERINCLUDE ..\..\..\..\engines
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/backends/platform/symbian/mmp/scummvm_scumm.mmp.in b/backends/platform/symbian/mmp/scummvm_scumm.mmp.in
index 6b5d249422..0a3cd9bb7d 100644
--- a/backends/platform/symbian/mmp/scummvm_scumm.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_scumm.mmp.in
@@ -82,7 +82,7 @@ SOURCE smush/codec47ARM.s // ARM version: add ASM routines
// *** Include paths
USERINCLUDE ..\..\..\..\engines ..\..\..\..\engines\scumm\smush ..\..\..\..\engines\scumm\insane
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version
SYSTEMINCLUDE \epoc32\include\libc
diff --git a/backends/platform/symbian/mmp/scummvm_sky.mmp.in b/backends/platform/symbian/mmp/scummvm_sky.mmp.in
index d1d222b1fa..1bc2301745 100644
--- a/backends/platform/symbian/mmp/scummvm_sky.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_sky.mmp.in
@@ -59,5 +59,5 @@ SOURCEPATH ..\..\..\..\engines\sky
// *** Include paths
USERINCLUDE ..\..\..\..\engines ..\..\..\..\engines\sky\music
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/backends/platform/symbian/mmp/scummvm_sword1.mmp.in b/backends/platform/symbian/mmp/scummvm_sword1.mmp.in
index 0aaf9d504b..6bf543070b 100644
--- a/backends/platform/symbian/mmp/scummvm_sword1.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_sword1.mmp.in
@@ -59,5 +59,5 @@ SOURCEPATH ..\..\..\..\engines\sword1
// *** Include paths
USERINCLUDE ..\..\..\..\engines
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/backends/platform/symbian/mmp/scummvm_sword2.mmp.in b/backends/platform/symbian/mmp/scummvm_sword2.mmp.in
index 4a7181709f..cee4143f94 100644
--- a/backends/platform/symbian/mmp/scummvm_sword2.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_sword2.mmp.in
@@ -59,5 +59,5 @@ SOURCEPATH ..\..\..\..\engines\sword2
// *** Include paths
USERINCLUDE ..\..\..\..\engines
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/backends/platform/symbian/mmp/scummvm_teenagent.mmp.in b/backends/platform/symbian/mmp/scummvm_teenagent.mmp.in
index 7832fc4880..fa4ef79692 100644
--- a/backends/platform/symbian/mmp/scummvm_teenagent.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_teenagent.mmp.in
@@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\teenagent
// *** Include paths
USERINCLUDE ..\..\..\..\engines
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/backends/platform/symbian/mmp/scummvm_tinsel.mmp.in b/backends/platform/symbian/mmp/scummvm_tinsel.mmp.in
index 6190ec8152..569b79ba3c 100644
--- a/backends/platform/symbian/mmp/scummvm_tinsel.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_tinsel.mmp.in
@@ -59,5 +59,5 @@ SOURCEPATH ..\..\..\..\engines\tinsel
// *** Include paths
USERINCLUDE ..\..\..\..\engines
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/backends/platform/symbian/mmp/scummvm_toon.mmp.in b/backends/platform/symbian/mmp/scummvm_toon.mmp.in
index f2301c4ae2..f309e6d0fa 100644
--- a/backends/platform/symbian/mmp/scummvm_toon.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_toon.mmp.in
@@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\toon
// *** Include paths
USERINCLUDE ..\..\..\..\engines
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/backends/platform/symbian/mmp/scummvm_touche.mmp.in b/backends/platform/symbian/mmp/scummvm_touche.mmp.in
index 9e4c3d0496..ab42afe304 100644
--- a/backends/platform/symbian/mmp/scummvm_touche.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_touche.mmp.in
@@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\touche
// *** Include paths
USERINCLUDE ..\..\..\..\engines
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/backends/platform/symbian/mmp/scummvm_tucker.mmp.in b/backends/platform/symbian/mmp/scummvm_tucker.mmp.in
index d193ac49a9..434072bc96 100644
--- a/backends/platform/symbian/mmp/scummvm_tucker.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_tucker.mmp.in
@@ -59,6 +59,6 @@ SOURCEPATH ..\..\..\..\engines\tucker
// *** Include paths
USERINCLUDE ..\..\..\..\engines
-USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\audio ..\src
SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/base/commandLine.cpp b/base/commandLine.cpp
index 36218376c8..f920dd0170 100644
--- a/base/commandLine.cpp
+++ b/base/commandLine.cpp
@@ -178,6 +178,9 @@ void registerDefaults() {
ConfMan.registerDefault("midi_gain", 100);
// ConfMan.registerDefault("music_driver", ???);
+ ConfMan.registerDefault("mt32_device", "null");
+ ConfMan.registerDefault("gm_device", "null");
+
ConfMan.registerDefault("cdrom", 0);
// Game specific
diff --git a/common/config-manager.cpp b/common/config-manager.cpp
index 0561f390a4..c8c0999a25 100644
--- a/common/config-manager.cpp
+++ b/common/config-manager.cpp
@@ -103,9 +103,9 @@ void ConfigManager::loadConfigFile(const String &filename) {
FSNode node(filename);
File cfg_file;
if (!cfg_file.open(node)) {
- debug("Creating configuration file: %s\n", filename.c_str());
+ debug("Creating configuration file: %s", filename.c_str());
} else {
- debug("Using configuration file: %s\n", _filename.c_str());
+ debug("Using configuration file: %s", _filename.c_str());
loadFromStream(cfg_file);
}
}
diff --git a/configure b/configure
index 714404130a..fb453d1afa 100755
--- a/configure
+++ b/configure
@@ -1505,12 +1505,21 @@ case $_host_os in
;;
android)
CXXFLAGS="$CXXFLAGS --sysroot=$ANDROID_NDK/platforms/android-4/arch-arm"
- CXXFLAGS="$CXXFLAGS -Os -mandroid -msoft-float -mthumb-interwork"
- CXXFLAGS="$CXXFLAGS -march=armv5te -mtune=xscale"
+ CXXFLAGS="$CXXFLAGS -march=armv5te -mtune=xscale -msoft-float"
+ CXXFLAGS="$CXXFLAGS -fpic -ffunction-sections -funwind-tables"
+ if test "$_release_build" = yes; then
+ CXXFLAGS="$CXXFLAGS -fomit-frame-pointer -fstrict-aliasing"
+ else
+ CXXFLAGS="$CXXFLAGS -fno-omit-frame-pointer -fno-strict-aliasing"
+ fi
+ CXXFLAGS="$CXXFLAGS -finline-limit=300"
+ CXXFLAGS="$CXXFLAGS -Os -mthumb-interwork"
+ CXXFLAGS="$CXXFLAGS -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__"
+ CXXFLAGS="$CXXFLAGS -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__"
# supress 'mangling of 'va_list' has changed in GCC 4.4'
CXXFLAGS="$CXXFLAGS -Wno-psabi"
LDFLAGS="$LDFLAGS --sysroot=$ANDROID_NDK/platforms/android-4/arch-arm"
- LDFLAGS="$LDFLAGS -mandroid -mthumb-interwork"
+ LDFLAGS="$LDFLAGS -mthumb-interwork"
add_line_to_config_mk "ANDROID_SDK = $ANDROID_SDK"
_unix=yes
_seq_midi=no
@@ -1580,7 +1589,6 @@ case $_host_os in
DEFINES="$DEFINES -DSYSTEM_NOT_SUPPORTING_D_TYPE"
# Needs -lnetwork for the timidity MIDI driver
LIBS="$LIBS -lnetwork"
- CXXFLAGS="$CXXFLAGS -fhuge-objects"
_unix=yes
_seq_midi=no
;;
@@ -2096,7 +2104,7 @@ fi
# Enable 16bit support only for backends which support it
#
case $_backend in
- dingux | dreamcast | gph | openpandora | psp | samsungtv | sdl | wii)
+ android | dingux | dreamcast | gph | openpandora | psp | samsungtv | sdl | wii)
if test "$_16bit" = auto ; then
_16bit=yes
else
@@ -2956,6 +2964,10 @@ fi
#
case $_backend in
android)
+ # ssp at this point so the cxxtests link
+ CXXFLAGS="$CXXFLAGS -fstack-protector -Wa,--noexecstack"
+ LDFLAGS="$LDFLAGS -Wl,-z,noexecstack"
+
static_libs=''
system_libs=''
for lib in $LIBS; do
@@ -2974,7 +2986,7 @@ case $_backend in
# than pick up anything unhygenic from the Android libs.
LIBS="-Wl,-Bstatic $static_libs"
LIBS="$LIBS -Wl,-Bdynamic -lgcc $system_libs -llog -lGLESv1_CM"
- DEFINES="$DEFINES -D__ANDROID__ -DREDUCE_MEMORY_USAGE"
+ DEFINES="$DEFINES -DREDUCE_MEMORY_USAGE"
;;
dc)
INCLUDES="$INCLUDES "'-I$(srcdir)/backends/platform/dc -isystem $(ronindir)/include'
diff --git a/dists/android/AndroidManifest.xml b/dists/android/AndroidManifest.xml
index 2ea2b484ac..68c58d9aea 100644
--- a/dists/android/AndroidManifest.xml
+++ b/dists/android/AndroidManifest.xml
@@ -1,58 +1,61 @@
-<?xml version="1.0" encoding="utf-8"?> <!-- -*- xml -*- -->
+<?xml version="1.0" encoding="utf-8"?>
<!-- NB: android:versionCode needs to be bumped for formal releases -->
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="org.inodes.gus.scummvm"
- android:versionCode="6" android:versionName="1.3.0git"
- android:installLocation="preferExternal">
-
- <!-- This version works on Android 1.5 (SDK 3) and newer, but we
- want Android 2.2 (SDK 8) defaults and features.
- -->
- <uses-sdk android:minSdkVersion="3"
- android:targetSdkVersion="8" />
-
- <application android:name=".ScummVMApplication"
- android:label="@string/app_name"
- android:description="@string/app_desc"
- android:icon="@drawable/scummvm"
- android:persistent="true">
- <activity android:name=".ScummVMActivity"
- android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
- android:screenOrientation="landscape"
- android:configChanges="orientation|keyboardHidden"
- android:windowSoftInputMode="adjustResize">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- </intent-filter>
- </activity>
- <activity android:name=".Unpacker"
- android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
- android:screenOrientation="landscape"
- android:configChanges="orientation|keyboardHidden">
- <meta-data android:name="org.inodes.gus.unpacker.nextActivity"
- android:value="org.inodes.gus.scummvm/.ScummVMActivity" />
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-
- <permission android:name="org.inodes.gus.scummvm.permission.SCUMMVM_PLUGIN"
- android:label="@string/scummvm_perm_plugin_label"
- android:description="@string/scummvm_perm_plugin_desc"
- android:protectionLevel="signature" />
-
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-
-
- <!-- Always needs some sort of qwerty keyboard.
- Can work with a D-pad / trackball -->
- <uses-configuration android:reqFiveWayNav="true"
- android:reqKeyboardType="qwerty"/>
- <!-- .. or touchscreen -->
- <uses-configuration android:reqTouchScreen="finger"
- android:reqKeyboardType="qwerty"/>
- <uses-configuration android:reqTouchScreen="stylus"
- android:reqKeyboardType="qwerty"/>
+ package="org.inodes.gus.scummvm"
+ android:versionCode="6"
+ android:versionName="1.3.0git"
+ android:installLocation="preferExternal">
+
+ <!-- This version works on Android 1.5 (SDK 3) and newer, but we
+ want Android 2.2 (SDK 8) defaults and features. -->
+ <uses-sdk android:minSdkVersion="3"
+ android:targetSdkVersion="8"/>
+
+ <application android:name=".ScummVMApplication"
+ android:label="@string/app_name"
+ android:description="@string/app_desc"
+ android:icon="@drawable/scummvm">
+ <activity android:name=".ScummVMActivity"
+ android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+ android:screenOrientation="landscape"
+ android:configChanges="orientation|keyboardHidden"
+ android:windowSoftInputMode="adjustResize">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ </intent-filter>
+ </activity>
+
+ <activity android:name=".Unpacker"
+ android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+ android:screenOrientation="landscape"
+ android:configChanges="orientation|keyboardHidden">
+ <meta-data android:name="org.inodes.gus.unpacker.nextActivity"
+ android:value="org.inodes.gus.scummvm/.ScummVMActivity"/>
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+
+ <permission android:name="org.inodes.gus.scummvm.permission.SCUMMVM_PLUGIN"
+ android:label="@string/scummvm_perm_plugin_label"
+ android:description="@string/scummvm_perm_plugin_desc"
+ android:protectionLevel="signature"/>
+
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+
+ <!-- Always needs some sort of qwerty keyboard.
+ Can work with a D-pad / trackball -->
+ <uses-configuration android:reqFiveWayNav="true"
+ android:reqKeyboardType="qwerty"/>
+
+ <!-- .. or touchscreen -->
+ <uses-configuration android:reqTouchScreen="finger"
+ android:reqKeyboardType="qwerty"/>
+
+ <uses-configuration android:reqTouchScreen="stylus"
+ android:reqKeyboardType="qwerty"/>
</manifest>
+
diff --git a/dists/android/AndroidManifest.xml.in b/dists/android/AndroidManifest.xml.in
index 26a94f957b..7a75b6fd0b 100644
--- a/dists/android/AndroidManifest.xml.in
+++ b/dists/android/AndroidManifest.xml.in
@@ -1,58 +1,61 @@
-<?xml version="1.0" encoding="utf-8"?> <!-- -*- xml -*- -->
+<?xml version="1.0" encoding="utf-8"?>
<!-- NB: android:versionCode needs to be bumped for formal releases -->
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="org.inodes.gus.scummvm"
- android:versionCode="6" android:versionName="@VERSION@"
- android:installLocation="preferExternal">
-
- <!-- This version works on Android 1.5 (SDK 3) and newer, but we
- want Android 2.2 (SDK 8) defaults and features.
- -->
- <uses-sdk android:minSdkVersion="3"
- android:targetSdkVersion="8" />
-
- <application android:name=".ScummVMApplication"
- android:label="@string/app_name"
- android:description="@string/app_desc"
- android:icon="@drawable/scummvm"
- android:persistent="true">
- <activity android:name=".ScummVMActivity"
- android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
- android:screenOrientation="landscape"
- android:configChanges="orientation|keyboardHidden"
- android:windowSoftInputMode="adjustResize">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- </intent-filter>
- </activity>
- <activity android:name=".Unpacker"
- android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
- android:screenOrientation="landscape"
- android:configChanges="orientation|keyboardHidden">
- <meta-data android:name="org.inodes.gus.unpacker.nextActivity"
- android:value="org.inodes.gus.scummvm/.ScummVMActivity" />
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-
- <permission android:name="org.inodes.gus.scummvm.permission.SCUMMVM_PLUGIN"
- android:label="@string/scummvm_perm_plugin_label"
- android:description="@string/scummvm_perm_plugin_desc"
- android:protectionLevel="signature" />
-
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-
-
- <!-- Always needs some sort of qwerty keyboard.
- Can work with a D-pad / trackball -->
- <uses-configuration android:reqFiveWayNav="true"
- android:reqKeyboardType="qwerty"/>
- <!-- .. or touchscreen -->
- <uses-configuration android:reqTouchScreen="finger"
- android:reqKeyboardType="qwerty"/>
- <uses-configuration android:reqTouchScreen="stylus"
- android:reqKeyboardType="qwerty"/>
+ package="org.inodes.gus.scummvm"
+ android:versionCode="6"
+ android:versionName="@VERSION@"
+ android:installLocation="preferExternal">
+
+ <!-- This version works on Android 1.5 (SDK 3) and newer, but we
+ want Android 2.2 (SDK 8) defaults and features. -->
+ <uses-sdk android:minSdkVersion="3"
+ android:targetSdkVersion="8"/>
+
+ <application android:name=".ScummVMApplication"
+ android:label="@string/app_name"
+ android:description="@string/app_desc"
+ android:icon="@drawable/scummvm">
+ <activity android:name=".ScummVMActivity"
+ android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+ android:screenOrientation="landscape"
+ android:configChanges="orientation|keyboardHidden"
+ android:windowSoftInputMode="adjustResize">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ </intent-filter>
+ </activity>
+
+ <activity android:name=".Unpacker"
+ android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+ android:screenOrientation="landscape"
+ android:configChanges="orientation|keyboardHidden">
+ <meta-data android:name="org.inodes.gus.unpacker.nextActivity"
+ android:value="org.inodes.gus.scummvm/.ScummVMActivity"/>
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+
+ <permission android:name="org.inodes.gus.scummvm.permission.SCUMMVM_PLUGIN"
+ android:label="@string/scummvm_perm_plugin_label"
+ android:description="@string/scummvm_perm_plugin_desc"
+ android:protectionLevel="signature"/>
+
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+
+ <!-- Always needs some sort of qwerty keyboard.
+ Can work with a D-pad / trackball -->
+ <uses-configuration android:reqFiveWayNav="true"
+ android:reqKeyboardType="qwerty"/>
+
+ <!-- .. or touchscreen -->
+ <uses-configuration android:reqTouchScreen="finger"
+ android:reqKeyboardType="qwerty"/>
+
+ <uses-configuration android:reqTouchScreen="stylus"
+ android:reqKeyboardType="qwerty"/>
</manifest>
+
diff --git a/engines/agi/agi.cpp b/engines/agi/agi.cpp
index fdc05f0ba9..c5a1f81af6 100644
--- a/engines/agi/agi.cpp
+++ b/engines/agi/agi.cpp
@@ -273,23 +273,16 @@ void AgiEngine::processEvents() {
}
void AgiEngine::pollTimer() {
- uint32 dm;
+ _lastTick += 50;
- if (_tickTimer < _lastTickTimer)
- _lastTickTimer = 0;
-
- while ((dm = _tickTimer - _lastTickTimer) < 5) {
+ while (_system->getMillis() < _lastTick) {
processEvents();
_console->onFrame();
_system->delayMillis(10);
_system->updateScreen();
}
- _lastTickTimer = _tickTimer;
-}
-void AgiEngine::agiTimerFunctionLow(void *refCon) {
- AgiEngine *self = (AgiEngine *)refCon;
- self->_tickTimer++;
+ _lastTick = _system->getMillis();
}
void AgiEngine::pause(uint32 msec) {
@@ -532,9 +525,6 @@ AgiEngine::AgiEngine(OSystem *syst, const AGIGameDescription *gameDesc) : AgiBas
_allowSynthetic = false;
- _tickTimer = 0;
- _lastTickTimer = 0;
-
_intobj = NULL;
_menu = NULL;
@@ -646,11 +636,10 @@ void AgiEngine::initialize() {
_lastSaveTime = 0;
- _timer->installTimerProc(agiTimerFunctionLow, 10 * 1000, this);
+ _lastTick = _system->getMillis();
debugC(2, kDebugLevelMain, "Detect game");
-
if (agiDetectGame() == errOK) {
_game.state = STATE_LOADED;
debugC(2, kDebugLevelMain, "game loaded");
@@ -662,8 +651,6 @@ void AgiEngine::initialize() {
}
AgiEngine::~AgiEngine() {
- _timer->removeTimerProc(agiTimerFunctionLow);
-
// If the engine hasn't been initialized yet via AgiEngine::initialize(), don't attempt to free any resources,
// as they haven't been allocated. Fixes bug #1742432 - AGI: Engine crashes if no game is detected
if (_game.state == STATE_INIT) {
@@ -719,7 +706,7 @@ void AgiEngine::parseFeatures() {
/* FIXME: Seems this method doesn't really do anything. It might
be a leftover that could be removed, except that some of its
intended purpose may still need to be reimplemented.
-
+
[0:29] <Fingolfin> can you tell me what the point behind AgiEngine::parseFeatures() is?
[0:30] <_sev> when games are created with WAGI studio
[0:31] <_sev> it creates .wag site with game-specific features such as full game title, whether to use AGIMOUSE etc
diff --git a/engines/agi/agi.h b/engines/agi/agi.h
index 89b116daec..df19f55b52 100644
--- a/engines/agi/agi.h
+++ b/engines/agi/agi.h
@@ -810,9 +810,7 @@ public:
Common::Error saveGameState(int slot, const char *desc);
private:
-
- uint32 _tickTimer;
- uint32 _lastTickTimer;
+ uint32 _lastTick;
int _keyQueue[KEY_QUEUE_SIZE];
int _keyQueueStart;
@@ -883,7 +881,6 @@ public:
virtual bool isKeypress();
virtual void clearKeyQueue();
- static void agiTimerFunctionLow(void *refCon);
void initPriTable();
void newInputMode(InputMode mode);
diff --git a/engines/agi/detection_tables.h b/engines/agi/detection_tables.h
index 711701f55a..fb1b830e24 100644
--- a/engines/agi/detection_tables.h
+++ b/engines/agi/detection_tables.h
@@ -569,6 +569,7 @@ static const AGIGameDescription gameDescriptions[] = {
GAME_PS("xmascard", "", "25ad35e9628fc77e5e0dd35852a272b6", 768, 0x2440, GID_XMASCARD, Common::kPlatformCoCo3),
FANMADE_F("2 Player Demo", "4279f46b3cebd855132496476b1d2cca", GF_AGIMOUSE),
+ FANMADE("AGI Combat", "0be6a8a9e19203dcca0067d280798871"),
FANMADE("AGI Contest 1 Template", "d879aed25da6fc655564b29567358ae2"),
FANMADE("AGI Contest 2 Template", "5a2fb2894207eff36c72f5c1b08bcc07"),
FANMADE("AGI Mouse Demo 0.60 demo 1", "c07e2519de674c67386cb2cc6f2e3904"),
diff --git a/engines/agi/preagi.cpp b/engines/agi/preagi.cpp
index fe864d7659..1aa6ef5cc4 100644
--- a/engines/agi/preagi.cpp
+++ b/engines/agi/preagi.cpp
@@ -122,9 +122,6 @@ void PreAgiEngine::initialize() {
_mixer->playStream(Audio::Mixer::kSFXSoundType, &_speakerHandle,
_speakerStream, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
-
- //_timer->installTimerProc(agiTimerFunctionLow, 10 * 1000, NULL);
-
debugC(2, kDebugLevelMain, "Detect game");
// clear all resources and events
diff --git a/engines/agos/agos.h b/engines/agos/agos.h
index 7201dfd9d3..9c2a2929e3 100644
--- a/engines/agos/agos.h
+++ b/engines/agos/agos.h
@@ -2018,7 +2018,7 @@ protected:
void scrollOracleUp();
void scrollOracleDown();
- void listSaveGames(int n);
+ void listSaveGamesFeeble();
void saveUserGame(int slot);
void windowBackSpace(WindowBlock *window);
diff --git a/engines/agos/animation.cpp b/engines/agos/animation.cpp
index d39ca377dc..10d3d7f1ff 100644
--- a/engines/agos/animation.cpp
+++ b/engines/agos/animation.cpp
@@ -368,7 +368,7 @@ void MoviePlayerDXA::handleNextFrame() {
bool MoviePlayerDXA::processFrame() {
Graphics::Surface *screen = _vm->_system->lockScreen();
- copyFrameToBuffer((byte *)screen->pixels, (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, _vm->_screenWidth);
+ copyFrameToBuffer((byte *)screen->pixels, (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, screen->pitch);
_vm->_system->unlockScreen();
Common::Rational soundTime(_mixer->getSoundElapsedTime(_bgSound), 1000);
@@ -482,7 +482,7 @@ void MoviePlayerSMK::nextFrame() {
bool MoviePlayerSMK::processFrame() {
Graphics::Surface *screen = _vm->_system->lockScreen();
- copyFrameToBuffer((byte *)screen->pixels, (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, _vm->_screenWidth);
+ copyFrameToBuffer((byte *)screen->pixels, (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, screen->pitch);
_vm->_system->unlockScreen();
uint32 waitTime = getTimeToNextFrame();
diff --git a/engines/agos/oracle.cpp b/engines/agos/oracle.cpp
index 0a4ab8246f..8ff79965e4 100644
--- a/engines/agos/oracle.cpp
+++ b/engines/agos/oracle.cpp
@@ -369,7 +369,7 @@ void AGOSEngine_Feeble::swapCharacterLogo() {
}
}
-void AGOSEngine_Feeble::listSaveGames(int n) {
+void AGOSEngine_Feeble::listSaveGamesFeeble() {
char b[108];
Common::InSaveFile *in;
uint16 j, k, z, maxFiles;
@@ -377,8 +377,8 @@ void AGOSEngine_Feeble::listSaveGames(int n) {
memset(b, 0, 108);
maxFiles = countSaveGames() - 1;
- j = maxFiles - n + 1;
- k = maxFiles - j + 1;
+ j = maxFiles;
+ k = 1;
z = maxFiles;
if (getBitFlag(95)) {
j++;
diff --git a/engines/agos/script_ff.cpp b/engines/agos/script_ff.cpp
index 3198b1b499..dbd89cebf5 100644
--- a/engines/agos/script_ff.cpp
+++ b/engines/agos/script_ff.cpp
@@ -430,8 +430,7 @@ void AGOSEngine_Feeble::off_loadUserGame() {
}
void AGOSEngine_Feeble::off_listSaveGames() {
- // 134: dummy opcode?
- listSaveGames(1);
+ listSaveGamesFeeble();
}
void AGOSEngine_Feeble::off_checkCD() {
diff --git a/engines/cruise/cruise_main.cpp b/engines/cruise/cruise_main.cpp
index 6b487fadc9..99de66deb6 100644
--- a/engines/cruise/cruise_main.cpp
+++ b/engines/cruise/cruise_main.cpp
@@ -1889,7 +1889,7 @@ void CruiseEngine::mainLoop() {
// FIXME: I suspect that the original game does multiple script executions between game frames; the bug with
// Raoul appearing when looking at the book is being there are 3 script iterations separation between the
// scene being changed to the book, and the Raoul actor being frozen/disabled. This loop is a hack to ensure
- // that when a background changes, a few extra script executions are done
+ // that does a few extra script executions for that scene
bool bgChanged;
int numIterations = 1;
@@ -1902,7 +1902,8 @@ void CruiseEngine::mainLoop() {
removeFinishedScripts(&relHead);
removeFinishedScripts(&procHead);
- if (!bgChanged && backgroundChanged[masterScreen]) {
+ if (!bgChanged && backgroundChanged[masterScreen] &&
+ !strcmp(backgroundTable[0].name, "S06B.PI1")) {
bgChanged = true;
numIterations += 2;
}
diff --git a/engines/cruise/script.cpp b/engines/cruise/script.cpp
index 317dcfbbe6..aae4dba475 100644
--- a/engines/cruise/script.cpp
+++ b/engines/cruise/script.cpp
@@ -619,13 +619,13 @@ int executeScripts(scriptInstanceStruct *ptr) {
positionInStack = 0;
do {
-//#ifdef SKIP_INTRO
+#ifdef SKIP_INTRO
if (currentScriptPtr->scriptOffset == 290
&& currentScriptPtr->overlayNumber == 4
&& currentScriptPtr->scriptNumber == 0) {
currentScriptPtr->scriptOffset = 923;
}
-//#endif
+#endif
opcodeType = getByteFromScript();
debugC(5, kCruiseDebugScript, "Script %s/%d ip=%d opcode=%d",
diff --git a/engines/engine.cpp b/engines/engine.cpp
index 0d92c1aef1..d773f370f5 100644
--- a/engines/engine.cpp
+++ b/engines/engine.cpp
@@ -145,7 +145,11 @@ void initCommonGFX(bool defaultTo1XScaler) {
assert(transientDomain);
const bool useDefaultGraphicsMode =
- !transientDomain->contains("gfx_mode") &&
+ (!transientDomain->contains("gfx_mode") ||
+ !scumm_stricmp(transientDomain->getVal("gfx_mode").c_str(), "normal") ||
+ !scumm_stricmp(transientDomain->getVal("gfx_mode").c_str(), "default")
+ )
+ &&
(
!gameDomain ||
!gameDomain->contains("gfx_mode") ||
@@ -155,10 +159,7 @@ void initCommonGFX(bool defaultTo1XScaler) {
// See if the game should default to 1x scaler
if (useDefaultGraphicsMode && defaultTo1XScaler) {
- // FIXME: As a hack, we use "1x" here. Would be nicer to use
- // getDefaultGraphicsMode() instead, but right now, we do not specify
- // whether that is a 1x scaler or not...
- g_system->setGraphicsMode("1x");
+ g_system->resetGraphicsScale();
} else {
// Override global scaler with any game-specific define
if (ConfMan.hasKey("gfx_mode")) {
diff --git a/engines/gob/save/saveload.h b/engines/gob/save/saveload.h
index c231c1dbbb..dc1c184504 100644
--- a/engines/gob/save/saveload.h
+++ b/engines/gob/save/saveload.h
@@ -305,7 +305,7 @@ protected:
int getSlot(int32 offset) const;
int getSlotRemainder(int32 offset) const;
- void buildIndex(byte *buffer) const;
+ void buildScreenshotIndex(byte *buffer) const;
protected:
uint32 _shotSize;
@@ -430,7 +430,7 @@ protected:
int getSlot(int32 offset) const;
int getSlotRemainder(int32 offset) const;
- void buildIndex(byte *buffer) const;
+ void buildScreenshotIndex(byte *buffer) const;
};
File *_file;
diff --git a/engines/gob/save/saveload_inca2.cpp b/engines/gob/save/saveload_inca2.cpp
index 68c76c3f2b..5fa1b69fa7 100644
--- a/engines/gob/save/saveload_inca2.cpp
+++ b/engines/gob/save/saveload_inca2.cpp
@@ -260,7 +260,7 @@ int SaveLoad_Inca2::ScreenshotHandler::File::getSlotRemainder(int32 offset) cons
return (offset - 80) % 15168;
}
-void SaveLoad_Inca2::ScreenshotHandler::File::buildIndex(byte *buffer) const {
+void SaveLoad_Inca2::ScreenshotHandler::File::buildScreenshotIndex(byte *buffer) const {
Common::SaveFileManager *saveMan = g_system->getSavefileManager();
Common::InSaveFile *in;
@@ -306,7 +306,7 @@ bool SaveLoad_Inca2::ScreenshotHandler::load(int16 dataVar, int32 size, int32 of
}
// Create/Fake the index
- _file->buildIndex(_index + 40);
+ _file->buildScreenshotIndex(_index + 40);
_vm->_inter->_variables->copyFrom(dataVar, _index + offset, size);
diff --git a/engines/gob/save/saveload_v3.cpp b/engines/gob/save/saveload_v3.cpp
index 098b8e1160..39edddb66f 100644
--- a/engines/gob/save/saveload_v3.cpp
+++ b/engines/gob/save/saveload_v3.cpp
@@ -367,7 +367,7 @@ int SaveLoad_v3::ScreenshotHandler::File::getSlotRemainder(int32 offset) const {
return ((offset - _shotIndexSize) % _shotSize);
}
-void SaveLoad_v3::ScreenshotHandler::File::buildIndex(byte *buffer) const {
+void SaveLoad_v3::ScreenshotHandler::File::buildScreenshotIndex(byte *buffer) const {
Common::SaveFileManager *saveMan = g_system->getSavefileManager();
Common::InSaveFile *in;
@@ -418,12 +418,12 @@ bool SaveLoad_v3::ScreenshotHandler::load(int16 dataVar, int32 size, int32 offse
if (_sShotType == kScreenshotTypeGob3) {
// Create/Fake the index
- _file->buildIndex(_index + 40);
+ _file->buildScreenshotIndex(_index + 40);
// The last 10 bytes are 0
memset(_index + 70, 0, 10);
} else if (_sShotType == kScreenshotTypeLost) {
// Create/Fake the index
- _file->buildIndex(_index);
+ _file->buildScreenshotIndex(_index);
// The last byte is 0
_index[30] = 0;
}
diff --git a/engines/gob/sound/bgatmosphere.cpp b/engines/gob/sound/bgatmosphere.cpp
index f0977aa45b..8850a727d3 100644
--- a/engines/gob/sound/bgatmosphere.cpp
+++ b/engines/gob/sound/bgatmosphere.cpp
@@ -47,7 +47,7 @@ BackgroundAtmosphere::~BackgroundAtmosphere() {
queueClear();
}
-void BackgroundAtmosphere::play() {
+void BackgroundAtmosphere::playBA() {
Common::StackLock slock(_mutex);
_queuePos = -1;
@@ -59,7 +59,7 @@ void BackgroundAtmosphere::play() {
SoundMixer::play(*_queue[_queuePos], 1, 0);
}
-void BackgroundAtmosphere::stop() {
+void BackgroundAtmosphere::stopBA() {
SoundMixer::stop(0);
}
diff --git a/engines/gob/sound/bgatmosphere.h b/engines/gob/sound/bgatmosphere.h
index 71a2263341..7e58c0b4e9 100644
--- a/engines/gob/sound/bgatmosphere.h
+++ b/engines/gob/sound/bgatmosphere.h
@@ -46,8 +46,8 @@ public:
BackgroundAtmosphere(Audio::Mixer &mixer);
~BackgroundAtmosphere();
- void play();
- void stop();
+ void playBA();
+ void stopBA();
void setPlayMode(PlayMode mode);
diff --git a/engines/gob/sound/sound.cpp b/engines/gob/sound/sound.cpp
index 5950388c28..1aa63eb940 100644
--- a/engines/gob/sound/sound.cpp
+++ b/engines/gob/sound/sound.cpp
@@ -648,7 +648,7 @@ void Sound::bgPlay(const char *file, SoundType type) {
debugC(1, kDebugSound, "BackgroundAtmosphere: Playing \"%s\"", file);
- _bgatmos->stop();
+ _bgatmos->stopBA();
_bgatmos->queueClear();
SoundDesc *sndDesc = new SoundDesc;
@@ -658,7 +658,7 @@ void Sound::bgPlay(const char *file, SoundType type) {
}
_bgatmos->queueSample(*sndDesc);
- _bgatmos->play();
+ _bgatmos->playBA();
}
void Sound::bgPlay(const char *base, const char *ext, SoundType type, int count) {
@@ -667,7 +667,7 @@ void Sound::bgPlay(const char *base, const char *ext, SoundType type, int count)
debugC(1, kDebugSound, "BackgroundAtmosphere: Playing \"%s\" (%d)", base, count);
- _bgatmos->stop();
+ _bgatmos->stopBA();
_bgatmos->queueClear();
int length = strlen(base) + 7;
@@ -684,7 +684,7 @@ void Sound::bgPlay(const char *base, const char *ext, SoundType type, int count)
delete sndDesc;
}
- _bgatmos->play();
+ _bgatmos->playBA();
}
void Sound::bgStop() {
@@ -693,7 +693,7 @@ void Sound::bgStop() {
debugC(1, kDebugSound, "BackgroundAtmosphere: Stopping playback");
- _bgatmos->stop();
+ _bgatmos->stopBA();
_bgatmos->queueClear();
}
diff --git a/engines/groovie/music.h b/engines/groovie/music.h
index 74bb6f98e5..5974559c53 100644
--- a/engines/groovie/music.h
+++ b/engines/groovie/music.h
@@ -94,14 +94,14 @@ public:
~MusicPlayerMidi();
// MidiDriver interface
- int open();
- void close();
- void send(uint32 b);
- void metaEvent(byte type, byte *data, uint16 length);
- void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc);
- uint32 getBaseTempo();
- MidiChannel *allocateChannel();
- MidiChannel *getPercussionChannel();
+ virtual int open();
+ virtual void close();
+ virtual void send(uint32 b);
+ virtual void metaEvent(byte type, byte *data, uint16 length);
+ virtual void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc);
+ virtual uint32 getBaseTempo();
+ virtual MidiChannel *allocateChannel();
+ virtual MidiChannel *getPercussionChannel();
private:
// Channel volumes
@@ -115,7 +115,7 @@ protected:
MidiParser *_midiParser;
MidiDriver *_driver;
- void onTimerInternal();
+ virtual void onTimerInternal();
void updateVolume();
void unload();
diff --git a/engines/hugo/display.cpp b/engines/hugo/display.cpp
index 0210eb8f04..aa4d4384c1 100644
--- a/engines/hugo/display.cpp
+++ b/engines/hugo/display.cpp
@@ -189,13 +189,13 @@ void Screen::displayRect(const int16 x, const int16 y, const int16 dx, const int
* Alse save the new color in the current palette.
*/
void Screen::remapPal(const uint16 oldIndex, const uint16 newIndex) {
- debugC(1, kDebugDisplay, "Remap_pal(%d, %d)", oldIndex, newIndex);
+ debugC(1, kDebugDisplay, "RemapPal(%d, %d)", oldIndex, newIndex);
_curPalette[3 * oldIndex + 0] = _mainPalette[newIndex * 3 + 0];
_curPalette[3 * oldIndex + 1] = _mainPalette[newIndex * 3 + 1];
_curPalette[3 * oldIndex + 2] = _mainPalette[newIndex * 3 + 2];
- g_system->getPaletteManager()->setPalette(_curPalette, oldIndex, 1);
+ g_system->getPaletteManager()->setPalette(_curPalette, 0, _paletteSize / 3);
}
/**
@@ -214,10 +214,10 @@ void Screen::savePal(Common::WriteStream *f) const {
void Screen::restorePal(Common::ReadStream *f) {
debugC(1, kDebugDisplay, "restorePal()");
- for (int i = 0; i < _paletteSize; i++) {
+ for (int i = 0; i < _paletteSize; i++)
_curPalette[i] = f->readByte();
- g_system->getPaletteManager()->setPalette(_curPalette, i, 1);
- }
+
+ g_system->getPaletteManager()->setPalette(_curPalette, 0, _paletteSize / 3);
}
@@ -233,24 +233,6 @@ void Screen::setBackgroundColor(const uint16 color) {
}
/**
- * Return the overlay state (Foreground/Background) of the currently
- * processed object by looking down the current column for an overlay
- * base bit set (in which case the object is foreground).
- */
-overlayState_t Screen::findOvl(seq_t *seq_p, image_pt dst_p, uint16 y) {
- debugC(4, kDebugDisplay, "findOvl()");
-
- for (; y < seq_p->lines; y++) { // Each line in object
- byte ovb = _vm->_object->getBaseBoundary((uint16)(dst_p - _frontBuffer) >> 3); // Ptr into overlay bits
- if (ovb & (0x80 >> ((uint16)(dst_p - _frontBuffer) & 7))) // Overlay bit is set
- return kOvlForeground; // Found a bit - must be foreground
- dst_p += kXPix;
- }
-
- return kOvlBackground; // No bits set, must be background
-}
-
-/**
* Merge an object frame into _frontBuffer at sx, sy and update rectangle list.
* If fore TRUE, force object above any overlay
*/
@@ -261,7 +243,7 @@ void Screen::displayFrame(const int sx, const int sy, seq_t *seq, const bool for
image_pt subFrontBuffer = &_frontBuffer[sy * kXPix + sx]; // Ptr to offset in _frontBuffer
int16 frontBufferwrap = kXPix - seq->x2 - 1; // Wraps dest_p after each line
int16 imageWrap = seq->bytesPerLine8 - seq->x2 - 1;
- overlayState_t overlayState = kOvlUndef; // Overlay state of object
+ overlayState_t overlayState = (foreFl) ? kOvlForeground : kOvlUndef; // Overlay state of object
for (uint16 y = 0; y < seq->lines; y++) { // Each line in object
for (uint16 x = 0; x <= seq->x2; x++) {
if (*image) { // Non-transparent
@@ -269,7 +251,7 @@ void Screen::displayFrame(const int sx, const int sy, seq_t *seq, const bool for
if (ovlBound & (0x80 >> ((uint16)(subFrontBuffer - _frontBuffer) & 7))) { // Overlay bit is set
if (overlayState == kOvlUndef) // Overlay defined yet?
overlayState = findOvl(seq, subFrontBuffer, y);// No, find it.
- if (foreFl || overlayState == kOvlForeground) // Object foreground
+ if (overlayState == kOvlForeground) // Object foreground
*subFrontBuffer = *image; // Copy pixel
} else { // No overlay
*subFrontBuffer = *image; // Copy pixel
@@ -740,6 +722,25 @@ void Screen_v1d::loadFontArr(Common::ReadStream &in) {
}
}
+/**
+ * Return the overlay state (Foreground/Background) of the currently
+ * processed object by looking down the current column for an overlay
+ * base byte set (in which case the object is foreground).
+ */
+overlayState_t Screen_v1d::findOvl(seq_t *seq_p, image_pt dst_p, uint16 y) {
+ debugC(4, kDebugDisplay, "findOvl()");
+
+ uint16 index = (uint16)(dst_p - _frontBuffer) >> 3;
+
+ for (int i = 0; i < seq_p->lines-y; i++) { // Each line in object
+ if (_vm->_object->getBaseBoundary(index)) // If any overlay base byte is non-zero then the object is foreground, else back.
+ return kOvlForeground;
+ index += kCompLineSize;
+ }
+
+ return kOvlBackground; // No bits set, must be background
+}
+
Screen_v1w::Screen_v1w(HugoEngine *vm) : Screen(vm) {
}
@@ -790,5 +791,23 @@ void Screen_v1w::loadFontArr(Common::ReadStream &in) {
}
}
+/**
+ * Return the overlay state (Foreground/Background) of the currently
+ * processed object by looking down the current column for an overlay
+ * base bit set (in which case the object is foreground).
+ */
+overlayState_t Screen_v1w::findOvl(seq_t *seq_p, image_pt dst_p, uint16 y) {
+ debugC(4, kDebugDisplay, "findOvl()");
+
+ for (; y < seq_p->lines; y++) { // Each line in object
+ byte ovb = _vm->_object->getBaseBoundary((uint16)(dst_p - _frontBuffer) >> 3); // Ptr into overlay bits
+ if (ovb & (0x80 >> ((uint16)(dst_p - _frontBuffer) & 7))) // Overlay bit is set
+ return kOvlForeground; // Found a bit - must be foreground
+ dst_p += kXPix;
+ }
+
+ return kOvlBackground; // No bits set, must be background
+}
+
} // End of namespace Hugo
diff --git a/engines/hugo/display.h b/engines/hugo/display.h
index f234f76019..91e1752df0 100644
--- a/engines/hugo/display.h
+++ b/engines/hugo/display.h
@@ -101,10 +101,6 @@ protected:
static const byte stdMouseCursorHeight = 20;
static const byte stdMouseCursorWidth = 12;
- inline bool isInX(const int16 x, const rect_t *rect) const;
- inline bool isInY(const int16 y, const rect_t *rect) const;
- inline bool isOverlapping(const rect_t *rectA, const rect_t *rectB) const;
-
bool fontLoadedFl[kNumFonts];
// Fonts used in dib (non-GDI)
@@ -115,6 +111,14 @@ protected:
byte *_mainPalette;
int16 _arrayFontSize[kNumFonts];
+ viewdib_t _frontBuffer;
+
+ inline bool isInX(const int16 x, const rect_t *rect) const;
+ inline bool isInY(const int16 y, const rect_t *rect) const;
+ inline bool isOverlapping(const rect_t *rectA, const rect_t *rectB) const;
+
+ virtual overlayState_t findOvl(seq_t *seq_p, image_pt dst_p, uint16 y) = 0;
+
private:
byte *_curPalette;
byte _iconImage[kInvDx * kInvDy];
@@ -125,9 +129,6 @@ private:
int16 mergeLists(rect_t *list, rect_t *blist, const int16 len, int16 blen);
int16 center(const char *s) const;
- overlayState_t findOvl(seq_t *seq_p, image_pt dst_p, uint16 y);
-
- viewdib_t _frontBuffer;
viewdib_t _backBuffer;
viewdib_t _GUIBuffer; // User interface images
viewdib_t _backBufferBackup; // Backup _backBuffer during inventory
@@ -151,6 +152,8 @@ public:
void loadFont(int16 fontId);
void loadFontArr(Common::ReadStream &in);
+protected:
+ overlayState_t findOvl(seq_t *seq_p, image_pt dst_p, uint16 y);
};
class Screen_v1w : public Screen {
@@ -160,6 +163,8 @@ public:
void loadFont(int16 fontId);
void loadFontArr(Common::ReadStream &in);
+protected:
+ overlayState_t findOvl(seq_t *seq_p, image_pt dst_p, uint16 y);
};
} // End of namespace Hugo
diff --git a/engines/hugo/file.cpp b/engines/hugo/file.cpp
index 740513d7b2..c287ebf6e8 100644
--- a/engines/hugo/file.cpp
+++ b/engines/hugo/file.cpp
@@ -563,6 +563,8 @@ void FileManager::readBootFile() {
if (_vm->_gameVariant == kGameVariantH1Dos) {
//TODO initialize properly _boot structure
warning("readBootFile - Skipping as H1 Dos may be a freeware");
+ memset(_vm->_boot.distrib, '\0', sizeof(_vm->_boot.distrib));
+ _vm->_boot.registered = kRegFreeware;
return;
} else {
error("Missing startup file");
diff --git a/engines/hugo/hugo.cpp b/engines/hugo/hugo.cpp
index 2143b8cfde..9a622290f9 100644
--- a/engines/hugo/hugo.cpp
+++ b/engines/hugo/hugo.cpp
@@ -28,6 +28,7 @@
#include "common/events.h"
#include "common/EventRecorder.h"
#include "common/debug-channels.h"
+#include "common/config-manager.h"
#include "hugo/hugo.h"
#include "hugo/game.h"
@@ -253,14 +254,20 @@ Common::Error HugoEngine::run() {
// Start the state machine
_status.viewState = kViewIntroInit;
-
_status.doQuitFl = false;
+ int16 loadSlot = Common::ConfigManager::instance().getInt("save_slot");
+ if (loadSlot >= 0) {
+ _status.skipIntroFl = true;
+ _file->restoreGame(loadSlot);
+ _scheduler->restoreScreen(*_screen_p);
+ _status.viewState = kViewPlay;
+ }
while (!_status.doQuitFl) {
_screen->drawBoundaries();
-
g_system->updateScreen();
runMachine();
+
// Handle input
Common::Event event;
while (_eventMan->pollEvent(event)) {
@@ -285,6 +292,7 @@ Common::Error HugoEngine::run() {
break;
}
}
+
_mouse->mouseHandler(); // Mouse activity - adds to display list
_screen->displayList(kDisplayDisplay); // Blit the display list to screen
_status.doQuitFl |= shouldQuit(); // update game quit flag
@@ -623,6 +631,13 @@ void HugoEngine::readScreenFiles(const int screenNum) {
_file->readBackground(screenNum); // Scenery file
memcpy(_screen->getBackBuffer(), _screen->getFrontBuffer(), sizeof(_screen->getFrontBuffer())); // Make a copy
+
+ // Workaround for graphic glitches in DOS versions. Cleaning the overlays fix the problem
+ memset(_object->_objBound, '\0', sizeof(overlay_t));
+ memset(_object->_boundary, '\0', sizeof(overlay_t));
+ memset(_object->_overlay, '\0', sizeof(overlay_t));
+ memset(_object->_ovlBase, '\0', sizeof(overlay_t));
+
_file->readOverlay(screenNum, _object->_boundary, kOvlBoundary); // Boundary file
_file->readOverlay(screenNum, _object->_overlay, kOvlOverlay); // Overlay file
_file->readOverlay(screenNum, _object->_ovlBase, kOvlBase); // Overlay base file
@@ -658,7 +673,7 @@ void HugoEngine::calcMaxScore() {
void HugoEngine::endGame() {
debugC(1, kDebugEngine, "endGame");
- if (!_boot.registered)
+ if (_boot.registered != kRegRegistered)
Utils::Box(kBoxAny, "%s", _text->getTextEngine(kEsAdvertise));
Utils::Box(kBoxAny, "%s\n%s", _episode, getCopyrightString());
_status.viewState = kViewExit;
diff --git a/engines/hugo/hugo.h b/engines/hugo/hugo.h
index b2739c829e..2de442e0f7 100644
--- a/engines/hugo/hugo.h
+++ b/engines/hugo/hugo.h
@@ -58,7 +58,7 @@ class RandomSource;
*/
namespace Hugo {
-static const int kSavegameVersion = 3;
+static const int kSavegameVersion = 5;
static const int kInvDx = 32; // Width of an inventory icon
static const int kInvDy = 32; // Height of inventory icon
static const int kMaxTunes = 16; // Max number of tunes
@@ -125,6 +125,12 @@ enum HugoDebugChannels {
kDebugMusic = 1 << 9
};
+enum HugoRegistered {
+ kRegShareware = 0,
+ kRegRegistered,
+ kRegFreeware
+};
+
/**
* Ways to dismiss a text/prompt box
*/
@@ -242,7 +248,6 @@ public:
object_t *_hero;
byte *_screen_p;
byte _heroImage;
-
byte *_screenStates;
command_t _line; // Line of user text input
config_t _config; // User's config
diff --git a/engines/hugo/intro.cpp b/engines/hugo/intro.cpp
index 689dfbfd7c..7551476300 100644
--- a/engines/hugo/intro.cpp
+++ b/engines/hugo/intro.cpp
@@ -119,15 +119,19 @@ bool intro_v1d::introPlay() {
error("Unable to load font TMSRB.FON, face 'Tms Rmn', size 8");
char buffer[80];
- if (_vm->_boot.registered)
+ if (_vm->_boot.registered == kRegRegistered)
strcpy(buffer, "Registered Version");
- else
+ else if (_vm->_boot.registered == kRegShareware)
strcpy(buffer, "Shareware Version");
+ else if (_vm->_boot.registered == kRegFreeware)
+ strcpy(buffer, "Freeware Version");
+ else
+ error("Unknown registration flag in hugo.bsf: %d", _vm->_boot.registered);
font.drawString(&surf, buffer, 0, 163, 320, _TLIGHTMAGENTA, Graphics::kTextAlignCenter);
font.drawString(&surf, _vm->getCopyrightString(), 0, 176, 320, _TLIGHTMAGENTA, Graphics::kTextAlignCenter);
- if (scumm_stricmp(_vm->_boot.distrib, "David P. Gray")) {
+ if ((*_vm->_boot.distrib != '\0') && (scumm_stricmp(_vm->_boot.distrib, "David P. Gray"))) {
sprintf(buffer, "Distributed by %s.", _vm->_boot.distrib);
font.drawString(&surf, buffer, 0, 75, 320, _TMAGENTA, Graphics::kTextAlignCenter);
}
diff --git a/engines/hugo/object.cpp b/engines/hugo/object.cpp
index 04e3449cbb..f82a6a53c6 100644
--- a/engines/hugo/object.cpp
+++ b/engines/hugo/object.cpp
@@ -418,20 +418,25 @@ void ObjectHandler::readUse(Common::ReadStream &in, uses_t &curUse) {
*/
void ObjectHandler::loadObjectUses(Common::ReadStream &in) {
uses_t tmpUse;
+ tmpUse.targets = 0;
+
//Read _uses
for (int varnt = 0; varnt < _vm->_numVariant; varnt++) {
- tmpUse.targets = 0;
uint16 numElem = in.readUint16BE();
if (varnt == _vm->_gameVariant) {
_usesSize = numElem;
_uses = (uses_t *)malloc(sizeof(uses_t) * numElem);
}
- for (int i = 0; i < numElem; i++)
- readUse(in, (varnt == _vm->_gameVariant) ? _uses[i] : tmpUse);
-
- if (tmpUse.targets)
- free(tmpUse.targets);
+ for (int i = 0; i < numElem; i++) {
+ if (varnt == _vm->_gameVariant)
+ readUse(in, _uses[i]);
+ else {
+ readUse(in, tmpUse);
+ free(tmpUse.targets);
+ tmpUse.targets = 0;
+ }
+ }
}
}
@@ -497,20 +502,26 @@ void ObjectHandler::readObject(Common::ReadStream &in, object_t &curObject) {
void ObjectHandler::loadObjectArr(Common::ReadStream &in) {
debugC(6, kDebugObject, "loadObject(&in)");
object_t tmpObject;
+ tmpObject.stateDataIndex = 0;
for (int varnt = 0; varnt < _vm->_numVariant; varnt++) {
uint16 numElem = in.readUint16BE();
- tmpObject.stateDataIndex = 0;
+
if (varnt == _vm->_gameVariant) {
_objCount = numElem;
_objects = (object_t *)malloc(sizeof(object_t) * numElem);
}
- for (int i = 0; i < numElem; i++)
- readObject(in, (varnt == _vm->_gameVariant) ? _objects[i] : tmpObject);
-
- if (tmpObject.stateDataIndex)
- free(tmpObject.stateDataIndex);
+ for (int i = 0; i < numElem; i++) {
+ if (varnt == _vm->_gameVariant)
+ readObject(in, _objects[i]);
+ else {
+ // Skip over uneeded objects.
+ readObject(in, tmpObject);
+ free(tmpObject.stateDataIndex);
+ tmpObject.stateDataIndex = 0;
+ }
+ }
}
}
diff --git a/engines/hugo/schedule.cpp b/engines/hugo/schedule.cpp
index 5843c2317e..1556f3a154 100644
--- a/engines/hugo/schedule.cpp
+++ b/engines/hugo/schedule.cpp
@@ -708,21 +708,19 @@ void Scheduler::saveEvents(Common::WriteStream *f) {
f->writeSint16BE(tailIndex);
// Convert event ptrs to indexes
- event_t saveEventArr[kMaxEvents]; // Convert event ptrs to indexes
for (int16 i = 0; i < kMaxEvents; i++) {
event_t *wrkEvent = &_events[i];
- saveEventArr[i] = *wrkEvent;
- // fix up action pointer (to do better)
+ // fix up action pointer (to do better)
int16 index, subElem;
- findAction(saveEventArr[i].action, &index, &subElem);
- saveEventArr[i].action = (act*)((index << 16)| subElem);
-
- saveEventArr[i].prevEvent = (wrkEvent->prevEvent == 0) ? (event_t *) - 1 : (event_t *)(wrkEvent->prevEvent - _events);
- saveEventArr[i].nextEvent = (wrkEvent->nextEvent == 0) ? (event_t *) - 1 : (event_t *)(wrkEvent->nextEvent - _events);
+ findAction(wrkEvent->action, &index, &subElem);
+ f->writeSint16BE(index);
+ f->writeSint16BE(subElem);
+ f->writeByte((wrkEvent->localActionFl) ? 1 : 0);
+ f->writeUint32BE(wrkEvent->time);
+ f->writeSint16BE((wrkEvent->prevEvent == 0) ? -1 : (wrkEvent->prevEvent - _events));
+ f->writeSint16BE((wrkEvent->nextEvent == 0) ? -1 : (wrkEvent->nextEvent - _events));
}
-
- f->write(saveEventArr, sizeof(saveEventArr));
}
/**
@@ -730,27 +728,11 @@ void Scheduler::saveEvents(Common::WriteStream *f) {
*/
void Scheduler::restoreActions(Common::ReadStream *f) {
-
for (int i = 0; i < _actListArrSize; i++) {
-
- // read all the sub elems
- int j = 0;
- do {
-
- // handle special case for a3, keep list pointer
- int* responsePtr = 0;
- if (_actListArr[i][j].a3.actType == PROMPT) {
- responsePtr = _actListArr[i][j].a3.responsePtr;
- }
-
- f->read(&_actListArr[i][j], sizeof(act));
-
- // handle special case for a3, reset list pointer
- if (_actListArr[i][j].a3.actType == PROMPT) {
- _actListArr[i][j].a3.responsePtr = responsePtr;
- }
- j++;
- } while (_actListArr[i][j-1].a0.actType != ANULL);
+ uint16 numSubElem = f->readUint16BE();
+ for (int j = 0; j < numSubElem; j++) {
+ readAct(*f, _actListArr[i][j]);
+ }
}
}
@@ -764,23 +746,301 @@ int16 Scheduler::calcMaxPoints() const {
/*
* Save the action data in the file with handle f
*/
-void Scheduler::saveActions(Common::WriteStream* f) const {
+void Scheduler::saveActions(Common::WriteStream *f) const {
+ byte subElemType;
+ int16 nbrCpt;
+ uint16 nbrSubElem;
+
for (int i = 0; i < _actListArrSize; i++) {
// write all the sub elems data
-
- int j = 0;
- do {
- f->write(&_actListArr[i][j], sizeof(act));
- j++;
- } while (_actListArr[i][j-1].a0.actType != ANULL);
+ for (nbrSubElem = 1; _actListArr[i][nbrSubElem - 1].a0.actType != ANULL; nbrSubElem++)
+ ;
+
+ f->writeUint16BE(nbrSubElem);
+ for (int j = 0; j < nbrSubElem; j++) {
+ subElemType = _actListArr[i][j].a0.actType;
+ f->writeByte(subElemType);
+ switch (subElemType) {
+ case ANULL: // -1
+ break;
+ case ASCHEDULE: // 0
+ f->writeSint16BE(_actListArr[i][j].a0.timer);
+ f->writeUint16BE(_actListArr[i][j].a0.actIndex);
+ break;
+ case START_OBJ: // 1
+ f->writeSint16BE(_actListArr[i][j].a1.timer);
+ f->writeSint16BE(_actListArr[i][j].a1.objIndex);
+ f->writeSint16BE(_actListArr[i][j].a1.cycleNumb);
+ f->writeByte(_actListArr[i][j].a1.cycle);
+ break;
+ case INIT_OBJXY: // 2
+ f->writeSint16BE(_actListArr[i][j].a2.timer);
+ f->writeSint16BE(_actListArr[i][j].a2.objIndex);
+ f->writeSint16BE(_actListArr[i][j].a2.x);
+ f->writeSint16BE(_actListArr[i][j].a2.y);
+ break;
+ case PROMPT: // 3
+ f->writeSint16BE(_actListArr[i][j].a3.timer);
+ f->writeSint16BE(_actListArr[i][j].a3.promptIndex);
+ for (nbrCpt = 0; _actListArr[i][j].a3.responsePtr[nbrCpt] != -1; nbrCpt++)
+ ;
+ nbrCpt++;
+ f->writeUint16BE(nbrCpt);
+ for (int k = 0; k < nbrCpt; k++)
+ f->writeSint16BE(_actListArr[i][j].a3.responsePtr[k]);
+ f->writeUint16BE(_actListArr[i][j].a3.actPassIndex);
+ f->writeUint16BE(_actListArr[i][j].a3.actFailIndex);
+ f->writeByte((_actListArr[i][j].a3.encodedFl) ? 1 : 0);
+ break;
+ case BKGD_COLOR: // 4
+ f->writeSint16BE(_actListArr[i][j].a4.timer);
+ f->writeUint32BE(_actListArr[i][j].a4.newBackgroundColor);
+ break;
+ case INIT_OBJVXY: // 5
+ f->writeSint16BE(_actListArr[i][j].a5.timer);
+ f->writeSint16BE(_actListArr[i][j].a5.objIndex);
+ f->writeSint16BE(_actListArr[i][j].a5.vx);
+ f->writeSint16BE(_actListArr[i][j].a5.vy);
+ break;
+ case INIT_CARRY: // 6
+ f->writeSint16BE(_actListArr[i][j].a6.timer);
+ f->writeSint16BE(_actListArr[i][j].a6.objIndex);
+ f->writeByte((_actListArr[i][j].a6.carriedFl) ? 1 : 0);
+ break;
+ case INIT_HF_COORD: // 7
+ f->writeSint16BE(_actListArr[i][j].a7.timer);
+ f->writeSint16BE(_actListArr[i][j].a7.objIndex);
+ break;
+ case NEW_SCREEN: // 8
+ f->writeSint16BE(_actListArr[i][j].a8.timer);
+ f->writeSint16BE(_actListArr[i][j].a8.screenIndex);
+ break;
+ case INIT_OBJSTATE: // 9
+ f->writeSint16BE(_actListArr[i][j].a9.timer);
+ f->writeSint16BE(_actListArr[i][j].a9.objIndex);
+ f->writeByte(_actListArr[i][j].a9.newState);
+ break;
+ case INIT_PATH: // 10
+ f->writeSint16BE(_actListArr[i][j].a10.timer);
+ f->writeSint16BE(_actListArr[i][j].a10.objIndex);
+ f->writeSint16BE(_actListArr[i][j].a10.newPathType);
+ f->writeByte(_actListArr[i][j].a10.vxPath);
+ f->writeByte(_actListArr[i][j].a10.vyPath);
+ break;
+ case COND_R: // 11
+ f->writeSint16BE(_actListArr[i][j].a11.timer);
+ f->writeSint16BE(_actListArr[i][j].a11.objIndex);
+ f->writeByte(_actListArr[i][j].a11.stateReq);
+ f->writeUint16BE(_actListArr[i][j].a11.actPassIndex);
+ f->writeUint16BE(_actListArr[i][j].a11.actFailIndex);
+ break;
+ case TEXT: // 12
+ f->writeSint16BE(_actListArr[i][j].a12.timer);
+ f->writeSint16BE(_actListArr[i][j].a12.stringIndex);
+ break;
+ case SWAP_IMAGES: // 13
+ f->writeSint16BE(_actListArr[i][j].a13.timer);
+ f->writeSint16BE(_actListArr[i][j].a13.objIndex1);
+ f->writeSint16BE(_actListArr[i][j].a13.objIndex2);
+ break;
+ case COND_SCR: // 14
+ f->writeSint16BE(_actListArr[i][j].a14.timer);
+ f->writeSint16BE(_actListArr[i][j].a14.objIndex);
+ f->writeSint16BE(_actListArr[i][j].a14.screenReq);
+ f->writeUint16BE(_actListArr[i][j].a14.actPassIndex);
+ f->writeUint16BE(_actListArr[i][j].a14.actFailIndex);
+ break;
+ case AUTOPILOT: // 15
+ f->writeSint16BE(_actListArr[i][j].a15.timer);
+ f->writeSint16BE(_actListArr[i][j].a15.objIndex1);
+ f->writeSint16BE(_actListArr[i][j].a15.objIndex2);
+ f->writeByte(_actListArr[i][j].a15.dx);
+ f->writeByte(_actListArr[i][j].a15.dy);
+ break;
+ case INIT_OBJ_SEQ: // 16
+ f->writeSint16BE(_actListArr[i][j].a16.timer);
+ f->writeSint16BE(_actListArr[i][j].a16.objIndex);
+ f->writeSint16BE(_actListArr[i][j].a16.seqIndex);
+ break;
+ case SET_STATE_BITS: // 17
+ f->writeSint16BE(_actListArr[i][j].a17.timer);
+ f->writeSint16BE(_actListArr[i][j].a17.objIndex);
+ f->writeSint16BE(_actListArr[i][j].a17.stateMask);
+ break;
+ case CLEAR_STATE_BITS: // 18
+ f->writeSint16BE(_actListArr[i][j].a18.timer);
+ f->writeSint16BE(_actListArr[i][j].a18.objIndex);
+ f->writeSint16BE(_actListArr[i][j].a18.stateMask);
+ break;
+ case TEST_STATE_BITS: // 19
+ f->writeSint16BE(_actListArr[i][j].a19.timer);
+ f->writeSint16BE(_actListArr[i][j].a19.objIndex);
+ f->writeSint16BE(_actListArr[i][j].a19.stateMask);
+ f->writeUint16BE(_actListArr[i][j].a19.actPassIndex);
+ f->writeUint16BE(_actListArr[i][j].a19.actFailIndex);
+ break;
+ case DEL_EVENTS: // 20
+ f->writeSint16BE(_actListArr[i][j].a20.timer);
+ f->writeByte(_actListArr[i][j].a20.actTypeDel);
+ break;
+ case GAMEOVER: // 21
+ f->writeSint16BE(_actListArr[i][j].a21.timer);
+ break;
+ case INIT_HH_COORD: // 22
+ f->writeSint16BE(_actListArr[i][j].a22.timer);
+ f->writeSint16BE(_actListArr[i][j].a22.objIndex);
+ break;
+ case EXIT: // 23
+ f->writeSint16BE(_actListArr[i][j].a23.timer);
+ break;
+ case BONUS: // 24
+ f->writeSint16BE(_actListArr[i][j].a24.timer);
+ f->writeSint16BE(_actListArr[i][j].a24.pointIndex);
+ break;
+ case COND_BOX: // 25
+ f->writeSint16BE(_actListArr[i][j].a25.timer);
+ f->writeSint16BE(_actListArr[i][j].a25.objIndex);
+ f->writeSint16BE(_actListArr[i][j].a25.x1);
+ f->writeSint16BE(_actListArr[i][j].a25.y1);
+ f->writeSint16BE(_actListArr[i][j].a25.x2);
+ f->writeSint16BE(_actListArr[i][j].a25.y2);
+ f->writeUint16BE(_actListArr[i][j].a25.actPassIndex);
+ f->writeUint16BE(_actListArr[i][j].a25.actFailIndex);
+ break;
+ case SOUND: // 26
+ f->writeSint16BE(_actListArr[i][j].a26.timer);
+ f->writeSint16BE(_actListArr[i][j].a26.soundIndex);
+ break;
+ case ADD_SCORE: // 27
+ f->writeSint16BE(_actListArr[i][j].a27.timer);
+ f->writeSint16BE(_actListArr[i][j].a27.objIndex);
+ break;
+ case SUB_SCORE: // 28
+ f->writeSint16BE(_actListArr[i][j].a28.timer);
+ f->writeSint16BE(_actListArr[i][j].a28.objIndex);
+ break;
+ case COND_CARRY: // 29
+ f->writeSint16BE(_actListArr[i][j].a29.timer);
+ f->writeSint16BE(_actListArr[i][j].a29.objIndex);
+ f->writeUint16BE(_actListArr[i][j].a29.actPassIndex);
+ f->writeUint16BE(_actListArr[i][j].a29.actFailIndex);
+ break;
+ case INIT_MAZE: // 30
+ f->writeSint16BE(_actListArr[i][j].a30.timer);
+ f->writeByte(_actListArr[i][j].a30.mazeSize);
+ f->writeSint16BE(_actListArr[i][j].a30.x1);
+ f->writeSint16BE(_actListArr[i][j].a30.y1);
+ f->writeSint16BE(_actListArr[i][j].a30.x2);
+ f->writeSint16BE(_actListArr[i][j].a30.y2);
+ f->writeSint16BE(_actListArr[i][j].a30.x3);
+ f->writeSint16BE(_actListArr[i][j].a30.x4);
+ f->writeByte(_actListArr[i][j].a30.firstScreenIndex);
+ break;
+ case EXIT_MAZE: // 31
+ f->writeSint16BE(_actListArr[i][j].a31.timer);
+ break;
+ case INIT_PRIORITY: // 32
+ f->writeSint16BE(_actListArr[i][j].a32.timer);
+ f->writeSint16BE(_actListArr[i][j].a32.objIndex);
+ f->writeByte(_actListArr[i][j].a32.priority);
+ break;
+ case INIT_SCREEN: // 33
+ f->writeSint16BE(_actListArr[i][j].a33.timer);
+ f->writeSint16BE(_actListArr[i][j].a33.objIndex);
+ f->writeSint16BE(_actListArr[i][j].a33.screenIndex);
+ break;
+ case AGSCHEDULE: // 34
+ f->writeSint16BE(_actListArr[i][j].a34.timer);
+ f->writeUint16BE(_actListArr[i][j].a34.actIndex);
+ break;
+ case REMAPPAL: // 35
+ f->writeSint16BE(_actListArr[i][j].a35.timer);
+ f->writeSint16BE(_actListArr[i][j].a35.oldColorIndex);
+ f->writeSint16BE(_actListArr[i][j].a35.newColorIndex);
+ break;
+ case COND_NOUN: // 36
+ f->writeSint16BE(_actListArr[i][j].a36.timer);
+ f->writeUint16BE(_actListArr[i][j].a36.nounIndex);
+ f->writeUint16BE(_actListArr[i][j].a36.actPassIndex);
+ f->writeUint16BE(_actListArr[i][j].a36.actFailIndex);
+ break;
+ case SCREEN_STATE: // 37
+ f->writeSint16BE(_actListArr[i][j].a37.timer);
+ f->writeSint16BE(_actListArr[i][j].a37.screenIndex);
+ f->writeByte(_actListArr[i][j].a37.newState);
+ break;
+ case INIT_LIPS: // 38
+ f->writeSint16BE(_actListArr[i][j].a38.timer);
+ f->writeSint16BE(_actListArr[i][j].a38.lipsObjIndex);
+ f->writeSint16BE(_actListArr[i][j].a38.objIndex);
+ f->writeByte(_actListArr[i][j].a38.dxLips);
+ f->writeByte(_actListArr[i][j].a38.dyLips);
+ break;
+ case INIT_STORY_MODE: // 39
+ f->writeSint16BE(_actListArr[i][j].a39.timer);
+ f->writeByte((_actListArr[i][j].a39.storyModeFl) ? 1 : 0);
+ break;
+ case WARN: // 40
+ f->writeSint16BE(_actListArr[i][j].a40.timer);
+ f->writeSint16BE(_actListArr[i][j].a40.stringIndex);
+ break;
+ case COND_BONUS: // 41
+ f->writeSint16BE(_actListArr[i][j].a41.timer);
+ f->writeSint16BE(_actListArr[i][j].a41.BonusIndex);
+ f->writeUint16BE(_actListArr[i][j].a41.actPassIndex);
+ f->writeUint16BE(_actListArr[i][j].a41.actFailIndex);
+ break;
+ case TEXT_TAKE: // 42
+ f->writeSint16BE(_actListArr[i][j].a42.timer);
+ f->writeSint16BE(_actListArr[i][j].a42.objIndex);
+ break;
+ case YESNO: // 43
+ f->writeSint16BE(_actListArr[i][j].a43.timer);
+ f->writeSint16BE(_actListArr[i][j].a43.promptIndex);
+ f->writeUint16BE(_actListArr[i][j].a43.actYesIndex);
+ f->writeUint16BE(_actListArr[i][j].a43.actNoIndex);
+ break;
+ case STOP_ROUTE: // 44
+ f->writeSint16BE(_actListArr[i][j].a44.timer);
+ break;
+ case COND_ROUTE: // 45
+ f->writeSint16BE(_actListArr[i][j].a45.timer);
+ f->writeSint16BE(_actListArr[i][j].a45.routeIndex);
+ f->writeUint16BE(_actListArr[i][j].a45.actPassIndex);
+ f->writeUint16BE(_actListArr[i][j].a45.actFailIndex);
+ break;
+ case INIT_JUMPEXIT: // 46
+ f->writeSint16BE(_actListArr[i][j].a46.timer);
+ f->writeByte((_actListArr[i][j].a46.jumpExitFl) ? 1 : 0);
+ break;
+ case INIT_VIEW: // 47
+ f->writeSint16BE(_actListArr[i][j].a47.timer);
+ f->writeSint16BE(_actListArr[i][j].a47.objIndex);
+ f->writeSint16BE(_actListArr[i][j].a47.viewx);
+ f->writeSint16BE(_actListArr[i][j].a47.viewy);
+ f->writeSint16BE(_actListArr[i][j].a47.direction);
+ break;
+ case INIT_OBJ_FRAME: // 48
+ f->writeSint16BE(_actListArr[i][j].a48.timer);
+ f->writeSint16BE(_actListArr[i][j].a48.objIndex);
+ f->writeSint16BE(_actListArr[i][j].a48.seqIndex);
+ f->writeSint16BE(_actListArr[i][j].a48.frameIndex);
+ break;
+ case OLD_SONG: // 49, Added by Strangerke for DOS versions
+ f->writeSint16BE(_actListArr[i][j].a49.timer);
+ f->writeUint16BE(_actListArr[i][j].a49.songIndex);
+ break;
+ default:
+ error("Unknown action %d", subElemType);
+ }
+ }
}
}
/*
* Find the index in the action list to be able to serialize the action to save game
*/
-
-void Scheduler::findAction(act* action, int16* index, int16* subElem) {
+void Scheduler::findAction(const act* action, int16* index, int16* subElem) {
assert(index && subElem);
if (!action) {
@@ -831,28 +1091,30 @@ void Scheduler::restoreSchedulerData(Common::ReadStream *in) {
void Scheduler::restoreEvents(Common::ReadStream *f) {
debugC(1, kDebugSchedule, "restoreEvents");
- event_t savedEvents[kMaxEvents]; // Convert event ptrs to indexes
-
uint32 saveTime = f->readUint32BE(); // time of save
int16 freeIndex = f->readSint16BE(); // Free list index
int16 headIndex = f->readSint16BE(); // Head of list index
int16 tailIndex = f->readSint16BE(); // Tail of list index
- f->read(savedEvents, sizeof(savedEvents));
- event_t *wrkEvent;
// Restore events indexes to pointers
for (int i = 0; i < kMaxEvents; i++) {
- wrkEvent = &savedEvents[i];
- _events[i] = *wrkEvent;
+ int16 index = f->readSint16BE();
+ int16 subElem = f->readSint16BE();
+
// fix up action pointer (to do better)
- int32 val = (size_t)_events[i].action;
- if ((val & 0xffff) == 0xffff) {
+ if ((index == -1) && (subElem == -1))
_events[i].action = 0;
- } else {
- _events[i].action = (act*)&_actListArr[val >> 16][val & 0xffff];
- }
- _events[i].prevEvent = (wrkEvent->prevEvent == (event_t *) - 1) ? (event_t *)0 : &_events[(size_t)wrkEvent->prevEvent ];
- _events[i].nextEvent = (wrkEvent->nextEvent == (event_t *) - 1) ? (event_t *)0 : &_events[(size_t)wrkEvent->nextEvent ];
+ else
+ _events[i].action = (act*)&_actListArr[index][subElem];
+
+ _events[i].localActionFl = (f->readByte() == 1) ? true : false;
+ _events[i].time = f->readUint32BE();
+
+ int16 prevIndex = f->readSint16BE();
+ int16 nextIndex = f->readSint16BE();
+
+ _events[i].prevEvent = (prevIndex == -1) ? (event_t *)0 : &_events[prevIndex];
+ _events[i].nextEvent = (nextIndex == -1) ? (event_t *)0 : &_events[nextIndex];
}
_freeEvent = (freeIndex == -1) ? 0 : &_events[freeIndex];
_headEvent = (headIndex == -1) ? 0 : &_events[headIndex];
@@ -860,7 +1122,7 @@ void Scheduler::restoreEvents(Common::ReadStream *f) {
// Adjust times to fit our time
uint32 curTime = getTicks();
- wrkEvent = _headEvent; // The earliest event
+ event_t *wrkEvent = _headEvent; // The earliest event
while (wrkEvent) { // While mature events found
wrkEvent->time = wrkEvent->time - saveTime + curTime;
wrkEvent = wrkEvent->nextEvent;
diff --git a/engines/hugo/schedule.h b/engines/hugo/schedule.h
index 953e9affea..a066fc63c4 100644
--- a/engines/hugo/schedule.h
+++ b/engines/hugo/schedule.h
@@ -583,7 +583,7 @@ protected:
void delEventType(const action_t actTypeDel);
void delQueue(event_t *curEvent);
- void findAction(act* action, int16* index, int16* subElem);
+ void findAction(const act* action, int16* index, int16* subElem);
void insertAction(act *action);
void readAct(Common::ReadStream &in, act &curAct);
void restoreActions(Common::ReadStream *f);
diff --git a/engines/mohawk/myst.cpp b/engines/mohawk/myst.cpp
index f842269893..1aba820fed 100644
--- a/engines/mohawk/myst.cpp
+++ b/engines/mohawk/myst.cpp
@@ -1143,20 +1143,6 @@ void MohawkEngine_Myst::loadResources() {
delete rlstStream;
}
-void MohawkEngine_Myst::runLoadDialog() {
- const Common::String gameId = ConfMan.get("gameid");
-
- const EnginePlugin *plugin = 0;
- EngineMan.findGame(gameId, &plugin);
-
- pauseEngine(true);
- int slot = _loadDialog->runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName());
- if (slot >= 0) {
- // TODO
- }
- pauseEngine(false);
-}
-
Common::Error MohawkEngine_Myst::loadGameState(int slot) {
if (_gameState->load(_gameState->generateSaveGameList()[slot]))
return Common::kNoError;
diff --git a/engines/mohawk/myst.h b/engines/mohawk/myst.h
index 919509384b..47e8a6562c 100644
--- a/engines/mohawk/myst.h
+++ b/engines/mohawk/myst.h
@@ -155,8 +155,6 @@ public:
Common::String wrapMovieFilename(const Common::String &movieName, uint16 stack);
void reloadSaveList();
- void runLoadDialog();
- void runSaveDialog();
void changeToStack(uint16 stack, uint16 card, uint16 linkSrcSound, uint16 linkDstSound);
void changeToCard(uint16 card, bool updateScreen);
diff --git a/engines/mohawk/myst_areas.h b/engines/mohawk/myst_areas.h
index 66430f2068..aa06d1a5b4 100644
--- a/engines/mohawk/myst_areas.h
+++ b/engines/mohawk/myst_areas.h
@@ -233,6 +233,7 @@ public:
void drawFrame(uint16 frame);
bool pullLeverV();
void releaseLeverV();
+ uint16 getNumFrames() { return _numFrames; }
protected:
uint16 _numFrames;
diff --git a/engines/mohawk/myst_stacks/mechanical.cpp b/engines/mohawk/myst_stacks/mechanical.cpp
index 0ae9078974..3dab2f7939 100644
--- a/engines/mohawk/myst_stacks/mechanical.cpp
+++ b/engines/mohawk/myst_stacks/mechanical.cpp
@@ -23,6 +23,7 @@
*
*/
+#include "mohawk/cursors.h"
#include "mohawk/myst.h"
#include "mohawk/graphics.h"
#include "mohawk/myst_areas.h"
@@ -54,9 +55,12 @@ void Mechanical::setupOpcodes() {
OPCODE(100, o_throneEnablePassage);
OPCODE(104, o_snakeBoxTrigger);
OPCODE(105, o_fortressStaircaseMovie);
- OPCODE(121, opcode_121);
- OPCODE(122, opcode_122);
- OPCODE(123, opcode_123);
+ OPCODE(106, o_elevatorRotationStart);
+ OPCODE(107, o_elevatorRotationMove);
+ OPCODE(108, o_elevatorRotationStop);
+ OPCODE(121, o_elevatorWindowMovie);
+ OPCODE(122, o_elevatorGoMiddle);
+ OPCODE(123, o_elevatorTopMovie);
OPCODE(124, opcode_124);
OPCODE(125, o_mystStaircaseMovie);
OPCODE(126, opcode_126);
@@ -72,28 +76,34 @@ void Mechanical::setupOpcodes() {
OPCODE(201, o_fortressStaircase_init);
OPCODE(202, opcode_202);
OPCODE(203, o_snakeBox_init);
- OPCODE(204, opcode_204);
+ OPCODE(204, o_elevatorRotation_init);
OPCODE(205, opcode_205);
OPCODE(206, opcode_206);
OPCODE(209, opcode_209);
// "Exit" Opcodes
- OPCODE(300, opcode_300);
+ OPCODE(300, NOP);
}
#undef OPCODE
void Mechanical::disablePersistentScripts() {
opcode_202_disable();
- opcode_204_disable();
opcode_205_disable();
opcode_206_disable();
opcode_209_disable();
+ _elevatorGoingMiddle = false;
}
void Mechanical::runPersistentScripts() {
opcode_202_run();
- opcode_204_run();
+
+ if (_elevatorRotationLeverMoving)
+ elevatorRotation_run();
+
+ if (_elevatorGoingMiddle)
+ elevatorGoMiddle_run();
+
opcode_205_run();
opcode_206_run();
opcode_209_run();
@@ -131,13 +141,15 @@ uint16 Mechanical::getVar(uint16 var) {
return _state.staircaseState;
case 11: // Fortress Elevator Rotation Position
return _state.elevatorRotation;
-// case 12: // Fortress Elevator Rotation Cog Position
-// return 0;
-// return 1;
-// return 2;
-// return 3;
-// return 4;
-// return 5;
+ case 12: // Fortress Elevator Rotation Cog Position
+ return 5 - (uint16)(_elevatorRotationGearPosition + 0.5) % 6;
+ case 13: // Elevator position
+ return _elevatorPosition;
+ case 14: // Elevator going down when at top
+ if (_elevatorGoingDown && _elevatorTooLate)
+ return 2;
+ else
+ return _elevatorGoingDown;
case 15: // Code Lock Execute Button Script
if (_mystStaircaseState)
return 0;
@@ -184,6 +196,9 @@ void Mechanical::toggleVar(uint16 var) {
case 19: // Code Lock Shape #4 - Right
_state.codeShape[var - 16] = (_state.codeShape[var - 16] + 1) % 10;
break;
+ case 23: // Elevator player is in cabin
+ _elevatorInCabin = false;
+ break;
case 102: // Red page
if (!(_globals.redPagesInBook & 4)) {
if (_globals.heldPage == 9)
@@ -210,6 +225,11 @@ bool Mechanical::setVarValue(uint16 var, uint16 value) {
bool refresh = false;
switch (var) {
+ case 13:
+ _elevatorPosition = value;
+ case 14: // Elevator going down when at top
+ _elevatorGoingDown = value;
+ break;
default:
refresh = MystScriptParser::setVarValue(var, value);
break;
@@ -245,47 +265,165 @@ void Mechanical::o_fortressStaircaseMovie(uint16 op, uint16 var, uint16 argc, ui
_vm->_video->waitUntilMovieEnds(staircase);
}
+void Mechanical::o_elevatorRotationStart(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
+ debugC(kDebugScript, "Opcode %d: Elevator rotation lever start", op);
-void Mechanical::opcode_121(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
- varUnusedCheck(op, var);
+ MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ lever->drawFrame(0);
- if (argc == 2) {
- uint16 startTime = argv[0];
- uint16 endTime = argv[1];
+ _elevatorRotationLeverMoving = true;
+ _elevatorRotationSpeed = 0;
- warning("TODO: Opcode %d Movie Time Index %d to %d\n", op, startTime, endTime);
- // TODO: Need version of playMovie blocking which allows selection
- // of start and finish points.
- _vm->_video->playMovieBlocking(_vm->wrapMovieFilename("ewindow", kMechanicalStack), 253, 0);
- } else
- unknown(op, var, argc, argv);
+ _vm->_sound->stopBackgroundMyst();
+
+ _vm->_cursor->setCursor(700);
}
-void Mechanical::opcode_122(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
- if (argc == 0) {
- // Used on Card 6120 (Elevator)
- // Called when Exit Midde Button Pressed
+void Mechanical::o_elevatorRotationMove(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
+ debugC(kDebugScript, "Opcode %d: Elevator rotation lever move", op);
- // TODO: hcelev? Movie of Elevator?
- } else
- unknown(op, var, argc, argv);
+ const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
+ MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+
+ // Make the handle follow the mouse
+ int16 maxStep = lever->getNumFrames() - 1;
+ Common::Rect rect = lever->getRect();
+ int16 step = ((rect.bottom - mouse.y) * lever->getNumFrames()) / rect.height();
+ step = CLIP<int16>(step, 0, maxStep);
+
+ _elevatorRotationSpeed = step * 0.1;
+
+ // Draw current frame
+ lever->drawFrame(step);
}
-void Mechanical::opcode_123(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
- varUnusedCheck(op, var);
+void Mechanical::o_elevatorRotationStop(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
+ debugC(kDebugScript, "Opcode %d: Elevator rotation lever stop", op);
- if (argc == 2) {
- // Used on Card 6154
- uint16 start_time = argv[0];
- uint16 end_time = argv[1];
+ const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
+ MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
- warning("TODO: Opcode %d Movie Time Index %d to %d\n", op, start_time, end_time);
- // TODO: Need version of playMovie blocking which allows selection
- // of start and finish points.
- // TODO: Not 100% sure about movie position
- _vm->_video->playMovieBlocking(_vm->wrapMovieFilename("hcelev", kMechanicalStack), 205, 40);
- } else
- unknown(op, var, argc, argv);
+ // Get current lever frame
+ int16 maxStep = lever->getNumFrames() - 1;
+ Common::Rect rect = lever->getRect();
+ int16 step = ((rect.bottom - mouse.y) * lever->getNumFrames()) / rect.height();
+ step = CLIP<int16>(step, 0, maxStep);
+
+ // Release lever
+ for (int i = step; i >= 0; i--) {
+ lever->drawFrame(i);
+ _vm->_system->delayMillis(10);
+ }
+
+ // Stop persistent script
+ _elevatorRotationLeverMoving = false;
+
+ float speed = _elevatorRotationSpeed * 10;
+
+ if (speed > 0) {
+
+ // Decrease speed
+ while (speed > 2) {
+ speed -= 0.5;
+
+ _elevatorRotationGearPosition += speed * 0.1;
+
+ if (_elevatorRotationGearPosition > 12)
+ break;
+
+ _vm->redrawArea(12);
+ _vm->_system->delayMillis(100);
+ }
+
+ // Increment position
+ _state.elevatorRotation = (_state.elevatorRotation + 1) % 10;
+
+ _vm->_sound->replaceSoundMyst(_elevatorRotationSoundId);
+ _vm->redrawArea(11);
+ }
+
+ _vm->checkCursorHints();
+}
+
+void Mechanical::o_elevatorWindowMovie(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
+ uint16 startTime = argv[0];
+ uint16 endTime = argv[1];
+
+ debugC(kDebugScript, "Opcode %d Movie Time Index %d to %d", op, startTime, endTime);
+
+ VideoHandle window = _vm->_video->playMovie(_vm->wrapMovieFilename("ewindow", kMechanicalStack), 253, 0);
+ _vm->_video->setVideoBounds(window, Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, endTime, 600));
+ _vm->_video->waitUntilMovieEnds(window);
+}
+
+void Mechanical::o_elevatorGoMiddle(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
+ debugC(kDebugScript, "Opcode %d: Elevator go middle from top", op);
+
+ _elevatorTooLate = false;
+ _elevatorTopCounter = 5;
+ _elevatorGoingMiddle = true;
+ _elevatorInCabin = true;
+ _elevatorNextTime = _vm->_system->getMillis() + 1000;
+}
+
+void Mechanical::elevatorGoMiddle_run() {
+ uint32 time = _vm->_system->getMillis();
+ if (_elevatorNextTime < time) {
+ _elevatorNextTime = time + 1000;
+ _elevatorTopCounter--;
+
+ if (_elevatorTopCounter > 0) {
+ // Draw button pressed
+ if (_elevatorInCabin) {
+ _vm->_gfx->copyImageSectionToScreen(6332, Common::Rect(0, 35, 51, 63), Common::Rect(10, 137, 61, 165));
+ _vm->_system->updateScreen();
+ }
+
+ // Blip
+ _vm->_sound->playSoundBlocking(14120);
+
+ // Restore button
+ if (_elevatorInCabin) {
+ _vm->_gfx->copyBackBufferToScreen(Common::Rect(10, 137, 61, 165));
+ _vm->_system->updateScreen();
+ }
+ } else if (_elevatorInCabin) {
+ _elevatorTooLate = true;
+
+ // Elevator going to middle animation
+ _vm->_cursor->hideCursor();
+ _vm->_sound->playSoundBlocking(11120);
+ _vm->_gfx->copyImageToBackBuffer(6118, Common::Rect(544, 333));
+ _vm->_sound->replaceSoundMyst(12120);
+ _vm->_gfx->runTransition(2, Common::Rect(177, 0, 370, 333), 25, 0);
+ _vm->_sound->playSoundBlocking(13120);
+ _vm->_sound->replaceSoundMyst(8120);
+ _vm->_gfx->copyImageToBackBuffer(6327, Common::Rect(544, 333));
+ _vm->_system->delayMillis(500);
+ _vm->_sound->replaceSoundMyst(9120);
+ static uint16 moviePos[2] = { 3540, 5380 };
+ o_elevatorWindowMovie(121, 0, 2, moviePos);
+ _vm->_gfx->copyBackBufferToScreen(Common::Rect(544, 333));
+ _vm->_sound->replaceSoundMyst(10120);
+ _vm->_cursor->showCursor();
+
+ _elevatorGoingMiddle = false;
+ _elevatorPosition = 1;
+
+ _vm->changeToCard(6327, true);
+ }
+ }
+}
+
+void Mechanical::o_elevatorTopMovie(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
+ uint16 startTime = argv[0];
+ uint16 endTime = argv[1];
+
+ debugC(kDebugScript, "Opcode %d Movie Time Index %d to %d", op, startTime, endTime);
+
+ VideoHandle window = _vm->_video->playMovie(_vm->wrapMovieFilename("hcelev", kMechanicalStack), 206, 38);
+ _vm->_video->setVideoBounds(window, Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, endTime, 600));
+ _vm->_video->waitUntilMovieEnds(window);
}
void Mechanical::opcode_124(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
@@ -403,36 +541,29 @@ void Mechanical::o_snakeBox_init(uint16 op, uint16 var, uint16 argc, uint16 *arg
_snakeBox = static_cast<MystResourceType6 *>(_invokingResource);
}
-static struct {
- bool enabled;
- uint16 soundId;
-} g_opcode204Parameters;
-
-void Mechanical::opcode_204_run() {
- if (g_opcode204Parameters.enabled) {
- // TODO: Fill in Logic.
- // Var 12 holds Large Cog Position in range 0 to 5
- // - For animation
- // Var 11 holds C position in range 0 to 9
- // - 4 for Correct Answer
- // C Movement Sound
- //_vm->_sound->playSound(g_opcode204Parameters.soundId);
+void Mechanical::elevatorRotation_run() {
+ _vm->redrawArea(12);
+
+ _elevatorRotationGearPosition += _elevatorRotationSpeed;
+
+ if (_elevatorRotationGearPosition > 12) {
+ uint16 position = (uint16)_elevatorRotationGearPosition;
+ _elevatorRotationGearPosition = _elevatorRotationGearPosition - position + position % 6;
+
+ _state.elevatorRotation = (_state.elevatorRotation + 1) % 10;
+
+ _vm->_sound->replaceSoundMyst(_elevatorRotationSoundId);
+ _vm->redrawArea(11);
+ _vm->_system->delayMillis(100);
}
}
-void Mechanical::opcode_204_disable() {
- g_opcode204Parameters.enabled = false;
-}
+void Mechanical::o_elevatorRotation_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
+ debugC(kDebugScript, "Opcode %d: Elevator rotation init", op);
-void Mechanical::opcode_204(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
- varUnusedCheck(op, var);
-
- // Used for Card 6180 (Lower Elevator Puzzle)
- if (argc == 1) {
- g_opcode204Parameters.soundId = argv[0];
- g_opcode204Parameters.enabled = true;
- } else
- unknown(op, var, argc, argv);
+ _elevatorRotationSoundId = argv[0];
+ _elevatorRotationGearPosition = 0;
+ _elevatorRotationLeverMoving = false;
}
static struct {
@@ -532,11 +663,5 @@ void Mechanical::opcode_209(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
unknown(op, var, argc, argv);
}
-void Mechanical::opcode_300(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
- // Used in Card 6156 (Fortress Elevator View)
- varUnusedCheck(op, var);
- // TODO: Fill in Logic. Clearing Variable for View?
-}
-
} // End of namespace MystStacks
} // End of namespace Mohawk
diff --git a/engines/mohawk/myst_stacks/mechanical.h b/engines/mohawk/myst_stacks/mechanical.h
index 7142425eb4..6a0aa30f5c 100644
--- a/engines/mohawk/myst_stacks/mechanical.h
+++ b/engines/mohawk/myst_stacks/mechanical.h
@@ -54,8 +54,8 @@ private:
void opcode_202_run();
void opcode_202_disable();
- void opcode_204_run();
- void opcode_204_disable();
+ void elevatorRotation_run();
+ void elevatorGoMiddle_run();
void opcode_205_run();
void opcode_205_disable();
void opcode_206_run();
@@ -66,9 +66,12 @@ private:
DECLARE_OPCODE(o_throneEnablePassage);
DECLARE_OPCODE(o_snakeBoxTrigger);
DECLARE_OPCODE(o_fortressStaircaseMovie);
- DECLARE_OPCODE(opcode_121);
- DECLARE_OPCODE(opcode_122);
- DECLARE_OPCODE(opcode_123);
+ DECLARE_OPCODE(o_elevatorRotationStart);
+ DECLARE_OPCODE(o_elevatorRotationMove);
+ DECLARE_OPCODE(o_elevatorRotationStop);
+ DECLARE_OPCODE(o_elevatorWindowMovie);
+ DECLARE_OPCODE(o_elevatorGoMiddle);
+ DECLARE_OPCODE(o_elevatorTopMovie);
DECLARE_OPCODE(opcode_124);
DECLARE_OPCODE(o_mystStaircaseMovie);
DECLARE_OPCODE(opcode_126);
@@ -83,19 +86,31 @@ private:
DECLARE_OPCODE(o_fortressStaircase_init);
DECLARE_OPCODE(opcode_202);
DECLARE_OPCODE(o_snakeBox_init);
- DECLARE_OPCODE(opcode_204);
+ DECLARE_OPCODE(o_elevatorRotation_init);
DECLARE_OPCODE(opcode_205);
DECLARE_OPCODE(opcode_206);
DECLARE_OPCODE(opcode_209);
- DECLARE_OPCODE(opcode_300);
-
MystGameState::Mechanical &_state;
bool _mystStaircaseState; // 76
uint16 _fortressPosition; // 82
+ uint16 _elevatorGoingDown; // 112
+
+ float _elevatorRotationSpeed; // 120
+ float _elevatorRotationGearPosition; // 124
+ uint16 _elevatorRotationSoundId; // 128
+ bool _elevatorRotationLeverMoving; // 184
+
+ bool _elevatorGoingMiddle; // 148
+ bool _elevatorTooLate;
+ uint16 _elevatorPosition; // 104
+ bool _elevatorInCabin; // 108
+ uint16 _elevatorTopCounter;
+ uint32 _elevatorNextTime;
+
uint16 _crystalLit; // 130
MystResourceType6 *_snakeBox; // 156
diff --git a/engines/mohawk/myst_stacks/myst.cpp b/engines/mohawk/myst_stacks/myst.cpp
index 29048eab39..caadf09f56 100644
--- a/engines/mohawk/myst_stacks/myst.cpp
+++ b/engines/mohawk/myst_stacks/myst.cpp
@@ -50,6 +50,7 @@ Myst::Myst(MohawkEngine_Myst *vm) :
_towerRotationBlinkLabel = false;
_libraryBookcaseChanged = false;
_dockVaultState = 0;
+ _cabinDoorOpened = 0;
_cabinMatchState = 2;
_matchBurning = false;
_tree = 0;
diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp
index d77ac858c8..de0cfe20d8 100644
--- a/engines/sci/console.cpp
+++ b/engines/sci/console.cpp
@@ -40,12 +40,14 @@
#include "sci/sound/music.h"
#include "sci/sound/drivers/mididriver.h"
#include "sci/sound/drivers/map-mt32-to-gm.h"
+#include "sci/graphics/cache.h"
#include "sci/graphics/cursor.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/paint.h"
#include "sci/graphics/paint16.h"
#include "sci/graphics/paint32.h"
#include "sci/graphics/palette.h"
+#include "sci/graphics/view.h"
#include "sci/parser/vocabulary.h"
@@ -1503,7 +1505,14 @@ bool Console::cmdDrawCel(int argc, const char **argv) {
uint16 loopNo = atoi(argv[2]);
uint16 celNo = atoi(argv[3]);
- _engine->_gfxPaint->kernelDrawCel(resourceId, loopNo, celNo, 50, 50, 0, 0, false, NULL_REG);
+ if (_engine->_gfxPaint16) {
+ _engine->_gfxPaint16->kernelDrawCel(resourceId, loopNo, celNo, 50, 50, 0, 0, 128, 128, false, NULL_REG);
+ } else {
+ GfxView *view = _engine->_gfxCache->getView(resourceId);
+ Common::Rect celRect(50, 50, 50 + view->getWidth(loopNo, celNo), 50 + view->getHeight(loopNo, celNo));
+ view->draw(celRect, celRect, celRect, loopNo, celNo, 255, 0, false);
+ _engine->_gfxScreen->copyRectToScreen(celRect);
+ }
return true;
}
@@ -3758,11 +3767,16 @@ int Console::printObject(reg_t pos) {
return 0;
}
+static void printChar(byte c) {
+ if (c < 32 || c >= 127)
+ c = '.';
+ debugN("%c", c);
+}
+
void Console::hexDumpReg(const reg_t *data, int len, int regsPerLine, int startOffset, bool isArray) {
// reg_t version of Common::hexdump
assert(1 <= regsPerLine && regsPerLine <= 8);
int i;
- byte c;
int offset = startOffset;
while (len >= regsPerLine) {
debugN("%06x: ", offset);
@@ -3771,14 +3785,13 @@ void Console::hexDumpReg(const reg_t *data, int len, int regsPerLine, int startO
}
debugN(" |");
for (i = 0; i < regsPerLine; i++) {
- c = data[i].toUint16() & 0xff;
- if (c < 32 || c >= 127)
- c = '.';
- debugN("%c", c);
- c = data[i].toUint16() >> 8;
- if (c < 32 || c >= 127)
- c = '.';
- debugN("%c", c);
+ if (g_sci->isBE()) {
+ printChar(data[i].toUint16() >> 8);
+ printChar(data[i].toUint16() & 0xff);
+ } else {
+ printChar(data[i].toUint16() & 0xff);
+ printChar(data[i].toUint16() >> 8);
+ }
}
debugN("|\n");
data += regsPerLine;
@@ -3798,14 +3811,13 @@ void Console::hexDumpReg(const reg_t *data, int len, int regsPerLine, int startO
}
debugN(" |");
for (i = 0; i < len; i++) {
- c = data[i].toUint16() & 0xff;
- if (c < 32 || c >= 127)
- c = '.';
- debugN("%c", c);
- c = data[i].toUint16() >> 8;
- if (c < 32 || c >= 127)
- c = '.';
- debugN("%c", c);
+ if (g_sci->isBE()) {
+ printChar(data[i].toUint16() >> 8);
+ printChar(data[i].toUint16() & 0xff);
+ } else {
+ printChar(data[i].toUint16() & 0xff);
+ printChar(data[i].toUint16() >> 8);
+ }
}
for (; i < regsPerLine; i++)
debugN(" ");
diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h
index c7ef720e1f..0158aa46ff 100644
--- a/engines/sci/detection_tables.h
+++ b/engines/sci/detection_tables.h
@@ -1992,31 +1992,31 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
- // Larry 6 - English/German/French DOS CD - LORES
+ // Larry 6 - English/German/French DOS CD - LOWRES
// SCI interpreter version 1.001.115
{"lsl6", "", {
{"resource.map", 0, "0b91234b7112782962cb480b7791b6e2", 7263},
{"resource.000", 0, "57d5fe8bb9e044158514476ea7678eb0", 5754790},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO_NONE },
- // Larry 6 - German DOS CD - LORES (provided by richiefs in bug report #2670691)
+ // Larry 6 - German DOS CD - LOWRES (provided by richiefs in bug report #2670691)
// SCI interpreter version 1.001.115
{"lsl6", "", {
{"resource.map", 0, "bafe85f32738854135991d4324ad147e", 7268},
{"resource.000", 0, "f6cbc6da7b90ea135883e0759848ca2c", 5773160},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NONE },
+ Common::DE_DEU, Common::kPlatformPC, ADGF_CD, GUIO_NONE },
- // Larry 6 - French DOS CD - LORES (provided by richiefs in bug report #2670691)
+ // Larry 6 - French DOS CD - LOWRES (provided by richiefs in bug report #2670691)
// SCI interpreter version 1.001.115
{"lsl6", "", {
{"resource.map", 0, "97797ea775baaf18a1907d357d3c0ea6", 7268},
{"resource.000", 0, "f6cbc6da7b90ea135883e0759848ca2c", 5776092},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformPC, 0, GUIO_NONE },
+ Common::FR_FRA, Common::kPlatformPC, ADGF_CD, GUIO_NONE },
- // Larry 6 - Spanish DOS - LORES (from the Leisure Suit Larry Collection)
+ // Larry 6 - Spanish DOS - LOWRES (from the Leisure Suit Larry Collection)
// Executable scanning reports "1.001.113", VERSION file reports "1.000, 11.06.93, FIVE PATCHES ADDED TO DISK 6 ON 11-18-93"
{"lsl6", "", {
{"resource.map", 0, "633bf8f42170b6271019917c8009989b", 6943},
@@ -2588,13 +2588,13 @@ static const struct ADGameDescription SciGameDescriptions[] = {
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
#ifdef ENABLE_SCI32
- // Police Quest 4 - English DOS (from the Police Quest Collection)
+ // Police Quest 4 - English DOS CD (from the Police Quest Collection)
// Executable scanning reports "2.100.002", VERSION file reports "1.100.000"
- {"pq4", "", {
+ {"pq4", "CD", {
{"resource.map", 0, "379dfe80ed6bd16c47e4b950c4722eac", 11374},
{"resource.000", 0, "fd316a09b628b7032248139003369022", 18841068},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO_NONE },
// Police Quest 4 - English DOS
// SCI interpreter version 2.000.000 (a guess?)
@@ -2628,6 +2628,14 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
+ // Police Quest: SWAT - English DOS (from GOG.com)
+ // Executable scanning reports "2.100.002", VERSION file reports "1.0c"
+ {"pqswat", "", {
+ {"resmap.000", 0, "1c2563fee189885e29d9348f37306d94", 12175},
+ {"ressci.000", 0, "b2e1826ca81ce2e7e764587f5a14eee9", 127149181},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE },
+
// Police Quest: SWAT - English Windows (from the Police Quest Collection)
// Executable scanning reports "2.100.002", VERSION file reports "1.0c"
// Original DOS/Windows release VERSION file reports "1.000" is the same
@@ -2641,7 +2649,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.004", 0, "4228038906f041623e65789500b22285", 6835},
{"ressci.004", 0, "b7e619e6ecf62fe65d5116a3a422e5f0", 46223872},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, 0, GUIO_NOSPEECH },
+ Common::EN_ANY, Common::kPlatformWindows, 0, GUIO_NONE },
#endif // ENABLE_SCI32
// Quest for Glory 1 / Hero's Quest - English DOS 3.5" Floppy (supplied by merkur in bug report #2718784)
@@ -2959,13 +2967,13 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH },
- // Quest for Glory 4 - English DOS/Windows (from jvprat)
+ // Quest for Glory 4 CD - English DOS/Windows (from jvprat)
// Executable scanning reports "2.100.002", VERSION file reports "1.0"
- {"qfg4", "", {
+ {"qfg4", "CD", {
{"resource.map", 0, "aba367f2102e81782d961b14fbe3d630", 10246},
{"resource.000", 0, "263dce4aa34c49d3ad29bec889007b1c", 11571394},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO_NONE },
// RAMA - English DOS/Windows Demo
// Executable scanning reports "2.100.002", VERSION file reports "000.000.008"
diff --git a/engines/sci/engine/features.cpp b/engines/sci/engine/features.cpp
index 206624f87e..964097f57d 100644
--- a/engines/sci/engine/features.cpp
+++ b/engines/sci/engine/features.cpp
@@ -285,20 +285,16 @@ SciVersion GameFeatures::detectLofsType() {
}
// Find a function of the "Game" object (which is the game super class) which invokes lofsa/lofss
- reg_t gameSuperClass = g_sci->getGameSuperClassAddress();
+ const Object *gameObject = _segMan->getObject(g_sci->getGameObject());
+ const Object *gameSuperObject = _segMan->getObject(gameObject->getSuperClassSelector());
bool found = false;
- if (!gameSuperClass.isNull()) {
- Common::String gameSuperClassName = _segMan->getObjectName(gameSuperClass);
- const Object *gameSuperObject = _segMan->getObject(gameSuperClass);
+ if (gameSuperObject) {
+ Common::String gameSuperClassName = _segMan->getObjectName(gameObject->getSuperClassSelector());
- if (gameSuperObject) {
- for (uint m = 0; m < gameSuperObject->getMethodCount(); m++) {
- found = autoDetectLofsType(gameSuperClassName, m);
- if (found)
- break;
- }
- } else {
- warning("detectLofsType(): Could not get superclass object");
+ for (uint m = 0; m < gameSuperObject->getMethodCount(); m++) {
+ found = autoDetectLofsType(gameSuperClassName, m);
+ if (found)
+ break;
}
} else {
warning("detectLofsType(): Could not find superclass of game object");
diff --git a/engines/sci/engine/gc.cpp b/engines/sci/engine/gc.cpp
index 7692613ee5..85238ec851 100644
--- a/engines/sci/engine/gc.cpp
+++ b/engines/sci/engine/gc.cpp
@@ -25,6 +25,7 @@
#include "sci/engine/gc.h"
#include "common/array.h"
+#include "sci/graphics/ports.h"
namespace Sci {
@@ -84,6 +85,18 @@ static void processWorkList(SegManager *segMan, WorklistManager &wm, const Commo
}
}
+static void processEngineHunkList(WorklistManager &wm) {
+ PortList windowList = g_sci->_gfxPorts->_windowList;
+
+ for (PortList::const_iterator it = windowList.begin(); it != windowList.end(); ++it) {
+ if ((*it)->isWindow()) {
+ Window *wnd = ((Window *)*it);
+ wm.push(wnd->hSaved1);
+ wm.push(wnd->hSaved2);
+ }
+ }
+}
+
AddrSet *findAllActiveReferences(EngineState *s) {
assert(!s->_executionStack.empty());
@@ -143,6 +156,9 @@ AddrSet *findAllActiveReferences(EngineState *s) {
processWorkList(s->_segMan, wm, heap);
+ if (getSciVersion() <= SCI_VERSION_1_1)
+ processEngineHunkList(wm);
+
return normalizeAddresses(s->_segMan, wm._map);
}
diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h
index 71892a8bea..6dd08d78ff 100644
--- a/engines/sci/engine/kernel_tables.h
+++ b/engines/sci/engine/kernel_tables.h
@@ -56,7 +56,7 @@ struct SciKernelMapSubEntry {
#define SIG_SCIALL SCI_VERSION_NONE, SCI_VERSION_NONE
#define SIG_SCI0 SCI_VERSION_NONE, SCI_VERSION_01
-#define SIG_SCI1 SCI_VERSION_1_EGA, SCI_VERSION_1_LATE
+#define SIG_SCI1 SCI_VERSION_1_EGA_ONLY, SCI_VERSION_1_LATE
#define SIG_SCI11 SCI_VERSION_1_1, SCI_VERSION_1_1
#define SIG_SINCE_SCI11 SCI_VERSION_1_1, SCI_VERSION_NONE
#define SIG_SCI21 SCI_VERSION_2_1, SCI_VERSION_3
diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp
index f6dec5da64..45182761aa 100644
--- a/engines/sci/engine/kfile.cpp
+++ b/engines/sci/engine/kfile.cpp
@@ -253,6 +253,7 @@ reg_t kFGets(EngineState *s, int argc, reg_t *argv) {
debugC(kDebugLevelFile, "kFGets(%d, %d)", handle, maxsize);
int readBytes = fgets_wrapper(s, buf, maxsize, handle);
s->_segMan->memcpy(argv[0], (const byte*)buf, maxsize);
+ delete[] buf;
return readBytes ? argv[0] : NULL_REG;
}
@@ -1155,10 +1156,11 @@ reg_t kCD(EngineState *s, int argc, reg_t *argv) {
reg_t kSave(EngineState *s, int argc, reg_t *argv) {
switch (argv[0].toUint16()) {
- case 0: // Called by kq7 when starting chapters
+ case 0:
return kSaveGame(s, argc - 1,argv + 1);
- case 2: // GetSaveDir
- // Yay! Reusing the old kernel function!
+ case 1:
+ return kRestoreGame(s, argc - 1,argv + 1);
+ case 2:
return kGetSaveDir(s, argc - 1, argv + 1);
case 5:
// TODO
diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp
index 8730724d68..fd052bff6a 100644
--- a/engines/sci/engine/kgraphics.cpp
+++ b/engines/sci/engine/kgraphics.cpp
@@ -58,6 +58,17 @@
namespace Sci {
+static int16 adjustGraphColor(int16 color) {
+ // WORKAROUND: SCI1 EGA and Amiga games can set invalid colors (above 0 - 15).
+ // Colors above 15 are all white in SCI1 EGA games, which is why this was never
+ // observed. We clip them all to (0, 15) instead, as colors above 15 are used
+ // for the undithering algorithm in EGA games - bug #3048908.
+ if (getSciVersion() >= SCI_VERSION_1_EARLY && g_sci->getResMan()->getViewType() == kViewEga)
+ return color & 0x0F; // 0 - 15
+ else
+ return color;
+}
+
void showScummVMDialog(const Common::String &message) {
GUI::MessageDialog dialog(message, "OK");
dialog.runModal();
@@ -242,23 +253,14 @@ reg_t kGraph(EngineState *s, int argc, reg_t *argv) {
}
reg_t kGraphGetColorCount(EngineState *s, int argc, reg_t *argv) {
- if (g_sci->getResMan()->isAmiga32color())
- return make_reg(0, 32);
- return make_reg(0, !g_sci->getResMan()->isVGA() ? 16 : 256);
+ return make_reg(0, g_sci->_gfxPalette->getTotalColorCount());
}
reg_t kGraphDrawLine(EngineState *s, int argc, reg_t *argv) {
- int16 color = argv[4].toSint16();
+ int16 color = adjustGraphColor(argv[4].toSint16());
int16 priority = (argc > 5) ? argv[5].toSint16() : -1;
int16 control = (argc > 6) ? argv[6].toSint16() : -1;
- // WORKAROUND: SCI1 EGA games can set invalid colors (above 0 - 15).
- // Colors above 15 are all white in SCI1 EGA games, which is why this was never
- // observed. We clip them all to (0, 15) instead, as colors above 15 are used
- // for the undithering algorithm in EGA games - bug #3048908.
- if (g_sci->getResMan()->getViewType() == kViewEga && getSciVersion() >= SCI_VERSION_1_EARLY)
- color &= 0x0F;
-
g_sci->_gfxPaint16->kernelGraphDrawLine(getGraphPoint(argv), getGraphPoint(argv + 2), color, priority, control);
return s->r_acc;
}
@@ -290,17 +292,10 @@ reg_t kGraphFillBoxForeground(EngineState *s, int argc, reg_t *argv) {
reg_t kGraphFillBoxAny(EngineState *s, int argc, reg_t *argv) {
Common::Rect rect = getGraphRect(argv);
int16 colorMask = argv[4].toUint16();
- int16 color = argv[5].toSint16();
+ int16 color = adjustGraphColor(argv[5].toSint16());
int16 priority = argv[6].toSint16(); // yes, we may read from stack sometimes here
int16 control = argv[7].toSint16(); // sierra did the same
- // WORKAROUND: SCI1 EGA games can set invalid colors (above 0 - 15).
- // Colors above 15 are all white in SCI1 EGA games, which is why this was never
- // observed. We clip them all to (0, 15) instead, as colors above 15 are used
- // for the undithering algorithm in EGA games - bug #3048908.
- if (g_sci->getResMan()->getViewType() == kViewEga && getSciVersion() >= SCI_VERSION_1_EARLY)
- color &= 0x0F;
-
g_sci->_gfxPaint16->kernelGraphFillBox(rect, colorMask, color, priority, control);
return s->r_acc;
}
@@ -364,6 +359,7 @@ reg_t kTextSize(EngineState *s, int argc, reg_t *argv) {
if (!g_sci->_gfxText16) {
// TODO: Implement this
textWidth = 0; textHeight = 0;
+ warning("TODO: implement kTextSize for SCI32");
} else
#endif
g_sci->_gfxText16->kernelTextSize(g_sci->strSplit(text.c_str(), sep).c_str(), font_nr, maxwidth, &textWidth, &textHeight);
@@ -553,8 +549,6 @@ reg_t kSetNowSeen(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
-// we are called on EGA/amiga games as well, this doesnt make sense.
-// doing this would actually break the system EGA/amiga palette
reg_t kPalette(EngineState *s, int argc, reg_t *argv) {
if (!s)
return make_reg(0, getSciVersion());
@@ -562,86 +556,85 @@ reg_t kPalette(EngineState *s, int argc, reg_t *argv) {
}
reg_t kPaletteSetFromResource(EngineState *s, int argc, reg_t *argv) {
- if (g_sci->getResMan()->isVGA()) {
- GuiResourceId resourceId = argv[0].toUint16();
- bool force = false;
- if (argc == 2)
- force = argv[1].toUint16() == 2 ? true : false;
- g_sci->_gfxPalette->kernelSetFromResource(resourceId, force);
- }
+ GuiResourceId resourceId = argv[0].toUint16();
+ bool force = false;
+ if (argc == 2)
+ force = argv[1].toUint16() == 2 ? true : false;
+
+ // Non-VGA games don't use palette resources.
+ // This has been changed to 64 colors because Longbow Amiga does have
+ // one palette (palette 999).
+ if (g_sci->_gfxPalette->getTotalColorCount() < 64)
+ return s->r_acc;
+
+ g_sci->_gfxPalette->kernelSetFromResource(resourceId, force);
return s->r_acc;
}
reg_t kPaletteSetFlag(EngineState *s, int argc, reg_t *argv) {
- if (g_sci->getResMan()->isVGA()) {
- uint16 fromColor = CLIP<uint16>(argv[0].toUint16(), 1, 255);
- uint16 toColor = CLIP<uint16>(argv[1].toUint16(), 1, 255);
- uint16 flags = argv[2].toUint16();
- g_sci->_gfxPalette->kernelSetFlag(fromColor, toColor, flags);
- }
+ uint16 fromColor = CLIP<uint16>(argv[0].toUint16(), 1, 255);
+ uint16 toColor = CLIP<uint16>(argv[1].toUint16(), 1, 255);
+ uint16 flags = argv[2].toUint16();
+ g_sci->_gfxPalette->kernelSetFlag(fromColor, toColor, flags);
return s->r_acc;
}
reg_t kPaletteUnsetFlag(EngineState *s, int argc, reg_t *argv) {
- if (g_sci->getResMan()->isVGA()) {
- uint16 fromColor = CLIP<uint16>(argv[0].toUint16(), 1, 255);
- uint16 toColor = CLIP<uint16>(argv[1].toUint16(), 1, 255);
- uint16 flags = argv[2].toUint16();
- g_sci->_gfxPalette->kernelUnsetFlag(fromColor, toColor, flags);
- }
+ uint16 fromColor = CLIP<uint16>(argv[0].toUint16(), 1, 255);
+ uint16 toColor = CLIP<uint16>(argv[1].toUint16(), 1, 255);
+ uint16 flags = argv[2].toUint16();
+ g_sci->_gfxPalette->kernelUnsetFlag(fromColor, toColor, flags);
return s->r_acc;
}
reg_t kPaletteSetIntensity(EngineState *s, int argc, reg_t *argv) {
- if (g_sci->getResMan()->isVGA()) {
- uint16 fromColor = CLIP<uint16>(argv[0].toUint16(), 1, 255);
- uint16 toColor = CLIP<uint16>(argv[1].toUint16(), 1, 255);
- uint16 intensity = argv[2].toUint16();
- bool setPalette = (argc < 4) ? true : (argv[3].isNull()) ? true : false;
+ uint16 fromColor = CLIP<uint16>(argv[0].toUint16(), 1, 255);
+ uint16 toColor = CLIP<uint16>(argv[1].toUint16(), 1, 255);
+ uint16 intensity = argv[2].toUint16();
+ bool setPalette = (argc < 4) ? true : (argv[3].isNull()) ? true : false;
- g_sci->_gfxPalette->kernelSetIntensity(fromColor, toColor, intensity, setPalette);
- }
+ // Palette intensity in non-VGA SCI1 games has been removed
+ if (g_sci->_gfxPalette->getTotalColorCount() < 256)
+ return s->r_acc;
+
+ g_sci->_gfxPalette->kernelSetIntensity(fromColor, toColor, intensity, setPalette);
return s->r_acc;
}
reg_t kPaletteFindColor(EngineState *s, int argc, reg_t *argv) {
- if (g_sci->getResMan()->isVGA()) {
- uint16 r = argv[0].toUint16();
- uint16 g = argv[1].toUint16();
- uint16 b = argv[2].toUint16();
- return make_reg(0, g_sci->_gfxPalette->kernelFindColor(r, g, b));
- }
- return NULL_REG;
+ uint16 r = argv[0].toUint16();
+ uint16 g = argv[1].toUint16();
+ uint16 b = argv[2].toUint16();
+ return make_reg(0, g_sci->_gfxPalette->kernelFindColor(r, g, b));
}
reg_t kPaletteAnimate(EngineState *s, int argc, reg_t *argv) {
- if (g_sci->getResMan()->isVGA()) {
- int16 argNr;
- bool paletteChanged = false;
- for (argNr = 0; argNr < argc; argNr += 3) {
- uint16 fromColor = argv[argNr].toUint16();
- uint16 toColor = argv[argNr + 1].toUint16();
- int16 speed = argv[argNr + 2].toSint16();
- if (g_sci->_gfxPalette->kernelAnimate(fromColor, toColor, speed))
- paletteChanged = true;
- }
- if (paletteChanged)
- g_sci->_gfxPalette->kernelAnimateSet();
+ int16 argNr;
+ bool paletteChanged = false;
+
+ // Palette animation in non-VGA SCI1 games has been removed
+ if (g_sci->_gfxPalette->getTotalColorCount() < 256)
+ return s->r_acc;
+
+ for (argNr = 0; argNr < argc; argNr += 3) {
+ uint16 fromColor = argv[argNr].toUint16();
+ uint16 toColor = argv[argNr + 1].toUint16();
+ int16 speed = argv[argNr + 2].toSint16();
+ if (g_sci->_gfxPalette->kernelAnimate(fromColor, toColor, speed))
+ paletteChanged = true;
}
+ if (paletteChanged)
+ g_sci->_gfxPalette->kernelAnimateSet();
+
return s->r_acc;
}
reg_t kPaletteSave(EngineState *s, int argc, reg_t *argv) {
- if (g_sci->getResMan()->isVGA()) {
- return g_sci->_gfxPalette->kernelSave();
- }
- return NULL_REG;
+ return g_sci->_gfxPalette->kernelSave();
}
reg_t kPaletteRestore(EngineState *s, int argc, reg_t *argv) {
- if (g_sci->getResMan()->isVGA()) {
- g_sci->_gfxPalette->kernelRestore(argv[0]);
- }
+ g_sci->_gfxPalette->kernelRestore(argv[0]);
return argv[0];
}
@@ -1082,17 +1075,8 @@ reg_t kNewWindow(EngineState *s, int argc, reg_t *argv) {
int argextra = argc >= 13 ? 4 : 0; // Triggers in PQ3 and SCI1.1 games, argc 13 for DOS argc 15 for mac
int style = argv[5 + argextra].toSint16();
int priority = (argc > 6 + argextra) ? argv[6 + argextra].toSint16() : -1;
- int colorPen = (argc > 7 + argextra) ? argv[7 + argextra].toSint16() : 0;
- int colorBack = (argc > 8 + argextra) ? argv[8 + argextra].toSint16() : 255;
-
- // WORKAROUND: SCI1 EGA games can set invalid colors (above 0 - 15).
- // Colors above 15 are all white in SCI1 EGA games, which is why this was never
- // observed. We clip them all to (0, 15) instead, as colors above 15 are used
- // for the undithering algorithm in EGA games - bug #3048908.
- if (g_sci->getResMan()->getViewType() == kViewEga && getSciVersion() >= SCI_VERSION_1_EARLY) {
- colorPen &= 0x0F;
- colorBack &= 0x0F;
- }
+ int colorPen = adjustGraphColor((argc > 7 + argextra) ? argv[7 + argextra].toSint16() : 0);
+ int colorBack = adjustGraphColor((argc > 8 + argextra) ? argv[8 + argextra].toSint16() : 255);
// const char *title = argv[4 + argextra].segment ? kernel_dereference_char_pointer(s, argv[4 + argextra], 0) : NULL;
if (argc>=13) {
@@ -1201,14 +1185,12 @@ reg_t kShow(EngineState *s, int argc, reg_t *argv) {
}
reg_t kRemapColors(EngineState *s, int argc, reg_t *argv) {
- // TODO: This is all a stub/skeleton, thus we're invoking kStub() for now
- kStub(s, argc, argv);
-
uint16 operation = argv[0].toUint16();
switch (operation) {
- case 0: { // Initialize remapping to base. 0 turns remapping off.
- //int16 unk1 = (argc >= 2) ? argv[1].toSint16() : 0;
+ case 0: { // Set remapping to base. 0 turns remapping off.
+ int16 base = (argc >= 2) ? argv[1].toSint16() : 0;
+ warning("kRemapColors: Set remapping to base %d", base);
}
break;
case 1: { // unknown
@@ -1218,18 +1200,32 @@ reg_t kRemapColors(EngineState *s, int argc, reg_t *argv) {
//int16 unk3 = argv[3].toSint16();
//uint16 unk4 = argv[4].toUint16();
//uint16 unk5 = (argc >= 6) ? argv[5].toUint16() : 0;
+ kStub(s, argc, argv);
}
break;
case 2: { // remap by percent
- //int16 unk1 = argv[1].toSint16();
- //uint16 percent = argv[2].toUint16();
- //uint16 unk3 = (argc >= 4) ? argv[3].toUint16() : 0;
+ // This adjusts the alpha value of a specific color, and it operates on
+ // an RGBA palette. Since we're operating on an RGB palette, we just
+ // modify the color intensity instead
+ // TODO: From what I understand, palette remapping should be placed
+ // separately, so that it can be reset by case 0 above. Thus, we
+ // should adjust the functionality of the Palette class accordingly.
+ int16 color = argv[1].toSint16();
+ if (color >= 10)
+ color -= 10;
+ uint16 percent = argv[2].toUint16(); // 0 - 100
+ if (argc >= 4)
+ warning("RemapByPercent called with 4 parameters, unknown parameter is %d", argv[3].toUint16());
+ g_sci->_gfxPalette->kernelSetIntensity(color, 255, percent, false);
}
break;
case 3: { // remap to gray
- //int16 unk1 = argv[1].toSint16();
- //int16 percent = argv[2].toSint16(); // 0 - 100
- //uint16 unk3 = (argc >= 4) ? argv[3].toUint16() : 0;
+ // NOTE: This adjusts the alpha value of a specific color, and it operates on
+ // an RGBA palette
+ int16 color = argv[1].toSint16(); // this is subtracted from a maximum color value, and can be offset by 10
+ int16 percent = argv[2].toSint16(); // 0 - 100
+ uint16 unk3 = (argc >= 4) ? argv[3].toUint16() : 0;
+ warning("kRemapColors: RemapToGray color %d by %d percent (unk3 = %d)", color, percent, unk3);
}
break;
case 4: { // unknown
@@ -1237,11 +1233,13 @@ reg_t kRemapColors(EngineState *s, int argc, reg_t *argv) {
//uint16 unk2 = argv[2].toUint16();
//uint16 unk3 = argv[3].toUint16();
//uint16 unk4 = (argc >= 5) ? argv[4].toUint16() : 0;
+ kStub(s, argc, argv);
}
break;
case 5: { // increment color
//int16 unk1 = argv[1].toSint16();
//uint16 unk2 = argv[2].toUint16();
+ kStub(s, argc, argv);
}
break;
default:
diff --git a/engines/sci/engine/kmenu.cpp b/engines/sci/engine/kmenu.cpp
index 428c27ca73..3986966a71 100644
--- a/engines/sci/engine/kmenu.cpp
+++ b/engines/sci/engine/kmenu.cpp
@@ -29,6 +29,7 @@
#include "sci/engine/kernel.h"
#include "sci/graphics/cursor.h"
#include "sci/graphics/menu.h"
+#include "sci/graphics/screen.h"
namespace Sci {
@@ -71,7 +72,7 @@ reg_t kDrawStatus(EngineState *s, int argc, reg_t *argv) {
reg_t textReference = argv[0];
Common::String text;
int16 colorPen = (argc > 1) ? argv[1].toSint16() : 0;
- int16 colorBack = (argc > 2) ? argv[2].toSint16() : g_sci->getResMan()->isVGA() ? 255 : 15;
+ int16 colorBack = (argc > 2) ? argv[2].toSint16() : g_sci->_gfxScreen->getColorWhite();
if (!textReference.isNull()) {
// Sometimes this is called without giving text, if thats the case dont process it.
diff --git a/engines/sci/engine/kmisc.cpp b/engines/sci/engine/kmisc.cpp
index 6d7c4580e6..73c92a9394 100644
--- a/engines/sci/engine/kmisc.cpp
+++ b/engines/sci/engine/kmisc.cpp
@@ -298,12 +298,9 @@ reg_t kMemory(EngineState *s, int argc, reg_t *argv) {
error("Attempt to peek invalid memory at %04x:%04x", PRINT_REG(argv[1]));
return s->r_acc;
}
- if (ref.isRaw) {
- if (g_sci->getPlatform() == Common::kPlatformAmiga)
- return make_reg(0, (int16)READ_BE_UINT16(ref.raw)); // Amiga versions are BE
- else
- return make_reg(0, (int16)READ_LE_UINT16(ref.raw));
- } else {
+ if (ref.isRaw)
+ return make_reg(0, (int16)READ_SCIENDIAN_UINT16(ref.raw));
+ else {
if (ref.skipByte)
error("Attempt to peek memory at odd offset %04X:%04X", PRINT_REG(argv[1]));
return *(ref.reg);
@@ -323,10 +320,7 @@ reg_t kMemory(EngineState *s, int argc, reg_t *argv) {
error("Attempt to poke memory reference %04x:%04x to %04x:%04x", PRINT_REG(argv[2]), PRINT_REG(argv[1]));
return s->r_acc;
}
- if (g_sci->getPlatform() == Common::kPlatformAmiga)
- WRITE_BE_UINT16(ref.raw, argv[2].offset); // Amiga versions are BE
- else
- WRITE_LE_UINT16(ref.raw, argv[2].offset);
+ WRITE_SCIENDIAN_UINT16(ref.raw, argv[2].offset); // Amiga versions are BE
} else {
if (ref.skipByte)
error("Attempt to poke memory at odd offset %04X:%04X", PRINT_REG(argv[1]));
diff --git a/engines/sci/engine/kmovement.cpp b/engines/sci/engine/kmovement.cpp
index 3c516f63f2..392db56419 100644
--- a/engines/sci/engine/kmovement.cpp
+++ b/engines/sci/engine/kmovement.cpp
@@ -275,7 +275,7 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) {
bool completed = false;
bool handleMoveCount = g_sci->_features->handleMoveCount();
- if (getSciVersion() >= SCI_VERSION_1_EGA) {
+ if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY) {
uint client_signal = readSelectorValue(segMan, client, SELECTOR(signal));
writeSelectorValue(segMan, client, SELECTOR(signal), client_signal & ~kSignalHitObstacle);
}
@@ -307,7 +307,7 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) {
int16 mover_org_i2 = mover_i2;
int16 mover_org_di = mover_di;
- if ((getSciVersion() >= SCI_VERSION_1_EGA)) {
+ if ((getSciVersion() >= SCI_VERSION_1_EGA_ONLY)) {
// save current position into mover
writeSelectorValue(segMan, mover, SELECTOR(xLast), client_x);
writeSelectorValue(segMan, mover, SELECTOR(yLast), client_y);
@@ -374,26 +374,23 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) {
writeSelectorValue(segMan, mover, SELECTOR(b_i2), mover_i2);
writeSelectorValue(segMan, mover, SELECTOR(b_di), mover_di);
- if ((getSciVersion() >= SCI_VERSION_1_EGA)) {
- // this calling code here was right before the last return in
- // sci1ega and got changed to this position since sci1early
- // this was an uninitialized issue in sierra sci
- if ((handleMoveCount) && (getSciVersion() >= SCI_VERSION_1_EARLY))
+ if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY) {
+ // In sci1egaonly this block of code was outside of the main if,
+ // but client_x/client_y aren't set there, so it was an
+ // uninitialized read in SSCI. (This issue was fixed in sci1early.)
+ if (handleMoveCount)
writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), mover_moveCnt);
// We need to compare directly in here, complete may have happened during
// the current move
if ((client_x == mover_x) && (client_y == mover_y))
invokeSelector(s, mover, SELECTOR(moveDone), argc, argv);
- if (getSciVersion() >= SCI_VERSION_1_EARLY)
- return s->r_acc;
+ return s->r_acc;
}
}
- if (handleMoveCount) {
- if (getSciVersion() <= SCI_VERSION_1_EGA)
- writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), mover_moveCnt);
- else
- writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), client_moveSpeed);
- }
+
+ if (handleMoveCount)
+ writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), mover_moveCnt);
+
return s->r_acc;
}
diff --git a/engines/sci/engine/kparse.cpp b/engines/sci/engine/kparse.cpp
index e8f8ee7152..c0a5e95811 100644
--- a/engines/sci/engine/kparse.cpp
+++ b/engines/sci/engine/kparse.cpp
@@ -169,7 +169,7 @@ reg_t kSetSynonyms(EngineState *s, int argc, reg_t *argv) {
Vocabulary *voc = g_sci->getVocabulary();
// Only SCI0-SCI1 EGA games had a parser. In newer versions, this is a stub
- if (getSciVersion() > SCI_VERSION_1_EGA)
+ if (getSciVersion() > SCI_VERSION_1_EGA_ONLY)
return s->r_acc;
voc->clearSynonyms();
diff --git a/engines/sci/engine/kpathing.cpp b/engines/sci/engine/kpathing.cpp
index cb70cf91e0..7786f9b093 100644
--- a/engines/sci/engine/kpathing.cpp
+++ b/engines/sci/engine/kpathing.cpp
@@ -264,9 +264,9 @@ struct PathfindingState {
static Common::Point readPoint(SegmentRef list_r, int offset) {
Common::Point point;
- if (list_r.isRaw) {
- point.x = (int16)READ_LE_UINT16(list_r.raw + offset * POLY_POINT_SIZE);
- point.y = (int16)READ_LE_UINT16(list_r.raw + offset * POLY_POINT_SIZE + 2);
+ if (list_r.isRaw) { // dynmem blocks are raw
+ point.x = (int16)READ_SCIENDIAN_UINT16(list_r.raw + offset * POLY_POINT_SIZE);
+ point.y = (int16)READ_SCIENDIAN_UINT16(list_r.raw + offset * POLY_POINT_SIZE + 2);
} else {
point.x = list_r.reg[offset * 2].toUint16();
point.y = list_r.reg[offset * 2 + 1].toUint16();
@@ -275,9 +275,9 @@ static Common::Point readPoint(SegmentRef list_r, int offset) {
}
static void writePoint(SegmentRef ref, int offset, const Common::Point &point) {
- if (ref.isRaw) {
- WRITE_LE_UINT16(ref.raw + offset * POLY_POINT_SIZE, point.x);
- WRITE_LE_UINT16(ref.raw + offset * POLY_POINT_SIZE + 2, point.y);
+ if (ref.isRaw) { // dynmem blocks are raw
+ WRITE_SCIENDIAN_UINT16(ref.raw + offset * POLY_POINT_SIZE, point.x);
+ WRITE_SCIENDIAN_UINT16(ref.raw + offset * POLY_POINT_SIZE + 2, point.y);
} else {
ref.reg[offset * 2] = make_reg(0, point.x);
ref.reg[offset * 2 + 1] = make_reg(0, point.y);
diff --git a/engines/sci/engine/kscripts.cpp b/engines/sci/engine/kscripts.cpp
index 810e8a13ee..30c8b6a130 100644
--- a/engines/sci/engine/kscripts.cpp
+++ b/engines/sci/engine/kscripts.cpp
@@ -56,14 +56,6 @@ reg_t kUnLoad(EngineState *s, int argc, reg_t *argv) {
ResourceType restype = g_sci->getResMan()->convertResType(argv[0].toUint16());
reg_t resnr = argv[1];
- // WORKAROUND for a broken script in room 320 in Castle of Dr. Brain.
- // Script 377 tries to free the hunk memory allocated for the saved area
- // (underbits) beneath the pop up window, which results in having the
- // window stay on screen even when it's closed. Ignore this request here.
- if (restype == kResourceTypeMemory && g_sci->getGameId() == GID_CASTLEBRAIN &&
- s->currentRoomNumber() == 320)
- return s->r_acc;
-
if (restype == kResourceTypeMemory)
s->_segMan->freeHunkEntry(resnr);
}
diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp
index 5c6ef06910..730f7af9b8 100644
--- a/engines/sci/engine/kstring.cpp
+++ b/engines/sci/engine/kstring.cpp
@@ -113,7 +113,7 @@ reg_t kStrAt(EngineState *s, int argc, reg_t *argv) {
reg_t &tmp = dest_r.reg[offset / 2];
bool oddOffset = offset & 1;
- if (g_sci->getPlatform() == Common::kPlatformAmiga)
+ if (g_sci->isBE())
oddOffset = !oddOffset;
if (!oddOffset) {
@@ -159,17 +159,9 @@ reg_t kReadNumber(EngineState *s, int argc, reg_t *argv) {
source++;
}
while (*source) {
- if ((*source < '0') || (*source > '9')) {
- // Sierra's atoi stopped processing at anything which is not
- // a digit. Sometimes the input has a trailing space, that's
- // fine (example: lsl3)
- if (*source != ' ') {
- // TODO: this happens in lsl5 right in the intro -> we get '1' '3' 0xCD 0xCD 0xCD 0xCD 0xCD
- // find out why this happens and fix it
- warning("Invalid character in kReadNumber input");
- }
+ if ((*source < '0') || (*source > '9'))
+ // Stop if we encounter anything other than a digit (like atoi)
break;
- }
result *= 10;
result += *source - 0x30;
source++;
diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp
index 6719b73aa5..3c81a93767 100644
--- a/engines/sci/engine/script.cpp
+++ b/engines/sci/engine/script.cpp
@@ -129,6 +129,17 @@ void Script::load(ResourceManager *resMan) {
Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, _nr), 0);
assert(script != 0);
+ uint extraLocalsWorkaround = 0;
+ if (g_sci->getGameId() == GID_FANMADE && _nr == 1 && script->size == 11140) {
+ // WORKAROUND: Script 1 in Ocean Battle doesn't have enough locals to
+ // fit the string showing how many shots are left (a nasty script bug,
+ // corrupting heap memory). We add 10 more locals so that it has enough
+ // space to use as the target for its kFormat operation. Fixes bug
+ // #3059871.
+ extraLocalsWorkaround = 10;
+ }
+ _bufSize += extraLocalsWorkaround * 2;
+
_buf = (byte *)malloc(_bufSize);
assert(_buf);
@@ -187,6 +198,9 @@ void Script::load(ResourceManager *resMan) {
_localsOffset = 24 + _numExports * 2;
}
+ // WORKAROUND: Increase locals, if needed (check above)
+ _localsCount += extraLocalsWorkaround;
+
if (getSciVersion() == SCI_VERSION_0_EARLY) {
// SCI0 early
// Old script block. There won't be a localvar block in this case.
@@ -202,7 +216,7 @@ void Script::load(ResourceManager *resMan) {
if (_localsOffset + _localsCount * 2 + 1 >= (int)_bufSize) {
error("Locals extend beyond end of script: offset %04x, count %d vs size %d", _localsOffset, _localsCount, _bufSize);
- _localsCount = (_bufSize - _localsOffset) >> 1;
+ //_localsCount = (_bufSize - _localsOffset) >> 1;
}
}
}
diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp
index f3b6ff8a46..8c622b1264 100644
--- a/engines/sci/engine/script_patches.cpp
+++ b/engines/sci/engine/script_patches.cpp
@@ -60,55 +60,6 @@ struct SciScriptSignature {
// - if not EOS, an adjust offset and the actual bytes
// - rinse and repeat
-#if 0
-
-// ===========================================================================
-// Castle of Dr. Brain
-// cipher::init (script 391) is called on room 380 init. This resets the word
-// cipher puzzle. The puzzle sadly operates on some hep strings, which aren't
-// saved in our sci. So saving/restoring in this room will break the puzzle
-// Because of this issue, we just init the puzzle each time it's accessed.
-// this is not 100% sierra behaviour, in fact we will actually reset the puzzle
-// during each access which makes it impossible to cheat.
-const byte castlebrainSignatureCipherPuzzle[] = {
- 22,
- 0x35, 0x00, // ldi 00
- 0xa3, 0x26, // sal local[26]
- 0xa3, 0x25, // sal local[25]
- 0x35, 0x00, // ldi 00
- 0xa3, 0x2a, // sal local[2a] (local is not used)
- 0xa3, 0x29, // sal local[29] (local is not used)
- 0x35, 0xff, // ldi ff
- 0xa3, 0x2c, // sal local[2c]
- 0xa3, 0x2b, // sal local[2b]
- 0x35, 0x00, // ldi 00
- 0x65, 0x16, // aTop highlightedIcon
- 0
-};
-
-const uint16 castlebrainPatchCipherPuzzle[] = {
- 0x39, 0x6b, // pushi 6b (selector init)
- 0x76, // push0
- 0x55, 0x04, // self 04
- 0x35, 0x00, // ldi 00
- 0xa3, 0x25, // sal local[25]
- 0xa3, 0x26, // sal local[26]
- 0xa3, 0x29, // sal local[29]
- 0x65, 0x16, // aTop highlightedIcon
- 0x34, 0xff, 0xff, // ldi ffff
- 0xa3, 0x2b, // sal local[2b]
- 0xa3, 0x2c, // sal local[2c]
- PATCH_END
-};
-
-// script, description, magic DWORD, adjust
-const SciScriptSignature castlebrainSignatures[] = {
- { 391, "cipher puzzle save/restore break", 1, PATCH_MAGICDWORD(0xa3, 0x26, 0xa3, 0x25), -2, castlebrainSignatureCipherPuzzle, castlebrainPatchCipherPuzzle },
- SCI_SIGNATUREENTRY_TERMINATOR
-};
-
-#endif
-
// ===========================================================================
// stayAndHelp::changeState (0) is called when ego swims to the left or right
// boundaries of room 660. Normally a textbox is supposed to get on screen
@@ -497,74 +448,6 @@ const SciScriptSignature gk1Signatures[] = {
SCI_SIGNATUREENTRY_TERMINATOR
};
-#if 0
-
-// ===========================================================================
-// this here gets called on entry and when going out of game windows
-// uEvt::port will not get changed after kDisposeWindow but a bit later, so
-// we would get an invalid port handle to a kSetPort call. We just patch in
-// resetting of the port selector. We destroy the stop/fade code in there,
-// it seems it isn't used at all in the game.
-const byte hoyle4SignaturePortFix[] = {
- 28,
- 0x39, 0x09, // pushi 09
- 0x89, 0x0b, // lsg 0b
- 0x39, 0x64, // pushi 64
- 0x38, 0xc8, 0x00, // pushi 00c8
- 0x38, 0x2c, 0x01, // pushi 012c
- 0x38, 0x90, 0x01, // pushi 0190
- 0x38, 0xf4, 0x01, // pushi 01f4
- 0x38, 0x58, 0x02, // pushi 0258
- 0x38, 0xbc, 0x02, // pushi 02bc
- 0x38, 0x20, 0x03, // pushi 0320
- 0x46, // calle [xxxx] [xxxx] [xx]
- +5, 43, // [skip 5 bytes]
- 0x30, 0x27, 0x00, // bnt 0027 -> end of routine
- 0x87, 0x00, // lap 00
- 0x30, 0x19, 0x00, // bnt 0019 -> fade out
- 0x87, 0x01, // lap 01
- 0x30, 0x14, 0x00, // bnt 0014 -> fade out
- 0x38, 0xa7, 0x00, // pushi 00a7
- 0x76, // push0
- 0x80, 0x29, 0x01, // lag 0129
- 0x4a, 0x04, // send 04 - call song::stop
- 0x39, 0x27, // pushi 27
- 0x78, // push1
- 0x8f, 0x01, // lsp 01
- 0x51, 0x54, // class 54
- 0x4a, 0x06, // send 06 - call PlaySong::play
- 0x33, 0x09, // jmp 09 -> end of routine
- 0x38, 0xaa, 0x00, // pushi 00aa
- 0x76, // push0
- 0x80, 0x29, 0x01, // lag 0129
- 0x4a, 0x04, // send 04
- 0x48, // ret
- 0
-};
-
-const uint16 hoyle4PatchPortFix[] = {
- PATCH_ADDTOOFFSET | +33,
- 0x38, 0x31, 0x01, // pushi 0131 (selector curEvent)
- 0x76, // push0
- 0x80, 0x50, 0x00, // lag 0050 (global var 80h, "User")
- 0x4a, 0x04, // send 04 - read User::curEvent
-
- 0x38, 0x93, 0x00, // pushi 0093 (selector port)
- 0x78, // push1
- 0x76, // push0
- 0x4a, 0x06, // send 06 - write 0 to that object::port
- 0x48, // ret
- PATCH_END
-};
-
-// script, description, magic DWORD, adjust
-const SciScriptSignature hoyle4Signatures[] = {
- { 0, "port fix when disposing windows", PATCH_MAGICDWORD(0x64, 0x38, 0xC8, 0x00), -5, hoyle4SignaturePortFix, hoyle4PatchPortFix },
- { 0, NULL, 0, 0, NULL, NULL }
-};
-
-#endif
-
// ===========================================================================
// at least during harpy scene export 29 of script 0 is called in kq5cd and
// has an issue for those calls, where temp 3 won't get inititialized, but
@@ -622,6 +505,36 @@ const SciScriptSignature kq5Signatures[] = {
};
// ===========================================================================
+// When giving the milk bottle to one of the babies in the garden in KQ6 (room
+// 480), script 481 starts a looping baby cry sound. However, that particular
+// script also has an overriden check method (cryMusic::check). This method
+// explicitly restarts the sound, even if it's set to be looped, thus the same
+// sound is played twice, squelching all other sounds. We just rip the
+// unnecessary cryMusic::check method out, thereby stopping the sound from
+// constantly restarting (since it's being looped anyway), thus the normal
+// game speech can work while the baby cry sound is heard. Fixes bug #3034579.
+const byte kq6SignatureDuplicateBabyCry[] = {
+ 10,
+ 0x83, 0x00, // lal 00
+ 0x31, 0x1e, // bnt 1e [07f4]
+ 0x78, // push1
+ 0x39, 0x04, // pushi 04
+ 0x43, 0x75, 0x02, // callk DoAudio[75] 02
+ 0
+};
+
+const uint16 kq6PatchDuplicateBabyCry[] = {
+ 0x48, // ret
+ PATCH_END
+};
+
+// script, description, magic DWORD, adjust
+const SciScriptSignature kq6Signatures[] = {
+ { 481, "duplicate baby cry", 1, PATCH_MAGICDWORD(0x83, 0x00, 0x31, 0x1e), 0, kq6SignatureDuplicateBabyCry, kq6PatchDuplicateBabyCry },
+ SCI_SIGNATUREENTRY_TERMINATOR
+};
+
+// ===========================================================================
// this is called on every death dialog. Problem is at least the german
// version of lsl6 gets title text that is far too long for the
// available temp space resulting in temp space corruption
@@ -673,7 +586,7 @@ const SciScriptSignature larry6Signatures[] = {
};
// ===========================================================================
-// rm560::doit was supposed to close the painting, when heimlich enters the
+// rm560::doit was supposed to close the painting, when Heimlich enters the
// room. The code is buggy, so it actually closes the painting, when heimlich
// is not in the room. We fix that.
const byte laurabow2SignaturePaintingClosing[] = {
@@ -796,44 +709,10 @@ const uint16 qfg1vgaPatchFightEvents[] = {
PATCH_END
};
-// When QFG1VGA and QFG3 dispose of a child window. For example, when choosing
-// a spell (parent window), if the spell can't be casted, a subsequent window
-// opens, notifying that it can't be casted. When showing the child window, the
-// scripts restore the area below the parent window, draw the child window, and
-// then attempt to redraw the parent window, which leads to the background
-// picture (which has just been restored) overwriting the child window. It
-// appers that kGraph(redrawBox) is different in QFG1VGA and QFG3. However, we
-// can just remove the window redraw and update calls when the window is
-// supposed to be disposed, and the window is disposed of correctly. Fixes bug
-// #3053093.
-const byte qfg1vgaWindowDispose[] = {
- 17,
- 0x39, 0x05, // pushi 05
- 0x39, 0x0d, // pushi 0d
- 0x67, 0x2e, // pTos 2e
- 0x67, 0x30, // pTos 30
- 0x67, 0x32, // pTos 32
- 0x67, 0x34, // pTos 34
- 0x43, 0x6c, 0x0a, // callk kGraph 10
- 0x39, 0x06, // pushi 06
- 0
-};
-
-const uint16 qfg1vgaPatchWindowDispose[] = {
- 0x34, 0x00, 0x00, // ldi 0000 (dummy)
- 0x34, 0x00, 0x00, // ldi 0000 (dummy)
- 0x34, 0x00, 0x00, // ldi 0000 (dummy)
- 0x34, 0x00, 0x00, // ldi 0000 (dummy)
- 0x34, 0x00, 0x00, // ldi 0000 (dummy)
- 0x33, 0x3e, // jmp 0x3e (skip 62 bytes - this skips the subsequent 2 kGraph(update) calls, before kDisposeWindow is invoked)
- PATCH_END
-};
-
// script, description, magic DWORD, adjust
const SciScriptSignature qfg1vgaSignatures[] = {
{ 215, "fight event issue", 1, PATCH_MAGICDWORD(0x6d, 0x76, 0x51, 0x07), -1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents },
{ 216, "weapon master event issue", 1, PATCH_MAGICDWORD(0x6d, 0x76, 0x51, 0x07), -1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents },
- { 559, "window dispose", 1, PATCH_MAGICDWORD(0x39, 0x05, 0x39, 0x0d), 0, qfg1vgaWindowDispose, qfg1vgaPatchWindowDispose },
SCI_SIGNATUREENTRY_TERMINATOR
};
@@ -897,37 +776,6 @@ const uint16 qfg3PatchImportDialog[] = {
PATCH_END
};
-// When QFG1VGA and QFG3 dispose of a child window. For example, when choosing
-// a spell (parent window), if the spell can't be casted, a subsequent window
-// opens, notifying that it can't be casted. When showing the child window, the
-// scripts restore the area below the parent window, draw the child window, and
-// then attempt to redraw the parent window, which leads to the background
-// picture (which has just been restored) overwriting the child window. It
-// appers that kGraph(redrawBox) is different in QFG1VGA and QFG3. However, we
-// can just remove the window redraw and update calls when the window is
-// supposed to be disposed, and the window is disposed of correctly. Fixes bug
-// #3053093.
-const byte qfg3WindowDispose[] = {
- 15,
- 0x39, 0x05, // pushi 05
- 0x39, 0x0d, // pushi 0d
- 0x67, 0x2e, // pTos 2e
- 0x67, 0x30, // pTos 30
- 0x67, 0x32, // pTos 32
- 0x67, 0x34, // pTos 34
- 0x43, 0x6c, 0x0a, // callk kGraph 10
- 0
-};
-
-const uint16 qfg3PatchWindowDispose[] = {
- 0x34, 0x00, 0x00, // ldi 0000 (dummy)
- 0x34, 0x00, 0x00, // ldi 0000 (dummy)
- 0x34, 0x00, 0x00, // ldi 0000 (dummy)
- 0x34, 0x00, 0x00, // ldi 0000 (dummy)
- 0x34, 0x00, 0x00, // ldi 0000 (dummy)
- PATCH_END
-};
-
// Script 23 in QFG3 has a typo/bug which makes it loop endlessly and
// read garbage. Fixes bug #3040722.
const byte qfg3DialogCrash[] = {
@@ -946,7 +794,6 @@ const uint16 qfg3PatchDialogCrash[] = {
// script, description, magic DWORD, adjust
const SciScriptSignature qfg3Signatures[] = {
- { 22, "window dispose", 1, PATCH_MAGICDWORD(0x39, 0x05, 0x39, 0x0d), 0, qfg3WindowDispose, qfg3PatchWindowDispose },
{ 23, "dialog crash", 1, PATCH_MAGICDWORD(0xe7, 0x03, 0x22, 0x33), -1, qfg3DialogCrash, qfg3PatchDialogCrash },
{ 944, "import dialog continuous calls", 1, PATCH_MAGICDWORD(0x2a, 0x31, 0x0b, 0x7a), -1, qfg3SignatureImportDialog, qfg3PatchImportDialog },
SCI_SIGNATUREENTRY_TERMINATOR
@@ -1065,55 +912,6 @@ const SciScriptSignature sq4Signatures[] = {
SCI_SIGNATUREENTRY_TERMINATOR
};
-// ===========================================================================
-// It seems to scripts warp ego outside the screen somehow (or maybe kDoBresen?)
-// ego::mover is set to 0 and rm119::doit will crash in that case. This here
-// fixes part of the problem and actually checks ego::mover to be 0 and skips
-// TODO: this should get further investigated by waltervn and maybe properly
-// patched. For now ego will shortly disappear and reappear a bit after
-// this isn't good, but sierra sci also "crashed" (endless looped) so this
-// is at least better than the original code
-const byte sq5SignatureScrubbing[] = {
- 19,
- 0x18, // not
- 0x31, 0x37, // bnt 37
- 0x78, // push1 (selector x)
- 0x76, // push0
- 0x39, 0x38, // pushi 38 (selector mover)
- 0x76, // push0
- 0x81, 0x00, // lag 00
- 0x4a, 0x04, // send 04 - read ego::mover
- 0x4a, 0x04, // send 04 - read ego::mover::x
- 0x36, // push
- 0x34, 0xa0, 0x00, // ldi 00a0
- 0x1c, // ne?
- 0
-};
-
-const uint16 sq5PatchScrubbing[] = {
- 0x18, // not
- 0x31, 0x37, // bnt 37
-// 0x2f, 0x38, // bt 37 (would save another byte, isn't needed
- 0x39, 0x38, // pushi 38 (selector mover)
- 0x76, // push0
- 0x81, 0x00, // lag 00
- 0x4a, 0x04, // send 04 - read ego::mover
- 0x31, 0x2e, // bnt 2e (jump if ego::mover is 0)
- 0x78, // push1 (selector x)
- 0x76, // push0
- 0x4a, 0x04, // send 04 - read ego::mover::x
- 0x39, 0xa0, // pushi a0 (saving 2 bytes)
- 0x1c, // ne?
- PATCH_END
-};
-
-// script, description, magic DWORD, adjust
-const SciScriptSignature sq5Signatures[] = {
- { 119, "scrubbing send crash", 1, PATCH_MAGICDWORD(0x18, 0x31, 0x37, 0x78), 0, sq5SignatureScrubbing, sq5PatchScrubbing },
- SCI_SIGNATUREENTRY_TERMINATOR
-};
-
-
// will actually patch previously found signature area
void Script::applyPatch(const uint16 *patch, byte *scriptData, const uint32 scriptSize, int32 signatureOffset) {
byte orgData[PATCH_VALUELIMIT];
@@ -1206,12 +1004,6 @@ int32 Script::findSignature(const SciScriptSignature *signature, const byte *scr
void Script::matchSignatureAndPatch(uint16 scriptNr, byte *scriptData, const uint32 scriptSize) {
const SciScriptSignature *signatureTable = NULL;
switch (g_sci->getGameId()) {
- // Dr. Brain now works because we properly maintain the state of the string heap in savegames
-#if 0
- case GID_CASTLEBRAIN:
- signatureTable = castlebrainSignatures;
- break;
-#endif
case GID_ECOQUEST:
signatureTable = ecoquest1Signatures;
break;
@@ -1227,15 +1019,12 @@ void Script::matchSignatureAndPatch(uint16 scriptNr, byte *scriptData, const uin
case GID_GK1:
signatureTable = gk1Signatures;
break;
- // hoyle4 now works due to workaround inside GfxPorts
-#if 0
- case GID_HOYLE4:
- signatureTable = hoyle4Signatures;
- break;
-#endif
case GID_KQ5:
signatureTable = kq5Signatures;
break;
+ case GID_KQ6:
+ signatureTable = kq6Signatures;
+ break;
case GID_LAURABOW2:
signatureTable = laurabow2Signatures;
break;
@@ -1257,9 +1046,6 @@ void Script::matchSignatureAndPatch(uint16 scriptNr, byte *scriptData, const uin
case GID_SQ4:
signatureTable = sq4Signatures;
break;
- case GID_SQ5:
- signatureTable = sq5Signatures;
- break;
default:
break;
}
diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp
index ffc81f0fde..c75ceab280 100644
--- a/engines/sci/engine/seg_manager.cpp
+++ b/engines/sci/engine/seg_manager.cpp
@@ -403,7 +403,7 @@ void SegManager::freeHunkEntry(reg_t addr) {
return;
}
- ht->freeEntry(addr.offset);
+ ht->freeEntryContents(addr.offset);
}
reg_t SegManager::allocateHunkEntry(const char *hunk_type, int size) {
@@ -602,13 +602,13 @@ static inline char getChar(const SegmentRef &ref, uint offset) {
warning("Attempt to read character from non-raw data");
bool oddOffset = offset & 1;
- if (g_sci->getPlatform() == Common::kPlatformAmiga)
- oddOffset = !oddOffset; // Amiga versions are BE
+ if (g_sci->isBE())
+ oddOffset = !oddOffset;
return (oddOffset ? val.offset >> 8 : val.offset & 0xff);
}
-static inline void setChar(const SegmentRef &ref, uint offset, char value) {
+static inline void setChar(const SegmentRef &ref, uint offset, byte value) {
if (ref.skipByte)
offset++;
@@ -617,8 +617,8 @@ static inline void setChar(const SegmentRef &ref, uint offset, char value) {
val->segment = 0;
bool oddOffset = offset & 1;
- if (g_sci->getPlatform() == Common::kPlatformAmiga)
- oddOffset = !oddOffset; // Amiga versions are BE
+ if (g_sci->isBE())
+ oddOffset = !oddOffset;
if (oddOffset)
val->offset = (val->offset & 0x00ff) | (value << 8);
diff --git a/engines/sci/engine/segment.cpp b/engines/sci/engine/segment.cpp
index 05d914cffb..d1f56f0da2 100644
--- a/engines/sci/engine/segment.cpp
+++ b/engines/sci/engine/segment.cpp
@@ -275,6 +275,11 @@ Common::Array<reg_t> DataStack::listAllOutgoingReferences(reg_t object) const {
}
+//-------------------- hunk ---------------------
+void HunkTable::freeAtAddress(SegManager *segMan, reg_t sub_addr) {
+ freeEntry(sub_addr.offset);
+}
+
//-------------------- lists --------------------
void ListTable::freeAtAddress(SegManager *segMan, reg_t sub_addr) {
freeEntry(sub_addr.offset);
diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h
index 9aaa3a4b08..56a6c71590 100644
--- a/engines/sci/engine/segment.h
+++ b/engines/sci/engine/segment.h
@@ -315,15 +315,18 @@ struct ListTable : public Table<List> {
struct HunkTable : public Table<Hunk> {
HunkTable() : Table<Hunk>(SEG_TYPE_HUNK) {}
- virtual void freeEntry(int idx) {
- Table<Hunk>::freeEntry(idx);
-
- if (!_table[idx].mem)
- warning("Attempt to free an already freed hunk");
+ void freeEntryContents(int idx) {
free(_table[idx].mem);
_table[idx].mem = 0;
}
+ virtual void freeEntry(int idx) {
+ Table<Hunk>::freeEntry(idx);
+ freeEntryContents(idx);
+ }
+
+ virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr);
+
virtual void saveLoadWithSerializer(Common::Serializer &ser);
};
diff --git a/engines/sci/engine/static_selectors.cpp b/engines/sci/engine/static_selectors.cpp
index 4bb61a0658..96507e3e1b 100644
--- a/engines/sci/engine/static_selectors.cpp
+++ b/engines/sci/engine/static_selectors.cpp
@@ -185,7 +185,7 @@ Common::StringArray Kernel::checkStaticSelectorNames() {
// dispose comes right after init
names[initSelectorPos + 1] = "dispose";
- if ((getSciVersion() >= SCI_VERSION_1_EGA)) {
+ if ((getSciVersion() >= SCI_VERSION_1_EGA_ONLY)) {
// Find the xLast and yLast selectors, used in kDoBresen
// xLast and yLast always come between illegalBits and xStep
@@ -205,7 +205,7 @@ Common::StringArray Kernel::checkStaticSelectorNames() {
names[xLastSelectorPos] = "xLast";
names[yLastSelectorPos] = "yLast";
- } // if ((getSciVersion() >= SCI_VERSION_1_EGA))
+ } // if ((getSciVersion() >= SCI_VERSION_1_EGA_ONLY))
} // if (actorClass)
_segMan->uninstantiateScript(998);
diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp
index a70ff5ab72..24f3c96f62 100644
--- a/engines/sci/engine/vm.cpp
+++ b/engines/sci/engine/vm.cpp
@@ -47,6 +47,10 @@ const reg_t NULL_REG = {0, 0};
const reg_t SIGNAL_REG = {0, SIGNAL_OFFSET};
const reg_t TRUE_REG = {0, 1};
//#define VM_DEBUG_SEND
+// Enable the define below to have the VM abort on cases where a conditional
+// statement is followed by an unconditional jump (which will most likely lead
+// to an infinite loop). Aids in detecting script bugs such as #3040722.
+//#define ABORT_ON_INFINITE_LOOP
#define SCI_XS_CALLEE_LOCALS ((SegmentId)-1)
@@ -114,8 +118,8 @@ static reg_t &validate_property(EngineState *s, Object *obj, int index) {
if (index < 0 || (uint)index >= obj->getVarCount()) {
// This is same way sierra does it and there are some games, that contain such scripts like
// iceman script 998 (fred::canBeHere, executed right at the start)
- debugC(kDebugLevelVM, "[VM] Invalid property #%d (out of [0..%d]) requested!",
- index, obj->getVarCount());
+ debugC(kDebugLevelVM, "[VM] Invalid property #%d (out of [0..%d]) requested from object %04x:%04x (%s)",
+ index, obj->getVarCount(), PRINT_REG(obj->getPos()), s->_segMan->getObjectName(obj->getPos()));
return dummyReg;
}
@@ -628,18 +632,41 @@ static void logKernelCall(const KernelFunction *kernelCall, const KernelSubFunct
debugN(" (%s)", s->_segMan->getObjectName(argv[parmNr]));
break;
case SIG_TYPE_REFERENCE:
- if (kernelCall->function == kSaid) {
- SegmentRef saidSpec = s->_segMan->dereference(argv[parmNr]);
- if (saidSpec.isRaw) {
- debugN(" ('");
- g_sci->getVocabulary()->debugDecipherSaidBlock(saidSpec.raw);
- debugN("')");
+ {
+ SegmentObj *mobj = s->_segMan->getSegmentObj(argv[parmNr].segment);
+ switch (mobj->getType()) {
+ case SEG_TYPE_HUNK:
+ {
+ HunkTable *ht = (HunkTable*)mobj;
+ int index = argv[parmNr].offset;
+ if (ht->isValidEntry(index)) {
+ // NOTE: This ", deleted" isn't as useful as it could
+ // be because it prints the status _after_ the kernel
+ // call.
+ debugN(" ('%s' hunk%s)", ht->_table[index].type, ht->_table[index].mem ? "" : ", deleted");
+ } else
+ debugN(" (INVALID hunk ref)");
+ break;
+ }
+ default:
+ // TODO: Any other segment types which could
+ // use special handling?
+
+ if (kernelCall->function == kSaid) {
+ SegmentRef saidSpec = s->_segMan->dereference(argv[parmNr]);
+ if (saidSpec.isRaw) {
+ debugN(" ('");
+ g_sci->getVocabulary()->debugDecipherSaidBlock(saidSpec.raw);
+ debugN("')");
+ } else {
+ debugN(" (non-raw said-spec)");
+ }
} else {
- debugN(" (non-raw said-spec)");
+ debugN(" ('%s')", s->_segMan->getString(argv[parmNr]).c_str());
}
- } else {
- debugN(" ('%s')", s->_segMan->getString(argv[parmNr]).c_str());
+ break;
}
+ }
default:
break;
}
@@ -871,6 +898,10 @@ void run_vm(EngineState *s) {
s->_executionStackPosChanged = true; // Force initialization
+#ifdef ABORT_ON_INFINITE_LOOP
+ byte prevOpcode = 0xFF;
+#endif
+
while (1) {
int var_type; // See description below
int var_number;
@@ -935,7 +966,22 @@ void run_vm(EngineState *s) {
byte extOpcode;
s->xs->addr.pc.offset += readPMachineInstruction(scr->getBuf() + s->xs->addr.pc.offset, extOpcode, opparams);
const byte opcode = extOpcode >> 1;
- //debug("%s: %d, %d, %d, %d, acc = %04x:%04x", opcodeNames[opcode], opparams[0], opparams[1], opparams[2], opparams[3], PRINT_REG(s->r_acc));
+ //debug("%s: %d, %d, %d, %d, acc = %04x:%04x, script %d, local script %d", opcodeNames[opcode], opparams[0], opparams[1], opparams[2], opparams[3], PRINT_REG(s->r_acc), scr->getScriptNumber(), local_script->getScriptNumber());
+
+#ifdef ABORT_ON_INFINITE_LOOP
+ if (prevOpcode != 0xFF) {
+ if (prevOpcode == op_eq_ || prevOpcode == op_ne_ ||
+ prevOpcode == op_gt_ || prevOpcode == op_ge_ ||
+ prevOpcode == op_lt_ || prevOpcode == op_le_ ||
+ prevOpcode == op_ugt_ || prevOpcode == op_uge_ ||
+ prevOpcode == op_ult_ || prevOpcode == op_ule_) {
+ if (opcode == op_jmp)
+ error("Infinite loop detected in script %d", scr->getScriptNumber());
+ }
+ }
+
+ prevOpcode = opcode;
+#endif
switch (opcode) {
diff --git a/engines/sci/engine/vm_types.cpp b/engines/sci/engine/vm_types.cpp
index dea4d63bf0..0b790bb8af 100644
--- a/engines/sci/engine/vm_types.cpp
+++ b/engines/sci/engine/vm_types.cpp
@@ -31,13 +31,11 @@
namespace Sci {
-extern const char *opcodeNames[]; // from scriptdebug.cpp
-
reg_t reg_t::lookForWorkaround(const reg_t right) const {
SciTrackOriginReply originReply;
SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, arithmeticWorkarounds, &originReply);
if (solution.type == WORKAROUND_NONE)
- error("arithmetic operation on non-integer (%04x:%04x, %04x:%04x) from method %s::%s (script %d, room %d, localCall %x)",
+ error("Invalid arithmetic operation (params: %04x:%04x and %04x:%04x) from method %s::%s (script %d, room %d, localCall %x)",
PRINT_REG(*this), PRINT_REG(right), originReply.objectName.c_str(),
originReply.methodName.c_str(), originReply.scriptNr, g_sci->getEngineState()->currentRoomNumber(),
originReply.localCallOffset);
@@ -46,7 +44,7 @@ reg_t reg_t::lookForWorkaround(const reg_t right) const {
}
reg_t reg_t::operator+(const reg_t right) const {
- if (isPointer() && isInitialized()) {
+ if (isPointer() && right.isNumber()) {
// Pointer arithmetics. Only some pointer types make sense here
SegmentObj *mobj = g_sci->getEngineState()->_segMan->getSegmentObj(segment);
@@ -58,29 +56,18 @@ reg_t reg_t::operator+(const reg_t right) const {
case SEG_TYPE_SCRIPT:
case SEG_TYPE_STACK:
case SEG_TYPE_DYNMEM:
- // Make sure that we are adding an offset to the pointer
- if (right.isPointer())
- return lookForWorkaround(right);
return make_reg(segment, offset + right.toSint16());
default:
return lookForWorkaround(right);
}
- } else if (isNumber() && isInitialized() && right.isPointer()) {
+ } else if (isNumber() && right.isPointer()) {
// Adding a pointer to a number, flip the order
return right + *this;
+ } else if (isNumber() && right.isNumber()) {
+ // Normal arithmetics
+ return make_reg(0, toSint16() + right.toSint16());
} else {
- // Normal arithmetics. Make sure we're adding a number
- if (right.isPointer())
- return lookForWorkaround(right);
- // If the current variable is uninitialized, it'll be set
- // to zero in order to perform the operation. Such a case
- // happens in SQ1, room 28, when throwing the water at Orat.
- if (!isInitialized())
- return make_reg(0, right.toSint16());
- else if (!right.isInitialized())
- return *this;
- else
- return make_reg(0, toSint16() + right.toSint16());
+ return lookForWorkaround(right);
}
}
@@ -97,24 +84,19 @@ reg_t reg_t::operator-(const reg_t right) const {
reg_t reg_t::operator*(const reg_t right) const {
if (isNumber() && right.isNumber())
return make_reg(0, toSint16() * right.toSint16());
- else if (!isInitialized() || !right.isInitialized())
- return NULL_REG; // unitialized variables - always return 0
else
return lookForWorkaround(right);
}
reg_t reg_t::operator/(const reg_t right) const {
- if (isNumber() && right.isNumber()) {
- if (right.isNull())
- return NULL_REG; // division by zero
- else
- return make_reg(0, toSint16() / right.toSint16());
- } else
+ if (isNumber() && right.isNumber() && !right.isNull())
+ return make_reg(0, toSint16() / right.toSint16());
+ else
return lookForWorkaround(right);
}
reg_t reg_t::operator%(const reg_t right) const {
- if (isNumber() && right.isNumber()) {
+ if (isNumber() && right.isNumber() && !right.isNull()) {
// Support for negative numbers was added in Iceman, and perhaps in
// SCI0 0.000.685 and later. Theoretically, this wasn't really used
// in SCI0, so the result is probably unpredictable. Such a case
@@ -125,7 +107,7 @@ reg_t reg_t::operator%(const reg_t right) const {
warning("Modulo of a negative number has been requested for SCI0. This *could* lead to issues");
int16 value = toSint16();
int16 modulo = ABS(right.toSint16());
- int16 result = (modulo != 0 ? value % modulo : 0);
+ int16 result = value % modulo;
if (result < 0)
result += modulo;
return make_reg(0, result);
@@ -159,6 +141,8 @@ uint16 reg_t::requireUint16() const {
if (isNumber())
return toUint16();
else
+ // The right parameter is NULL_REG because
+ // we're not comparing *this with anything here.
return lookForWorkaround(NULL_REG).toUint16();
}
@@ -166,6 +150,8 @@ int16 reg_t::requireSint16() const {
if (isNumber())
return toSint16();
else
+ // The right parameter is NULL_REG because
+ // we're not comparing *this with anything here.
return lookForWorkaround(NULL_REG).toSint16();
}
@@ -190,77 +176,51 @@ reg_t reg_t::operator^(const reg_t right) const {
return lookForWorkaround(right);
}
-bool reg_t::operator>(const reg_t right) const {
- if (isNumber() && right.isNumber())
- return toSint16() > right.toSint16();
- else if (isPointer() && segment == right.segment)
- return toUint16() > right.toUint16(); // pointer comparison
- else if (pointerComparisonWithInteger(right))
- return true;
- else if (right.pointerComparisonWithInteger(*this))
- return false;
- else
- return lookForWorkaround(right).toSint16();
-}
-
-bool reg_t::operator<(const reg_t right) const {
- if (isNumber() && right.isNumber())
- return toSint16() < right.toSint16();
- else if (isPointer() && segment == right.segment)
- return toUint16() < right.toUint16(); // pointer comparison
- else if (pointerComparisonWithInteger(right))
- return false;
- else if (right.pointerComparisonWithInteger(*this))
- return true;
- else
- return lookForWorkaround(right).toSint16();
-}
-
-bool reg_t::gtU(const reg_t right) const {
- if (isNumber() && right.isNumber())
- return toUint16() > right.toUint16();
- else if (isPointer() && segment == right.segment)
- return toUint16() > right.toUint16(); // pointer comparison
- else if (pointerComparisonWithInteger(right))
- return true;
- else if (right.pointerComparisonWithInteger(*this))
- return false;
- else
- return lookForWorkaround(right).toSint16();
-}
-
-bool reg_t::ltU(const reg_t right) const {
- if (isNumber() && right.isNumber())
- return toUint16() < right.toUint16();
- else if (isPointer() && segment == right.segment)
- return toUint16() < right.toUint16(); // pointer comparison
- else if (pointerComparisonWithInteger(right))
- return false;
- else if (right.pointerComparisonWithInteger(*this))
- return true;
- else
+int reg_t::cmp(const reg_t right, bool treatAsUnsigned) const {
+ if (segment == right.segment) { // can compare things in the same segment
+ if (treatAsUnsigned || !isNumber())
+ return toUint16() - right.toUint16();
+ else
+ return toSint16() - right.toSint16();
+ } else if (pointerComparisonWithInteger(right)) {
+ return 1;
+ } else if (right.pointerComparisonWithInteger(*this)) {
+ return -1;
+ } else
return lookForWorkaround(right).toSint16();
}
bool reg_t::pointerComparisonWithInteger(const reg_t right) const {
- // SCI0 - SCI1.1 scripts use this to check whether a parameter is a pointer
- // or a far text reference. It is used e.g. by the standard library Print
- // function to distinguish two ways of calling it:
+ // This function handles the case where a script tries to compare a pointer
+ // to a number. Normally, we would not want to allow that. However, SCI0 -
+ // SCI1 scripts do this in order to distinguish references to
+ // external resources (which are numbers) from pointers. In
+ // our SCI implementation, such a check may seem pointless, as
+ // one can simply use the segment value to achieve this goal.
+ // But Sierra's SCI did not have the notion of segment IDs, so
+ // both pointer and numbers were simple integers.
+ //
+ // But for some things, scripts had (and have) to distinguish between
+ // numbers and pointers. Lacking the segment information, Sierra's
+ // developers resorted to a hack: If an integer is smaller than a certain
+ // bound, it can be assumed to be a number, otherwise it is assumed to be a
+ // pointer. This allowed them to implement polymorphic functions, such as
+ // the Print function, which can be called in two different ways, with a
+ // pointer or a far text reference:
//
// (Print "foo") // Pointer to a string
// (Print 420 5) // Reference to the fifth message in text resource 420
// It works because in those games, the maximum resource number is 999,
// so any parameter value above that threshold must be a pointer.
// PQ2 japanese compares pointers to 2000 to find out if its a pointer
- // or a resource ID.
- // There are cases where game scripts check for arbitrary numbers against
- // pointers, e.g.:
+ // or a resource ID. Thus, we check for all integers <= 2000.
+ //
+ // Some examples where game scripts check for arbitrary numbers against
+ // pointers:
// Hoyle 3, Pachisi, when any opponent is about to talk
// SQ1, room 28, when throwing water at the Orat
// SQ1, room 58, when giving the ID card to the robot
- // QFG3, room 440, when talking to Uhura
- // Thus we check for all integers <= 2000
- return (isPointer() && right.isNumber() && right.offset <= 2000 && getSciVersion() <= SCI_VERSION_1_1);
+ return (isPointer() && right.isNumber() && right.offset <= 2000 && getSciVersion() <= SCI_VERSION_1_LATE);
}
} // End of namespace Sci
diff --git a/engines/sci/engine/vm_types.h b/engines/sci/engine/vm_types.h
index ac23bbe7dc..b927df339e 100644
--- a/engines/sci/engine/vm_types.h
+++ b/engines/sci/engine/vm_types.h
@@ -37,20 +37,20 @@ struct reg_t {
SegmentId segment;
uint16 offset;
- bool isNull() const {
- return !(offset || segment);
+ inline bool isNull() const {
+ return (offset | segment) == 0;
}
- uint16 toUint16() const {
+ inline uint16 toUint16() const {
return offset;
}
- int16 toSint16() const {
- return (int16) offset;
+ inline int16 toSint16() const {
+ return (int16)offset;
}
bool isNumber() const {
- return !segment;
+ return segment == 0;
}
bool isPointer() const {
@@ -60,7 +60,7 @@ struct reg_t {
uint16 requireUint16() const;
int16 requireSint16() const;
- bool isInitialized() const {
+ inline bool isInitialized() const {
return segment != 0xFFFF;
}
@@ -73,35 +73,39 @@ struct reg_t {
return (offset != x.offset) || (segment != x.segment);
}
- bool operator>(const reg_t right) const;
+ bool operator>(const reg_t right) const {
+ return cmp(right, false) > 0;
+ }
+
bool operator>=(const reg_t right) const {
- if (*this == right)
- return true;
- return *this > right;
+ return cmp(right, false) >= 0;
+ }
+
+ bool operator<(const reg_t right) const {
+ return cmp(right, false) < 0;
}
- bool operator<(const reg_t right) const;
+
bool operator<=(const reg_t right) const {
- if (*this == right)
- return true;
- return *this < right;
+ return cmp(right, false) <= 0;
}
// Same as the normal operators, but perform unsigned
// integer checking
- bool gtU(const reg_t right) const;
+ bool gtU(const reg_t right) const {
+ return cmp(right, true) > 0;
+ }
+
bool geU(const reg_t right) const {
- if (*this == right)
- return true;
- return gtU(right);
+ return cmp(right, true) >= 0;
}
- bool ltU(const reg_t right) const;
- bool leU(const reg_t right) const {
- if (*this == right)
- return true;
- return ltU(right);
+
+ bool ltU(const reg_t right) const {
+ return cmp(right, true) < 0;
}
- bool pointerComparisonWithInteger(const reg_t right) const;
+ bool leU(const reg_t right) const {
+ return cmp(right, true) <= 0;
+ }
// Arithmetic operators
reg_t operator+(const reg_t right) const;
@@ -125,7 +129,17 @@ struct reg_t {
reg_t operator|(const reg_t right) const;
reg_t operator^(const reg_t right) const;
+private:
+ /**
+ * Compares two reg_t's.
+ * Returns:
+ * - a positive number if *this > right
+ * - 0 if *this == right
+ * - a negative number if *this < right
+ */
+ int cmp(const reg_t right, bool treatAsUnsigned) const;
reg_t lookForWorkaround(const reg_t right) const;
+ bool pointerComparisonWithInteger(const reg_t right) const;
};
static inline reg_t make_reg(SegmentId segment, uint16 offset) {
diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp
index 64cbc5ec90..2b60b1aa81 100644
--- a/engines/sci/engine/workarounds.cpp
+++ b/engines/sci/engine/workarounds.cpp
@@ -44,8 +44,6 @@ const SciWorkaroundEntry arithmeticWorkarounds[] = {
{ 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_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 #3039879
- // TODO: The SQ5 workaround below may no longer be necessary
- { GID_SQ5, 200, 939, 0, "Osc", "cycleDone", -1, 0, { WORKAROUND_FAKE, 1 } }, // op_dpToa: when going back to bridge the crew is goofing off, we get an object as cycle count
SCI_WORKAROUNDENTRY_TERMINATOR
};
@@ -120,6 +118,7 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
{ 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 #3040012
{ GID_PEPPER, 150, 928, 0, "Narrator", "startText", -1, 0, { WORKAROUND_FAKE, 0 } }, // happens during the non-interactive demo of Pepper
+ { 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 #3053268
@@ -273,10 +272,6 @@ const SciWorkaroundEntry kGraphSaveBox_workarounds[] = {
// gameID, room,script,lvl, object-name, method-name, call,index, workaround
const SciWorkaroundEntry kGraphRestoreBox_workarounds[] = {
- { GID_LSL6, -1, 85, 0, "rScroller", "hide", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens when restoring (sometimes), same as the one below
- { GID_LSL6, -1, 85, 0, "lScroller", "hide", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens when restoring (sometimes), same as the one below
- { GID_LSL6, -1, 86, 0, "LL6Inv", "show", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens when restoring, is called with hunk segment, but hunk is not allocated at that time
- // ^^ TODO: check, if this is really a script error or an issue with our restore code
{ GID_LSL6, -1, 86, 0, "LL6Inv", "hide", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens during the game, gets called with 1 extra parameter
{ GID_SQ5, 850, 850, 0, NULL, "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens while playing Battle Cruiser (invalid segment) - bug #3056811
SCI_WORKAROUNDENTRY_TERMINATOR
@@ -316,7 +311,7 @@ const SciWorkaroundEntry kGraphRedrawBox_workarounds[] = {
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", "movePt", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #3038077
- { GID_PQ3, 202, 202, 0, "MapEdit", "addPt", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #3038077
+ { GID_PQ3, 202, 202, 0, "MapEdit", "addPt", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #3038077
SCI_WORKAROUNDENTRY_TERMINATOR
};
@@ -330,7 +325,7 @@ const SciWorkaroundEntry kIsObject_workarounds[] = {
// gameID, room,script,lvl, object-name, method-name, call,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, talking to Mr. Augustini, etc. - bug #3034490
+ { 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 #3034490
SCI_WORKAROUNDENTRY_TERMINATOR
};
@@ -361,7 +356,7 @@ const SciWorkaroundEntry kSetCursor_workarounds[] = {
// gameID, room,script,lvl, object-name, method-name, call,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, bug #3040844
+ { 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 #3040844)
SCI_WORKAROUNDENTRY_TERMINATOR
};
@@ -386,23 +381,13 @@ const SciWorkaroundEntry kStrLen_workarounds[] = {
// gameID, room,script,lvl, object-name, method-name, call,index, workaround
const SciWorkaroundEntry kUnLoad_workarounds[] = {
- { GID_CAMELOT, 921, 921, 1, "Script", "changeState", 0x36, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: While showing Camelot (and other places), the reference is invalid - bug #3035000
- { GID_CAMELOT, 921, 921, 1, "Script", "init", 0x36, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: When being attacked by the boar (and other places), the reference is invalid - bug #3035000
- { GID_CASTLEBRAIN, 320, 377, 0, "SWord", "upDate", -1, 0, { WORKAROUND_IGNORE, 0 } }, // after solving the cross-word-puzzle, trying to unload invalid reference
- { GID_CASTLEBRAIN, 320, 377, 0, "theWord", "show", -1, 0, { WORKAROUND_IGNORE, 0 } }, // 2nd word puzzle, when exiting before solving, trying to unload invalid reference - bug #3034473
- { GID_ECOQUEST, 380, 61, 0, "gotIt", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // after talking to the dolphin the first time
- { GID_ECOQUEST, 380, 69, 0, "lookAtBlackBoard", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // German version, when closing the blackboard closeup in the dolphin room - bug #3098353
- { GID_LAURABOW2, 1, 1, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during the intro, a 3rd parameter is passed by accident - bug #3034902
- { GID_LAURABOW2, 2, 2, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during the intro, a 3rd parameter is passed by accident - bug #3034902
- { GID_LAURABOW2, 4, 4, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: inside the museum, a 3rd parameter is passed by accident - bug #3034902
- { GID_LAURABOW2, 6, 6, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: after the murder, a 3rd parameter is passed by accident - bug #3034902
- { GID_LAURABOW2, 7, 7, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: after the logo is shown, a 3rd parameter is passed by accident - bug #3034902
+ { 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 #3098353
+ { GID_LAURABOW2, -1, 1, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during the intro, a 3rd parameter is passed by accident - bug #3034902
{ 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_PQ3, 877, 998, 0, "View", "delete", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when getting run over on the freeway, the reference is invalid
{ 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_SQ3, 2, 998, 0, "View", "delete", -1, 0, { WORKAROUND_IGNORE, 0 } }, // clicking the mouse button during the intro, after the escape pod gets pulled into the garbage freighter, the reference is invalid - bug #3050856
SCI_WORKAROUNDENTRY_TERMINATOR
};
diff --git a/engines/sci/graphics/animate.cpp b/engines/sci/graphics/animate.cpp
index 2e5e94c52a..e02b27c788 100644
--- a/engines/sci/graphics/animate.cpp
+++ b/engines/sci/graphics/animate.cpp
@@ -191,11 +191,88 @@ void GfxAnimate::makeSortedList(List *list) {
Common::sort(_list.begin(), _list.end(), sortHelper);
}
-void GfxAnimate::applyGlobalScaling(AnimateList::iterator entry, GfxView *view) {
- reg_t curObject = entry->object;
+void GfxAnimate::fill(byte &old_picNotValid) {
+ GfxView *view = NULL;
+ AnimateList::iterator it;
+ const AnimateList::iterator end = _list.end();
+
+ for (it = _list.begin(); it != end; ++it) {
+ // Get the corresponding view
+ view = _cache->getView(it->viewId);
+
+ adjustInvalidCels(view, it);
+ processViewScaling(view, it);
+ setNsRect(view, it);
+
+ //warning("%s view %d, loop %d, cel %d, signal %x", _s->_segMan->getObjectName(curObject), it->viewId, it->loopNo, it->celNo, it->signal);
+
+ // Calculate current priority according to y-coordinate
+ if (!(it->signal & kSignalFixedPriority)) {
+ it->priority = _ports->kernelCoordinateToPriority(it->y);
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(priority), it->priority);
+ }
+
+ if (it->signal & kSignalNoUpdate) {
+ if ((it->signal & (kSignalForceUpdate | kSignalViewUpdated))
+ || (it->signal & kSignalHidden && !(it->signal & kSignalRemoveView))
+ || (!(it->signal & kSignalHidden) && it->signal & kSignalRemoveView)
+ || (it->signal & kSignalAlwaysUpdate))
+ old_picNotValid++;
+ it->signal &= ~kSignalStopUpdate;
+ } else {
+ if ((it->signal & kSignalStopUpdate) || (it->signal & kSignalAlwaysUpdate))
+ old_picNotValid++;
+ it->signal &= ~kSignalForceUpdate;
+ }
+ }
+}
+
+void GfxAnimate::adjustInvalidCels(GfxView *view, AnimateList::iterator it) {
+ // adjust loop and cel, if any of those is invalid
+ // this seems to be completely crazy code
+ // sierra sci checked signed int16 to be above or equal the counts and reseted to 0 in those cases
+ // later during view processing those are compared unsigned again and then set to maximum count - 1
+ // Games rely on this behaviour. For example laura bow 1 has a knight standing around in room 37
+ // which has cel set to 3. This cel does not exist and the actual knight is 0
+ // In kq5 on the other hand during the intro, when the trunk is opened, cel is set to some real
+ // high number, which is negative when considered signed. This actually requires to get fixed to
+ // maximum cel, otherwise the trunk would be closed.
+ int16 viewLoopCount = view->getLoopCount();
+ if (it->loopNo >= viewLoopCount) {
+ it->loopNo = 0;
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(loop), it->loopNo);
+ } else if (it->loopNo < 0) {
+ it->loopNo = viewLoopCount - 1;
+ // not setting selector is right, sierra sci didn't do it during view processing as well
+ }
+ int16 viewCelCount = view->getCelCount(it->loopNo);
+ if (it->celNo >= viewCelCount) {
+ it->celNo = 0;
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(cel), it->celNo);
+ } else if (it->celNo < 0) {
+ it->celNo = viewCelCount - 1;
+ }
+}
+
+void GfxAnimate::processViewScaling(GfxView *view, AnimateList::iterator it) {
+ if (!view->isScaleable()) {
+ // Laura Bow 2 (especially floppy) depends on this, some views are not supposed to be scaleable
+ // this "feature" was removed in later versions of SCI1.1
+ it->scaleSignal = 0;
+ it->scaleY = it->scaleX = 128;
+ } else {
+ // Process global scaling, if needed
+ if (it->scaleSignal & kScaleSignalDoScaling) {
+ if (it->scaleSignal & kScaleSignalGlobalScaling) {
+ applyGlobalScaling(it, view);
+ }
+ }
+ }
+}
+void GfxAnimate::applyGlobalScaling(AnimateList::iterator entry, GfxView *view) {
// Global scaling uses global var 2 and some other stuff to calculate scaleX/scaleY
- int16 maxScale = readSelectorValue(_s->_segMan, curObject, SELECTOR(maxScale));
+ int16 maxScale = readSelectorValue(_s->_segMan, entry->object, SELECTOR(maxScale));
int16 celHeight = view->getHeight(entry->loopNo, entry->celNo);
int16 maxCelHeight = (maxScale * celHeight) >> 7;
reg_t globalVar2 = _s->variables[VAR_GLOBAL][2]; // current room object
@@ -215,120 +292,43 @@ void GfxAnimate::applyGlobalScaling(AnimateList::iterator entry, GfxView *view)
entry->scaleX = entry->scaleY;
// and set objects scale selectors
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(scaleX), entry->scaleX);
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(scaleY), entry->scaleY);
+ writeSelectorValue(_s->_segMan, entry->object, SELECTOR(scaleX), entry->scaleX);
+ writeSelectorValue(_s->_segMan, entry->object, SELECTOR(scaleY), entry->scaleY);
}
-void GfxAnimate::fill(byte &old_picNotValid) {
- reg_t curObject;
- uint16 signal;
- GfxView *view = NULL;
- AnimateList::iterator it;
- const AnimateList::iterator end = _list.end();
-
- for (it = _list.begin(); it != end; ++it) {
- curObject = it->object;
- signal = it->signal;
-
- // Get the corresponding view
- view = _cache->getView(it->viewId);
-
- // adjust loop and cel, if any of those is invalid
- // this seems to be completely crazy code
- // sierra sci checked signed int16 to be above or equal the counts and reseted to 0 in those cases
- // later during view processing those are compared unsigned again and then set to maximum count - 1
- // Games rely on this behaviour. For example laura bow 1 has a knight standing around in room 37
- // which has cel set to 3. This cel does not exist and the actual knight is 0
- // In kq5 on the other hand during the intro, when the trunk is opened, cel is set to some real
- // high number, which is negative when considered signed. This actually requires to get fixed to
- // maximum cel, otherwise the trunk would be closed.
- int16 viewLoopCount = view->getLoopCount();
- if (it->loopNo >= viewLoopCount) {
- it->loopNo = 0;
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(loop), it->loopNo);
- } else if (it->loopNo < 0) {
- it->loopNo = viewLoopCount - 1;
- // not setting selector is right, sierra sci didn't do it during view processing as well
- }
- int16 viewCelCount = view->getCelCount(it->loopNo);
- if (it->celNo >= viewCelCount) {
- it->celNo = 0;
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(cel), it->celNo);
- } else if (it->celNo < 0) {
- it->celNo = viewCelCount - 1;
- }
-
- if (!view->isScaleable()) {
- // Laura Bow 2 (especially floppy) depends on this, some views are not supposed to be scaleable
- // this "feature" was removed in later versions of SCI1.1
- it->scaleSignal = 0;
- it->scaleY = it->scaleX = 128;
- } else {
- // Process global scaling, if needed
- if (it->scaleSignal & kScaleSignalDoScaling) {
- if (it->scaleSignal & kScaleSignalGlobalScaling) {
- applyGlobalScaling(it, view);
- }
- }
- }
-
- //warning("%s view %d, loop %d, cel %d, signal %x", _s->_segMan->getObjectName(curObject), it->viewId, it->loopNo, it->celNo, it->signal);
-
- bool setNsRect = true;
+void GfxAnimate::setNsRect(GfxView *view, AnimateList::iterator it) {
+ bool shouldSetNsRect = true;
- // Create rect according to coordinates and given cel
- if (it->scaleSignal & kScaleSignalDoScaling) {
- view->getCelScaledRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->scaleX, it->scaleY, it->celRect);
- // when being scaled, only set nsRect, if object will get drawn
- if ((signal & kSignalHidden) && !(signal & kSignalAlwaysUpdate))
- setNsRect = false;
+ // Create rect according to coordinates and given cel
+ if (it->scaleSignal & kScaleSignalDoScaling) {
+ view->getCelScaledRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->scaleX, it->scaleY, it->celRect);
+ // when being scaled, only set nsRect, if object will get drawn
+ if ((it->signal & kSignalHidden) && !(it->signal & kSignalAlwaysUpdate))
+ shouldSetNsRect = false;
+ } else {
+ // This special handling is not included in the other SCI1.1 interpreters and MUST NOT be
+ // checked in those cases, otherwise we will break games (e.g. EcoQuest 2, room 200)
+ if ((g_sci->getGameId() == GID_HOYLE4) && (it->scaleSignal & kScaleSignalHoyle4SpecialHandling)) {
+ it->celRect.left = readSelectorValue(_s->_segMan, it->object, SELECTOR(nsLeft));
+ it->celRect.top = readSelectorValue(_s->_segMan, it->object, SELECTOR(nsTop));
+ it->celRect.right = readSelectorValue(_s->_segMan, it->object, SELECTOR(nsRight));
+ it->celRect.bottom = readSelectorValue(_s->_segMan, it->object, SELECTOR(nsBottom));
+ view->getCelSpecialHoyle4Rect(it->loopNo, it->celNo, it->x, it->y, it->z, it->celRect);
+ shouldSetNsRect = false;
} else {
- // This special handling is not included in the other SCI1.1 interpreters and MUST NOT be
- // checked in those cases, otherwise we will break games (e.g. EcoQuest 2, room 200)
- if ((g_sci->getGameId() == GID_HOYLE4) && (it->scaleSignal & kScaleSignalHoyle4SpecialHandling)) {
- it->celRect.left = readSelectorValue(_s->_segMan, curObject, SELECTOR(nsLeft));
- it->celRect.top = readSelectorValue(_s->_segMan, curObject, SELECTOR(nsTop));
- it->celRect.right = readSelectorValue(_s->_segMan, curObject, SELECTOR(nsRight));
- it->celRect.bottom = readSelectorValue(_s->_segMan, curObject, SELECTOR(nsBottom));
- view->getCelSpecialHoyle4Rect(it->loopNo, it->celNo, it->x, it->y, it->z, it->celRect);
- setNsRect = false;
- } else {
- view->getCelRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->celRect);
- }
- }
-
- if (setNsRect) {
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsLeft), it->celRect.left);
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsTop), it->celRect.top);
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsRight), it->celRect.right);
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsBottom), it->celRect.bottom);
- }
-
- // Calculate current priority according to y-coordinate
- if (!(signal & kSignalFixedPriority)) {
- it->priority = _ports->kernelCoordinateToPriority(it->y);
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(priority), it->priority);
+ view->getCelRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->celRect);
}
+ }
- if (signal & kSignalNoUpdate) {
- if (signal & (kSignalForceUpdate | kSignalViewUpdated)
- || (signal & kSignalHidden && !(signal & kSignalRemoveView))
- || (!(signal & kSignalHidden) && signal & kSignalRemoveView)
- || (signal & kSignalAlwaysUpdate))
- old_picNotValid++;
- signal &= ~kSignalStopUpdate;
- } else {
- if (signal & kSignalStopUpdate || signal & kSignalAlwaysUpdate)
- old_picNotValid++;
- signal &= ~kSignalForceUpdate;
- }
- it->signal = signal;
+ if (shouldSetNsRect) {
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(nsLeft), it->celRect.left);
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(nsTop), it->celRect.top);
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(nsRight), it->celRect.right);
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(nsBottom), it->celRect.bottom);
}
}
void GfxAnimate::update() {
- reg_t curObject;
- uint16 signal;
reg_t bitsHandle;
Common::Rect rect;
AnimateList::iterator it;
@@ -336,81 +336,66 @@ void GfxAnimate::update() {
// Remove all no-update cels, if requested
for (it = _list.reverse_begin(); it != end; --it) {
- curObject = it->object;
- signal = it->signal;
-
- if (signal & kSignalNoUpdate) {
- if (!(signal & kSignalRemoveView)) {
- bitsHandle = readSelector(_s->_segMan, curObject, SELECTOR(underBits));
+ if (it->signal & kSignalNoUpdate) {
+ if (!(it->signal & kSignalRemoveView)) {
+ bitsHandle = readSelector(_s->_segMan, it->object, SELECTOR(underBits));
if (_screen->_picNotValid != 1) {
_paint16->bitsRestore(bitsHandle);
it->showBitsFlag = true;
} else {
_paint16->bitsFree(bitsHandle);
}
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(underBits), 0);
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(underBits), 0);
}
- signal &= ~kSignalForceUpdate;
- if (signal & kSignalViewUpdated)
- signal &= ~(kSignalViewUpdated | kSignalNoUpdate);
- } else if (signal & kSignalStopUpdate) {
- signal &= ~kSignalStopUpdate;
- signal |= kSignalNoUpdate;
+ it->signal &= ~kSignalForceUpdate;
+ if (it->signal & kSignalViewUpdated)
+ it->signal &= ~(kSignalViewUpdated | kSignalNoUpdate);
+ } else if (it->signal & kSignalStopUpdate) {
+ it->signal &= ~kSignalStopUpdate;
+ it->signal |= kSignalNoUpdate;
}
- it->signal = signal;
}
// Draw always-update cels
for (it = _list.begin(); it != end; ++it) {
- curObject = it->object;
- signal = it->signal;
-
- if (signal & kSignalAlwaysUpdate) {
+ if (it->signal & kSignalAlwaysUpdate) {
// draw corresponding cel
_paint16->drawCel(it->viewId, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY);
it->showBitsFlag = true;
- signal &= ~(kSignalStopUpdate | kSignalViewUpdated | kSignalNoUpdate | kSignalForceUpdate);
- if ((signal & kSignalIgnoreActor) == 0) {
+ it->signal &= ~(kSignalStopUpdate | kSignalViewUpdated | kSignalNoUpdate | kSignalForceUpdate);
+ if (!(it->signal & kSignalIgnoreActor)) {
rect = it->celRect;
rect.top = CLIP<int16>(_ports->kernelPriorityToCoordinate(it->priority) - 1, rect.top, rect.bottom - 1);
_paint16->fillRect(rect, GFX_SCREEN_MASK_CONTROL, 0, 0, 15);
}
- it->signal = signal;
}
}
// Saving background for all NoUpdate-cels
for (it = _list.begin(); it != end; ++it) {
- curObject = it->object;
- signal = it->signal;
-
- if (signal & kSignalNoUpdate) {
- if (signal & kSignalHidden) {
- signal |= kSignalRemoveView;
+ if (it->signal & kSignalNoUpdate) {
+ if (it->signal & kSignalHidden) {
+ it->signal |= kSignalRemoveView;
} else {
- signal &= ~kSignalRemoveView;
- if (signal & kSignalIgnoreActor)
+ it->signal &= ~kSignalRemoveView;
+ if (it->signal & kSignalIgnoreActor)
bitsHandle = _paint16->bitsSave(it->celRect, GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY);
else
bitsHandle = _paint16->bitsSave(it->celRect, GFX_SCREEN_MASK_ALL);
- writeSelector(_s->_segMan, curObject, SELECTOR(underBits), bitsHandle);
+ writeSelector(_s->_segMan, it->object, SELECTOR(underBits), bitsHandle);
}
- it->signal = signal;
}
}
// Draw NoUpdate cels
for (it = _list.begin(); it != end; ++it) {
- curObject = it->object;
- signal = it->signal;
-
- if (signal & kSignalNoUpdate && !(signal & kSignalHidden)) {
+ if (it->signal & kSignalNoUpdate && !(it->signal & kSignalHidden)) {
// draw corresponding cel
_paint16->drawCel(it->viewId, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY);
it->showBitsFlag = true;
- if (!(signal & kSignalIgnoreActor)) {
+ if (!(it->signal & kSignalIgnoreActor)) {
rect = it->celRect;
rect.top = CLIP<int16>(_ports->kernelPriorityToCoordinate(it->priority) - 1, rect.top, rect.bottom - 1);
_paint16->fillRect(rect, GFX_SCREEN_MASK_CONTROL, 0, 0, 15);
@@ -420,30 +405,23 @@ void GfxAnimate::update() {
}
void GfxAnimate::drawCels() {
- reg_t curObject;
- uint16 signal;
reg_t bitsHandle;
AnimateList::iterator it;
const AnimateList::iterator end = _list.end();
_lastCastData.clear();
for (it = _list.begin(); it != end; ++it) {
- curObject = it->object;
- signal = it->signal;
-
- if (!(signal & (kSignalNoUpdate | kSignalHidden | kSignalAlwaysUpdate))) {
+ if (!(it->signal & (kSignalNoUpdate | kSignalHidden | kSignalAlwaysUpdate))) {
// Save background
bitsHandle = _paint16->bitsSave(it->celRect, GFX_SCREEN_MASK_ALL);
- writeSelector(_s->_segMan, curObject, SELECTOR(underBits), bitsHandle);
+ writeSelector(_s->_segMan, it->object, SELECTOR(underBits), bitsHandle);
// draw corresponding cel
_paint16->drawCel(it->viewId, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY);
it->showBitsFlag = true;
- if (signal & kSignalRemoveView) {
- signal &= ~kSignalRemoveView;
- }
- it->signal = signal;
+ if (it->signal & kSignalRemoveView)
+ it->signal &= ~kSignalRemoveView;
// Remember that entry in lastCast
_lastCastData.push_back(*it);
@@ -452,23 +430,18 @@ void GfxAnimate::drawCels() {
}
void GfxAnimate::updateScreen(byte oldPicNotValid) {
- reg_t curObject;
- uint16 signal;
AnimateList::iterator it;
const AnimateList::iterator end = _list.end();
Common::Rect lsRect;
Common::Rect workerRect;
for (it = _list.begin(); it != end; ++it) {
- curObject = it->object;
- signal = it->signal;
-
- if (it->showBitsFlag || !(signal & (kSignalRemoveView | kSignalNoUpdate) ||
- (!(signal & kSignalRemoveView) && (signal & kSignalNoUpdate) && oldPicNotValid))) {
- lsRect.left = readSelectorValue(_s->_segMan, curObject, SELECTOR(lsLeft));
- lsRect.top = readSelectorValue(_s->_segMan, curObject, SELECTOR(lsTop));
- lsRect.right = readSelectorValue(_s->_segMan, curObject, SELECTOR(lsRight));
- lsRect.bottom = readSelectorValue(_s->_segMan, curObject, SELECTOR(lsBottom));
+ if (it->showBitsFlag || !(it->signal & (kSignalRemoveView | kSignalNoUpdate) ||
+ (!(it->signal & kSignalRemoveView) && (it->signal & kSignalNoUpdate) && oldPicNotValid))) {
+ lsRect.left = readSelectorValue(_s->_segMan, it->object, SELECTOR(lsLeft));
+ lsRect.top = readSelectorValue(_s->_segMan, it->object, SELECTOR(lsTop));
+ lsRect.right = readSelectorValue(_s->_segMan, it->object, SELECTOR(lsRight));
+ lsRect.bottom = readSelectorValue(_s->_segMan, it->object, SELECTOR(lsBottom));
workerRect = lsRect;
workerRect.clip(it->celRect);
@@ -480,17 +453,16 @@ void GfxAnimate::updateScreen(byte oldPicNotValid) {
_paint16->bitsShow(lsRect);
workerRect = it->celRect;
}
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsLeft), it->celRect.left);
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsTop), it->celRect.top);
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsRight), it->celRect.right);
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsBottom), it->celRect.bottom);
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(lsLeft), it->celRect.left);
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(lsTop), it->celRect.top);
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(lsRight), it->celRect.right);
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(lsBottom), it->celRect.bottom);
// may get used for debugging
//_paint16->frameRect(workerRect);
_paint16->bitsShow(workerRect);
- if (signal & kSignalHidden) {
+ if (it->signal & kSignalHidden)
it->signal |= kSignalRemoveView;
- }
}
}
// use this for debug purposes
@@ -498,37 +470,30 @@ void GfxAnimate::updateScreen(byte oldPicNotValid) {
}
void GfxAnimate::restoreAndDelete(int argc, reg_t *argv) {
- reg_t curObject;
- uint16 signal;
AnimateList::iterator it;
const AnimateList::iterator end = _list.end();
-
// This has to be done in a separate loop. At least in sq1 some .dispose
// modifies FIXEDLOOP flag in signal for another object. In that case we
// would overwrite the new signal with our version of the old signal.
for (it = _list.begin(); it != end; ++it) {
- curObject = it->object;
- signal = it->signal;
-
// Finally update signal
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(signal), signal);
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(signal), it->signal);
}
for (it = _list.reverse_begin(); it != end; --it) {
- curObject = it->object;
// We read out signal here again, this is not by accident but to ensure
// that we got an up-to-date signal
- signal = readSelectorValue(_s->_segMan, curObject, SELECTOR(signal));
+ it->signal = readSelectorValue(_s->_segMan, it->object, SELECTOR(signal));
- if ((signal & (kSignalNoUpdate | kSignalRemoveView)) == 0) {
- _paint16->bitsRestore(readSelector(_s->_segMan, curObject, SELECTOR(underBits)));
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(underBits), 0);
+ if ((it->signal & (kSignalNoUpdate | kSignalRemoveView)) == 0) {
+ _paint16->bitsRestore(readSelector(_s->_segMan, it->object, SELECTOR(underBits)));
+ writeSelectorValue(_s->_segMan, it->object, SELECTOR(underBits), 0);
}
- if (signal & kSignalDisposeMe) {
+ if (it->signal & kSignalDisposeMe) {
// Call .delete_ method of that object
- invokeSelector(_s, curObject, SELECTOR(delete_), argc, argv, 0);
+ invokeSelector(_s, it->object, SELECTOR(delete_), argc, argv, 0);
}
}
}
@@ -591,7 +556,7 @@ void GfxAnimate::addToPicDrawCels() {
// draw corresponding cel
_paint16->drawCel(view, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY);
- if ((it->signal & kSignalIgnoreActor) == 0) {
+ if (!(it->signal & kSignalIgnoreActor)) {
it->celRect.top = CLIP<int16>(_ports->kernelPriorityToCoordinate(it->priority) - 1, it->celRect.top, it->celRect.bottom - 1);
_paint16->fillRect(it->celRect, GFX_SCREEN_MASK_CONTROL, 0, 0, 15);
}
@@ -665,10 +630,10 @@ void GfxAnimate::kernelAnimate(reg_t listReference, bool cycle, int argc, reg_t
// beginUpdate()/endUpdate() were introduced SCI1.
// Calling those for SCI0 will work most of the time but breaks minor
// stuff like percentage bar of qfg1ega at the character skill screen.
- if (getSciVersion() >= SCI_VERSION_1_EGA)
+ if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY)
_ports->beginUpdate(_ports->_picWind);
update();
- if (getSciVersion() >= SCI_VERSION_1_EGA)
+ if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY)
_ports->endUpdate(_ports->_picWind);
}
@@ -687,6 +652,10 @@ void GfxAnimate::kernelAnimate(reg_t listReference, bool cycle, int argc, reg_t
_ports->setPort(oldPort);
// Now trigger speed throttler
+ throttleSpeed();
+}
+
+void GfxAnimate::throttleSpeed() {
switch (_lastCastData.size()) {
case 0:
// No entries drawn -> no speed throttler triggering
diff --git a/engines/sci/graphics/animate.h b/engines/sci/graphics/animate.h
index 44ffdd53af..87072b7a8d 100644
--- a/engines/sci/graphics/animate.h
+++ b/engines/sci/graphics/animate.h
@@ -115,6 +115,10 @@ private:
void addToPicSetPicNotValid();
void animateShowPic();
+ void throttleSpeed();
+ void adjustInvalidCels(GfxView *view, AnimateList::iterator it);
+ void processViewScaling(GfxView *view, AnimateList::iterator it);
+ void setNsRect(GfxView *view, AnimateList::iterator it);
EngineState *_s;
GfxCache *_cache;
diff --git a/engines/sci/graphics/compare.cpp b/engines/sci/graphics/compare.cpp
index 5f6aef0153..875b328d4f 100644
--- a/engines/sci/graphics/compare.cpp
+++ b/engines/sci/graphics/compare.cpp
@@ -81,23 +81,14 @@ reg_t GfxCompare::canBeHereCheckRectList(reg_t checkObject, const Common::Rect &
curObject = curNode->value;
if (curObject != checkObject) {
signal = readSelectorValue(_segMan, curObject, SELECTOR(signal));
- if ((signal & (kSignalIgnoreActor | kSignalRemoveView | kSignalNoUpdate)) == 0) {
+ if (!(signal & (kSignalIgnoreActor | kSignalRemoveView | kSignalNoUpdate))) {
curRect.left = readSelectorValue(_segMan, curObject, SELECTOR(brLeft));
curRect.top = readSelectorValue(_segMan, curObject, SELECTOR(brTop));
curRect.right = readSelectorValue(_segMan, curObject, SELECTOR(brRight));
curRect.bottom = readSelectorValue(_segMan, curObject, SELECTOR(brBottom));
// Check if curRect is within checkRect
- // TODO: This check is slightly odd, because it means that a rect is not contained
- // in itself. It may very well be that the original SCI engine did it just
- // this way, so it should not be changed lightly. However, somebody should
- // confirm whether the original engine really did it this way. Then, update
- // this comment accordingly, and, if necessary, fix the code.
- if (curRect.right > checkRect.left &&
- curRect.left < checkRect.right &&
- curRect.bottom > checkRect.top &&
- curRect.top < checkRect.bottom) {
+ if (checkRect.contains(curRect))
return curObject;
- }
}
}
curAddress = curNode->succ;
diff --git a/engines/sci/graphics/cursor.cpp b/engines/sci/graphics/cursor.cpp
index 567e02eeb9..9d1c3dabd6 100644
--- a/engines/sci/graphics/cursor.cpp
+++ b/engines/sci/graphics/cursor.cpp
@@ -435,9 +435,15 @@ void GfxCursor::kernelSetMacCursor(GuiResourceId viewNum, int loopNum, int celNu
if (_macCursorRemap.empty()) {
// QFG1/Freddy/Hoyle4 use a straight viewNum->cursor ID mapping
- // KQ6 seems to use this mapping for its cursors
- if (g_sci->getGameId() == GID_KQ6)
- viewNum = loopNum * 1000 + celNum;
+ // KQ6 uses this mapping for its cursors
+ if (g_sci->getGameId() == GID_KQ6) {
+ if (viewNum == 990) // Inventory Cursors
+ viewNum = loopNum * 16 + celNum + 2000;
+ else if (viewNum == 998) // Regular Cursors
+ viewNum = celNum + 1000;
+ else // Unknown cursor, ignored
+ return;
+ }
} else {
// If we do have the list, we'll be using a remap based on what the
// scripts have given us.
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index ab4a2c9c1a..fbfd140e6b 100644
--- a/engines/sci/graphics/frameout.cpp
+++ b/engines/sci/graphics/frameout.cpp
@@ -337,6 +337,8 @@ static int16 GetLongest(const char *text, int16 maxWidth, GfxFont *font) {
maxChars = curCharCount; // return count up to (but not including) breaking space
break;
}
+ if (width + font->getCharWidth(curChar) > maxWidth)
+ break;
width += font->getCharWidth(curChar);
curCharCount++;
}
@@ -592,9 +594,9 @@ void GfxFrameout::kernelFrameout() {
const char *txt = text.c_str();
// HACK. The plane sometimes doesn't contain the correct width. This
// hack breaks the dialog options when speaking with Grace, but it's
- // the best we got up to now.
- // TODO: Remove this, and figure out why the plane in question isn't
- // initialized correctly (its width is 0).
+ // the best we got up to now. This happens because of the unimplemented
+ // kTextWidth function in SCI32.
+ // TODO: Remove this once kTextWidth has been implemented.
uint16 w = it->planeRect.width() >= 20 ? it->planeRect.width() : _screen->getWidth() - 10;
int16 charCount;
diff --git a/engines/sci/graphics/helpers.h b/engines/sci/graphics/helpers.h
index f6cb214a2b..343f3c7e6e 100644
--- a/engines/sci/graphics/helpers.h
+++ b/engines/sci/graphics/helpers.h
@@ -45,6 +45,10 @@ typedef int GuiResourceId; // is a resource-number and -1 means no parameter giv
typedef int16 TextAlignment;
+#define PORTS_FIRSTWINDOWID 2
+#define PORTS_FIRSTSCRIPTWINDOWID 3
+
+
struct Port {
uint16 id;
int16 top, left;
@@ -62,6 +66,8 @@ struct Port {
fontHeight(0), fontId(0), greyedOutput(false),
penClr(0), backClr(0xFF), penMode(0), counterTillFree(0) {
}
+
+ bool isWindow() const { return id >= PORTS_FIRSTWINDOWID && id != 0xFFFF; }
};
struct Window : public Port, public Common::Serializable {
@@ -132,12 +138,14 @@ struct PalSchedule {
uint32 schedule;
};
+// Game view types, sorted by the number of colors
enum ViewType {
- kViewUnknown,
- kViewEga,
- kViewVga,
- kViewVga11,
- kViewAmiga
+ kViewUnknown, // uninitialized, or non-SCI
+ kViewEga, // EGA SCI0/SCI1 and Amiga SCI0/SCI1 ECS 16 colors
+ kViewAmiga, // Amiga SCI1 ECS 32 colors
+ kViewAmiga64, // Amiga SCI1 AGA 64 colors (i.e. Longbow)
+ kViewVga, // VGA SCI1 256 colors
+ kViewVga11 // VGA SCI1.1 and newer 256 colors
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/maciconbar.cpp b/engines/sci/graphics/maciconbar.cpp
index ea44db654b..f0931e0121 100644
--- a/engines/sci/graphics/maciconbar.cpp
+++ b/engines/sci/graphics/maciconbar.cpp
@@ -110,10 +110,12 @@ void GfxMacIconBar::drawDisabledImage(Graphics::Surface *surface, const Common::
newSurf.copyFrom(*surface);
for (int i = 0; i < newSurf.h; i++) {
- int startX = rect.left & 3;
+ // Start at the next four byte boundary
+ int startX = 3 - ((rect.left + 3) & 3);
+ // Start odd rows at two bytes past that (also properly aligned)
if ((i + rect.top) & 1)
- startX += 2;
+ startX = (startX + 2) & 3;
for (int j = startX; j < newSurf.w; j += 4)
*((byte *)newSurf.getBasePtr(j, i)) = 0;
diff --git a/engines/sci/graphics/paint.cpp b/engines/sci/graphics/paint.cpp
index 50b0534ba7..c347da3c0f 100644
--- a/engines/sci/graphics/paint.cpp
+++ b/engines/sci/graphics/paint.cpp
@@ -43,9 +43,6 @@ GfxPaint::~GfxPaint() {
void GfxPaint::kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, bool animationBlackoutFlag, bool mirroredFlag, bool addToFlag, int16 EGApaletteNo) {
}
-void GfxPaint::kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, bool hiresMode, reg_t upscaledHiresHandle) {
-}
-
void GfxPaint::kernelGraphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control) {
}
diff --git a/engines/sci/graphics/paint.h b/engines/sci/graphics/paint.h
index 994bc4e5e9..a79e8993c2 100644
--- a/engines/sci/graphics/paint.h
+++ b/engines/sci/graphics/paint.h
@@ -36,7 +36,6 @@ public:
virtual ~GfxPaint();
virtual void kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, bool animationBlackoutFlag, bool mirroredFlag, bool addToFlag, int16 EGApaletteNo);
- virtual void kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, bool hiresMode, reg_t upscaledHiresHandle);
virtual void kernelGraphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control);
};
diff --git a/engines/sci/graphics/paint16.cpp b/engines/sci/graphics/paint16.cpp
index 935dd4e62e..33986f1196 100644
--- a/engines/sci/graphics/paint16.cpp
+++ b/engines/sci/graphics/paint16.cpp
@@ -360,7 +360,7 @@ void GfxPaint16::bitsRestore(reg_t memoryHandle) {
if (memoryPtr) {
_screen->bitsRestore(memoryPtr);
- _segMan->freeHunkEntry(memoryHandle);
+ bitsFree(memoryHandle);
}
}
}
@@ -532,20 +532,7 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) {
case SCI_DISPLAY_RESTOREUNDER:
bitsGetRect(argv[0], &rect);
rect.translate(-_ports->getPort()->left, -_ports->getPort()->top);
- if (g_sci->getGameId() == GID_PQ3 && g_sci->getEngineState()->currentRoomNumber() == 29) {
- // WORKAROUND: PQ3 calls this without calling the associated
- // kDisplay(SCI_DISPLAY_SAVEUNDER) call before. Theoretically,
- // this would result in no rect getting restored. However, we
- // still maintain a pointer from the previous room, resulting
- // in invalidated content being restored on screen, and causing
- // graphics glitches. Thus, we simply don't restore a rect in
- // that room. The correct fix for this would be to erase hunk
- // pointers when changing rooms, but this will suffice for now,
- // as restoring from a totally invalid pointer is very rare.
- // Fixes bug #3037945.
- } else {
- bitsRestore(argv[0]);
- }
+ bitsRestore(argv[0]);
kernelGraphRedrawBox(rect);
// finishing loop
argc = 0;
@@ -581,7 +568,10 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) {
// now drawing the text
_text16->Size(rect, text, -1, width);
rect.moveTo(_ports->getPort()->curLeft, _ports->getPort()->curTop);
- if (getSciVersion() >= SCI_VERSION_1_LATE) {
+ // Note: This code has been found in SCI1 middle and newer games. It was
+ // previously only for SCI1 late and newer, but the LSL1 interpreter contains
+ // this code.
+ if (getSciVersion() >= SCI_VERSION_1_MIDDLE) {
int16 leftPos = rect.right <= _screen->getWidth() ? 0 : _screen->getWidth() - rect.right;
int16 topPos = rect.bottom <= _screen->getHeight() ? 0 : _screen->getHeight() - rect.bottom;
_ports->move(leftPos, topPos);
diff --git a/engines/sci/graphics/paint32.cpp b/engines/sci/graphics/paint32.cpp
index aa3bf8dfb3..69a278eb8b 100644
--- a/engines/sci/graphics/paint32.cpp
+++ b/engines/sci/graphics/paint32.cpp
@@ -65,17 +65,6 @@ void GfxPaint32::kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, b
delete picture;
}
-// This is "hacked" together, because its only used by debug command
-void GfxPaint32::kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, bool hiresMode, reg_t upscaledHiresHandle) {
- GfxView *view = _cache->getView(viewId);
- Common::Rect celRect(50, 50, 50, 50);
- Common::Rect translatedRect;
- celRect.bottom += view->getHeight(loopNo, celNo);
- celRect.right += view->getWidth(loopNo, celNo);
- view->draw(celRect, celRect, celRect, loopNo, celNo, 255, 0, false);
- _screen->copyRectToScreen(celRect);
-}
-
void GfxPaint32::kernelGraphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control) {
_screen->drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y, color, priority, control);
}
diff --git a/engines/sci/graphics/paint32.h b/engines/sci/graphics/paint32.h
index 3fa9d11d9b..e412bdf1c4 100644
--- a/engines/sci/graphics/paint32.h
+++ b/engines/sci/graphics/paint32.h
@@ -45,7 +45,6 @@ public:
void fillRect(Common::Rect rect, byte color);
void kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, bool animationBlackoutFlag, bool mirroredFlag, bool addToFlag, int16 EGApaletteNo);
- void kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, bool hiresMode, reg_t upscaledHiresHandle);
void kernelGraphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control);
private:
diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp
index 534890315c..5a6b1859cd 100644
--- a/engines/sci/graphics/palette.cpp
+++ b/engines/sci/graphics/palette.cpp
@@ -76,6 +76,24 @@ GfxPalette::GfxPalette(ResourceManager *resMan, GfxScreen *screen, bool useMergi
#ifdef ENABLE_SCI32
_clutTable = 0;
#endif
+
+ switch (_resMan->getViewType()) {
+ case kViewEga:
+ _totalScreenColors = 16;
+ break;
+ case kViewAmiga:
+ _totalScreenColors = 32;
+ break;
+ case kViewAmiga64:
+ _totalScreenColors = 64;
+ break;
+ case kViewVga:
+ case kViewVga11:
+ _totalScreenColors = 256;
+ break;
+ default:
+ error("GfxPalette: Unknown view type");
+ }
}
GfxPalette::~GfxPalette() {
@@ -97,7 +115,7 @@ bool GfxPalette::isMerging() {
void GfxPalette::setDefault() {
if (_resMan->getViewType() == kViewEga)
setEGA();
- else if (_resMan->isAmiga32color())
+ else if (_resMan->getViewType() == kViewAmiga)
setAmiga();
else
kernelSetFromResource(999, true);
@@ -418,10 +436,6 @@ void GfxPalette::getSys(Palette *pal) {
}
void GfxPalette::setOnScreen() {
- // We dont change palette at all times for amiga
- if (_resMan->isAmiga32color())
- return;
-
copySysPaletteToScreen();
}
@@ -847,24 +861,28 @@ void GfxPalette::palVaryProcess(int signal, bool setPalette) {
}
}
+static inline uint getMacColorDiff(byte r1, byte g1, byte b1, byte r2, byte g2, byte b2) {
+ // Use the difference of the top 4 bits and add together the differences
+ return ABS((r2 & 0xf0) - (r1 & 0xf0)) + ABS((g2 & 0xf0) - (g1 & 0xf0)) + ABS((b2 & 0xf0) - (b1 & 0xf0));
+}
+
byte GfxPalette::findMacIconBarColor(byte r, byte g, byte b) {
// Find the best color for use with the Mac icon bar
+ // Check white, then the palette colors, and then black
- // For black, always use 0
- if (r == 0 && g == 0 && b == 0)
- return 0;
+ // Try white first
+ byte found = 0xff;
+ uint diff = getMacColorDiff(r, g, b, 0xff, 0xff, 0xff);
- byte found = 0xFF;
- uint diff = 0xFFFFFFFF;
+ if (diff == 0)
+ return found;
+ // Go through the main colors of the CLUT
for (uint16 i = 1; i < 255; i++) {
- int dr = _macClut[i * 3 ] - r;
- int dg = _macClut[i * 3 + 1] - g;
- int db = _macClut[i * 3 + 2] - b;
+ if (!colorIsFromMacClut(i))
+ continue;
- // Use the largest difference. This is what the Mac Palette Manager does.
- uint cdiff = MAX<int>(ABS(dr), ABS(dg));
- cdiff = MAX<int>(cdiff, ABS(db));
+ uint cdiff = getMacColorDiff(r, g, b, _macClut[i * 3], _macClut[i * 3 + 1], _macClut[i * 3 + 2]);
if (cdiff == 0)
return i;
@@ -874,6 +892,10 @@ byte GfxPalette::findMacIconBarColor(byte r, byte g, byte b) {
}
}
+ // Also check black here
+ if (getMacColorDiff(r, g, b, 0, 0, 0) < diff)
+ return 0;
+
return found;
}
@@ -889,7 +911,9 @@ void GfxPalette::loadMacIconBarPalette() {
clutStream->readUint32BE(); // seed
clutStream->readUint16BE(); // flags
uint16 colorCount = clutStream->readUint16BE() + 1;
- _macClut = new byte[colorCount * 3];
+ assert(colorCount == 256);
+
+ _macClut = new byte[256 * 3];
for (uint16 i = 0; i < colorCount; i++) {
clutStream->readUint16BE();
@@ -898,6 +922,19 @@ void GfxPalette::loadMacIconBarPalette() {
_macClut[i * 3 + 2] = clutStream->readUint16BE() >> 8;
}
+ // Adjust bounds on the KQ6 palette
+ // We don't use all of it for the icon bar
+ if (g_sci->getGameId() == GID_KQ6)
+ memset(_macClut + 32 * 3, 0, (256 - 32) * 3);
+
+ // Force black/white
+ _macClut[0x00 * 3 ] = 0;
+ _macClut[0x00 * 3 + 1] = 0;
+ _macClut[0x00 * 3 + 2] = 0;
+ _macClut[0xff * 3 ] = 0xff;
+ _macClut[0xff * 3 + 1] = 0xff;
+ _macClut[0xff * 3 + 2] = 0xff;
+
delete clutStream;
}
diff --git a/engines/sci/graphics/palette.h b/engines/sci/graphics/palette.h
index 317401ac1f..d2e5151d6a 100644
--- a/engines/sci/graphics/palette.h
+++ b/engines/sci/graphics/palette.h
@@ -54,6 +54,7 @@ public:
bool merge(Palette *pFrom, bool force, bool forceRealMerge);
uint16 matchColor(byte r, byte g, byte b);
void getSys(Palette *pal);
+ uint16 getTotalColorCount() const { return _totalScreenColors; }
void setOnScreen();
void copySysPaletteToScreen();
@@ -123,6 +124,7 @@ private:
uint16 _palVaryTicks;
int _palVaryPaused;
int _palVarySignal;
+ uint16 _totalScreenColors;
void loadMacIconBarPalette();
byte *_macClut;
diff --git a/engines/sci/graphics/picture.cpp b/engines/sci/graphics/picture.cpp
index 7e71c1a258..e60a62e423 100644
--- a/engines/sci/graphics/picture.cpp
+++ b/engines/sci/graphics/picture.cpp
@@ -36,6 +36,8 @@
namespace Sci {
+//#define DEBUG_PICTURE_DRAW
+
GfxPicture::GfxPicture(ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxPorts *ports, GfxScreen *screen, GfxPalette *palette, GuiResourceId resourceId, bool EGAdrawingVisualize)
: _resMan(resMan), _coordAdjuster(coordAdjuster), _ports(ports), _screen(screen), _palette(palette), _resourceId(resourceId), _EGAdrawingVisualize(EGAdrawingVisualize) {
assert(resourceId != -1);
@@ -222,19 +224,20 @@ void GfxPicture::drawSci32Vga(int16 celNo, int16 drawX, int16 drawY, int16 pictu
}
#endif
+extern void unpackCelData(byte *inBuffer, byte *celBitmap, byte clearColor, int pixelCount, int rlePos, int literalPos, ViewType viewType, uint16 width, bool isMacSci11ViewData);
+
void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 drawX, int16 drawY, int16 pictureX) {
byte *celBitmap = NULL;
byte *ptr = NULL;
byte *headerPtr = inbuffer + headerPos;
byte *rlePtr = inbuffer + rlePos;
- byte *literalPtr = inbuffer + literalPos;
int16 displaceX, displaceY;
byte priority = _addToFlag ? _priority : 0;
byte clearColor;
bool compression = true;
- byte curByte, runLength;
+ byte curByte;
int16 y, lastY, x, leftX, rightX;
- int pixelNr, pixelCount;
+ int pixelCount;
uint16 width, height;
#ifdef ENABLE_SCI32
@@ -245,12 +248,11 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos
height = READ_LE_UINT16(headerPtr + 2);
displaceX = (signed char)headerPtr[4];
displaceY = (unsigned char)headerPtr[5];
- if (_resourceType == SCI_PICTURE_TYPE_SCI11) {
+ if (_resourceType == SCI_PICTURE_TYPE_SCI11)
// SCI1.1 uses hardcoded clearcolor for pictures, even if cel header specifies otherwise
clearColor = _screen->getColorWhite();
- } else {
+ else
clearColor = headerPtr[6];
- }
#ifdef ENABLE_SCI32
} else {
width = READ_SCI11ENDIAN_UINT16(headerPtr + 0);
@@ -266,91 +268,18 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos
if (displaceX || displaceY)
error("unsupported embedded cel-data in picture");
+ // We will unpack cel-data into a temporary buffer and then plot it to screen
+ // That needs to be done cause a mirrored picture may be requested
pixelCount = width * height;
celBitmap = new byte[pixelCount];
if (!celBitmap)
error("Unable to allocate temporary memory for picture drawing");
- if (compression) {
- // We will unpack cel-data into a temporary buffer and then plot it to screen
- // That needs to be done cause a mirrored picture may be requested
- memset(celBitmap, clearColor, pixelCount);
- pixelNr = 0;
- ptr = celBitmap;
- if (literalPos == 0) {
- // decompression for data that has only one stream (vecor embedded view data)
- switch (_resMan->getViewType()) {
- case kViewEga:
- while (pixelNr < pixelCount) {
- curByte = *rlePtr++;
- runLength = curByte >> 4;
- memset(ptr + pixelNr, curByte & 0x0F, MIN<uint16>(runLength, pixelCount - pixelNr));
- pixelNr += runLength;
- }
- break;
- case kViewVga:
- case kViewVga11:
- while (pixelNr < pixelCount) {
- curByte = *rlePtr++;
- runLength = curByte & 0x3F;
- switch (curByte & 0xC0) {
- case 0: // copy bytes as-is
- while (runLength-- && pixelNr < pixelCount)
- ptr[pixelNr++] = *rlePtr++;
- break;
- case 0x80: // fill with color
- memset(ptr + pixelNr, *rlePtr++, MIN<uint16>(runLength, pixelCount - pixelNr));
- pixelNr += runLength;
- break;
- case 0xC0: // fill with transparent
- pixelNr += runLength;
- break;
- }
- }
- break;
- case kViewAmiga:
- while (pixelNr < pixelCount) {
- curByte = *rlePtr++;
- if (curByte & 0x07) { // fill with color
- runLength = curByte & 0x07;
- curByte = curByte >> 3;
- while (runLength-- && pixelNr < pixelCount) {
- ptr[pixelNr++] = curByte;
- }
- } else { // fill with transparent
- runLength = curByte >> 3;
- pixelNr += runLength;
- }
- }
- break;
-
- default:
- error("Unsupported picture viewtype");
- }
- } else {
- // decompression for data that has two separate streams (probably SCI 1.1 picture)
- while (pixelNr < pixelCount) {
- curByte = *rlePtr++;
- runLength = curByte & 0x3F;
- switch (curByte & 0xC0) {
- case 0: // copy bytes as-is
- while (runLength-- && pixelNr < pixelCount)
- ptr[pixelNr++] = *literalPtr++;
- break;
- case 0x80: // fill with color
- memset(ptr + pixelNr, *literalPtr++, MIN<uint16>(runLength, pixelCount - pixelNr));
- pixelNr += runLength;
- break;
- case 0xC0: // fill with transparent
- pixelNr += runLength;
- break;
- }
- }
- }
- } else {
+ if (compression)
+ unpackCelData(inbuffer, celBitmap, clearColor, pixelCount, rlePos, literalPos, _resMan->getViewType(), width, false);
+ else
// No compression (some SCI32 pictures)
memcpy(celBitmap, rlePtr, pixelCount);
- }
Common::Rect displayArea = _coordAdjuster->pictureGetDisplayArea();
@@ -443,6 +372,7 @@ enum {
PIC_OP_OPX = 0xfe,
PIC_OP_TERMINATE = 0xff
};
+
#define PIC_OP_FIRST PIC_OP_SET_COLOR
enum {
@@ -465,6 +395,47 @@ enum {
PIC_OPX_VGA_PRIORITY_TABLE_EXPLICIT = 4
};
+#ifdef DEBUG_PICTURE_DRAW
+const char *picOpcodeNames[] = {
+ "Set color",
+ "Disable visual",
+ "Set priority",
+ "Disable priority",
+ "Short patterns",
+ "Medium lines",
+ "Long lines",
+ "Short lines",
+ "Fill",
+ "Set pattern",
+ "Absolute pattern",
+ "Set control",
+ "Disable control",
+ "Medium patterns",
+ "Extended opcode",
+ "Terminate"
+};
+
+const char *picExOpcodeNamesEGA[] = {
+ "Set palette entries",
+ "Set palette",
+ "Mono0",
+ "Mono1",
+ "Mono2",
+ "Mono3",
+ "Mono4",
+ "Embedded view",
+ "Set priority table"
+};
+
+const char *picExOpcodeNamesVGA[] = {
+ "Set palette entries",
+ "Embedded view",
+ "Set palette",
+ "Set priority table (eqdist)",
+ "Set priority table (explicit)"
+};
+#endif
+
#define PIC_EGAPALETTE_COUNT 4
#define PIC_EGAPALETTE_SIZE 40
#define PIC_EGAPALETTE_TOTALSIZE PIC_EGAPALETTE_COUNT*PIC_EGAPALETTE_SIZE
@@ -543,7 +514,9 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
// Drawing
while (curPos < dataSize) {
- //warning("%X at %d", data[curPos], curPos);
+#ifdef DEBUG_PICTURE_DRAW
+ debug("Picture op: %X (%s) at %d", data[curPos], picOpcodeNames[data[curPos] - 0xF0], curPos);
+#endif
switch (pic_op = data[curPos++]) {
case PIC_OP_SET_COLOR:
pic_color = data[curPos++];
@@ -681,6 +654,9 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
case PIC_OP_OPX: // Extended functions
if (isEGA) {
+#ifdef DEBUG_PICTURE_DRAW
+ debug("* Picture ex op: %X (%s) at %d", data[curPos], picExOpcodeNamesEGA[data[curPos]], curPos);
+#endif
switch (pic_op = data[curPos++]) {
case PIC_OPX_EGA_SET_PALETTE_ENTRIES:
while (vectorIsNonOpcode(data[curPos])) {
@@ -726,6 +702,9 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
error("Unsupported sci1 extended pic-operation %X", pic_op);
}
} else {
+#ifdef DEBUG_PICTURE_DRAW
+ debug("* Picture ex op: %X (%s) at %d", data[curPos], picExOpcodeNamesVGA[data[curPos]], curPos);
+#endif
switch (pic_op = data[curPos++]) {
case PIC_OPX_VGA_SET_PALETTE_ENTRIES:
while (vectorIsNonOpcode(data[curPos])) {
@@ -733,7 +712,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
}
break;
case PIC_OPX_VGA_SET_PALETTE:
- if (_resMan->isAmiga32color()) {
+ if (_resMan->getViewType() == kViewAmiga || _resMan->getViewType() == kViewAmiga64) {
if ((data[curPos] == 0x00) && (data[curPos + 1] == 0x01) && ((data[curPos + 32] & 0xF0) != 0xF0)) {
// Left-Over VGA palette, we simply ignore it
curPos += 256 + 4 + 1024;
@@ -866,6 +845,8 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by
byte matchedMask, matchMask;
int16 w, e, a_set, b_set;
+ bool isEGA = (_resMan->getViewType() == kViewEga);
+
p.x = x + curPort->left;
p.y = y + curPort->top;
stack.push(p);
@@ -874,6 +855,18 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by
byte searchPriority = _screen->getPriority(p.x, p.y);
byte searchControl = _screen->getControl(p.x, p.y);
+ if (isEGA) {
+ // In EGA games a pixel in the framebuffer is only 4 bits. We store
+ // a full byte per pixel to allow undithering, but when comparing
+ // pixels for flood-fill purposes, we should only compare the
+ // visible color of a pixel.
+
+ if ((x ^ y) & 1)
+ searchColor = (searchColor ^ (searchColor >> 4)) & 0x0F;
+ else
+ searchColor = searchColor & 0x0F;
+ }
+
// This logic was taken directly from sierra sci, floodfill will get aborted on various occations
if (screenMask & GFX_SCREEN_MASK_VISUAL) {
if ((color == _screen->getColorWhite()) || (searchColor != _screen->getColorWhite()))
@@ -913,20 +906,20 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by
int b = curPort->rect.bottom + curPort->top - 1;
while (stack.size()) {
p = stack.pop();
- if ((matchedMask = _screen->isFillMatch(p.x, p.y, matchMask, searchColor, searchPriority, searchControl)) == 0) // already filled
+ if ((matchedMask = _screen->isFillMatch(p.x, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA)) == 0) // already filled
continue;
_screen->putPixel(p.x, p.y, screenMask, color, priority, control);
w = p.x;
e = p.x;
// moving west and east pointers as long as there is a matching color to fill
- while (w > l && (matchedMask = _screen->isFillMatch(w - 1, p.y, matchMask, searchColor, searchPriority, searchControl)))
+ while (w > l && (matchedMask = _screen->isFillMatch(w - 1, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA)))
_screen->putPixel(--w, p.y, screenMask, color, priority, control);
- while (e < r && (matchedMask = _screen->isFillMatch(e + 1, p.y, matchMask, searchColor, searchPriority, searchControl)))
+ while (e < r && (matchedMask = _screen->isFillMatch(e + 1, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA)))
_screen->putPixel(++e, p.y, screenMask, color, priority, control);
// checking lines above and below for possible flood targets
a_set = b_set = 0;
while (w <= e) {
- if (p.y > t && (matchedMask = _screen->isFillMatch(w, p.y - 1, matchMask, searchColor, searchPriority, searchControl))) { // one line above
+ if (p.y > t && (matchedMask = _screen->isFillMatch(w, p.y - 1, matchMask, searchColor, searchPriority, searchControl, isEGA))) { // one line above
if (a_set == 0) {
p1.x = w;
p1.y = p.y - 1;
@@ -936,7 +929,7 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by
} else
a_set = 0;
- if (p.y < b && (matchedMask = _screen->isFillMatch(w, p.y + 1, matchMask, searchColor, searchPriority, searchControl))) { // one line below
+ if (p.y < b && (matchedMask = _screen->isFillMatch(w, p.y + 1, matchMask, searchColor, searchPriority, searchControl, isEGA))) { // one line below
if (b_set == 0) {
p1.x = w;
p1.y = p.y + 1;
diff --git a/engines/sci/graphics/ports.cpp b/engines/sci/graphics/ports.cpp
index 57c76126c0..c89f46b300 100644
--- a/engines/sci/graphics/ports.cpp
+++ b/engines/sci/graphics/ports.cpp
@@ -246,8 +246,10 @@ void GfxPorts::beginUpdate(Window *wnd) {
PortList::iterator it = _windowList.reverse_begin();
const PortList::iterator end = Common::find(_windowList.begin(), _windowList.end(), wnd);
while (it != end) {
- // FIXME: We also store Port objects in the window list.
- // We should add a check that we really only pass windows here...
+ // We also store Port objects in the window list, but they
+ // shouldn't be encountered during this iteration.
+ assert((*it)->isWindow());
+
updateWindow((Window *)*it);
--it;
}
@@ -263,11 +265,16 @@ void GfxPorts::endUpdate(Window *wnd) {
assert(it != end);
while (++it != end) {
- // FIXME: We also store Port objects in the window list.
- // We should add a check that we really only pass windows here...
+ // We also store Port objects in the window list, but they
+ // shouldn't be encountered during this iteration.
+ assert((*it)->isWindow());
+
updateWindow((Window *)*it);
}
+ if (getSciVersion() < SCI_VERSION_1_EGA_ONLY)
+ g_sci->_gfxPaint16->kernelGraphRedrawBox(_curPort->rect);
+
setPort(oldPort);
}
@@ -300,7 +307,14 @@ Window *GfxPorts::addWindow(const Common::Rect &dims, const Common::Rect *restor
}
_windowsById[id] = pwnd;
- if (style & SCI_WINDOWMGR_STYLE_TOPMOST)
+
+ // KQ1sci, KQ4, iceman, QfG2 always add windows to the back of the list.
+ // KQ5CD checks style.
+ // Hoyle3-demo also always adds to the back (#3036763).
+ bool forceToBack = (getSciVersion() <= SCI_VERSION_1_EGA_ONLY) ||
+ (g_sci->getGameId() == GID_HOYLE3 && g_sci->isDemo());
+
+ if (!forceToBack && (style & SCI_WINDOWMGR_STYLE_TOPMOST))
_windowList.push_front(pwnd);
else
_windowList.push_back(pwnd);
@@ -311,7 +325,7 @@ Window *GfxPorts::addWindow(const Common::Rect &dims, const Common::Rect *restor
// bit of the left dimension in their interpreter. It seems Sierra did it
// for EGA byte alignment (EGA uses 1 byte for 2 pixels) and left it in
// their interpreter even in the newer VGA games.
- r.left = r.left & 0x7FFE;
+ r.left = r.left & 0xFFFE;
if (r.width() > _screen->getWidth()) {
// We get invalid dimensions at least at the end of sq3 (script bug!).
diff --git a/engines/sci/graphics/ports.h b/engines/sci/graphics/ports.h
index 21c6d31ebd..9faee2be8d 100644
--- a/engines/sci/graphics/ports.h
+++ b/engines/sci/graphics/ports.h
@@ -37,9 +37,6 @@ class GfxPaint16;
class GfxScreen;
class GfxText16;
-#define PORTS_FIRSTWINDOWID 2
-#define PORTS_FIRSTSCRIPTWINDOWID 3
-
// window styles
enum {
SCI_WINDOWMGR_STYLE_TRANSPARENT = (1 << 0),
@@ -49,6 +46,9 @@ enum {
SCI_WINDOWMGR_STYLE_USER = (1 << 7)
};
+typedef Common::List<Port *> PortList;
+typedef Common::Array<Port *> PortArray;
+
/**
* Ports class, includes all port managment for SCI0->SCI1.1 games. Ports are some sort of windows in SCI
* this class also handles adjusting coordinates to a specific port
@@ -115,8 +115,12 @@ public:
virtual void saveLoadWithSerializer(Common::Serializer &ser);
+ /** The list of open 'windows' (and ports), in visual order. */
+ PortList _windowList;
+
private:
- typedef Common::List<Port *> PortList;
+ /** The list of all open 'windows' (and ports), ordered by their id. */
+ PortArray _windowsById;
SegManager *_segMan;
GfxPaint16 *_paint16;
@@ -130,12 +134,6 @@ private:
// counts windows that got disposed but are not freed yet
uint16 _freeCounter;
- /** The list of open 'windows' (and ports), in visual order. */
- PortList _windowList;
-
- /** The list of all open 'windows' (and ports), ordered by their id. */
- Common::Array<Port *> _windowsById;
-
Common::Rect _bounds;
// Priority Bands related variables
diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp
index b019853a2b..757fdc53c7 100644
--- a/engines/sci/graphics/screen.cpp
+++ b/engines/sci/graphics/screen.cpp
@@ -113,7 +113,7 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) {
_unditherState = true;
_fontIsUpscaled = false;
- if (_resMan->isVGA() || (_resMan->isAmiga32color())) {
+ if (_resMan->getViewType() != kViewEga) {
// It is not 100% accurate to set white to be 255 for Amiga 32-color
// games. But 255 is defined as white in our SCI at all times, so it
// doesn't matter.
@@ -353,12 +353,30 @@ byte GfxScreen::getControl(int x, int y) {
return _controlScreen[y * _width + x];
}
-byte GfxScreen::isFillMatch(int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con) {
+byte GfxScreen::isFillMatch(int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con, bool isEGA) {
int offset = y * _width + x;
byte match = 0;
- if ((screenMask & GFX_SCREEN_MASK_VISUAL) && *(_visualScreen + offset) == t_color)
- match |= GFX_SCREEN_MASK_VISUAL;
+ // FIXME:
+ if (screenMask & GFX_SCREEN_MASK_VISUAL) {
+ if (!isEGA) {
+ if (*(_visualScreen + offset) == t_color)
+ match |= GFX_SCREEN_MASK_VISUAL;
+ } else {
+ // In EGA games a pixel in the framebuffer is only 4 bits. We store
+ // a full byte per pixel to allow undithering, but when comparing
+ // pixels for flood-fill purposes, we should only compare the
+ // visible color of a pixel.
+
+ byte c = *(_visualScreen + offset);
+ if ((x ^ y) & 1)
+ c = (c ^ (c >> 4)) & 0x0F;
+ else
+ c = c & 0x0F;
+ if (c == t_color)
+ match |= GFX_SCREEN_MASK_VISUAL;
+ }
+ }
if ((screenMask & GFX_SCREEN_MASK_PRIORITY) && *(_priorityScreen + offset) == t_pri)
match |= GFX_SCREEN_MASK_PRIORITY;
if ((screenMask & GFX_SCREEN_MASK_CONTROL) && *(_controlScreen + offset) == t_con)
diff --git a/engines/sci/graphics/screen.h b/engines/sci/graphics/screen.h
index fc1f38ed41..6f9b08deff 100644
--- a/engines/sci/graphics/screen.h
+++ b/engines/sci/graphics/screen.h
@@ -99,7 +99,7 @@ public:
byte getVisual(int x, int y);
byte getPriority(int x, int y);
byte getControl(int x, int y);
- byte isFillMatch(int16 x, int16 y, byte drawMask, byte t_color, byte t_pri, byte t_con);
+ byte isFillMatch(int16 x, int16 y, byte drawMask, byte t_color, byte t_pri, byte t_con, bool isEGA);
int bitsGetDataSize(Common::Rect rect, byte mask);
void bitsSave(Common::Rect rect, byte mask, byte *memoryPtr);
diff --git a/engines/sci/graphics/text16.cpp b/engines/sci/graphics/text16.cpp
index 21605ccb8e..d7a92042eb 100644
--- a/engines/sci/graphics/text16.cpp
+++ b/engines/sci/graphics/text16.cpp
@@ -204,6 +204,8 @@ int16 GfxText16::GetLongest(const char *text, int16 maxWidth, GuiResourceId orgF
maxChars = curCharCount; // return count up to (but not including) breaking space
break;
}
+ if (width + _font->getCharWidth(curChar) > maxWidth)
+ break;
width += _font->getCharWidth(curChar);
curCharCount++;
}
diff --git a/engines/sci/graphics/transitions.cpp b/engines/sci/graphics/transitions.cpp
index e025a2f9ab..fac9a97efe 100644
--- a/engines/sci/graphics/transitions.cpp
+++ b/engines/sci/graphics/transitions.cpp
@@ -39,8 +39,8 @@ namespace Sci {
//#define DISABLE_TRANSITIONS // uncomment to disable room transitions (for development only! helps in testing games quickly)
-GfxTransitions::GfxTransitions(GfxScreen *screen, GfxPalette *palette, bool isVGA)
- : _screen(screen), _palette(palette), _isVGA(isVGA) {
+GfxTransitions::GfxTransitions(GfxScreen *screen, GfxPalette *palette)
+ : _screen(screen), _palette(palette) {
init();
}
@@ -124,6 +124,7 @@ void GfxTransitions::setup(int16 number, bool blackoutFlag) {
_number = SCI_TRANSITIONS_NONE;
#endif
_blackoutFlag = blackoutFlag;
+ debugC(kDebugLevelGraphics, "Transition %d, blackout %d", number, blackoutFlag);
}
}
@@ -271,7 +272,7 @@ void GfxTransitions::doTransition(int16 number, bool blackoutFlag) {
}
void GfxTransitions::setNewPalette(bool blackoutFlag) {
- if (!blackoutFlag && _isVGA)
+ if (!blackoutFlag)
_palette->setOnScreen();
}
diff --git a/engines/sci/graphics/transitions.h b/engines/sci/graphics/transitions.h
index 674b7a8173..a8f0ca6f07 100644
--- a/engines/sci/graphics/transitions.h
+++ b/engines/sci/graphics/transitions.h
@@ -65,7 +65,7 @@ class Screen;
*/
class GfxTransitions {
public:
- GfxTransitions(GfxScreen *screen, GfxPalette *palette, bool isVGA);
+ GfxTransitions(GfxScreen *screen, GfxPalette *palette);
~GfxTransitions();
void setup(int16 number, bool blackoutFlag);
@@ -97,7 +97,6 @@ private:
GfxScreen *_screen;
GfxPalette *_palette;
- bool _isVGA;
const GfxTransitionTranslateEntry *_translationTable;
int16 _number;
bool _blackoutFlag;
diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp
index 3c1f417740..fd74714495 100644
--- a/engines/sci/graphics/view.cpp
+++ b/engines/sci/graphics/view.cpp
@@ -104,9 +104,10 @@ void GfxView::initData(GuiResourceId resourceId) {
}
switch (curViewType) {
- case kViewEga: // View-format SCI0 (and Amiga 16 colors)
+ case kViewEga: // SCI0 (and Amiga 16 colors)
isEGA = true;
- case kViewAmiga: // View-format Amiga (32 colors)
+ case kViewAmiga: // Amiga ECS (32 colors)
+ case kViewAmiga64: // Amiga AGA (64 colors)
case kViewVga: // View-format SCI1
// LoopCount:WORD MirrorMask:WORD Version:WORD PaletteOffset:WORD LoopOffset0:WORD LoopOffset1:WORD...
@@ -132,7 +133,7 @@ void GfxView::initData(GuiResourceId resourceId) {
// SCI1 VGA conversion games (which will get detected as SCI1EARLY/MIDDLE/LATE) have some views
// with broken mapping tables. I guess those games won't use the mapping, so I rather disable it
// for them
- if (getSciVersion() == SCI_VERSION_1_EGA) {
+ if (getSciVersion() == SCI_VERSION_1_EGA_ONLY) {
_EGAmapping = &_resourceData[palOffset];
for (EGAmapNr = 0; EGAmapNr < SCI_VIEW_EGAMAPPING_COUNT; EGAmapNr++) {
if (memcmp(_EGAmapping, EGAmappingStraight, SCI_VIEW_EGAMAPPING_SIZE) != 0)
@@ -370,22 +371,129 @@ void GfxView::getCelScaledRect(int16 loopNo, int16 celNo, int16 x, int16 y, int1
outRect.top = outRect.bottom - scaledHeight;
}
+void unpackCelData(byte *inBuffer, byte *celBitmap, byte clearColor, int pixelCount, int rlePos, int literalPos, ViewType viewType, uint16 width, bool isMacSci11ViewData) {
+ byte *outPtr = celBitmap;
+ byte curByte, runLength;
+ byte *rlePtr = inBuffer + rlePos;
+ byte *literalPtr = inBuffer + literalPos;
+ int pixelNr = 0;
+
+ memset(celBitmap, clearColor, pixelCount);
+
+ if (!literalPos) {
+ // decompression for data that has only one combined stream
+ switch (viewType) {
+ case kViewEga:
+ while (pixelNr < pixelCount) {
+ curByte = *rlePtr++;
+ runLength = curByte >> 4;
+ memset(outPtr + pixelNr, curByte & 0x0F, MIN<uint16>(runLength, pixelCount - pixelNr));
+ pixelNr += runLength;
+ }
+ break;
+ case kViewAmiga:
+ while (pixelNr < pixelCount) {
+ curByte = *rlePtr++;
+ if (curByte & 0x07) { // fill with color
+ runLength = curByte & 0x07;
+ curByte = curByte >> 3;
+ while (runLength-- && pixelNr < pixelCount)
+ outPtr[pixelNr++] = curByte;
+ } else { // fill with transparent
+ runLength = curByte >> 3;
+ pixelNr += runLength;
+ }
+ }
+ break;
+ case kViewAmiga64:
+ // TODO: This isn't 100% right. Implement it fully.
+ while (pixelNr < pixelCount) {
+ curByte = *rlePtr++;
+ runLength = curByte >> 6;
+ memset(outPtr + pixelNr, curByte & 0x3F, MIN<uint16>(runLength, pixelCount - pixelNr));
+ pixelNr += runLength;
+ }
+ break;
+ case kViewVga:
+ case kViewVga11:
+ while (pixelNr < pixelCount) {
+ curByte = *rlePtr++;
+ runLength = curByte & 0x3F;
+ switch (curByte & 0xC0) {
+ case 0: // copy bytes as-is
+ while (runLength-- && pixelNr < pixelCount)
+ outPtr[pixelNr++] = *rlePtr++;
+ break;
+ case 0x40: // copy bytes as is (In copy case, runLength can go upto 127 i.e. pixel & 0x40). Fixes bug #3135872.
+ runLength += 64;
+ break;
+ case 0x80: // fill with color
+ memset(outPtr + pixelNr, *rlePtr++, MIN<uint16>(runLength, pixelCount - pixelNr));
+ pixelNr += runLength;
+ break;
+ case 0xC0: // fill with transparent
+ pixelNr += runLength;
+ break;
+ }
+ }
+ break;
+ default:
+ error("Unsupported picture viewtype");
+ }
+ } else {
+ // decompression for data that has two separate streams (probably a SCI 1.1 view)
+ if (isMacSci11ViewData) {
+ // KQ6/Freddy Pharkas use byte lengths, all others use uint16
+ // The SCI devs must have realized that a max of 255 pixels wide
+ // was not very good for 320 or 640 width games.
+ bool hasByteLengths = (g_sci->getGameId() == GID_KQ6 || g_sci->getGameId() == GID_FREDDYPHARKAS);
+
+ // compression for SCI1.1+ Mac
+ while (pixelNr < pixelCount) {
+ uint32 pixelLine = pixelNr;
+
+ if (hasByteLengths) {
+ pixelNr += *rlePtr++;
+ runLength = *rlePtr++;
+ } else {
+ pixelNr += READ_BE_UINT16(rlePtr);
+ runLength = READ_BE_UINT16(rlePtr + 2);
+ rlePtr += 4;
+ }
+
+ while (runLength-- && pixelNr < pixelCount)
+ outPtr[pixelNr++] = *literalPtr++;
+
+ pixelNr = pixelLine + width;
+ }
+ } else {
+ while (pixelNr < pixelCount) {
+ curByte = *rlePtr++;
+ runLength = curByte & 0x3F;
+ switch (curByte & 0xC0) {
+ case 0: // copy bytes as-is
+ while (runLength-- && pixelNr < pixelCount)
+ outPtr[pixelNr++] = *literalPtr++;
+ break;
+ case 0x80: // fill with color
+ memset(outPtr + pixelNr, *literalPtr++, MIN<uint16>(runLength, pixelCount - pixelNr));
+ pixelNr += runLength;
+ break;
+ case 0xC0: // fill with transparent
+ pixelNr += runLength;
+ break;
+ }
+ }
+ }
+ }
+}
+
void GfxView::unpackCel(int16 loopNo, int16 celNo, byte *outPtr, uint32 pixelCount) {
const CelInfo *celInfo = getCelInfo(loopNo, celNo);
- byte *rlePtr;
- byte *literalPtr;
- uint32 pixelNo = 0, runLength;
- byte pixel;
if (celInfo->offsetEGA) {
// decompression for EGA views
- literalPtr = _resourceData + _loop[loopNo].cel[celNo].offsetEGA;
- while (pixelNo < pixelCount) {
- pixel = *literalPtr++;
- runLength = pixel >> 4;
- memset(outPtr + pixelNo, pixel & 0x0F, MIN<uint32>(runLength, pixelCount - pixelNo));
- pixelNo += runLength;
- }
+ unpackCelData(_resourceData, outPtr, 0, pixelCount, celInfo->offsetEGA, 0, _resMan->getViewType(), celInfo->width, false);
} else {
// We fill the buffer with transparent pixels, so that we can later skip
// over pixels to automatically have them transparent
@@ -408,100 +516,8 @@ void GfxView::unpackCel(int16 loopNo, int16 celNo, byte *outPtr, uint32 pixelCou
clearColor = 0;
}
- memset(outPtr, clearColor, pixelCount);
-
- rlePtr = _resourceData + celInfo->offsetRLE;
- if (!celInfo->offsetLiteral) { // no additional literal data
- if (_resMan->isAmiga32color()) {
- // decompression for amiga views
- while (pixelNo < pixelCount) {
- pixel = *rlePtr++;
- if (pixel & 0x07) { // fill with color
- runLength = pixel & 0x07;
- pixel = pixel >> 3;
- while (runLength-- && pixelNo < pixelCount) {
- outPtr[pixelNo++] = pixel;
- }
- } else { // fill with transparent
- runLength = pixel >> 3;
- pixelNo += runLength;
- }
- }
- } else {
- // decompression for data that has just one combined stream
- while (pixelNo < pixelCount) {
- pixel = *rlePtr++;
- runLength = pixel & 0x3F;
- switch (pixel & 0xC0) {
- case 0x40: // copy bytes as is (In copy case, runLength can go upto 127 i.e. pixel & 0x40)
- runLength += 64;
- case 0x00: // copy bytes as-is
- while (runLength-- && pixelNo < pixelCount)
- outPtr[pixelNo++] = *rlePtr++;
- break;
- case 0x80: // fill with color
- memset(outPtr + pixelNo, *rlePtr++, MIN<uint32>(runLength, pixelCount - pixelNo));
- pixelNo += runLength;
- break;
- case 0xC0: // fill with transparent
- pixelNo += runLength;
- break;
- }
- }
- }
- } else {
- literalPtr = _resourceData + celInfo->offsetLiteral;
- if (celInfo->offsetRLE) {
- if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() == SCI_VERSION_1_1) {
- // KQ6/Freddy Pharkas use byte lengths, all others use uint16
- // The SCI devs must have realized that a max of 255 pixels wide
- // was not very good for 320 or 640 width games.
- bool hasByteLengths = (g_sci->getGameId() == GID_KQ6 || g_sci->getGameId() == GID_FREDDYPHARKAS);
-
- // compression for SCI1.1+ Mac
- while (pixelNo < pixelCount) {
- uint32 pixelLine = pixelNo;
-
- if (hasByteLengths) {
- pixelNo += *rlePtr++;
- runLength = *rlePtr++;
- } else {
- pixelNo += READ_BE_UINT16(rlePtr);
- runLength = READ_BE_UINT16(rlePtr + 2);
- rlePtr += 4;
- }
-
- while (runLength-- && pixelNo < pixelCount)
- outPtr[pixelNo++] = *literalPtr++;
-
- pixelNo = pixelLine + celInfo->width;
- }
- } else {
- // decompression for data that has separate rle and literal streams
- while (pixelNo < pixelCount) {
- pixel = *rlePtr++;
- runLength = pixel & 0x3F;
- switch (pixel & 0xC0) {
- case 0: // copy bytes as-is
- while (runLength-- && pixelNo < pixelCount)
- outPtr[pixelNo++] = *literalPtr++;
- break;
- case 0x80: // fill with color
- memset(outPtr + pixelNo, *literalPtr++, MIN<uint32>(runLength, pixelCount - pixelNo));
- pixelNo += runLength;
- break;
- case 0xC0: // fill with transparent
- pixelNo += runLength;
- break;
- }
- }
- }
- } else {
- // literal stream only, so no compression
- memcpy(outPtr, literalPtr, pixelCount);
- pixelNo = pixelCount;
- }
- }
+ bool isMacSci11ViewData = g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() == SCI_VERSION_1_1;
+ unpackCelData(_resourceData, outPtr, clearColor, pixelCount, celInfo->offsetRLE, celInfo->offsetLiteral, _resMan->getViewType(), celInfo->width, isMacSci11ViewData);
// Swap 0 and 0xff pixels for Mac SCI1.1+ games (see above)
if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_1_1) {
@@ -531,9 +547,8 @@ const byte *GfxView::getBitmap(int16 loopNo, int16 celNo) {
// unpack the actual cel bitmap data
unpackCel(loopNo, celNo, pBitmap, pixelCount);
- if (!_resMan->isVGA()) {
+ if (_resMan->getViewType() == kViewEga)
unditherBitmap(pBitmap, width, height, _loop[loopNo].cel[celNo].clearKey);
- }
// mirroring the cel if needed
if (_loop[loopNo].mirrorFlag) {
diff --git a/engines/sci/parser/vocabulary.cpp b/engines/sci/parser/vocabulary.cpp
index 8d59df5d58..25043401cc 100644
--- a/engines/sci/parser/vocabulary.cpp
+++ b/engines/sci/parser/vocabulary.cpp
@@ -63,7 +63,7 @@ Vocabulary::Vocabulary(ResourceManager *resMan, bool foreign) : _resMan(resMan),
_resourceIdBranches += 10;
}
- if (getSciVersion() <= SCI_VERSION_1_EGA && loadParserWords()) {
+ if (getSciVersion() <= SCI_VERSION_1_EGA_ONLY && loadParserWords()) {
loadSuffixes();
if (loadBranches())
// Now build a GNF grammar out of this
diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp
index a16f7126c3..7a4534d3ac 100644
--- a/engines/sci/resource.cpp
+++ b/engines/sci/resource.cpp
@@ -67,7 +67,7 @@ const char *getSciVersionDesc(SciVersion version) {
return "Late SCI0";
case SCI_VERSION_01:
return "SCI01";
- case SCI_VERSION_1_EGA:
+ case SCI_VERSION_1_EGA_ONLY:
return "SCI1 EGA";
case SCI_VERSION_1_EARLY:
return "Early SCI1";
@@ -948,15 +948,18 @@ void ResourceManager::init(bool initFromFallbackDetector) {
case kViewEga:
debugC(1, kDebugLevelResMan, "resMan: Detected EGA graphic resources");
break;
+ case kViewAmiga:
+ debugC(1, kDebugLevelResMan, "resMan: Detected Amiga ECS graphic resources");
+ break;
+ case kViewAmiga64:
+ debugC(1, kDebugLevelResMan, "resMan: Detected Amiga AGA graphic resources");
+ break;
case kViewVga:
debugC(1, kDebugLevelResMan, "resMan: Detected VGA graphic resources");
break;
case kViewVga11:
debugC(1, kDebugLevelResMan, "resMan: Detected SCI1.1 VGA graphic resources");
break;
- case kViewAmiga:
- debugC(1, kDebugLevelResMan, "resMan: Detected Amiga graphic resources");
- break;
default:
#ifdef ENABLE_SCI32
error("resMan: Couldn't determine view type");
@@ -1535,10 +1538,18 @@ void ResourceManager::readResourcePatches() {
mask += s_resourceTypeSuffixes[i];
SearchMan.listMatchingMembers(files, mask);
- if (i == kResourceTypeScript && files.size() == 0) {
- // SCI3 (we can't use getSciVersion() at this point)
- mask = "*.csc";
- SearchMan.listMatchingMembers(files, mask);
+ if (i == kResourceTypeView) {
+ SearchMan.listMatchingMembers(files, "*.v16"); // EGA SCI1 view patches
+ SearchMan.listMatchingMembers(files, "*.v32"); // Amiga SCI1 view patches
+ SearchMan.listMatchingMembers(files, "*.v64"); // Amiga AGA SCI1 (i.e. Longbow) view patches
+ } else if (i == kResourceTypePic) {
+ SearchMan.listMatchingMembers(files, "*.p16"); // EGA SCI1 picture patches
+ SearchMan.listMatchingMembers(files, "*.p32"); // Amiga SCI1 picture patches
+ SearchMan.listMatchingMembers(files, "*.p64"); // Amiga AGA SCI1 (i.e. Longbow) picture patches
+ } else if (i == kResourceTypeScript) {
+ if (files.size() == 0)
+ // SCI3 (we can't use getSciVersion() at this point)
+ SearchMan.listMatchingMembers(files, "*.csc");
}
for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
@@ -2049,7 +2060,13 @@ ViewType ResourceManager::detectViewType() {
switch (res->data[1]) {
case 128:
- // If the 2nd byte is 128, it's a VGA game
+ // If the 2nd byte is 128, it's a VGA game.
+ // However, Longbow Amiga (AGA, 64 colors), also sets this byte
+ // to 128, but it's a mixed VGA/Amiga format. Detect this from
+ // the platform here.
+ if (g_sci && g_sci->getPlatform() == Common::kPlatformAmiga)
+ return kViewAmiga64;
+
return kViewVga;
case 0:
// EGA or Amiga, try to read as Amiga view
@@ -2127,9 +2144,9 @@ void ResourceManager::detectSciVersion() {
if (viewCompression != kCompLZW) {
// If it's a different compression type from kCompLZW, the game is probably
- // SCI_VERSION_1_EGA or later. If the views are uncompressed, it is
+ // SCI_VERSION_1_EGA_ONLY or later. If the views are uncompressed, it is
// likely not an early disk game.
- s_sciVersion = SCI_VERSION_1_EGA;
+ s_sciVersion = SCI_VERSION_1_EGA_ONLY;
oldDecompressors = false;
}
@@ -2243,9 +2260,9 @@ void ResourceManager::detectSciVersion() {
return;
}
- // New decompressors. It's either SCI_VERSION_1_EGA or SCI_VERSION_1_EARLY.
+ // New decompressors. It's either SCI_VERSION_1_EGA_ONLY or SCI_VERSION_1_EARLY.
if (hasSci1Voc900()) {
- s_sciVersion = SCI_VERSION_1_EGA;
+ s_sciVersion = SCI_VERSION_1_EGA_ONLY;
return;
}
@@ -2255,6 +2272,9 @@ void ResourceManager::detectSciVersion() {
case kResVersionSci1Middle:
case kResVersionKQ5FMT:
s_sciVersion = SCI_VERSION_1_MIDDLE;
+ // Amiga SCI1 middle games are actually SCI1 late
+ if (_viewType == kViewAmiga || _viewType == kViewAmiga64)
+ s_sciVersion = SCI_VERSION_1_LATE;
return;
case kResVersionSci1Late:
if (_volVersion == kResVersionSci11) {
diff --git a/engines/sci/resource.h b/engines/sci/resource.h
index 76b5a421ee..e941f666d9 100644
--- a/engines/sci/resource.h
+++ b/engines/sci/resource.h
@@ -331,8 +331,6 @@ public:
int getAudioLanguage() const;
void changeAudioDirectory(Common::String path);
bool isGMTrackIncluded();
- bool isVGA() const { return (_viewType == kViewVga) || (_viewType == kViewVga11); }
- bool isAmiga32color() const { return _viewType == kViewAmiga; }
bool isSci11Mac() const { return _volVersion == kResVersionSci11Mac; }
ViewType getViewType() const { return _viewType; }
const char *getMapVersionDesc() const { return versionDescription(_mapVersion); }
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index 7147b17b82..3dfa5e0a97 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -213,7 +213,6 @@ Common::Error SciEngine::run() {
// Add the after market GM patches for the specified game, if they exist
_resMan->addNewGMPatch(_gameId);
_gameObjectAddress = _resMan->findGameObject();
- _gameSuperClassAddress = NULL_REG;
SegManager *segMan = new SegManager(_resMan);
@@ -227,7 +226,7 @@ Common::Error SciEngine::run() {
_features = new GameFeatures(segMan, _kernel);
// Only SCI0, SCI01 and SCI1 EGA games used a parser
- _vocabulary = (getSciVersion() <= SCI_VERSION_1_EGA) ? new Vocabulary(_resMan, false) : NULL;
+ _vocabulary = (getSciVersion() <= SCI_VERSION_1_EGA_ONLY) ? new Vocabulary(_resMan, false) : NULL;
// Also, XMAS1990 apparently had a parser too. Refer to http://forums.scummvm.org/viewtopic.php?t=9135
if (getGameId() == GID_CHRISTMAS1990)
_vocabulary = new Vocabulary(_resMan, false);
@@ -250,7 +249,6 @@ Common::Error SciEngine::run() {
warning("Could not get game object, aborting...");
return Common::kUnknownError;
}
- _gameSuperClassAddress = gameObject->getSuperClassSelector();
script_adjust_opcode_formats();
@@ -267,8 +265,6 @@ Common::Error SciEngine::run() {
// Initialize all graphics related subsystems
initGraphics();
- debug("Emulating SCI version %s\n", getSciVersionDesc(getSciVersion()));
-
// Patch in our save/restore code, so that dialogs are replaced
patchGameSaveRestore();
setLauncherLanguage();
@@ -427,34 +423,65 @@ static byte patchGameRestoreSave[] = {
0x76, // push0
0x38, 0xff, 0xff, // pushi -1
0x76, // push0
- 0x43, 0xff, 0x06, // call kRestoreGame/kSaveGame (will get fixed directly)
+ 0x43, 0xff, 0x06, // callk kRestoreGame/kSaveGame (will get changed afterwards)
+ 0x48, // ret
+};
+
+// SCI2 version: Same as above, but the second parameter to callk is a word
+static byte patchGameRestoreSaveSci2[] = {
+ 0x39, 0x03, // pushi 03
+ 0x76, // push0
+ 0x38, 0xff, 0xff, // pushi -1
+ 0x76, // push0
+ 0x43, 0xff, 0x06, 0x00, // callk kRestoreGame/kSaveGame (will get changed afterwards)
+ 0x48, // ret
+};
+
+// SCI21 version: Same as above, but the second parameter to callk is a word
+static byte patchGameRestoreSaveSci21[] = {
+ 0x39, 0x04, // pushi 04
+ 0x76, // push0 // 0: save, 1: restore (will get changed afterwards)
+ 0x76, // push0
+ 0x38, 0xff, 0xff, // pushi -1
+ 0x76, // push0
+ 0x43, 0xff, 0x08, 0x00, // callk kSave (will get changed afterwards)
0x48, // ret
};
+static void patchGameSaveRestoreCode(SegManager *segMan, reg_t methodAddress, byte id) {
+ Script *script = segMan->getScript(methodAddress.segment);
+ byte *patchPtr = const_cast<byte *>(script->getBuf(methodAddress.offset));
+ if (getSciVersion() <= SCI_VERSION_1_1)
+ memcpy(patchPtr, patchGameRestoreSave, sizeof(patchGameRestoreSave));
+ else // SCI2+
+ memcpy(patchPtr, patchGameRestoreSaveSci2, sizeof(patchGameRestoreSaveSci2));
+ patchPtr[8] = id;
+}
+
+static void patchGameSaveRestoreCodeSci21(SegManager *segMan, reg_t methodAddress, byte id, bool doRestore) {
+ Script *script = segMan->getScript(methodAddress.segment);
+ byte *patchPtr = const_cast<byte *>(script->getBuf(methodAddress.offset));
+ memcpy(patchPtr, patchGameRestoreSaveSci21, sizeof(patchGameRestoreSaveSci21));
+ if (doRestore)
+ patchPtr[2] = 0x78; // push1
+ patchPtr[9] = id;
+}
+
void SciEngine::patchGameSaveRestore() {
SegManager *segMan = _gamestate->_segMan;
const Object *gameObject = segMan->getObject(_gameObjectAddress);
- const uint16 gameMethodCount = gameObject->getMethodCount();
- const Object *gameSuperObject = segMan->getObject(_gameSuperClassAddress);
+ const Object *gameSuperObject = segMan->getObject(gameObject->getSuperClassSelector());
if (!gameSuperObject)
gameSuperObject = gameObject; // happens in KQ5CD, when loading saved games before r54510
- const uint16 gameSuperMethodCount = gameSuperObject->getMethodCount();
- reg_t methodAddress;
- const uint16 kernelCount = _kernel->getKernelNamesSize();
- const byte *scriptRestorePtr = NULL;
byte kernelIdRestore = 0;
- const byte *scriptSavePtr = NULL;
byte kernelIdSave = 0;
- // this feature is currently not supported on SCI32
- if (getSciVersion() >= SCI_VERSION_2)
- return;
-
switch (_gameId) {
- case GID_MOTHERGOOSE256: // mother goose saves/restores directly and has no save/restore dialogs
- case GID_JONES: // gets confused, when we patch us in, the game is only able to save to 1 slot, so hooking is not required
case GID_HOYLE1: // gets confused, although the game doesnt support saving/restoring at all
case GID_HOYLE2: // gets confused, see hoyle1
+ case GID_JONES: // gets confused, when we patch us in, the game is only able to save to 1 slot, so hooking is not required
+ case GID_MOTHERGOOSE256: // mother goose saves/restores directly and has no save/restore dialogs
+ case GID_PHANTASMAGORIA: // has custom save/load code
return;
default:
break;
@@ -463,61 +490,53 @@ void SciEngine::patchGameSaveRestore() {
if (ConfMan.getBool("sci_originalsaveload"))
return;
- for (uint16 kernelNr = 0; kernelNr < kernelCount; kernelNr++) {
+ uint16 kernelNamesSize = _kernel->getKernelNamesSize();
+ for (uint16 kernelNr = 0; kernelNr < kernelNamesSize; kernelNr++) {
Common::String kernelName = _kernel->getKernelName(kernelNr);
if (kernelName == "RestoreGame")
kernelIdRestore = kernelNr;
if (kernelName == "SaveGame")
kernelIdSave = kernelNr;
+ if (kernelName == "Save")
+ kernelIdSave = kernelIdRestore = kernelNr;
}
- // Search for gameobject-superclass ::restore
- for (uint16 methodNr = 0; methodNr < gameSuperMethodCount; methodNr++) {
+ // Search for gameobject superclass ::restore
+ uint16 gameSuperObjectMethodCount = gameSuperObject->getMethodCount();
+ for (uint16 methodNr = 0; methodNr < gameSuperObjectMethodCount; methodNr++) {
uint16 selectorId = gameSuperObject->getFuncSelector(methodNr);
Common::String methodName = _kernel->getSelectorName(selectorId);
if (methodName == "restore") {
- methodAddress = gameSuperObject->getFunction(methodNr);
- Script *script = segMan->getScript(methodAddress.segment);
- scriptRestorePtr = script->getBuf(methodAddress.offset);
+ if (kernelIdSave != kernelIdRestore)
+ patchGameSaveRestoreCode(segMan, gameSuperObject->getFunction(methodNr), kernelIdRestore);
+ else
+ patchGameSaveRestoreCodeSci21(segMan, gameSuperObject->getFunction(methodNr), kernelIdRestore, true);
}
- if (methodName == "save") {
- methodAddress = gameSuperObject->getFunction(methodNr);
- Script *script = segMan->getScript(methodAddress.segment);
- scriptSavePtr = script->getBuf(methodAddress.offset);
+ else if (methodName == "save") {
+ if (_gameId != GID_FAIRYTALES) { // Fairy Tales saves automatically without a dialog
+ if (kernelIdSave != kernelIdRestore)
+ patchGameSaveRestoreCode(segMan, gameSuperObject->getFunction(methodNr), kernelIdSave);
+ else
+ patchGameSaveRestoreCodeSci21(segMan, gameSuperObject->getFunction(methodNr), kernelIdSave, false);
+ }
}
}
- // Search for gameobject ::save, if there is one patch that one instead
- for (uint16 methodNr = 0; methodNr < gameMethodCount; methodNr++) {
+ // Search for gameobject ::save, if there is one patch that one too
+ uint16 gameObjectMethodCount = gameObject->getMethodCount();
+ for (uint16 methodNr = 0; methodNr < gameObjectMethodCount; methodNr++) {
uint16 selectorId = gameObject->getFuncSelector(methodNr);
Common::String methodName = _kernel->getSelectorName(selectorId);
if (methodName == "save") {
- methodAddress = gameObject->getFunction(methodNr);
- Script *script = segMan->getScript(methodAddress.segment);
- scriptSavePtr = script->getBuf(methodAddress.offset);
+ if (_gameId != GID_FAIRYTALES) { // Fairy Tales saves automatically without a dialog
+ if (kernelIdSave != kernelIdRestore)
+ patchGameSaveRestoreCode(segMan, gameObject->getFunction(methodNr), kernelIdSave);
+ else
+ patchGameSaveRestoreCodeSci21(segMan, gameObject->getFunction(methodNr), kernelIdSave, false);
+ }
break;
}
}
-
- switch (_gameId) {
- case GID_FAIRYTALES: // fairy tales automatically saves w/o dialog
- scriptSavePtr = NULL;
- default:
- break;
- }
-
- if (scriptRestorePtr) {
- // Now patch in our code
- byte *patchPtr = const_cast<byte *>(scriptRestorePtr);
- memcpy(patchPtr, patchGameRestoreSave, sizeof(patchGameRestoreSave));
- patchPtr[8] = kernelIdRestore;
- }
- if (scriptSavePtr) {
- // Now patch in our code
- byte *patchPtr = const_cast<byte *>(scriptSavePtr);
- memcpy(patchPtr, patchGameRestoreSave, sizeof(patchGameRestoreSave));
- patchPtr[8] = kernelIdSave;
- }
}
bool SciEngine::initGame() {
@@ -623,7 +642,7 @@ void SciEngine::initGraphics() {
_gfxCoordAdjuster = new GfxCoordAdjuster16(_gfxPorts);
_gfxCursor->init(_gfxCoordAdjuster, _eventMan);
_gfxCompare = new GfxCompare(_gamestate->_segMan, _kernel, _gfxCache, _gfxScreen, _gfxCoordAdjuster);
- _gfxTransitions = new GfxTransitions(_gfxScreen, _gfxPalette, _resMan->isVGA());
+ _gfxTransitions = new GfxTransitions(_gfxScreen, _gfxPalette);
_gfxPaint16 = new GfxPaint16(_resMan, _gamestate->_segMan, _kernel, _gfxCache, _gfxPorts, _gfxCoordAdjuster, _gfxScreen, _gfxPalette, _gfxTransitions, _audio);
_gfxPaint = _gfxPaint16;
_gfxAnimate = new GfxAnimate(_gamestate, _gfxCache, _gfxPorts, _gfxPaint16, _gfxScreen, _gfxPalette, _gfxCursor, _gfxTransitions);
@@ -758,6 +777,16 @@ bool SciEngine::isCD() const {
return _gameDescription->flags & ADGF_CD;
}
+bool SciEngine::isBE() const{
+ switch(_gameDescription->platform) {
+ case Common::kPlatformAmiga:
+ case Common::kPlatformMacintosh:
+ return true;
+ default:
+ return false;
+ }
+}
+
bool SciEngine::hasMacIconBar() const {
return _resMan->isSci11Mac() && getSciVersion() == SCI_VERSION_1_1 &&
(getGameId() == GID_KQ6 || getGameId() == GID_FREDDYPHARKAS);
diff --git a/engines/sci/sci.h b/engines/sci/sci.h
index b3e398325f..26ddb00a01 100644
--- a/engines/sci/sci.h
+++ b/engines/sci/sci.h
@@ -180,20 +180,24 @@ enum SciGameId {
GID_FANMADE // FIXME: Do we really need/want this?
};
-/** SCI versions */
+/**
+ * SCI versions
+ * For more information, check here:
+ * http://wiki.scummvm.org/index.php/Sierra_Game_Versions#SCI_Games
+ */
enum SciVersion {
SCI_VERSION_NONE,
- SCI_VERSION_0_EARLY, // Early KQ4, 1988 xmas card
+ SCI_VERSION_0_EARLY, // KQ4 early, LSL2 early, XMAS card 1988
SCI_VERSION_0_LATE, // KQ4, LSL2, LSL3, SQ3 etc
SCI_VERSION_01, // KQ1 and multilingual games (S.old.*)
- SCI_VERSION_1_EGA, // EGA with parser, QFG2
- SCI_VERSION_1_EARLY, // KQ5. (EGA/VGA)
- SCI_VERSION_1_MIDDLE, // LSL1, JONESCD. (EGA?/VGA)
- SCI_VERSION_1_LATE, // ECO1, LSL5. (EGA/VGA)
- SCI_VERSION_1_1, // KQ6, ECO2
- SCI_VERSION_2, // GK1, PQ4 (Floppy), QFG4 (Floppy)
- SCI_VERSION_2_1, // GK2, KQ7, SQ6, Torin
- SCI_VERSION_3 // LSL7, RAMA, Lighthouse
+ SCI_VERSION_1_EGA_ONLY, // SCI 1 EGA with parser (i.e. QFG2 only)
+ SCI_VERSION_1_EARLY, // KQ5 floppy, SQ4 floppy, XMAS card 1990, Fairy tales, Jones floppy
+ SCI_VERSION_1_MIDDLE, // LSL1, Jones CD
+ SCI_VERSION_1_LATE, // Dr. Brain 1, EcoQuest 1, Longbow, PQ3, SQ1, LSL5, KQ5 CD
+ SCI_VERSION_1_1, // Dr. Brain 2, EcoQuest 1 CD, EcoQuest 2, KQ6, QFG3, SQ4CD, XMAS 1992 and many more
+ SCI_VERSION_2, // GK1, PQ4 floppy, QFG4 floppy
+ SCI_VERSION_2_1, // GK2, KQ7, LSL6 hires, MUMG Deluxe, Phantasmagoria 1, PQ4CD, PQ:SWAT, QFG4CD, Shivers 1, SQ6, Torin
+ SCI_VERSION_3 // LSL7, Lighthouse, RAMA, Phantasmagoria 2
};
/** Supported languages */
@@ -234,6 +238,10 @@ public:
Common::Platform getPlatform() const;
bool isDemo() const;
bool isCD() const;
+
+ /** Returns true if the game's original platform is big-endian. */
+ bool isBE() const;
+
bool hasMacIconBar() const;
inline ResourceManager *getResMan() const { return _resMan; }
@@ -242,7 +250,6 @@ public:
inline Vocabulary *getVocabulary() const { return _vocabulary; }
inline EventManager *getEventManager() const { return _eventMan; }
inline reg_t getGameObject() const { return _gameObjectAddress; }
- inline reg_t getGameSuperClassAddress() const { return _gameSuperClassAddress; }
Common::RandomSource &getRNG() { return _rng; }
@@ -371,7 +378,6 @@ private:
int16 _vocabularyLanguage;
EventManager *_eventMan;
reg_t _gameObjectAddress; /**< Pointer to the game object */
- reg_t _gameSuperClassAddress; // Address of the super class of the game object
Console *_console;
Common::RandomSource _rng;
Common::MacResManager _macExecutable;
diff --git a/engines/sci/sound/drivers/adlib.cpp b/engines/sci/sound/drivers/adlib.cpp
index 057b7c177f..65a8e2e3da 100644
--- a/engines/sci/sound/drivers/adlib.cpp
+++ b/engines/sci/sound/drivers/adlib.cpp
@@ -55,7 +55,7 @@ public:
virtual ~MidiDriver_AdLib() { }
// MidiDriver
- int open(bool isSCI0);
+ int openAdLib(bool isSCI0);
void close();
void send(uint32 b);
MidiChannel *allocateChannel() { return NULL; }
@@ -215,7 +215,7 @@ static const int ym3812_note[13] = {
0x2ae
};
-int MidiDriver_AdLib::open(bool isSCI0) {
+int MidiDriver_AdLib::openAdLib(bool isSCI0) {
int rate = _mixer->getOutputRate();
_stereo = STEREO;
@@ -273,10 +273,6 @@ void MidiDriver_AdLib::send(uint32 b) {
case 0x90:
noteOn(channel, op1, op2);
break;
- case 0xe0:
- _channels[channel].pitchWheel = (op1 & 0x7f) | ((op2 & 0x7f) << 7);
- renewNotes(channel, true);
- break;
case 0xb0:
switch (op1) {
case 0x07:
@@ -321,7 +317,9 @@ void MidiDriver_AdLib::send(uint32 b) {
case 0xa0: // Polyphonic key pressure (aftertouch)
case 0xd0: // Channel pressure (aftertouch)
break;
- case 0xf0: // SysEx, ignore it
+ case 0xe0:
+ _channels[channel].pitchWheel = (op1 & 0x7f) | ((op2 & 0x7f) << 7);
+ renewNotes(channel, true);
break;
default:
warning("ADLIB: Unknown event %02x", command);
@@ -828,7 +826,7 @@ int MidiPlayer_AdLib::open(ResourceManager *resMan) {
return -1;
}
- return static_cast<MidiDriver_AdLib *>(_driver)->open(_version <= SCI_VERSION_0_LATE);
+ return static_cast<MidiDriver_AdLib *>(_driver)->openAdLib(_version <= SCI_VERSION_0_LATE);
}
void MidiPlayer_AdLib::close() {
diff --git a/engines/sci/sound/drivers/amigamac.cpp b/engines/sci/sound/drivers/amigamac.cpp
index 0ec4c283f7..d64dfac23c 100644
--- a/engines/sci/sound/drivers/amigamac.cpp
+++ b/engines/sci/sound/drivers/amigamac.cpp
@@ -34,7 +34,7 @@
namespace Sci {
-/* #define DEBUG */
+//#define DEBUG
class MidiDriver_AmigaMac : public MidiDriver_Emulated {
public:
@@ -289,9 +289,9 @@ void MidiDriver_AmigaMac::playInstrument(int16 *dest, Voice *channel, int count)
void MidiDriver_AmigaMac::changeInstrument(int channel, int instrument) {
#ifdef DEBUG
if (_bank.instruments[instrument][0])
- debugN("[sfx:seq:amiga] Setting channel %i to \"%s\" (%i)\n", channel, _bank.instruments[instrument]->name, instrument);
+ debugN("Amiga/Mac driver: Setting channel %i to \"%s\" (%i)\n", channel, _bank.instruments[instrument].name, instrument);
else
- warning("[sfx:seq:amiga] instrument %i does not exist (channel %i)", instrument, channel);
+ warning("Amiga/Mac driver: instrument %i does not exist (channel %i)", instrument, channel);
#endif
_channels[channel].instrument = instrument;
}
@@ -326,7 +326,7 @@ void MidiDriver_AmigaMac::stopNote(int ch, int note) {
if (channel == kChannels) {
#ifdef DEBUG
- warning("[sfx:seq:amiga] cannot stop note %i on channel %i", note, ch);
+ warning("Amiga/Mac driver: cannot stop note %i on channel %i", note, ch);
#endif
return;
}
@@ -366,7 +366,7 @@ void MidiDriver_AmigaMac::setOutputFrac(int voice) {
fnote += instrument->transpose;
if (fnote < 0 || fnote > 127) {
- warning("[sfx:seq:amiga] illegal note %i", fnote);
+ warning("Amiga/Mac driver: illegal note %i", fnote);
return;
}
} else
@@ -403,14 +403,14 @@ void MidiDriver_AmigaMac::startNote(int ch, int note, int velocity) {
int channel;
if (_channels[ch].instrument < 0 || _channels[ch].instrument > 255) {
- warning("[sfx:seq:amiga] invalid instrument %i on channel %i", _channels[ch].instrument, ch);
+ warning("Amiga/Mac driver: invalid instrument %i on channel %i", _channels[ch].instrument, ch);
return;
}
InstrumentSample *instrument = findInstrument(_channels[ch].instrument, note);
if (!instrument) {
- warning("[sfx:seq:amiga] instrument %i does not exist", _channels[ch].instrument);
+ warning("Amiga/Mac driver: instrument %i does not exist", _channels[ch].instrument);
return;
}
@@ -419,7 +419,7 @@ void MidiDriver_AmigaMac::startNote(int ch, int note, int velocity) {
break;
if (channel == kChannels) {
- warning("[sfx:seq:amiga] could not find a free channel");
+ warning("Amiga/Mac driver: could not find a free channel");
return;
}
@@ -447,14 +447,14 @@ MidiDriver_AmigaMac::InstrumentSample *MidiDriver_AmigaMac::readInstrumentSCI0(C
byte header[61];
if (file.read(header, 61) < 61) {
- warning("[sfx:seq:amiga] failed to read instrument header");
+ warning("Amiga/Mac driver: failed to read instrument header");
return NULL;
}
int seg_size[3];
- seg_size[0] = READ_BE_UINT16(header + 35) * 2;
- seg_size[1] = READ_BE_UINT16(header + 41) * 2;
- seg_size[2] = READ_BE_UINT16(header + 47) * 2;
+ seg_size[0] = (int16)READ_BE_UINT16(header + 35) * 2;
+ seg_size[1] = (int16)READ_BE_UINT16(header + 41) * 2;
+ seg_size[2] = (int16)READ_BE_UINT16(header + 47) * 2;
InstrumentSample *instrument = new InstrumentSample;
@@ -489,18 +489,18 @@ MidiDriver_AmigaMac::InstrumentSample *MidiDriver_AmigaMac::readInstrumentSCI0(C
instrument->name[29] = 0;
#ifdef DEBUG
- debugN("[sfx:seq:amiga] Reading instrument %i: \"%s\" (%i bytes)\n",
+ debugN("Amiga/Mac driver: Reading instrument %i: \"%s\" (%i bytes)\n",
*id, instrument->name, size);
debugN(" Mode: %02x\n", instrument->mode);
debugN(" Looping: %s\n", instrument->mode & kModeLoop ? "on" : "off");
debugN(" Pitch changes: %s\n", instrument->mode & kModePitch ? "on" : "off");
debugN(" Segment sizes: %i %i %i\n", seg_size[0], seg_size[1], seg_size[2]);
- debugN(" Segment offsets: 0 %i %i\n", loop_offset, read_int32(header + 43));
+ debugN(" Segment offsets: 0 %i %i\n", loop_offset, (int32)READ_BE_UINT32(header + 43));
#endif
instrument->samples = (int8 *) malloc(size + 1);
if (file.read(instrument->samples, size) < (unsigned int)size) {
- warning("[sfx:seq:amiga] failed to read instrument samples");
+ warning("Amiga/Mac driver: failed to read instrument samples");
free(instrument->samples);
delete instrument;
return NULL;
@@ -512,14 +512,14 @@ MidiDriver_AmigaMac::InstrumentSample *MidiDriver_AmigaMac::readInstrumentSCI0(C
if (instrument->mode & kModeLoop) {
if (loop_offset + seg_size[1] > size) {
#ifdef DEBUG
- warning("[sfx:seq:amiga] looping samples extend %i bytes past end of sample block",
+ warning("Amiga/Mac driver: looping samples extend %i bytes past end of sample block",
loop_offset + seg_size[1] - size);
#endif
seg_size[1] = size - loop_offset;
}
if (seg_size[1] < 0) {
- warning("[sfx:seq:amiga] invalid looping point");
+ warning("Amiga/Mac driver: invalid looping point");
free(instrument->samples);
delete instrument;
return NULL;
@@ -666,26 +666,42 @@ void MidiDriver_AmigaMac::send(uint32 b) {
case 0x07:
_channels[channel].volume = op2;
break;
- case 0x0a:
+ case 0x0a: // pan
+ // TODO
#ifdef DEBUG
- warning("[sfx:seq:amiga] ignoring pan 0x%02x event for channel %i", op2, channel);
+ warning("Amiga/Mac driver: ignoring pan 0x%02x event for channel %i", op2, channel);
#endif
break;
+ case 0x40: // hold
+ // TODO
+#ifdef DEBUG
+ warning("Amiga/Mac driver: ignoring hold 0x%02x event for channel %i", op2, channel);
+#endif
+ break;
+ case 0x4b: // voice mapping
+ break;
+ case 0x4e: // velocity
+ break;
case 0x7b:
stopChannel(channel);
break;
default:
- warning("[sfx:seq:amiga] unknown control event 0x%02x", op1);
+ //warning("Amiga/Mac driver: unknown control event 0x%02x", op1);
+ break;
}
break;
case 0xc0:
changeInstrument(channel, op1);
break;
+ // The original MIDI driver from sierra ignores aftertouch completely, so should we
+ case 0xa0: // Polyphonic key pressure (aftertouch)
+ case 0xd0: // Channel pressure (aftertouch)
+ break;
case 0xe0:
pitchWheel(channel, (op2 << 7) | op1);
break;
default:
- warning("[sfx:seq:amiga] unknown event %02x", command);
+ warning("Amiga/Mac driver: unknown event %02x", command);
}
}
@@ -738,7 +754,7 @@ bool MidiDriver_AmigaMac::loadInstrumentsSCI0(Common::File &file) {
byte header[40];
if (file.read(header, 40) < 40) {
- warning("[sfx:seq:amiga] failed to read header of file bank.001");
+ warning("Amiga/Mac driver: failed to read header of file bank.001");
return false;
}
@@ -746,7 +762,7 @@ bool MidiDriver_AmigaMac::loadInstrumentsSCI0(Common::File &file) {
strncpy(_bank.name, (char *) header + 8, 29);
_bank.name[29] = 0;
#ifdef DEBUG
- debugN("[sfx:seq:amiga] Reading %i instruments from bank \"%s\"\n", _bank.size, _bank.name);
+ debugN("Amiga/Mac driver: Reading %i instruments from bank \"%s\"\n", _bank.size, _bank.name);
#endif
for (uint i = 0; i < _bank.size; i++) {
@@ -754,12 +770,12 @@ bool MidiDriver_AmigaMac::loadInstrumentsSCI0(Common::File &file) {
InstrumentSample *instrument = readInstrumentSCI0(file, &id);
if (!instrument) {
- warning("[sfx:seq:amiga] failed to read bank.001");
+ warning("Amiga/Mac driver: failed to read bank.001");
return false;
}
if (id < 0 || id > 255) {
- warning("[sfx:seq:amiga] Error: instrument ID out of bounds");
+ warning("Amiga/Mac driver: Error: instrument ID out of bounds");
return false;
}
@@ -777,7 +793,7 @@ bool MidiDriver_AmigaMac::loadInstrumentsSCI0Mac(Common::SeekableReadStream &fil
byte header[40];
if (file.read(header, 40) < 40) {
- warning("[sfx:seq:amiga] failed to read header of file patch.200");
+ warning("Amiga/Mac driver: failed to read header of file patch.200");
return false;
}
@@ -785,7 +801,7 @@ bool MidiDriver_AmigaMac::loadInstrumentsSCI0Mac(Common::SeekableReadStream &fil
strncpy(_bank.name, (char *) header + 8, 29);
_bank.name[29] = 0;
#ifdef DEBUG
- debugN("[sfx:seq:amiga] Reading %i instruments from bank \"%s\"\n", _bank.size, _bank.name);
+ debugN("Amiga/Mac driver: Reading %i instruments from bank \"%s\"\n", _bank.size, _bank.name);
#endif
Common::Array<uint32> instrumentOffsets;
@@ -848,7 +864,7 @@ bool MidiDriver_AmigaMac::loadInstrumentsSCI0Mac(Common::SeekableReadStream &fil
instrument->samples = (int8 *)malloc(size + 1);
if (file.read(instrument->samples, size) < size) {
- warning("[sfx:seq:amiga] failed to read instrument sample");
+ warning("Amiga/Mac driver: failed to read instrument sample");
free(instrument->samples);
delete instrument;
continue;
diff --git a/engines/sci/sound/drivers/midi.cpp b/engines/sci/sound/drivers/midi.cpp
index 08e93d3213..d16655928e 100644
--- a/engines/sci/sound/drivers/midi.cpp
+++ b/engines/sci/sound/drivers/midi.cpp
@@ -248,11 +248,17 @@ void MidiPlayer_Midi::controlChange(int channel, int control, int value) {
_channels[channel].hold = value;
break;
+ case 0x4b: // voice mapping
+ break;
+ case 0x4e: // velocity
+ break;
case 0x7b:
if (!_channels[channel].playing)
return;
_channels[channel].playing = false;
+ default:
+ break;
}
_driver->send(0xb0 | channel, control, value);
@@ -604,41 +610,83 @@ void MidiPlayer_Midi::readMt32DrvData() {
if (f.open("MT32.DRV")) {
int size = f.size();
- assert(size >= 166);
-
- // Send before-SysEx text
- f.seek(0x59);
+ // Skip before-SysEx text
+ if (size == 1773 || size == 1759) // XMAS88 / KQ4 early
+ f.seek(0x59);
+ else if (size == 2771) // LSL2 early
+ f.seek(0x29);
+ else
+ error("Unknown MT32.DRV size (%d)", size);
// Skip 2 extra 0 bytes in some drivers
if (f.readUint16LE() != 0)
f.seek(-2, SEEK_CUR);
+ // Send before-SysEx text
sendMt32SysEx(0x200000, static_cast<Common::SeekableReadStream *>(&f), 20);
- // Send after-SysEx text (SSCI sends this before every song)
- sendMt32SysEx(0x200000, static_cast<Common::SeekableReadStream *>(&f), 20);
+ if (size != 2271) {
+ // Send after-SysEx text (SSCI sends this before every song).
+ // There aren't any SysEx calls in old drivers, so this can
+ // be sent right after the before-SysEx text.
+ sendMt32SysEx(0x200000, static_cast<Common::SeekableReadStream *>(&f), 20);
+ } else {
+ // Skip the after-SysEx text in the newer patch version, we'll send
+ // it after the SysEx messages are sent.
+ f.skip(20);
+ }
- // Save goodbye message
+ // Save goodbye message. This isn't a C string, so it may not be
+ // nul-terminated.
f.read(_goodbyeMsg, 20);
// Set volume
byte volume = CLIP<uint16>(f.readUint16LE(), 0, 100);
setMt32Volume(volume);
- byte reverbSysEx[13];
- // This old driver should have a full reverb SysEx
- if ((f.read(reverbSysEx, 13) != 13) || (reverbSysEx[0] != 0xf0) || (reverbSysEx[12] != 0xf7))
- error("Error reading MT32.DRV");
+ if (size == 2771) {
+ // MT32.DRV in LSL2 early contains more data, like a normal patch
+ byte reverb = f.readByte();
- // Send reverb SysEx
- sysEx(reverbSysEx + 1, 11);
- _hasReverb = false;
+ _hasReverb = true;
- f.seek(0x29);
+ // Skip reverb SysEx message
+ f.skip(11);
- // Read AdLib->MT-32 patch map
- for (int i = 0; i < 48; i++) {
- _patchMap[i] = f.readByte();
+ // Read reverb data (stored vertically - patch #3117434)
+ for (int j = 0; j < 3; ++j) {
+ for (int i = 0; i < kReverbConfigNr; i++) {
+ _reverbConfig[i][j] = f.readByte();
+ }
+ }
+
+ f.skip(2235); // skip driver code
+
+ // Patches 1-48
+ sendMt32SysEx(0x50000, static_cast<Common::SeekableReadStream *>(&f), 256);
+ sendMt32SysEx(0x50200, static_cast<Common::SeekableReadStream *>(&f), 128);
+
+ setReverb(reverb);
+
+ // Send the after-SysEx text
+ f.seek(0x3d);
+ sendMt32SysEx(0x200000, static_cast<Common::SeekableReadStream *>(&f), 20);
+ } else {
+ byte reverbSysEx[13];
+ // This old driver should have a full reverb SysEx
+ if ((f.read(reverbSysEx, 13) != 13) || (reverbSysEx[0] != 0xf0) || (reverbSysEx[12] != 0xf7))
+ error("Error reading MT32.DRV");
+
+ // Send reverb SysEx
+ sysEx(reverbSysEx + 1, 11);
+ _hasReverb = false;
+
+ f.seek(0x29);
+
+ // Read AdLib->MT-32 patch map
+ for (int i = 0; i < 48; i++) {
+ _patchMap[i] = f.readByte();
+ }
}
f.close();
@@ -913,7 +961,7 @@ int MidiPlayer_Midi::open(ResourceManager *resMan) {
// TODO: The MT-32 <-> GM mapping hasn't been worked on for SCI1 games. Throw
// a warning to the user
- if (getSciVersion() >= SCI_VERSION_1_EGA)
+ if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY)
warning("The automatic mapping for General MIDI hasn't been worked on for "
"SCI1 games. Music might sound wrong or broken. Please choose another "
"music driver for this game (e.g. Adlib or MT-32) if you are "
diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp
index f0963e7d64..a028617e74 100644
--- a/engines/sci/sound/music.cpp
+++ b/engines/sci/sound/music.cpp
@@ -75,7 +75,7 @@ void SciMusic::init() {
deviceFlags |= MDT_PREFER_GM;
// Currently our CMS implementation only supports SCI1(.1)
- if (getSciVersion() >= SCI_VERSION_1_EGA && getSciVersion() <= SCI_VERSION_1_1)
+ if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY && getSciVersion() <= SCI_VERSION_1_1)
deviceFlags |= MDT_CMS;
uint32 dev = MidiDriver::detectDevice(deviceFlags);
@@ -318,8 +318,21 @@ void SciMusic::soundInitSnd(MusicEntry *pSnd) {
channelFilterMask = pSnd->soundRes->getChannelFilterMask(_pMidiDrv->getPlayId(), _pMidiDrv->hasRhythmChannel());
pSnd->pMidiParser->mainThreadBegin();
+ // loadMusic() below calls jumpToTick.
+ // Disable sound looping and hold before jumpToTick is called,
+ // otherwise the song may keep looping forever when it ends in
+ // jumpToTick (e.g. LSL3, when going left from room 210).
+ uint16 prevLoop = pSnd->loop;
+ int16 prevHold = pSnd->hold;
+ pSnd->loop = 0;
+ pSnd->hold = -1;
+
pSnd->pMidiParser->loadMusic(track, pSnd, channelFilterMask, _soundVersion);
pSnd->reverb = pSnd->pMidiParser->getSongReverb();
+
+ // Restore looping and hold
+ pSnd->loop = prevLoop;
+ pSnd->hold = prevHold;
pSnd->pMidiParser->mainThreadEnd();
_mutex.unlock();
}
@@ -434,18 +447,26 @@ void SciMusic::soundPlay(MusicEntry *pSnd) {
if (pSnd->status != kSoundPaused)
pSnd->pMidiParser->sendInitCommands();
pSnd->pMidiParser->setVolume(pSnd->volume);
- if (pSnd->status == kSoundStopped) {
+
+ // Disable sound looping and hold before jumpToTick is called,
+ // otherwise the song may keep looping forever when it ends in jumpToTick.
+ // This is needed when loading saved games, or when a game
+ // stops the same sound twice (e.g. LSL3 Amiga, going left from
+ // room 210 to talk with Kalalau). Fixes bugs #3083151 and #3106107.
+ uint16 prevLoop = pSnd->loop;
+ int16 prevHold = pSnd->hold;
+ pSnd->loop = 0;
+ pSnd->hold = -1;
+
+ if (pSnd->status == kSoundStopped)
pSnd->pMidiParser->jumpToTick(0);
- } else {
- // Disable sound looping before fast forwarding to the last position,
- // when loading a saved game. Fixes bug #3083151.
- uint16 prevLoop = pSnd->loop;
- pSnd->loop = 0;
+ else
// Fast forward to the last position and perform associated events when loading
pSnd->pMidiParser->jumpToTick(pSnd->ticker, true, true, true);
- // Restore looping
- pSnd->loop = prevLoop;
- }
+
+ // Restore looping and hold
+ pSnd->loop = prevLoop;
+ pSnd->hold = prevHold;
pSnd->pMidiParser->mainThreadEnd();
_mutex.unlock();
}
diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp
index 45a3e09453..1e6d0aef87 100644
--- a/engines/sci/sound/soundcmd.cpp
+++ b/engines/sci/sound/soundcmd.cpp
@@ -445,8 +445,16 @@ void SoundCommandParser::processUpdateCues(reg_t obj) {
if (musicSlot->fadeCompleted) {
musicSlot->fadeCompleted = false;
- // We need signal for sci0 at least in iceman as well (room 14, fireworks)
- writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
+ // We need signal for sci0 at least in iceman as well (room 14,
+ // fireworks).
+ // It is also needed in other games, e.g. LSL6 when talking to the
+ // receptionist (bug #3192166).
+ if (g_sci->getGameId() == GID_LONGBOW && g_sci->getEngineState()->currentRoomNumber() == 95) {
+ // HACK: Don't set a signal here in the intro of Longbow, as that makes some dialog
+ // boxes disappear too soon (bug #3044844).
+ } else {
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
+ }
if (_soundVersion <= SCI_VERSION_0_LATE) {
processStopSound(obj, false);
} else {
diff --git a/engines/sci/util.cpp b/engines/sci/util.cpp
index f6a2465682..f346adddeb 100644
--- a/engines/sci/util.cpp
+++ b/engines/sci/util.cpp
@@ -30,25 +30,39 @@
namespace Sci {
+uint16 READ_SCIENDIAN_UINT16(const void *ptr) {
+ if (g_sci->isBE())
+ return READ_BE_UINT16(ptr);
+ else
+ return READ_LE_UINT16(ptr);
+}
+
+void WRITE_SCIENDIAN_UINT16(void *ptr, uint16 val) {
+ if (g_sci->isBE())
+ WRITE_BE_UINT16(ptr, val);
+ else
+ WRITE_LE_UINT16(ptr, val);
+}
+
uint16 READ_SCI11ENDIAN_UINT16(const void *ptr) {
if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_1_1)
return READ_BE_UINT16(ptr);
-
- return READ_LE_UINT16(ptr);
+ else
+ return READ_LE_UINT16(ptr);
}
uint16 READ_SCI32ENDIAN_UINT16(const void *ptr) {
if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_2_1)
return READ_BE_UINT16(ptr);
-
- return READ_LE_UINT16(ptr);
+ else
+ return READ_LE_UINT16(ptr);
}
uint32 READ_SCI11ENDIAN_UINT32(const void *ptr) {
if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_1_1)
return READ_BE_UINT32(ptr);
-
- return READ_LE_UINT32(ptr);
+ else
+ return READ_LE_UINT32(ptr);
}
void WRITE_SCI11ENDIAN_UINT16(void *ptr, uint16 val) {
diff --git a/engines/sci/util.h b/engines/sci/util.h
index d9ced5c9f6..7a2abb1873 100644
--- a/engines/sci/util.h
+++ b/engines/sci/util.h
@@ -30,6 +30,11 @@
namespace Sci {
+// Wrappers for reading/writing 16-bit values in the endianness
+// of the original game platform.
+uint16 READ_SCIENDIAN_UINT16(const void *ptr);
+void WRITE_SCIENDIAN_UINT16(void *ptr, uint16 val);
+
// Wrappers for reading integer values for SCI1.1+.
// Mac versions have big endian data for some fields.
uint16 READ_SCI11ENDIAN_UINT16(const void *ptr);
diff --git a/engines/sci/video/robot_decoder.cpp b/engines/sci/video/robot_decoder.cpp
index bf52de67d5..ecdce3bd6b 100644
--- a/engines/sci/video/robot_decoder.cpp
+++ b/engines/sci/video/robot_decoder.cpp
@@ -251,6 +251,11 @@ const Graphics::Surface *RobotDecoder::decodeNextFrame() {
_fileStream->skip(4); // unknown, almost always 0
uint16 frameX = _fileStream->readUint16();
uint16 frameY = _fileStream->readUint16();
+ // TODO: In v4 robot files, frameX and frameY have a different meaning.
+ // Set them both to 0 for v4 for now, so that robots in PQ:SWAT show up
+ // correctly.
+ if (_header.version == 4)
+ frameX = frameY = 0;
uint16 compressedSize = _fileStream->readUint16();
uint16 frameFragments = _fileStream->readUint16();
_fileStream->skip(4); // unknown
diff --git a/engines/sword2/animation.cpp b/engines/sword2/animation.cpp
index 3c506c0dae..8d1a9836f4 100644
--- a/engines/sword2/animation.cpp
+++ b/engines/sword2/animation.cpp
@@ -110,7 +110,7 @@ void MoviePlayer::play(MovieText *movieTexts, uint32 numMovieTexts, uint32 leadI
terminated = !playVideo();
- closeTextObject(_currentMovieText, NULL);
+ closeTextObject(_currentMovieText, NULL, 0);
if (terminated) {
_snd->stopHandle(*_bgSoundHandle);
@@ -165,7 +165,7 @@ void MoviePlayer::openTextObject(uint32 index) {
}
}
-void MoviePlayer::closeTextObject(uint32 index, byte *screen) {
+void MoviePlayer::closeTextObject(uint32 index, byte *screen, uint16 pitch) {
if (index < _numMovieTexts) {
MovieText *text = &_movieTexts[index];
@@ -182,20 +182,21 @@ void MoviePlayer::closeTextObject(uint32 index, byte *screen) {
int frameHeight = _decoder->getHeight();
int frameX = (_system->getWidth() - frameWidth) / 2;
int frameY = (_system->getHeight() - frameHeight) / 2;
+ byte black = findBlackPalIndex();
- byte *dst = screen + _textY * _system->getWidth();
+ byte *dst = screen + _textY * pitch;
for (int y = 0; y < text->_textSprite.h; y++) {
if (_textY + y < frameY || _textY + y >= frameY + frameHeight) {
- memset(dst + _textX, findBlackPalIndex(), text->_textSprite.w);
+ memset(dst + _textX, black, text->_textSprite.w);
} else {
if (frameX > _textX)
- memset(dst + _textX, findBlackPalIndex(), frameX - _textX);
+ memset(dst + _textX, black, frameX - _textX);
if (frameX + frameWidth < _textX + text->_textSprite.w)
- memset(dst + frameX + frameWidth, findBlackPalIndex(), _textX + text->_textSprite.w - (frameX + frameWidth));
+ memset(dst + frameX + frameWidth, black, _textX + text->_textSprite.w - (frameX + frameWidth));
}
- dst += _system->getWidth();
+ dst += pitch;
}
}
@@ -205,7 +206,7 @@ void MoviePlayer::closeTextObject(uint32 index, byte *screen) {
}
}
-void MoviePlayer::drawTextObject(uint32 index, byte *screen) {
+void MoviePlayer::drawTextObject(uint32 index, byte *screen, uint16 pitch) {
MovieText *text = &_movieTexts[index];
byte white = findWhitePalIndex();
@@ -217,14 +218,15 @@ void MoviePlayer::drawTextObject(uint32 index, byte *screen) {
uint16 height = text->_textSprite.h;
// Resize text sprites for PSX version
+ byte *psxSpriteBuffer = 0;
if (Sword2Engine::isPsx()) {
height *= 2;
- byte *buffer = (byte *)malloc(width * height);
- Screen::resizePsxSprite(buffer, src, width, height);
- src = buffer;
+ psxSpriteBuffer = (byte *)malloc(width * height);
+ Screen::resizePsxSprite(psxSpriteBuffer, src, width, height);
+ src = psxSpriteBuffer;
}
- byte *dst = screen + _textY * RENDERWIDE + _textX;
+ byte *dst = screen + _textY * pitch + _textX;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
@@ -234,12 +236,16 @@ void MoviePlayer::drawTextObject(uint32 index, byte *screen) {
dst[x] = white;
}
src += width;
- dst += RENDERWIDE;
+ dst += pitch;
}
+
+ // Free buffer used to resize psx sprite
+ if (Sword2Engine::isPsx())
+ free(psxSpriteBuffer);
}
}
-void MoviePlayer::performPostProcessing(byte *screen) {
+void MoviePlayer::performPostProcessing(byte *screen, uint16 pitch) {
MovieText *text;
int frame = _decoder->getCurFrame();
@@ -261,9 +267,9 @@ void MoviePlayer::performPostProcessing(byte *screen) {
_vm->_sound->playCompSpeech(text->_speechId, 16, 0);
}
if (frame < text->_endFrame) {
- drawTextObject(_currentMovieText, screen);
+ drawTextObject(_currentMovieText, screen, pitch);
} else {
- closeTextObject(_currentMovieText, screen);
+ closeTextObject(_currentMovieText, screen, pitch);
_currentMovieText++;
}
}
@@ -313,7 +319,7 @@ bool MoviePlayer::playVideo() {
}
Graphics::Surface *screen = _vm->_system->lockScreen();
- performPostProcessing((byte *)screen->pixels);
+ performPostProcessing((byte *)screen->pixels, screen->pitch);
_vm->_system->unlockScreen();
_vm->_system->updateScreen();
}
diff --git a/engines/sword2/animation.h b/engines/sword2/animation.h
index 550ac0fac4..afe7dfcc68 100644
--- a/engines/sword2/animation.h
+++ b/engines/sword2/animation.h
@@ -96,12 +96,12 @@ protected:
uint32 _leadOut;
int _leadOutFrame;
- void performPostProcessing(byte *screen);
+ void performPostProcessing(byte *screen, uint16 pitch);
bool playVideo();
void openTextObject(uint32 index);
- void closeTextObject(uint32 index, byte *screen);
- void drawTextObject(uint32 index, byte *screen);
+ void closeTextObject(uint32 index, byte *screen, uint16 pitch);
+ void drawTextObject(uint32 index, byte *screen, uint16 pitch);
byte findBlackPalIndex();
byte findWhitePalIndex();
diff --git a/engines/testbed/graphics.cpp b/engines/testbed/graphics.cpp
index a0e2754fe4..3cdff5f432 100644
--- a/engines/testbed/graphics.cpp
+++ b/engines/testbed/graphics.cpp
@@ -229,16 +229,16 @@ void rotatePalette(byte *palette, int size) {
// Rotate the colors starting from address palette "size" times
// take a temporary palette color
- byte tColor[4] = {0};
+ byte tColor[3] = {0};
// save first color in it.
- memcpy(tColor, &palette[0], 4 * sizeof(byte));
+ memcpy(tColor, &palette[0], 3 * sizeof(byte));
// Move each color upward by 1
for (int i = 0; i < size - 1; i++) {
- memcpy(&palette[i * 4], &palette[(i + 1) * 4], 4 * sizeof(byte));
+ memcpy(&palette[i * 3], &palette[(i + 1) * 3], 3 * sizeof(byte));
}
// Assign last color to tcolor
- memcpy(&palette[(size - 1) * 4], tColor, 4 * sizeof(byte));
+ memcpy(&palette[(size - 1) * 3], tColor, 3 * sizeof(byte));
}
/**
diff --git a/engines/tinsel/music.cpp b/engines/tinsel/music.cpp
index 29d4dbc92d..7461cfca72 100644
--- a/engines/tinsel/music.cpp
+++ b/engines/tinsel/music.cpp
@@ -115,26 +115,6 @@ static const int enhancedAudioSCNVersion[] = {
97, 98, 99, 99 // 151-154
};
-// TODO. This mapping is wrong
-static const int enhancedAudioSCNVersionALT[] = {
- 301, 302, 2, 1, 1, 301, 302, 3, 3, 4, // 1-10
- 4, 5, 6, 1, 7, 8, 9, 10, 8, 11, // 11-20
- 11, 12, 13, 13, 13, 13, 13, 14, 13, 13, // 21-30
- 15, 16, 17, 15, 18, 19, 20, 338, 21, 21, // 31-40
- 341, 342, 22, 22, 23, 24, 25, 26, 27, 28, // 41-50
- 29, 30, 31, 32, 33, 34, 35, 35, 36, 37, // 51-60
- 38, 39, 39, 39, 39, 40, 39, 41, 41, 42, // 61-70
- 43, 42, 44, 45, 41, 46, 48, 47, 48, 49, // 71-80
- 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, // 81-90
- 60, 61, 62, 63, 61, 64, 65, 66, 67, 68, // 91-100
- 69, 70, 68, 71, 72, 73, 74, 75, 12, 76, // 101-110
- 77, 78, 79, 80, 4, 4, 82, 83, 77, 4, // 111-120
- 84, 85, 86, 3124, 88, 89, 90, 88, 2, 2, // 121-130
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 131-140
- 3142, 91, 92, 93, 94, 94, 95, 96, 52, 4, // 141-150
- 97, 98, 99 // 151-153
-};
-
int GetTrackNumber(SCNHANDLE hMidi) {
for (int i = 0; i < ARRAYSIZE(midiOffsets); i++)
if (midiOffsets[i] == hMidi)
@@ -179,11 +159,13 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) {
// Support for external music from the music enhancement project
if (_vm->getFeatures() & GF_ENHANCED_AUDIO_SUPPORT) {
int trackNumber = GetTrackNumber(dwFileOffset);
+ // Track 8 has been removed in the German CD re-release "Neon Edition"
+ if ((_vm->getFeatures() & GF_ALT_MIDI) && trackNumber >= 8)
+ trackNumber++;
+
int track = 0;
if (trackNumber >= 0) {
- if (_vm->getFeatures() & GF_ALT_MIDI)
- track = enhancedAudioSCNVersionALT[trackNumber];
- else if (_vm->getFeatures() & GF_SCNFILES)
+ if (_vm->getFeatures() & GF_SCNFILES)
track = enhancedAudioSCNVersion[trackNumber];
else
track = enhancedAudioGRAVersion[trackNumber];
diff --git a/engines/toon/movie.cpp b/engines/toon/movie.cpp
index c6b57d96e2..bf4b6639fa 100644
--- a/engines/toon/movie.cpp
+++ b/engines/toon/movie.cpp
@@ -38,8 +38,8 @@ void ToonstruckSmackerDecoder::handleAudioTrack(byte track, uint32 chunkSize, ui
Video::SmackerDecoder::handleAudioTrack(track, chunkSize, unpackedSize);
}
-bool ToonstruckSmackerDecoder::loadFile(const Common::String &filename, int forcedflags) {
- debugC(1, kDebugMovie, "loadFile(%s, %d)", filename.c_str(), forcedflags);
+bool ToonstruckSmackerDecoder::loadFile(const Common::String &filename) {
+ debugC(1, kDebugMovie, "loadFile(%s)", filename.c_str());
_lowRes = false;
@@ -88,7 +88,7 @@ void Movie::play(Common::String video, int32 flags) {
_playing = true;
if (flags & 1)
_vm->getAudioManager()->setMusicVolume(0);
- _decoder->loadFile(video.c_str(), flags);
+ _decoder->loadFile(video.c_str());
playVideo(isFirstIntroVideo);
_vm->flushPalette(false);
if (flags & 1)
diff --git a/engines/toon/movie.h b/engines/toon/movie.h
index 2a9173850f..bed2ceceae 100644
--- a/engines/toon/movie.h
+++ b/engines/toon/movie.h
@@ -36,7 +36,7 @@ public:
ToonstruckSmackerDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType = Audio::Mixer::kSFXSoundType);
virtual ~ToonstruckSmackerDecoder() {}
void handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize);
- bool loadFile(const Common::String &filename, int forcedflags);
+ bool loadFile(const Common::String &filename);
bool isLowRes() { return _lowRes; }
protected:
bool _lowRes;
diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp
index ee83ca620c..026370abf1 100644
--- a/gui/ThemeEngine.cpp
+++ b/gui/ThemeEngine.cpp
@@ -1193,7 +1193,7 @@ void ThemeEngine::debugWidgetPosition(const char *name, const Common::Rect &r) {
/**********************************************************
* Screen/overlay management
*********************************************************/
-void ThemeEngine::updateScreen() {
+void ThemeEngine::updateScreen(bool render) {
if (!_bufferQueue.empty()) {
_vectorRenderer->setSurface(&_backBuffer);
@@ -1218,7 +1218,8 @@ void ThemeEngine::updateScreen() {
_screenQueue.clear();
}
- renderDirtyScreen();
+ if (render)
+ renderDirtyScreen();
}
void ThemeEngine::addDirtyRect(Common::Rect r) {
diff --git a/gui/ThemeEngine.h b/gui/ThemeEngine.h
index e852760e44..78ea06f3b6 100644
--- a/gui/ThemeEngine.h
+++ b/gui/ThemeEngine.h
@@ -32,7 +32,7 @@
#include "graphics/surface.h"
#include "graphics/font.h"
-#define SCUMMVM_THEME_VERSION_STR "SCUMMVM_STX0.8.2"
+#define SCUMMVM_THEME_VERSION_STR "SCUMMVM_STX0.8.3"
namespace Graphics {
struct DrawStep;
@@ -280,7 +280,7 @@ public:
* It processes all the drawing queues and then copies dirty rects
* in the current Screen surface to the overlay.
*/
- void updateScreen();
+ void updateScreen(bool render = true);
/** @name FONT MANAGEMENT METHODS */
diff --git a/gui/ThemeParser.cpp b/gui/ThemeParser.cpp
index 70d81a962e..e3523d11e8 100644
--- a/gui/ThemeParser.cpp
+++ b/gui/ThemeParser.cpp
@@ -861,30 +861,50 @@ bool ThemeParser::resolutionCheck(const Common::String &resolution) {
return true;
Common::StringTokenizer globTokenizer(resolution, ", ");
- Common::String cur, w, h;
- bool definedRes = false;
+ Common::String cur;
while (!globTokenizer.empty()) {
- bool ignore = false;
cur = globTokenizer.nextToken();
- if (cur[0] == '-') {
- ignore = true;
- cur.deleteChar(0);
+ bool lt;
+ int val;
+
+ if (cur.size() < 5) {
+ warning("Invalid theme 'resolution' token '%s'", resolution.c_str());
+ return false;
+ }
+
+ if (cur[0] == 'x') {
+ val = g_system->getOverlayWidth();
+ } else if (cur[0] == 'y') {
+ val = g_system->getOverlayHeight();
+ } else {
+ warning("Error parsing theme 'resolution' token '%s'", resolution.c_str());
+ return false;
+ }
+
+ if (cur[1] == '<') {
+ lt = true;
+ } else if (cur[1] == '>') {
+ lt = false;
} else {
- definedRes = true;
+ warning("Error parsing theme 'resolution' token '%s'", resolution.c_str());
+ return false;
}
- Common::StringTokenizer resTokenizer(cur, "x");
- w = resTokenizer.nextToken();
- h = resTokenizer.nextToken();
+ int token = atoi(cur.c_str() + 2);
- if ((w == "X" || atoi(w.c_str()) == g_system->getOverlayWidth()) &&
- (h == "Y" || atoi(h.c_str()) == g_system->getOverlayHeight()))
- return !ignore;
+ // check inverse for unfulfilled requirements
+ if (lt) {
+ if (val >= token)
+ return false;
+ } else {
+ if (val <= token)
+ return false;
+ }
}
- return !definedRes;
+ return true;
}
} // End of namespace GUI
diff --git a/gui/credits.h b/gui/credits.h
index 926adf4c9c..a75af3c0dd 100644
--- a/gui/credits.h
+++ b/gui/credits.h
@@ -456,7 +456,7 @@ static const char *credits[] = {
"C0""Martin Doucha",
"C2""CinE engine objectification",
"C0""Thomas Fach-Pedersen",
-"C2""ProTracker module player",
+"C2""ProTracker module player, Smacker video decoder",
"C0""Tobias Gunkel",
"C2""Sound support for C64 version of MM/Zak, Loom PCE support",
"C0""Janne Huttunen",
diff --git a/gui/gui-manager.cpp b/gui/gui-manager.cpp
index 7644cbe7b2..3ad4b2ee18 100644
--- a/gui/gui-manager.cpp
+++ b/gui/gui-manager.cpp
@@ -201,14 +201,15 @@ void GuiManager::redraw() {
_theme->clearAll();
_theme->openDialog(true, ThemeEngine::kShadingNone);
- for (i = 0; i < _dialogStack.size() - 1; i++) {
+ for (i = 0; i < _dialogStack.size() - 1; i++)
_dialogStack[i]->drawDialog();
- }
_theme->finishBuffering();
+ // fall through
+
case kRedrawOpenDialog:
- _theme->updateScreen();
+ _theme->updateScreen(false);
_theme->openDialog(true, shading);
_dialogStack.top()->drawDialog();
_theme->finishBuffering();
diff --git a/gui/themes/default.inc b/gui/themes/default.inc
index 3236071055..2716e6ca72 100644
--- a/gui/themes/default.inc
+++ b/gui/themes/default.inc
@@ -1,5 +1,5 @@
"<?xml version = '1.0'?>"
-"<layout_info resolution='-320xY,-256x240,-Xx272,-544x332,-Xx350'> "
+"<layout_info resolution='y>399'> "
"<globals> "
"<def var='Line.Height' value='16' /> "
"<def var='Font.Height' value='16' /> "
@@ -789,7 +789,7 @@
"</layout> "
"</dialog> "
"</layout_info> "
-"<layout_info resolution='320xY,256x240,Xx272,544x332,Xx350'> "
+"<layout_info resolution='y<400'> "
"<globals> "
"<def var='Line.Height' value='12' /> "
"<def var='Font.Height' value='10' /> "
@@ -1602,21 +1602,21 @@
"<font id='text_default' "
"file='helvb12.bdf' "
"/> "
-"<font resolution='320xY,256x240' "
+"<font resolution='y<400' "
"id='text_default' "
"file='clR6x12.bdf' "
"/> "
"<font id='text_button' "
"file='helvb12.bdf' "
"/> "
-"<font resolution='320xY,256x240' "
+"<font resolution='y<400' "
"id='text_button' "
"file='clR6x12.bdf' "
"/> "
"<font id='text_normal' "
"file='helvb12.bdf' "
"/> "
-"<font resolution='320xY,256x240' "
+"<font resolution='y<400' "
"id='text_normal' "
"file='clR6x12.bdf' "
"/> "
diff --git a/gui/themes/scummclassic.zip b/gui/themes/scummclassic.zip
index 9fd2c187fd..4dbedd4f14 100644
--- a/gui/themes/scummclassic.zip
+++ b/gui/themes/scummclassic.zip
Binary files differ
diff --git a/gui/themes/scummclassic/THEMERC b/gui/themes/scummclassic/THEMERC
index f0276969fe..17e934d5ef 100644
--- a/gui/themes/scummclassic/THEMERC
+++ b/gui/themes/scummclassic/THEMERC
@@ -1 +1 @@
-[SCUMMVM_STX0.8.2:ScummVM Classic Theme:No Author]
+[SCUMMVM_STX0.8.3:ScummVM Classic Theme:No Author]
diff --git a/gui/themes/scummclassic/classic_gfx.stx b/gui/themes/scummclassic/classic_gfx.stx
index d672db2540..3fd00abbb9 100644
--- a/gui/themes/scummclassic/classic_gfx.stx
+++ b/gui/themes/scummclassic/classic_gfx.stx
@@ -46,21 +46,21 @@
<font id = 'text_default'
file = 'helvb12.bdf'
/>
- <font resolution = '320xY, 256x240'
+ <font resolution = 'y<400'
id = 'text_default'
file = 'clR6x12.bdf'
/>
<font id = 'text_button'
file = 'helvb12.bdf'
/>
- <font resolution = '320xY, 256x240'
+ <font resolution = 'y<400'
id = 'text_button'
file = 'clR6x12.bdf'
/>
<font id = 'text_normal'
file = 'helvb12.bdf'
/>
- <font resolution = '320xY, 256x240'
+ <font resolution = 'y<400'
id = 'text_normal'
file = 'clR6x12.bdf'
/>
diff --git a/gui/themes/scummclassic/classic_layout.stx b/gui/themes/scummclassic/classic_layout.stx
index 416ffb30eb..f09c29e360 100644
--- a/gui/themes/scummclassic/classic_layout.stx
+++ b/gui/themes/scummclassic/classic_layout.stx
@@ -23,7 +23,7 @@
- $Id$
-
-->
-<layout_info resolution = '-320xY, -256x240, -Xx272, -544x332, -Xx350'>
+<layout_info resolution = 'y>399'>
<globals>
<def var = 'Line.Height' value = '16' />
<def var = 'Font.Height' value = '16' />
diff --git a/gui/themes/scummclassic/classic_layout_lowres.stx b/gui/themes/scummclassic/classic_layout_lowres.stx
index fe0eb66b8d..a440be7694 100644
--- a/gui/themes/scummclassic/classic_layout_lowres.stx
+++ b/gui/themes/scummclassic/classic_layout_lowres.stx
@@ -23,7 +23,7 @@
- $Id$
-
-->
-<layout_info resolution = "320xY, 256x240, Xx272, 544x332, Xx350">
+<layout_info resolution = 'y<400'>
<globals>
<def var = 'Line.Height' value = '12' />
<def var = 'Font.Height' value = '10' />
diff --git a/gui/themes/scummmodern.zip b/gui/themes/scummmodern.zip
index f4e18ef00c..77951475e6 100644
--- a/gui/themes/scummmodern.zip
+++ b/gui/themes/scummmodern.zip
Binary files differ
diff --git a/gui/themes/scummmodern/THEMERC b/gui/themes/scummmodern/THEMERC
index b8f41fc207..f947a5685a 100644
--- a/gui/themes/scummmodern/THEMERC
+++ b/gui/themes/scummmodern/THEMERC
@@ -1 +1 @@
-[SCUMMVM_STX0.8.2:ScummVM Modern Theme:No Author]
+[SCUMMVM_STX0.8.3:ScummVM Modern Theme:No Author]
diff --git a/gui/themes/scummmodern/scummmodern_gfx.stx b/gui/themes/scummmodern/scummmodern_gfx.stx
index cfe00a7016..a325d4982b 100644
--- a/gui/themes/scummmodern/scummmodern_gfx.stx
+++ b/gui/themes/scummmodern/scummmodern_gfx.stx
@@ -108,21 +108,21 @@
<font id = 'text_default'
file = 'helvb12.bdf'
/>
- <font resolution = '320xY, 256x240'
+ <font resolution = 'y<400'
id = 'text_default'
file = 'clR6x12.bdf'
/>
<font id = 'text_button'
file = 'helvb12.bdf'
/>
- <font resolution = '320xY, 256x240'
+ <font resolution = 'y<400'
id = 'text_button'
file = 'clR6x12.bdf'
/>
<font id = 'text_normal'
file = 'helvb12.bdf'
/>
- <font resolution = '320xY, 256x240'
+ <font resolution = 'y<400'
id = 'text_normal'
file = 'clR6x12.bdf'
/>
@@ -178,7 +178,7 @@
<!-- <defaults fill = 'gradient' fg_color = 'white'/> -->
<cursor file = 'cursor.bmp' hotspot = '0, 0' scale = '3'/>
- <cursor resolution = '320xY, 256x240' file = 'cursor_small.bmp' hotspot = '0, 0' scale = '3'/>
+ <cursor resolution = 'y<400' file = 'cursor_small.bmp' hotspot = '0, 0' scale = '3'/>
<!-- Selection (text or list items) -->
<drawdata id = 'text_selection' cache = 'false'>
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index 879be2aafe..32d6d19d1a 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -23,7 +23,7 @@
- $Id$
-
-->
-<layout_info resolution = '-320xY, -256x240, -Xx272, -544x332, -Xx350'>
+<layout_info resolution = 'y>399'>
<globals>
<def var = 'Line.Height' value = '16' />
<def var = 'Font.Height' value = '16' />
diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
index 3a7a4b63b9..06916a80f1 100644
--- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx
+++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
@@ -23,7 +23,7 @@
- $Id$
-
-->
-<layout_info resolution = "320xY, 256x240, Xx272, 544x332, Xx350">
+<layout_info resolution = 'y<400'>
<globals>
<def var = 'Line.Height' value = '12' />
<def var = 'Font.Height' value = '10' />
diff --git a/tools/credits.pl b/tools/credits.pl
index b72d38bcd1..0d5b3326e6 100755
--- a/tools/credits.pl
+++ b/tools/credits.pl
@@ -958,7 +958,7 @@ begin_credits("Credits");
add_person("Stuart Caie", "", "Decoders for Amiga and AtariST data files (AGOS engine)");
add_person("Paolo Costabel", "", "PSP port contributions");
add_person("Martin Doucha", "next_ghost", "CinE engine objectification");
- add_person("Thomas Fach-Pedersen", "madmoose", "ProTracker module player");
+ add_person("Thomas Fach-Pedersen", "madmoose", "ProTracker module player, Smacker video decoder");
add_person("Tobias Gunkel", "hennymcc", "Sound support for C64 version of MM/Zak, Loom PCE support");
add_person("Janne Huttunen", "", "V3 actor mask support, Dig/FT SMUSH audio");
add_person("Kov&aacute;cs Endre J&aacute;nos", "", "Several fixes for Simon1");