aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlejandro Marzini2010-06-08 03:31:27 +0000
committerAlejandro Marzini2010-06-08 03:31:27 +0000
commit7ea78b10364d34ae607a9a1da00e4d42ad691aa1 (patch)
treec16c3d224b2ad1179dbf3e7c54b5c0212b0581a4
parent7e9d54a69a3444f5335b8cf6ecabdeffe2830644 (diff)
parentea2e2053f25c216342c74bb7a74dabc682766720 (diff)
downloadscummvm-rg350-7ea78b10364d34ae607a9a1da00e4d42ad691aa1.tar.gz
scummvm-rg350-7ea78b10364d34ae607a9a1da00e4d42ad691aa1.tar.bz2
scummvm-rg350-7ea78b10364d34ae607a9a1da00e4d42ad691aa1.zip
Merged from trunk.
svn-id: r49499
-rw-r--r--AUTHORS3
-rw-r--r--COPYRIGHT1
-rw-r--r--NEWS18
-rw-r--r--README21
-rw-r--r--backends/fs/psp/psp-stream.cpp294
-rw-r--r--backends/fs/psp/psp-stream.h32
-rw-r--r--backends/graphics/sdl/sdl-graphics.cpp1
-rw-r--r--backends/platform/android/README.build84
-rw-r--r--backends/platform/android/android.cpp1413
-rw-r--r--backends/platform/android/android.mk52
-rw-r--r--backends/platform/android/asset-archive.cpp414
-rw-r--r--backends/platform/android/asset-archive.h53
-rw-r--r--backends/platform/android/module.mk85
-rw-r--r--backends/platform/android/org/inodes/gus/scummvm/EditableSurfaceView.java59
-rw-r--r--backends/platform/android/org/inodes/gus/scummvm/Event.java330
-rw-r--r--backends/platform/android/org/inodes/gus/scummvm/PluginProvider.java52
-rw-r--r--backends/platform/android/org/inodes/gus/scummvm/ScummVM.java317
-rw-r--r--backends/platform/android/org/inodes/gus/scummvm/ScummVMActivity.java446
-rw-r--r--backends/platform/android/org/inodes/gus/scummvm/ScummVMApplication.java29
-rw-r--r--backends/platform/android/org/inodes/gus/scummvm/Unpacker.java370
-rw-r--r--backends/platform/android/scummvm-android-themeengine.patch135
-rw-r--r--backends/platform/ds/arm7/source/main.cpp4
-rw-r--r--backends/platform/ds/arm9/dist/readme_ds.txt6
-rw-r--r--backends/platform/ds/arm9/makefile2
-rw-r--r--backends/platform/ds/arm9/source/dsmain.cpp7
-rw-r--r--backends/platform/ds/commoninclude/NDS/scummvm_ipc.h12
-rw-r--r--backends/platform/gp2x/gp2x-common.h31
-rw-r--r--backends/platform/gp2x/gp2x.cpp38
-rw-r--r--backends/platform/gp2x/graphics.cpp142
-rwxr-xr-xbackends/platform/gp2xwiz/build/build.sh4
-rwxr-xr-xbackends/platform/gp2xwiz/build/bundle.sh6
-rwxr-xr-xbackends/platform/gp2xwiz/build/clean.sh2
-rwxr-xr-xbackends/platform/gp2xwiz/build/config.sh2
-rw-r--r--backends/platform/gp2xwiz/gp2xwiz-events.cpp1
-rw-r--r--backends/platform/gp2xwiz/gp2xwiz-graphics.cpp10
-rw-r--r--backends/platform/iphone/iphone_common.h1
-rw-r--r--backends/platform/iphone/iphone_video.h2
-rw-r--r--backends/platform/iphone/iphone_video.m6
-rw-r--r--backends/platform/iphone/osys_main.cpp3
-rw-r--r--backends/platform/iphone/osys_video.cpp11
-rw-r--r--backends/platform/linuxmoto/linuxmoto-graphics.cpp10
-rw-r--r--backends/platform/ps2/systemps2.cpp17
-rw-r--r--backends/platform/ps2/systemps2.h6
-rw-r--r--backends/platform/psp/Makefile6
-rw-r--r--backends/platform/psp/display_client.cpp15
-rw-r--r--backends/platform/psp/module.mk3
-rw-r--r--backends/platform/psp/mp3.cpp487
-rw-r--r--backends/platform/psp/mp3.h121
-rw-r--r--backends/platform/psp/osys_psp.cpp3
-rw-r--r--backends/platform/psp/osys_psp.h2
-rw-r--r--backends/platform/psp/psp.spec2
-rw-r--r--backends/platform/psp/thread.cpp41
-rw-r--r--backends/platform/psp/thread.h16
-rw-r--r--backends/platform/samsungtv/samsungtv.cpp9
-rw-r--r--backends/platform/symbian/src/SymbianOS.cpp1
-rw-r--r--backends/platform/wince/wince-sdl.cpp61
-rw-r--r--base/commandLine.cpp4
-rw-r--r--common/str.h6
-rw-r--r--common/stream.h2
-rw-r--r--common/system.h12
-rw-r--r--common/textconsole.cpp12
-rw-r--r--common/unzip.cpp4
-rwxr-xr-xconfigure43
-rw-r--r--dists/android/mkmanifest.pl169
-rw-r--r--dists/android/res/drawable/gradient.xml7
-rw-r--r--dists/android/res/layout/main.xml10
-rw-r--r--dists/android/res/layout/splash.xml19
-rw-r--r--dists/android/res/values/strings.xml22
-rwxr-xr-xdists/iphone/scummvm.xcodeproj/project.pbxproj104
-rw-r--r--dists/os2/readme.os213
-rw-r--r--dists/os2/scummvm.icobin0 -> 3428 bytes
-rw-r--r--engines/dialogs.cpp45
-rw-r--r--engines/dialogs.h43
-rw-r--r--engines/groovie/cell.h6
-rw-r--r--engines/groovie/cursor.h3
-rw-r--r--engines/groovie/debug.cpp6
-rw-r--r--engines/groovie/debug.h4
-rw-r--r--engines/groovie/detection.cpp4
-rw-r--r--engines/groovie/font.cpp157
-rw-r--r--engines/groovie/font.h36
-rw-r--r--engines/groovie/graphics.cpp3
-rw-r--r--engines/groovie/graphics.h2
-rw-r--r--engines/groovie/groovie.cpp33
-rw-r--r--engines/groovie/groovie.h19
-rw-r--r--engines/groovie/music.cpp3
-rw-r--r--engines/groovie/music.h10
-rw-r--r--engines/groovie/player.cpp2
-rw-r--r--engines/groovie/resource.cpp2
-rw-r--r--engines/groovie/roq.cpp3
-rw-r--r--engines/groovie/script.cpp62
-rw-r--r--engines/groovie/script.h15
-rw-r--r--engines/groovie/vdx.cpp3
-rw-r--r--engines/groovie/vdx.h4
-rw-r--r--engines/m4/animation.cpp546
-rw-r--r--engines/m4/animation.h109
-rw-r--r--engines/m4/assets.cpp36
-rw-r--r--engines/m4/assets.h8
-rw-r--r--engines/m4/compression.h4
-rw-r--r--engines/m4/console.cpp56
-rw-r--r--engines/m4/console.h3
-rw-r--r--engines/m4/converse.cpp8
-rw-r--r--engines/m4/dialogs.cpp26
-rw-r--r--engines/m4/font.cpp57
-rw-r--r--engines/m4/font.h48
-rw-r--r--engines/m4/graphics.cpp104
-rw-r--r--engines/m4/graphics.h18
-rw-r--r--engines/m4/gui.cpp30
-rw-r--r--engines/m4/m4.cpp18
-rw-r--r--engines/m4/m4.h3
-rw-r--r--engines/m4/m4_views.cpp2
-rw-r--r--engines/m4/mads_anim.cpp149
-rw-r--r--engines/m4/mads_anim.h39
-rw-r--r--engines/m4/mads_logic.cpp18
-rw-r--r--engines/m4/mads_logic.h2
-rw-r--r--engines/m4/mads_menus.cpp52
-rw-r--r--engines/m4/mads_scene.cpp590
-rw-r--r--engines/m4/mads_scene.h69
-rw-r--r--engines/m4/mads_views.cpp697
-rw-r--r--engines/m4/mads_views.h132
-rw-r--r--engines/m4/viewmgr.h13
-rw-r--r--engines/mohawk/detection.cpp36
-rw-r--r--engines/mohawk/riven.cpp16
-rw-r--r--engines/mohawk/riven.h8
-rw-r--r--engines/mohawk/riven_external.cpp56
-rw-r--r--engines/mohawk/riven_external.h2
-rw-r--r--engines/mohawk/riven_scripts.cpp16
-rw-r--r--engines/mohawk/video.cpp58
-rw-r--r--engines/mohawk/video.h11
-rw-r--r--engines/parallaction/parser_ns.cpp2
-rw-r--r--engines/saga/saga.cpp14
-rw-r--r--engines/sci/console.cpp134
-rw-r--r--engines/sci/console.h1
-rw-r--r--engines/sci/detection.cpp53
-rw-r--r--engines/sci/engine/features.cpp88
-rw-r--r--engines/sci/engine/game.cpp34
-rw-r--r--engines/sci/engine/kernel.cpp8
-rw-r--r--engines/sci/engine/kernel.h1
-rw-r--r--engines/sci/engine/kernel32.cpp95
-rw-r--r--engines/sci/engine/kevent.cpp58
-rw-r--r--engines/sci/engine/kfile.cpp132
-rw-r--r--engines/sci/engine/kgraphics.cpp109
-rw-r--r--engines/sci/engine/klists.cpp52
-rw-r--r--engines/sci/engine/kmisc.cpp9
-rw-r--r--engines/sci/engine/kmovement.cpp138
-rw-r--r--engines/sci/engine/kparse.cpp40
-rw-r--r--engines/sci/engine/kpathing.cpp62
-rw-r--r--engines/sci/engine/kscripts.cpp36
-rw-r--r--engines/sci/engine/kstring.cpp36
-rw-r--r--engines/sci/engine/savegame.cpp203
-rw-r--r--engines/sci/engine/savegame.h2
-rw-r--r--engines/sci/engine/script.cpp277
-rw-r--r--engines/sci/engine/scriptdebug.cpp86
-rw-r--r--engines/sci/engine/seg_manager.cpp46
-rw-r--r--engines/sci/engine/seg_manager.h44
-rw-r--r--engines/sci/engine/segment.cpp425
-rw-r--r--engines/sci/engine/segment.h238
-rw-r--r--engines/sci/engine/selector.cpp43
-rw-r--r--engines/sci/engine/selector.h31
-rw-r--r--engines/sci/engine/state.cpp61
-rw-r--r--engines/sci/engine/state.h33
-rw-r--r--engines/sci/engine/vm.cpp408
-rw-r--r--engines/sci/engine/vm.h77
-rw-r--r--engines/sci/event.cpp3
-rw-r--r--engines/sci/graphics/animate.cpp76
-rw-r--r--engines/sci/graphics/compare.cpp68
-rw-r--r--engines/sci/graphics/controls.cpp18
-rw-r--r--engines/sci/graphics/coordadjuster.cpp16
-rw-r--r--engines/sci/graphics/frameout.cpp54
-rw-r--r--engines/sci/graphics/maciconbar.cpp2
-rw-r--r--engines/sci/graphics/menu.cpp8
-rw-r--r--engines/sci/graphics/paint16.cpp3
-rw-r--r--engines/sci/graphics/palette.cpp2
-rw-r--r--engines/sci/graphics/picture.cpp51
-rw-r--r--engines/sci/graphics/picture.h9
-rw-r--r--engines/sci/module.mk1
-rw-r--r--engines/sci/parser/said.cpp7
-rw-r--r--engines/sci/parser/said.y7
-rw-r--r--engines/sci/resource.cpp1112
-rw-r--r--engines/sci/resource.h49
-rw-r--r--engines/sci/resource_audio.cpp711
-rw-r--r--engines/sci/sci.cpp13
-rw-r--r--engines/sci/sound/audio.cpp12
-rw-r--r--engines/sci/sound/midiparser_sci.cpp26
-rw-r--r--engines/sci/sound/midiparser_sci.h13
-rw-r--r--engines/sci/sound/music.cpp68
-rw-r--r--engines/sci/sound/music.h11
-rw-r--r--engines/sci/sound/soundcmd.cpp221
-rw-r--r--engines/scumm/debugger.cpp2
-rw-r--r--engines/scumm/dialogs.cpp213
-rw-r--r--engines/scumm/dialogs.h24
-rw-r--r--engines/scumm/he/resource_he.cpp4
-rw-r--r--engines/scumm/imuse/instrument.cpp6
-rw-r--r--engines/scumm/input.cpp2
-rw-r--r--engines/scumm/scumm.cpp18
-rw-r--r--engines/scumm/scumm.h2
-rw-r--r--engines/tinsel/handle.cpp3
-rw-r--r--engines/tinsel/saveload.cpp3
-rw-r--r--engines/tucker/sequences.cpp67
-rw-r--r--engines/tucker/tucker.h5
-rw-r--r--graphics/font.h6
-rw-r--r--graphics/video/avi_decoder.cpp21
-rw-r--r--graphics/video/qt_decoder.cpp73
-rw-r--r--graphics/video/qt_decoder.h3
-rw-r--r--graphics/video/smk_decoder.cpp36
-rw-r--r--graphics/video/smk_decoder.h10
-rw-r--r--gui/about.cpp35
-rw-r--r--gui/credits.h3
-rw-r--r--gui/editable.cpp2
-rw-r--r--gui/themes/default.inc1383
-rw-r--r--gui/themes/scummclassic.zipbin54108 -> 52242 bytes
-rw-r--r--gui/themes/scummclassic/classic_layout.stx35
-rw-r--r--gui/themes/scummclassic/classic_layout_lowres.stx44
-rw-r--r--gui/themes/scummmodern.zipbin159866 -> 158233 bytes
-rw-r--r--gui/themes/scummmodern/scummmodern_layout.stx35
-rw-r--r--gui/themes/scummmodern/scummmodern_layout_lowres.stx37
-rw-r--r--ports.mk8
-rw-r--r--sound/decoders/adpcm.cpp4
-rw-r--r--sound/decoders/mp3.cpp15
-rw-r--r--sound/decoders/voc.cpp2
-rw-r--r--sound/decoders/vorbis.cpp2
-rw-r--r--sound/midiparser.cpp4
-rw-r--r--sound/mods/rjp1.cpp2
-rw-r--r--test/common/str.h24
-rw-r--r--tools/create_msvc/create_msvc.cpp3
-rwxr-xr-xtools/credits.pl4
-rwxr-xr-xtools/update-version.pl1
226 files changed, 11295 insertions, 5714 deletions
diff --git a/AUTHORS b/AUTHORS
index 7a8490ddf7..28d71b3698 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -166,6 +166,9 @@ ScummVM Team
Backend Teams
-------------
+ Android:
+ Angus Lees
+
Dreamcast:
Marcus Comstedt
diff --git a/COPYRIGHT b/COPYRIGHT
index 980ca32463..cc522d2215 100644
--- a/COPYRIGHT
+++ b/COPYRIGHT
@@ -48,6 +48,7 @@ Martin Kiewitz
Pawel Kolodziejski
Mutwin Kraus
Andrew Kurushin
+Angus Lees
Claudio Matsuoka
Thomas Mayer
Neil Millstone
diff --git a/NEWS b/NEWS
index 5d48d8e4e4..5796a7f885 100644
--- a/NEWS
+++ b/NEWS
@@ -2,12 +2,22 @@ For a more comprehensive changelog for the latest experimental SVN code, see:
http://scummvm.svn.sourceforge.net/viewvc/scummvm/?view=log
1.2.0 (????-??-??)
- PSP port:
- - Switched to new backend design which fixes minor graphical issues,
- speeds things up, and provides 16-bit support.
+ New Ports:
+ - Added Android port.
General:
- Switched to the "fast" DOSBox OPL emulator.
+ - Fixed a crash in the rjp1 player code affecting the FOTAQ Amiga version.
+
+ Groovie:
+ - Added support for the Macintosh version of The 7th Guest.
+ - Added support for custom MT-32 instruments.
+
+ PSP port:
+ - Switched to new backend design which fixes minor graphical issues,
+ speeds things up, and provides 16-bit support.
+ - Enabled playback of MP3 files using the hardware decoder (ME). This means that
+ the port is now optimized for MP3 playback (as opposed to OGG).
1.1.2 (????-??-??)
Broken Sword 2
@@ -17,7 +27,7 @@ For a more comprehensive changelog for the latest experimental SVN code, see:
1.1.1 (2010-05-02)
New Ports:
- - Added Nintendo 64 Port. (Actually added in 1.1.0, but forgot to mention it. oops)
+ - Added Nintendo 64 port. (Actually added in 1.1.0, but forgot to mention it. oops)
General:
- Fixed several minor bugs here and there.
diff --git a/README b/README
index 043a087fab..3c97bbbbd7 100644
--- a/README
+++ b/README
@@ -81,9 +81,10 @@ You can find a thorough list with details on which games are supported
and how well on the compatibility page. ScummVM is continually
improving, so check back often.
-Among the systems on which you can play those games are Windows, Linux,
-Mac OS X, Dreamcast, PocketPC, PalmOS, AmigaOS, BeOS, OS/2, PSP, PS2,
-SymbianOS/EPOC, iPhone and many more.
+Among the systems on which you can play those games are regular desktop
+computers (running Windows, Linux, Mac OS X, ...), game consoles
+(Dreamcast, Nintendo DS & Wii, PS2, PSP, ...), smartphones (Android,
+iPhone, PocketPC, Symbian ...) and more.
At this time ScummVM should be considered beta software, and is still
under heavy development. Be aware that whilst we attempt to make sure
@@ -843,21 +844,25 @@ contact us!
Supported platforms include (but are not limited to):
- UNIX (Linux, Solaris, IRIX, *BSD)
+ UNIX (Linux, Solaris, IRIX, *BSD, ...)
Windows
Windows CE and Windows Mobile (including Smartphones and PocketPCs)
Mac OS X
AmigaOS
+ Android
BeOS
Dreamcast
- iPhone (also includes the iPod Touch)
+ GP2x
+ iPhone (also includes iPod Touch and iPad)
+ Maemo (Nokia Internet tablets 770, N800, N810, N900)
+ Nintendo 64
Nintendo DS
+ Nintendo Wii
+ OS/2
PalmOS
PlayStation 2
PlayStation Portable
- RISC OS
Symbian
- Maemo (Nokia Internet tablets 770, N800, N810, N900)
The Dreamcast port does not support The Curse of Monkey Island, nor The
Dig. The PalmOS port does not support The Curse of Monkey Island,
@@ -865,6 +870,8 @@ Beneath a Steel Sky, nor either Simon the Sorcerer 1 or 2. The Dig will
only work on some Palm devices (those with a large dynamic heap). The
Nintendo DS port does not support Full Throttle, The Dig, or The Curse
of Monkey Island.
+For more platform specific limitations, please refer to our Wiki:
+ http://wiki.scummvm.org/index.php/Platforms
In the Macintosh port, the right mouse button is emulated via Cmd-Click
(that is, you click the mouse button while holding the
diff --git a/backends/fs/psp/psp-stream.cpp b/backends/fs/psp/psp-stream.cpp
index 8cb7dfea17..83ff095aa8 100644
--- a/backends/fs/psp/psp-stream.cpp
+++ b/backends/fs/psp/psp-stream.cpp
@@ -24,6 +24,8 @@
*/
#ifdef __PSP__
+#include <pspiofilemgr_stat.h>
+#include <pspiofilemgr.h>
#include <SDL/SDL_thread.h>
#include <SDL/SDL_mutex.h>
@@ -32,28 +34,48 @@
#include <errno.h>
-//#define __PSP_PRINT_TO_FILE__
-//#define __PSP_DEBUG_FUNCS__ /* For debugging function calls */
+#define MIN2(a,b) ((a < b) ? a : b)
+#define MIN3(a,b,c) ( (a < b) ? (a < c ? a : c) : (b < c ? b : c) )
+
+//#define __PSP_PRINT_TO_FILE__ /* For debugging suspend stuff, we have no screen output */
+//#define __PSP_DEBUG_FUNCS__ /* For debugging function calls */
//#define __PSP_DEBUG_PRINT__ /* For debug printouts */
+
#include "backends/platform/psp/trace.h"
+//#define DEBUG_BUFFERS /* to see the contents of the buffers being read */
+
+#ifdef DEBUG_BUFFERS
+void printBuffer(byte *ptr, uint32 len) {
+ uint32 printLen = len <= 10 ? len : 10;
+
+ for (int i = 0; i < printLen; i++) {
+ PSP_INFO_PRINT("%x ", ptr[i]);
+ }
+
+ if (len > 10) {
+ PSP_INFO_PRINT("... ");
+ for (int i = len - 10; i < len; i++)
+ PSP_INFO_PRINT("%x ", ptr[i]);
+ }
+
+ PSP_INFO_PRINT("\n");
+}
+#endif
+
+
PSPIoStream::PSPIoStream(const Common::String &path, bool writeMode)
- : StdioStream((void *)1), _path(path), _writeMode(writeMode) {
+ : StdioStream((void *)1), _path(path), _writeMode(writeMode),
+ _ferror(false), _pos(0),
+ _physicalPos(0), _fileSize(0), _inCache(false), _eos(false),
+ _cacheStartOffset(-1), _cache(0),
+ _errorSuspend(0), _errorSource(0),
+ _errorPos(0), _errorHandle(0), _suspendCount(0) {
DEBUG_ENTER_FUNC();
- assert(!path.empty());
+ // assert(!path.empty()); // do we need this?
_handle = (void *)0; // Need to do this since base class asserts not 0.
- _ferror = false;
- _feof = false;
- _pos = 0;
-
- /* for error checking */
- _errorSuspend = 0;
- _errorSource = 0;
- _errorPos = 0;
- _errorHandle = 0;
- _suspendCount = 0;
}
PSPIoStream::~PSPIoStream() {
@@ -63,9 +85,12 @@ PSPIoStream::~PSPIoStream() {
PSP_DEBUG_PRINT_FUNC("Suspended\n");
PowerMan.unregisterSuspend(this); // Unregister with powermanager to be suspended
- // Must do this before fclose() or resume() will reopen.
+ // Must do this before fclose() or resume() will reopen.
- fclose((FILE *)_handle); // We don't need a critical section(?). Worst case, the handle gets closed on its own
+ fclose((FILE *)_handle); // We don't need a critical section. Worst case, the handle gets closed on its own
+
+ if (_cache)
+ free(_cache);
PowerMan.endCriticalSection();
}
@@ -82,6 +107,17 @@ void *PSPIoStream::open() {
_handle = fopen(_path.c_str(), _writeMode ? "wb" : "rb"); // open
+ if (_handle) {
+ // Get the file size. This way is much faster than going to the end of the file and back
+ SceIoStat stat;
+ sceIoGetstat(_path.c_str(), &stat);
+ _fileSize = *((uint32 *)(void *)&stat.st_size); // 4GB file is big enough for us
+ PSP_DEBUG_PRINT("%s filesize = %d\n", _path.c_str(), _fileSize);
+
+ // Allocate the cache
+ _cache = (char *)memalign(64, CACHE_SIZE);
+ }
+
PowerMan.registerSuspend(this); // Register with the powermanager to be suspended
PowerMan.endCriticalSection();
@@ -91,100 +127,183 @@ void *PSPIoStream::open() {
bool PSPIoStream::err() const {
DEBUG_ENTER_FUNC();
- if (_ferror)
- PSP_ERROR("mem_ferror[%d], source[%d], suspend error[%d], pos[%d], _errorPos[%d], _errorHandle[%p], suspendCount[%d]\n",
- _ferror, _errorSource, _errorSuspend, _pos, _errorPos, _errorHandle, _suspendCount);
+
+ if (_ferror) // We dump since no printing to screen with suspend
+ PSP_ERROR("mem_ferror[%d], source[%d], suspend error[%d], pos[%d], \
+ _errorPos[%d], _errorHandle[%p], suspendCount[%d]\n",
+ _ferror, _errorSource, _errorSuspend, _pos,
+ _errorPos, _errorHandle, _suspendCount);
return _ferror;
}
void PSPIoStream::clearErr() {
- _ferror = false; // Remove regular error bit
+ _ferror = false;
}
bool PSPIoStream::eos() const {
- return _feof;
+ return _eos;
}
int32 PSPIoStream::pos() const {
return _pos;
}
-
int32 PSPIoStream::size() const {
- DEBUG_ENTER_FUNC();
- if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
- PSP_DEBUG_PRINT_FUNC("Suspended\n");
-
- fseek((FILE *)_handle, 0, SEEK_END);
- int32 length = ftell((FILE *)_handle);
- fseek((FILE *)_handle, _pos, SEEK_SET);
-
- if (_pos < 0 || length < 0) { // Check for errors
- _errorSource = 2;
- PSP_ERROR("pos[%d] or length[%d] < 0!\n", _pos, length);
- _ferror = true;
- length = -1; // If our oldPos is bad, we want length to be bad too to signal
- clearerr((FILE *)_handle);
- }
-
- PowerMan.endCriticalSection();
-
- return length;
+ return _fileSize;
}
bool PSPIoStream::seek(int32 offs, int whence) {
DEBUG_ENTER_FUNC();
-
- // Check if we can access the file
- if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
- PSP_DEBUG_PRINT_FUNC("Suspended\n");
-
- int ret = fseek((FILE *)_handle, offs, whence);
-
- if (ret != 0) {
+ PSP_DEBUG_PRINT_FUNC("offset[0x%x], whence[%d], _pos[0x%x], _physPos[0x%x]\n", offs, whence, _pos, _physicalPos);
+ _eos = false;
+
+ int32 posToSearchFor = 0;
+ switch (whence) {
+ case SEEK_CUR:
+ posToSearchFor = _pos;
+ break;
+ case SEEK_END:
+ posToSearchFor = _fileSize; // unsure. Does it take us here or to EOS - 1?
+ break;
+ }
+ posToSearchFor += offs;
+
+ // Check for bad values
+ if (posToSearchFor < 0) {
_ferror = true;
- PSP_ERROR("fseek returned with [%d], non-zero\n", ret);
- clearerr((FILE *)_handle);
- _feof = feof((FILE *)_handle);
- _errorSource = 3;
- } else { // everything ok
- _feof = false; // Reset eof flag since we know it was ok
+ return false;
}
-
- _pos = ftell((FILE *)_handle); // update pos
-
- PowerMan.endCriticalSection();
-
- return (ret == 0);
+
+ if (posToSearchFor > _fileSize) {
+ _ferror = true;
+ _eos = true;
+ return false;
+ }
+
+ // See if we can find it in cache
+ if (isOffsetInCache(posToSearchFor)) {
+ PSP_DEBUG_PRINT("seek offset[0x%x] found in cache. Cache starts[0x%x]\n", posToSearchFor, _cacheStartOffset);
+ _inCache = true;
+ } else { // not in cache
+ _inCache = false;
+ }
+ _pos = posToSearchFor;
+ return true;
}
uint32 PSPIoStream::read(void *ptr, uint32 len) {
DEBUG_ENTER_FUNC();
- // Check if we can access the file
+ PSP_DEBUG_PRINT_FUNC("filename[%s], len[0x%x], ptr[%p]\n", _path.c_str(), len, ptr);
+
+ if (_ferror || _eos)
+ return 0;
+
+ byte *destPtr = (byte *)ptr;
+ uint32 lenFromFile = len; // how much we read from the actual file
+ uint32 lenFromCache = 0; // how much we read from cache
+ uint32 lenRemainingInFile = _fileSize - _pos;
+
+ if (lenFromFile > lenRemainingInFile) {
+ lenFromFile = lenRemainingInFile;
+ _eos = true;
+ }
+
+ // Are we in cache?
+ if (_inCache && isCacheValid()) {
+ uint32 offsetInCache = _pos - _cacheStartOffset;
+ // We can read at most what's in the cache or the remaining size of the file
+ lenFromCache = MIN2(lenFromFile, CACHE_SIZE - offsetInCache); // unsure
+
+ PSP_DEBUG_PRINT("reading 0x%x bytes from cache to %p. pos[0x%x] physPos[0x%x] cacheStart[0x%x]\n", lenFromCache, destPtr, _pos, _physicalPos, _cacheStartOffset);
+
+ memcpy(destPtr, &_cache[offsetInCache], lenFromCache);
+ _pos += lenFromCache;
+
+ if (lenFromCache < lenFromFile) { // there's more to copy from the file
+ lenFromFile -= lenFromCache;
+ lenRemainingInFile -= lenFromCache; // since we moved pos
+ destPtr += lenFromCache;
+ } else { // we're done
+#ifdef DEBUG_BUFFERS
+ printBuffer((byte *)ptr, len);
+#endif
+
+ return lenFromCache; // how much we actually read
+ }
+ }
+
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
PSP_DEBUG_PRINT_FUNC("Suspended\n");
-
- PSP_DEBUG_PRINT_FUNC("filename[%s], len[%d]\n", _path.c_str(), len);
-
- size_t ret = fread((byte *)ptr, 1, len, (FILE *)_handle);
-
- _pos += ret; // Update pos
-
- if (ret != len) { // Check for eof
- _feof = feof((FILE *)_handle);
- if (!_feof) { // It wasn't an eof. Must be an error
+
+
+ synchronizePhysicalPos(); // we need to update our physical position
+
+ if (lenFromFile <= MIN_READ_SIZE) { // We load the cache in case the read is small enough
+ // This optimization is based on the principle that reading 1 byte is as expensive as 1000 bytes
+ uint32 lenToCopyToCache = MIN2((uint32)MIN_READ_SIZE, lenRemainingInFile); // at most remaining file size
+
+ PSP_DEBUG_PRINT("filling cache with 0x%x bytes from physicalPos[0x%x]. cacheStart[0x%x], pos[0x%x], fileSize[0x%x]\n", lenToCopyToCache, _physicalPos, _cacheStartOffset, _pos, _fileSize);
+
+ size_t ret = fread(_cache, 1, lenToCopyToCache, (FILE *)_handle);
+ if (ret != lenToCopyToCache) {
+ PSP_ERROR("in filling cache, failed to get 0x%x bytes. Only got 0x%x\n", lenToCopyToCache, ret);
+ _ferror = true;
+ clearerr((FILE *)_handle);
+ }
+ _cacheStartOffset = _physicalPos;
+ _inCache = true;
+
+ _physicalPos += ret;
+
+ PSP_DEBUG_PRINT("copying 0x%x bytes from cache to %p\n", lenFromFile, destPtr);
+
+ // Copy to the destination buffer from cache
+ memcpy(destPtr, _cache, lenFromFile);
+ _pos += lenFromFile;
+
+ } else { // Too big for cache. No caching
+ PSP_DEBUG_PRINT("reading 0x%x bytes from file to %p. Pos[0x%x], physPos[0x%x]\n", lenFromFile, destPtr, _pos, _physicalPos);
+ size_t ret = fread(destPtr, 1, lenFromFile, (FILE *)_handle);
+
+ _physicalPos += ret; // Update pos
+ _pos = _physicalPos;
+
+ if (ret != lenFromFile) { // error
+ PSP_ERROR("fread returned [0x%x] instead of len[0x%x]\n", ret, lenFromFile);
_ferror = true;
clearerr((FILE *)_handle);
- _pos = ftell((FILE *)_handle); // Update our position
- _errorSource = 4;
- PSP_ERROR("fread returned ret[%d] instead of len[%d]\n", ret, len);
+ _errorSource = 4;
}
+ _inCache = false;
}
PowerMan.endCriticalSection();
- return ret;
+#ifdef DEBUG_BUFFERS
+ printBuffer((byte *)ptr, len);
+#endif
+
+ return lenFromCache + lenFromFile; // total of what was copied
+}
+
+// TODO: Test if seeking backwards/forwards has any effect on performance
+inline bool PSPIoStream::synchronizePhysicalPos() {
+ if (_pos != _physicalPos) {
+ if (fseek((FILE *)_handle, _pos - _physicalPos, SEEK_CUR) != 0)
+ return false;
+ _physicalPos = _pos;
+ }
+
+ return true;
+}
+
+inline bool PSPIoStream::isOffsetInCache(uint32 offset) {
+ if (_cacheStartOffset != -1 &&
+ offset >= (uint32)_cacheStartOffset &&
+ offset < (uint32)(_cacheStartOffset + CACHE_SIZE))
+ return true;
+ return false;
}
uint32 PSPIoStream::write(const void *ptr, uint32 len) {
@@ -193,18 +312,30 @@ uint32 PSPIoStream::write(const void *ptr, uint32 len) {
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
PSP_DEBUG_PRINT_FUNC("Suspended\n");
- PSP_DEBUG_PRINT_FUNC("filename[%s], len[%d]\n", _path.c_str(), len);
+ PSP_DEBUG_PRINT_FUNC("filename[%s], len[0x%x]\n", _path.c_str(), len);
+ if (_ferror)
+ return 0;
+
+ _eos = false; // we can't have eos with write
+ synchronizePhysicalPos();
+
size_t ret = fwrite(ptr, 1, len, (FILE *)_handle);
- _pos += ret;
+ // If we're making the file bigger, adjust the size
+ if (_physicalPos + (int)ret > _fileSize)
+ _fileSize = _physicalPos + ret;
+ _physicalPos += ret;
+ _pos = _physicalPos;
+ _inCache = false;
+ _cacheStartOffset = -1; // invalidate cache
if (ret != len) { // Set error
_ferror = true;
clearerr((FILE *)_handle);
_pos = ftell((FILE *)_handle); // Update pos
_errorSource = 5;
- PSP_ERROR("fwrite returned[%d] instead of len[%d]\n", ret, len);
+ PSP_ERROR("fwrite returned[0x%x] instead of len[0x%x]\n", ret, len);
}
PowerMan.endCriticalSection();
@@ -224,7 +355,7 @@ bool PSPIoStream::flush() {
_ferror = true;
clearerr((FILE *)_handle);
_errorSource = 6;
- PSP_ERROR("fflush returned ret[%u]\n", ret);
+ PSP_ERROR("fflush returned ret[%d]\n", ret);
}
PowerMan.endCriticalSection();
@@ -286,6 +417,9 @@ int PSPIoStream::resume() {
// Resume our previous position
if (_handle > 0 && _pos > 0) {
ret = fseek((FILE *)_handle, _pos, SEEK_SET);
+
+ _physicalPos = _pos;
+ _inCache = false;
if (ret != 0) { // Check for problem
_errorSuspend = ResumeError;
diff --git a/backends/fs/psp/psp-stream.h b/backends/fs/psp/psp-stream.h
index 673630b685..9fd1ad0470 100644
--- a/backends/fs/psp/psp-stream.h
+++ b/backends/fs/psp/psp-stream.h
@@ -35,25 +35,39 @@
*/
class PSPIoStream : public StdioStream, public Suspendable {
protected:
- Common::String _path; /* Need to maintain for reopening after suspend */
- bool _writeMode; /* "" */
- int _pos; /* "" */
- mutable int _ferror; /* Save file ferror */
- mutable bool _feof; /* and eof */
-
+ Common::String _path;
+ int _fileSize;
+ bool _writeMode; // for resuming in the right mode
+ int _physicalPos; // position in the real file
+ int _pos; // position. Sometimes virtual
+ bool _inCache; // whether we're in cache (virtual) mode
+ bool _eos; // EOS flag
+
enum {
SuspendError = 2,
ResumeError = 3
};
- int _errorSuspend;
+ enum {
+ CACHE_SIZE = 1024,
+ MIN_READ_SIZE = 1024 // reading less than 1024 takes exactly the same time as 1024
+ };
+
+ // For caching
+ char *_cache;
+ int _cacheStartOffset; // starting offset of the cache. -1 when cache is invalid
+
+ mutable int _ferror; // file error state
+ int _errorSuspend; // for debugging
mutable int _errorSource;
-
- // Error checking
int _errorPos;
void * _errorHandle;
int _suspendCount;
+ bool synchronizePhysicalPos(); // synchronize the physical and virtual positions
+ bool isOffsetInCache(uint32 pos); // check if an offset is found in cache
+ bool isCacheValid() { return _cacheStartOffset != -1; }
+
public:
/**
diff --git a/backends/graphics/sdl/sdl-graphics.cpp b/backends/graphics/sdl/sdl-graphics.cpp
index 295448811d..5276cbbf30 100644
--- a/backends/graphics/sdl/sdl-graphics.cpp
+++ b/backends/graphics/sdl/sdl-graphics.cpp
@@ -195,7 +195,6 @@ bool SdlGraphicsManager::hasFeature(OSystem::Feature f) {
return
(f == OSystem::kFeatureFullscreenMode) ||
(f == OSystem::kFeatureAspectRatioCorrection) ||
- (f == OSystem::kFeatureAutoComputeDirtyRects) ||
(f == OSystem::kFeatureCursorHasPalette) ||
(f == OSystem::kFeatureIconifyWindow);
}
diff --git a/backends/platform/android/README.build b/backends/platform/android/README.build
new file mode 100644
index 0000000000..fa56bfc180
--- /dev/null
+++ b/backends/platform/android/README.build
@@ -0,0 +1,84 @@
+Building the ScummVM Android port
+=================================
+
+You will need these things to build:
+1. Android EGL headers and library
+2. Android SDK
+3. An arm-android-eabi GCC toolchain
+
+In the example commands, we are going to build against the Android 1.5
+native ABI (but using the Android 1.6 SDK tools). Other version
+combinations might/should be possible with a bit of tweaking.
+
+In detail:
+
+1. Android EGL headers and library
+
+You can build these from the full Android source, but it is far easier
+to just download the 3 Android EGL headers from here:
+ http://android.git.kernel.org/?p=platform/frameworks/base.git;a=tree;f=opengl/include/EGL;hb=HEAD
+ (copy them to a directory called "EGL" somewhere)
+
+... and grab libEGL.so off an existing phone/emulator:
+ adb pull /system/lib/libEGL.so /tmp
+
+2. Android SDK
+
+Download and install somewhere.
+
+3. arm-android-eabi GCC toolchain
+
+You have several choices for toolchains:
+
+- Use Google arm-eabi prebuilt toolchain.
+
+This is shipped with both the Android source release and Android NDK.
+The problem is that "arm-eabi-gcc" can't actually link anything
+successfully without extra command line flags. To use this with the
+ScummVM configure/build environment you will need to create a family
+of shell wrapper scripts that convert "arm-android-eabi-foo" to
+"arm-eabi-foo -mandroid".
+
+For example, I use this script:
+ #!/bin/sh
+ exec arm-eabi-${0##*-} -mandroid -DANDROID "$@"
+
+... and create a family of symlinks/hardlinks pointing to it called
+arm-android-eabi-gcc, arm-android-eabi-g++, etc. For tools that don't
+take a "-mandroid" argument - like arm-eabi-strip - I bypass the shell
+wrapper and just create an arm-android-eabi-strip symlink to the tool
+directly.
+
+- Build your own arm-android-eabi toolchain from GCC source.
+
+This is lots of fun. I suggest my Android openembedded patches, see:
+ http://wiki.github.com/anguslees/openembedded-android/
+(You just need to have lots of disk space and type a few commands)
+If you get stuck, ask
+
+Alternatively, do a websearch - there are several other cross-compile
+toolchains around.
+
+
+Building ScummVM
+================
+
+ export ANDROID_SDK=<root of Android SDK>
+
+ PATH=$ANDROID_SDK/platforms/android-1.6/tools:$ANDROID_SDK/tools:$PATH
+ # You also want to ensure your arm-android-eabi toolchain is in your $PATH
+
+ export ANDROID_TOP=<root of built Android source>
+
+ EGL_INC="-I<location of EGL/ header directory>"
+ EGL_LIBS="-L<location of libEGL.so>"
+
+ CPPFLAGS="$EGL_INC" \
+ LDFLAGS="-g $EGL_LIBS" \
+ ./configure --backend=android --host=android --enable-zlib #and any other flags
+ make scummvm.apk
+
+This will build a "monolithic" ScummVM package, with the engines
+statically linked in. If you want to build separate engine packages,
+like on the market, add "--enable-plugins --default-dynamic" to
+configure and also make scummvm-engine-scumm.apk, etc.
diff --git a/backends/platform/android/android.cpp b/backends/platform/android/android.cpp
new file mode 100644
index 0000000000..a6258df554
--- /dev/null
+++ b/backends/platform/android/android.cpp
@@ -0,0 +1,1413 @@
+/* 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$
+ *
+ */
+
+#include "backends/base-backend.h"
+#include "base/main.h"
+#include "graphics/surface.h"
+
+#include "backends/platform/android/video.h"
+
+#if defined(ANDROID_BACKEND)
+
+#define ANDROID_VERSION_GE(major,minor) \
+ (ANDROID_MAJOR_VERSION > (major) || \
+ (ANDROID_MAJOR_VERSION == (major) && ANDROID_MINOR_VERSION >= (minor)))
+
+#include <jni.h>
+
+#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <EGL/egl.h>
+#include <android/log.h>
+
+#include "common/archive.h"
+#include "common/util.h"
+#include "common/rect.h"
+#include "common/queue.h"
+#include "common/mutex.h"
+#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 "sound/mixer_intern.h"
+
+#include "backends/platform/android/asset-archive.h"
+
+#undef LOG_TAG
+#define LOG_TAG "ScummVM"
+
+#if 0
+#define ENTER(args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, args)
+#else
+#define ENTER(args...) /**/
+#endif
+
+// Fix JNIEXPORT declaration to actually do something useful
+#undef JNIEXPORT
+#define JNIEXPORT __attribute__ ((visibility("default")))
+
+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;
+ bool version_unsupported =
+ cached_jvm->GetEnv((void**)&env, JNI_VERSION_1_2);
+ assert(! version_unsupported);
+ return env;
+}
+
+static void JNU_ThrowByName(JNIEnv* env, const char* name, const char* msg) {
+ jclass cls = env->FindClass(name);
+ // if cls is NULL, an exception has already been thrown
+ if (cls != NULL)
+ 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
+
+
+#if 0
+#define CHECK_GL_ERROR() checkGlError(__FILE__, __LINE__)
+static const char* getGlErrStr(GLenum error) {
+ switch (error) {
+ case GL_NO_ERROR: return "GL_NO_ERROR";
+ case GL_INVALID_ENUM: return "GL_INVALID_ENUM";
+ case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION";
+ case GL_STACK_OVERFLOW: return "GL_STACK_OVERFLOW";
+ case GL_STACK_UNDERFLOW: return "GL_STACK_UNDERFLOW";
+ case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY";
+ }
+
+ static char buf[40];
+ snprintf(buf, sizeof(buf), "(Unknown GL error code 0x%x)", error);
+ return buf;
+}
+static void checkGlError(const char* file, int line) {
+ GLenum error = glGetError();
+ if (error != GL_NO_ERROR)
+ warning("%s:%d: GL error: %s", file, line, getGlErrStr(error));
+}
+#else
+#define CHECK_GL_ERROR() do {} while (false)
+#endif
+
+class OSystem_Android : public BaseBackend {
+private:
+ jobject _back_ptr; // back pointer to (java) peer instance
+ 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;
+
+ int _screen_changeid;
+ EGLDisplay _egl_display;
+ EGLSurface _egl_surface;
+ EGLint _egl_surface_width;
+ EGLint _egl_surface_height;
+
+ bool _force_redraw;
+
+ // Game layer
+ GLESPaletteTexture* _game_texture;
+ int _shake_offset;
+ bool _full_screen_dirty;
+ Common::Array<Common::Rect> _dirty_rects;
+
+ // 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 _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;
+
+ 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 void setPalette(const byte *colors, uint start, uint num);
+ virtual void grabPalette(byte *colors, uint start, uint num);
+ 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();
+ virtual Graphics::PixelFormat getOverlayFormat() const {
+ // RGBA 4444
+ 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 addSysArchivesToSearchSet(Common::SearchSet &s, int priority = 0);
+};
+
+OSystem_Android::OSystem_Android(jobject am)
+ : _back_ptr(0),
+ _egl_display(EGL_NO_DISPLAY),
+ _egl_surface(EGL_NO_SURFACE),
+ _screen_changeid(0),
+ _force_redraw(false),
+ _game_texture(NULL),
+ _overlay_texture(NULL),
+ _mouse_texture(NULL),
+ _use_mouse_palette(false),
+ _show_mouse(false),
+ _show_overlay(false),
+ _savefile(0),
+ _mixer(0),
+ _timer(0),
+ _fsFactory(new POSIXFilesystemFactory()),
+ _asset_archive(new AndroidAssetArchive(am)),
+ _shake_offset(0),
+ _full_screen_dirty(false),
+ _event_queue_lock(createMutex()) {
+}
+
+OSystem_Android::~OSystem_Android() {
+ ENTER("~OSystem_Android()");
+ 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 _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;
+}
+
+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 == NULL) \
+ 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");
+
+#undef FIND_METHOD
+
+ return true;
+}
+
+static void ScummVM_create(JNIEnv* env, jobject self, jobject am) {
+ OSystem_Android* cpp_obj = new OSystem_Android(am);
+ if (!cpp_obj->initJavaHooks(env, self))
+ // Exception already thrown by initJavaHooks
+ return;
+
+ env->SetLongField(self, FID_ScummVM_nativeScummVM, (jlong)cpp_obj);
+
+#ifdef DYNAMIC_MODULES
+ PluginManager::instance().addPluginProvider(new AndroidPluginProvider());
+#endif
+}
+
+static void ScummVM_nativeDestroy(JNIEnv* env, jobject self) {
+ OSystem_Android* cpp_obj = OSystem_Android::fromJavaObject(env, self);
+ delete cpp_obj;
+}
+
+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, NULL);
+ if (buf == NULL) {
+ warning("Unable to get Java audio byte array. Skipping.");
+ return;
+ }
+ Audio::MixerImpl* mixer =
+ static_cast<Audio::MixerImpl*>(cpp_obj->getMixer());
+ assert(mixer);
+ mixer->mixCallback(reinterpret_cast<byte*>(buf), len);
+ env->ReleaseByteArrayElements(jbuf, buf, 0);
+}
+
+static void ScummVM_setConfManInt(JNIEnv* env, jclass cls,
+ jstring key_obj, jint value) {
+ ENTER("setConfManInt(%p, %d)", key_obj, (int)value);
+ const char* key = env->GetStringUTFChars(key_obj, NULL);
+ if (key == NULL)
+ return;
+ ConfMan.setInt(key, value);
+ env->ReleaseStringUTFChars(key_obj, key);
+}
+
+static void ScummVM_setConfManString(JNIEnv* env, jclass cls, jstring key_obj,
+ jstring value_obj) {
+ ENTER("setConfManStr(%p, %p)", key_obj, value_obj);
+ const char* key = env->GetStringUTFChars(key_obj, NULL);
+ if (key == NULL)
+ return;
+ const char* value = env->GetStringUTFChars(value_obj, NULL);
+ if (value == NULL) {
+ env->ReleaseStringUTFChars(key_obj, key);
+ return;
+ }
+ ConfMan.set(key, value);
+ env->ReleaseStringUTFChars(value_obj, value);
+ env->ReleaseStringUTFChars(key_obj, key);
+}
+
+void* OSystem_Android::timerThreadFunc(void* arg) {
+ OSystem_Android* system = (OSystem_Android*)arg;
+ DefaultTimerManager* timer = (DefaultTimerManager*)(system->_timer);
+
+ struct timespec tv;
+ tv.tv_sec = 0;
+ tv.tv_nsec = 100 * 1000 * 1000; // 100ms
+
+ while (!system->_timer_thread_exit) {
+ timer->handler();
+ nanosleep(&tv, NULL);
+ }
+
+ return NULL;
+}
+
+void OSystem_Android::initBackend() {
+ ENTER("initBackend()");
+ JNIEnv* env = JNU_GetEnv();
+
+ ConfMan.setInt("autosave_period", 0);
+ ConfMan.setInt("FM_medium_quality", true);
+
+ // must happen before creating TimerManager to avoid race in
+ // creating EventManager
+ setupKeymapper();
+
+ // BUG: "transient" ConfMan settings get nuked by the options
+ // screen. Passing the savepath in this way makes it stick
+ // (via ConfMan.registerDefault)
+ _savefile = new DefaultSaveFileManager(ConfMan.get("savepath"));
+ _timer = new DefaultTimerManager();
+
+ gettimeofday(&_startTime, NULL);
+
+ 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->setReady(true);
+
+ env->CallVoidMethod(_back_ptr, MID_initBackend);
+ if (env->ExceptionCheck()) {
+ error("Error in Java initBackend");
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+
+ _timer_thread_exit = false;
+ pthread_create(&_timer_thread, NULL, timerThreadFunc, this);
+
+ OSystem::initBackend();
+
+ setupScummVMSurface();
+}
+
+void OSystem_Android::addPluginDirectories(Common::FSList &dirs) const {
+ ENTER("OSystem_Android::addPluginDirectories()");
+ 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 == NULL)
+ continue;
+ const char* path = env->GetStringUTFChars(path_obj, NULL);
+ if (path == NULL) {
+ 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);
+ }
+}
+
+bool OSystem_Android::hasFeature(Feature f) {
+ return (f == kFeatureCursorHasPalette ||
+ f == kFeatureVirtualKeyboard ||
+ f == kFeatureOverlaySupportsAlpha);
+}
+
+void OSystem_Android::setFeatureState(Feature f, bool enable) {
+ ENTER("setFeatureState(%d, %d)", f, enable);
+ switch (f) {
+ case kFeatureVirtualKeyboard:
+ _virtkeybd_on = enable;
+ showVirtualKeyboard(enable);
+ break;
+ default:
+ break;
+ }
+}
+
+bool OSystem_Android::getFeatureState(Feature f) {
+ switch (f) {
+ case kFeatureVirtualKeyboard:
+ return _virtkeybd_on;
+ default:
+ return false;
+ }
+}
+
+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("setGraphicsMode(%s)", mode);
+ return true;
+}
+
+bool OSystem_Android::setGraphicsMode(int mode) {
+ ENTER("setGraphicsMode(%d)", mode);
+ return true;
+}
+
+int OSystem_Android::getGraphicsMode() const {
+ return 1;
+}
+
+void OSystem_Android::setupScummVMSurface() {
+ JNIEnv* env = JNU_GetEnv();
+ env->CallVoidMethod(_back_ptr, MID_setupScummVMSurface);
+ if (env->ExceptionCheck())
+ return;
+
+ // EGL set up with a new surface. Initialise OpenGLES context.
+
+ _egl_display = eglGetCurrentDisplay();
+ _egl_surface = eglGetCurrentSurface(EGL_DRAW);
+
+ static bool log_version = true;
+ if (log_version) {
+ __android_log_print(ANDROID_LOG_INFO, LOG_TAG,
+ "Using EGL %s (%s); GL %s/%s (%s)",
+ eglQueryString(_egl_display, EGL_VERSION),
+ eglQueryString(_egl_display, EGL_VENDOR),
+ glGetString(GL_VERSION),
+ glGetString(GL_RENDERER),
+ glGetString(GL_VENDOR));
+ log_version = false; // only log this once
+ }
+
+ GLESTexture::initGLExtensions();
+
+ if (!eglQuerySurface(_egl_display, _egl_surface,
+ EGL_WIDTH, &_egl_surface_width) ||
+ !eglQuerySurface(_egl_display, _egl_surface,
+ EGL_HEIGHT, &_egl_surface_height)) {
+ JNU_ThrowByName(env, "java/lang/RuntimeException",
+ "Error fetching EGL surface width/height");
+ return;
+ }
+ __android_log_print(ANDROID_LOG_INFO, LOG_TAG,
+ "New surface is %dx%d",
+ _egl_surface_width, _egl_surface_height);
+
+ CHECK_GL_ERROR();
+
+ // Turn off anything that looks like 3D ;)
+ glDisable(GL_CULL_FACE);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_LIGHTING);
+ glDisable(GL_FOG);
+ glDisable(GL_DITHER);
+ glShadeModel(GL_FLAT);
+ glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ 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();
+
+ glViewport(0, 0, _egl_surface_width, _egl_surface_height);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrthof(0, _egl_surface_width, _egl_surface_height, 0, -1, 1);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ CHECK_GL_ERROR();
+
+ _force_redraw = true;
+}
+
+void OSystem_Android::destroyScummVMSurface() {
+ _egl_surface = EGL_NO_SURFACE;
+ 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("initSize(%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("setPalette(%p, %u, %u)", colors, start, num);
+
+ if (!_use_mouse_palette)
+ _setCursorPalette(colors, start, num);
+
+ byte* palette = _game_texture->palette() + start*3;
+ do {
+ for (int i = 0; i < 3; ++i)
+ palette[i] = colors[i];
+ palette += 3;
+ colors += 4;
+ } while (--num);
+}
+
+void OSystem_Android::grabPalette(byte *colors, uint start, uint num) {
+ ENTER("grabPalette(%p, %u, %u)", colors, start, num);
+ const byte* palette = _game_texture->palette_const() + start*3;
+ do {
+ for (int i = 0; i < 3; ++i)
+ colors[i] = palette[i];
+ colors[3] = 0xff; // alpha
+
+ palette += 3;
+ colors += 4;
+ } while (--num);
+}
+
+void OSystem_Android::copyRectToScreen(const byte *buf, int pitch,
+ int x, int y, int w, int h) {
+ ENTER("copyRectToScreen(%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("updateScreen()");
+
+ if (!_force_redraw &&
+ !_game_texture->dirty() &&
+ !_overlay_texture->dirty() &&
+ !_mouse_texture->dirty())
+ return;
+
+ _force_redraw = false;
+
+ glPushMatrix();
+
+ if (_shake_offset != 0) {
+ // This is the only case where _game_texture doesn't
+ // cover the entire screen.
+ glClearColorx(0, 0, 0, 1 << 16);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ // Move everything up by _shake_offset (game) pixels
+ glTranslatex(0, -_shake_offset << 16, 0);
+ }
+
+ _game_texture->drawTexture(0, 0,
+ _egl_surface_width, _egl_surface_height);
+
+ CHECK_GL_ERROR();
+
+ if (_show_overlay) {
+ _overlay_texture->drawTexture(0, 0,
+ _egl_surface_width,
+ _egl_surface_height);
+ CHECK_GL_ERROR();
+ }
+
+ if (_show_mouse) {
+ glPushMatrix();
+
+ glTranslatex(-_mouse_hotspot.x << 16,
+ -_mouse_hotspot.y << 16,
+ 0);
+
+ // Scale up ScummVM -> OpenGL (pixel) coordinates
+ int texwidth, texheight;
+ if (_show_overlay) {
+ texwidth = getOverlayWidth();
+ texheight = getOverlayHeight();
+ } else {
+ texwidth = getWidth();
+ texheight = getHeight();
+ }
+ glScalex(xdiv(_egl_surface_width, texwidth),
+ xdiv(_egl_surface_height, texheight),
+ 1 << 16);
+
+ // Note the extra half texel to position the mouse in
+ // the middle of the x,y square:
+ const Common::Point& mouse = getEventManager()->getMousePos();
+ glTranslatex((mouse.x << 16) | 1 << 15,
+ (mouse.y << 16) | 1 << 15, 0);
+
+ // Mouse targetscale just seems to make the cursor way
+ // too big :/
+ //glScalex(_mouse_targetscale << 16, _mouse_targetscale << 16,
+ // 1 << 16);
+
+ _mouse_texture->drawTexture();
+
+ glPopMatrix();
+ }
+
+ glPopMatrix();
+
+ CHECK_GL_ERROR();
+
+ if (!eglSwapBuffers(_egl_display, _egl_surface)) {
+ EGLint error = eglGetError();
+ warning("eglSwapBuffers exited with error 0x%x", error);
+ // Some errors mean we need to reinit GL
+ if (error == EGL_CONTEXT_LOST) {
+ destroyScummVMSurface();
+ setupScummVMSurface();
+ }
+ }
+}
+
+Graphics::Surface *OSystem_Android::lockScreen() {
+ ENTER("lockScreen()");
+ Graphics::Surface* surface = _game_texture->surface();
+ assert(surface->pixels);
+ return surface;
+}
+
+void OSystem_Android::unlockScreen() {
+ ENTER("unlockScreen()");
+ assert(_game_texture->dirty());
+}
+
+void OSystem_Android::setShakePos(int shake_offset) {
+ ENTER("setShakePos(%d)", shake_offset);
+ if (_shake_offset != shake_offset) {
+ _shake_offset = shake_offset;
+ _force_redraw = true;
+ }
+}
+
+void OSystem_Android::fillScreen(uint32 col) {
+ ENTER("fillScreen(%u)", col);
+ assert(col < 256);
+ _game_texture->fillBuffer(col);
+}
+
+void OSystem_Android::setFocusRectangle(const Common::Rect& rect) {
+ ENTER("setFocusRectangle(%d,%d,%d,%d)",
+ rect.left, rect.top, rect.right, rect.bottom);
+#if 0
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrthof(rect.left, rect.right, rect.top, rect.bottom, 0, 1);
+ glMatrixMode(GL_MODELVIEW);
+
+ _force_redraw = true;
+#endif
+}
+
+void OSystem_Android::clearFocusRectangle() {
+ ENTER("clearFocusRectangle()");
+#if 0
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrthof(0, _egl_surface_width, _egl_surface_height, 0, -1, 1);
+ glMatrixMode(GL_MODELVIEW);
+
+ _force_redraw = true;
+#endif
+}
+
+void OSystem_Android::showOverlay() {
+ ENTER("showOverlay()");
+ _show_overlay = true;
+ _force_redraw = true;
+}
+
+void OSystem_Android::hideOverlay() {
+ ENTER("hideOverlay()");
+ _show_overlay = false;
+ _force_redraw = true;
+}
+
+void OSystem_Android::clearOverlay() {
+ ENTER("clearOverlay()");
+ _overlay_texture->fillBuffer(0);
+}
+
+void OSystem_Android::grabOverlay(OverlayColor *buf, int pitch) {
+ ENTER("grabOverlay(%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]));
+ buf += pitch; // This 'pitch' is pixels not bytes
+ } while (--h);
+}
+
+void OSystem_Android::copyRectToOverlay(const OverlayColor *buf, int pitch,
+ int x, int y, int w, int h) {
+ ENTER("copyRectToOverlay(%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]));
+}
+
+int16 OSystem_Android::getOverlayHeight() {
+ return _overlay_texture->height();
+}
+
+int16 OSystem_Android::getOverlayWidth() {
+ return _overlay_texture->width();
+}
+
+bool OSystem_Android::showMouse(bool visible) {
+ ENTER("showMouse(%d)", visible);
+ _show_mouse = visible;
+ return true;
+}
+
+void OSystem_Android::warpMouse(int x, int y) {
+ ENTER("warpMouse(%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("setMouseCursor(%p, %u, %u, %d, %d, %d, %d, %p)",
+ buf, w, h, hotspotX, hotspotY, (int)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 += 4;
+ } while (--num);
+}
+
+void OSystem_Android::setCursorPalette(const byte *colors,
+ uint start, uint num) {
+ ENTER("setCursorPalette(%p, %u, %u)", colors, start, num);
+ _setCursorPalette(colors, start, num);
+ _use_mouse_palette = true;
+}
+
+void OSystem_Android::disableCursorPalette(bool disable) {
+ ENTER("disableCursorPalette(%d)", disable);
+ _use_mouse_palette = !disable;
+}
+
+void OSystem_Android::setupKeymapper() {
+#ifdef ENABLE_KEYMAPPER
+ using namespace Common;
+
+ Keymapper *mapper = getEventManager()->getKeymapper();
+
+ HardwareKeySet *keySet = new HardwareKeySet();
+ keySet->addHardwareKey(
+ new HardwareKey("n", KeyState(KEYCODE_n), "n (vk)",
+ kTriggerLeftKeyType,
+ kVirtualKeyboardActionType));
+ mapper->registerHardwareKeySet(keySet);
+
+ Keymap *globalMap = new Keymap("global");
+ Action *act;
+
+ act = new Action(globalMap, "VIRT", "Display keyboard",
+ kVirtualKeyboardActionType);
+ act->addKeyEvent(KeyState(KEYCODE_F7, ASCII_F7, 0));
+
+ mapper->addGlobalKeymap(globalMap);
+
+ mapper->pushKeymap("global");
+#endif
+}
+
+bool OSystem_Android::pollEvent(Common::Event &event) {
+ //ENTER("pollEvent()");
+ lockMutex(_event_queue_lock);
+ if (_event_queue.empty()) {
+ unlockMutex(_event_queue_lock);
+ return false;
+ }
+ event = _event_queue.pop();
+ unlockMutex(_event_queue_lock);
+
+ switch (event.type) {
+ case Common::EVENT_MOUSEMOVE:
+ // TODO: only dirty/redraw move bounds
+ _force_redraw = true;
+ // fallthrough
+ 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: {
+ if (event.kbd.flags == 1) { // relative mouse hack
+ // Relative (trackball) mouse hack.
+ const Common::Point& mouse_pos =
+ getEventManager()->getMousePos();
+ event.mouse.x += mouse_pos.x;
+ event.mouse.y += mouse_pos.y;
+ event.mouse.x = CLIP(event.mouse.x, (int16)0, _show_overlay ?
+ getOverlayWidth() : getWidth());
+ event.mouse.y = CLIP(event.mouse.y, (int16)0, _show_overlay ?
+ getOverlayHeight() : getHeight());
+ } 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);
+ event.mouse.x = scalef(event.mouse.x, tex->width(),
+ _egl_surface_width);
+ event.mouse.y = scalef(event.mouse.y, tex->height(),
+ _egl_surface_height);
+ event.mouse.x -= _shake_offset;
+ }
+ break;
+ }
+ case Common::EVENT_SCREEN_CHANGED:
+ debug("EVENT_SCREEN_CHANGED");
+ _screen_changeid++;
+ destroyScummVMSurface();
+ setupScummVMSurface();
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+void OSystem_Android::pushEvent(const Common::Event& event) {
+ lockMutex(_event_queue_lock);
+
+ // Try to combine multiple queued mouse move events
+ if (event.type == Common::EVENT_MOUSEMOVE &&
+ !_event_queue.empty() &&
+ _event_queue.back().type == Common::EVENT_MOUSEMOVE) {
+ Common::Event tail = _event_queue.back();
+ if (event.kbd.flags) {
+ // relative movement hack
+ tail.mouse.x += event.mouse.x;
+ tail.mouse.y += event.mouse.y;
+ } else {
+ // absolute position
+ tail.kbd.flags = 0; // clear relative flag
+ tail.mouse.x = event.mouse.x;
+ tail.mouse.y = event.mouse.y;
+ }
+ }
+ else
+ _event_queue.push(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;
+ gettimeofday(&curTime, NULL);
+ return (uint32)(((curTime.tv_sec - _startTime.tv_sec) * 1000) + \
+ ((curTime.tv_usec - _startTime.tv_usec) / 1000));
+}
+
+void OSystem_Android::delayMillis(uint msecs) {
+ usleep(msecs * 1000);
+}
+
+OSystem::MutexRef OSystem_Android::createMutex() {
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+
+ pthread_mutex_t *mutex = new pthread_mutex_t;
+ if (pthread_mutex_init(mutex, &attr) != 0) {
+ warning("pthread_mutex_init() failed!");
+ delete mutex;
+ return NULL;
+ }
+ return (MutexRef)mutex;
+}
+
+void OSystem_Android::lockMutex(MutexRef mutex) {
+ if (pthread_mutex_lock((pthread_mutex_t*)mutex) != 0)
+ warning("pthread_mutex_lock() failed!");
+}
+
+void OSystem_Android::unlockMutex(MutexRef mutex) {
+ if (pthread_mutex_unlock((pthread_mutex_t*)mutex) != 0)
+ warning("pthread_mutex_unlock() failed!");
+}
+
+void OSystem_Android::deleteMutex(MutexRef mutex) {
+ pthread_mutex_t* m = (pthread_mutex_t*)mutex;
+ if (pthread_mutex_destroy(m) != 0)
+ warning("pthread_mutex_destroy() failed!");
+ else
+ delete m;
+}
+
+void OSystem_Android::quit() {
+ ENTER("quit()");
+
+ _timer_thread_exit = true;
+ pthread_join(_timer_thread, NULL);
+}
+
+void OSystem_Android::setWindowCaption(const char *caption) {
+ ENTER("setWindowCaption(%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);
+}
+
+void OSystem_Android::displayMessageOnOSD(const char *msg) {
+ ENTER("displayMessageOnOSD(%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);
+}
+
+void OSystem_Android::showVirtualKeyboard(bool enable) {
+ ENTER("showVirtualKeyboard(%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();
+ }
+}
+
+Common::SaveFileManager *OSystem_Android::getSavefileManager() {
+ assert(_savefile);
+ return _savefile;
+}
+
+Audio::Mixer *OSystem_Android::getMixer() {
+ assert(_mixer);
+ return _mixer;
+}
+
+Common::TimerManager *OSystem_Android::getTimerManager() {
+ assert(_timer);
+ return _timer;
+}
+
+void OSystem_Android::getTimeAndDate(TimeDate &td) const {
+ struct tm tm;
+ const time_t curTime = time(NULL);
+ localtime_r(&curTime, &tm);
+ td.tm_sec = tm.tm_sec;
+ td.tm_min = tm.tm_min;
+ td.tm_hour = tm.tm_hour;
+ td.tm_mday = tm.tm_mday;
+ td.tm_mon = tm.tm_mon;
+ td.tm_year = tm.tm_year;
+}
+
+FilesystemFactory *OSystem_Android::getFilesystemFactory() {
+ return _fsFactory;
+}
+
+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();
+ 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, NULL);
+ if (path != NULL) {
+ s.addDirectory(path, path, priority);
+ env->ReleaseStringUTFChars(path_obj, path);
+ }
+ env->DeleteLocalRef(path_obj);
+ }
+}
+
+
+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];
+ int nargs; // note use in cleanup loop below
+ for (nargs = 0; nargs < argc; ++nargs) {
+ jstring arg = (jstring)env->GetObjectArrayElement(args, nargs);
+ if (arg == NULL) {
+ argv[nargs] = NULL;
+ } else {
+ const char* cstr = env->GetStringUTFChars(arg, NULL);
+ argv[nargs] = const_cast<char*>(cstr);
+ if (cstr == NULL)
+ goto cleanup; // exception already thrown
+ }
+ env->DeleteLocalRef(arg);
+ }
+
+ g_system = cpp_obj;
+ assert(g_system);
+ __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
+ "Entering scummvm_main with %d args", argc);
+ res = scummvm_main(argc, argv);
+ __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "Exiting scummvm_main");
+ g_system->quit();
+
+cleanup:
+ nargs--;
+ for (int i = 0; i < nargs; ++i) {
+ if (argv[i] == NULL)
+ continue;
+ jstring arg = (jstring)env->GetObjectArrayElement(args, nargs);
+ if (arg == NULL)
+ // Exception already thrown
+ 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);
+}
+#endif
+
+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 },
+};
+
+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 == NULL)
+ 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 == NULL)
+ return JNI_ERR;
+
+ jclass event = env->FindClass("org/inodes/gus/scummvm/Event");
+ if (event == NULL)
+ return JNI_ERR;
+ FID_Event_type = env->GetFieldID(event, "type", "I");
+ if (FID_Event_type == NULL)
+ return JNI_ERR;
+ FID_Event_synthetic = env->GetFieldID(event, "synthetic", "Z");
+ if (FID_Event_synthetic == NULL)
+ return JNI_ERR;
+ FID_Event_kbd_keycode = env->GetFieldID(event, "kbd_keycode", "I");
+ if (FID_Event_kbd_keycode == NULL)
+ return JNI_ERR;
+ FID_Event_kbd_ascii = env->GetFieldID(event, "kbd_ascii", "I");
+ if (FID_Event_kbd_ascii == NULL)
+ return JNI_ERR;
+ FID_Event_kbd_flags = env->GetFieldID(event, "kbd_flags", "I");
+ if (FID_Event_kbd_flags == NULL)
+ return JNI_ERR;
+ FID_Event_mouse_x = env->GetFieldID(event, "mouse_x", "I");
+ if (FID_Event_mouse_x == NULL)
+ return JNI_ERR;
+ FID_Event_mouse_y = env->GetFieldID(event, "mouse_y", "I");
+ if (FID_Event_mouse_y == NULL)
+ return JNI_ERR;
+ FID_Event_mouse_relative = env->GetFieldID(event, "mouse_relative", "Z");
+ if (FID_Event_mouse_relative == NULL)
+ return JNI_ERR;
+
+ cls = env->FindClass("java/lang/Object");
+ if (cls == NULL)
+ return JNI_ERR;
+ MID_Object_wait = env->GetMethodID(cls, "wait", "()V");
+ if (MID_Object_wait == NULL)
+ return JNI_ERR;
+
+ return JNI_VERSION_1_2;
+}
+
+#endif
diff --git a/backends/platform/android/android.mk b/backends/platform/android/android.mk
new file mode 100644
index 0000000000..0bc8fa265e
--- /dev/null
+++ b/backends/platform/android/android.mk
@@ -0,0 +1,52 @@
+# Android specific build targets
+
+AAPT = aapt
+DX = dx
+APKBUILDER = apkbuilder
+ADB = adb -e
+ANDROID_JAR = $(ANDROID_SDK)/platforms/android-1.6/android.jar
+JAVAC ?= javac
+JAVACFLAGS = -source 1.5 -target 1.5
+
+# FIXME: find/mark plugin entry points and add all this back again:
+#LDFLAGS += -Wl,--gc-sections
+#CXXFLAGS += -ffunction-sections -fdata-sections -fvisibility=hidden -fvisibility-inlines-hidden
+
+scummvm.apk: build.tmp/libscummvm.so resources.ap_ classes.dex
+ # Package installer won't delete old libscummvm.so on upgrade so
+ # replace it with a zero size file
+ $(INSTALL) -d build.stage/common/lib/armeabi
+ touch build.stage/common/lib/armeabi/libscummvm.so
+ # We now handle the library unpacking ourselves from mylib/
+ $(INSTALL) -d build.stage/common/mylib/armeabi
+ $(INSTALL) -c -m 644 build.tmp/libscummvm.so build.stage/common/mylib/armeabi/
+ $(STRIP) build.stage/common/mylib/armeabi/libscummvm.so
+ # "-nf lib/armeabi/libscummvm.so" builds bogus paths?
+ $(APKBUILDER) $@ -z resources.ap_ -f classes.dex -rf build.stage/common || { $(RM) $@; exit 1; }
+
+scummvm-engine-%.apk: plugins/lib%.so build.tmp/%/resources.ap_ build.tmp/plugins/classes.dex
+ $(INSTALL) -d build.stage/$*/apk/mylib/armeabi/
+ $(INSTALL) -c -m 644 plugins/lib$*.so build.stage/$*/apk/mylib/armeabi/
+ $(STRIP) build.stage/$*/apk/mylib/armeabi/lib$*.so
+ $(APKBUILDER) $@ -z build.tmp/$*/resources.ap_ -f build.tmp/plugins/classes.dex -rf build.stage/$*/apk || { $(RM) $@; exit 1; }
+
+release/%.apk: %.apk
+ @$(MKDIR) -p $(@D)
+ @$(RM) $@
+ $(CP) $< $@.tmp
+ # remove debugging signature
+ zip -d $@.tmp META-INF/\*
+ jarsigner $(JARSIGNER_FLAGS) $@.tmp release
+ zipalign 4 $@.tmp $@
+ $(RM) $@.tmp
+
+androidrelease: release/scummvm.apk $(patsubst plugins/lib%.so,release/scummvm-engine-%.apk,$(PLUGINS))
+
+androidtest: scummvm.apk scummvm-engine-scumm.apk scummvm-engine-kyra.apk
+ @set -e; for apk in $^; do \
+ echo $(ADB) install -r $$apk; \
+ $(ADB) install -r $$apk; \
+ done
+ $(ADB) shell am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -n org.inodes.gus.scummvm/.Unpacker
+
+.PHONY: androidrelease androidtest
diff --git a/backends/platform/android/asset-archive.cpp b/backends/platform/android/asset-archive.cpp
new file mode 100644
index 0000000000..20c6a653c0
--- /dev/null
+++ b/backends/platform/android/asset-archive.cpp
@@ -0,0 +1,414 @@
+/* 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 <jni.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "common/str.h"
+#include "common/stream.h"
+#include "common/util.h"
+#include "common/archive.h"
+#include "common/debug.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;
+
+// This might be useful to someone else. Assumes markSupported() == true.
+class JavaInputStream : public Common::SeekableReadStream {
+public:
+ JavaInputStream(JNIEnv* env, jobject is);
+ virtual ~JavaInputStream();
+ virtual bool eos() const { return _eos; }
+ virtual bool err() const { return _err; }
+ virtual void clearErr() { _eos = _err = false; }
+ virtual uint32 read(void *dataPtr, uint32 dataSize);
+ virtual int32 pos() const { return _pos; }
+ virtual int32 size() const { return _len; }
+ virtual bool seek(int32 offset, int whence = SEEK_SET);
+private:
+ void close(JNIEnv* env);
+ jmethodID MID_mark;
+ jmethodID MID_available;
+ jmethodID MID_close;
+ jmethodID MID_read;
+ jmethodID MID_reset;
+ jmethodID MID_skip;
+ jobject _input_stream;
+ jsize _buflen;
+ jbyteArray _buf;
+ uint32 _pos;
+ jint _len;
+ bool _eos;
+ bool _err;
+};
+
+JavaInputStream::JavaInputStream(JNIEnv* env, jobject is) :
+ _eos(false), _err(false), _pos(0)
+{
+ _input_stream = env->NewGlobalRef(is);
+ _buflen = 8192;
+ _buf = static_cast<jbyteArray>(env->NewGlobalRef(env->NewByteArray(_buflen)));
+
+ jclass cls = env->GetObjectClass(_input_stream);
+ MID_mark = env->GetMethodID(cls, "mark", "(I)V");
+ assert(MID_mark);
+ MID_available = env->GetMethodID(cls, "available", "()I");
+ assert(MID_mark);
+ MID_close = env->GetMethodID(cls, "close", "()V");
+ assert(MID_close);
+ MID_read = env->GetMethodID(cls, "read", "([BII)I");
+ assert(MID_read);
+ MID_reset = env->GetMethodID(cls, "reset", "()V");
+ assert(MID_reset);
+ MID_skip = env->GetMethodID(cls, "skip", "(J)J");
+ assert(MID_skip);
+
+ // Mark start of stream, so we can reset back to it.
+ // readlimit is set to something bigger than anything we might
+ // want to seek within.
+ env->CallVoidMethod(_input_stream, MID_mark, 10*1024*1024);
+ _len = env->CallIntMethod(_input_stream, MID_available);
+}
+
+JavaInputStream::~JavaInputStream() {
+ JNIEnv* env = JNU_GetEnv();
+ close(env);
+ env->DeleteGlobalRef(_buf);
+ env->DeleteGlobalRef(_input_stream);
+}
+
+void JavaInputStream::close(JNIEnv* env) {
+ env->CallVoidMethod(_input_stream, MID_close);
+ if (env->ExceptionCheck())
+ env->ExceptionClear();
+}
+
+uint32 JavaInputStream::read(void *dataPtr, uint32 dataSize) {
+ JNIEnv* env = JNU_GetEnv();
+
+ if (_buflen < dataSize) {
+ _buflen = dataSize;
+ env->DeleteGlobalRef(_buf);
+ _buf = static_cast<jbyteArray>(env->NewGlobalRef(env->NewByteArray(_buflen)));
+ }
+
+ jint ret = env->CallIntMethod(_input_stream, MID_read, _buf, 0, dataSize);
+ if (env->ExceptionCheck()) {
+ warning("Exception during JavaInputStream::read(%p, %d)",
+ dataPtr, dataSize);
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ _err = true;
+ ret = -1;
+ } else if (ret == -1) {
+ _eos = true;
+ ret = 0;
+ } else {
+ env->GetByteArrayRegion(_buf, 0, ret, static_cast<jbyte*>(dataPtr));
+ _pos += ret;
+ }
+ return ret;
+}
+
+bool JavaInputStream::seek(int32 offset, int whence) {
+ JNIEnv* env = JNU_GetEnv();
+ uint32 newpos;
+ switch (whence) {
+ case SEEK_SET:
+ newpos = offset;
+ break;
+ case SEEK_CUR:
+ newpos = _pos + offset;
+ break;
+ case SEEK_END:
+ newpos = _len + offset;
+ break;
+ default:
+ debug("Unknown 'whence' arg %d", whence);
+ return false;
+ }
+
+ jlong skip_bytes;
+ if (newpos > _pos) {
+ skip_bytes = newpos - _pos;
+ } else {
+ // Can't skip backwards, so jump back to start and skip from there.
+ env->CallVoidMethod(_input_stream, MID_reset);
+ if (env->ExceptionCheck()) {
+ warning("Failed to rewind to start of asset stream");
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ return false;
+ }
+ _pos = 0;
+ skip_bytes = newpos;
+ }
+
+ while (skip_bytes > 0) {
+ jlong ret = env->CallLongMethod(_input_stream, MID_skip, skip_bytes);
+ if (env->ExceptionCheck()) {
+ warning("Failed to skip %ld bytes into asset stream",
+ static_cast<long>(skip_bytes));
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ return false;
+ } else if (ret == 0) {
+ warning("InputStream->skip(%ld) didn't skip any bytes. Aborting seek.",
+ static_cast<long>(skip_bytes));
+ return false; // No point looping forever...
+ }
+ _pos += ret;
+ skip_bytes -= ret;
+ }
+ _eos = false;
+ return true;
+}
+
+
+// Must match android.content.res.AssetFileDescriptor.UNKNOWN_LENGTH
+const jlong UNKNOWN_LENGTH = -1;
+
+// Reading directly from a fd is so much more efficient, that it is
+// worth optimising for.
+class AssetFdReadStream : public Common::SeekableReadStream {
+public:
+ AssetFdReadStream(JNIEnv* env, jobject assetfd);
+ virtual ~AssetFdReadStream();
+ virtual bool eos() const { return _eos; }
+ virtual bool err() const { return _err; }
+ virtual void clearErr() { _eos = _err = false; }
+ virtual uint32 read(void *dataPtr, uint32 dataSize);
+ virtual int32 pos() const { return _pos; }
+ virtual int32 size() const { return _declared_len; }
+ virtual bool seek(int32 offset, int whence = SEEK_SET);
+private:
+ void close(JNIEnv* env);
+ int _fd;
+ jmethodID MID_close;
+ jobject _assetfd;
+ jlong _start_off;
+ jlong _declared_len;
+ uint32 _pos;
+ bool _eos;
+ bool _err;
+};
+
+AssetFdReadStream::AssetFdReadStream(JNIEnv* env, jobject assetfd) :
+ _eos(false), _err(false), _pos(0)
+{
+ _assetfd = env->NewGlobalRef(assetfd);
+
+ jclass cls = env->GetObjectClass(_assetfd);
+ MID_close = env->GetMethodID(cls, "close", "()V");
+ assert(MID_close);
+
+ jmethodID MID_getStartOffset =
+ env->GetMethodID(cls, "getStartOffset", "()J");
+ assert(MID_getStartOffset);
+ _start_off = env->CallLongMethod(_assetfd, MID_getStartOffset);
+
+ jmethodID MID_getDeclaredLength =
+ env->GetMethodID(cls, "getDeclaredLength", "()J");
+ assert(MID_getDeclaredLength);
+ _declared_len = env->CallLongMethod(_assetfd, MID_getDeclaredLength);
+
+ jmethodID MID_getFileDescriptor =
+ env->GetMethodID(cls, "getFileDescriptor", "()Ljava/io/FileDescriptor;");
+ assert(MID_getFileDescriptor);
+ jobject javafd = env->CallObjectMethod(_assetfd, MID_getFileDescriptor);
+ assert(javafd);
+ jclass fd_cls = env->GetObjectClass(javafd);
+ jfieldID FID_descriptor = env->GetFieldID(fd_cls, "descriptor", "I");
+ assert(FID_descriptor);
+ _fd = env->GetIntField(javafd, FID_descriptor);
+}
+
+AssetFdReadStream::~AssetFdReadStream() {
+ JNIEnv* env = JNU_GetEnv();
+ env->CallVoidMethod(_assetfd, MID_close);
+ if (env->ExceptionCheck())
+ env->ExceptionClear();
+ env->DeleteGlobalRef(_assetfd);
+}
+
+uint32 AssetFdReadStream::read(void *dataPtr, uint32 dataSize) {
+ if (_declared_len != UNKNOWN_LENGTH) {
+ jlong cap = _declared_len - _pos;
+ if (dataSize > cap)
+ dataSize = cap;
+ }
+ int ret = ::read(_fd, dataPtr, dataSize);
+ if (ret == 0)
+ _eos = true;
+ else if (ret == -1)
+ _err = true;
+ else
+ _pos += ret;
+ return ret;
+}
+
+bool AssetFdReadStream::seek(int32 offset, int whence) {
+ if (whence == SEEK_SET) {
+ if (_declared_len != UNKNOWN_LENGTH && offset > _declared_len)
+ offset = _declared_len;
+ offset += _start_off;
+ } else if (whence == SEEK_END && _declared_len != UNKNOWN_LENGTH) {
+ whence = SEEK_SET;
+ offset = _start_off + _declared_len + offset;
+ }
+ int ret = lseek(_fd, offset, whence);
+ if (ret == -1)
+ return false;
+ _pos = ret - _start_off;
+ _eos = false;
+ return true;
+}
+
+AndroidAssetArchive::AndroidAssetArchive(jobject am) {
+ JNIEnv* env = JNU_GetEnv();
+ _am = env->NewGlobalRef(am);
+
+ jclass cls = env->GetObjectClass(_am);
+ MID_open = env->GetMethodID(cls, "open",
+ "(Ljava/lang/String;I)Ljava/io/InputStream;");
+ assert(MID_open);
+ MID_openFd = env->GetMethodID(cls, "openFd",
+ "(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;");
+ assert(MID_openFd);
+ MID_list = env->GetMethodID(cls, "list",
+ "(Ljava/lang/String;)[Ljava/lang/String;");
+ assert(MID_list);
+}
+
+AndroidAssetArchive::~AndroidAssetArchive() {
+ JNIEnv* env = JNU_GetEnv();
+ env->DeleteGlobalRef(_am);
+}
+
+bool AndroidAssetArchive::hasFile(const Common::String &name) {
+ JNIEnv* env = JNU_GetEnv();
+ jstring path = env->NewStringUTF(name.c_str());
+ jobject result = env->CallObjectMethod(_am, MID_open, path, ACCESS_UNKNOWN);
+ if (env->ExceptionCheck()) {
+ // Assume FileNotFoundException
+ //warning("Error while calling AssetManager->open(%s)", name.c_str());
+ //env->ExceptionDescribe();
+ env->ExceptionClear();
+ env->DeleteLocalRef(path);
+ return false;
+ }
+ env->DeleteLocalRef(result);
+ env->DeleteLocalRef(path);
+ return true;
+}
+
+int AndroidAssetArchive::listMembers(Common::ArchiveMemberList &member_list) {
+ JNIEnv* env = JNU_GetEnv();
+ Common::List<Common::String> dirlist;
+ dirlist.push_back("");
+
+ int count = 0;
+ while (!dirlist.empty()) {
+ const Common::String dir = dirlist.back();
+ dirlist.pop_back();
+
+ jstring jpath = env->NewStringUTF(dir.c_str());
+ jobjectArray jpathlist = static_cast<jobjectArray>(env->CallObjectMethod(_am, MID_list, jpath));
+ if (env->ExceptionCheck()) {
+ warning("Error while calling AssetManager->list(%s). Ignoring.",
+ dir.c_str());
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ continue; // May as well keep going ...
+ }
+ env->DeleteLocalRef(jpath);
+
+ for (jsize i = 0; i < env->GetArrayLength(jpathlist); ++i) {
+ jstring elem = (jstring)env->GetObjectArrayElement(jpathlist, i);
+ const char* p = env->GetStringUTFChars(elem, NULL);
+ Common::String thispath = dir;
+ if (!thispath.empty())
+ thispath += "/";
+ thispath += p;
+
+ // Assume files have a . in them, and directories don't
+ if (strchr(p, '.')) {
+ member_list.push_back(getMember(thispath));
+ ++count;
+ } else
+ dirlist.push_back(thispath);
+
+ env->ReleaseStringUTFChars(elem, p);
+ env->DeleteLocalRef(elem);
+ }
+
+ env->DeleteLocalRef(jpathlist);
+ }
+
+ return count;
+}
+
+Common::ArchiveMemberPtr AndroidAssetArchive::getMember(const Common::String &name) {
+ return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this));
+}
+
+Common::SeekableReadStream *AndroidAssetArchive::createReadStreamForMember(const Common::String &path) const {
+ JNIEnv* env = JNU_GetEnv();
+ jstring jpath = env->NewStringUTF(path.c_str());
+
+ // Try openFd() first ...
+ jobject afd = env->CallObjectMethod(_am, MID_openFd, jpath);
+ if (env->ExceptionCheck())
+ env->ExceptionClear();
+ else if (afd != NULL) {
+ // success :)
+ env->DeleteLocalRef(jpath);
+ return new AssetFdReadStream(env, afd);
+ }
+
+ // ... and fallback to normal open() if that doesn't work
+ jobject is = env->CallObjectMethod(_am, MID_open, jpath, ACCESS_RANDOM);
+ if (env->ExceptionCheck()) {
+ // Assume FileNotFoundException
+ //warning("Error opening %s", path.c_str());
+ //env->ExceptionDescribe();
+ env->ExceptionClear();
+ env->DeleteLocalRef(jpath);
+ return NULL;
+ }
+
+ return new JavaInputStream(env, is);
+}
+
+#endif
diff --git a/backends/platform/android/asset-archive.h b/backends/platform/android/asset-archive.h
new file mode 100644
index 0000000000..b3f6993c50
--- /dev/null
+++ b/backends/platform/android/asset-archive.h
@@ -0,0 +1,53 @@
+/* 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 <jni.h>
+
+#include "common/str.h"
+#include "common/stream.h"
+#include "common/util.h"
+#include "common/archive.h"
+
+class AndroidAssetArchive : public Common::Archive {
+public:
+ AndroidAssetArchive(jobject am);
+ virtual ~AndroidAssetArchive();
+
+ virtual bool hasFile(const Common::String &name);
+ virtual int listMembers(Common::ArchiveMemberList &list);
+ virtual Common::ArchiveMemberPtr getMember(const Common::String &name);
+ virtual Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const;
+
+private:
+ jmethodID MID_open;
+ jmethodID MID_openFd;
+ jmethodID MID_list;
+
+ jobject _am;
+};
+
+#endif
diff --git a/backends/platform/android/module.mk b/backends/platform/android/module.mk
new file mode 100644
index 0000000000..fdb0ed2ac4
--- /dev/null
+++ b/backends/platform/android/module.mk
@@ -0,0 +1,85 @@
+MODULE := backends/platform/android
+
+MODULE_OBJS := \
+ android.o asset-archive.o video.o
+
+MODULE_DIRS += \
+ backends/platform/android/
+
+# We don't use the rules.mk here on purpose
+OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS)) $(OBJS)
+
+JAVA_SRC = \
+ $(MODULE)/org/inodes/gus/scummvm/ScummVM.java \
+ $(MODULE)/org/inodes/gus/scummvm/ScummVMApplication.java \
+ $(MODULE)/org/inodes/gus/scummvm/ScummVMActivity.java \
+ $(MODULE)/org/inodes/gus/scummvm/EditableSurfaceView.java \
+ $(MODULE)/org/inodes/gus/scummvm/Unpacker.java \
+ $(MODULE)/org/inodes/gus/scummvm/Manifest.java \
+ $(MODULE)/org/inodes/gus/scummvm/R.java
+
+JAVA_PLUGIN_SRC = \
+ $(MODULE)/org/inodes/gus/scummvm/PluginProvider.java
+
+RESOURCES = \
+ $(srcdir)/dists/android/res/values/strings.xml \
+ $(srcdir)/dists/android/res/layout/main.xml \
+ $(srcdir)/dists/android/res/layout/splash.xml \
+ $(srcdir)/dists/android/res/drawable/gradient.xml \
+ $(srcdir)/dists/android/res/drawable/scummvm.png \
+ $(srcdir)/dists/android/res/drawable/scummvm_big.png
+
+ASSETS = $(DIST_FILES_ENGINEDATA) $(DIST_FILES_THEMES)
+
+PLUGIN_RESOURCES = \
+ $(srcdir)/dists/android/res/values/strings.xml \
+ $(srcdir)/dists/android/res/drawable/scummvm.png
+
+# These must be incremented for each market upload
+#ANDROID_VERSIONCODE = 6 Specified in dists/android/AndroidManifest.xml.in
+ANDROID_PLUGIN_VERSIONCODE = 6
+
+# This library contains scummvm proper
+build.tmp/libscummvm.so: $(OBJS)
+ @$(MKDIR) -p $(@D)
+ $(CXX) $(PLUGIN_LDFLAGS) -shared $(LDFLAGS) -Wl,-soname,$(@F) -Wl,--no-undefined -o $@ $(PRE_OBJS_FLAGS) $(OBJS) $(POST_OBJS_FLAGS) $(LIBS)
+
+
+backends/platform/android/org/inodes/gus/scummvm/R.java backends/platform/android/org/inodes/gus/scummvm/Manifest.java: $(srcdir)/dists/android/AndroidManifest.xml $(filter %.xml,$(RESOURCES)) $(ANDROID_JAR)
+ $(AAPT) package -m -J backends/platform/android -M $< -S $(srcdir)/dists/android/res -I $(ANDROID_JAR)
+
+build.tmp/classes/%.class: $(srcdir)/backends/platform/android/%.java $(srcdir)/backends/platform/android/org/inodes/gus/scummvm/R.java
+ @$(MKDIR) -p $(@D)
+ $(JAVAC) $(JAVACFLAGS) -cp $(srcdir)/backends/platform/android -d build.tmp/classes -bootclasspath $(ANDROID_JAR) $<
+
+build.tmp/classes.plugin/%.class: $(srcdir)/backends/platform/android/%.java
+ @$(MKDIR) -p $(@D)
+ $(JAVAC) $(JAVACFLAGS) -cp $(srcdir)/backends/platform/android -d build.tmp/classes.plugin -bootclasspath $(ANDROID_JAR) $<
+
+classes.dex: $(JAVA_SRC:backends/platform/android/%.java=build.tmp/classes/%.class)
+ $(DX) --dex --output=$@ build.tmp/classes
+
+build.tmp/plugins/classes.dex: $(JAVA_PLUGIN_SRC:backends/platform/android/%.java=build.tmp/classes.plugin/%.class)
+ @$(MKDIR) -p $(@D)
+ $(DX) --dex --output=$@ build.tmp/classes.plugin
+
+resources.ap_: $(srcdir)/dists/android/AndroidManifest.xml $(RESOURCES) $(ASSETS) $(ANDROID_JAR) $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA)
+ $(INSTALL) -d build.tmp/assets/
+ $(INSTALL) -c -m 644 $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) build.tmp/assets/
+ $(AAPT) package -f -M $< -S $(srcdir)/dists/android/res -A build.tmp/assets -I $(ANDROID_JAR) -F $@
+
+build.tmp/%/resources.ap_: build.tmp/%/AndroidManifest.xml build.stage/%/res/values/strings.xml build.stage/%/res/drawable/scummvm.png $(ANDROID_JAR)
+ $(AAPT) package -f -M $< -S build.stage/$*/res -I $(ANDROID_JAR) -F $@
+
+build.tmp/%/AndroidManifest.xml build.stage/%/res/values/strings.xml: dists/android/mkmanifest.pl configure dists/android/AndroidManifest.xml
+ dists/android/mkmanifest.pl --id=$* --configure=configure \
+ --version-name=$(VERSION) \
+ --version-code=$(ANDROID_PLUGIN_VERSIONCODE) \
+ --stringres=build.stage/$*/res/values/strings.xml \
+ --manifest=build.tmp/$*/AndroidManifest.xml \
+ --master-manifest=dists/android/AndroidManifest.xml \
+ --unpacklib=mylib/armeabi/lib$*.so
+
+build.stage/%/res/drawable/scummvm.png: dists/android/res/drawable/scummvm.png
+ @$(MKDIR) -p $(@D)
+ $(CP) $< $@
diff --git a/backends/platform/android/org/inodes/gus/scummvm/EditableSurfaceView.java b/backends/platform/android/org/inodes/gus/scummvm/EditableSurfaceView.java
new file mode 100644
index 0000000000..5b71d4a3a5
--- /dev/null
+++ b/backends/platform/android/org/inodes/gus/scummvm/EditableSurfaceView.java
@@ -0,0 +1,59 @@
+package org.inodes.gus.scummvm;
+
+import android.content.Context;
+import android.text.InputType;
+import android.util.AttributeSet;
+import android.view.SurfaceView;
+import android.view.inputmethod.BaseInputConnection;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
+
+public class EditableSurfaceView extends SurfaceView {
+ public EditableSurfaceView(Context context) {
+ super(context);
+ }
+
+ public EditableSurfaceView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public EditableSurfaceView(Context context, AttributeSet attrs,
+ int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ public boolean onCheckIsTextEditor() {
+ return true;
+ }
+
+ private class MyInputConnection extends BaseInputConnection {
+ public MyInputConnection() {
+ super(EditableSurfaceView.this, false);
+ }
+
+ @Override
+ public boolean performEditorAction(int actionCode) {
+ if (actionCode == EditorInfo.IME_ACTION_DONE) {
+ InputMethodManager imm = (InputMethodManager)
+ getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(getWindowToken(), 0);
+ }
+ return super.performEditorAction(actionCode); // Sends enter key
+ }
+ }
+
+ @Override
+ public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+ 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);
+ outAttrs.imeOptions = (EditorInfo.IME_ACTION_DONE |
+ EditorInfo.IME_FLAG_NO_EXTRACT_UI);
+
+ return new MyInputConnection();
+ }
+}
diff --git a/backends/platform/android/org/inodes/gus/scummvm/Event.java b/backends/platform/android/org/inodes/gus/scummvm/Event.java
new file mode 100644
index 0000000000..f9c7aba93b
--- /dev/null
+++ b/backends/platform/android/org/inodes/gus/scummvm/Event.java
@@ -0,0 +1,330 @@
+package org.inodes.gus.scummvm;
+
+import android.view.KeyEvent;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public class Event {
+ // Common::EventType enum.
+ // Must be kept in sync with common/events.h
+ public final static int EVENT_INVALID = 0;
+ public final static int EVENT_KEYDOWN = 1;
+ public final static int EVENT_KEYUP = 2;
+ public final static int EVENT_MOUSEMOVE = 3;
+ public final static int EVENT_LBUTTONDOWN = 4;
+ public final static int EVENT_LBUTTONUP = 5;
+ public final static int EVENT_RBUTTONDOWN = 6;
+ public final static int EVENT_RBUTTONUP = 7;
+ public final static int EVENT_WHEELUP = 8;
+ public final static int EVENT_WHEELDOWN = 9;
+ public final static int EVENT_QUIT = 10;
+ public final static int EVENT_SCREEN_CHANGED = 11;
+ public final static int EVENT_PREDICTIVE_DIALOG = 12;
+ public final static int EVENT_MBUTTONDOWN = 13;
+ public final static int EVENT_MBUTTONUP = 14;
+ public final static int EVENT_MAINMENU = 15;
+ public final static int EVENT_RTL = 16;
+
+ // common/keyboard.h
+ public final static int ASCII_F1 = 315;
+ public final static int ASCII_F2 = 316;
+ public final static int ASCII_F3 = 317;
+ public final static int ASCII_F4 = 318;
+ public final static int ASCII_F5 = 319;
+ public final static int ASCII_F6 = 320;
+ public final static int ASCII_F7 = 321;
+ public final static int ASCII_F8 = 322;
+ public final static int ASCII_F9 = 323;
+ public final static int ASCII_F10 = 324;
+ public final static int ASCII_F11 = 325;
+ public final static int ASCII_F12 = 326;
+ public final static int KBD_CTRL = 1 << 0;
+ public final static int KBD_ALT = 1 << 1;
+ public final static int KBD_SHIFT = 1 << 2;
+
+ public final static int KEYCODE_INVALID = 0;
+ public final static int KEYCODE_BACKSPACE = 8;
+ public final static int KEYCODE_TAB = 9;
+ public final static int KEYCODE_CLEAR = 12;
+ public final static int KEYCODE_RETURN = 13;
+ public final static int KEYCODE_PAUSE = 19;
+ public final static int KEYCODE_ESCAPE = 27;
+ public final static int KEYCODE_SPACE = 32;
+ public final static int KEYCODE_EXCLAIM = 33;
+ public final static int KEYCODE_QUOTEDBL = 34;
+ public final static int KEYCODE_HASH = 35;
+ public final static int KEYCODE_DOLLAR = 36;
+ public final static int KEYCODE_AMPERSAND = 38;
+ public final static int KEYCODE_QUOTE = 39;
+ public final static int KEYCODE_LEFTPAREN = 40;
+ public final static int KEYCODE_RIGHTPAREN = 41;
+ public final static int KEYCODE_ASTERISK = 42;
+ public final static int KEYCODE_PLUS = 43;
+ public final static int KEYCODE_COMMA = 44;
+ public final static int KEYCODE_MINUS = 45;
+ public final static int KEYCODE_PERIOD = 46;
+ public final static int KEYCODE_SLASH = 47;
+ public final static int KEYCODE_0 = 48;
+ public final static int KEYCODE_1 = 49;
+ public final static int KEYCODE_2 = 50;
+ public final static int KEYCODE_3 = 51;
+ public final static int KEYCODE_4 = 52;
+ public final static int KEYCODE_5 = 53;
+ public final static int KEYCODE_6 = 54;
+ public final static int KEYCODE_7 = 55;
+ public final static int KEYCODE_8 = 56;
+ public final static int KEYCODE_9 = 57;
+ public final static int KEYCODE_COLON = 58;
+ public final static int KEYCODE_SEMICOLON = 59;
+ public final static int KEYCODE_LESS = 60;
+ public final static int KEYCODE_EQUALS = 61;
+ public final static int KEYCODE_GREATER = 62;
+ public final static int KEYCODE_QUESTION = 63;
+ public final static int KEYCODE_AT = 64;
+ public final static int KEYCODE_LEFTBRACKET = 91;
+ public final static int KEYCODE_BACKSLASH = 92;
+ public final static int KEYCODE_RIGHTBRACKET = 93;
+ public final static int KEYCODE_CARET = 94;
+ public final static int KEYCODE_UNDERSCORE = 95;
+ public final static int KEYCODE_BACKQUOTE = 96;
+ public final static int KEYCODE_a = 97;
+ public final static int KEYCODE_b = 98;
+ public final static int KEYCODE_c = 99;
+ public final static int KEYCODE_d = 100;
+ public final static int KEYCODE_e = 101;
+ public final static int KEYCODE_f = 102;
+ public final static int KEYCODE_g = 103;
+ public final static int KEYCODE_h = 104;
+ public final static int KEYCODE_i = 105;
+ public final static int KEYCODE_j = 106;
+ public final static int KEYCODE_k = 107;
+ public final static int KEYCODE_l = 108;
+ public final static int KEYCODE_m = 109;
+ public final static int KEYCODE_n = 110;
+ public final static int KEYCODE_o = 111;
+ public final static int KEYCODE_p = 112;
+ public final static int KEYCODE_q = 113;
+ public final static int KEYCODE_r = 114;
+ public final static int KEYCODE_s = 115;
+ public final static int KEYCODE_t = 116;
+ public final static int KEYCODE_u = 117;
+ public final static int KEYCODE_v = 118;
+ public final static int KEYCODE_w = 119;
+ public final static int KEYCODE_x = 120;
+ public final static int KEYCODE_y = 121;
+ public final static int KEYCODE_z = 122;
+ public final static int KEYCODE_DELETE = 127;
+ // Numeric keypad
+ public final static int KEYCODE_KP0 = 256;
+ public final static int KEYCODE_KP1 = 257;
+ public final static int KEYCODE_KP2 = 258;
+ public final static int KEYCODE_KP3 = 259;
+ public final static int KEYCODE_KP4 = 260;
+ public final static int KEYCODE_KP5 = 261;
+ public final static int KEYCODE_KP6 = 262;
+ public final static int KEYCODE_KP7 = 263;
+ public final static int KEYCODE_KP8 = 264;
+ public final static int KEYCODE_KP9 = 265;
+ public final static int KEYCODE_KP_PERIOD = 266;
+ public final static int KEYCODE_KP_DIVIDE = 267;
+ public final static int KEYCODE_KP_MULTIPLY = 268;
+ public final static int KEYCODE_KP_MINUS = 269;
+ public final static int KEYCODE_KP_PLUS = 270;
+ public final static int KEYCODE_KP_ENTER = 271;
+ public final static int KEYCODE_KP_EQUALS = 272;
+ // Arrows + Home/End pad
+ public final static int KEYCODE_UP = 273;
+ public final static int KEYCODE_DOWN = 274;
+ public final static int KEYCODE_RIGHT = 275;
+ public final static int KEYCODE_LEFT = 276;
+ public final static int KEYCODE_INSERT = 277;
+ public final static int KEYCODE_HOME = 278;
+ public final static int KEYCODE_END = 279;
+ public final static int KEYCODE_PAGEUP = 280;
+ public final static int KEYCODE_PAGEDOWN = 281;
+ // Function keys
+ public final static int KEYCODE_F1 = 282;
+ public final static int KEYCODE_F2 = 283;
+ public final static int KEYCODE_F3 = 284;
+ public final static int KEYCODE_F4 = 285;
+ public final static int KEYCODE_F5 = 286;
+ public final static int KEYCODE_F6 = 287;
+ public final static int KEYCODE_F7 = 288;
+ public final static int KEYCODE_F8 = 289;
+ public final static int KEYCODE_F9 = 290;
+ public final static int KEYCODE_F10 = 291;
+ public final static int KEYCODE_F11 = 292;
+ public final static int KEYCODE_F12 = 293;
+ public final static int KEYCODE_F13 = 294;
+ public final static int KEYCODE_F14 = 295;
+ public final static int KEYCODE_F15 = 296;
+ // Key state modifier keys
+ public final static int KEYCODE_NUMLOCK = 300;
+ public final static int KEYCODE_CAPSLOCK = 301;
+ public final static int KEYCODE_SCROLLOCK = 302;
+ public final static int KEYCODE_RSHIFT = 303;
+ public final static int KEYCODE_LSHIFT = 304;
+ public final static int KEYCODE_RCTRL = 305;
+ public final static int KEYCODE_LCTRL = 306;
+ public final static int KEYCODE_RALT = 307;
+ public final static int KEYCODE_LALT = 308;
+ public final static int KEYCODE_RMETA = 309;
+ public final static int KEYCODE_LMETA = 310;
+ public final static int KEYCODE_LSUPER = 311; // Left "Windows" key
+ public final static int KEYCODE_RSUPER = 312; // Right "Windows" key
+ public final static int KEYCODE_MODE = 313; // "Alt Gr" key
+ public final static int KEYCODE_COMPOSE = 314; // Multi-key compose key
+ // Miscellaneous function keys
+ public final static int KEYCODE_HELP = 315;
+ public final static int KEYCODE_PRINT = 316;
+ public final static int KEYCODE_SYSREQ = 317;
+ public final static int KEYCODE_BREAK = 318;
+ public final static int KEYCODE_MENU = 319;
+ public final static int KEYCODE_POWER = 320; // Power Macintosh power key
+ public final static int KEYCODE_EURO = 321; // Some european keyboards
+ public final static int KEYCODE_UNDO = 322; // Atari keyboard has Undo
+
+ // Android KeyEvent keycode -> ScummVM keycode
+ public final static Map<Integer, Integer> androidKeyMap;
+ static {
+ Map<Integer, Integer> map = new HashMap<Integer, Integer>();
+
+ map.put(KeyEvent.KEYCODE_DEL, KEYCODE_BACKSPACE);
+ map.put(KeyEvent.KEYCODE_TAB, KEYCODE_TAB);
+ map.put(KeyEvent.KEYCODE_CLEAR, KEYCODE_CLEAR);
+ map.put(KeyEvent.KEYCODE_ENTER, KEYCODE_RETURN);
+ //map.put(??, KEYCODE_PAUSE);
+ map.put(KeyEvent.KEYCODE_BACK, KEYCODE_ESCAPE);
+ map.put(KeyEvent.KEYCODE_SPACE, KEYCODE_SPACE);
+ //map.put(??, KEYCODE_EXCLAIM);
+ //map.put(??, KEYCODE_QUOTEDBL);
+ map.put(KeyEvent.KEYCODE_POUND, KEYCODE_HASH);
+ //map.put(??, KEYCODE_DOLLAR);
+ //map.put(??, KEYCODE_AMPERSAND);
+ map.put(KeyEvent.KEYCODE_APOSTROPHE, KEYCODE_QUOTE);
+ //map.put(??, KEYCODE_LEFTPAREN);
+ //map.put(??, KEYCODE_RIGHTPAREN);
+ //map.put(??, KEYCODE_ASTERISK);
+ map.put(KeyEvent.KEYCODE_PLUS, KEYCODE_PLUS);
+ map.put(KeyEvent.KEYCODE_COMMA, KEYCODE_COMMA);
+ map.put(KeyEvent.KEYCODE_MINUS, KEYCODE_MINUS);
+ map.put(KeyEvent.KEYCODE_PERIOD, KEYCODE_PERIOD);
+ map.put(KeyEvent.KEYCODE_SLASH, KEYCODE_SLASH);
+ map.put(KeyEvent.KEYCODE_0, KEYCODE_0);
+ map.put(KeyEvent.KEYCODE_1, KEYCODE_1);
+ map.put(KeyEvent.KEYCODE_2, KEYCODE_2);
+ map.put(KeyEvent.KEYCODE_3, KEYCODE_3);
+ map.put(KeyEvent.KEYCODE_4, KEYCODE_4);
+ map.put(KeyEvent.KEYCODE_5, KEYCODE_5);
+ map.put(KeyEvent.KEYCODE_6, KEYCODE_6);
+ map.put(KeyEvent.KEYCODE_7, KEYCODE_7);
+ map.put(KeyEvent.KEYCODE_8, KEYCODE_8);
+ map.put(KeyEvent.KEYCODE_9, KEYCODE_9);
+ //map.put(??, KEYCODE_COLON);
+ map.put(KeyEvent.KEYCODE_SEMICOLON, KEYCODE_SEMICOLON);
+ //map.put(??, KEYCODE_LESS);
+ map.put(KeyEvent.KEYCODE_EQUALS, KEYCODE_EQUALS);
+ //map.put(??, KEYCODE_GREATER);
+ //map.put(??, KEYCODE_QUESTION);
+ map.put(KeyEvent.KEYCODE_AT, KEYCODE_AT);
+ map.put(KeyEvent.KEYCODE_LEFT_BRACKET, KEYCODE_LEFTBRACKET);
+ map.put(KeyEvent.KEYCODE_BACKSLASH, KEYCODE_BACKSLASH);
+ map.put(KeyEvent.KEYCODE_RIGHT_BRACKET, KEYCODE_RIGHTBRACKET);
+ //map.put(??, KEYCODE_CARET);
+ //map.put(??, KEYCODE_UNDERSCORE);
+ //map.put(??, KEYCODE_BACKQUOTE);
+ map.put(KeyEvent.KEYCODE_A, KEYCODE_a);
+ map.put(KeyEvent.KEYCODE_B, KEYCODE_b);
+ map.put(KeyEvent.KEYCODE_C, KEYCODE_c);
+ map.put(KeyEvent.KEYCODE_D, KEYCODE_d);
+ map.put(KeyEvent.KEYCODE_E, KEYCODE_e);
+ map.put(KeyEvent.KEYCODE_F, KEYCODE_f);
+ map.put(KeyEvent.KEYCODE_G, KEYCODE_g);
+ map.put(KeyEvent.KEYCODE_H, KEYCODE_h);
+ map.put(KeyEvent.KEYCODE_I, KEYCODE_i);
+ map.put(KeyEvent.KEYCODE_J, KEYCODE_j);
+ map.put(KeyEvent.KEYCODE_K, KEYCODE_k);
+ map.put(KeyEvent.KEYCODE_L, KEYCODE_l);
+ map.put(KeyEvent.KEYCODE_M, KEYCODE_m);
+ map.put(KeyEvent.KEYCODE_N, KEYCODE_n);
+ map.put(KeyEvent.KEYCODE_O, KEYCODE_o);
+ map.put(KeyEvent.KEYCODE_P, KEYCODE_p);
+ map.put(KeyEvent.KEYCODE_Q, KEYCODE_q);
+ map.put(KeyEvent.KEYCODE_R, KEYCODE_r);
+ map.put(KeyEvent.KEYCODE_S, KEYCODE_s);
+ map.put(KeyEvent.KEYCODE_T, KEYCODE_t);
+ map.put(KeyEvent.KEYCODE_U, KEYCODE_u);
+ map.put(KeyEvent.KEYCODE_V, KEYCODE_v);
+ map.put(KeyEvent.KEYCODE_W, KEYCODE_w);
+ map.put(KeyEvent.KEYCODE_X, KEYCODE_x);
+ map.put(KeyEvent.KEYCODE_Y, KEYCODE_y);
+ map.put(KeyEvent.KEYCODE_Z, KEYCODE_z);
+ //map.put(KeyEvent.KEYCODE_DEL, KEYCODE_DELETE); use BACKSPACE instead
+ //map.put(??, KEYCODE_KP_*);
+ map.put(KeyEvent.KEYCODE_DPAD_UP, KEYCODE_UP);
+ map.put(KeyEvent.KEYCODE_DPAD_DOWN, KEYCODE_DOWN);
+ map.put(KeyEvent.KEYCODE_DPAD_RIGHT, KEYCODE_RIGHT);
+ map.put(KeyEvent.KEYCODE_DPAD_LEFT, KEYCODE_LEFT);
+ //map.put(??, KEYCODE_INSERT);
+ //map.put(??, KEYCODE_HOME);
+ //map.put(??, KEYCODE_END);
+ //map.put(??, KEYCODE_PAGEUP);
+ //map.put(??, KEYCODE_PAGEDOWN);
+ //map.put(??, KEYCODE_F{1-15});
+ map.put(KeyEvent.KEYCODE_NUM, KEYCODE_NUMLOCK);
+ //map.put(??, KEYCODE_CAPSLOCK);
+ //map.put(??, KEYCODE_SCROLLLOCK);
+ map.put(KeyEvent.KEYCODE_SHIFT_RIGHT, KEYCODE_RSHIFT);
+ map.put(KeyEvent.KEYCODE_SHIFT_LEFT, KEYCODE_LSHIFT);
+ //map.put(??, KEYCODE_RCTRL);
+ //map.put(??, KEYCODE_LCTRL);
+ map.put(KeyEvent.KEYCODE_ALT_RIGHT, KEYCODE_RALT);
+ map.put(KeyEvent.KEYCODE_ALT_LEFT, KEYCODE_LALT);
+ // ?? META, SUPER
+ // ?? MODE, COMPOSE
+ // ?? HELP, PRINT, SYSREQ, BREAK, EURO, UNDO
+ map.put(KeyEvent.KEYCODE_MENU, KEYCODE_MENU);
+ map.put(KeyEvent.KEYCODE_POWER, KEYCODE_POWER);
+
+ androidKeyMap = Collections.unmodifiableMap(map);
+ }
+
+ public int type;
+ public boolean synthetic;
+ public int kbd_keycode;
+ public int kbd_ascii;
+ public int kbd_flags;
+ public int mouse_x;
+ public int mouse_y;
+ public boolean mouse_relative; // Used for trackball events
+
+ public Event() {
+ type = EVENT_INVALID;
+ synthetic = false;
+ }
+
+ public Event(int type) {
+ this.type = type;
+ synthetic = false;
+ }
+
+ public static Event KeyboardEvent(int type, int keycode, int ascii,
+ int flags) {
+ Event e = new Event();
+ e.type = type;
+ e.kbd_keycode = keycode;
+ e.kbd_ascii = ascii;
+ e.kbd_flags = flags;
+ return e;
+ }
+
+ public static Event MouseEvent(int type, int x, int y) {
+ Event e = new Event();
+ e.type = type;
+ e.mouse_x = x;
+ e.mouse_y = y;
+ return e;
+ }
+}
diff --git a/backends/platform/android/org/inodes/gus/scummvm/PluginProvider.java b/backends/platform/android/org/inodes/gus/scummvm/PluginProvider.java
new file mode 100644
index 0000000000..840f3440d5
--- /dev/null
+++ b/backends/platform/android/org/inodes/gus/scummvm/PluginProvider.java
@@ -0,0 +1,52 @@
+package org.inodes.gus.scummvm;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+public class PluginProvider extends BroadcastReceiver {
+ public final static String META_UNPACK_LIB =
+ "org.inodes.gus.scummvm.meta.UNPACK_LIB";
+
+ public void onReceive(Context context, Intent intent) {
+ if (!intent.getAction().equals(ScummVMApplication.ACTION_PLUGIN_QUERY))
+ return;
+
+ Bundle extras = getResultExtras(true);
+
+ final ActivityInfo info;
+ try {
+ info = context.getPackageManager()
+ .getReceiverInfo(new ComponentName(context, this.getClass()),
+ PackageManager.GET_META_DATA);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(this.toString(), "Error finding my own info?", e);
+ return;
+ }
+
+ String mylib = info.metaData.getString(META_UNPACK_LIB);
+ 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());
+
+ extras.putStringArrayList(ScummVMApplication.EXTRA_UNPACK_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
new file mode 100644
index 0000000000..f4dca0e7e5
--- /dev/null
+++ b/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java
@@ -0,0 +1,317 @@
+package org.inodes.gus.scummvm;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+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.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
+
+import java.io.File;
+import java.util.concurrent.Semaphore;
+
+
+// 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 class ScummVM implements SurfaceHolder.Callback {
+ private final static String LOG_TAG = "ScummVM.java";
+
+ private final int AUDIO_FRAME_SIZE = 2 * 2; // bytes. 16bit audio * stereo
+ public static class AudioSetupException extends Exception {}
+
+ private long nativeScummVM; // native code hangs itself here
+ boolean scummVMRunning = false;
+
+ private native void create(AssetManager am);
+
+ public ScummVM(Context context) {
+ create(context.getAssets()); // Init C++ code, set nativeScummVM
+ }
+
+ private native void nativeDestroy();
+
+ public synchronized void destroy() {
+ if (nativeScummVM != 0) {
+ nativeDestroy();
+ nativeScummVM = 0;
+ }
+ }
+ protected void finalize() {
+ destroy();
+ }
+
+ // 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,
+ EGL10.EGL_BLUE_SIZE, 5,
+ EGL10.EGL_DEPTH_SIZE, 0,
+ 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) {
+ pushEvent(new Event(Event.EVENT_SCREEN_CHANGED));
+ try {
+ surfaceLock.acquire();
+ } catch (InterruptedException e) {
+ Log.e(this.toString(),
+ "Interrupted while waiting for surface lock", e);
+ }
+ }
+
+ // 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);
+ eglConfig = configs[0];
+
+ eglContext = egl.eglCreateContext(eglDisplay, eglConfig,
+ EGL10.EGL_NO_CONTEXT, null);
+ }
+
+ // Called by ScummVM thread
+ protected void setupScummVMSurface() {
+ try {
+ surfaceLock.acquire();
+ } catch (InterruptedException e) {
+ Log.e(this.toString(),
+ "Interrupted while waiting for surface lock", e);
+ return;
+ }
+ eglSurface = egl.eglCreateWindowSurface(eglDisplay, eglConfig,
+ nativeSurface, null);
+ egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
+ }
+
+ // 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);
+ }
+
+ // 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);
+
+ // 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(this.toString(), "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);
+ } 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
new file mode 100644
index 0000000000..fb3cd6348f
--- /dev/null
+++ b/backends/platform/android/org/inodes/gus/scummvm/ScummVMActivity.java
@@ -0,0 +1,446 @@
+package org.inodes.gus.scummvm;
+
+import android.app.AlertDialog;
+import android.app.Activity;
+import android.content.DialogInterface;
+import android.content.res.Configuration;
+import android.media.AudioManager;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.inputmethod.InputMethodManager;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.widget.Toast;
+
+import java.io.IOException;
+
+public class ScummVMActivity extends Activity {
+ private boolean _do_right_click;
+ private boolean _last_click_was_right;
+
+ // game pixels to move per trackball/dpad event.
+ // FIXME: replace this with proper mouse acceleration
+ private final static int TRACKBALL_SCALE = 2;
+
+ private class MyScummVM extends ScummVM {
+ private boolean scummvmRunning = false;
+
+ public MyScummVM() {
+ super(ScummVMActivity.this);
+ }
+
+ @Override
+ protected void initBackend() throws ScummVM.AudioSetupException {
+ synchronized (this) {
+ scummvmRunning = true;
+ notifyAll();
+ }
+ super.initBackend();
+ }
+
+ public void waitUntilRunning() throws InterruptedException {
+ synchronized (this) {
+ while (!scummvmRunning)
+ wait();
+ }
+ }
+
+ @Override
+ protected void displayMessageOnOSD(String msg) {
+ Log.i(this.toString(), "OSD: " + msg);
+ Toast.makeText(ScummVMActivity.this, msg, Toast.LENGTH_LONG).show();
+ }
+
+ @Override
+ protected void setWindowCaption(final String caption) {
+ runOnUiThread(new Runnable() {
+ public void run() {
+ setTitle(caption);
+ }
+ });
+ }
+
+ @Override
+ protected String[] getPluginDirectories() {
+ String[] dirs = new String[1];
+ dirs[0] = ScummVMApplication.getLastCacheDir().getPath();
+ return dirs;
+ }
+
+ @Override
+ protected void showVirtualKeyboard(final boolean enable) {
+ if (getResources().getConfiguration().keyboard ==
+ Configuration.KEYBOARD_NOKEYS) {
+ runOnUiThread(new Runnable() {
+ public void run() {
+ showKeyboard(enable);
+ }
+ });
+ }
+ }
+ }
+ private MyScummVM scummvm;
+ private Thread scummvm_thread;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ _do_right_click = false;
+ setVolumeControlStream(AudioManager.STREAM_MUSIC);
+
+ setContentView(R.layout.main);
+ takeKeyEvents(true);
+
+ // This is a common enough error that we should warn about it
+ // explicitly.
+ if (!Environment.getExternalStorageDirectory().canRead()) {
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.no_sdcard_title)
+ .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();
+ }
+ })
+ .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", "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(this.toString(),
+ "Interrupted while waiting for ScummVM.initBackend", e);
+ finish();
+ }
+
+ scummvm.setSurface(main_surface.getHolder());
+ }
+
+ // Runs in another thread
+ private void runScummVM() throws IOException {
+ getFilesDir().mkdirs();
+ String[] args = {
+ "ScummVM-lib",
+ "--config=" + getFileStreamPath("scummvmrc").getPath(),
+ "--path=" + Environment.getExternalStorageDirectory().getPath(),
+ "--gui-theme=scummmodern",
+ "--savepath=" + getDir("saves", 0).getPath(),
+ };
+
+ int ret = scummvm.scummVMMain(args);
+
+ // On exit, tear everything down for a fresh
+ // restart next time.
+ System.exit(ret);
+ }
+
+ private boolean was_paused = false;
+
+ @Override
+ public void onPause() {
+ if (scummvm != null) {
+ was_paused = true;
+ scummvm.pause();
+ }
+ super.onPause();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (scummvm != null && was_paused)
+ scummvm.resume();
+ was_paused = false;
+ }
+
+ @Override
+ public void onStop() {
+ if (scummvm != null) {
+ scummvm.pushEvent(new Event(Event.EVENT_QUIT));
+ try {
+ scummvm_thread.join(1000); // 1s timeout
+ } catch (InterruptedException e) {
+ Log.i(this.toString(),
+ "Error while joining ScummVM thread", e);
+ }
+ }
+ super.onStop();
+ }
+
+ static final int MSG_MENU_LONG_PRESS = 1;
+ private final Handler keycodeMenuTimeoutHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_MENU_LONG_PRESS) {
+ InputMethodManager imm = (InputMethodManager)
+ getSystemService(INPUT_METHOD_SERVICE);
+ if (imm != null)
+ imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
+ }
+ }
+ };
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent kevent) {
+ return onKeyDown(keyCode, kevent);
+ }
+
+ @Override
+ public boolean onKeyMultiple(int keyCode, int repeatCount,
+ KeyEvent kevent) {
+ return onKeyDown(keyCode, kevent);
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent kevent) {
+ // Filter out "special" keys
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_MENU:
+ // Have to reimplement hold-down-menu-brings-up-softkeybd
+ // 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.
+ 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;
+ }
+ }
+ if (kevent.getAction() == KeyEvent.ACTION_UP) {
+ if (!timeout_fired)
+ 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:
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ case KeyEvent.KEYCODE_DPAD_RIGHT: {
+ // HTC Hero doesn't seem to generate
+ // MotionEvent.ACTION_DOWN events on trackball press :(
+ // We'll have to just fake one here.
+ // 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
+ return false;
+ }
+ } 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);
+ return true;
+ }
+ case KeyEvent.KEYCODE_BACK:
+ // skip isSystem() check and fall through to main code
+ break;
+ default:
+ if (kevent.isSystem())
+ return false;
+ }
+
+ // FIXME: what do I need to do for composed characters?
+
+ Event e = new Event();
+
+ switch (kevent.getAction()) {
+ case KeyEvent.ACTION_DOWN:
+ 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 (?)
+ e.kbd_flags |= Event.KBD_CTRL;
+ if (kevent.isShiftPressed()) {
+ if (keyCode >= KeyEvent.KEYCODE_0 &&
+ 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
+ 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);
+ e.type = Event.EVENT_KEYUP;
+ scummvm.pushEvent(e);
+ }
+ } else
+ scummvm.pushEvent(e);
+
+ return true;
+ }
+
+ private int getEventType(int action) {
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ _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;
+ }
+ }
+
+ @Override
+ public boolean onTrackballEvent(MotionEvent event) {
+ int type = getEventType(event.getAction());
+ if (type == Event.EVENT_INVALID)
+ return false;
+
+ Event e = new Event(type);
+ e.mouse_x =
+ (int)(event.getX() * event.getXPrecision()) * TRACKBALL_SCALE;
+ e.mouse_y =
+ (int)(event.getY() * event.getYPrecision()) * TRACKBALL_SCALE;
+ e.mouse_relative = true;
+ scummvm.pushEvent(e);
+
+ return true;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ int type = getEventType(event.getAction());
+ if (type == Event.EVENT_INVALID)
+ return false;
+
+ Event e = new Event(type);
+ e.mouse_x = (int)event.getX();
+ e.mouse_y = (int)event.getY();
+ e.mouse_relative = false;
+ scummvm.pushEvent(e);
+
+ return true;
+ }
+
+ private void showKeyboard(boolean show) {
+ 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
+ imm.hideSoftInputFromWindow(main_surface.getWindowToken(),
+ 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
new file mode 100644
index 0000000000..37a9d09e1a
--- /dev/null
+++ b/backends/platform/android/org/inodes/gus/scummvm/ScummVMApplication.java
@@ -0,0 +1,29 @@
+package org.inodes.gus.scummvm;
+
+import android.app.Application;
+
+import java.io.File;
+
+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;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ // This is still on /data :(
+ cache_dir = getCacheDir();
+ // This is mounted noexec :(
+ //cache_dir = new File(Environment.getExternalStorageDirectory(),
+ // "/.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;
+ }
+}
diff --git a/backends/platform/android/org/inodes/gus/scummvm/Unpacker.java b/backends/platform/android/org/inodes/gus/scummvm/Unpacker.java
new file mode 100644
index 0000000000..efa3e1d2ef
--- /dev/null
+++ b/backends/platform/android/org/inodes/gus/scummvm/Unpacker.java
@@ -0,0 +1,370 @@
+package org.inodes.gus.scummvm;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.ProgressBar;
+
+import java.io.IOException;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipEntry;
+
+public class Unpacker extends Activity {
+ private final static String META_NEXT_ACTIVITY =
+ "org.inodes.gus.unpacker.nextActivity";
+ private ProgressBar mProgress;
+ private File mUnpackDest; // location to unpack into
+ private AsyncTask<String, Integer, Void> mUnpacker;
+ private final static int REQUEST_MARKET = 1;
+
+ private static class UnpackJob {
+ public ZipFile zipfile;
+ public Set<String> paths;
+
+ public UnpackJob(ZipFile zipfile, Set<String> paths) {
+ this.zipfile = zipfile;
+ this.paths = paths;
+ }
+
+ public long UnpackSize() {
+ long size = 0;
+ for (String path: paths) {
+ ZipEntry entry = zipfile.getEntry(path);
+ if (entry != null) size += entry.getSize();
+ }
+ return size;
+ }
+ }
+
+ private class UnpackTask extends AsyncTask<String, Integer, Void> {
+ @Override
+ protected void onProgressUpdate(Integer... progress) {
+ mProgress.setIndeterminate(false);
+ mProgress.setMax(progress[1]);
+ mProgress.setProgress(progress[0]);
+ mProgress.postInvalidate();
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ Bundle md = getMetaData();
+ String nextActivity = md.getString(META_NEXT_ACTIVITY);
+ if (nextActivity != null) {
+ final ComponentName cn =
+ ComponentName.unflattenFromString(nextActivity);
+ if (cn != null) {
+ final Intent origIntent = getIntent();
+ Intent intent = new Intent();
+ intent.setPackage(origIntent.getPackage());
+ intent.setComponent(cn);
+ if (origIntent.getExtras() != null)
+ intent.putExtras(origIntent.getExtras());
+ intent.putExtra(Intent.EXTRA_INTENT, origIntent);
+ intent.setDataAndType(origIntent.getData(),
+ origIntent.getType());
+ //intent.fillIn(getIntent(), 0);
+ intent.addFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
+ Log.i(this.toString(),
+ "Starting next activity with intent " + intent);
+ startActivity(intent);
+ } else {
+ Log.w(this.toString(),
+ "Unable to extract a component name from " + nextActivity);
+ }
+ }
+
+ finish();
+ }
+
+ @Override
+ protected Void doInBackground(String... all_libs) {
+ // This will contain all unpack jobs
+ Map<String, UnpackJob> unpack_jobs =
+ new HashMap<String, UnpackJob>(all_libs.length);
+
+ // This will contain all unpack filenames (so we can
+ // detect stale files in the unpack directory)
+ Set<String> all_files = new HashSet<String>(all_libs.length);
+
+ for (String lib: all_libs) {
+ final Uri uri = Uri.parse(lib);
+ final String pkg = uri.getAuthority();
+ final String path = uri.getPath().substring(1); // skip first /
+
+ all_files.add(new File(path).getName());
+
+ UnpackJob job = unpack_jobs.get(pkg);
+ if (job == null) {
+ try {
+ // getPackageResourcePath is hidden in Context,
+ // but exposed in ContextWrapper...
+ ContextWrapper context =
+ new ContextWrapper(createPackageContext(pkg, 0));
+ ZipFile zipfile =
+ new ZipFile(context.getPackageResourcePath());
+ job = new UnpackJob(zipfile, new HashSet<String>(1));
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(this.toString(), "Package " + pkg +
+ " not found", e);
+ continue;
+ } catch (IOException e) {
+ // FIXME: show some sort of GUI error dialog
+ Log.e(this.toString(),
+ "Error opening ZIP for package " + pkg, e);
+ continue;
+ }
+ unpack_jobs.put(pkg, job);
+ }
+ job.paths.add(path);
+ }
+
+ // Delete stale filenames from mUnpackDest
+ for (File file: mUnpackDest.listFiles()) {
+ if (!all_files.contains(file.getName())) {
+ Log.i(this.toString(),
+ "Deleting stale cached file " + file);
+ file.delete();
+ }
+ }
+
+ int total_size = 0;
+ for (UnpackJob job: unpack_jobs.values())
+ total_size += job.UnpackSize();
+
+ publishProgress(0, total_size);
+
+ mUnpackDest.mkdirs();
+
+ int progress = 0;
+
+ for (UnpackJob job: unpack_jobs.values()) {
+ try {
+ ZipFile zipfile = job.zipfile;
+ for (String path: job.paths) {
+ ZipEntry zipentry = zipfile.getEntry(path);
+ if (zipentry == null)
+ throw new FileNotFoundException(
+ "Couldn't find " + path + " in zip");
+ File dest = new File(mUnpackDest, new File(path).getName());
+ if (dest.exists() &&
+ dest.lastModified() == zipentry.getTime() &&
+ dest.length() == zipentry.getSize()) {
+ // Already unpacked
+ progress += zipentry.getSize();
+ } else {
+ if (dest.exists())
+ Log.d(this.toString(),
+ "Replacing " + dest.getPath() +
+ " old.mtime=" + dest.lastModified() +
+ " new.mtime=" + zipentry.getTime() +
+ " old.size=" + dest.length() +
+ " new.size=" + zipentry.getSize());
+ else
+ Log.i(this.toString(),
+ "Extracting " + zipentry.getName() +
+ " from " + zipfile.getName() +
+ " to " + dest.getPath());
+
+ long next_update = progress;
+
+ InputStream in = zipfile.getInputStream(zipentry);
+ OutputStream out = new FileOutputStream(dest);
+ int len;
+ byte[] buffer = new byte[4096];
+ while ((len = in.read(buffer)) != -1) {
+ out.write(buffer, 0, len);
+ progress += len;
+ if (progress >= next_update) {
+ publishProgress(progress, total_size);
+ // Arbitrary limit of 2% update steps
+ next_update += total_size / 50;
+ }
+ }
+
+ in.close();
+ out.close();
+ dest.setLastModified(zipentry.getTime());
+ }
+ publishProgress(progress, total_size);
+ }
+
+ zipfile.close();
+ } catch (IOException e) {
+ // FIXME: show some sort of GUI error dialog
+ Log.e(this.toString(), "Error unpacking plugin", e);
+ }
+ }
+
+ if (progress != total_size)
+ Log.d(this.toString(), "Ended with progress " + progress +
+ " != total size " + total_size);
+
+ setResult(RESULT_OK);
+
+ return null;
+ }
+ }
+
+ private class PluginBroadcastReciever extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!intent.getAction()
+ .equals(ScummVMApplication.ACTION_PLUGIN_QUERY)) {
+ Log.e(this.toString(),
+ "Received unexpected action " + intent.getAction());
+ return;
+ }
+
+ Bundle extras = getResultExtras(false);
+ if (extras == null) {
+ // Nothing for us to do.
+ Unpacker.this.setResult(RESULT_OK);
+ finish();
+ }
+
+ ArrayList<String> unpack_libs =
+ extras.getStringArrayList(ScummVMApplication.EXTRA_UNPACK_LIBS);
+
+ if (unpack_libs != null && !unpack_libs.isEmpty()) {
+ final String[] libs =
+ unpack_libs.toArray(new String[unpack_libs.size()]);
+ mUnpacker = new UnpackTask().execute(libs);
+ }
+ }
+ }
+
+ private void initPlugins() {
+ Bundle extras = new Bundle(1);
+
+ ArrayList<String> unpack_libs = new ArrayList<String>(1);
+ // This is the common ScummVM code (not really a "plugin" as such)
+ unpack_libs.add(new Uri.Builder()
+ .scheme("plugin")
+ .authority(getPackageName())
+ .path("mylib/armeabi/libscummvm.so")
+ .toString());
+ extras.putStringArrayList(ScummVMApplication.EXTRA_UNPACK_LIBS,
+ unpack_libs);
+
+ Intent intent = new Intent(ScummVMApplication.ACTION_PLUGIN_QUERY);
+ sendOrderedBroadcast(intent, Manifest.permission.SCUMMVM_PLUGIN,
+ new PluginBroadcastReciever(),
+ null, RESULT_OK, null, extras);
+ }
+
+ @Override
+ public void onCreate(Bundle b) {
+ super.onCreate(b);
+
+ mUnpackDest = ScummVMApplication.getLastCacheDir();
+
+ setContentView(R.layout.splash);
+ mProgress = (ProgressBar)findViewById(R.id.progress);
+
+ setResult(RESULT_CANCELED);
+
+ tryUnpack();
+ }
+
+ private void tryUnpack() {
+ Intent intent = new Intent(ScummVMApplication.ACTION_PLUGIN_QUERY);
+ List<ResolveInfo> plugins = getPackageManager()
+ .queryBroadcastReceivers(intent, 0);
+ if (plugins.isEmpty()) {
+ // No plugins installed
+ AlertDialog.Builder alert = new AlertDialog.Builder(this)
+ .setTitle(R.string.no_plugins_title)
+ .setMessage(R.string.no_plugins_found)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setOnCancelListener(new DialogInterface.OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
+ finish();
+ }
+ })
+ .setNegativeButton(R.string.quit,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ finish();
+ }
+ });
+
+ final Uri uri = Uri.parse("market://search?q=ScummVM plugin");
+ final Intent market_intent = new Intent(Intent.ACTION_VIEW, uri);
+ if (getPackageManager().resolveActivity(market_intent, 0) != null) {
+ alert.setPositiveButton(R.string.to_market,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ try {
+ startActivityForResult(market_intent,
+ REQUEST_MARKET);
+ } catch (ActivityNotFoundException e) {
+ Log.e(this.toString(),
+ "Error starting market", e);
+ }
+ }
+ });
+ }
+
+ alert.show();
+
+ } else {
+ // Already have at least one plugin installed
+ initPlugins();
+ }
+ }
+
+ @Override
+ public void onStop() {
+ if (mUnpacker != null)
+ mUnpacker.cancel(true);
+ super.onStop();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode,
+ Intent data) {
+ switch (requestCode) {
+ case REQUEST_MARKET:
+ if (resultCode != RESULT_OK)
+ Log.w(this.toString(), "Market returned " + resultCode);
+ tryUnpack();
+ break;
+ }
+ }
+
+ private Bundle getMetaData() {
+ try {
+ ActivityInfo ai = getPackageManager()
+ .getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
+ return ai.metaData;
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(this.toString(), "Unable to find my own meta-data", e);
+ return new Bundle();
+ }
+ }
+}
diff --git a/backends/platform/android/scummvm-android-themeengine.patch b/backends/platform/android/scummvm-android-themeengine.patch
new file mode 100644
index 0000000000..1eafe7fb62
--- /dev/null
+++ b/backends/platform/android/scummvm-android-themeengine.patch
@@ -0,0 +1,135 @@
+diff -r 884e66fd1b9c gui/ThemeEngine.cpp
+--- a/gui/ThemeEngine.cpp Tue Apr 13 09:30:52 2010 +1000
++++ b/gui/ThemeEngine.cpp Fri May 28 23:24:43 2010 +1000
+@@ -390,21 +390,19 @@
+
+ // Try to create a Common::Archive with the files of the theme.
+ if (!_themeArchive && !_themeFile.empty()) {
+- Common::FSNode node(_themeFile);
+- if (node.getName().hasSuffix(".zip") && !node.isDirectory()) {
++ Common::ArchiveMemberPtr member = SearchMan.getMember(_themeFile);
++ if (member && member->getName().hasSuffix(".zip")) {
+ #ifdef USE_ZLIB
+- Common::Archive *zipArchive = Common::makeZipArchive(node);
++ Common::Archive *zipArchive = Common::makeZipArchive(member->createReadStream());
+
+ if (!zipArchive) {
+- warning("Failed to open Zip archive '%s'.", node.getPath().c_str());
++ warning("Failed to open Zip archive '%s'.", member->getDisplayName().c_str());
+ }
+ _themeArchive = zipArchive;
+ #else
+ warning("Trying to load theme '%s' in a Zip archive without zLib support", _themeFile.c_str());
+ return false;
+ #endif
+- } else if (node.isDirectory()) {
+- _themeArchive = new Common::FSDirectory(node);
+ }
+ }
+
+@@ -1436,6 +1434,30 @@
+ return tok.empty();
+ }
+
++bool ThemeEngine::themeConfigUsable(const Common::ArchiveMember &member, Common::String &themeName) {
++ Common::File stream;
++ bool foundHeader = false;
++
++ if (member.getName().hasSuffix(".zip")) {
++#ifdef USE_ZLIB
++ Common::Archive *zipArchive = Common::makeZipArchive(member.createReadStream());
++
++ if (zipArchive && zipArchive->hasFile("THEMERC")) {
++ stream.open("THEMERC", *zipArchive);
++ }
++
++ delete zipArchive;
++#endif
++ }
++
++ if (stream.isOpen()) {
++ Common::String stxHeader = stream.readLine();
++ foundHeader = themeConfigParseHeader(stxHeader, themeName);
++ }
++
++ return foundHeader;
++}
++
+ bool ThemeEngine::themeConfigUsable(const Common::FSNode &node, Common::String &themeName) {
+ Common::File stream;
+ bool foundHeader = false;
+@@ -1493,10 +1515,6 @@
+ if (ConfMan.hasKey("themepath"))
+ listUsableThemes(Common::FSNode(ConfMan.get("themepath")), list);
+
+-#ifdef DATA_PATH
+- listUsableThemes(Common::FSNode(DATA_PATH), list);
+-#endif
+-
+ #if defined(MACOSX) || defined(IPHONE)
+ CFURLRef resourceUrl = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle());
+ if (resourceUrl) {
+@@ -1509,10 +1527,7 @@
+ }
+ #endif
+
+- if (ConfMan.hasKey("extrapath"))
+- listUsableThemes(Common::FSNode(ConfMan.get("extrapath")), list);
+-
+- listUsableThemes(Common::FSNode("."), list, 1);
++ listUsableThemes(SearchMan, list);
+
+ // Now we need to strip all duplicates
+ // TODO: It might not be the best idea to strip duplicates. The user might
+@@ -1531,6 +1546,34 @@
+ output.clear();
+ }
+
++void ThemeEngine::listUsableThemes(Common::Archive &archive, Common::List<ThemeDescriptor> &list) {
++ ThemeDescriptor td;
++
++#ifdef USE_ZLIB
++ Common::ArchiveMemberList fileList;
++ archive.listMatchingMembers(fileList, "*.zip");
++ for (Common::ArchiveMemberList::iterator i = fileList.begin();
++ i != fileList.end(); ++i) {
++ td.name.clear();
++ if (themeConfigUsable(**i, td.name)) {
++ td.filename = (*i)->getName();
++ td.id = (*i)->getDisplayName();
++
++ // If the name of the node object also contains
++ // the ".zip" suffix, we will strip it.
++ if (td.id.hasSuffix(".zip")) {
++ for (int j = 0; j < 4; ++j)
++ td.id.deleteLastChar();
++ }
++
++ list.push_back(td);
++ }
++ }
++
++ fileList.clear();
++#endif
++}
++
+ void ThemeEngine::listUsableThemes(const Common::FSNode &node, Common::List<ThemeDescriptor> &list, int depth) {
+ if (!node.exists() || !node.isReadable() || !node.isDirectory())
+ return;
+diff -r 884e66fd1b9c gui/ThemeEngine.h
+--- a/gui/ThemeEngine.h Tue Apr 13 09:30:52 2010 +1000
++++ b/gui/ThemeEngine.h Fri May 28 23:24:43 2010 +1000
+@@ -560,11 +560,13 @@
+ static void listUsableThemes(Common::List<ThemeDescriptor> &list);
+ private:
+ static bool themeConfigUsable(const Common::FSNode &node, Common::String &themeName);
++ static bool themeConfigUsable(const Common::ArchiveMember &member, Common::String &themeName);
+ static bool themeConfigParseHeader(Common::String header, Common::String &themeName);
+
+ static Common::String getThemeFile(const Common::String &id);
+ static Common::String getThemeId(const Common::String &filename);
+ static void listUsableThemes(const Common::FSNode &node, Common::List<ThemeDescriptor> &list, int depth = -1);
++ static void listUsableThemes(Common::Archive &archive, Common::List<ThemeDescriptor> &list);
+
+ protected:
+ OSystem *_system; /** Global system object. */
diff --git a/backends/platform/ds/arm7/source/main.cpp b/backends/platform/ds/arm7/source/main.cpp
index 7029d96405..a4cde02ba6 100644
--- a/backends/platform/ds/arm7/source/main.cpp
+++ b/backends/platform/ds/arm7/source/main.cpp
@@ -38,7 +38,7 @@
#include <system.h>
#include <stdlib.h>
#include <string.h>
-//#include <registers_alt.h> // not needed in current libnds
+#include <registers_alt.h> // Needed for SOUND_CR
#include <NDS/scummvm_ipc.h>
//////////////////////////////////////////////////////////////////////
#ifdef USE_DEBUGGER
@@ -590,7 +590,7 @@ int main(int argc, char ** argv) {
IPC->reset = false;
- fifoInit();
+ //fifoInit();
for (int r = 0; r < 8; r++) {
IPC->adpcm.arm7Buffer[r] = (u8 *) malloc(512);
diff --git a/backends/platform/ds/arm9/dist/readme_ds.txt b/backends/platform/ds/arm9/dist/readme_ds.txt
index 191b358f78..c4f1263452 100644
--- a/backends/platform/ds/arm9/dist/readme_ds.txt
+++ b/backends/platform/ds/arm9/dist/readme_ds.txt
@@ -41,6 +41,10 @@ Visit the main ScummVM website <http://www.scummvm.org>
What's New?
------------------------------------------------------------------------
+ScummVM DS 1.1.1
+
+ * Bugfix release: no new features
+
ScummVM DS 1.1.0
* New games are supported in this stable build: Return to Zork, Rodney's
@@ -319,7 +323,7 @@ CANNOT DO THIS.
------------------------------------------------------------------------
I'm glad you asked. Here is a list of the compatible games in version
-1.1.0. Demo versions of the games listed should work too.
+1.1.1. Demo versions of the games listed should work too.
Flight of the Amazon Queen, Beneath a Steel Sky, and Lure of the
Temptress have generously been released as freeware by the original
diff --git a/backends/platform/ds/arm9/makefile b/backends/platform/ds/arm9/makefile
index eca170ef96..7f03f4c310 100644
--- a/backends/platform/ds/arm9/makefile
+++ b/backends/platform/ds/arm9/makefile
@@ -75,7 +75,7 @@ else
ifdef DS_BUILD_K
else
- USE_MAD = 1
+ # USE_MAD = 1
endif
endif
endif
diff --git a/backends/platform/ds/arm9/source/dsmain.cpp b/backends/platform/ds/arm9/source/dsmain.cpp
index 1e9986e4e9..5a63e5f08f 100644
--- a/backends/platform/ds/arm9/source/dsmain.cpp
+++ b/backends/platform/ds/arm9/source/dsmain.cpp
@@ -103,6 +103,7 @@
#include "profiler/cyg-profile.h"
#endif
#include "backends/fs/ds/ds-fs.h"
+#include "base/version.h"
#include "engine.h"
extern "C" void OurIntrMain(void);
@@ -701,7 +702,7 @@ void displayMode8Bit() {
- consoleInit(NULL, 0, BgType_Text4bpp, BgSize_T_256x256, 2, 0, true);
+ consoleInit(NULL, 0, BgType_Text4bpp, BgSize_T_256x256, 2, 0, true, true);
// Set this again because consoleinit resets it
videoSetMode(MODE_5_2D | (consoleEnable? DISPLAY_BG0_ACTIVE: 0) | DISPLAY_BG3_ACTIVE | DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D | DISPLAY_SPR_1D_BMP);
@@ -939,7 +940,7 @@ void displayMode16Bit() {
SUB_BG0_CR = BG_MAP_BASE(4) | BG_TILE_BASE(0);
SUB_BG0_Y0 = 0;
- consoleInit(NULL, 0, BgType_Text4bpp, BgSize_T_256x256, 4, 0, false);
+ consoleInit(NULL, 0, BgType_Text4bpp, BgSize_T_256x256, 4, 0, false, true);
// consoleInitDefault((u16*)SCREEN_BASE_BLOCK_SUB(4), (u16*)CHAR_BASE_BLOCK_SUB(0), 16);
for (int r = 0; r < 32 * 32; r++) {
@@ -3162,7 +3163,7 @@ int main(void) {
consolePrintf("-------------------------------\n");
consolePrintf("ScummVM DS\n");
consolePrintf("Ported by Neil Millstone\n");
- consolePrintf("Version 1.1.0 ");
+ consolePrintf("Version %s ", gScummVMVersion);
#if defined(DS_BUILD_A)
consolePrintf("build A\n");
consolePrintf("Lucasarts SCUMM games (SCUMM)\n");
diff --git a/backends/platform/ds/commoninclude/NDS/scummvm_ipc.h b/backends/platform/ds/commoninclude/NDS/scummvm_ipc.h
index 9344be68f9..f41548f400 100644
--- a/backends/platform/ds/commoninclude/NDS/scummvm_ipc.h
+++ b/backends/platform/ds/commoninclude/NDS/scummvm_ipc.h
@@ -33,6 +33,18 @@
//////////////////////////////////////////////////////////////////////
+typedef struct sTransferSoundData {
+//---------------------------------------------------------------------------------
+ const void *data;
+ u32 len;
+ u32 rate;
+ u8 vol;
+ u8 pan;
+ u8 format;
+ u8 PADDING;
+} TransferSoundData, * pTransferSoundData;
+
+
//---------------------------------------------------------------------------------
diff --git a/backends/platform/gp2x/gp2x-common.h b/backends/platform/gp2x/gp2x-common.h
index 79eb3f65cf..b54e2d4d4f 100644
--- a/backends/platform/gp2x/gp2x-common.h
+++ b/backends/platform/gp2x/gp2x-common.h
@@ -121,19 +121,6 @@ public:
virtual Audio::Mixer *getMixer();
- // Poll CD status
- // Returns true if cd audio is playing
- bool pollCD();
-
- // Play CD audio track
- void playCD(int track, int num_loops, int start_frame, int duration);
-
- // Stop CD audio track
- void stopCD();
-
- // Update CD audio status
- void updateCD();
-
// Quit
void quit();
@@ -163,8 +150,6 @@ public:
bool setGraphicsMode(int mode);
int getGraphicsMode() const;
- bool openCD(int drive);
-
bool hasFeature(Feature f);
void setFeatureState(Feature f, bool enable);
bool getFeatureState(Feature f);
@@ -206,15 +191,6 @@ protected:
bool _overlayVisible;
Graphics::PixelFormat _overlayFormat;
- // CD Audio
- SDL_CD *_cdrom;
- int _cdTrack, _cdNumLoops, _cdStartFrame, _cdDuration;
- uint32 _cdEndTime, _cdStopTime;
-
- enum {
- DF_WANT_RECT_OPTIM = 1 << 0
- };
-
enum {
kTransactionNone = 0,
kTransactionActive = 1,
@@ -255,7 +231,6 @@ protected:
Graphics::Surface _framebuffer;
/** Current video mode flags (see DF_* constants) */
- uint32 _modeFlags;
bool _modeChanged;
int _screenChangeCount;
@@ -272,9 +247,6 @@ protected:
// Dirty rect management
SDL_Rect _dirtyRectList[NUM_DIRTY_RECT];
int _numDirtyRects;
- uint32 *_dirtyChecksums;
- bool _cksumValid;
- int _cksumNum;
// Keyboard mouse emulation. Disabled by fingolfin 2004-12-18.
// I am keeping the rest of the code in for now, since the joystick
@@ -371,9 +343,6 @@ protected:
Common::TimerManager *_timer;
protected:
- void addDirtyRgnAuto(const byte *buf);
- void makeChecksums(const byte *buf);
-
virtual void addDirtyRect(int x, int y, int w, int h, bool realCoordinates = false);
void drawMouse();
diff --git a/backends/platform/gp2x/gp2x.cpp b/backends/platform/gp2x/gp2x.cpp
index 3d4ed51c1f..88d4f9d632 100644
--- a/backends/platform/gp2x/gp2x.cpp
+++ b/backends/platform/gp2x/gp2x.cpp
@@ -193,13 +193,11 @@ void OSystem_GP2X::initBackend() {
memset(&_videoMode, 0, sizeof(_videoMode));
memset(&_transactionDetails, 0, sizeof(_transactionDetails));
- _cksumValid = false;
_videoMode.mode = GFX_NORMAL;
_videoMode.scaleFactor = 1;
_scalerProc = Normal1x;
_videoMode.aspectRatioCorrection = ConfMan.getBool("aspect_ratio");
_scalerType = 0;
- _modeFlags = 0;
_adjustZoomOnMouse = false;
ConfMan.setBool("FM_low_quality", true);
@@ -246,7 +244,7 @@ OSystem_GP2X::OSystem_GP2X()
_hwscreen(0), _screen(0), _tmpscreen(0),
_overlayVisible(false),
_overlayscreen(0), _tmpscreen2(0),
- _cdrom(0), _scalerProc(0), _modeChanged(false), _screenChangeCount(0), _dirtyChecksums(0),
+ _scalerProc(0), _modeChanged(false), _screenChangeCount(0),
_mouseVisible(false), _mouseNeedsRedraw(false), _mouseData(0), _mouseSurface(0),
_mouseOrigSurface(0), _cursorTargetScale(1), _cursorPaletteDisabled(true),
_joystick(0),
@@ -281,7 +279,6 @@ OSystem_GP2X::~OSystem_GP2X() {
SDL_RemoveTimer(_timerID);
closeMixer();
- free(_dirtyChecksums);
free(_currentPalette);
free(_cursorPalette);
free(_mouseData);
@@ -380,7 +377,6 @@ bool OSystem_GP2X::hasFeature(Feature f) {
return
(f == kFeatureFullscreenMode) ||
(f == kFeatureAspectRatioCorrection) ||
- (f == kFeatureAutoComputeDirtyRects) ||
(f == kFeatureCursorHasPalette);
}
@@ -391,12 +387,6 @@ void OSystem_GP2X::setFeatureState(Feature f, bool enable) {
case kFeatureAspectRatioCorrection:
setAspectRatioCorrection(enable);
break;
- case kFeatureAutoComputeDirtyRects:
- if (enable)
- _modeFlags |= DF_WANT_RECT_OPTIM;
- else
- _modeFlags &= ~DF_WANT_RECT_OPTIM;
- break;
case kFeatureDisableKeyFiltering:
// TODO: Extend as more support for this is added to engines.
return;
@@ -413,8 +403,6 @@ bool OSystem_GP2X::getFeatureState(Feature f) {
return false;
case kFeatureAspectRatioCorrection:
return _videoMode.aspectRatioCorrection;
- case kFeatureAutoComputeDirtyRects:
- return _modeFlags & DF_WANT_RECT_OPTIM;
default:
return false;
}
@@ -431,7 +419,6 @@ void OSystem_GP2X::quit() {
SDL_RemoveTimer(_timerID);
closeMixer();
- free(_dirtyChecksums);
free(_currentPalette);
free(_cursorPalette);
free(_mouseData);
@@ -650,26 +637,3 @@ Audio::Mixer *OSystem_GP2X::getMixer() {
assert(_mixer);
return _mixer;
}
-
-#pragma mark -
-#pragma mark --- CD Audio ---
-#pragma mark -
-
-bool OSystem_GP2X::openCD(int drive) {
- return (_cdrom = NULL);
-}
-
-void OSystem_GP2X::stopCD() {
-}
-
-void OSystem_GP2X::playCD(int track, int num_loops, int start_frame, int duration) {
- return;
-}
-
-bool OSystem_GP2X::pollCD() {
- return false;
-}
-
-void OSystem_GP2X::updateCD() {
- return;
-}
diff --git a/backends/platform/gp2x/graphics.cpp b/backends/platform/gp2x/graphics.cpp
index 243c37dcb1..4a3c668c52 100644
--- a/backends/platform/gp2x/graphics.cpp
+++ b/backends/platform/gp2x/graphics.cpp
@@ -270,12 +270,7 @@ void OSystem_GP2X::initSize(uint w, uint h, const Graphics::PixelFormat *format)
_videoMode.screenWidth = w;
_videoMode.screenHeight = h;
- _cksumNum = (w * h / (8 * 8));
-
_transactionDetails.sizeChanged = true;
-
- free(_dirtyChecksums);
- _dirtyChecksums = (uint32 *)calloc(_cksumNum * 2, sizeof(uint32));
}
int OSystem_GP2X::effectiveScreenHeight() const {
@@ -724,40 +719,31 @@ void OSystem_GP2X::copyRectToScreen(const byte *src, int pitch, int x, int y, in
assert(h > 0 && y + h <= _videoMode.screenHeight);
assert(w > 0 && x + w <= _videoMode.screenWidth);
- if (IS_ALIGNED(src, 4) && pitch == _videoMode.screenWidth && x == 0 && y == 0 &&
- w == _videoMode.screenWidth && h == _videoMode.screenHeight && _modeFlags & DF_WANT_RECT_OPTIM) {
- /* Special, optimized case for full screen updates.
- * It tries to determine what areas were actually changed,
- * and just updates those, on the actual display. */
- addDirtyRgnAuto(src);
- } else {
- /* Clip the coordinates */
- if (x < 0) {
- w += x;
- src -= x;
- x = 0;
- }
+ /* Clip the coordinates */
+ if (x < 0) {
+ w += x;
+ src -= x;
+ x = 0;
+ }
- if (y < 0) {
- h += y;
- src -= y * pitch;
- y = 0;
- }
+ if (y < 0) {
+ h += y;
+ src -= y * pitch;
+ y = 0;
+ }
- if (w > _videoMode.screenWidth - x) {
- w = _videoMode.screenWidth - x;
- }
+ if (w > _videoMode.screenWidth - x) {
+ w = _videoMode.screenWidth - x;
+ }
- if (h > _videoMode.screenHeight - y) {
- h = _videoMode.screenHeight - y;
- }
+ if (h > _videoMode.screenHeight - y) {
+ h = _videoMode.screenHeight - y;
+ }
- if (w <= 0 || h <= 0)
- return;
+ if (w <= 0 || h <= 0)
+ return;
- _cksumValid = false;
- addDirtyRect(x, y, w, h);
- }
+ addDirtyRect(x, y, w, h);
// Try to lock the screen surface
if (SDL_LockSurface(_screen) == -1)
@@ -885,88 +871,6 @@ void OSystem_GP2X::addDirtyRect(int x, int y, int w, int h, bool realCoordinates
}
}
-void OSystem_GP2X::makeChecksums(const byte *buf) {
- assert(buf);
- uint32 *sums = _dirtyChecksums;
- uint x,y;
- const uint last_x = (uint)_videoMode.screenWidth / 8;
- const uint last_y = (uint)_videoMode.screenHeight / 8;
-
- const uint BASE = 65521; /* largest prime smaller than 65536 */
-
- /* the 8x8 blocks in buf are enumerated starting in the top left corner and
- * reading each line at a time from left to right */
- for (y = 0; y != last_y; y++, buf += _videoMode.screenWidth * (8 - 1))
- for (x = 0; x != last_x; x++, buf += 8) {
- // Adler32 checksum algorithm (from RFC1950, used by gzip and zlib).
- // This computes the Adler32 checksum of a 8x8 pixel block. Note
- // that we can do the modulo operation (which is the slowest part)
- // of the algorithm) at the end, instead of doing each iteration,
- // since we only have 64 iterations in total - and thus s1 and
- // s2 can't overflow anyway.
- uint32 s1 = 1;
- uint32 s2 = 0;
- const byte *ptr = buf;
- for (int subY = 0; subY < 8; subY++) {
- for (int subX = 0; subX < 8; subX++) {
- s1 += ptr[subX];
- s2 += s1;
- }
- ptr += _videoMode.screenWidth;
- }
-
- s1 %= BASE;
- s2 %= BASE;
-
- /* output the checksum for this block */
- *sums++ = (s2 << 16) + s1;
- }
-}
-
-void OSystem_GP2X::addDirtyRgnAuto(const byte *buf) {
- assert(buf);
- assert(IS_ALIGNED(buf, 4));
-
- /* generate a table of the checksums */
- makeChecksums(buf);
-
- if (!_cksumValid) {
- _forceFull = true;
- _cksumValid = true;
- }
-
- /* go through the checksum list, compare it with the previous checksums,
- and add all dirty rectangles to a list. try to combine small rectangles
- into bigger ones in a simple way */
- if (!_forceFull) {
- int x, y, w;
- uint32 *ck = _dirtyChecksums;
-
- for (y = 0; y != _videoMode.screenHeight / 8; y++) {
- for (x = 0; x != _videoMode.screenWidth / 8; x++, ck++) {
- if (ck[0] != ck[_cksumNum]) {
- /* found a dirty 8x8 block, now go as far to the right as possible,
- and at the same time, unmark the dirty status by setting old to new. */
- w=0;
- do {
- ck[w + _cksumNum] = ck[w];
- w++;
- } while (x + w != _videoMode.screenWidth / 8 && ck[w] != ck[w + _cksumNum]);
-
- addDirtyRect(x * 8, y * 8, w * 8, 8);
-
- if (_forceFull)
- goto get_out;
- }
- }
- }
- } else {
- get_out:;
- /* Copy old checksums to new */
- memcpy(_dirtyChecksums + _cksumNum, _dirtyChecksums, _cksumNum * sizeof(uint32));
- }
-}
-
int16 OSystem_GP2X::getHeight() {
return _videoMode.screenHeight;
}
@@ -1175,7 +1079,6 @@ void OSystem_GP2X::copyRectToOverlay(const OverlayColor *buf, int pitch, int x,
return;
// Mark the modified region as dirty
- _cksumValid = false;
addDirtyRect(x, y, w, h);
if (SDL_LockSurface(_overlayscreen) == -1)
@@ -1502,7 +1405,6 @@ void OSystem_GP2X::drawMouse() {
SDL_Rect zoomdst;
SDL_Rect dst;
int scale;
- int width, height;
int hotX, hotY;
int tmpScreenWidth, tmpScreenHeight;
@@ -1523,16 +1425,12 @@ void OSystem_GP2X::drawMouse() {
if (!_overlayVisible) {
scale = _videoMode.scaleFactor;
- width = _videoMode.screenWidth;
- height = _videoMode.screenHeight;
dst.w = _mouseCurState.vW;
dst.h = _mouseCurState.vH;
hotX = _mouseCurState.vHotX;
hotY = _mouseCurState.vHotY;
} else {
scale = 1;
- width = _videoMode.overlayWidth;
- height = _videoMode.overlayHeight;
dst.w = _mouseCurState.rW;
dst.h = _mouseCurState.rH;
hotX = _mouseCurState.rHotX;
diff --git a/backends/platform/gp2xwiz/build/build.sh b/backends/platform/gp2xwiz/build/build.sh
index 1bdc020f17..876c3e378a 100755
--- a/backends/platform/gp2xwiz/build/build.sh
+++ b/backends/platform/gp2xwiz/build/build.sh
@@ -13,7 +13,7 @@ export ASFLAGS=-mfloat-abi=soft
cd ../../../..
-echo Building ScummVM for GP2X.
+echo Building ScummVM for GP2X Wiz.
make
-echo Build for GP2X - SDL - complete - Please check build logs.
+echo Build for GP2X Wiz - complete - Please check build logs.
diff --git a/backends/platform/gp2xwiz/build/bundle.sh b/backends/platform/gp2xwiz/build/bundle.sh
index 492ba9e1c6..065bd7a685 100755
--- a/backends/platform/gp2xwiz/build/bundle.sh
+++ b/backends/platform/gp2xwiz/build/bundle.sh
@@ -35,10 +35,12 @@ loc=`dirname "$f"`
cp $loc/../lib/libz.so.1.2.3 ./scummvm-wiz-`date '+%Y-%m-%d'`/scummvm/lib/libz.so.1
cp $loc/../lib/libvorbisidec.so.1.0.2 ./scummvm-wiz-`date '+%Y-%m-%d'`/scummvm/lib/libvorbisidec.so.1
-
-echo Making Stripped exe.
+echo Making Stripped Binary.
arm-open2x-linux-strip ./scummvm-wiz-`date '+%Y-%m-%d'`/scummvm/scummvm.wiz
+echo Making Stripped Plugins.
+arm-open2x-linux-strip ./scummvm-wiz-`date '+%Y-%m-%d'`/scummvm/plugins/*
+
echo Building ZIP bundle.
if [ -f /usr/bin/zip ]
then
diff --git a/backends/platform/gp2xwiz/build/clean.sh b/backends/platform/gp2xwiz/build/clean.sh
index 2862887bb3..5ec1b9e62c 100755
--- a/backends/platform/gp2xwiz/build/clean.sh
+++ b/backends/platform/gp2xwiz/build/clean.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
echo Quick script to make building all the time less painful.
diff --git a/backends/platform/gp2xwiz/build/config.sh b/backends/platform/gp2xwiz/build/config.sh
index 27c1fbf0bf..54c4795298 100755
--- a/backends/platform/gp2xwiz/build/config.sh
+++ b/backends/platform/gp2xwiz/build/config.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
echo Quick script to make running configure all the time less painful
echo and let all the build work be done from the backend/build folder.
diff --git a/backends/platform/gp2xwiz/gp2xwiz-events.cpp b/backends/platform/gp2xwiz/gp2xwiz-events.cpp
index 2774efce1b..a69aa42967 100644
--- a/backends/platform/gp2xwiz/gp2xwiz-events.cpp
+++ b/backends/platform/gp2xwiz/gp2xwiz-events.cpp
@@ -30,6 +30,7 @@
#include "backends/platform/gp2xwiz/gp2xwiz-sdl.h"
#include "backends/platform/gp2xwiz/gp2xwiz-hw.h"
+#include "graphics/scaler/aspect.h"
#include "common/util.h"
#include "common/events.h"
diff --git a/backends/platform/gp2xwiz/gp2xwiz-graphics.cpp b/backends/platform/gp2xwiz/gp2xwiz-graphics.cpp
index b20087e6d9..6abddd52f3 100644
--- a/backends/platform/gp2xwiz/gp2xwiz-graphics.cpp
+++ b/backends/platform/gp2xwiz/gp2xwiz-graphics.cpp
@@ -127,12 +127,7 @@ void OSystem_GP2XWIZ::initSize(uint w, uint h) {
toggleMouseGrab();
}
- _cksumNum = (w * h / (8 * 8));
-
_transactionDetails.sizeChanged = true;
-
- free(_dirtyChecksums);
- _dirtyChecksums = (uint32 *)calloc(_cksumNum * 2, sizeof(uint32));
}
bool OSystem_GP2XWIZ::loadGFXMode() {
@@ -154,7 +149,6 @@ void OSystem_GP2XWIZ::drawMouse() {
SDL_Rect dst;
int scale;
- int width, height;
int hotX, hotY;
if (_videoMode.mode == GFX_HALF && !_overlayVisible){
@@ -167,16 +161,12 @@ void OSystem_GP2XWIZ::drawMouse() {
if (!_overlayVisible) {
scale = _videoMode.scaleFactor;
- width = _videoMode.screenWidth;
- height = _videoMode.screenHeight;
dst.w = _mouseCurState.vW;
dst.h = _mouseCurState.vH;
hotX = _mouseCurState.vHotX;
hotY = _mouseCurState.vHotY;
} else {
scale = 1;
- width = _videoMode.overlayWidth;
- height = _videoMode.overlayHeight;
dst.w = _mouseCurState.rW;
dst.h = _mouseCurState.rH;
hotX = _mouseCurState.rHotX;
diff --git a/backends/platform/iphone/iphone_common.h b/backends/platform/iphone/iphone_common.h
index 9f7e3e808f..1f5ed01982 100644
--- a/backends/platform/iphone/iphone_common.h
+++ b/backends/platform/iphone/iphone_common.h
@@ -72,6 +72,7 @@ void iPhone_updateScreenRect(unsigned short* screen, int x1, int y1, int x2, int
void iPhone_initSurface(int width, int height);
bool iPhone_fetchEvent(int *outEvent, float *outX, float *outY);
const char* iPhone_getDocumentsDir();
+bool iPhone_isHighResDevice();
#ifdef __cplusplus
}
diff --git a/backends/platform/iphone/iphone_video.h b/backends/platform/iphone/iphone_video.h
index daa5e1d18e..1060a2a223 100644
--- a/backends/platform/iphone/iphone_video.h
+++ b/backends/platform/iphone/iphone_video.h
@@ -43,8 +43,6 @@
SoftKeyboard* _keyboardView;
CALayer* _screenLayer;
- int _fullWidth;
- int _fullHeight;
int _widthOffset;
int _heightOffset;
diff --git a/backends/platform/iphone/iphone_video.m b/backends/platform/iphone/iphone_video.m
index e8977be2f2..faa0719b6c 100644
--- a/backends/platform/iphone/iphone_video.m
+++ b/backends/platform/iphone/iphone_video.m
@@ -29,6 +29,8 @@
static iPhoneView *sharedInstance = nil;
static int _width = 0;
static int _height = 0;
+static int _fullWidth;
+static int _fullHeight;
static CGRect _screenRect;
static char* _textureBuffer = 0;
static int _textureWidth = 0;
@@ -42,6 +44,10 @@ static UITouch* _secondTouch = NULL;
// static long lastTick = 0;
// static int frames = 0;
+bool iPhone_isHighResDevice() {
+ return _fullHeight > 480;
+}
+
void iPhone_updateScreen() {
if (!_needsScreenUpdate) {
_needsScreenUpdate = 1;
diff --git a/backends/platform/iphone/osys_main.cpp b/backends/platform/iphone/osys_main.cpp
index b151688e4e..6c26b6ca8d 100644
--- a/backends/platform/iphone/osys_main.cpp
+++ b/backends/platform/iphone/osys_main.cpp
@@ -57,7 +57,7 @@ OSystem_IPHONE::OSystem_IPHONE() :
_overlayVisible(false), _overlayBuffer(NULL), _fullscreen(NULL),
_mouseHeight(0), _mouseWidth(0), _mouseBuf(NULL), _lastMouseTap(0),
_secondaryTapped(false), _lastSecondaryTap(0), _screenOrientation(kScreenOrientationFlippedLandscape),
- _needEventRestPeriod(false), _mouseClickAndDragEnabled(false), _touchpadModeEnabled(true),
+ _needEventRestPeriod(false), _mouseClickAndDragEnabled(false),
_gestureStartX(-1), _gestureStartY(-1), _fullScreenIsDirty(false), _fullScreenOverlayIsDirty(false),
_mouseDirty(false), _timeSuspended(0), _lastDragPosX(-1), _lastDragPosY(-1), _screenChangeCount(0)
@@ -65,6 +65,7 @@ OSystem_IPHONE::OSystem_IPHONE() :
_queuedInputEvent.type = (Common::EventType)0;
_lastDrawnMouseRect = Common::Rect(0, 0, 0, 0);
+ _touchpadModeEnabled = !iPhone_isHighResDevice();
_fsFactory = new POSIXFilesystemFactory();
}
diff --git a/backends/platform/iphone/osys_video.cpp b/backends/platform/iphone/osys_video.cpp
index 6cb5e18d95..76c2031758 100644
--- a/backends/platform/iphone/osys_video.cpp
+++ b/backends/platform/iphone/osys_video.cpp
@@ -86,7 +86,7 @@ int16 OSystem_IPHONE::getWidth() {
}
void OSystem_IPHONE::setPalette(const byte *colors, uint start, uint num) {
- //printf("setPalette()\n");
+ assert(start + num <= 256);
const byte *b = colors;
for (uint i = start; i < start + num; ++i) {
@@ -98,7 +98,14 @@ void OSystem_IPHONE::setPalette(const byte *colors, uint start, uint num) {
}
void OSystem_IPHONE::grabPalette(byte *colors, uint start, uint num) {
- //printf("grabPalette()\n");
+ assert(start + num <= 256);
+ byte *b = colors;
+
+ for (uint i = start; i < start + num; ++i) {
+ Graphics::colorToRGB<Graphics::ColorMasks<565> >(_palette[i], b[0], b[1], b[2]);
+ b[3] = 0xFF;
+ b += 4;
+ }
}
void OSystem_IPHONE::copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h) {
diff --git a/backends/platform/linuxmoto/linuxmoto-graphics.cpp b/backends/platform/linuxmoto/linuxmoto-graphics.cpp
index 8f718c82f6..a39416ebc4 100644
--- a/backends/platform/linuxmoto/linuxmoto-graphics.cpp
+++ b/backends/platform/linuxmoto/linuxmoto-graphics.cpp
@@ -128,12 +128,7 @@ void OSystem_LINUXMOTO::initSize(uint w, uint h) {
toggleMouseGrab();
}
- _cksumNum = (w * h / (8 * 8));
-
_transactionDetails.sizeChanged = true;
-
- free(_dirtyChecksums);
- _dirtyChecksums = (uint32 *)calloc(_cksumNum * 2, sizeof(uint32));
}
bool OSystem_LINUXMOTO::loadGFXMode() {
@@ -173,7 +168,6 @@ void OSystem_LINUXMOTO::drawMouse() {
SDL_Rect dst;
int scale;
- int width, height;
int hotX, hotY;
if (_videoMode.mode == GFX_HALF && !_overlayVisible) {
@@ -186,16 +180,12 @@ void OSystem_LINUXMOTO::drawMouse() {
if (!_overlayVisible) {
scale = _videoMode.scaleFactor;
- width = _videoMode.screenWidth;
- height = _videoMode.screenHeight;
dst.w = _mouseCurState.vW;
dst.h = _mouseCurState.vH;
hotX = _mouseCurState.vHotX;
hotY = _mouseCurState.vHotY;
} else {
scale = 1;
- width = _videoMode.overlayWidth;
- height = _videoMode.overlayHeight;
dst.w = _mouseCurState.rW;
dst.h = _mouseCurState.rH;
hotX = _mouseCurState.rHotX;
diff --git a/backends/platform/ps2/systemps2.cpp b/backends/platform/ps2/systemps2.cpp
index 49d583d1a1..7659d5194d 100644
--- a/backends/platform/ps2/systemps2.cpp
+++ b/backends/platform/ps2/systemps2.cpp
@@ -624,23 +624,6 @@ void OSystem_PS2::setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x,
_screen->setMouseOverlay(buf, w, h, hotspot_x, hotspot_y, keycolor);
}
-bool OSystem_PS2::openCD(int drive) {
- return false;
-}
-
-bool OSystem_PS2::pollCD(void) {
- return false;
-}
-
-void OSystem_PS2::playCD(int track, int num_loops, int start_frame, int duration) {
-}
-
-void OSystem_PS2::stopCD(void) {
-}
-
-void OSystem_PS2::updateCD(void) {
-}
-
void OSystem_PS2::showOverlay(void) {
_screen->showOverlay();
}
diff --git a/backends/platform/ps2/systemps2.h b/backends/platform/ps2/systemps2.h
index 0068ffd93f..78973ed3f0 100644
--- a/backends/platform/ps2/systemps2.h
+++ b/backends/platform/ps2/systemps2.h
@@ -97,12 +97,6 @@ public:
virtual Audio::Mixer *getMixer();
- virtual bool openCD(int drive);
- virtual bool pollCD();
- virtual void playCD(int track, int num_loops, int start_frame, int duration);
- virtual void stopCD();
- virtual void updateCD();
-
virtual MutexRef createMutex(void);
virtual void lockMutex(MutexRef mutex);
virtual void unlockMutex(MutexRef mutex);
diff --git a/backends/platform/psp/Makefile b/backends/platform/psp/Makefile
index 7c33999b4d..7f8bb63b0a 100644
--- a/backends/platform/psp/Makefile
+++ b/backends/platform/psp/Makefile
@@ -129,7 +129,8 @@ SDLFLAGS := $(shell $(PSPBIN)/sdl-config --cflags)
SDLLIBS := $(shell $(PSPBIN)/sdl-config --libs)
# PSP LIBS
PSPLIBS = -lpspprof -lpspvfpu -lpspdebug -lpspgu -lpspge -lpspdisplay -lpspctrl -lpspsdk \
- -lpsputility -lpspuser -lpsppower -lpsphprm -lpspsdk -lpsprtc -lpspaudio -lpspkernel
+ -lpsputility -lpspuser -lpsppower -lpsphprm -lpspsdk -lpsprtc -lpspaudio -lpspaudiocodec \
+ -lpspkernel
# Add in PSPSDK includes and libraries.
CXXFLAGS += $(SDLFLAGS)
@@ -149,7 +150,8 @@ OBJS := powerman.o \
psploader.o \
pspkeyboard.o \
audio.o \
- thread.o
+ thread.o \
+ mp3.o
# Include common Scummvm makefile
include $(srcdir)/Makefile.common
diff --git a/backends/platform/psp/display_client.cpp b/backends/platform/psp/display_client.cpp
index 90c41e796d..c5a6250188 100644
--- a/backends/platform/psp/display_client.cpp
+++ b/backends/platform/psp/display_client.cpp
@@ -686,17 +686,18 @@ void GuRenderer::fillVertices(Vertex *vertices) {
uint32 gapX = _useGlobalScaler ? (PSP_SCREEN_WIDTH - outputWidth) >> 1 : 0;
uint32 gapY = _useGlobalScaler ? (PSP_SCREEN_HEIGHT - outputHeight) >> 1 : 0;
+ // Save scaled offset on screen
+ float scaledOffsetOnScreenX = scaleSourceToOutputX(_offsetOnScreen.x);
+ float scaledOffsetOnScreenY = scaleSourceToOutputY(_offsetOnScreen.y);
+
float imageStartX, imageStartY, imageEndX, imageEndY;
- imageStartX = gapX + (scaleSourceToOutputX(_maxTextureOffset.x));
- imageStartY = gapY;
-
- imageStartX += scaleSourceToOutputX(_offsetOnScreen.x);
- imageStartY += scaleSourceToOutputY(_offsetOnScreen.y);
+ imageStartX = gapX + scaledOffsetOnScreenX + (scaleSourceToOutputX(_maxTextureOffset.x));
+ imageStartY = gapY + scaledOffsetOnScreenY;
if (_fullScreen) { // shortcut
- imageEndX = PSP_SCREEN_WIDTH - gapX;
- imageEndY = PSP_SCREEN_HEIGHT - gapY;
+ imageEndX = PSP_SCREEN_WIDTH - gapX + scaledOffsetOnScreenX;
+ imageEndY = PSP_SCREEN_HEIGHT - gapY + scaledOffsetOnScreenY; // needed for screen shake
} else { /* !fullScreen */
imageEndX = imageStartX + scaleSourceToOutputX(_drawSize.width);
imageEndY = imageStartY + scaleSourceToOutputY(_drawSize.height);
diff --git a/backends/platform/psp/module.mk b/backends/platform/psp/module.mk
index 4d375bcef0..99170ce7fb 100644
--- a/backends/platform/psp/module.mk
+++ b/backends/platform/psp/module.mk
@@ -14,7 +14,8 @@ MODULE_OBJS := powerman.o \
psploader.o \
pspkeyboard.o \
audio.o \
- thread.o
+ thread.o \
+ mp3.o
MODULE_DIRS += \
backends/platform/psp/
diff --git a/backends/platform/psp/mp3.cpp b/backends/platform/psp/mp3.cpp
new file mode 100644
index 0000000000..972c5a8ba8
--- /dev/null
+++ b/backends/platform/psp/mp3.cpp
@@ -0,0 +1,487 @@
+/* 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$
+ *
+ */
+
+
+#include "common/debug.h"
+#include "common/stream.h"
+#include "common/util.h"
+#include "common/singleton.h"
+#include "common/mutex.h"
+
+#include "sound/audiostream.h"
+
+#include <pspaudiocodec.h>
+#include <psputility_modules.h>
+#include <pspthreadman.h>
+#include <pspsysmem.h>
+#include <pspmodulemgr.h>
+#include <psputility_avmodules.h>
+#include <mad.h>
+#include "backends/platform/psp/mp3.h"
+
+//#define DISABLE_PSP_MP3 // to make us use the regular MAD decoder instead
+
+//#define __PSP_DEBUG_FUNCS__ /* For debugging the stack */
+//#define __PSP_DEBUG_PRINT__
+#include "backends/platform/psp/trace.h"
+
+//#define PRINT_BUFFERS /* to debug MP3 buffers */
+
+namespace Audio {
+
+class Mp3PspStream;
+
+bool Mp3PspStream::_decoderInit = false; // has the decoder been initialized
+#ifdef DISABLE_PSP_MP3
+bool Mp3PspStream::_decoderFail = true; // pretend the decoder failed
+#else
+bool Mp3PspStream::_decoderFail = false; // has the decoder failed to load
+#endif
+
+bool Mp3PspStream::initDecoder() {
+ DEBUG_ENTER_FUNC();
+
+ if (_decoderInit) {
+ PSP_ERROR("Already initialized!");
+ return true;
+ }
+
+ // Based on PSP firmware version, we need to do different things to do Media Engine processing
+ uint32 firmware = sceKernelDevkitVersion();
+ PSP_DEBUG_PRINT("Firmware version 0x%x\n", firmware);
+ if (firmware == 0x01050001){
+ if (!loadStartAudioModule((char *)(void *)"flash0:/kd/me_for_vsh.prx",
+ PSP_MEMORY_PARTITION_KERNEL)) {
+ PSP_ERROR("failed to load me_for_vsh.prx. ME cannot start.\n");
+ _decoderFail = true;
+ return false;
+ }
+ if (!loadStartAudioModule((char *)(void *)"flash0:/kd/audiocodec.prx", PSP_MEMORY_PARTITION_KERNEL)) {
+ PSP_ERROR("failed to load audiocodec.prx. ME cannot start.\n");
+ _decoderFail = true;
+ return false;
+ }
+ } else {
+ if (sceUtilityLoadAvModule(PSP_AV_MODULE_AVCODEC) < 0) {
+ PSP_ERROR("failed to load AVCODEC module.\n");
+ _decoderFail = true;
+ return false;
+ }
+ }
+
+ PSP_INFO_PRINT("Using PSP's ME for MP3\n"); // important to know this is happening
+
+ _decoderInit = true;
+ return true;
+}
+
+bool Mp3PspStream::stopDecoder() {
+ DEBUG_ENTER_FUNC();
+
+ if (!_decoderInit)
+ return true;
+
+ // Based on PSP firmware version, we need to do different things to do Media Engine processing
+ if (sceKernelDevkitVersion() == 0x01050001){
+/* if (!unloadAudioModule("flash0:/kd/me_for_vsh.prx", PSP_MEMORY_PARTITION_KERNEL) ||
+ !unloadAudioModule("flash0:/kd/audiocodec.prx", PSP_MEMORY_PARTITION_KERNEL) {
+ PSP_ERROR("failed to unload audio module\n");
+ return false;
+ }
+*/
+ }else{
+ if (sceUtilityUnloadModule(PSP_MODULE_AV_AVCODEC) < 0) {
+ PSP_ERROR("failed to unload avcodec module\n");
+ return false;
+ }
+ }
+
+ _decoderInit = false;
+ return true;
+}
+
+//Load a PSP audio module
+bool Mp3PspStream::loadStartAudioModule(const char *modname, int partition){
+ DEBUG_ENTER_FUNC();
+
+ SceKernelLMOption option;
+ SceUID modid;
+
+ memset(&option, 0, sizeof(option));
+ option.size = sizeof(option);
+ option.mpidtext = partition;
+ option.mpiddata = partition;
+ option.position = 0;
+ option.access = 1;
+
+ modid = sceKernelLoadModule(modname, 0, &option);
+ if (modid < 0) {
+ PSP_ERROR("Failed to load module %s. Got error 0x%x\n", modname, modid);
+ return false;
+ }
+
+ int ret = sceKernelStartModule(modid, 0, NULL, NULL, NULL);
+ if (ret < 0) {
+ PSP_ERROR("Failed to start module %s. Got error 0x%x\n", modname, ret);
+ return false;
+ }
+ return true;
+}
+
+// TODO: make parallel function for unloading the 1.50 modules
+
+Mp3PspStream::Mp3PspStream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose) :
+ _inStream(inStream),
+ _disposeAfterUse(dispose),
+ _pcmLength(0),
+ _posInFrame(0),
+ _state(MP3_STATE_INIT),
+ _length(0, 1000),
+ _sampleRate(0),
+ _totalTime(mad_timer_zero) {
+
+ DEBUG_ENTER_FUNC();
+
+ assert(_decoderInit); // must be initialized by now
+
+ // let's leave the buffer guard -- who knows, it may be good?
+ memset(_buf, 0, sizeof(_buf));
+ memset(_codecInBuffer, 0, sizeof(_codecInBuffer));
+
+ initStream(); // init needed stuff for the stream
+
+ while (_state != MP3_STATE_EOS)
+ findValidHeader(); // get a first header so we can read basic stuff
+
+ _sampleRate = _header.samplerate; // copy it before it gets destroyed
+
+ _length = Timestamp(mad_timer_count(_totalTime, MAD_UNITS_MILLISECONDS), getRate());
+
+ //initStreamME(); // init the stuff needed for the ME to work
+
+ deinitStream();
+ //releaseStreamME();
+
+ _state = MP3_STATE_INIT;
+}
+
+int Mp3PspStream::initStream() {
+ DEBUG_ENTER_FUNC();
+
+ if (_state != MP3_STATE_INIT)
+ deinitStream();
+
+ // Init MAD
+ mad_stream_init(&_stream);
+ mad_header_init(&_header);
+
+ // Reset the stream data
+ _inStream->seek(0, SEEK_SET);
+ _totalTime = mad_timer_zero;
+ _posInFrame = 0;
+
+ // Update state
+ _state = MP3_STATE_READY;
+
+ // Read the first few sample bytes into the buffer
+ readMP3DataIntoBuffer();
+
+ return true;
+}
+
+bool Mp3PspStream::initStreamME() {
+ // The following will eventually go into the thread
+ sceAudiocodecReleaseEDRAM(_codecParams); // do we need this?
+
+ memset(_codecParams, 0, sizeof(_codecParams));
+
+ // Init the MP3 hardware
+ int ret = 0;
+ ret = sceAudiocodecCheckNeedMem(_codecParams, 0x1002);
+ if (ret < 0) {
+ PSP_ERROR("failed to init MP3 ME module. sceAudiocodecCheckNeedMem returned 0x%x.\n", ret);
+ return false;
+ }
+ PSP_DEBUG_PRINT("sceAudiocodecCheckNeedMem returned %d\n", ret);
+ ret = sceAudiocodecGetEDRAM(_codecParams, 0x1002);
+ if (ret < 0) {
+ PSP_ERROR("failed to init MP3 ME module. sceAudiocodecGetEDRAM returned 0x%x.\n", ret);
+ return false;
+ }
+ PSP_DEBUG_PRINT("sceAudioCodecGetEDRAM returned %d\n", ret);
+
+ PSP_DEBUG_PRINT("samplerate[%d]\n", _sampleRate);
+ _codecParams[10] = _sampleRate;
+
+ ret = sceAudiocodecInit(_codecParams, 0x1002);
+ if (ret < 0) {
+ PSP_ERROR("failed to init MP3 ME module. sceAudiocodecInit returned 0x%x.\n", ret);
+ return false;
+ }
+
+ return true;
+}
+
+Mp3PspStream::~Mp3PspStream() {
+ DEBUG_ENTER_FUNC();
+
+ deinitStream();
+ releaseStreamME(); // free the memory used for this stream
+
+ if (_disposeAfterUse == DisposeAfterUse::YES)
+ delete _inStream;
+}
+
+void Mp3PspStream::deinitStream() {
+ DEBUG_ENTER_FUNC();
+
+ if (_state == MP3_STATE_INIT)
+ return;
+
+ // Deinit MAD
+ mad_header_finish(&_header);
+ mad_stream_finish(&_stream);
+
+ _state = MP3_STATE_EOS;
+}
+
+void Mp3PspStream::releaseStreamME() {
+ sceAudiocodecReleaseEDRAM(_codecParams);
+}
+
+void Mp3PspStream::decodeMP3Data() {
+ DEBUG_ENTER_FUNC();
+
+ do {
+ if (_state == MP3_STATE_INIT) {
+ initStream();
+ initStreamME();
+ }
+
+ if (_state == MP3_STATE_EOS)
+ return;
+
+ findValidHeader(); // seach for next valid header
+
+ while (_state == MP3_STATE_READY) {
+ _stream.error = MAD_ERROR_NONE;
+
+ uint32 frame_size = _stream.next_frame - _stream.this_frame;
+ uint32 samplesPerFrame = _header.layer == MAD_LAYER_III ? 576 : 1152; // Varies by layer
+ // calculate frame size -- try
+ //uint32 calc_frame_size = ((144 * _header.bitrate) / 22050) + (_header.flags & MAD_FLAG_PADDING ? 1 : 0);
+
+ // Get stereo/mono
+ uint32 multFactor = 1;
+ if (_header.mode != MAD_MODE_SINGLE_CHANNEL) // mono - x2 for 16bit
+ multFactor *= 2; // stereo - x4 for 16bit
+
+ PSP_DEBUG_PRINT("MP3 frame size[%d]. Samples[%d]. Multfactor[%d] pad[%d]\n", frame_size, samplesPerFrame, multFactor, _header.flags & MAD_FLAG_PADDING);
+ memcpy(_codecInBuffer, _stream.this_frame, frame_size); // we need it aligned
+
+ // set up parameters for ME
+ _codecParams[6] = (unsigned long)_codecInBuffer;
+ _codecParams[8] = (unsigned long)_pcmSamples;
+ _codecParams[7] = frame_size;
+ _codecParams[9] = samplesPerFrame * multFactor; // x2 for stereo
+
+ // debug
+#ifdef PRINT_BUFFERS
+ PSP_DEBUG_PRINT("mp3 frame:\n");
+ for (int i=0; i < (int)frame_size; i++) {
+ PSP_DEBUG_PRINT_SAMELN("%x ", _codecInBuffer[i]);
+ }
+ PSP_DEBUG_PRINT("\n");
+#endif
+ // Decode the next frame
+ // This function blocks. We'll want to put it in a thread
+ int ret = sceAudiocodecDecode(_codecParams, 0x1002);
+ if (ret < 0) {
+ PSP_ERROR("failed to decode MP3 data in ME. sceAudiocodecDecode returned 0x%x\n", ret);
+ // handle error here
+ }
+
+#ifdef PRINT_BUFFERS
+ PSP_DEBUG_PRINT("PCM frame:\n");
+ for (int i=0; i < (int)_codecParams[9]; i+=2) { // changed from i+=2
+ PSP_DEBUG_PRINT_SAMELN("%d ", (int16)_pcmSamples[i]);
+ }
+ PSP_DEBUG_PRINT("\n");
+#endif
+ _pcmLength = samplesPerFrame;
+ _posInFrame = 0;
+ break;
+ }
+ } while (_state != MP3_STATE_EOS && _stream.error == MAD_ERROR_BUFLEN);
+
+ if (_stream.error != MAD_ERROR_NONE) // catch EOS
+ _state = MP3_STATE_EOS;
+}
+
+void Mp3PspStream::readMP3DataIntoBuffer() {
+ DEBUG_ENTER_FUNC();
+
+ uint32 remaining = 0;
+
+ // Give up immediately if we already used up all data in the stream
+ if (_inStream->eos()) {
+ _state = MP3_STATE_EOS;
+ return;
+ }
+
+ if (_stream.next_frame) {
+ // If there is still data in the MAD stream, we need to preserve it.
+ // Note that we use memmove, as we are reusing the same buffer,
+ // and hence the data regions we copy from and to may overlap.
+ remaining = _stream.bufend - _stream.next_frame;
+ assert(remaining < BUFFER_SIZE); // Paranoia check
+ memmove(_buf, _stream.next_frame, remaining); // TODO: may want another buffer
+ }
+
+ // Try to read the next block
+ uint32 size = _inStream->read(_buf + remaining, BUFFER_SIZE - remaining);
+ if (size <= 0) {
+ _state = MP3_STATE_EOS;
+ return;
+ }
+
+ // Feed the data we just read into the stream decoder
+ _stream.error = MAD_ERROR_NONE;
+ mad_stream_buffer(&_stream, _buf, size + remaining); // just setup the pointers
+}
+
+bool Mp3PspStream::seek(const Timestamp &where) {
+ DEBUG_ENTER_FUNC();
+
+ if (where == _length) {
+ _state = MP3_STATE_EOS;
+ return true;
+ } else if (where > _length) {
+ return false;
+ }
+
+ const uint32 time = where.msecs();
+
+ mad_timer_t destination;
+ mad_timer_set(&destination, time / 1000, time % 1000, 1000);
+
+ // Check if we need to rewind
+ if (_state != MP3_STATE_READY || mad_timer_compare(destination, _totalTime) < 0) {
+ initStream();
+ initStreamME();
+ }
+
+ // The ME will need clear data no matter what once we seek?
+ //if (mad_timer_compare(destination, _totalTime) > 0 && _state != MP3_STATE_EOS)
+ // initStreamME();
+
+ // Skip ahead
+ while (mad_timer_compare(destination, _totalTime) > 0 && _state != MP3_STATE_EOS)
+ findValidHeader();
+
+ return (_state != MP3_STATE_EOS);
+}
+
+// Seek in the stream, finding the next valid header
+void Mp3PspStream::findValidHeader() {
+ DEBUG_ENTER_FUNC();
+
+ if (_state != MP3_STATE_READY)
+ return;
+
+ // If necessary, load more data into the stream decoder
+ if (_stream.error == MAD_ERROR_BUFLEN)
+ readMP3DataIntoBuffer();
+
+ while (_state != MP3_STATE_EOS) {
+ _stream.error = MAD_ERROR_NONE;
+
+ // Decode the next header.
+ if (mad_header_decode(&_header, &_stream) == -1) {
+ if (_stream.error == MAD_ERROR_BUFLEN) {
+ readMP3DataIntoBuffer(); // Read more data
+ continue;
+ } else if (MAD_RECOVERABLE(_stream.error)) {
+ debug(6, "MP3PSPStream: Recoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream));
+ continue;
+ } else {
+ warning("MP3PSPStream: Unrecoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream));
+ break;
+ }
+ }
+
+ // Sum up the total playback time so far
+ mad_timer_add(&_totalTime, _header.duration);
+ break;
+ }
+
+ if (_stream.error != MAD_ERROR_NONE)
+ _state = MP3_STATE_EOS;
+}
+
+int Mp3PspStream::readBuffer(int16 *buffer, const int numSamples) {
+ DEBUG_ENTER_FUNC();
+
+ int samples = 0;
+#ifdef PRINT_BUFFERS
+ int16 *debugBuffer = buffer;
+#endif
+
+ // Keep going as long as we have input available
+ while (samples < numSamples && _state != MP3_STATE_EOS) {
+ const int len = MIN(numSamples, samples + (int)(_pcmLength - _posInFrame) * MAD_NCHANNELS(&_header));
+
+ while (samples < len) {
+ *buffer++ = _pcmSamples[_posInFrame << 1];
+ samples++;
+ if (MAD_NCHANNELS(&_header) == 2) {
+ *buffer++ = _pcmSamples[(_posInFrame << 1) + 1];
+ samples++;
+ }
+ _posInFrame++; // always skip an extra sample since ME always outputs stereo
+ }
+
+ //memcpy(buffer, &_pcmSamples[_posInFrame], len << 1); // 16 bits
+ //_posInFrame += len; // next time we start from the middle
+
+ if (_posInFrame >= _pcmLength) {
+ // We used up all PCM data in the current frame -- read & decode more
+ decodeMP3Data();
+ }
+ }
+
+#ifdef PRINT_BUFFERS
+ PSP_INFO_PRINT("buffer:\n");
+ for (int i = 0; i<numSamples; i++)
+ PSP_INFO_PRINT("%d ", debugBuffer[i]);
+ PSP_INFO_PRINT("\n\n");
+#endif
+
+ return samples;
+}
+
+} // End of namespace Audio
+
+
diff --git a/backends/platform/psp/mp3.h b/backends/platform/psp/mp3.h
new file mode 100644
index 0000000000..f8802f930c
--- /dev/null
+++ b/backends/platform/psp/mp3.h
@@ -0,0 +1,121 @@
+/* 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 SOUND_MP3_PSP_H
+#define SOUND_MP3_PSP_H
+
+#include "common/types.h"
+#include "common/scummsys.h"
+
+namespace Common {
+ class SeekableReadStream;
+}
+
+namespace Audio {
+
+class AudioStream;
+class SeekableAudioStream;
+
+class Mp3PspStream : public SeekableAudioStream {
+protected:
+ enum State {
+ MP3_STATE_INIT, // Need to init the decoder
+ MP3_STATE_READY, // ready for processing data
+ MP3_STATE_EOS // end of data reached (may need to loop)
+ };
+
+ #define MAX_SAMPLES_PER_FRAME 2048 * 2
+ int16 _pcmSamples[MAX_SAMPLES_PER_FRAME] __attribute__((aligned(64))); // samples to output PCM data into
+ byte _codecInBuffer[3072] __attribute__((aligned(64))); // the codec always needs alignment
+ unsigned long _codecParams[65]__attribute__((aligned(64))); // TODO: change to struct
+
+ Common::SeekableReadStream *_inStream;
+ DisposeAfterUse::Flag _disposeAfterUse;
+
+ uint32 _pcmLength; // how many pcm samples we have (/2 for mono)
+
+ uint _posInFrame; // position in frame
+ State _state; // what state the stream is in
+
+ Timestamp _length;
+ uint32 _sampleRate;
+
+ mad_timer_t _totalTime;
+ mad_stream _stream; //
+ mad_header _header; // This is all we need from libmad
+
+ static bool _decoderInit; // has the decoder been initialized
+ static bool _decoderFail; // has the decoder failed to load
+
+ enum {
+ BUFFER_SIZE = 5 * 8192
+ };
+
+ // This buffer contains a slab of input data
+ byte _buf[BUFFER_SIZE + MAD_BUFFER_GUARD];
+
+ void decodeMP3Data();
+ void readMP3DataIntoBuffer();
+
+ static bool loadStartAudioModule(const char *modname, int partition);
+ int initStream();
+ void findValidHeader();
+ void deinitStream();
+
+ // to init and uninit ME decoder
+ static bool initDecoder();
+ static bool stopDecoder();
+
+ // ME functions for stream
+ bool initStreamME();
+ void releaseStreamME();
+
+public:
+ Mp3PspStream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose);
+ ~Mp3PspStream();
+
+ // This function avoids having to create streams when it's not possible
+ static inline bool isOkToCreateStream() {
+ if (_decoderFail) // fatal failure
+ return false;
+ if (!_decoderInit) // if we're not initialized
+ if (!initDecoder()) // check if we failed init
+ return false;
+ return true;
+ }
+
+ int readBuffer(int16 *buffer, const int numSamples);
+
+ bool endOfData() const { return _state == MP3_STATE_EOS; }
+ bool isStereo() const { return MAD_NCHANNELS(&_header) == 2; }
+ int getRate() const { return _header.samplerate; }
+
+ bool seek(const Timestamp &where);
+ Timestamp getLength() const { return _length; }
+};
+
+} // End of namespace Audio
+
+#endif // #ifndef SOUND_MP3_PSP_H
diff --git a/backends/platform/psp/osys_psp.cpp b/backends/platform/psp/osys_psp.cpp
index a36ae1847f..2043a4bef2 100644
--- a/backends/platform/psp/osys_psp.cpp
+++ b/backends/platform/psp/osys_psp.cpp
@@ -37,7 +37,6 @@
#include "backends/platform/psp/psppixelformat.h"
#include "backends/platform/psp/osys_psp.h"
#include "backends/platform/psp/powerman.h"
-#include "backends/platform/psp/thread.h"
#include "backends/saves/psp/psp-saves.h"
#include "backends/timer/default/default-timer.h"
@@ -300,7 +299,7 @@ bool OSystem_PSP::pollEvent(Common::Event &event) {
}
uint32 OSystem_PSP::getMillis() {
- return PspThread::getMillis();
+ return _pspRtc.getMillis();
}
void OSystem_PSP::delayMillis(uint msecs) {
diff --git a/backends/platform/psp/osys_psp.h b/backends/platform/psp/osys_psp.h
index 3f075d0139..a6c84ba39a 100644
--- a/backends/platform/psp/osys_psp.h
+++ b/backends/platform/psp/osys_psp.h
@@ -40,6 +40,7 @@
#include "backends/platform/psp/input.h"
#include "backends/platform/psp/audio.h"
#include "backends/timer/psp/timer.h"
+#include "backends/platform/psp/thread.h"
#include <SDL.h>
@@ -59,6 +60,7 @@ private:
InputHandler _inputHandler;
PspAudio _audio;
PspTimer _pspTimer;
+ PspRtc _pspRtc;
void initSDL();
diff --git a/backends/platform/psp/psp.spec b/backends/platform/psp/psp.spec
index debdab3208..ac325b7fd6 100644
--- a/backends/platform/psp/psp.spec
+++ b/backends/platform/psp/psp.spec
@@ -1,3 +1,3 @@
%rename lib old_lib
*lib:
-%(old_lib) -lz -lstdc++ -lc -lm -lpspprof -lpspvfpu -lpspdebug -lpspgu -lpspge -lpspdisplay -lpspctrl -lpspsdk -lpsputility -lpspuser -lpsppower -lpsphprm -lpsprtc -lpspaudio -lpspkernel
+%(old_lib) -lz -lstdc++ -lc -lm -lpspprof -lpspvfpu -lpspdebug -lpspgu -lpspge -lpspdisplay -lpspctrl -lpspsdk -lpsputility -lpspuser -lpsppower -lpsphprm -lpsprtc -lpspaudio -lpspaudiocodec -lpspkernel
diff --git a/backends/platform/psp/thread.cpp b/backends/platform/psp/thread.cpp
index 88e7b6fe38..4e7d5eada9 100644
--- a/backends/platform/psp/thread.cpp
+++ b/backends/platform/psp/thread.cpp
@@ -29,6 +29,7 @@
#include <pspthreadman.h>
#include "backends/platform/psp/thread.h"
+#include "backends/platform/psp/trace.h"
void PspThread::delayMillis(uint32 ms) {
sceKernelDelayThread(ms * 1000);
@@ -38,15 +39,49 @@ void PspThread::delayMicros(uint32 us) {
sceKernelDelayThread(us);
}
-uint32 PspThread::getMillis() {
+void PspRtc::init() { // init our starting ticks
uint32 ticks[2];
sceRtcGetCurrentTick((u64 *)ticks);
- return (ticks[0]/1000);
+
+ _startMillis = ticks[0]/1000;
+ _startMicros = ticks[0];
+ //_lastMillis = ticks[0]/1000; //debug - only when we don't subtract startMillis
+}
+
+#define MS_LOOP_AROUND 4294967 /* We loop every 2^32 / 1000 = 71 minutes */
+#define MS_LOOP_CHECK 60000 /* Threading can cause weird mixups without this */
+
+// Note that after we fill up 32 bits ie 50 days we'll loop back to 0, which may cause
+// unpredictable results
+uint32 PspRtc::getMillis() {
+ uint32 ticks[2];
+
+ sceRtcGetCurrentTick((u64 *)ticks); // can introduce weird thread delays
+
+ uint32 millis = ticks[0]/1000;
+ millis -= _startMillis; // get ms since start of program
+
+ if ((int)_lastMillis - (int)millis > MS_LOOP_CHECK) { // we must have looped around
+ if (_looped == false) { // check to make sure threads do this once
+ _looped = true;
+ _milliOffset += MS_LOOP_AROUND; // add the needed offset
+ PSP_DEBUG_PRINT("looping around. last ms[%d], curr ms[%d]\n", _lastMillis, millis);
+ }
+ } else {
+ _looped = false;
+ }
+
+ _lastMillis = millis;
+
+ return millis + _milliOffset;
}
-uint32 PspThread::getMicros() {
+uint32 PspRtc::getMicros() {
uint32 ticks[2];
+
sceRtcGetCurrentTick((u64 *)ticks);
+ ticks[0] -= _startMicros;
+
return ticks[0];
}
diff --git a/backends/platform/psp/thread.h b/backends/platform/psp/thread.h
index e83eead68e..380159fa2d 100644
--- a/backends/platform/psp/thread.h
+++ b/backends/platform/psp/thread.h
@@ -32,8 +32,20 @@ class PspThread {
public:
static void delayMillis(uint32 ms);
static void delayMicros(uint32 us);
- static uint32 getMillis();
- static uint32 getMicros();
+};
+
+class PspRtc {
+private:
+ uint32 _startMillis;
+ uint32 _startMicros;
+ uint32 _lastMillis;
+ uint32 _milliOffset; // to prevent looping around of millis
+ bool _looped; // make sure we only loop once
+public:
+ PspRtc() : _startMillis(0), _startMicros(0), _lastMillis(0), _milliOffset(0), _looped(false) { init(); }
+ void init();
+ uint32 getMillis();
+ uint32 getMicros();
};
enum ThreadPriority {
diff --git a/backends/platform/samsungtv/samsungtv.cpp b/backends/platform/samsungtv/samsungtv.cpp
index 4f0f3a1e3e..aa79b92558 100644
--- a/backends/platform/samsungtv/samsungtv.cpp
+++ b/backends/platform/samsungtv/samsungtv.cpp
@@ -30,7 +30,6 @@
bool OSystem_SDL_SamsungTV::hasFeature(Feature f) {
return
(f == kFeatureAspectRatioCorrection) ||
- (f == kFeatureAutoComputeDirtyRects) ||
(f == kFeatureCursorHasPalette);
}
@@ -39,12 +38,6 @@ void OSystem_SDL_SamsungTV::setFeatureState(Feature f, bool enable) {
case kFeatureAspectRatioCorrection:
setAspectRatioCorrection(enable);
break;
- case kFeatureAutoComputeDirtyRects:
- if (enable)
- _modeFlags |= DF_WANT_RECT_OPTIM;
- else
- _modeFlags &= ~DF_WANT_RECT_OPTIM;
- break;
default:
break;
}
@@ -56,8 +49,6 @@ bool OSystem_SDL_SamsungTV::getFeatureState(Feature f) {
switch (f) {
case kFeatureAspectRatioCorrection:
return _videoMode.aspectRatioCorrection;
- case kFeatureAutoComputeDirtyRects:
- return _modeFlags & DF_WANT_RECT_OPTIM;
default:
return false;
}
diff --git a/backends/platform/symbian/src/SymbianOS.cpp b/backends/platform/symbian/src/SymbianOS.cpp
index f8df2a5d5c..2ae47b07a8 100644
--- a/backends/platform/symbian/src/SymbianOS.cpp
+++ b/backends/platform/symbian/src/SymbianOS.cpp
@@ -83,7 +83,6 @@ bool OSystem_SDL_Symbian::hasFeature(Feature f) {
switch (f) {
case kFeatureFullscreenMode:
case kFeatureAspectRatioCorrection:
- case kFeatureAutoComputeDirtyRects:
case kFeatureCursorHasPalette:
#ifdef USE_VIBRA_SE_PXXX
case kFeatureVibration:
diff --git a/backends/platform/wince/wince-sdl.cpp b/backends/platform/wince/wince-sdl.cpp
index 7ce689fb63..b3480702b5 100644
--- a/backends/platform/wince/wince-sdl.cpp
+++ b/backends/platform/wince/wince-sdl.cpp
@@ -926,7 +926,7 @@ const OSystem::GraphicsMode *OSystem_WINCE3::getSupportedGraphicsModes() const {
}
bool OSystem_WINCE3::hasFeature(Feature f) {
- return (f == kFeatureAutoComputeDirtyRects || f == kFeatureVirtualKeyboard);
+ return (f == kFeatureVirtualKeyboard);
}
void OSystem_WINCE3::setFeatureState(Feature f, bool enable) {
@@ -1151,14 +1151,12 @@ bool OSystem_WINCE3::update_scalers() {
_scaleFactorYm = 1;
_scaleFactorYd = 1;
_scalerProc = DownscaleHorizByThreeQuarters;
- _modeFlags = 0;
} else {
_scaleFactorXm = 1;
_scaleFactorXd = 1;
_scaleFactorYm = 1;
_scaleFactorYd = 1;
_scalerProc = Normal1x;
- _modeFlags = 0;
}
} else if ( _orientationLandscape && (_videoMode.screenWidth == 320 || !_videoMode.screenWidth)) {
if (!_panelVisible && !_hasSmartphoneResolution && !_overlayVisible && _canBeAspectScaled) {
@@ -1167,7 +1165,6 @@ bool OSystem_WINCE3::update_scalers() {
_scaleFactorYm = 6;
_scaleFactorYd = 5;
_scalerProc = Normal1xAspect;
- _modeFlags = 0;
_videoMode.aspectRatioCorrection = true;
} else {
_scaleFactorXm = 1;
@@ -1175,7 +1172,6 @@ bool OSystem_WINCE3::update_scalers() {
_scaleFactorYm = 1;
_scaleFactorYd = 1;
_scalerProc = Normal1x;
- _modeFlags = 0;
}
} else if (_videoMode.screenWidth == 640 && !(isOzone() && (getScreenWidth() >= 640 || getScreenHeight() >= 640))) {
_scaleFactorXm = 1;
@@ -1183,14 +1179,12 @@ bool OSystem_WINCE3::update_scalers() {
_scaleFactorYm = 1;
_scaleFactorYd = 2;
_scalerProc = DownscaleAllByHalf;
- _modeFlags = 0;
} else if (_videoMode.screenWidth == 640 && (isOzone() && (getScreenWidth() >= 640 || getScreenHeight() >= 640))) {
_scaleFactorXm = 1;
_scaleFactorXd = 1;
_scaleFactorYm = 1;
_scaleFactorYd = 1;
_scalerProc = Normal1x;
- _modeFlags = 0;
}
return true;
@@ -1203,7 +1197,6 @@ bool OSystem_WINCE3::update_scalers() {
_scaleFactorYm = 12;
_scaleFactorYd = 5;
_scalerProc = Normal2xAspect;
- _modeFlags = 0;
_videoMode.aspectRatioCorrection = true;
} else if ( (_panelVisible || _overlayVisible) && _canBeAspectScaled ) {
_scaleFactorXm = 2;
@@ -1211,7 +1204,6 @@ bool OSystem_WINCE3::update_scalers() {
_scaleFactorYm = 2;
_scaleFactorYd = 1;
_scalerProc = Normal2x;
- _modeFlags = 0;
}
return true;
}
@@ -1232,7 +1224,6 @@ bool OSystem_WINCE3::update_scalers() {
_scaleFactorYm = 7;
_scaleFactorYd = 8;
_scalerProc = SmartphoneLandscape;
- _modeFlags = 0;
initZones();
return true;
}
@@ -1824,7 +1815,6 @@ void OSystem_WINCE3::copyRectToOverlay(const OverlayColor *buf, int pitch, int x
return;
// Mark the modified region as dirty
- _cksumValid = false;
addDirtyRect(x, y, w, h);
undrawMouse();
@@ -1851,40 +1841,31 @@ void OSystem_WINCE3::copyRectToScreen(const byte *src, int pitch, int x, int y,
Common::StackLock lock(_graphicsMutex); // Lock the mutex until this function ends
- if (((long)src & 3) == 0 && pitch == _videoMode.screenWidth && x == 0 && y == 0 &&
- w == _videoMode.screenWidth && h == _videoMode.screenHeight && _modeFlags & DF_WANT_RECT_OPTIM) {
- /* Special, optimized case for full screen updates.
- * It tries to determine what areas were actually changed,
- * and just updates those, on the actual display. */
- addDirtyRgnAuto(src);
- } else {
- /* Clip the coordinates */
- if (x < 0) {
- w += x;
- src -= x;
- x = 0;
- }
+ /* Clip the coordinates */
+ if (x < 0) {
+ w += x;
+ src -= x;
+ x = 0;
+ }
- if (y < 0) {
- h += y;
- src -= y * pitch;
- y = 0;
- }
+ if (y < 0) {
+ h += y;
+ src -= y * pitch;
+ y = 0;
+ }
- if (w > _videoMode.screenWidth - x) {
- w = _videoMode.screenWidth - x;
- }
+ if (w > _videoMode.screenWidth - x) {
+ w = _videoMode.screenWidth - x;
+ }
- if (h > _videoMode.screenHeight - y) {
- h = _videoMode.screenHeight - y;
- }
+ if (h > _videoMode.screenHeight - y) {
+ h = _videoMode.screenHeight - y;
+ }
- if (w <= 0 || h <= 0)
- return;
+ if (w <= 0 || h <= 0)
+ return;
- _cksumValid = false;
- addDirtyRect(x, y, w, h);
- }
+ addDirtyRect(x, y, w, h);
undrawMouse();
diff --git a/base/commandLine.cpp b/base/commandLine.cpp
index 207ff79c4c..1c548d3f50 100644
--- a/base/commandLine.cpp
+++ b/base/commandLine.cpp
@@ -51,7 +51,7 @@ static const char USAGE_STRING[] =
;
// DONT FIXME: DO NOT ORDER ALPHABETICALLY, THIS IS ORDERED BY IMPORTANCE/CATEGORY! :)
-#if defined(PALMOS_MODE) || defined(__SYMBIAN32__) || defined(__GP32__)
+#if defined(PALMOS_MODE) || defined(__SYMBIAN32__) || defined(__GP32__) || defined(ANDROID)
static const char HELP_STRING[] = "NoUsageString"; // save more data segment space
#else
static const char HELP_STRING[] =
@@ -948,7 +948,7 @@ Common::Error processSettings(Common::String &command, Common::StringMap &settin
// environment variable. This is weaker than a --savepath on the
// command line, but overrides the default savepath, hence it is
// handled here, just before the command line gets parsed.
-#if !defined(MACOS_CARBON) && !defined(_WIN32_WCE) && !defined(PALMOS_MODE) && !defined(__GP32__)
+#if !defined(MACOS_CARBON) && !defined(_WIN32_WCE) && !defined(PALMOS_MODE) && !defined(__GP32__) && !defined(ANDROID)
if (!settings.contains("savepath")) {
const char *dir = getenv("SCUMMVM_SAVEPATH");
if (dir && *dir && strlen(dir) < MAXPATHLEN) {
diff --git a/common/str.h b/common/str.h
index 12e2b0d2d3..189c37adb4 100644
--- a/common/str.h
+++ b/common/str.h
@@ -222,6 +222,12 @@ public:
typedef const char * const_iterator;
iterator begin() {
+ // Since the user could potentially
+ // change the string via the returned
+ // iterator we have to assure we are
+ // pointing to a unique storage.
+ makeUnique();
+
return _str;
}
diff --git a/common/stream.h b/common/stream.h
index b6afcd85a9..11041fa3ce 100644
--- a/common/stream.h
+++ b/common/stream.h
@@ -156,7 +156,7 @@ public:
class ReadStream : virtual public Stream {
public:
/**
- * Returns true if a read failed because the stream has been reached.
+ * Returns true if a read failed because the stream end has been reached.
* This flag is cleared by clearErr().
* For a SeekableReadStream, it is also cleared by a successful seek.
*/
diff --git a/common/system.h b/common/system.h
index 76689bf381..60cea49f87 100644
--- a/common/system.h
+++ b/common/system.h
@@ -151,18 +151,6 @@ public:
kFeatureVirtualKeyboard,
/**
- * This flag is a bit more obscure: it gives a hint to the backend that
- * the frontend code is very inefficient in doing screen updates. So
- * the frontend might do a lot of fullscreen blits even though only a
- * tiny portion of the actual screen data changed. In that case, it
- * might pay off for the backend to compute which parts actually changed,
- * and then only mark those as dirty.
- * Implementing this is purely optional, and no harm should arise
- * when not doing so (except for decreased speed in said frontends).
- */
- kFeatureAutoComputeDirtyRects,
-
- /**
* This flag determines whether or not the cursor can have its own palette.
* It is currently used only by some Macintosh versions of Humongous
* Entertainment games. If the backend doesn't implement this feature then
diff --git a/common/textconsole.cpp b/common/textconsole.cpp
index eef58fa39c..87ba55ebf1 100644
--- a/common/textconsole.cpp
+++ b/common/textconsole.cpp
@@ -43,6 +43,10 @@ extern bool isSmartphone();
#define fputs(str, file) DS::std_fwrite(str, strlen(str), 1, file)
#endif
+#ifdef ANDROID
+ #include <android/log.h>
+#endif
+
namespace Common {
static OutputFormatter s_errorOutputFormatter = 0;
@@ -71,7 +75,9 @@ void warning(const char *s, ...) {
vsnprintf(buf, STRINGBUFLEN, s, va);
va_end(va);
-#if !defined (__SYMBIAN32__)
+#if defined( ANDROID )
+ __android_log_write(ANDROID_LOG_WARN, "ScummVM", buf);
+#elif !defined (__SYMBIAN32__)
fputs("WARNING: ", stderr);
fputs(buf, stderr);
fputs("!\n", stderr);
@@ -141,6 +147,10 @@ void NORETURN_PRE error(const char *s, ...) {
#endif
#endif
+#ifdef ANDROID
+ __android_log_assert("Fatal error", "ScummVM", "%s", buf_output);
+#endif
+
#ifdef PALMOS_MODE
extern void PalmFatalError(const char *err);
PalmFatalError(buf_output);
diff --git a/common/unzip.cpp b/common/unzip.cpp
index a83f70d671..e46106025e 100644
--- a/common/unzip.cpp
+++ b/common/unzip.cpp
@@ -1433,11 +1433,11 @@ Common::SeekableReadStream *ZipArchive::createReadStreamForMember(const Common::
unz_file_info fileInfo;
unzOpenCurrentFile(_zipFile);
unzGetCurrentFileInfo(_zipFile, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
- byte *buffer = (byte *)calloc(fileInfo.uncompressed_size+1, 1);
+ byte *buffer = (byte *)malloc(fileInfo.uncompressed_size);
assert(buffer);
unzReadCurrentFile(_zipFile, buffer, fileInfo.uncompressed_size);
unzCloseCurrentFile(_zipFile);
- return new Common::MemoryReadStream(buffer, fileInfo.uncompressed_size+1, DisposeAfterUse::YES);
+ return new Common::MemoryReadStream(buffer, fileInfo.uncompressed_size, DisposeAfterUse::YES);
// FIXME: instead of reading all into a memory stream, we could
// instead create a new ZipStream class. But then we have to be
diff --git a/configure b/configure
index 17da22fb8f..0c06be13ea 100755
--- a/configure
+++ b/configure
@@ -999,6 +999,11 @@ wince)
_host_cpu=arm
_host_alias=arm-wince-mingw32ce
;;
+android)
+ _host_os=android
+ _host_cpu=arm
+ _host_alias=arm-android-eabi
+ ;;
*)
if test -n "$_host"; then
guessed_host=`$_srcdir/config.sub $_host`
@@ -1077,6 +1082,12 @@ psp)
exit 1
fi
;;
+android)
+ if test -z "$ANDROID_SDK"; then
+ echo "Please set ANDROID_SDK in your environment. export ANDROID_SDK=<path to Android SDK>"
+ exit 1
+ fi
+ ;;
*)
;;
esac
@@ -1399,6 +1410,11 @@ case $_host_os in
DEFINES="$DEFINES -D_WIN32_WCE=300 -D__ARM__ -D_ARM_ -DUNICODE -DFPM_DEFAULT -DNONSTANDARD_PORT"
DEFINES="$DEFINES -DWIN32 -Dcdecl= -D__cdecl__="
;;
+ android)
+ DEFINES="$DEFINES -DUNIX"
+ CXXFLAGS="$CXXFLAGS -Os -msoft-float -mtune=xscale -march=armv5te -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5TE__"
+ add_line_to_config_mk "ANDROID_SDK = $ANDROID_SDK"
+ ;;
# given this is a shell script assume some type of unix
*)
echo "WARNING: could not establish system type, assuming unix like"
@@ -1647,6 +1663,19 @@ if test -n "$_host"; then
_mt32emu="no"
_port_mk="backends/platform/wince/wince.mk"
;;
+ android)
+ DEFINES="$DEFINES -DANDROID -DUNIX -DUSE_ARM_SMUSH_ASM"
+ _endian=little
+ _need_memalign=yes
+ add_line_to_config_mk 'USE_ARM_SOUND_ASM = 1'
+ add_line_to_config_mk 'USE_ARM_SMUSH_ASM = 1'
+ add_line_to_config_mk 'USE_ARM_GFX_ASM = 1'
+ add_line_to_config_mk 'USE_ARM_SCALER_ASM = 1'
+ add_line_to_config_mk 'USE_ARM_COSTUME_ASM = 1'
+ _backend="android"
+ _port_mk="backends/platform/android/android.mk"
+ _build_hq_scalers="no"
+ ;;
*)
echo "WARNING: Unknown target, continuing with auto-detected values"
;;
@@ -1825,7 +1854,7 @@ POST_OBJS_FLAGS := -Wl,-no-whole-archive
LIBS += -ldl
'
;;
- linux*)
+ linux*|android)
_def_plugin='
#define PLUGIN_PREFIX "lib"
#define PLUGIN_SUFFIX ".so"
@@ -2432,6 +2461,14 @@ case $_backend in
INCLUDES="$INCLUDES "'-I$(srcdir) -I$(srcdir)/backends/platform/wince -I$(srcdir)/engines -I$(srcdir)/backends/platform/wince/missing/gcc -I$(srcdir)/backends/platform/wince/CEgui -I$(srcdir)/backends/platform/wince/CEkeys'
LIBS="$LIBS -static -lSDL"
;;
+ android)
+ # -lgcc is carefully placed here - we want to catch
+ # all toolchain symbols in *our* libraries rather
+ # than pick up anything unhygenic from the Android libs.
+ LIBS="$LIBS -lgcc -lstdc++ -llog -lGLESv1_CM -lEGL"
+ DEFINES="$DEFINES -D__ANDROID__ -DANDROID_BACKEND -DREDUCE_MEMORY_USAGE"
+ add_line_to_config_mk 'PLUGIN_LDFLAGS += $(LDFLAGS) -Wl,-shared,-Bsymbolic'
+ ;;
*)
echo "support for $_backend backend not implemented in configure script yet"
exit 1
@@ -2447,7 +2484,7 @@ if test "$have_gcc" = yes ; then
case $_host_os in
# newlib-based system include files suppress non-C89 function
# declarations under __STRICT_ANSI__
- mingw* | dreamcast | wii | gamecube | psp | wince | amigaos*)
+ mingw* | dreamcast | wii | gamecube | psp | wince | amigaos* | android)
CXXFLAGS="$CXXFLAGS -W -Wno-unused-parameter"
;;
*)
@@ -2468,7 +2505,7 @@ fi;
# Some platforms use certain GNU extensions in header files
case $_host_os in
-gamecube | psp | wii)
+gamecube | psp | wii | android)
;;
*)
CXXFLAGS="$CXXFLAGS -pedantic"
diff --git a/dists/android/mkmanifest.pl b/dists/android/mkmanifest.pl
new file mode 100644
index 0000000000..00d15f561e
--- /dev/null
+++ b/dists/android/mkmanifest.pl
@@ -0,0 +1,169 @@
+#!/usr/bin/perl
+
+use File::Basename qw(dirname);
+use File::Path qw(mkpath);
+use IO::File;
+use XML::Writer;
+use XML::Parser;
+use Getopt::Long;
+
+use warnings;
+use strict;
+
+use constant ANDROID => 'http://schemas.android.com/apk/res/android';
+
+my $id;
+my $package_versionName;
+my $package_versionCode;
+my $configure = 'configure';
+my $stringres = 'res/string/values.xml';
+my $manifest = 'AndroidManifest.xml';
+my $master_manifest;
+my @unpack_libs;
+GetOptions('id=s' => \$id,
+ 'version-name=s' => \$package_versionName,
+ 'version-code=i' => \$package_versionCode,
+ 'configure=s' => \$configure,
+ 'stringres=s' => \$stringres,
+ 'manifest=s' => \$manifest,
+ 'master-manifest=s' => \$master_manifest,
+ 'unpacklib=s' => \@unpack_libs,
+ ) or die;
+die "Missing required arg"
+ unless $id and $package_versionName and $package_versionCode;
+
+
+sub grope_engine_info {
+ my $configure = shift;
+ my @ret;
+ while (<$configure>) {
+ m/^add_engine \s+ (\w+) \s+ "(.*?)" \s+ \w+ (?:\s+ "([\w\s]*)")?/x
+ or next;
+ my $subengines = $3 || '';
+ my %info = (id => $1, name => $2,
+ subengines => [split / /, $subengines]);
+ push @ret, \%info;
+ }
+ return @ret;
+}
+
+sub read_constraints {
+ my $manifest = shift;
+ my @constraints;
+ my $parser = new XML::Parser Handlers => {
+ Start => sub {
+ my $expat = shift;
+ my $elem = shift;
+ return if $elem !~
+ /^(uses-configuration|supports-screens|uses-sdk)$/;
+ my @constraint = ($elem);
+ while (@_) {
+ my $attr = shift;
+ my $value = shift;
+ $attr = [ANDROID, $attr] if $attr =~ s/^android://;
+ push @constraint, $attr, $value;
+ }
+ push @constraints, \@constraint;
+ },
+ };
+ $parser->parse($manifest);
+ return @constraints;
+}
+
+sub print_stringres {
+ my $output = shift;
+ my $info = shift;
+
+ my $writer = new XML::Writer(OUTPUT => $output, ENCODING => 'utf-8',
+ DATA_MODE => 1, DATA_INDENT => 2);
+
+ $writer->xmlDecl();
+ $writer->startTag('resources');
+
+ while (my ($k,$v) = each %$info) {
+ $writer->dataElement('string', $v, name => $k);
+ }
+
+ $writer->endTag('resources');
+ $writer->end();
+}
+
+sub print_manifest {
+ my $output = shift;
+ my $info = shift;
+ my $constraints = shift;
+
+ my $writer = new XML::Writer(OUTPUT => $output, ENCODING => 'utf-8',
+ DATA_MODE => 1, DATA_INDENT => 2,
+ NAMESPACES => 1,
+ PREFIX_MAP => {ANDROID, 'android'});
+
+ $writer->xmlDecl();
+
+ $writer->startTag(
+ 'manifest',
+ 'package' => "org.inodes.gus.scummvm.plugin.$info->{name}",
+ [ANDROID, 'versionCode'] => $package_versionCode,
+ [ANDROID, 'versionName'] => $package_versionName,
+ );
+
+ $writer->startTag(
+ 'application',
+ [ANDROID, 'label'] => '@string/app_name',
+ [ANDROID, 'description'] => '@string/app_desc',
+ [ANDROID, 'icon'] => '@drawable/scummvm',
+ );
+
+ $writer->startTag(
+ 'receiver',
+ [ANDROID, 'name'] => 'org.inodes.gus.scummvm.PluginProvider',
+ [ANDROID, 'process'] => 'org.inodes.gus.scummvm');
+
+ $writer->startTag('intent-filter');
+ $writer->emptyTag('action', [ANDROID, 'name'] =>
+ 'org.inodes.gus.scummvm.action.PLUGIN_QUERY');
+ $writer->emptyTag('category', [ANDROID, 'name'] =>
+ 'android.intent.category.INFO');
+ $writer->endTag('intent-filter');
+ $writer->emptyTag(
+ 'meta-data',
+ [ANDROID, 'name'] => 'org.inodes.gus.scummvm.meta.UNPACK_LIB',
+ [ANDROID, 'value'] => $_)
+ for @{$info->{unpack_libs}};
+
+ $writer->endTag('receiver');
+ $writer->endTag('application');
+
+ $writer->emptyTag('uses-permission', [ANDROID, 'name'] =>
+ 'org.inodes.gus.scummvm.permission.SCUMMVM_PLUGIN');
+
+ $writer->emptyTag(@$_) foreach @$constraints;
+
+ $writer->endTag('manifest');
+ $writer->end();
+}
+
+
+my %engines;
+for my $engine (grope_engine_info(new IO::File $configure, 'r')) {
+ $engines{$engine->{id}} = $engine;
+}
+
+my @games = ($id, @{$engines{$id}{subengines}});
+my $games_desc = join('; ', map $engines{$_}{name}, @games);
+
+my @constraints = read_constraints(new IO::File $master_manifest, 'r');
+
+print "Writing $stringres ...\n";
+mkpath(dirname($stringres));
+print_stringres(IO::File->new($stringres, 'w'),
+ {app_name => qq{ScummVM plugin: "$id"},
+ app_desc => "Game engine for: $games_desc",
+ });
+
+print "Writing $manifest ...\n";
+mkpath(dirname($manifest));
+print_manifest(IO::File->new($manifest, 'w'),
+ {name => $id, unpack_libs => \@unpack_libs}, \@constraints);
+
+exit 0;
diff --git a/dists/android/res/drawable/gradient.xml b/dists/android/res/drawable/gradient.xml
new file mode 100644
index 0000000000..dbfd9b5b34
--- /dev/null
+++ b/dists/android/res/drawable/gradient.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <gradient
+ android:startColor="#e9bb8b"
+ android:endColor="#d16e09"
+ android:angle="315" />
+</shape>
diff --git a/dists/android/res/layout/main.xml b/dists/android/res/layout/main.xml
new file mode 100644
index 0000000000..f5276ce41b
--- /dev/null
+++ b/dists/android/res/layout/main.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<org.inodes.gus.scummvm.EditableSurfaceView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent" android:layout_height="fill_parent"
+ android:id="@+id/main_surface"
+ android:gravity="center"
+ android:keepScreenOn="true"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+/>
diff --git a/dists/android/res/layout/splash.xml b/dists/android/res/layout/splash.xml
new file mode 100644
index 0000000000..e9fd5f70e7
--- /dev/null
+++ b/dists/android/res/layout/splash.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:background="@drawable/gradient"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:adjustViewBounds="true"
+ android:scaleType="fitCenter"
+ android:src="@drawable/scummvm_big" />
+ <ProgressBar android:id="@+id/progress"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="300dip"
+ android:layout_height="wrap_content"
+ android:padding="20dip"/>
+</LinearLayout>
diff --git a/dists/android/res/values/strings.xml b/dists/android/res/values/strings.xml
new file mode 100644
index 0000000000..e06509d3ed
--- /dev/null
+++ b/dists/android/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">ScummVM</string>
+ <string name="app_desc">Graphic adventure game engine</string>
+ <string name="quit">Quit</string>
+ <string name="scummvm_perm_plugin_label">ScummVM plugin</string>
+ <string name="scummvm_perm_plugin_desc">Allows the application to
+ provide a ScummVM loadable plugin: code that will be executed in the
+ ScummVM application. Malicious plugins may do anything ScummVM
+ itself could do: write to your SD card, delete your savegames,
+ change the ScummVM background to puce, replace menu labels with rude
+ words, etc.</string>
+ <string name="no_sdcard_title">No SD card?</string>
+ <string name="no_sdcard">Unable to read your SD card. This usually
+ means you still have it mounted on your PC. Unmount, reinsert,
+ whatever and then try again.</string>
+ <string name="no_plugins_title">No plugins found</string>
+ <string name="no_plugins_found">ScummVM requires at least one <i>game
+ engine</i> to be useful. Engines are available as separate plugin
+ packages, from wherever you found ScummVM.</string>
+ <string name="to_market">To Market</string>
+</resources>
diff --git a/dists/iphone/scummvm.xcodeproj/project.pbxproj b/dists/iphone/scummvm.xcodeproj/project.pbxproj
index 95bf0690a3..032470cf25 100755
--- a/dists/iphone/scummvm.xcodeproj/project.pbxproj
+++ b/dists/iphone/scummvm.xcodeproj/project.pbxproj
@@ -735,7 +735,6 @@
DF09418A0F63CB26002D821E /* detection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFAAAFFB0F0112DF003E9390 /* detection.cpp */; };
DF09418B0F63CB26002D821E /* thumbnail_intern.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFAAB0010F011392003E9390 /* thumbnail_intern.cpp */; };
DF09418C0F63CB26002D821E /* dither.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFB900F485D890006E566 /* dither.cpp */; };
- DF0941910F63CB26002D821E /* video_player.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFBA70F485D950006E566 /* video_player.cpp */; };
DF0941920F63CB26002D821E /* debug.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFBD10F485DFB0006E566 /* debug.cpp */; };
DF0941930F63CB26002D821E /* GuiManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFBD50F485E360006E566 /* GuiManager.cpp */; };
DF0941940F63CB26002D821E /* posix-saves.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFBF80F4860A60006E566 /* posix-saves.cpp */; };
@@ -875,7 +874,6 @@
DF2EC51910E64EE600765801 /* wave6581.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2EC51710E64EE600765801 /* wave6581.cpp */; };
DF2EC51A10E64EE600765801 /* wave6581.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2EC51710E64EE600765801 /* wave6581.cpp */; };
DF2FFB930F485D890006E566 /* dither.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFB900F485D890006E566 /* dither.cpp */; };
- DF2FFBB70F485D950006E566 /* video_player.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFBA70F485D950006E566 /* video_player.cpp */; };
DF2FFBD30F485DFB0006E566 /* debug.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFBD10F485DFB0006E566 /* debug.cpp */; };
DF2FFBD90F485E360006E566 /* GuiManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFBD50F485E360006E566 /* GuiManager.cpp */; };
DF2FFBFC0F4860A60006E566 /* posix-saves.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFBF80F4860A60006E566 /* posix-saves.cpp */; };
@@ -1915,6 +1913,33 @@
DFAAAFFC0F0112DF003E9390 /* detection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFAAAFFB0F0112DF003E9390 /* detection.cpp */; };
DFAAB0020F011392003E9390 /* thumbnail_intern.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFAAB0010F011392003E9390 /* thumbnail_intern.cpp */; };
DFAAD23D0F50120E00C3A4E2 /* console.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFAAD2390F50120E00C3A4E2 /* console.cpp */; };
+ DFB0576811B753AF0015AE65 /* mpeg_player.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0576211B753AF0015AE65 /* mpeg_player.cpp */; };
+ DFB0576911B753AF0015AE65 /* qt_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0576411B753AF0015AE65 /* qt_decoder.cpp */; };
+ DFB0576A11B753AF0015AE65 /* video_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0576611B753AF0015AE65 /* video_decoder.cpp */; };
+ DFB0576B11B753AF0015AE65 /* mpeg_player.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0576211B753AF0015AE65 /* mpeg_player.cpp */; };
+ DFB0576C11B753AF0015AE65 /* qt_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0576411B753AF0015AE65 /* qt_decoder.cpp */; };
+ DFB0576D11B753AF0015AE65 /* video_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0576611B753AF0015AE65 /* video_decoder.cpp */; };
+ DFB0576E11B753AF0015AE65 /* mpeg_player.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0576211B753AF0015AE65 /* mpeg_player.cpp */; };
+ DFB0576F11B753AF0015AE65 /* qt_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0576411B753AF0015AE65 /* qt_decoder.cpp */; };
+ DFB0577011B753AF0015AE65 /* video_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0576611B753AF0015AE65 /* video_decoder.cpp */; };
+ DFB0577611B753DA0015AE65 /* rational.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0577411B753DA0015AE65 /* rational.cpp */; };
+ DFB0577711B753DA0015AE65 /* rational.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0577411B753DA0015AE65 /* rational.cpp */; };
+ DFB0577811B753DA0015AE65 /* rational.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0577411B753DA0015AE65 /* rational.cpp */; };
+ DFB0578011B7541F0015AE65 /* resource_audio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0577D11B7541F0015AE65 /* resource_audio.cpp */; };
+ DFB0578111B7541F0015AE65 /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0577E11B7541F0015AE65 /* util.cpp */; };
+ DFB0578211B7541F0015AE65 /* resource_audio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0577D11B7541F0015AE65 /* resource_audio.cpp */; };
+ DFB0578311B7541F0015AE65 /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0577E11B7541F0015AE65 /* util.cpp */; };
+ DFB0578411B7541F0015AE65 /* resource_audio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0577D11B7541F0015AE65 /* resource_audio.cpp */; };
+ DFB0578511B7541F0015AE65 /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0577E11B7541F0015AE65 /* util.cpp */; };
+ DFB0578A11B754570015AE65 /* maciconbar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0578811B754570015AE65 /* maciconbar.cpp */; };
+ DFB0578B11B754570015AE65 /* maciconbar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0578811B754570015AE65 /* maciconbar.cpp */; };
+ DFB0578C11B754570015AE65 /* maciconbar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0578811B754570015AE65 /* maciconbar.cpp */; };
+ DFB0579111B7547D0015AE65 /* pict.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0578F11B7547D0015AE65 /* pict.cpp */; };
+ DFB0579211B7547D0015AE65 /* pict.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0578F11B7547D0015AE65 /* pict.cpp */; };
+ DFB0579311B7547D0015AE65 /* pict.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0578F11B7547D0015AE65 /* pict.cpp */; };
+ DFB0579811B7549C0015AE65 /* cinepak.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0579611B7549C0015AE65 /* cinepak.cpp */; };
+ DFB0579911B7549C0015AE65 /* cinepak.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0579611B7549C0015AE65 /* cinepak.cpp */; };
+ DFB0579A11B7549C0015AE65 /* cinepak.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0579611B7549C0015AE65 /* cinepak.cpp */; };
DFC831210F48AF19005EF03C /* detection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFC8301A0F48AF18005EF03C /* detection.cpp */; };
DFC831230F48AF19005EF03C /* game.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFC8301D0F48AF18005EF03C /* game.cpp */; };
DFC831240F48AF19005EF03C /* gc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFC8301E0F48AF18005EF03C /* gc.cpp */; };
@@ -2796,7 +2821,6 @@
DFF95C1F0FB22D5700A3EC78 /* detection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFAAAFFB0F0112DF003E9390 /* detection.cpp */; };
DFF95C200FB22D5700A3EC78 /* thumbnail_intern.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFAAB0010F011392003E9390 /* thumbnail_intern.cpp */; };
DFF95C210FB22D5700A3EC78 /* dither.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFB900F485D890006E566 /* dither.cpp */; };
- DFF95C260FB22D5700A3EC78 /* video_player.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFBA70F485D950006E566 /* video_player.cpp */; };
DFF95C270FB22D5700A3EC78 /* debug.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFBD10F485DFB0006E566 /* debug.cpp */; };
DFF95C280FB22D5700A3EC78 /* GuiManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFBD50F485E360006E566 /* GuiManager.cpp */; };
DFF95C290FB22D5700A3EC78 /* posix-saves.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFBF80F4860A60006E566 /* posix-saves.cpp */; };
@@ -2985,8 +3009,6 @@
DF2FFB900F485D890006E566 /* dither.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dither.cpp; sourceTree = "<group>"; };
DF2FFB910F485D890006E566 /* dither.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dither.h; sourceTree = "<group>"; };
DF2FFB920F485D890006E566 /* pixelformat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pixelformat.h; sourceTree = "<group>"; };
- DF2FFBA70F485D950006E566 /* video_player.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = video_player.cpp; sourceTree = "<group>"; };
- DF2FFBA80F485D950006E566 /* video_player.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = video_player.h; sourceTree = "<group>"; };
DF2FFBD10F485DFB0006E566 /* debug.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = debug.cpp; sourceTree = "<group>"; };
DF2FFBD20F485DFB0006E566 /* debug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = debug.h; sourceTree = "<group>"; };
DF2FFBD50F485E360006E566 /* GuiManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GuiManager.cpp; sourceTree = "<group>"; };
@@ -4358,6 +4380,24 @@
DFAAB0010F011392003E9390 /* thumbnail_intern.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = thumbnail_intern.cpp; sourceTree = "<group>"; };
DFAAD2390F50120E00C3A4E2 /* console.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = console.cpp; sourceTree = "<group>"; };
DFAAD23A0F50120E00C3A4E2 /* console.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = console.h; sourceTree = "<group>"; };
+ DFB0576211B753AF0015AE65 /* mpeg_player.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mpeg_player.cpp; sourceTree = "<group>"; };
+ DFB0576311B753AF0015AE65 /* mpeg_player.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mpeg_player.h; sourceTree = "<group>"; };
+ DFB0576411B753AF0015AE65 /* qt_decoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = qt_decoder.cpp; sourceTree = "<group>"; };
+ DFB0576511B753AF0015AE65 /* qt_decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = qt_decoder.h; sourceTree = "<group>"; };
+ DFB0576611B753AF0015AE65 /* video_decoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = video_decoder.cpp; sourceTree = "<group>"; };
+ DFB0576711B753AF0015AE65 /* video_decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = video_decoder.h; sourceTree = "<group>"; };
+ DFB0577311B753DA0015AE65 /* debug-channels.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "debug-channels.h"; sourceTree = "<group>"; };
+ DFB0577411B753DA0015AE65 /* rational.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rational.cpp; sourceTree = "<group>"; };
+ DFB0577511B753DA0015AE65 /* rational.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rational.h; sourceTree = "<group>"; };
+ DFB0577D11B7541F0015AE65 /* resource_audio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = resource_audio.cpp; sourceTree = "<group>"; };
+ DFB0577E11B7541F0015AE65 /* util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = util.cpp; sourceTree = "<group>"; };
+ DFB0577F11B7541F0015AE65 /* util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = util.h; sourceTree = "<group>"; };
+ DFB0578811B754570015AE65 /* maciconbar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = maciconbar.cpp; sourceTree = "<group>"; };
+ DFB0578911B754570015AE65 /* maciconbar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = maciconbar.h; sourceTree = "<group>"; };
+ DFB0578F11B7547D0015AE65 /* pict.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pict.cpp; sourceTree = "<group>"; };
+ DFB0579011B7547D0015AE65 /* pict.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pict.h; sourceTree = "<group>"; };
+ DFB0579611B7549C0015AE65 /* cinepak.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cinepak.cpp; sourceTree = "<group>"; };
+ DFB0579711B7549C0015AE65 /* cinepak.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cinepak.h; sourceTree = "<group>"; };
DFC8301A0F48AF18005EF03C /* detection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = detection.cpp; sourceTree = "<group>"; };
DFC8301D0F48AF18005EF03C /* game.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = game.cpp; sourceTree = "<group>"; };
DFC8301E0F48AF18005EF03C /* gc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gc.cpp; sourceTree = "<group>"; };
@@ -4776,6 +4816,12 @@
DF2FFB940F485D950006E566 /* video */ = {
isa = PBXGroup;
children = (
+ DFB0576211B753AF0015AE65 /* mpeg_player.cpp */,
+ DFB0576311B753AF0015AE65 /* mpeg_player.h */,
+ DFB0576411B753AF0015AE65 /* qt_decoder.cpp */,
+ DFB0576511B753AF0015AE65 /* qt_decoder.h */,
+ DFB0576611B753AF0015AE65 /* video_decoder.cpp */,
+ DFB0576711B753AF0015AE65 /* video_decoder.h */,
DF90EABF10B023F300C8F93F /* codecs */,
DF90EAB610B023D100C8F93F /* avi_decoder.cpp */,
DF90EAB710B023D100C8F93F /* avi_decoder.h */,
@@ -4786,8 +4832,6 @@
DF6118630FE3A9410042AD3F /* flic_decoder.h */,
DF6118640FE3A9410042AD3F /* smk_decoder.cpp */,
DF6118650FE3A9410042AD3F /* smk_decoder.h */,
- DF2FFBA70F485D950006E566 /* video_player.cpp */,
- DF2FFBA80F485D950006E566 /* video_player.h */,
);
path = video;
sourceTree = "<group>";
@@ -4884,6 +4928,8 @@
DF45B175116628A5009B85CC /* graphics */ = {
isa = PBXGroup;
children = (
+ DFB0578811B754570015AE65 /* maciconbar.cpp */,
+ DFB0578911B754570015AE65 /* maciconbar.h */,
DF9B924F118E46A00069C19D /* fontsjis.cpp */,
DF9B9250118E46A00069C19D /* fontsjis.h */,
DF45B176116628A5009B85CC /* animate.cpp */,
@@ -6388,6 +6434,8 @@
DF90EABF10B023F300C8F93F /* codecs */ = {
isa = PBXGroup;
children = (
+ DFB0579611B7549C0015AE65 /* cinepak.cpp */,
+ DFB0579711B7549C0015AE65 /* cinepak.h */,
DFCDC6FC11662AD700A7D2A0 /* msrle.cpp */,
DFCDC6FD11662AD700A7D2A0 /* msrle.h */,
DF90EAC010B023F400C8F93F /* codec.h */,
@@ -6414,6 +6462,9 @@
DFC830190F48AF18005EF03C /* sci */ = {
isa = PBXGroup;
children = (
+ DFB0577D11B7541F0015AE65 /* resource_audio.cpp */,
+ DFB0577E11B7541F0015AE65 /* util.cpp */,
+ DFB0577F11B7541F0015AE65 /* util.h */,
DF45B175116628A5009B85CC /* graphics */,
DF45B1A5116628A5009B85CC /* parser */,
DF45B1AB116628A5009B85CC /* sound */,
@@ -6685,6 +6736,9 @@
DFE473950D81F4E800B6D1FB /* common */ = {
isa = PBXGroup;
children = (
+ DFB0577311B753DA0015AE65 /* debug-channels.h */,
+ DFB0577411B753DA0015AE65 /* rational.cpp */,
+ DFB0577511B753DA0015AE65 /* rational.h */,
DF9B9261118E46FE0069C19D /* error.cpp */,
DFEC5D0A1166C5CF00C90552 /* random.cpp */,
DFEC5D0B1166C5CF00C90552 /* random.h */,
@@ -6767,6 +6821,8 @@
DFE477520D81F4E900B6D1FB /* graphics */ = {
isa = PBXGroup;
children = (
+ DFB0578F11B7547D0015AE65 /* pict.cpp */,
+ DFB0579011B7547D0015AE65 /* pict.h */,
DF6BF4C010529DA50069811F /* conversion.cpp */,
DF6BF4C110529DA50069811F /* conversion.h */,
DF6BF4C210529DA50069811F /* jpeg.cpp */,
@@ -7834,7 +7890,6 @@
DFAAAFFC0F0112DF003E9390 /* detection.cpp in Sources */,
DFAAB0020F011392003E9390 /* thumbnail_intern.cpp in Sources */,
DF2FFB930F485D890006E566 /* dither.cpp in Sources */,
- DF2FFBB70F485D950006E566 /* video_player.cpp in Sources */,
DF2FFBD30F485DFB0006E566 /* debug.cpp in Sources */,
DF2FFBD90F485E360006E566 /* GuiManager.cpp in Sources */,
DF2FFBFC0F4860A60006E566 /* posix-saves.cpp in Sources */,
@@ -8055,6 +8110,15 @@
DF9B9249118E46730069C19D /* error.cpp in Sources */,
DF9B9254118E46A00069C19D /* fontsjis.cpp in Sources */,
DF9B9263118E46FE0069C19D /* error.cpp in Sources */,
+ DFB0576B11B753AF0015AE65 /* mpeg_player.cpp in Sources */,
+ DFB0576C11B753AF0015AE65 /* qt_decoder.cpp in Sources */,
+ DFB0576D11B753AF0015AE65 /* video_decoder.cpp in Sources */,
+ DFB0577711B753DA0015AE65 /* rational.cpp in Sources */,
+ DFB0578211B7541F0015AE65 /* resource_audio.cpp in Sources */,
+ DFB0578311B7541F0015AE65 /* util.cpp in Sources */,
+ DFB0578B11B754570015AE65 /* maciconbar.cpp in Sources */,
+ DFB0579211B7547D0015AE65 /* pict.cpp in Sources */,
+ DFB0579911B7549C0015AE65 /* cinepak.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -8781,7 +8845,6 @@
DF09418A0F63CB26002D821E /* detection.cpp in Sources */,
DF09418B0F63CB26002D821E /* thumbnail_intern.cpp in Sources */,
DF09418C0F63CB26002D821E /* dither.cpp in Sources */,
- DF0941910F63CB26002D821E /* video_player.cpp in Sources */,
DF0941920F63CB26002D821E /* debug.cpp in Sources */,
DF0941930F63CB26002D821E /* GuiManager.cpp in Sources */,
DF0941940F63CB26002D821E /* posix-saves.cpp in Sources */,
@@ -9006,6 +9069,15 @@
DF9B924A118E46730069C19D /* error.cpp in Sources */,
DF9B9256118E46A00069C19D /* fontsjis.cpp in Sources */,
DF9B9264118E46FE0069C19D /* error.cpp in Sources */,
+ DFB0576E11B753AF0015AE65 /* mpeg_player.cpp in Sources */,
+ DFB0576F11B753AF0015AE65 /* qt_decoder.cpp in Sources */,
+ DFB0577011B753AF0015AE65 /* video_decoder.cpp in Sources */,
+ DFB0577811B753DA0015AE65 /* rational.cpp in Sources */,
+ DFB0578411B7541F0015AE65 /* resource_audio.cpp in Sources */,
+ DFB0578511B7541F0015AE65 /* util.cpp in Sources */,
+ DFB0578C11B754570015AE65 /* maciconbar.cpp in Sources */,
+ DFB0579311B7547D0015AE65 /* pict.cpp in Sources */,
+ DFB0579A11B7549C0015AE65 /* cinepak.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -9737,7 +9809,6 @@
DFF95C1F0FB22D5700A3EC78 /* detection.cpp in Sources */,
DFF95C200FB22D5700A3EC78 /* thumbnail_intern.cpp in Sources */,
DFF95C210FB22D5700A3EC78 /* dither.cpp in Sources */,
- DFF95C260FB22D5700A3EC78 /* video_player.cpp in Sources */,
DFF95C270FB22D5700A3EC78 /* debug.cpp in Sources */,
DFF95C280FB22D5700A3EC78 /* GuiManager.cpp in Sources */,
DFF95C290FB22D5700A3EC78 /* posix-saves.cpp in Sources */,
@@ -9958,6 +10029,15 @@
DF9B9248118E46730069C19D /* error.cpp in Sources */,
DF9B9252118E46A00069C19D /* fontsjis.cpp in Sources */,
DF9B9262118E46FE0069C19D /* error.cpp in Sources */,
+ DFB0576811B753AF0015AE65 /* mpeg_player.cpp in Sources */,
+ DFB0576911B753AF0015AE65 /* qt_decoder.cpp in Sources */,
+ DFB0576A11B753AF0015AE65 /* video_decoder.cpp in Sources */,
+ DFB0577611B753DA0015AE65 /* rational.cpp in Sources */,
+ DFB0578011B7541F0015AE65 /* resource_audio.cpp in Sources */,
+ DFB0578111B7541F0015AE65 /* util.cpp in Sources */,
+ DFB0578A11B754570015AE65 /* maciconbar.cpp in Sources */,
+ DFB0579111B7547D0015AE65 /* pict.cpp in Sources */,
+ DFB0579811B7549C0015AE65 /* cinepak.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -10061,6 +10141,8 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
FRAMEWORK_SEARCH_PATHS = "";
GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_ENABLE_CPP_EXCEPTIONS = NO;
+ GCC_ENABLE_CPP_RTTI = NO;
GCC_INPUT_FILETYPE = automatic;
GCC_PREPROCESSOR_DEFINITIONS = (
XCODE,
@@ -10128,6 +10210,8 @@
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
FRAMEWORK_SEARCH_PATHS = "";
+ GCC_ENABLE_CPP_EXCEPTIONS = NO;
+ GCC_ENABLE_CPP_RTTI = NO;
GCC_INPUT_FILETYPE = automatic;
GCC_PREPROCESSOR_DEFINITIONS = (
XCODE,
diff --git a/dists/os2/readme.os2 b/dists/os2/readme.os2
new file mode 100644
index 0000000000..a753323f4a
--- /dev/null
+++ b/dists/os2/readme.os2
@@ -0,0 +1,13 @@
+This is an OS/2 & eComStation build of ScummVM 1.1.1
+
+To run, it requires:
+* The libc063 runtime dll, available from ftp://ftp.netlabs.org/pub/gcc/libc-0.6.3-csd3.zip
+* The gcc444 runtime dll, available from http://download.smedley.info/gcc444.zip
+* The SDL dll's available from ftp://ftp.netlabs.org/pub/sdl/sdl-1.2.10-bin-20080804.zip
+
+All feedback is appreciated!
+
+Thanks!
+
+Paul Smedley
+5th May, 2010 \ No newline at end of file
diff --git a/dists/os2/scummvm.ico b/dists/os2/scummvm.ico
new file mode 100644
index 0000000000..c22ca23608
--- /dev/null
+++ b/dists/os2/scummvm.ico
Binary files differ
diff --git a/engines/dialogs.cpp b/engines/dialogs.cpp
index 73ba591b4b..954bc81470 100644
--- a/engines/dialogs.cpp
+++ b/engines/dialogs.cpp
@@ -35,8 +35,9 @@
#include "gui/GuiManager.h"
#include "gui/launcher.h"
#include "gui/ListWidget.h"
-#include "gui/ThemeEval.h"
+#include "gui/options.h"
#include "gui/saveload.h"
+#include "gui/ThemeEval.h"
#include "engines/dialogs.h"
#include "engines/engine.h"
@@ -49,16 +50,17 @@
using GUI::CommandSender;
using GUI::StaticTextWidget;
-enum {
- kSaveCmd = 'SAVE',
- kLoadCmd = 'LOAD',
- kPlayCmd = 'PLAY',
- kOptionsCmd = 'OPTN',
- kHelpCmd = 'HELP',
- kAboutCmd = 'ABOU',
- kQuitCmd = 'QUIT',
- kRTLCmd = 'RTL ',
- kChooseCmd = 'CHOS'
+class ConfigDialog : public GUI::OptionsDialog {
+protected:
+#ifdef SMALL_SCREEN_DEVICE
+ GUI::Dialog *_keysDialog;
+#endif
+
+public:
+ ConfigDialog(bool subtitleControls);
+ ~ConfigDialog();
+
+ virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data);
};
MainMenuDialog::MainMenuDialog(Engine *engine)
@@ -95,6 +97,12 @@ MainMenuDialog::MainMenuDialog(Engine *engine)
new GUI::ButtonWidget(this, "GlobalMenu.Options", "Options", kOptionsCmd, 'O');
+ // The help button is disabled by default.
+ // To enable "Help", an engine needs to use a subclass of MainMenuDialog
+ // (at least for now, we might change how this works in the future).
+ _helpButton = new GUI::ButtonWidget(this, "GlobalMenu.Help", "Help", kHelpCmd, 'H');
+ _helpButton->setEnabled(false);
+
new GUI::ButtonWidget(this, "GlobalMenu.About", "About", kAboutCmd, 'A');
_rtlButton = new GUI::ButtonWidget(this, "GlobalMenu.RTL", "Return to Launcher", kRTLCmd, 'R');
@@ -135,6 +143,9 @@ void MainMenuDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
case kAboutCmd:
_aboutDialog->runModal();
break;
+ case kHelpCmd:
+ // Not handled here -- needs to be handled by a subclass (for now)
+ break;
case kRTLCmd: {
Common::Event eventRTL;
eventRTL.type = Common::EVENT_RTL;
@@ -263,13 +274,13 @@ enum {
// "" as value for the domain, and in fact provide a somewhat better user
// experience at the same time.
ConfigDialog::ConfigDialog(bool subtitleControls)
- : GUI::OptionsDialog("", "ScummConfig") {
+ : GUI::OptionsDialog("", "GlobalConfig") {
//
// Sound controllers
//
- addVolumeControls(this, "ScummConfig.");
+ addVolumeControls(this, "GlobalConfig.");
setVolumeSettingsState(true); // could disable controls by GUI options
//
@@ -278,7 +289,7 @@ ConfigDialog::ConfigDialog(bool subtitleControls)
if (subtitleControls) {
// Global talkspeed range of 0-255
- addSubtitleControls(this, "ScummConfig.", 255);
+ addSubtitleControls(this, "GlobalConfig.", 255);
setSubtitleSettingsState(true); // could disable controls by GUI options
}
@@ -286,11 +297,11 @@ ConfigDialog::ConfigDialog(bool subtitleControls)
// Add the buttons
//
- new GUI::ButtonWidget(this, "ScummConfig.Ok", "OK", GUI::kOKCmd, 'O');
- new GUI::ButtonWidget(this, "ScummConfig.Cancel", "Cancel", GUI::kCloseCmd, 'C');
+ new GUI::ButtonWidget(this, "GlobalConfig.Ok", "OK", GUI::kOKCmd, 'O');
+ new GUI::ButtonWidget(this, "GlobalConfig.Cancel", "Cancel", GUI::kCloseCmd, 'C');
#ifdef SMALL_SCREEN_DEVICE
- new GUI::ButtonWidget(this, "ScummConfig.Keys", "Keys", kKeysCmd, 'K');
+ new GUI::ButtonWidget(this, "GlobalConfig.Keys", "Keys", kKeysCmd, 'K');
_keysDialog = NULL;
#endif
}
diff --git a/engines/dialogs.h b/engines/dialogs.h
index 6bee7c5fb1..6e5338b317 100644
--- a/engines/dialogs.h
+++ b/engines/dialogs.h
@@ -27,7 +27,6 @@
#include "common/str.h"
#include "gui/dialog.h"
-#include "gui/options.h"
class Engine;
@@ -39,6 +38,19 @@ namespace GUI {
class MainMenuDialog : public GUI::Dialog {
public:
+ enum {
+ kSaveCmd = 'SAVE',
+ kLoadCmd = 'LOAD',
+ kPlayCmd = 'PLAY',
+ kOptionsCmd = 'OPTN',
+ kHelpCmd = 'HELP',
+ kAboutCmd = 'ABOU',
+ kQuitCmd = 'QUIT',
+ kRTLCmd = 'RTL ',
+ kChooseCmd = 'CHOS'
+ };
+
+public:
MainMenuDialog(Engine *engine);
~MainMenuDialog();
@@ -51,29 +63,20 @@ protected:
void load();
protected:
- Engine *_engine;
+ Engine *_engine;
- GUI::GraphicsWidget *_logo;
- GUI::ButtonWidget *_rtlButton;
- GUI::ButtonWidget *_loadButton;
- GUI::ButtonWidget *_saveButton;
- GUI::Dialog *_aboutDialog;
- GUI::Dialog *_optionsDialog;
- GUI::SaveLoadChooser *_loadDialog;
- GUI::SaveLoadChooser *_saveDialog;
-};
+ GUI::GraphicsWidget *_logo;
-class ConfigDialog : public GUI::OptionsDialog {
-protected:
-#ifdef SMALL_SCREEN_DEVICE
- GUI::Dialog *_keysDialog;
-#endif
+ GUI::ButtonWidget *_rtlButton;
+ GUI::ButtonWidget *_loadButton;
+ GUI::ButtonWidget *_saveButton;
+ GUI::ButtonWidget *_helpButton;
-public:
- ConfigDialog(bool subtitleControls);
- ~ConfigDialog();
+ GUI::Dialog *_aboutDialog;
+ GUI::Dialog *_optionsDialog;
- virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data);
+ GUI::SaveLoadChooser *_loadDialog;
+ GUI::SaveLoadChooser *_saveDialog;
};
#endif
diff --git a/engines/groovie/cell.h b/engines/groovie/cell.h
index 39ee529beb..a5feab4017 100644
--- a/engines/groovie/cell.h
+++ b/engines/groovie/cell.h
@@ -26,11 +26,7 @@
#ifndef GROOVIE_CELL_H
#define GROOVIE_CELL_H
-#include "common/file.h"
-#include "common/util.h"
-
-#include "groovie/cell.h"
-#include "groovie/groovie.h"
+#include "common/textconsole.h"
#define BOARDSIZE 7
#define CELL_CLEAR 0
diff --git a/engines/groovie/cursor.h b/engines/groovie/cursor.h
index 83aebb37d3..7a1f3ccc0e 100644
--- a/engines/groovie/cursor.h
+++ b/engines/groovie/cursor.h
@@ -26,9 +26,8 @@
#ifndef GROOVIE_CURSOR_H
#define GROOVIE_CURSOR_H
-#include "common/system.h"
#include "common/array.h"
-#include "common/file.h"
+#include "common/system.h"
namespace Common {
class MacResManager;
diff --git a/engines/groovie/debug.cpp b/engines/groovie/debug.cpp
index 41ebb2fbcd..7055965917 100644
--- a/engines/groovie/debug.cpp
+++ b/engines/groovie/debug.cpp
@@ -24,15 +24,17 @@
*/
#include "groovie/debug.h"
+#include "groovie/graphics.h"
#include "groovie/groovie.h"
#include "groovie/script.h"
#include "common/debug-channels.h"
+#include "common/system.h"
namespace Groovie {
Debugger::Debugger(GroovieEngine *vm) :
- _vm (vm), _script(_vm->_script), _syst(_vm->_system) {
+ _vm(vm), _script(_vm->_script) {
// Register the debugger comands
DCmd_Register("step", WRAP_METHOD(Debugger, cmd_step));
@@ -136,7 +138,7 @@ bool Debugger::cmd_playref(int argc, const char **argv) {
bool Debugger::cmd_dumppal(int argc, const char **argv) {
uint16 i;
byte palettedump[256 * 4];
- _syst->grabPalette(palettedump, 0, 256);
+ _vm->_system->grabPalette(palettedump, 0, 256);
for (i = 0; i < 256; i++) {
DebugPrintf("%3d: %3d,%3d,%3d,%3d\n", i, palettedump[(i * 4)], palettedump[(i * 4) + 1], palettedump[(i * 4) + 2], palettedump[(i * 4) + 3]);
diff --git a/engines/groovie/debug.h b/engines/groovie/debug.h
index dadba9482c..e21746a426 100644
--- a/engines/groovie/debug.h
+++ b/engines/groovie/debug.h
@@ -27,12 +27,11 @@
#define GROOVIE_DEBUG_H
#include "gui/debugger.h"
-#include "engines/engine.h"
namespace Groovie {
-class Script;
class GroovieEngine;
+class Script;
class Debugger : public GUI::Debugger {
public:
@@ -42,7 +41,6 @@ public:
private:
GroovieEngine *_vm;
Script *_script;
- OSystem *_syst;
int getNumber(const char *arg);
diff --git a/engines/groovie/detection.cpp b/engines/groovie/detection.cpp
index 5b0fa4b3b1..ec401e7d24 100644
--- a/engines/groovie/detection.cpp
+++ b/engines/groovie/detection.cpp
@@ -23,12 +23,12 @@
*
*/
-#include "common/savefile.h"
-
#include "groovie/groovie.h"
#include "groovie/detection.h"
#include "groovie/saveload.h"
+#include "common/system.h"
+
namespace Groovie {
static const PlainGameDescriptor groovieGames[] = {
diff --git a/engines/groovie/font.cpp b/engines/groovie/font.cpp
index ece8447735..6aa6c89d31 100644
--- a/engines/groovie/font.cpp
+++ b/engines/groovie/font.cpp
@@ -23,106 +23,105 @@
*
*/
-#include "common/file.h"
-#include "graphics/surface.h"
-
#include "groovie/font.h"
namespace Groovie {
-Font::Font(OSystem *syst) :
- _syst(syst), _sphinxfnt(NULL) {
-
- Common::File fontfile;
- if (!fontfile.open("sphinx.fnt")) {
- error("Groovie::Font: Couldn't open sphinx.fnt");
- }
- uint16 fontfilesize = fontfile.size();
- _sphinxfnt = fontfile.readStream(fontfilesize);
- fontfile.close();
+T7GFont::T7GFont() : _maxHeight(0), _maxWidth(0), _glyphs(0) {
}
-Font::~Font() {
- delete _sphinxfnt;
+T7GFont::~T7GFont() {
+ delete[] _glyphs;
}
-void Font::printstring(const char *messagein) {
- uint16 totalwidth = 0, currxoffset, i;
+bool T7GFont::load(Common::SeekableReadStream &stream) {
+ // Read the mapping of characters to glyphs
+ if (stream.read(_mapChar2Glyph, 128) < 128) {
+ error("Groovie::T7GFont: Couldn't read the character to glyph map");
+ return false;
+ }
- char message[15];
- memset(message, 0, 15);
+ // Calculate the number of glyphs
+ byte numGlyphs = 0;
+ for (int i = 0; i < 128; i++)
+ if (_mapChar2Glyph[i] >= numGlyphs)
+ numGlyphs = _mapChar2Glyph[i] + 1;
- // Clear the top bar
- Common::Rect topbar(640, 80);
- Graphics::Surface *gamescreen;
- gamescreen = _syst->lockScreen();
- gamescreen->fillRect(topbar, 0);
- _syst->unlockScreen();
+ // Read the glyph offsets
+ uint16 *glyphOffsets = new uint16[numGlyphs];
+ for (int i = 0; i < numGlyphs; i++)
+ glyphOffsets[i] = stream.readUint16LE();
- for (i = 0; i < 14; i++) {
- char chartocopy = messagein[i];
- if (chartocopy <= 0x00 || chartocopy == 0x24) {
- break;
- }
- message[i] = chartocopy;
+ if (stream.eos()) {
+ error("Groovie::T7GFont: Couldn't read the glyph offsets");
+ return false;
}
- Common::rtrim(message);
- for (i = 0; i < strlen(message); i++) {
- totalwidth += letterwidth(message[i]);
- }
- currxoffset = (640 - totalwidth) / 2;
- char *currpos = message;
- while (*(currpos) != 0) {
- currxoffset += printletter(*(currpos++), currxoffset);
- }
-}
-uint16 Font::letteroffset(char letter) {
- uint16 offset;
- offset = letter;
- _sphinxfnt->seek(offset);
- offset = _sphinxfnt->readByte() * 2 + 128;
- _sphinxfnt->seek(offset);
- offset = _sphinxfnt->readUint16LE();
- return offset;
-}
+ // Allocate the glyph data
+ delete[] _glyphs;
+ _glyphs = new Glyph[numGlyphs];
+
+ // Read the glyphs
+ _maxHeight = _maxWidth = 0;
+ for (int i = 0; (i < numGlyphs) && !stream.eos(); i++) {
+ // Verify we're at the expected stream position
+ if (stream.pos() != glyphOffsets[i]) {
+ error("Groovie::T7GFont: Glyph %d starts at %d but the current "
+ "offset is %d", i, glyphOffsets[i], stream.pos());
+ return false;
+ }
-uint8 Font::letterwidth(char letter) {
- uint16 offset = letteroffset(letter);
- _sphinxfnt->seek(offset);
- return _sphinxfnt->readByte();
-}
+ // Read the glyph information
+ Glyph *g = &_glyphs[i];
+ g->width = stream.readByte();
+ g->julia = stream.readByte();
+
+ // Read the pixels data into a dynamic array (we don't know its length)
+ Common::Array<byte> data;
+ data.reserve(300);
+ byte b = stream.readByte();
+ while (!stream.eos() && (b != 0xFF)) {
+ data.push_back(b);
+ b = stream.readByte();
+ }
+
+ // Verify the pixel data size
+ assert (data.size() % g->width == 0);
+ g->height = data.size() / g->width;
-uint8 Font::letterheight(char letter) {
- uint16 offset, width, julia, data, counter = 0;
- offset = letteroffset(letter);
- _sphinxfnt->seek(offset);
- width = _sphinxfnt->readByte();
- julia = _sphinxfnt->readByte();
- data = _sphinxfnt->readByte();
- while (data != 0xFF) {
- data = _sphinxfnt->readByte();
- counter++;
+ // Copy the pixel data into the definitive static array
+ g->pixels = new byte[data.size()];
+ memcpy(g->pixels, data.begin(), data.size());
+
+ // Update the max values
+ if (g->width > _maxWidth)
+ _maxWidth = g->width;
+ if (g->height > _maxHeight)
+ _maxHeight = g->height;
}
- if (counter % width != 0) assert("font file corrupt");
- return counter / width;
+
+ delete[] glyphOffsets;
+ return true;
}
+void T7GFont::drawChar(Graphics::Surface *dst, byte chr, int x, int y, uint32 color) const {
+ // We ignore the color, as the font is already colored
+ const Glyph *glyph = getGlyph(chr);
+ const byte *src = glyph->pixels;
+ byte *target = (byte *)dst->getBasePtr(x, y);
-uint8 Font::printletter(char letter, uint16 xoffset) {
- uint16 offset, width, height, julia;
- offset = letteroffset(letter);
- height = letterheight(letter);
- _sphinxfnt->seek(offset);
- width = _sphinxfnt->readByte();
- julia = _sphinxfnt->readByte();
+ for (int i = 0; i < glyph->height; i++) {
+ memcpy(target, src, glyph->width);
+ src += glyph->width;
+ target += dst->pitch;
+ }
+}
- byte *data = new byte[width * height];
- _sphinxfnt->read(data, width * height);
- _syst->copyRectToScreen(data, width, xoffset, 16, width, height);
- delete[] data;
+const T7GFont::Glyph *T7GFont::getGlyph(byte chr) const {
+ assert (chr < 128);
- return width;
+ byte numGlyph = _mapChar2Glyph[chr];
+ return &_glyphs[numGlyph];
}
} // End of Groovie namespace
diff --git a/engines/groovie/font.h b/engines/groovie/font.h
index 1a4a967fa6..71f8393d28 100644
--- a/engines/groovie/font.h
+++ b/engines/groovie/font.h
@@ -27,24 +27,38 @@
#define GROOVIE_FONT_H
#include "common/stream.h"
-#include "common/system.h"
+#include "graphics/font.h"
namespace Groovie {
-class Font {
+class T7GFont : public Graphics::Font {
public:
- Font(OSystem *syst);
- ~Font();
- void printstring(const char *messagein);
+ T7GFont();
+ ~T7GFont();
+
+ bool load(Common::SeekableReadStream &stream);
+
+ int getFontHeight() const { return _maxHeight; }
+ int getMaxCharWidth() const { return _maxWidth; }
+ int getCharWidth(byte chr) const { return getGlyph(chr)->width; }
+ void drawChar(Graphics::Surface *dst, byte chr, int x, int y, uint32 color) const;
private:
- OSystem *_syst;
- Common::MemoryReadStream *_sphinxfnt;
+ int _maxHeight, _maxWidth;
+
+ struct Glyph {
+ Glyph() : pixels(0) {}
+ ~Glyph() { delete[] pixels; }
+
+ byte width;
+ byte height;
+ byte julia;
+ byte *pixels;
+ };
- uint16 letteroffset(char letter);
- uint8 letterwidth(char letter);
- uint8 letterheight(char letter);
- uint8 printletter(char letter, uint16 xoffset);
+ byte _mapChar2Glyph[128];
+ Glyph *_glyphs;
+ const Glyph *getGlyph(byte chr) const;
};
} // End of Groovie namespace
diff --git a/engines/groovie/graphics.cpp b/engines/groovie/graphics.cpp
index 1e54f0e79b..8546a13d40 100644
--- a/engines/groovie/graphics.cpp
+++ b/engines/groovie/graphics.cpp
@@ -23,8 +23,9 @@
*
*/
-#include "groovie/groovie.h"
#include "groovie/graphics.h"
+#include "groovie/groovie.h"
+#include "common/system.h"
namespace Groovie {
diff --git a/engines/groovie/graphics.h b/engines/groovie/graphics.h
index ea3261c85f..c9bade9538 100644
--- a/engines/groovie/graphics.h
+++ b/engines/groovie/graphics.h
@@ -26,6 +26,8 @@
#ifndef GROOVIE_GRAPHICS_H
#define GROOVIE_GRAPHICS_H
+#include "graphics/surface.h"
+
namespace Groovie {
class GroovieEngine;
diff --git a/engines/groovie/groovie.cpp b/engines/groovie/groovie.cpp
index ba18b37690..e0760e1d41 100644
--- a/engines/groovie/groovie.cpp
+++ b/engines/groovie/groovie.cpp
@@ -23,27 +23,30 @@
*
*/
+#include "groovie/groovie.h"
+#include "groovie/cursor.h"
+#include "groovie/detection.h"
+#include "groovie/graphics.h"
+#include "groovie/music.h"
+#include "groovie/resource.h"
+#include "groovie/roq.h"
+#include "groovie/vdx.h"
+
#include "common/config-manager.h"
#include "common/debug-channels.h"
#include "common/events.h"
#include "common/macresman.h"
#include "engines/util.h"
-
+#include "graphics/fontman.h"
#include "sound/mixer.h"
-#include "groovie/groovie.h"
-#include "groovie/detection.h"
-#include "groovie/music.h"
-#include "groovie/roq.h"
-#include "groovie/vdx.h"
-
namespace Groovie {
GroovieEngine::GroovieEngine(OSystem *syst, const GroovieGameDescription *gd) :
Engine(syst), _gameDescription(gd), _debugger(NULL), _script(NULL),
_resMan(NULL), _grvCursorMan(NULL), _videoPlayer(NULL), _musicPlayer(NULL),
- _graphicsMan(NULL), _macResFork(NULL), _waitingForInput(false) {
+ _graphicsMan(NULL), _macResFork(NULL), _waitingForInput(false), _font(NULL) {
// Adding the default directories
const Common::FSNode gameDataDir(ConfMan.get("path"));
@@ -104,12 +107,26 @@ Common::Error GroovieEngine::run() {
_graphicsMan = new GraphicsMan(this);
// Create the resource and cursor managers and the video player
+ // Prepare the font too
switch (_gameDescription->version) {
case kGroovieT7G:
if (_gameDescription->desc.platform == Common::kPlatformMacintosh) {
_macResFork = new Common::MacResManager();
if (!_macResFork->open(_gameDescription->desc.filesDescriptions[0].fileName))
error("Could not open %s as a resource fork", _gameDescription->desc.filesDescriptions[0].fileName);
+ // The Macintosh release used system fonts. We use GUI fonts.
+ _font = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont);
+ } else {
+ Common::File fontfile;
+ if (!fontfile.open("sphinx.fnt")) {
+ error("Couldn't open sphinx.fnt");
+ return Common::kNoGameDataFoundError;
+ } else if (!_sphinxFont.load(fontfile)) {
+ error("Error loading sphinx.fnt");
+ return Common::kUnknownError;
+ }
+ fontfile.close();
+ _font = &_sphinxFont;
}
_resMan = new ResMan_t7g(_macResFork);
diff --git a/engines/groovie/groovie.h b/engines/groovie/groovie.h
index dae2df0595..437debfd17 100644
--- a/engines/groovie/groovie.h
+++ b/engines/groovie/groovie.h
@@ -26,15 +26,11 @@
#ifndef GROOVIE_H
#define GROOVIE_H
-#include "engines/engine.h"
-#include "graphics/surface.h"
-
-#include "groovie/cursor.h"
#include "groovie/debug.h"
-#include "groovie/graphics.h"
-#include "groovie/player.h"
-#include "groovie/resource.h"
-#include "groovie/script.h"
+#include "groovie/font.h"
+
+#include "engines/engine.h"
+#include "graphics/pixelformat.h"
namespace Common {
class MacResManager;
@@ -57,7 +53,12 @@ namespace Common {
*/
namespace Groovie {
+class GraphicsMan;
+class GrvCursorMan;
class MusicPlayer;
+class ResMan;
+class Script;
+class VideoPlayer;
enum DebugLevels {
kGroovieDebugAll = 1 << 0,
@@ -106,6 +107,7 @@ public:
VideoPlayer *_videoPlayer;
MusicPlayer *_musicPlayer;
GraphicsMan *_graphicsMan;
+ const Graphics::Font *_font;
Common::MacResManager *_macResFork;
@@ -113,6 +115,7 @@ private:
const GroovieGameDescription *_gameDescription;
Debugger *_debugger;
bool _waitingForInput;
+ T7GFont _sphinxFont;
};
} // End of namespace Groovie
diff --git a/engines/groovie/music.cpp b/engines/groovie/music.cpp
index f6670da716..2ea7454256 100644
--- a/engines/groovie/music.cpp
+++ b/engines/groovie/music.cpp
@@ -23,13 +23,14 @@
*
*/
-#include "groovie/lzss.h"
#include "groovie/music.h"
+#include "groovie/groovie.h"
#include "groovie/resource.h"
#include "common/config-manager.h"
#include "common/macresman.h"
#include "sound/audiocd.h"
+#include "sound/midiparser.h"
namespace Groovie {
diff --git a/engines/groovie/music.h b/engines/groovie/music.h
index 6302c81dcc..2feef9cbf7 100644
--- a/engines/groovie/music.h
+++ b/engines/groovie/music.h
@@ -26,14 +26,16 @@
#ifndef GROOVIE_MUSIC_H
#define GROOVIE_MUSIC_H
-#include "groovie/groovie.h"
-
-#include "sound/mididrv.h"
-#include "sound/midiparser.h"
+#include "common/array.h"
#include "common/mutex.h"
+#include "sound/mididrv.h"
+
+class MidiParser;
namespace Groovie {
+class GroovieEngine;
+
class MusicPlayer {
public:
MusicPlayer(GroovieEngine *vm);
diff --git a/engines/groovie/player.cpp b/engines/groovie/player.cpp
index 5bac190701..8badd90012 100644
--- a/engines/groovie/player.cpp
+++ b/engines/groovie/player.cpp
@@ -23,8 +23,8 @@
*
*/
-#include "groovie/groovie.h"
#include "groovie/player.h"
+#include "groovie/groovie.h"
namespace Groovie {
diff --git a/engines/groovie/resource.cpp b/engines/groovie/resource.cpp
index 42de1a804d..32cc1735ef 100644
--- a/engines/groovie/resource.cpp
+++ b/engines/groovie/resource.cpp
@@ -26,8 +26,8 @@
#include "common/archive.h"
#include "common/macresman.h"
-#include "groovie/groovie.h"
#include "groovie/resource.h"
+#include "groovie/groovie.h"
namespace Groovie {
diff --git a/engines/groovie/roq.cpp b/engines/groovie/roq.cpp
index de91bb2067..11bacef8e8 100644
--- a/engines/groovie/roq.cpp
+++ b/engines/groovie/roq.cpp
@@ -26,8 +26,9 @@
// ROQ video player based on this specification by Dr. Tim Ferguson:
// http://www.csse.monash.edu.au/~timf/videocodec/idroq.txt
-#include "groovie/groovie.h"
#include "groovie/roq.h"
+#include "groovie/graphics.h"
+#include "groovie/groovie.h"
#include "graphics/jpeg.h"
diff --git a/engines/groovie/script.cpp b/engines/groovie/script.cpp
index 15a0a473c0..297da6ccc2 100644
--- a/engines/groovie/script.cpp
+++ b/engines/groovie/script.cpp
@@ -23,18 +23,19 @@
*
*/
-#include "groovie/debug.h"
-#include "groovie/music.h"
#include "groovie/script.h"
-#include "groovie/groovie.h"
#include "groovie/cell.h"
+#include "groovie/cursor.h"
+#include "groovie/graphics.h"
+#include "groovie/groovie.h"
+#include "groovie/music.h"
+#include "groovie/player.h"
+#include "groovie/resource.h"
#include "groovie/saveload.h"
#include "common/archive.h"
#include "common/config-manager.h"
#include "common/debug-channels.h"
-#include "common/endian.h"
-#include "common/events.h"
#include "common/EventRecorder.h"
#include "common/macresman.h"
@@ -63,9 +64,8 @@ static void debugScript(int level, bool nl, const char *s, ...) {
}
Script::Script(GroovieEngine *vm, EngineVersion version) :
- _code(NULL), _savedCode(NULL), _stacktop(0),
- _debugger(NULL), _vm(vm),
- _videoFile(NULL), _videoRef(0), _font(NULL), _staufsMove(NULL) {
+ _code(NULL), _savedCode(NULL), _stacktop(0), _debugger(NULL), _vm(vm),
+ _videoFile(NULL), _videoRef(0), _staufsMove(NULL) {
// Initialize the opcode set depending on the engine version
switch (version) {
case kGroovieT7G:
@@ -112,7 +112,6 @@ Script::~Script() {
delete[] _code;
delete[] _savedCode;
- delete _font;
delete _videoFile;
}
@@ -429,6 +428,22 @@ void Script::savegame(uint slot) {
_saveNames[slot] = save;
}
+void Script::printString(Graphics::Surface *surface, const char *str) {
+ char message[15];
+ memset(message, 0, 15);
+
+ // Preprocess the string
+ for (int i = 0; i < 14; i++) {
+ if (str[i] <= 0x00 || str[i] == 0x24)
+ break;
+ message[i] = str[i];
+ }
+ Common::rtrim(message);
+
+ // Draw the string
+ _vm->_font->drawString(surface, message, 0, 16, 640, 0xE2, Graphics::kTextAlignCenter);
+}
+
// OPCODES
void Script::o_invalid() {
@@ -1249,11 +1264,16 @@ void Script::o_printstring() {
stringstorage[counter] = 0;
- // Load the font if required
- if (!_font) {
- _font = new Font(_vm->_system);
- }
- _font->printstring(stringstorage);
+ Common::Rect topbar(640, 80);
+ Graphics::Surface *gamescreen = _vm->_system->lockScreen();
+
+ // Clear the top bar
+ gamescreen->fillRect(topbar, 0);
+
+ // Draw the string
+ printString(gamescreen, stringstorage);
+
+ _vm->_system->unlockScreen();
}
void Script::o_hotspot_slot() {
@@ -1273,11 +1293,15 @@ void Script::o_hotspot_slot() {
return;
}
- // Load the font if required
- if (!_font) {
- _font = new Font(_vm->_system);
- }
- _font->printstring(_saveNames[slot].c_str());
+ Common::Rect topbar(640, 80);
+ Graphics::Surface *gamescreen = _vm->_system->lockScreen();
+
+ // Clear the top bar
+ gamescreen->fillRect(topbar, 0);
+
+ printString(gamescreen, _saveNames[slot].c_str());
+
+ _vm->_system->unlockScreen();
// Save the currently highlighted slot
_hotspotSlot = slot;
diff --git a/engines/groovie/script.h b/engines/groovie/script.h
index e4a6a288e6..43b7c06a54 100644
--- a/engines/groovie/script.h
+++ b/engines/groovie/script.h
@@ -26,12 +26,16 @@
#ifndef GROOVIE_SCRIPT_H
#define GROOVIE_SCRIPT_H
-#include "common/file.h"
#include "common/random.h"
#include "common/rect.h"
-#include "groovie/font.h"
-#include "groovie/cell.h"
+namespace Common {
+class SeekableReadStream;
+}
+
+namespace Graphics {
+class Surface;
+}
namespace Groovie {
@@ -40,8 +44,9 @@ enum EngineVersion {
kGroovieV2
};
-class GroovieEngine;
class CellGame;
+class Debugger;
+class GroovieEngine;
class Script {
friend class Debugger;
@@ -112,7 +117,6 @@ private:
uint16 _hotspotSlot;
// Video
- Font *_font;
Common::SeekableReadStream *_videoFile;
uint32 _videoRef;
uint16 _bitflags;
@@ -140,6 +144,7 @@ private:
void loadgame(uint slot);
void savegame(uint slot);
bool playvideofromref(uint32 fileref);
+ void printString(Graphics::Surface *surface, const char *str);
// Opcodes
typedef void (Script::*OpcodeFunc)();
diff --git a/engines/groovie/vdx.cpp b/engines/groovie/vdx.cpp
index fb6377349f..1d3108a2cc 100644
--- a/engines/groovie/vdx.cpp
+++ b/engines/groovie/vdx.cpp
@@ -23,9 +23,10 @@
*
*/
+#include "groovie/vdx.h"
+#include "groovie/graphics.h"
#include "groovie/groovie.h"
#include "groovie/lzss.h"
-#include "groovie/vdx.h"
#include "common/debug-channels.h"
#include "sound/mixer.h"
diff --git a/engines/groovie/vdx.h b/engines/groovie/vdx.h
index 207d2e0c18..0b29493108 100644
--- a/engines/groovie/vdx.h
+++ b/engines/groovie/vdx.h
@@ -28,6 +28,10 @@
#include "groovie/player.h"
+namespace Common {
+ class ReadStream;
+}
+
namespace Groovie {
class VDXPlayer : public VideoPlayer {
diff --git a/engines/m4/animation.cpp b/engines/m4/animation.cpp
index fe46e121f0..1142ba48d1 100644
--- a/engines/m4/animation.cpp
+++ b/engines/m4/animation.cpp
@@ -26,181 +26,463 @@
#include "m4/assets.h"
#include "m4/animation.h"
#include "m4/compression.h"
+#include "m4/mads_scene.h"
namespace M4 {
// TODO: this code needs cleanup
-Animation::Animation(MadsM4Engine *vm) {
- _vm = vm;
- _playing = false;
+MadsAnimation::MadsAnimation(MadsM4Engine *vm, MadsView *view): Animation(vm), _view(view) {
+ _font = NULL;
+ _resetFlag = false;
+ _freeFlag = false;
+ _skipLoad = false;
+ _unkIndex = -1;
+ _messageCtr= 0;
+ _field12 = 0;
+
+ _currentFrame = 0;
+ _oldFrameEntry = 0;
+ _nextFrameTimer = _madsVm->_currentTimer;
}
-void Animation::loadFullScreen(const char *filename) {
- _vm->_palette->deleteAllRanges();
- load(filename);
+MadsAnimation::~MadsAnimation() {
+ for (uint i = 0; i < _messages.size(); ++i) {
+ if (_messages[i].kernelMsgIndex >= 0)
+ _view->_kernelMessages.remove(_messages[i].kernelMsgIndex);
+ }
+
+ // Further deletion logic
+ if (_field12) {
+ _view->_spriteSlots.deleteSprites(_spriteListIndexes[_spriteListIndex]);
+ }
+
+ delete _font;
}
-void Animation::load(const char *filename) {
- MadsPack anim(filename, _vm);
+/**
+ * Initialises and loads the data of an animation
+ */
+void MadsAnimation::initialise(const Common::String &filename, uint16 flags, M4Surface *interfaceSurface, M4Surface *sceneSurface) {
+ MadsPack anim(filename.c_str(), _vm);
+ bool madsRes = filename[0] == '*';
char buffer[20];
+ int streamIndex = 1;
// Chunk 1: header
// header
- // TODO: there are some unknown fields here, plus we don't read
- // the entire chunk
+
Common::SeekableReadStream *animStream = anim.getItemStream(0);
- Common::SeekableReadStream *spriteSeriesStream;
- //printf("Chunk 0, size %i\n", animStream->size());
- _seriesCount = animStream->readUint16LE();
- _frameCount = animStream->readUint16LE();
- _frameEntryCount = animStream->readUint16LE();
- // Unknown
- for (int i = 0; i < 43; i++)
- animStream->readByte();
+ int spriteListCount = animStream->readUint16LE();
+ int miscEntriesCount = animStream->readUint16LE();
+ int frameEntryCount = animStream->readUint16LE();
+ int messagesCount = animStream->readUint16LE();
+ animStream->skip(1);
+ _flags = animStream->readByte();
+
+ animStream->skip(2);
+ _animMode = animStream->readUint16LE();
+ _roomNumber = animStream->readUint16LE();
+ animStream->skip(2);
+ _field12 = animStream->readUint16LE() != 0;
+ _spriteListIndex = animStream->readUint16LE();
+ _scrollX = animStream->readUint16LE();
+ _scrollY = animStream->readSint16LE();
+ animStream->skip(10);
+
+ animStream->read(buffer, 13);
+ _interfaceFile = Common::String(buffer, 13);
+
+ for (int i = 0; i < 10; ++i) {
+ animStream->read(buffer, 13);
+ _spriteSetNames[i] = Common::String(buffer, 13);
+ }
- _spriteSeriesNames = new Common::String[_seriesCount];
- printf("%i sprite series\n", _seriesCount);
+ animStream->skip(81);
+ animStream->read(buffer, 13);
+ _lbmFilename = Common::String(buffer, 13);
+ animStream->read(buffer, 13);
+ _spritesFilename = Common::String(buffer, 13);
+ animStream->skip(48);
+ animStream->read(buffer, 13);
+ _soundName = Common::String(buffer, 13);
+ animStream->skip(26);
+ animStream->read(buffer, 13);
+ Common::String fontResource(buffer, 13);
+
+ if (_animMode == 4)
+ flags |= 0x4000;
+ if (flags & 0x100)
+ loadInterface(interfaceSurface, sceneSurface);
+
+ // Initialise the reference list
+ for (int i = 0; i < spriteListCount; ++i)
+ _spriteListIndexes.push_back(-1);
- // TODO: for now, we only load the first sprite series
- if (_seriesCount > 1)
- printf("TODO: Anim has %i sprite series, for now, we only load the first one\n", _seriesCount);
- _seriesCount = 1; // TODO
+ delete animStream;
- for (int i = 0; i < _seriesCount; i++) {
- animStream->read(buffer, 13);
- _spriteSeriesNames[i] = Common::String(buffer);
- //printf("%03d: %s\n", i, _spriteSeriesNames[i].c_str());
-
- spriteSeriesStream = _vm->res()->get(_spriteSeriesNames[i].c_str());
- _spriteSeries = new SpriteAsset(_vm, spriteSeriesStream,
- spriteSeriesStream->size(), _spriteSeriesNames[i].c_str());
- _vm->res()->toss(_spriteSeriesNames[i].c_str());
-
- // Adjust the palette of the sprites in the sprite series
- // so that they can be displayed on screen correctly
- RGBList *palData = new RGBList(_spriteSeries->getColorCount(), _spriteSeries->getPalette(), true);
- _vm->_palette->addRange(palData);
-
- for (int k = 0; k < _spriteSeries->getCount(); k++) {
- M4Sprite *spr = _spriteSeries->getFrame(k);
- spr->translate(palData); // sprite pixel translation
+ if (messagesCount > 0) {
+ // Chunk 2
+ // Following is a list of any messages for the animation
+
+ animStream = anim.getItemStream(streamIndex++);
+
+ for (int i = 0; i < messagesCount; ++i) {
+ AnimMessage rec;
+ animStream->read(rec.msg, 70);
+ rec.pos.x = animStream->readUint16LE();
+ rec.pos.y = animStream->readUint16LE();
+ animStream->readUint16LE();
+ rec.rgb1.r = animStream->readByte();
+ rec.rgb1.g = animStream->readByte();
+ rec.rgb1.b = animStream->readByte();
+ rec.rgb2.r = animStream->readByte();
+ rec.rgb2.g = animStream->readByte();
+ rec.rgb2.b = animStream->readByte();
+ rec.kernelMsgIndex = animStream->readUint16LE();
+ animStream->skip(6);
+ rec.startFrame = animStream->readUint16LE();
+ rec.endFrame = animStream->readUint16LE();
+ animStream->readUint16LE();
+
+ _messages.push_back(rec);
}
+
+ delete animStream;
}
- //printf("End pos: %i\n", animStream->pos());
+ if (frameEntryCount > 0) {
+ // Chunk 3: animation frame info
+ animStream = anim.getItemStream(streamIndex++);
+
+ for (int i = 0; i < frameEntryCount; i++) {
+ AnimFrameEntry rec;
+ rec.frameNumber = animStream->readUint16LE();
+ rec.seqIndex = animStream->readByte();
+ rec.spriteSlot.spriteListIndex = animStream->readByte();
+ rec.spriteSlot.frameNumber = animStream->readUint16LE();
+ rec.spriteSlot.xp = animStream->readUint16LE();
+ rec.spriteSlot.yp = animStream->readUint16LE();
+ rec.spriteSlot.depth = animStream->readByte();
+ rec.spriteSlot.scale = (int8)animStream->readByte();
+
+ _frameEntries.push_back(rec);
+ }
- delete animStream;
+ delete animStream;
+ }
- // ------------------
-
- // Chunk 2: anim info
- AnimationFrame frame;
- animStream = anim.getItemStream(1);
- //printf("Chunk 1, size %i\n", animStream->size());
-
- _frameEntries = new AnimationFrame[_frameEntryCount];
-
- for (int i = 0; i < _frameEntryCount; i++) {
-
- frame.animFrameIndex = animStream->readUint16LE();
- frame.u = animStream->readByte();
- frame.seriesIndex = animStream->readByte();
- frame.seriesFrameIndex = animStream->readUint16LE();
- frame.x = animStream->readUint16LE();
- frame.y = animStream->readUint16LE();
- frame.v = animStream->readByte();
- frame.w = animStream->readByte();
-
- _frameEntries[i] = frame;
-
- /*
- printf(
- "animFrameIndex = %4d, "
- "u = %3d, "
- "seriesIndex = %3d, "
- "seriesFrameIndex = %6d, "
- "x = %3d, "
- "y = %3d, "
- "v = %3d, "
- "w = %3d\n",
-
- frame.animFrameIndex,
- frame.u,
- frame.seriesIndex,
- frame.seriesFrameIndex,
- frame.x,
- frame.y,
- frame.v,
- frame.w
- );
- */
- }
- //printf("End pos: %i\n", animStream->pos());
+ if (miscEntriesCount > 0) {
+ // Chunk 4: Misc Data
+ animStream = anim.getItemStream(streamIndex);
- delete animStream;
+ for (int i = 0; i < miscEntriesCount; ++i) {
+ AnimMiscEntry rec;
+ rec.soundNum = animStream->readByte();
+ animStream->skip(1);
+ rec.numTicks = animStream->readUint16LE();
+ rec.posAdjust.x = animStream->readUint16LE();
+ rec.posAdjust.y = animStream->readUint16LE();
+ animStream->readUint16LE();
- // Chunk 3: unknown (seems to be sound data?)
- // TODO
-}
+ _miscEntries.push_back(rec);
+ }
+
+ delete animStream;
+ }
+
+ // If the animation specifies a font, then load it for access
+ if (_flags & ANIM_CUSTOM_FONT) {
+ Common::String fontName;
+ if (madsRes)
+ fontName += "*";
+ fontName += fontResource;
-Animation::~Animation() {
- //delete[] _spriteSeriesNames;
- //delete[] _spriteSeries;
- //delete[] _frameEntries;
+ _font = _vm->_font->getFont(fontName);
+ }
+
+ // Load all the sprite sets for the animation
+ for (int i = 0; i < spriteListCount; ++i) {
+ if (_field12 && (i == _spriteListIndex))
+ // Skip over field, since it's manually loaded
+ continue;
+
+ _spriteListIndexes[i] = _view->_spriteSlots.addSprites(_spriteSetNames[i].c_str());
+ }
+
+
+ if (_field12) {
+ Common::String resName;
+ if (madsRes)
+ resName += "*";
+ resName += _spriteSetNames[_spriteListIndex];
+
+ _spriteListIndexes[_spriteListIndex] = _view->_spriteSlots.addSprites(resName.c_str());
+ }
+
+ // TODO: Unknown section about handling sprite set list combined with messages size
+
+ // TODO: The original has two separate loops for the loop below based on _animMode == 4. Is it
+ // perhaps that in that mode the sprite frames has a different format..?
+
+ // Remap the sprite list index fields from the initial value to the indexes of the loaded
+ // sprite sets for the animation
+ for (uint i = 0; i < _frameEntries.size(); ++i) {
+ int idx = _frameEntries[i].spriteSlot.spriteListIndex;
+ _frameEntries[i].spriteSlot.spriteListIndex = _spriteListIndexes[idx];
+ }
}
-void Animation::start() {
- _curFrame = 0;
- _curFrameEntry = 0;
- //for (int i = 0; i < _seriesCount; i++) {
- //_spriteSeries[i] = new SpriteSeries((char*)_spriteSeriesNames[i].c_str());
- //}
- _playing = true;
- updateAnim();
+/**
+ * Loads an animation file for display
+ */
+void MadsAnimation::load(const Common::String &filename, int abortTimers) {
+ initialise(filename, 0, NULL, NULL);
+ _messageCtr = 0;
+ _skipLoad = true;
+
+/* TODO: figure out extra stuff in this routine
+ if (_field12) {
+ _unkIndex = -1;
+ int listIndex = _spriteListIndexes[_spriteListIndex];
+ SpriteAsset &spriteSet = _view->_spriteSlots.getSprite(listIndex);
+ ..?..
+ }
+*/
+
+ // Initialise miscellaneous fields
+ _currentFrame = 0;
+ _oldFrameEntry = 0;
+ _nextFrameTimer = _madsVm->_currentTimer;
+ _abortTimers = abortTimers;
+ _abortMode = _madsVm->scene()->_abortTimersMode2;
+
+ for (int i = 0; i < 3; ++i)
+ _actionNouns[i] = _madsVm->scene()->actionNouns[i];
+
+ // Initialise kernel message list
+ for (uint i = 0; i < _messages.size(); ++i)
+ _messages[i].kernelMsgIndex = -1;
}
-bool Animation::updateAnim() {
- if (!_playing)
- return true;
+void MadsAnimation::update() {
+ if (_field12) {
+ int spriteListIndex = _spriteListIndexes[_spriteListIndex];
+ int newIndex = -1;
+
+ for (uint idx = _oldFrameEntry; idx < _frameEntries.size(); ++idx) {
+ if (_frameEntries[idx].frameNumber > _currentFrame)
+ break;
+ if (_frameEntries[idx].spriteSlot.spriteListIndex == spriteListIndex)
+ newIndex = _frameEntries[idx].spriteSlot.frameNumber;
+ }
+
+ if (newIndex >= 0)
+ load1(newIndex);
+ }
+
+ // If it's not time for the next frame, then exit
+ if (_madsVm->_currentTimer < _nextFrameTimer)
+ return;
- // Get the scene background surface
- M4Surface *bg = _vm->_scene->getBackgroundSurface();
+ // Loop checks for any prior animation sprite slots to be expired
+ for (int slotIndex = 0; slotIndex < _view->_spriteSlots.startIndex; ++slotIndex) {
+ if (_view->_spriteSlots[slotIndex].seqIndex >= 0x80) {
+ // Flag the frame as animation sprite slot
+ _view->_spriteSlots[slotIndex].spriteType = EXPIRED_SPRITE;
+ }
+ }
- while (_frameEntries[_curFrameEntry].animFrameIndex == _curFrame) {
- AnimationFrame *frame = &_frameEntries[_curFrameEntry];
- int seriesFrameIndex = (frame->seriesFrameIndex & 0x7FFF) - 1;
+ // Validate the current frame
+ if (_currentFrame >= (int)_miscEntries.size()) {
+ // Is the animation allowed to be repeated?
+ if (_resetFlag) {
+ _currentFrame = 0;
+ _oldFrameEntry = 0;
+ } else {
+ _freeFlag = true;
+ return;
+ }
+ }
- // Write the sprite onto the screen
- M4Sprite *spr = _spriteSeries->getFrame(seriesFrameIndex);
+ // Handle starting any sound for this frame
+ AnimMiscEntry &misc = _miscEntries[_currentFrame];
+ if (misc.soundNum)
+ _vm->_sound->playSound(misc.soundNum);
- // FIXME: correct x, y
- spr->copyTo(bg, frame->x, frame->y, (int)spr->getTransparentColor());
+ bool screenChanged = false;
- // HACK: wait a bit
- g_system->delayMillis(100);
+ // Handle any scrolling of the screen surface
+ if ((_scrollX != 0) || (_scrollY != 0)) {
+ _view->_bgSurface->scrollX(_scrollX);
+ _view->_bgSurface->scrollY(_scrollY);
- //printf("_curFrameEntry = %d\n", _curFrameEntry);
- _curFrameEntry++;
+ screenChanged = true;
}
- //printf("_curFrame = %d\n", _curFrame);
+ // Handle any offset adjustment for sprites as of this frame
+ if (_view->_posAdjust.x != misc.posAdjust.x) {
+ misc.posAdjust.x = _view->_posAdjust.x;
+ screenChanged = true;
+ }
+ if (_view->_posAdjust.y != misc.posAdjust.y) {
+ misc.posAdjust.y = _view->_posAdjust.y;
+ screenChanged = true;
+ }
+ if (screenChanged) {
+ // Signal the entire screen needs refreshing
+ _view->_spriteSlots.fullRefresh();
+ }
- _curFrame++;
- if (_curFrame >= _frameCount) // anim done
- stop();
+ int spriteSlotsMax = _view->_spriteSlots.startIndex;
+
+ // Main frame animation loop - frames get animated by being placed, as necessary, into the
+ // main sprite slot array
+ while ((uint)_oldFrameEntry < _frameEntries.size()) {
+ if (_frameEntries[_oldFrameEntry].frameNumber > _currentFrame)
+ break;
+ else if (_frameEntries[_oldFrameEntry].frameNumber == _currentFrame) {
+ // Found the correct frame
+ int spriteSlotIndex = 0;
+ int index = 0;
+
+ for (;;) {
+ if ((spriteSlotIndex == 0) && (index < spriteSlotsMax)) {
+ int seqIndex = _frameEntries[_oldFrameEntry].seqIndex - _view->_spriteSlots[index].seqIndex;
+ if (seqIndex == 0x80) {
+ if (_view->_spriteSlots[index] == _frameEntries[_oldFrameEntry].spriteSlot) {
+ _view->_spriteSlots[index].spriteType = SPRITE_ZERO;
+ spriteSlotIndex = -1;
+ }
+ }
+ ++index;
+ continue;
+ }
+
+ if (spriteSlotIndex == 0) {
+ int slotIndex = _view->_spriteSlots.getIndex();
+ MadsSpriteSlot &slot = _view->_spriteSlots[slotIndex];
+ slot.copy(_frameEntries[_oldFrameEntry].spriteSlot);
+ slot.seqIndex = _frameEntries[_oldFrameEntry].seqIndex + 0x80;
+
+ SpriteAsset &spriteSet = _view->_spriteSlots.getSprite(
+ _view->_spriteSlots[slotIndex].spriteListIndex);
+ slot.spriteType = spriteSet.isBackground() ? BACKGROUND_SPRITE : FOREGROUND_SPRITE;
+ }
+ break;
+ }
+ }
+
+ ++_oldFrameEntry;
+ }
+
+ // Handle the display of any messages
+ for (uint idx = 0; idx < _messages.size(); ++idx) {
+ if (_messages[idx].kernelMsgIndex >= 0) {
+ // Handle currently active message
+ if ((_currentFrame < _messages[idx].startFrame) || (_currentFrame > _messages[idx].endFrame)) {
+ _view->_kernelMessages.remove(_messages[idx].kernelMsgIndex);
+ _messages[idx].kernelMsgIndex = -1;
+ --_messageCtr;
+ }
+ } else if ((_currentFrame >= _messages[idx].startFrame) && (_currentFrame <= _messages[idx].endFrame)) {
+ // Start displaying the message
+ AnimMessage &me = _messages[idx];
+
+ // The colour index to use is dependant on how many messages are currently on-screen
+ uint8 colIndex;
+ switch (_messageCtr) {
+ case 1:
+ colIndex = 252;
+ break;
+ case 2:
+ colIndex = 16;
+ break;
+ default:
+ colIndex = 250;
+ break;
+ }
+
+ _vm->_palette->setEntry(colIndex, me.rgb1.r, me.rgb1.g, me.rgb1.b);
+ _vm->_palette->setEntry(colIndex + 1, me.rgb2.r, me.rgb2.g, me.rgb2.b);
+
+ // Add a kernel message to display the given text
+ me.kernelMsgIndex = _view->_kernelMessages.add(me.pos, colIndex * 101, 0, 0, INDEFINITE_TIMEOUT, me.msg);
+ ++_messageCtr;
+ }
+ }
+
+ // Move to the next frame
+ _currentFrame++;
+ if (_currentFrame >= (int)_miscEntries.size()) {
+ // Animation is complete
+ if (_abortTimers != 0) {
+ _view->_abortTimers = _abortTimers;
+ _view->_abortTimersMode = _abortMode;
+
+ if (_abortMode != ABORTMODE_1) {
+ // Copy the noun list
+ for (int i = 0; i < 3; ++i)
+ _madsVm->scene()->actionNouns[i] = _actionNouns[i];
+ }
+ }
+ }
- return _curFrame >= _frameCount;
+ int frameNum = MIN(_currentFrame, (int)_miscEntries.size() - 1);
+ _nextFrameTimer = _madsVm->_currentTimer + _miscEntries[frameNum].numTicks;
}
-void Animation::stop() {
- _playing = false;
+void MadsAnimation::setCurrentFrame(int frameNumber) {
+ _currentFrame = frameNumber;
+ _oldFrameEntry = 0;
+ _freeFlag = false;
+}
+
+void MadsAnimation::load1(int frameNumber) {
+ if (_skipLoad)
+ return;
+
+ Common::Point pt;
+ int listIndex = _spriteListIndexes[_spriteListIndex];
+ SpriteAsset &spriteSet = _view->_spriteSlots.getSprite(listIndex);
+
+ if (_unkIndex < 0) {
+ M4Surface *frame = spriteSet.getFrame(0);
+ pt.x = frame->bounds().left;
+ pt.y = frame->bounds().top;
+ } else {
+ pt.x = _unkList[_unkIndex].x;
+ pt.y = _unkList[_unkIndex].y;
+ _unkIndex = 1 - _unkIndex;
+ }
+
+ if (proc1(spriteSet, pt, frameNumber))
+ error("proc1 failure");
+}
+
+bool MadsAnimation::proc1(SpriteAsset &spriteSet, const Common::Point &pt, int frameNumber) {
+ return 0;
+}
- for (int i = 0; i < _seriesCount; i++) {
- // TODO: cleanup
- //delete _spriteSeries[i];
- //_spriteSeries[i] = NULL;
+void MadsAnimation::loadInterface(M4Surface *&interfaceSurface, M4Surface *&depthSurface) {
+ if (_animMode <= 2) {
+ MadsSceneResources sceneResources;
+ sceneResources.load(_roomNumber, _interfaceFile.c_str(), 0, depthSurface, interfaceSurface);
+
+ } else if (_animMode == 4) {
+ // Load a scene interface
+ interfaceSurface->madsLoadInterface(_interfaceFile);
+ } else {
+ // This mode allocates two large surfaces for the animation
+ // TODO: Are these ever properly freed?
+error("Anim mode %d - need to check free logic", _animMode);
+ assert(!interfaceSurface);
+ assert(!depthSurface);
+ depthSurface = new M4Surface(MADS_SURFACE_WIDTH, MADS_SCREEN_HEIGHT);
+ interfaceSurface = new M4Surface(MADS_SURFACE_WIDTH, MADS_SCREEN_HEIGHT);
+ depthSurface->clear();
+ interfaceSurface->clear();
}
}
diff --git a/engines/m4/animation.h b/engines/m4/animation.h
index c8be7f5cb3..5c7227a256 100644
--- a/engines/m4/animation.h
+++ b/engines/m4/animation.h
@@ -29,39 +29,92 @@
#include "m4/m4.h"
#include "m4/graphics.h"
#include "m4/assets.h"
+#include "m4/mads_views.h"
+#include "common/array.h"
namespace M4 {
-struct AnimationFrame {
- uint16 animFrameIndex;
- byte u;
- byte seriesIndex;
- uint16 seriesFrameIndex;
- uint16 x, y;
- byte v, w;
+class MadsView;
+class SpriteSlotSubset;
+
+class AnimMessage {
+public:
+ char msg[70];
+ Common::Point pos;
+ RGB8 rgb1, rgb2;
+ int kernelMsgIndex;
+
+ int startFrame, endFrame;
+};
+
+class AnimFrameEntry {
+public:
+ int frameNumber;
+ int seqIndex;
+ SpriteSlotSubset spriteSlot;
};
-class Animation {
- public:
- Animation(MadsM4Engine *vm);
- ~Animation();
-
- void load(const char *filename);
- void loadFullScreen(const char *filename);
- void start();
- bool updateAnim();
- void stop();
-
- private:
- bool _playing;
- MadsM4Engine *_vm;
- int _seriesCount;
- int _frameCount;
- int _frameEntryCount;
- AnimationFrame *_frameEntries;
- Common::String *_spriteSeriesNames;
- SpriteAsset *_spriteSeries;
- int _curFrame, _curFrameEntry;
+class AnimMiscEntry {
+public:
+ int soundNum;
+ int numTicks;
+ Common::Point posAdjust;
+};
+
+#define ANIM_SPRITE_SET_SIZE 50
+
+enum MadsAnimationFlags {ANIM_CUSTOM_FONT = 0x20, ANIM_HAS_SOUND = 0x8000};
+
+class MadsAnimation: public Animation {
+private:
+ MadsView *_view;
+
+ int _spriteListCount;
+ Common::Array<AnimMessage> _messages;
+ Common::Array<AnimFrameEntry> _frameEntries;
+ Common::Array<AnimMiscEntry> _miscEntries;
+ Font *_font;
+
+ uint8 _flags;
+ int _animMode;
+ int _roomNumber;
+ bool _field12;
+ int _spriteListIndex;
+ int _scrollX;
+ int _scrollY;
+ Common::String _interfaceFile;
+ Common::String _spriteSetNames[10];
+ Common::String _lbmFilename;
+ Common::String _spritesFilename;
+ Common::String _soundName;
+ Common::Array<int> _spriteListIndexes;
+
+ int _currentFrame, _oldFrameEntry;
+ bool _resetFlag;
+ bool _freeFlag;
+ bool _skipLoad;
+ int _unkIndex;
+ Common::Point _unkList[2];
+ uint32 _nextFrameTimer;
+ int _messageCtr;
+ int _abortTimers;
+ AbortTimerMode _abortMode;
+ uint16 _actionNouns[3];
+
+ void load1(int frameNumber);
+ bool proc1(SpriteAsset &spriteSet, const Common::Point &pt, int frameNumber);
+ void loadInterface(M4Surface *&interfaceSurface, M4Surface *&depthSurface);
+public:
+ MadsAnimation(MadsM4Engine *vm, MadsView *view);
+ virtual ~MadsAnimation();
+
+ virtual void initialise(const Common::String &filename, uint16 flags, M4Surface *interfaceSurface, M4Surface *sceneSurface);
+ virtual void load(const Common::String &filename, int abortTimers);
+ virtual void update();
+ virtual void setCurrentFrame(int frameNumber);
+
+ bool freeFlag() const { return _freeFlag; }
+ int roomNumber() const { return _roomNumber; }
};
} // End of namespace M4
diff --git a/engines/m4/assets.cpp b/engines/m4/assets.cpp
index 14857e6f2b..1f3cf278ae 100644
--- a/engines/m4/assets.cpp
+++ b/engines/m4/assets.cpp
@@ -30,13 +30,13 @@
namespace M4 {
-BaseAsset::BaseAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : _vm(vm) {
+BaseAsset::BaseAsset(MadsM4Engine *vm) : _vm(vm) {
}
BaseAsset::~BaseAsset() {
}
-MachineAsset::MachineAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm, stream, size, name) {
+MachineAsset::MachineAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm) {
uint32 stateCount = stream->readUint32LE();
for (uint32 curState = 0; curState < stateCount; curState++) {
uint32 stateOffset = stream->readUint32LE();
@@ -61,7 +61,7 @@ uint32 MachineAsset::getStateOffset(uint32 state) {
return _stateTable[state];
}
-SequenceAsset::SequenceAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm, stream, size, name) {
+SequenceAsset::SequenceAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm) {
_localVarCount = stream->readUint32LE();
_codeSize = size - 4;
_code = new byte[_codeSize];
@@ -78,7 +78,7 @@ void SequenceAsset::getCode(byte *&code, uint32 &codeSize) {
}
-DataAsset::DataAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm, stream, size, name) {
+DataAsset::DataAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm) {
_recCount = stream->readUint32LE();
_recSize = stream->readUint32LE();
@@ -98,7 +98,8 @@ long *DataAsset::getRow(int index) {
return &_data[_recSize * index];
}
-SpriteAsset::SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name, bool asStream) : BaseAsset(vm, stream, size, name) {
+SpriteAsset::SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name, bool asStream) :
+ BaseAsset(vm) {
_stream = stream;
_palInterface = NULL;
_paletteData = NULL;
@@ -110,6 +111,20 @@ SpriteAsset::SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, i
}
}
+SpriteAsset::SpriteAsset(MadsM4Engine *vm, const char *name): BaseAsset(vm) {
+ _stream = vm->res()->get(name);
+ _palInterface = NULL;
+ _paletteData = NULL;
+
+ if (_vm->isM4()) {
+ loadM4SpriteAsset(vm, _stream, true);
+ } else {
+ loadMadsSpriteAsset(vm, _stream);
+ }
+
+ vm->res()->toss(name);
+}
+
SpriteAsset::~SpriteAsset() {
if (_palInterface) {
// Internally stored palette translation data, so release it
@@ -195,11 +210,12 @@ void SpriteAsset::loadMadsSpriteAsset(MadsM4Engine *vm, Common::SeekableReadStre
_maxHeight = 0;
Common::SeekableReadStream *spriteStream = sprite.getItemStream(0);
-
- _assetType = spriteStream->readUint16LE();
- for (int i = 0; i < 18; i++) {
- spriteStream->readUint16LE();
- }
+ _mode = spriteStream->readByte();
+ spriteStream->skip(1);
+ int type1 = spriteStream->readUint16LE();
+ int type2 = spriteStream->readUint16LE();
+ _isBackground = (type1 != 0) && (type2 < 4);
+ spriteStream->skip(32);
_frameCount = spriteStream->readUint16LE();
// we skip the rest of the data
delete spriteStream;
diff --git a/engines/m4/assets.h b/engines/m4/assets.h
index cd0ae6ba78..e5beffbcae 100644
--- a/engines/m4/assets.h
+++ b/engines/m4/assets.h
@@ -49,7 +49,7 @@ class Palette;
class BaseAsset {
public:
- BaseAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name);
+ BaseAsset(MadsM4Engine *vm);
~BaseAsset();
const Common::String getName() const { return _name; }
protected:
@@ -103,6 +103,7 @@ struct SpriteAssetFrame {
class SpriteAsset : public BaseAsset {
public:
SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name, bool asStream = false);
+ SpriteAsset(MadsM4Engine *vm, const char *name);
~SpriteAsset();
void loadM4SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, bool asStream);
void loadMadsSpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream);
@@ -113,7 +114,7 @@ public:
int32 getFrameHeight(int index);
int32 getMaxFrameWidth() const { return _maxWidth; }
int32 getMaxFrameHeight() const { return _maxHeight; }
- uint16 getAssetType() const { return _assetType; }
+ bool isBackground() const { return _isBackground; }
M4Sprite *getFrame(int frameIndex);
void loadStreamingFrame(M4Sprite *frame, int frameIndex, int destX, int destY);
RGB8* getPalette() { return _palette; }
@@ -136,7 +137,8 @@ protected:
uint32 _frameStartOffset;
// MADS sprite set fields
- uint16 _assetType;
+ uint8 _mode;
+ bool _isBackground;
int32 parseSprite(bool isBigEndian = false);
void loadFrameHeader(SpriteAssetFrame &frameHeader, bool isBigEndian = false);
diff --git a/engines/m4/compression.h b/engines/m4/compression.h
index 74fed357ff..00e3d1f927 100644
--- a/engines/m4/compression.h
+++ b/engines/m4/compression.h
@@ -66,8 +66,8 @@ public:
class FabDecompressor {
private:
- int _bitsLeft;
- uint32 _bitBuffer;
+ int _bitsLeft;
+ uint32 _bitBuffer;
const byte *_srcData, *_srcP;
int _srcSize;
diff --git a/engines/m4/console.cpp b/engines/m4/console.cpp
index 0c2e80df0e..4e14afdfaf 100644
--- a/engines/m4/console.cpp
+++ b/engines/m4/console.cpp
@@ -47,7 +47,6 @@ Console::Console(MadsM4Engine *vm) : GUI::Debugger() {
DCmd_Register("start_conv", WRAP_METHOD(Console, cmdStartConversation));
DCmd_Register("textview", WRAP_METHOD(Console, cmdShowTextview));
DCmd_Register("animview", WRAP_METHOD(Console, cmdShowAnimview));
- DCmd_Register("anim", WRAP_METHOD(Console, cmdPlayAnimation));
}
Console::~Console() {
@@ -247,33 +246,6 @@ bool Console::cmdShowAnimview(int argc, const char **argv) {
return false;
}
-bool Console::cmdPlayAnimation(int argc, const char **argv) {
- View *view = _vm->_viewManager->getView(VIEWID_SCENE);
- if (view == NULL) {
- DebugPrintf("The scene view isn't currently active\n");
- } else if (argc != 2 && argc != 3) {
- DebugPrintf("Usage: %s <anim resource (*.aa)> <fullscreen>\n", argv[0]);
- DebugPrintf("If fullscreen is 1, the screen palette is replaced with the palette of the animation\n");
- } else {
- char resourceName[20];
- strncpy(resourceName, argv[1], 15);
- resourceName[15] = '\0';
- if (!strchr(resourceName, '.'))
- strcat(resourceName, ".AA");
-
- _vm->_viewManager->moveToFront(view);
- if (argc == 3 && atoi(argv[2]) == 1)
- _vm->_animation->loadFullScreen(resourceName);
- else
- _vm->_animation->load(resourceName);
- _vm->_animation->start();
- view->restore(0, 0, view->width(), view->height());
- return false;
- }
-
- return true;
-}
-
/*--------------------------------------------------------------------------*/
MadsConsole::MadsConsole(MadsEngine *vm): Console(vm) {
@@ -282,6 +254,7 @@ MadsConsole::MadsConsole(MadsEngine *vm): Console(vm) {
DCmd_Register("object", WRAP_METHOD(MadsConsole, cmdObject));
DCmd_Register("message", WRAP_METHOD(MadsConsole, cmdMessage));
DCmd_Register("scene_info", WRAP_METHOD(MadsConsole, cmdSceneInfo));
+ DCmd_Register("anim", WRAP_METHOD(MadsConsole, cmdPlayAnimation));
}
bool MadsConsole::cmdObject(int argc, const char **argv) {
@@ -386,6 +359,33 @@ bool MadsConsole::cmdSceneInfo(int argc, const char **argv) {
return true;
}
+bool MadsConsole::cmdPlayAnimation(int argc, const char **argv) {
+ View *view = _vm->_viewManager->getView(VIEWID_SCENE);
+ if (view == NULL) {
+ DebugPrintf("The scene view isn't currently active\n");
+ } else if (argc != 2 && argc != 3) {
+ DebugPrintf("Usage: %s <anim resource (*.aa)> <fullscreen>\n", argv[0]);
+ DebugPrintf("If fullscreen is 1, the screen palette is replaced with the palette of the animation\n");
+ } else {
+ char resourceName[20];
+ strncpy(resourceName, argv[1], 15);
+ resourceName[15] = '\0';
+ if (!strchr(resourceName, '.'))
+ strcat(resourceName, ".AA");
+
+ _vm->_viewManager->moveToFront(view);
+ if (argc == 3 && atoi(argv[2]) == 1)
+ _madsVm->_palette->deleteAllRanges();
+
+ _madsVm->scene()->_sceneAnimation->load(resourceName, 0);
+
+ view->restore(0, 0, view->width(), view->height());
+ return false;
+ }
+
+ return true;
+}
+
/*--------------------------------------------------------------------------*/
M4Console::M4Console(M4Engine *vm): Console(vm) {
diff --git a/engines/m4/console.h b/engines/m4/console.h
index b592f041cf..53a47dada9 100644
--- a/engines/m4/console.h
+++ b/engines/m4/console.h
@@ -50,7 +50,6 @@ private:
bool cmdStartConversation(int argc, const char **argv);
bool cmdShowTextview(int argc, const char **argv);
bool cmdShowAnimview(int argc, const char **argv);
- bool cmdPlayAnimation(int argc, const char **argv);
public:
Console(MadsM4Engine *vm);
@@ -64,6 +63,8 @@ private:
bool cmdObject(int argc, const char **argv);
bool cmdMessage(int argc, const char **argv);
bool cmdSceneInfo(int argc, const char **argv);
+ bool cmdPlayAnimation(int argc, const char **argv);
+
public:
MadsConsole(MadsEngine *vm);
virtual ~MadsConsole() {}
diff --git a/engines/m4/converse.cpp b/engines/m4/converse.cpp
index 746ced5d11..11bc165811 100644
--- a/engines/m4/converse.cpp
+++ b/engines/m4/converse.cpp
@@ -96,7 +96,7 @@ void ConversationView::setNode(int32 nodeIndex) {
_vm->_font->setFont(FONT_CONVERSATION);
// TODO: Conversation styles and colors
- _vm->_font->setColors(2, 1, 3);
+ _vm->_font->current()->setColours(2, 1, 3);
_currentNodeIndex = nodeIndex;
@@ -124,7 +124,7 @@ void ConversationView::setNode(int32 nodeIndex) {
}
// Figure out the longest string to determine where option highlighting ends
- int tempX = _vm->_font->getWidth(node->entries[i]->text, 0) +
+ int tempX = _vm->_font->current()->getWidth(node->entries[i]->text, 0) +
CONV_ENTRIES_X_OFFSET + 10;
_xEnd = MAX(_xEnd, tempX);
}
@@ -163,10 +163,10 @@ void ConversationView::onRefresh(RectList *rects, M4Surface *destSurface) {
if (i > CONV_MAX_SHOWN_ENTRIES - 1)
break;
- _vm->_font->setColor((_highlightedIndex == i) ? CONVERSATION_ENTRY_HIGHLIGHTED :
+ _vm->_font->current()->setColour((_highlightedIndex == i) ? CONVERSATION_ENTRY_HIGHLIGHTED :
CONVERSATION_ENTRY_NORMAL);
- _vm->_font->writeString(this, _activeItems[i]->text, CONV_ENTRIES_X_OFFSET,
+ _vm->_font->current()->writeString(this, _activeItems[i]->text, CONV_ENTRIES_X_OFFSET,
CONV_ENTRIES_Y_OFFSET + CONV_ENTRIES_HEIGHT * i, 0, 0);
}
}
diff --git a/engines/m4/dialogs.cpp b/engines/m4/dialogs.cpp
index 3af94af262..a7104537f5 100644
--- a/engines/m4/dialogs.cpp
+++ b/engines/m4/dialogs.cpp
@@ -127,7 +127,7 @@ void Dialog::writeChars(const char *srcLine) {
strcat(line, wordStr);
lineLen = strlen(line);
- lineWidth = _vm->_font->getWidth(line, DIALOG_SPACING);
+ lineWidth = _vm->_font->current()->getWidth(line, DIALOG_SPACING);
if (((_lineX + lineLen) > _widthChars) || ((_widthX + lineWidth) > _dialogWidth)) {
incLine();
@@ -146,7 +146,7 @@ void Dialog::writeChars(const char *srcLine) {
*/
void Dialog::appendText(const char *line) {
_lineX += strlen(line);
- _widthX += _vm->_font->getWidth(line, DIALOG_SPACING);
+ _widthX += _vm->_font->current()->getWidth(line, DIALOG_SPACING);
strcat(_lines[_lines.size() - 1].data, line);
}
@@ -158,7 +158,7 @@ void Dialog::addLine(const char *line, bool underlineP) {
if ((_widthX > 0) || (_lineX > 0))
incLine();
- int lineWidth = _vm->_font->getWidth(line, DIALOG_SPACING);
+ int lineWidth = _vm->_font->current()->getWidth(line, DIALOG_SPACING);
int lineLen = strlen(line);
if ((lineWidth > _dialogWidth) || (lineLen >= _widthChars))
@@ -383,7 +383,7 @@ Dialog::Dialog(MadsM4Engine *vm, const char *msgData, const char *title): View(v
if (id > 0) {
// Suffix provided - specifies the dialog width in number of chars
_widthChars = id * 2;
- _dialogWidth = id * (_vm->_font->getMaxWidth() + DIALOG_SPACING) + 10;
+ _dialogWidth = id * (_vm->_font->current()->getMaxWidth() + DIALOG_SPACING) + 10;
}
} else if (matchCommand(cmdText, "UNDER")) {
@@ -416,7 +416,7 @@ Dialog::Dialog(MadsM4Engine *vm, const char *msgData, const char *title): View(v
Dialog::Dialog(MadsM4Engine *vm, int widthChars): View(vm, Common::Rect(0, 0, 0, 0)) {
_vm->_font->setFont(FONT_INTERFACE_MADS);
_widthChars = widthChars * 2;
- _dialogWidth = widthChars * (_vm->_font->getMaxWidth() + DIALOG_SPACING) + 10;
+ _dialogWidth = widthChars * (_vm->_font->current()->getMaxWidth() + DIALOG_SPACING) + 10;
_screenType = LAYER_DIALOG;
_lineX = 0;
_widthX = 0;
@@ -439,7 +439,7 @@ void Dialog::draw() {
// Calculate bounds
int dlgWidth = _dialogWidth;
- int dlgHeight = _lines.size() * (_vm->_font->getHeight() + 1) + 10;
+ int dlgHeight = _lines.size() * (_vm->_font->current()->getHeight() + 1) + 10;
int dialogX = (_vm->_screen->width() - dlgWidth) / 2;
int dialogY = (_vm->_screen->height() - dlgHeight) / 2;
@@ -480,26 +480,26 @@ void Dialog::draw() {
}
// Handle drawing the text contents
- _vm->_font->setColours(7, 7, 7);
+ _vm->_font->current()->setColours(7, 7, 7);
setColour(7);
- for (uint lineCtr = 0, yp = 5; lineCtr < _lines.size(); ++lineCtr, yp += _vm->_font->getHeight() + 1) {
+ for (uint lineCtr = 0, yp = 5; lineCtr < _lines.size(); ++lineCtr, yp += _vm->_font->current()->getHeight() + 1) {
if (_lines[lineCtr].barLine) {
// Bar separation line
- hLine(5, width() - 6, ((_vm->_font->getHeight() + 1) >> 1) + yp);
+ hLine(5, width() - 6, ((_vm->_font->current()->getHeight() + 1) >> 1) + yp);
} else {
// Standard line
Common::Point pt(_lines[lineCtr].xp + 5, yp);
if (_lines[lineCtr].xp & 0x40)
++pt.y;
- _vm->_font->writeString(this, _lines[lineCtr].data, pt.x, pt.y, 0, DIALOG_SPACING);
+ _vm->_font->current()->writeString(this, _lines[lineCtr].data, pt.x, pt.y, 0, DIALOG_SPACING);
if (_lines[lineCtr].underline)
// Underline needed
- hLine(pt.x, pt.x + _vm->_font->getWidth(_lines[lineCtr].data, DIALOG_SPACING),
- pt.y + _vm->_font->getHeight());
+ hLine(pt.x, pt.x + _vm->_font->current()->getWidth(_lines[lineCtr].data, DIALOG_SPACING),
+ pt.y + _vm->_font->current()->getHeight());
}
}
@@ -528,7 +528,7 @@ void Dialog::display(MadsM4Engine *vm, int widthChars, const char **descEntries)
dlg->incLine();
dlg->writeChars(*descEntries);
- int lineWidth = vm->_font->getWidth(*descEntries, DIALOG_SPACING);
+ int lineWidth = vm->_font->current()->getWidth(*descEntries, DIALOG_SPACING);
dlg->_lines[dlg->_lines.size() - 1].xp = (dlg->_dialogWidth - 10 - lineWidth) / 2;
++descEntries;
}
diff --git a/engines/m4/font.cpp b/engines/m4/font.cpp
index f8dec65412..4afa158976 100644
--- a/engines/m4/font.cpp
+++ b/engines/m4/font.cpp
@@ -29,28 +29,46 @@
namespace M4 {
-Font::Font(MadsM4Engine *vm) : _vm(vm) {
+FontManager::~FontManager() {
+ for (uint i = 0; i < _entries.size(); ++i)
+ delete _entries[i];
+ _entries.clear();
+}
+
+Font *FontManager::getFont(const Common::String &filename) {
+ // Check if the font is already loaded
+ for (uint i = 0; i < _entries.size(); ++i)
+ {
+ if (_entries[i]->_filename.equals(filename))
+ return _entries[i];
+ }
+
+ Font *f = new Font(_vm, filename);
+ _entries.push_back(f);
+ return f;
+}
+
+void FontManager::setFont(const Common::String &filename) {
+ _currentFont = getFont(filename);
+}
+
+//--------------------------------------------------------------------------
+
+Font::Font(MadsM4Engine *vm, const Common::String &filename) : _vm(vm), _filename(filename) {
_sysFont = true;
- _filename = NULL;
+
//TODO: System font
_fontColors[0] = _vm->_palette->BLACK;
_fontColors[1] = _vm->_palette->WHITE;
_fontColors[2] = _vm->_palette->BLACK;
_fontColors[3] = _vm->_palette->DARK_GRAY;
-}
-
-void Font::setFont(const char *filename) {
- if ((_filename != NULL) && (strcmp(filename, _filename) == 0))
- // Already using specified font, so don't bother reloading
- return;
_sysFont = false;
- _filename = filename;
if (_vm->isM4())
- setFontM4(filename);
+ setFontM4(filename.c_str());
else
- setFontMads(filename);
+ setFontMads(filename.c_str());
}
void Font::setFontM4(const char *filename) {
@@ -134,20 +152,21 @@ Font::~Font() {
}
}
-void Font::setColor(uint8 color) {
+void Font::setColour(uint8 colour) {
if (_sysFont)
- _fontColors[1] = color;
+ _fontColors[1] = colour;
else
- _fontColors[3] = color;
+ _fontColors[3] = colour;
}
-void Font::setColors(uint8 alt1, uint8 alt2, uint8 foreground) {
+void Font::setColours(uint8 col1, uint8 col2, uint8 col3) {
if (_sysFont)
- _fontColors[1] = foreground;
+ _fontColors[1] = col3;
else {
- _fontColors[1] = alt1;
- _fontColors[2] = alt2;
- _fontColors[3] = foreground;
+ _fontColors[0] = 0xFF;
+ _fontColors[1] = col1;
+ _fontColors[2] = col2;
+ _fontColors[3] = col3;
}
}
diff --git a/engines/m4/font.h b/engines/m4/font.h
index e64f80b70d..ca47848c61 100644
--- a/engines/m4/font.h
+++ b/engines/m4/font.h
@@ -59,19 +59,11 @@ namespace M4 {
class Font {
public:
- Font(MadsM4Engine *vm);
+ Font(MadsM4Engine *vm, const Common::String &filename);
~Font();
- Font *getFont(const char *filename) {
- // TODO: Proper separation of font instances
- setFont(filename);
- return this;
- }
- void setFont(const char *filename);
- void setColor(uint8 color);
- void setColors(uint8 alt1, uint8 alt2, uint8 foreground);
- void setColour(uint8 colour) { setColor(colour); }
- void setColours(uint8 alt1, uint8 alt2, uint8 foreground) { setColors(alt1, alt2, foreground); }
+ void setColour(uint8 colour);
+ void setColours(uint8 col1, uint8 col2, uint8 col3);
int32 getWidth(const char *text, int spaceWidth = -1);
int32 getHeight() const { return _maxHeight; }
@@ -80,7 +72,8 @@ public:
int32 writeString(M4Surface *surface, const char *text, int x, int y, int width = 0, int spaceWidth = -1) {
return write(surface, text, x, y, width, spaceWidth, _fontColors);
}
-
+public:
+ const Common::String _filename;
private:
void setFontM4(const char *filename);
void setFontMads(const char *filename);
@@ -91,10 +84,39 @@ private:
uint16 *_charOffs;
uint8 *_charData;
bool _sysFont;
- const char *_filename;
uint8 _fontColors[4];
};
+class FontEntry {
+public:
+ Font *_font;
+
+ FontEntry() {
+ _font = NULL;
+ }
+ ~FontEntry() {
+ delete _font;
+ }
+};
+
+class FontManager {
+private:
+ MadsM4Engine *_vm;
+ Common::Array<Font *> _entries;
+ Font *_currentFont;
+public:
+ FontManager(MadsM4Engine *vm): _vm(vm) { _currentFont = NULL; }
+ ~FontManager();
+
+ Font *getFont(const Common::String &filename);
+ void setFont(const Common::String &filename);
+
+ Font *current() {
+ assert(_currentFont);
+ return _currentFont;
+ }
+};
+
} // End of namespace M4
#endif
diff --git a/engines/m4/graphics.cpp b/engines/m4/graphics.cpp
index fa0cd7ccd3..8624f18da1 100644
--- a/engines/m4/graphics.cpp
+++ b/engines/m4/graphics.cpp
@@ -69,6 +69,13 @@ void RGBList::setRange(int start, int count, const RGB8 *src) {
#define VGA_COLOR_TRANS(x) (x == 0x3f ? 255 : x << 2)
+M4Surface::~M4Surface() {
+ if (_rgbList) {
+ _madsVm->_palette->deleteRange(_rgbList);
+ delete _rgbList;
+ }
+}
+
void M4Surface::loadCodesM4(Common::SeekableReadStream *source) {
if (!source) {
free();
@@ -472,9 +479,18 @@ void M4Surface::loadBackground(int sceneNumber, RGBList **palData) {
if (_vm->getGameType() == GType_RexNebular) {
// Load Rex Nebular screen
+ bool hasPalette = palData != NULL;
+ if (!hasPalette)
+ palData = &_rgbList;
+
sprintf(resourceName, "rm%d.art", sceneNumber);
stream = _vm->_resourceManager->get(resourceName);
rexLoadBackground(stream, palData);
+
+ if (!hasPalette) {
+ _vm->_palette->addRange(_rgbList);
+ this->translate(_rgbList);
+ }
} else {
// Loads M4 game scene
if (palData)
@@ -617,16 +633,6 @@ void M4Surface::rexLoadBackground(Common::SeekableReadStream *source, RGBList **
int sceneWidth = sourceUnc->readUint16LE();
int sceneHeight = sourceUnc->readUint16LE();
int sceneSize = sceneWidth * sceneHeight;
- if (sceneWidth > this->width()) {
- warning("Background width is %i, too large to fit in screen. Setting it to %i", sceneWidth, this->width());
- sceneWidth = this->width();
- sceneSize = sceneWidth * sceneHeight;
- }
- if (sceneHeight > this->height()) {
- warning("Background height is %i, too large to fit in screen.Setting it to %i", sceneHeight, this->height());
- sceneHeight = this->height();
- sceneSize = sceneWidth * sceneHeight;
- }
// Set palette
if (!palData) {
@@ -642,6 +648,7 @@ void M4Surface::rexLoadBackground(Common::SeekableReadStream *source, RGBList **
sourceUnc = packData.getItemStream(1);
assert((int)sourceUnc->size() >= sceneSize);
+ create(sceneWidth, sceneHeight, 1);
byte *pData = (byte *)pixels;
sourceUnc->read(pData, sceneSize);
@@ -711,10 +718,8 @@ void M4Surface::m4LoadBackground(Common::SeekableReadStream *source) {
delete tileBuffer;
}
-void M4Surface::madsloadInterface(int index, RGBList **palData) {
- char resourceName[20];
- sprintf(resourceName, "i%d.int", index);
- MadsPack intFile(resourceName, _vm);
+void M4Surface::madsLoadInterface(const Common::String &filename) {
+ MadsPack intFile(filename.c_str(), _vm);
RGB8 *palette = new RGB8[16];
// Chunk 0, palette
@@ -728,7 +733,7 @@ void M4Surface::madsloadInterface(int index, RGBList **palData) {
intStream->readByte();
intStream->readByte();
}
- *palData = new RGBList(16, palette, true);
+ _rgbList = new RGBList(16, palette, true);
delete intStream;
// Chunk 1, data
@@ -736,8 +741,77 @@ void M4Surface::madsloadInterface(int index, RGBList **palData) {
create(320, 44, 1);
intStream->read(pixels, 320 * 44);
delete intStream;
+
+ // Translate the interface palette
+ _vm->_palette->addRange(_rgbList);
+ this->translate(_rgbList);
}
+void M4Surface::scrollX(int xAmount) {
+ if (xAmount == 0)
+ return;
+
+ byte buffer[80];
+ int direction = (xAmount > 0) ? 1 : -1;
+ int xSize = ABS(xAmount);
+ assert(xSize <= 80);
+
+ byte *srcP = (byte *)getBasePtr(0, 0);
+
+ for (int y = 0; y < height(); ++y, srcP += pitch) {
+ if (direction < 0) {
+ // Copy area to be overwritten
+ Common::copy(srcP, srcP + xSize, &buffer[0]);
+ // Shift the remainder of the line over the given area
+ Common::copy(srcP + xSize, srcP + width(), srcP);
+ // Move buffered area to the end of the line
+ Common::copy(&buffer[0], &buffer[xSize], srcP + width() - xSize);
+ } else {
+ // Copy area to be overwritten
+ Common::copy_backward(srcP + width() - xSize, srcP + width(), &buffer[80]);
+ // Shift the remainder of the line over the given area
+ Common::copy_backward(srcP, srcP + width() - xSize, srcP + width());
+ // Move buffered area to the start of the line
+ Common::copy_backward(&buffer[80 - xSize], &buffer[80], srcP + xSize);
+ }
+ }
+}
+
+void M4Surface::scrollY(int yAmount) {
+ if (yAmount == 0)
+ return;
+
+ int direction = (yAmount > 0) ? 1 : -1;
+ int ySize = ABS(yAmount);
+ assert(ySize < (height() / 2));
+ assert(width() == pitch);
+
+ int blockSize = ySize * width();
+ byte *tempData = (byte *)malloc(blockSize);
+ byte *pixelsP = (byte *)getBasePtr(0, 0);
+
+ if (direction > 0) {
+ // Buffer the lines to be overwritten
+ byte *srcP = (byte *)getBasePtr(0, height() - ySize);
+ Common::copy(srcP, srcP + (pitch * ySize), tempData);
+ // Vertically shift all the lines
+ Common::copy_backward(pixelsP, pixelsP + (pitch * (height() - ySize)),
+ pixelsP + (pitch * height()));
+ // Transfer the buffered lines top the top of the screen
+ Common::copy(tempData, tempData + blockSize, pixelsP);
+ } else {
+ // Buffer the lines to be overwritten
+ Common::copy(pixelsP, pixelsP + (pitch * ySize), tempData);
+ // Vertically shift all the lines
+ Common::copy(pixelsP + (pitch * ySize), pixelsP + (pitch * height()), pixelsP);
+ // Transfer the buffered lines to the bottom of the screen
+ Common::copy(tempData, tempData + blockSize, pixelsP + (pitch * (height() - ySize)));
+ }
+
+ ::free(tempData);
+}
+
+
void M4Surface::translate(RGBList *list, bool isTransparent) {
byte *p = getBasePtr(0, 0);
byte *palIndexes = list->palIndexes();
diff --git a/engines/m4/graphics.h b/engines/m4/graphics.h
index 6d0a82ad25..8c4b9ac072 100644
--- a/engines/m4/graphics.h
+++ b/engines/m4/graphics.h
@@ -35,6 +35,12 @@
namespace M4 {
+#define MADS_SURFACE_WIDTH 320
+#define MADS_SURFACE_HEIGHT 156
+#define MADS_SCREEN_HEIGHT 200
+#define MADS_Y_OFFSET ((MADS_SCREEN_HEIGHT - MADS_SURFACE_HEIGHT) / 2)
+
+
struct BGR8 {
uint8 b, g, r;
};
@@ -89,19 +95,23 @@ class M4Surface : protected Graphics::Surface {
private:
byte _color;
bool _isScreen;
+ RGBList *_rgbList;
void rexLoadBackground(Common::SeekableReadStream *source, RGBList **palData = NULL);
void madsLoadBackground(int roomNumber, RGBList **palData = NULL);
void m4LoadBackground(Common::SeekableReadStream *source);
public:
M4Surface(bool isScreen = false) {
- create(g_system->getWidth(), g_system->getHeight(), 1);
+ create(g_system->getWidth(), isScreen ? g_system->getHeight() : MADS_SURFACE_HEIGHT, 1);
_isScreen = isScreen;
+ _rgbList = NULL;
}
M4Surface(int width_, int height_) {
create(width_, height_, 1);
_isScreen = false;
+ _rgbList = NULL;
}
+ virtual ~M4Surface();
// loads a .COD file into the M4Surface
// TODO: maybe move this to the rail system? check where it makes sense
@@ -112,7 +122,8 @@ public:
// loads the specified background
void loadBackground(int sceneNumber, RGBList **palData = NULL);
void loadBackgroundRiddle(const char *sceneName);
- void madsloadInterface(int index, RGBList **palData);
+ void madsLoadInterface(int index, RGBList **palData = NULL);
+ void madsLoadInterface(const Common::String &filename);
void setColor(byte value) { _color = value; }
void setColour(byte value) { _color = value; }
@@ -173,7 +184,8 @@ public:
dest->copyFrom(this, destX, destY, depth, depthsSurface, scale, transparentColour);
}
-
+ void scrollX(int xAmount);
+ void scrollY(int yAmount);
void translate(RGBList *list, bool isTransparent = false);
};
diff --git a/engines/m4/gui.cpp b/engines/m4/gui.cpp
index 8f949de9c5..8665b4e767 100644
--- a/engines/m4/gui.cpp
+++ b/engines/m4/gui.cpp
@@ -290,26 +290,26 @@ void MenuButton::onRefresh() {
case OBJTYPE_SL_TEXT:
switch (_objectState) {
case OS_MOUSEOVER:
- _vm->_font->setColors(TEXT_COLOR_MOUSEOVER_SHADOW, TEXT_COLOR_MOUSEOVER_FOREGROUND,
+ _vm->_font->current()->setColours(TEXT_COLOR_MOUSEOVER_SHADOW, TEXT_COLOR_MOUSEOVER_FOREGROUND,
TEXT_COLOR_MOUSEOVER_HILIGHT);
sprite = sprites[SL_LINE_MOUSEOVER];
break;
case OS_PRESSED:
- _vm->_font->setColors(TEXT_COLOR_PRESSED_SHADOW, TEXT_COLOR_PRESSED_FOREGROUND,
+ _vm->_font->current()->setColours(TEXT_COLOR_PRESSED_SHADOW, TEXT_COLOR_PRESSED_FOREGROUND,
TEXT_COLOR_PRESSED_HILIGHT);
sprite = sprites[SL_LINE_PRESSED];
break;
case OS_GREYED:
- _vm->_font->setColors(TEXT_COLOR_GREYED_SHADOW, TEXT_COLOR_GREYED_FOREGROUND,
+ _vm->_font->current()->setColours(TEXT_COLOR_GREYED_SHADOW, TEXT_COLOR_GREYED_FOREGROUND,
TEXT_COLOR_GREYED_HILIGHT);
sprite = sprites[SL_LINE_NORMAL];
break;
default:
case OS_NORMAL:
- _vm->_font->setColors(TEXT_COLOR_NORMAL_SHADOW, TEXT_COLOR_NORMAL_FOREGROUND,
+ _vm->_font->current()->setColours(TEXT_COLOR_NORMAL_SHADOW, TEXT_COLOR_NORMAL_FOREGROUND,
TEXT_COLOR_NORMAL_HILIGHT);
sprite = sprites[SL_LINE_NORMAL];
break;
@@ -849,11 +849,11 @@ void MenuSaveLoadText::onRefresh() {
if (_displayValue != 0) {
char tempBuffer[5];
sprintf(tempBuffer, "%02d", _displayValue);
- _vm->_font->writeString(_parent, tempBuffer, xp, _bounds.top + 1, 0, -1);
+ _vm->_font->current()->writeString(_parent, tempBuffer, xp, _bounds.top + 1, 0, -1);
xp = _bounds.left + 26;
}
- _vm->_font->writeString(_parent, _displayText, xp, _bounds.top + 1, 0, -1);
+ _vm->_font->current()->writeString(_parent, _displayText, xp, _bounds.top + 1, 0, -1);
}
}
@@ -955,18 +955,18 @@ void MenuTextField::onRefresh() {
// Draw the text
_vm->_font->setFont(FONT_MENU);
- _vm->_font->setColors(TEXT_COLOR_NORMAL_SHADOW, TEXT_COLOR_NORMAL_FOREGROUND,
+ _vm->_font->current()->setColours(TEXT_COLOR_NORMAL_SHADOW, TEXT_COLOR_NORMAL_FOREGROUND,
TEXT_COLOR_NORMAL_HILIGHT);
int xp = _bounds.left + 4;
if (_displayValue != 0) {
char tempBuffer[5];
sprintf(tempBuffer, "%02d", _displayValue);
- _vm->_font->writeString(_parent, tempBuffer, xp, _bounds.top + 1, 0, -1);
+ _vm->_font->current()->writeString(_parent, tempBuffer, xp, _bounds.top + 1, 0, -1);
xp = _bounds.left + 26;
}
- _vm->_font->writeString(_parent, _displayText, xp, _bounds.top + 1, 0, -1);
+ _vm->_font->current()->writeString(_parent, _displayText, xp, _bounds.top + 1, 0, -1);
if (focused) {
// Draw in the cursor
@@ -975,7 +975,7 @@ void MenuTextField::onRefresh() {
// Get the width of the string up to the cursor position
char tempCh = *_cursor;
*_cursor = '\0';
- int stringWidth = _vm->_font->getWidth(_displayText);
+ int stringWidth = _vm->_font->current()->getWidth(_displayText);
*_cursor = tempCh;
parent()->setColor(TEXT_COLOR_MOUSEOVER_FOREGROUND);
@@ -1015,10 +1015,10 @@ bool MenuTextField::onEvent(M4EventType event, int32 param, int x, int y, MenuOb
tempP = &tempStr[tempLen];
_vm->_font->setFont(FONT_MENU);
- tempLen = _vm->_font->getWidth(tempStr);
+ tempLen = _vm->_font->current()->getWidth(tempStr);
while ((tempP != &tempStr[0]) && (tempLen > x - _bounds.left - 26)) {
*--tempP = '\0';
- tempLen = _vm->_font->getWidth(tempStr);
+ tempLen = _vm->_font->current()->getWidth(tempStr);
}
_cursor = &_displayText[tempP - &tempStr[0]];
@@ -1098,7 +1098,7 @@ bool MenuTextField::onEvent(M4EventType event, int32 param, int x, int y, MenuOb
parent()->_deleteSaveDesc = false;
_vm->_font->setFont(FONT_MENU);
- tempLen = _vm->_font->getWidth(_displayText);
+ tempLen = _vm->_font->current()->getWidth(_displayText);
if ((strlen(_displayText) < MAX_SAVEGAME_NAME - 1) &&
(tempLen < _pixelWidth - 12) && (param >= 32) && (param <= 127)) {
@@ -1140,9 +1140,9 @@ GUITextField::GUITextField(View *owner, const Common::Rect &bounds): GUIRect(own
void GUITextField::onRefresh() {
_parent->fillRect(_bounds, _vm->_palette->BLACK);
- _vm->_font->setColors(3, 3, 3);
+ _vm->_font->current()->setColours(3, 3, 3);
_vm->_font->setFont(FONT_INTERFACE);
- _vm->_font->writeString(_parent, _text.c_str(), _bounds.left, _bounds.top, 0, 1);
+ _vm->_font->current()->writeString(_parent, _text.c_str(), _bounds.left, _bounds.top, 0, 1);
}
//--------------------------------------------------------------------------
diff --git a/engines/m4/m4.cpp b/engines/m4/m4.cpp
index 897fb468cd..a5db6660d8 100644
--- a/engines/m4/m4.cpp
+++ b/engines/m4/m4.cpp
@@ -145,8 +145,9 @@ MadsM4Engine::~MadsM4Engine() {
delete _script;
delete _ws;
delete _random;
- delete _animation;
delete _palette;
+ delete _globals;
+ delete _resourceManager;
}
Common::Error MadsM4Engine::run() {
@@ -170,7 +171,7 @@ Common::Error MadsM4Engine::run() {
_events = new Events(this);
_kernel = new Kernel(this);
_player = new Player(this);
- _font = new Font(this);
+ _font = new FontManager(this);
if (getGameType() == GType_Burger) {
_actor = new Actor(this);
_conversationView = new ConversationView(this);
@@ -184,7 +185,6 @@ Common::Error MadsM4Engine::run() {
_sound = new Sound(this, _mixer, 255);
_script = new ScriptInterpreter(this);
_ws = new WoodScript(this);
- _animation = new Animation(this);
//_callbacks = new Callbacks(this);
_random = new Common::RandomSource();
g_eventRec.registerRandomSource(*_random, "m4");
@@ -305,8 +305,6 @@ M4Engine::M4Engine(OSystem *syst, const M4GameDescription *gameDesc): MadsM4Engi
}
M4Engine::~M4Engine() {
- delete _resourceManager;
- delete _globals;
delete _converse;
}
@@ -502,8 +500,6 @@ MadsEngine::MadsEngine(OSystem *syst, const M4GameDescription *gameDesc): MadsM4
}
MadsEngine::~MadsEngine() {
- delete _globals;
- delete _resourceManager;
}
Common::Error MadsEngine::run() {
@@ -554,9 +550,9 @@ Common::Error MadsEngine::run() {
_scene->show();
_font->setFont(FONT_MAIN_MADS);
- _font->setColors(2, 1, 3);
- _font->writeString(_scene->getBackgroundSurface(), "Testing the M4/MADS ScummVM engine", 5, 160, 310, 2);
- _font->writeString(_scene->getBackgroundSurface(), "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 5, 180, 310, 2);
+ _font->current()->setColours(2, 1, 3);
+ _font->current()->writeString(_scene->getBackgroundSurface(), "Testing the M4/MADS ScummVM engine", 5, 160, 310, 2);
+ _font->current()->writeString(_scene->getBackgroundSurface(), "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 5, 180, 310, 2);
if (getGameType() == GType_DragonSphere) {
//_scene->showMADSV2TextBox("Test", 10, 10, NULL);
@@ -581,8 +577,6 @@ Common::Error MadsEngine::run() {
while (!_events->quitFlag) {
eventHandler();
- _animation->updateAnim();
-
if (g_system->getMillis() >= nextFrame) {
nextFrame = g_system->getMillis() + GAME_FRAME_DELAY;
++_currentTimer;
diff --git a/engines/m4/m4.h b/engines/m4/m4.h
index 1f34bd3685..9937107668 100644
--- a/engines/m4/m4.h
+++ b/engines/m4/m4.h
@@ -189,7 +189,7 @@ public:
Player *_player;
Mouse *_mouse;
Events *_events;
- Font *_font;
+ FontManager *_font;
Actor *_actor;
Scene *_scene;
Dialogs *_dialogs;
@@ -200,7 +200,6 @@ public:
Rails *_rails;
ScriptInterpreter *_script;
WoodScript *_ws;
- Animation *_animation;
Common::RandomSource *_random;
Scene *scene() { return _scene; }
diff --git a/engines/m4/m4_views.cpp b/engines/m4/m4_views.cpp
index 3d633cef0d..f4345787df 100644
--- a/engines/m4/m4_views.cpp
+++ b/engines/m4/m4_views.cpp
@@ -34,7 +34,7 @@ namespace M4 {
GUIInventory::GUIInventory(View *owner, MadsM4Engine *vm, const Common::Rect &bounds, int horizCells,
int vertCells, int cellWidth, int cellHeight, int tag): GUIRect(owner, bounds, tag) {
- _vm = vm;
+ _vm = vm;
_cellCount.x = horizCells;
_cellCount.y = vertCells;
_cellSize.x = cellWidth;
diff --git a/engines/m4/mads_anim.cpp b/engines/m4/mads_anim.cpp
index 954916700c..e1dbbaf106 100644
--- a/engines/m4/mads_anim.cpp
+++ b/engines/m4/mads_anim.cpp
@@ -37,7 +37,7 @@ namespace M4 {
TextviewView::TextviewView(MadsM4Engine *vm):
View(vm, Common::Rect(0, 0, vm->_screen->width(), vm->_screen->height())),
_bgSurface(vm->_screen->width(), MADS_SURFACE_HEIGHT),
- _textSurface(vm->_screen->width(), MADS_SURFACE_HEIGHT + vm->_font->getHeight() +
+ _textSurface(vm->_screen->width(), MADS_SURFACE_HEIGHT + vm->_font->current()->getHeight() +
TEXTVIEW_LINE_SPACING) {
_screenType = VIEWID_TEXTVIEW;
@@ -60,7 +60,7 @@ TextviewView::TextviewView(MadsM4Engine *vm):
_vm->_palette->setPalette(&palData[0], 4, 3);
_vm->_palette->blockRange(4, 3);
- _vm->_font->setColors(5, 6, 4);
+ _vm->_font->current()->setColours(5, 6, 4);
clear();
_bgSurface.clear();
@@ -222,7 +222,7 @@ void TextviewView::updateState() {
}
} else {
// Handling a text row
- if (++_lineY == (_vm->_font->getHeight() + TEXTVIEW_LINE_SPACING))
+ if (++_lineY == (_vm->_font->current()->getHeight() + TEXTVIEW_LINE_SPACING))
processLines();
}
@@ -404,7 +404,7 @@ void TextviewView::processText() {
if (!strcmp(_currentLine, "***")) {
// Special signifier for end of script
- _scrollCount = _vm->_font->getHeight() * 13;
+ _scrollCount = _vm->_font->current()->getHeight() * 13;
_lineY = -1;
return;
}
@@ -416,7 +416,7 @@ void TextviewView::processText() {
char *centerP = strchr(_currentLine, '@');
if (centerP) {
*centerP = '\0';
- xStart = (width() / 2) - _vm->_font->getWidth(_currentLine);
+ xStart = (width() / 2) - _vm->_font->current()->getWidth(_currentLine);
// Delete the @ character and shift back the remainder of the string
char *p = centerP + 1;
@@ -424,16 +424,16 @@ void TextviewView::processText() {
strcpy(centerP, p);
} else {
- lineWidth = _vm->_font->getWidth(_currentLine);
+ lineWidth = _vm->_font->current()->getWidth(_currentLine);
xStart = (width() - lineWidth) / 2;
}
// Copy the text line onto the bottom of the textSurface surface, which will allow it
// to gradually scroll onto the screen
- int yp = _textSurface.height() - _vm->_font->getHeight() - TEXTVIEW_LINE_SPACING;
+ int yp = _textSurface.height() - _vm->_font->current()->getHeight() - TEXTVIEW_LINE_SPACING;
_textSurface.fillRect(Common::Rect(0, yp, _textSurface.width(), _textSurface.height()),
_vm->_palette->BLACK);
- _vm->_font->writeString(&_textSurface, _currentLine, xStart, yp);
+ _vm->_font->current()->writeString(&_textSurface, _currentLine, xStart, yp);
}
@@ -441,7 +441,12 @@ void TextviewView::processText() {
AnimviewView::AnimviewView(MadsM4Engine *vm):
View(vm, Common::Rect(0, 0, vm->_screen->width(), vm->_screen->height())),
- _bgSurface(vm->_screen->width(), MADS_SURFACE_HEIGHT) {
+ MadsView(this), _backgroundSurface(MADS_SURFACE_WIDTH, MADS_SURFACE_HEIGHT),
+ _codeSurface(MADS_SURFACE_WIDTH, MADS_SURFACE_HEIGHT) {
+
+ MadsView::_bgSurface = &_backgroundSurface;
+ MadsView::_depthSurface = &_codeSurface;
+ MadsView::_yOffset = MADS_Y_OFFSET;
_screenType = VIEWID_ANIMVIEW;
_screenFlags.layer = LAYER_BACKGROUND;
@@ -452,27 +457,28 @@ AnimviewView::AnimviewView(MadsM4Engine *vm):
_palData = NULL;
_previousUpdate = 0;
_transition = kTransitionNone;
+ _activeAnimation = NULL;
reset();
// Set up system palette colors
_vm->_palette->setMadsSystemPalette();
clear();
- _bgSurface.clear();
+ _backgroundSurface.clear();
- int y = (height() - MADS_SURFACE_HEIGHT) / 2;
setColor(2);
- hLine(0, width() - 1, y - 2);
- hLine(0, width() - 1, height() - y + 1);
+ hLine(0, width() - 1, MADS_Y_OFFSET - 2);
+ hLine(0, width() - 1, MADS_Y_OFFSET + MADS_SURFACE_HEIGHT + 2);
}
AnimviewView::~AnimviewView() {
if (_script)
_vm->res()->toss(_resourceName);
+ delete _activeAnimation;
}
void AnimviewView::reset() {
- _bgSurface.clear();
+ _backgroundSurface.clear();
_soundDriverLoaded = false;
}
@@ -507,27 +513,26 @@ void AnimviewView::updateState() {
if (!_script)
return;
- // Only update state if wait period has expired
- if (_previousUpdate > 0) {
- if (g_system->getMillis() - _previousUpdate < 100)
- return;
-
- _previousUpdate = g_system->getMillis();
+ if (!_activeAnimation) {
+ readNextCommand();
+ assert(_activeAnimation);
}
- // Check if we're ready for the next command
- bool animRunning = false;
- if (!animRunning) {
+ // Update the current animation
+ _activeAnimation->update();
+ if (_activeAnimation->freeFlag()) {
+ delete _activeAnimation;
+ _activeAnimation = NULL;
+
if (_script->eos() || _script->err()) {
scriptDone();
return;
}
readNextCommand();
-
- // FIXME: Replace flag with proper animation end check
- animRunning = true;
}
+
+ refresh();
}
void AnimviewView::readNextCommand() {
@@ -562,46 +567,15 @@ void AnimviewView::readNextCommand() {
if (strchr(_currentLine, '.') == NULL)
strcat(_currentLine, ".aa");
- AAFile aaFile(_currentLine, _vm);
-
- // Initial validation
- if (aaFile.flags & AA_HAS_FONT) {
- assert(_vm->_resourceManager->resourceExists(aaFile.fontResource.c_str()));
- }
-
- for (int seriesCtr = 0; seriesCtr < aaFile.seriesCount; ++seriesCtr)
- assert(_vm->_resourceManager->resourceExists(aaFile.filenames[seriesCtr].c_str()));
-
- // Start sound
- if (aaFile.flags & AA_HAS_SOUND) {
- char buffer[100];
- strcpy(buffer, aaFile.soundName.c_str());
- buffer[0] = 'A'; // A for AdLib resource
-
- /*Common::SeekableReadStream *stream = */_vm->_resourceManager->get(buffer);
-
- _vm->_resourceManager->toss(buffer);
- }
-
-
- char artFile[80];
- sprintf(artFile, "rm%d.art", aaFile.roomNumber);
-
- // Not all scenes have a background. If there is one, refresh it
- if (_vm->_resourceManager->resourceExists(artFile)) {
- if (_palData) {
- _vm->_palette->deleteRange(_palData);
- delete _palData;
- }
- _bgSurface.loadBackground(aaFile.roomNumber, &_palData);
- _vm->_palette->addRange(_palData);
- _bgSurface.translate(_palData);
- }
+ _activeAnimation = new MadsAnimation(_vm, this);
+ _activeAnimation->load(_currentLine, 0);
- // Grab what the final palete will be
- RGB8 destPalette[256];
- _vm->_palette->grabPalette(destPalette, 0, 256);
+ _backgroundSurface.loadBackground(_activeAnimation->roomNumber());
+ _codeSurface.setSize(_backgroundSurface.width(), _backgroundSurface.height());
+ _codeSurface.fillRect(_codeSurface.bounds(), 0xff);
+ _spriteSlots.fullRefresh();
+/*
// Handle scene transition
switch (_transition) {
case kTransitionNone:
@@ -631,16 +605,14 @@ void AnimviewView::readNextCommand() {
// nothing to do
break;
}
-
- // Refresh the view
- int yp = (height() - _bgSurface.height()) / 2;
- _bgSurface.copyTo(this, 0, yp);
+*/
_vm->_resourceManager->toss(_currentLine);
}
void AnimviewView::scriptDone() {
+return;
AnimviewCallback fn = _callback;
MadsM4Engine *vm = _vm;
@@ -714,45 +686,4 @@ void AnimviewView::processCommand() {
}
}
-AAFile::AAFile(const char *resourceName, MadsM4Engine* vm): MadsPack(resourceName, vm) {
- Common::MemoryReadStream stream1(*getItemStream(1));
- Common::MemoryReadStream stream2(*getItemStream(2));
-
- Common::MemoryReadStream stream(*getItemStream(0));
-
- seriesCount = stream.readUint16LE();
- frameCount = stream.readUint16LE();
- frameEntryCount = stream.readUint16LE();
- stream.skip(3);
- flags = stream.readByte();
- stream.skip(4);
- roomNumber = stream.readUint16LE();
- stream.skip(10);
- frameTicks = stream.readUint16LE();
-
- stream.skip(21);
- for (int i = 0; i < 10; ++i) {
- char filename[13];
- stream.read(filename, 13);
- filenames.push_back(Common::String(filename, 13));
- }
-
- stream.skip(81);
- char name[100];
- stream.read(name, 13);
- lbmFilename = Common::String(name, 13);
-
- stream.skip(365);
- stream.read(name, 13);
- spritesFilename = Common::String(name, 13);
-
- stream.skip(48);
- stream.read(name, 13);
- soundName = Common::String(name, 13);
-
- stream.skip(26);
- stream.read(name, 14);
- fontResource = Common::String(name, 14);
-}
-
}
diff --git a/engines/m4/mads_anim.h b/engines/m4/mads_anim.h
index 680c5ff901..8c4a5e6fb7 100644
--- a/engines/m4/mads_anim.h
+++ b/engines/m4/mads_anim.h
@@ -28,24 +28,12 @@
#include "m4/viewmgr.h"
#include "m4/compression.h"
+#include "m4/animation.h"
#include "common/str-array.h"
namespace M4 {
-enum SceneTransition {
- kTransitionNone = 0,
- kTransitionFadeIn = 1,
- kTransitionFadeIn2 = 2,
- kTransitionBoxInBottomLeft = 3,
- kTransitionBoxInBottomRight = 4,
- kTransitionBoxInTopLeft = 5,
- kTransitionBoxInTopRight = 6,
- kTransitionPanLeftToRight = 7,
- kTransitionPanRightToLeft = 8,
- kTransitionCircleIn = 9
-};
-
typedef void (*TextviewCallback)(MadsM4Engine *vm);
class TextviewView : public View {
@@ -89,36 +77,19 @@ public:
typedef void (*AnimviewCallback)(MadsM4Engine *vm);
-class AAFile : public MadsPack {
-public:
- AAFile(const char *resourceName, MadsM4Engine* vm);
-
- uint16 seriesCount;
- uint16 frameCount;
- uint16 frameEntryCount;
- uint8 flags;
- uint16 roomNumber;
- uint16 frameTicks;
- Common::StringArray filenames;
- Common::String lbmFilename;
- Common::String spritesFilename;
- Common::String soundName;
- Common::String fontResource;
-};
-
-enum AAFlags {AA_HAS_FONT = 0x20, AA_HAS_SOUND = 0x8000};
-
-class AnimviewView : public View {
+class AnimviewView : public View, MadsView {
private:
char _resourceName[80];
Common::SeekableReadStream *_script;
uint32 _previousUpdate;
char _currentLine[80];
- M4Surface _bgSurface;
+ M4Surface _backgroundSurface;
+ M4Surface _codeSurface;
AnimviewCallback _callback;
bool _soundDriverLoaded;
RGBList *_palData;
int _transition;
+ MadsAnimation *_activeAnimation;
void reset();
void readNextCommand();
diff --git a/engines/m4/mads_logic.cpp b/engines/m4/mads_logic.cpp
index ee65d3be5c..9cb053a876 100644
--- a/engines/m4/mads_logic.cpp
+++ b/engines/m4/mads_logic.cpp
@@ -59,8 +59,8 @@ void MadsSceneLogic::getSceneSpriteSet() {
// if ((_sceneNumber == 105) ((_sceneNumber == 109) && (word_84800 != 0)))
// _madsVm->globals()->playerSpriteChanged = true;
-// _vm->_palette->setEntry(16, 0x38, 0xFF, 0xFF);
-// _vm->_palette->setEntry(17, 0x38, 0xb4, 0xb4);
+ _vm->_palette->setEntry(16, 0x38, 0xFF, 0xFF);
+ _vm->_palette->setEntry(17, 0x38, 0xb4, 0xb4);
}
void MadsSceneLogic::getAnimName() {
@@ -83,7 +83,7 @@ uint16 MadsSceneLogic::startReversibleSpriteSequence(uint16 srcSpriteIdx, int v0
spriteFrame->y + (spriteFrame->height() / 2)));
return _madsVm->scene()->_sequenceList.add(srcSpriteIdx, v0, 1, triggerCountdown, timeoutTicks, extraTicks, numTicks, 0, 0,
- -1, 100, depth - 1, 1, ANIMTYPE_REVERSIBLE, 0, 0);
+ true, 100, depth - 1, 1, ANIMTYPE_REVERSIBLE, 0, 0);
}
uint16 MadsSceneLogic::startCycledSpriteSequence(uint16 srcSpriteIdx, int v0, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks) {
@@ -92,7 +92,7 @@ uint16 MadsSceneLogic::startCycledSpriteSequence(uint16 srcSpriteIdx, int v0, in
spriteFrame->y + (spriteFrame->height() / 2)));
return _madsVm->scene()->_sequenceList.add(srcSpriteIdx, v0, 1, triggerCountdown, timeoutTicks, extraTicks, numTicks, 0, 0,
- -1, 100, depth - 1, 1, ANIMTYPE_CYCLED, 0, 0);
+ true, 100, depth - 1, 1, ANIMTYPE_CYCLED, 0, 0);
}
uint16 MadsSceneLogic::startSpriteSequence3(uint16 srcSpriteIdx, int v0, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks) {
@@ -101,7 +101,7 @@ uint16 MadsSceneLogic::startSpriteSequence3(uint16 srcSpriteIdx, int v0, int num
spriteFrame->y + (spriteFrame->height() / 2)));
return _madsVm->scene()->_sequenceList.add(srcSpriteIdx, v0, 1, triggerCountdown, timeoutTicks, extraTicks, numTicks, 0, 0,
- -1, 100, depth - 1, -1, ANIMTYPE_CYCLED, 0, 0);
+ true, 100, depth - 1, -1, ANIMTYPE_CYCLED, 0, 0);
}
void MadsSceneLogic::activateHotspot(int idx, bool active) {
@@ -146,6 +146,7 @@ void MadsSceneLogic::lowRoomsEntrySound() {
}
}
+
/*--------------------------------------------------------------------------*/
/**
@@ -169,7 +170,9 @@ void MadsSceneLogic::setupScene() {
// sub_1e754(animName, 3);
- getSceneSpriteSet();
+ if ((_sceneNumber >= 101) && (_sceneNumber <= 112))
+ getSceneSpriteSet();
+
getAnimName();
}
@@ -231,7 +234,8 @@ void MadsSceneLogic::enterScene() {
_madsVm->globals()->loadQuoteSet(0x31, 0x32, 0x37, 0x38, 0x39, -1);
if (_madsVm->globals()->_globals[10]) {
- // TODO: Load scene animation
+ const char *animName = MADSResourceManager::getResourceName('S', 'e', EXTTYPE_AA, NULL, -1);
+ _madsVm->scene()->loadAnimation(animName, 0x47);
_madsVm->scene()->getSceneResources().playerPos = Common::Point(68, 140);
_madsVm->scene()->getSceneResources().playerDir = 4;
diff --git a/engines/m4/mads_logic.h b/engines/m4/mads_logic.h
index 774ed016a6..8c3f41d08b 100644
--- a/engines/m4/mads_logic.h
+++ b/engines/m4/mads_logic.h
@@ -29,6 +29,8 @@
#ifndef M4_MADS_LOGIC_H
#define M4_MADS_LOGIC_H
+#include "m4/mads_views.h"
+
namespace M4 {
class MadsSceneLogic {
diff --git a/engines/m4/mads_menus.cpp b/engines/m4/mads_menus.cpp
index da8ac4230e..94894e78be 100644
--- a/engines/m4/mads_menus.cpp
+++ b/engines/m4/mads_menus.cpp
@@ -49,7 +49,7 @@ RexMainMenuView::RexMainMenuView(MadsM4Engine *vm):
_skipFlag = false;
// Load the background for the Rex Nebular game
- _bgSurface = new M4Surface(width(), MADS_SURFACE_HEIGHT);
+ _bgSurface = new M4Surface();
_bgSurface->loadBackground(REX_MENUSCREEN, &_bgPalData);
_vm->_palette->addRange(_bgPalData);
_bgSurface->translate(_bgPalData);
@@ -619,7 +619,7 @@ void RexDialogView::initialiseLines() {
// Set up a default sprite slot entry for a full screen refresh
_spriteSlots.startIndex = 1;
_spriteSlots[0].spriteType = FULL_SCREEN_REFRESH;
- _spriteSlots[0].timerIndex = -1;
+ _spriteSlots[0].seqIndex = -1;
}
void RexDialogView::initialiseGraphics() {
@@ -796,7 +796,7 @@ bool RexDialogView::onEvent(M4EventType eventType, int32 param1, int x, int y, b
void RexDialogView::setFrame(int frameNumber, int depth) {
int slotIndex = _spriteSlots.getIndex();
_spriteSlots[slotIndex].spriteType = FOREGROUND_SPRITE;
- _spriteSlots[slotIndex].timerIndex = 1;
+ _spriteSlots[slotIndex].seqIndex = 1;
_spriteSlots[slotIndex].spriteListIndex = 0; //_menuSpritesIndex;
_spriteSlots[slotIndex].frameNumber = frameNumber;
@@ -985,15 +985,15 @@ RexGameMenuDialog::RexGameMenuDialog(): RexDialogView() {
void RexGameMenuDialog::addLines() {
// Add the title
- int top = MADS_Y_OFFSET - 2 - ((((_vm->_font->getHeight() + 2) * 6) >> 1) - 78);
+ int top = MADS_Y_OFFSET - 2 - ((((_vm->_font->current()->getHeight() + 2) * 6) >> 1) - 78);
- addQuote(_vm->_font, ALIGN_CENTER, 0, top, 10);
+ addQuote(_vm->_font->current(), ALIGN_CENTER, 0, top, 10);
// Loop for adding the option lines of the dialog
top += 6;
for (int idx = 0; idx < 5; ++idx) {
- top += _vm->_font->getHeight() + 1;
- addQuote(_vm->_font, ALIGN_CENTER, 0, top, 11 + idx);
+ top += _vm->_font->current()->getHeight() + 1;
+ addQuote(_vm->_font->current(), ALIGN_CENTER, 0, top, 11 + idx);
}
}
@@ -1069,42 +1069,42 @@ void RexOptionsDialog::reload() {
void RexOptionsDialog::addLines() {
// Add the title
- int top = MADS_Y_OFFSET - 2 - ((((_vm->_font->getHeight() + 1) * 9 + 12) >> 1) - 78);
+ int top = MADS_Y_OFFSET - 2 - ((((_vm->_font->current()->getHeight() + 1) * 9 + 12) >> 1) - 78);
- addQuote(_vm->_font, ALIGN_CENTER, 0, top, 16);
+ addQuote(_vm->_font->current(), ALIGN_CENTER, 0, top, 16);
// Music state line
- top += _vm->_font->getHeight() + 1 + 6;
- addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 17, _tempConfig.musicFlag ? 24 : 25);
+ top += _vm->_font->current()->getHeight() + 1 + 6;
+ addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 17, _tempConfig.musicFlag ? 24 : 25);
// Sound state line
- top += _vm->_font->getHeight() + 1;
- addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 18, _tempConfig.soundFlag ? 26 : 27);
+ top += _vm->_font->current()->getHeight() + 1;
+ addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 18, _tempConfig.soundFlag ? 26 : 27);
// Interface easy state line
- top += _vm->_font->getHeight() + 1;
- addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 19, _tempConfig.easyMouse ? 29 : 28);
+ top += _vm->_font->current()->getHeight() + 1;
+ addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 19, _tempConfig.easyMouse ? 29 : 28);
// Inventory sppinng state line
- top += _vm->_font->getHeight() + 1;
- addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 20, _tempConfig.invObjectsStill ? 31 : 30);
+ top += _vm->_font->current()->getHeight() + 1;
+ addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 20, _tempConfig.invObjectsStill ? 31 : 30);
// Text window state line
- top += _vm->_font->getHeight() + 1;
- addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 21, _tempConfig.textWindowStill ? 33 : 32);
+ top += _vm->_font->current()->getHeight() + 1;
+ addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 21, _tempConfig.textWindowStill ? 33 : 32);
// Screen fade state line
- top += _vm->_font->getHeight() + 1;
- addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 22, _tempConfig.screenFades + 34);
+ top += _vm->_font->current()->getHeight() + 1;
+ addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 22, _tempConfig.screenFades + 34);
// Storyline mode line
- top += _vm->_font->getHeight() + 1;
- addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 23, (_tempConfig.storyMode == 1) ? 37 : 38);
+ top += _vm->_font->current()->getHeight() + 1;
+ addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 23, (_tempConfig.storyMode == 1) ? 37 : 38);
// Add Done and Cancel button texts
- top += _vm->_font->getHeight() + 1 + 6;
- addQuote(_vm->_font, ALIGN_CENTER, -54, top, 1, 0);
- addQuote(_vm->_font, ALIGN_CENTER, 54, top, 2, 0);
+ top += _vm->_font->current()->getHeight() + 1 + 6;
+ addQuote(_vm->_font->current(), ALIGN_CENTER, -54, top, 1, 0);
+ addQuote(_vm->_font->current(), ALIGN_CENTER, 54, top, 2, 0);
}
bool RexOptionsDialog::onEvent(M4EventType eventType, int32 param1, int x, int y, bool &captureEvents) {
diff --git a/engines/m4/mads_scene.cpp b/engines/m4/mads_scene.cpp
index 1b8e44b581..a65224c722 100644
--- a/engines/m4/mads_scene.cpp
+++ b/engines/m4/mads_scene.cpp
@@ -37,11 +37,20 @@
#include "m4/mads_views.h"
#include "m4/compression.h"
#include "m4/staticres.h"
+#include "m4/animation.h"
namespace M4 {
+static const int INV_ANIM_FRAME_SPEED = 2;
+static const int INVENTORY_X = 160;
+static const int INVENTORY_Y = 159;
+static const int SCROLLER_DELAY = 200;
+
+//--------------------------------------------------------------------------
+
MadsScene::MadsScene(MadsEngine *vm): _sceneResources(), Scene(vm, &_sceneResources), MadsView(this) {
_vm = vm;
+ _activeAnimation = NULL;
MadsView::_bgSurface = Scene::_backgroundSurface;
MadsView::_depthSurface = Scene::_walkSurface;
@@ -51,6 +60,8 @@ MadsScene::MadsScene(MadsEngine *vm): _sceneResources(), Scene(vm, &_sceneResour
}
MadsScene::~MadsScene() {
+ delete _activeAnimation;
+ _activeAnimation = NULL;
leaveScene();
_vm->_viewManager->deleteView(_interfaceSurface);
}
@@ -66,10 +77,17 @@ void MadsScene::loadScene2(const char *aaName) {
_kernelMessages.clear();
// Load up the properties for the scene
- _sceneResources.load(_currentScene);
+ _sceneResources.load(_currentScene, NULL, 0/*word_83546*/, _walkSurface, _backgroundSurface);
// Load scene walk paths
loadSceneCodes(_currentScene);
+
+ // Initialise the scene animation
+ uint16 flags = 0x4100;
+ if (_madsVm->globals()->_config.textWindowStill)
+ flags |= 0x200;
+
+ _sceneAnimation->initialise(aaName, flags, _interfaceSurface, NULL);
}
/**
@@ -78,27 +96,12 @@ void MadsScene::loadScene2(const char *aaName) {
void MadsScene::loadSceneTemporary() {
/* Existing code that eventually needs to be replaced with the proper MADS code */
// Set system palette entries
- _vm->_palette->blockRange(0, 7);
+ _vm->_palette->blockRange(0, 18);
RGB8 sysColors[3] = { {0x1f<<2, 0x2d<<2, 0x31<<2, 0}, {0x24<<2, 0x37<<2, 0x3a<<2, 0},
{0x00<<2, 0x10<<2, 0x16<<2, 0}};
_vm->_palette->setPalette(&sysColors[0], 4, 3);
- _backgroundSurface->loadBackground(_currentScene, &_palData);
- _vm->_palette->addRange(_palData);
- _backgroundSurface->translate(_palData);
-
- if (_currentScene < 900) {
- _interfaceSurface->madsloadInterface(0, &_interfacePal);
- _vm->_palette->addRange(_interfacePal);
- _interfaceSurface->translate(_interfacePal);
- _backgroundSurface->copyFrom(_interfaceSurface, Common::Rect(0, 0, 320, 44), 0, 200 - 44);
-
- _interfaceSurface->initialise();
- }
-
- // Don't load other screen resources for system screens
- if (_currentScene >= 900)
- return;
+ _interfaceSurface->initialise();
loadSceneHotspots(_currentScene);
@@ -167,6 +170,11 @@ void MadsScene::leaveScene() {
delete _sceneResources.props;
delete _walkSurface;
+ if (_activeAnimation) {
+ delete _activeAnimation;
+ _activeAnimation = NULL;
+ }
+
Scene::leaveScene();
}
@@ -185,15 +193,6 @@ void MadsScene::loadSceneCodes(int sceneNumber, int index) {
sceneS = walkData.getItemStream(0);
_walkSurface->loadCodesMads(sceneS);
_vm->res()->toss(filename);
- } else if (_vm->getGameType() == GType_RexNebular) {
- // For Rex Nebular, the walk areas are part of the scene info
- byte *destP = _walkSurface->getBasePtr(0, 0);
- const byte *srcP = _sceneResources.walkData;
- byte runLength;
- while ((runLength = *srcP++) != 0) {
- Common::set_to(destP, destP + runLength, *srcP++);
- destP += runLength;
- }
}
}
@@ -289,21 +288,27 @@ void MadsScene::update() {
if (sStatusText[0]) {
// Text colors are inverted in Dragonsphere
if (_vm->getGameType() == GType_DragonSphere)
- _vm->_font->setColors(_vm->_palette->BLACK, _vm->_palette->WHITE, _vm->_palette->BLACK);
+ _vm->_font->current()->setColours(_vm->_palette->BLACK, _vm->_palette->WHITE, _vm->_palette->BLACK);
else
- _vm->_font->setColors(_vm->_palette->WHITE, _vm->_palette->BLACK, _vm->_palette->BLACK);
+ _vm->_font->current()->setColours(_vm->_palette->WHITE, _vm->_palette->BLACK, _vm->_palette->BLACK);
_vm->_font->setFont(FONT_MAIN_MADS);
- _vm->_font->writeString(this, sStatusText, (width() - _vm->_font->getWidth(sStatusText)) / 2, 142, 0);
+ _vm->_font->current()->writeString(this, sStatusText, (width() - _vm->_font->current()->getWidth(sStatusText)) / 2, 142, 0);
}
-
- //***DEBUG***
- _spriteSlots.getSprite(0).getFrame(1)->copyTo(this, 120, 90, 0);
}
void MadsScene::updateState() {
_sceneLogic.sceneStep();
_sequenceList.tick();
+
+ if ((_activeAnimation) && !_abortTimers) {
+ _activeAnimation->update();
+ if (((MadsAnimation *) _activeAnimation)->freeFlag()) {
+ delete _activeAnimation;
+ _activeAnimation = NULL;
+ }
+ }
+
_kernelMessages.update();
}
@@ -437,6 +442,15 @@ void MadsScene::showMADSV2TextBox(char *text, int x, int y, char *faceName) {
boxSprites->getFrame(bottomRight)->copyTo(_backgroundSurface, curX, curY + 1);
}
+void MadsScene::loadAnimation(const Common::String &animName, int v0) {
+ if (_activeAnimation)
+ error("Multiple active animations are not allowed");
+
+ MadsAnimation *anim = new MadsAnimation(_vm, this);
+ anim->load(animName.c_str(), 0);
+ _activeAnimation = anim;
+}
+
/*--------------------------------------------------------------------------*/
MadsAction::MadsAction() {
@@ -614,45 +628,531 @@ void MadsAction::set() {
/*--------------------------------------------------------------------------*/
-void MadsSceneResources::load(int sId) {
- const char *sceneInfoStr = MADSResourceManager::getResourceName(RESPREFIX_RM, sId, ".DAT");
- Common::SeekableReadStream *rawStream = _vm->_resourceManager->get(sceneInfoStr);
+void MadsSceneResources::load(int sceneNumber, const char *resName, int v0, M4Surface *depthSurface, M4Surface *surface) {
+ char buffer1[80];
+ const char *sceneName;
+
+ // TODO: Initialise spriteSet / xp_list
+
+ if (sceneNumber > 0) {
+ sceneName = MADSResourceManager::getResourceName(RESPREFIX_RM, sceneNumber, ".DAT");
+ } else {
+ strcat(buffer1, "*");
+ strcat(buffer1, resName);
+ sceneName = buffer1; // TODO: Check whether this needs to be converted to 'HAG form'
+ }
+
+ Common::SeekableReadStream *rawStream = _vm->_resourceManager->get(sceneName);
MadsPack sceneInfo(rawStream);
+ // Chunk 0:
// Basic scene info
Common::SeekableReadStream *stream = sceneInfo.getItemStream(0);
int resSceneId = stream->readUint16LE();
- assert(resSceneId == sId);
-
+ assert(resSceneId == sceneNumber);
artFileNum = stream->readUint16LE();
- field_4 = stream->readUint16LE();
+ drawStyle = stream->readUint16LE();
width = stream->readUint16LE();
height = stream->readUint16LE();
assert((width == 320) && (height == 156));
stream->skip(24);
- objectCount = stream->readUint16LE();
+ int objectCount = stream->readUint16LE();
stream->skip(40);
+ // Load in any scene objects
for (int i = 0; i < objectCount; ++i) {
- objects[i].load(stream);
+ MadsObject rec;
+ rec.load(stream);
+ objects.push_back(rec);
+ }
+ for (int i = 0; i < 20 - objectCount; ++i)
+ stream->skip(48);
+
+ int setCount = stream->readUint16LE();
+ stream->readUint16LE();
+ for (int i = 0; i < setCount; ++i) {
+ char buffer2[64];
+ Common::String s(buffer2, 64);
+ setNames.push_back(s);
+ }
+
+ // Initialise a copy of the surfaces if they weren't provided
+ bool dsFlag = false, ssFlag = false;
+ int gfxSize = width * height;
+ if (!surface) {
+ surface = new M4Surface(width, height);
+ ssFlag = true;
+ }
+ int walkSize = gfxSize;
+ if (drawStyle == 2) {
+ width >>= 2;
+ walkSize = width * height;
+ }
+ if (!depthSurface) {
+ depthSurface = new M4Surface(width, height);
+ dsFlag = true;
}
// For Rex Nebular, read in the scene's compressed walk surface information
if (_vm->getGameType() == GType_RexNebular) {
- delete walkData;
-
+ assert(depthSurface);
stream = sceneInfo.getItemStream(1);
- walkData = (byte *)malloc(stream->size());
+ byte *walkData = (byte *)malloc(stream->size());
stream->read(walkData, stream->size());
+
+ // For Rex Nebular, the walk areas are part of the scene info
+ byte *destP = depthSurface->getBasePtr(0, 0);
+ const byte *srcP = walkData;
+ byte runLength;
+ while ((runLength = *srcP++) != 0) {
+ Common::set_to(destP, destP + runLength, *srcP++);
+ destP += runLength;
+ }
+
+ delete walkData;
+ delete stream;
}
- _vm->_resourceManager->toss(sceneInfoStr);
+ _vm->_resourceManager->toss(sceneName);
+
+ // Load the surface artwork
+ surface->loadBackground(sceneNumber);
+
+ // Final cleanup
+ if (ssFlag)
+ delete surface;
+ if (dsFlag)
+ delete depthSurface;
}
+
/*--------------------------------------------------------------------------*/
+/*--------------------------------------------------------------------------
+ * MadsInterfaceView handles the user interface section at the bottom of
+ * game screens in MADS games
+ *--------------------------------------------------------------------------
+ */
+
+MadsInterfaceView::MadsInterfaceView(MadsM4Engine *vm): GameInterfaceView(vm,
+ Common::Rect(0, MADS_SURFACE_HEIGHT, vm->_screen->width(), vm->_screen->height())) {
+ _screenType = VIEWID_INTERFACE;
+ _highlightedElement = -1;
+ _topIndex = 0;
+ _selectedObject = -1;
+ _cheatKeyCtr = 0;
+
+ _objectSprites = NULL;
+ _objectPalData = NULL;
+
+ /* Set up the rect list for screen elements */
+ // Actions
+ for (int i = 0; i < 10; ++i)
+ _screenObjects.addRect((i / 5) * 32 + 1, (i % 5) * 8 + MADS_SURFACE_HEIGHT + 2,
+ ((i / 5) + 1) * 32 + 3, ((i % 5) + 1) * 8 + MADS_SURFACE_HEIGHT + 2);
+
+ // Scroller elements (up arrow, scroller, down arrow)
+ _screenObjects.addRect(73, 160, 82, 167);
+ _screenObjects.addRect(73, 168, 82, 190);
+ _screenObjects.addRect(73, 191, 82, 198);
+
+ // Inventory object names
+ for (int i = 0; i < 5; ++i)
+ _screenObjects.addRect(89, 158 + i * 8, 160, 166 + i * 8);
+
+ // Full rectangle area for all vocab actions
+ for (int i = 0; i < 5; ++i)
+ _screenObjects.addRect(239, 158 + i * 8, 320, 166 + i * 8);
+}
+
+MadsInterfaceView::~MadsInterfaceView() {
+ delete _objectSprites;
+}
+
+void MadsInterfaceView::setFontMode(InterfaceFontMode newMode) {
+ switch (newMode) {
+ case ITEM_NORMAL:
+ _vm->_font->current()->setColours(4, 4, 0xff);
+ break;
+ case ITEM_HIGHLIGHTED:
+ _vm->_font->current()->setColours(5, 5, 0xff);
+ break;
+ case ITEM_SELECTED:
+ _vm->_font->current()->setColours(6, 6, 0xff);
+ break;
+ }
+}
+
+void MadsInterfaceView::initialise() {
+ // Build up the inventory list
+ _inventoryList.clear();
+
+ for (uint i = 0; i < _madsVm->globals()->getObjectsSize(); ++i) {
+ MadsObject *obj = _madsVm->globals()->getObject(i);
+ if (obj->roomNumber == PLAYER_INVENTORY)
+ _inventoryList.push_back(i);
+ }
+
+ // If the inventory has at least one object, select it
+ if (_inventoryList.size() > 0)
+ setSelectedObject(_inventoryList[0]);
+}
+
+void MadsInterfaceView::setSelectedObject(int objectNumber) {
+ char resName[80];
+
+ // Load inventory resource
+ if (_objectSprites) {
+ _vm->_palette->deleteRange(_objectPalData);
+ delete _objectSprites;
+ }
+
+ // Check to make sure the object is in the inventory, and also visible on-screen
+ int idx = _inventoryList.indexOf(objectNumber);
+ if (idx == -1) {
+ // Object wasn't found, so return
+ _selectedObject = -1;
+ return;
+ }
+
+ // Found the object
+ if (idx < _topIndex)
+ _topIndex = idx;
+ else if (idx >= (_topIndex + 5))
+ _topIndex = MAX(0, idx - 4);
+
+ _selectedObject = objectNumber;
+ sprintf(resName, "*OB%.3dI.SS", objectNumber);
+
+ Common::SeekableReadStream *data = _vm->res()->get(resName);
+ _objectSprites = new SpriteAsset(_vm, data, data->size(), resName);
+ _vm->res()->toss(resName);
+
+ // Slot it into available palette space
+ _objectPalData = _objectSprites->getRgbList();
+ _vm->_palette->addRange(_objectPalData);
+ _objectSprites->translate(_objectPalData, true);
+
+ _objectFrameNumber = 0;
+}
+
+void MadsInterfaceView::addObjectToInventory(int objectNumber) {
+ if (_inventoryList.indexOf(objectNumber) == -1) {
+ _madsVm->globals()->getObject(objectNumber)->roomNumber = PLAYER_INVENTORY;
+ _inventoryList.push_back(objectNumber);
+ }
+
+ setSelectedObject(objectNumber);
+}
+
+void MadsInterfaceView::onRefresh(RectList *rects, M4Surface *destSurface) {
+ _vm->_font->setFont(FONT_INTERFACE_MADS);
+ char buffer[100];
+
+ // Check to see if any dialog is currently active
+ bool dialogVisible = _vm->_viewManager->getView(LAYER_DIALOG) != NULL;
+
+ // Highlighting logic for action list
+ int actionIndex = 0;
+ for (int x = 0; x < 2; ++x) {
+ for (int y = 0; y < 5; ++y, ++actionIndex) {
+ // Determine the font colour depending on whether an item is selected. Note that the first action,
+ // 'Look', is always 'selected', even when another action is clicked on
+ setFontMode((_highlightedElement == actionIndex) ? ITEM_HIGHLIGHTED :
+ ((actionIndex == 0) ? ITEM_SELECTED : ITEM_NORMAL));
+
+ // Get the verb action and capitalise it
+ const char *verbStr = _madsVm->globals()->getVocab(kVerbLook + actionIndex);
+ strcpy(buffer, verbStr);
+ if ((buffer[0] >= 'a') && (buffer[0] <= 'z')) buffer[0] -= 'a' - 'A';
+
+ // Display the verb
+ const Common::Rect r(_screenObjects[actionIndex]);
+ _vm->_font->current()->writeString(destSurface, buffer, r.left, r.top, r.width(), 0);
+ }
+ }
+
+ // Check for highlighting of the scrollbar controls
+ if ((_highlightedElement == SCROLL_UP) || (_highlightedElement == SCROLL_SCROLLER) || (_highlightedElement == SCROLL_DOWN)) {
+ // Highlight the control's borders
+ const Common::Rect r(_screenObjects[_highlightedElement]);
+ destSurface->frameRect(r, 5);
+ }
+
+ // Draw the horizontal line in the scroller representing the current top selected
+ const Common::Rect scroller(_screenObjects[SCROLL_SCROLLER]);
+ int yP = (_inventoryList.size() < 2) ? 0 : (scroller.height() - 5) * _topIndex / (_inventoryList.size() - 1);
+ destSurface->setColor(4);
+ destSurface->hLine(scroller.left + 2, scroller.right - 3, scroller.top + 2 + yP);
+
+ // List inventory items
+ for (uint i = 0; i < 5; ++i) {
+ if ((_topIndex + i) >= _inventoryList.size())
+ break;
+
+ const char *descStr = _madsVm->globals()->getVocab(_madsVm->globals()->getObject(
+ _inventoryList[_topIndex + i])->descId);
+ strcpy(buffer, descStr);
+ if ((buffer[0] >= 'a') && (buffer[0] <= 'z')) buffer[0] -= 'a' - 'A';
+
+ const Common::Rect r(_screenObjects[INVLIST_START + i]);
+
+ // Set the highlighting of the inventory item
+ if (_highlightedElement == (int)(INVLIST_START + i)) setFontMode(ITEM_HIGHLIGHTED);
+ else if (_selectedObject == _inventoryList[_topIndex + i]) setFontMode(ITEM_SELECTED);
+ else setFontMode(ITEM_NORMAL);
+
+ // Write out it's description
+ _vm->_font->current()->writeString(destSurface, buffer, r.left, r.top, r.width(), 0);
+ }
+
+ // Handle the display of any currently selected object
+ if (_objectSprites) {
+ // Display object sprite. Note that the frame number isn't used directly, because it would result
+ // in too fast an animation
+ M4Sprite *spr = _objectSprites->getFrame(_objectFrameNumber / INV_ANIM_FRAME_SPEED);
+ spr->copyTo(destSurface, INVENTORY_X, INVENTORY_Y, 0);
+
+ if (!_madsVm->globals()->_config.invObjectsStill && !dialogVisible) {
+ // If objects need to be animated, move to the next frame
+ if (++_objectFrameNumber >= (_objectSprites->getCount() * INV_ANIM_FRAME_SPEED))
+ _objectFrameNumber = 0;
+ }
+
+ // List the vocab actions for the currently selected object
+ MadsObject *obj = _madsVm->globals()->getObject(_selectedObject);
+ int yIndex = MIN(_highlightedElement - VOCAB_START, obj->vocabCount - 1);
+
+ for (int i = 0; i < obj->vocabCount; ++i) {
+ const Common::Rect r(_screenObjects[VOCAB_START + i]);
+
+ // Get the vocab description and capitalise it
+ const char *descStr = _madsVm->globals()->getVocab(obj->vocabList[i].vocabId);
+ strcpy(buffer, descStr);
+ if ((buffer[0] >= 'a') && (buffer[0] <= 'z')) buffer[0] -= 'a' - 'A';
+
+ // Set the highlighting and display the entry
+ setFontMode((i == yIndex) ? ITEM_HIGHLIGHTED : ITEM_NORMAL);
+ _vm->_font->current()->writeString(destSurface, buffer, r.left, r.top, r.width(), 0);
+ }
+ }
+}
+
+bool MadsInterfaceView::onEvent(M4EventType eventType, int32 param1, int x, int y, bool &captureEvents) {
+ MadsAction &act = _madsVm->scene()->getAction();
+
+ // If the mouse isn't being held down, then reset the repeated scroll timer
+ if (eventType != MEVENT_LEFT_HOLD)
+ _nextScrollerTicks = 0;
+
+ // Handle various event types
+ switch (eventType) {
+ case MEVENT_MOVE:
+ // If the cursor isn't in "wait mode", don't do any processing
+ if (_vm->_mouse->getCursorNum() == CURSOR_WAIT)
+ return true;
+
+ // Ensure the cursor is the standard arrow
+ _vm->_mouse->setCursorNum(CURSOR_ARROW);
+
+ // Check if any interface element is currently highlighted
+ _highlightedElement = _screenObjects.find(Common::Point(x, y));
+
+ return true;
+
+ case MEVENT_LEFT_CLICK:
+ // Left mouse click
+ {
+ // Check if an inventory object was selected
+ if ((_highlightedElement >= INVLIST_START) && (_highlightedElement < (INVLIST_START + 5))) {
+ // Ensure there is an inventory item listed in that cell
+ uint idx = _highlightedElement - INVLIST_START;
+ if ((_topIndex + idx) < _inventoryList.size()) {
+ // Set the selected object
+ setSelectedObject(_inventoryList[_topIndex + idx]);
+ }
+ } else if ((_highlightedElement >= ACTIONS_START) && (_highlightedElement < (ACTIONS_START + 10))) {
+ // A standard action was selected
+ int verbId = kVerbLook + (_highlightedElement - ACTIONS_START);
+ warning("Selected action #%d", verbId);
+
+ } else if ((_highlightedElement >= VOCAB_START) && (_highlightedElement < (VOCAB_START + 5))) {
+ // A vocab action was selected
+ MadsObject *obj = _madsVm->globals()->getObject(_selectedObject);
+ int vocabIndex = MIN(_highlightedElement - VOCAB_START, obj->vocabCount - 1);
+ if (vocabIndex >= 0) {
+ act._actionMode = ACTMODE_OBJECT;
+ act._actionMode2 = ACTMODE2_2;
+ act._flags1 = obj->vocabList[1].flags1;
+ act._flags2 = obj->vocabList[1].flags2;
+
+ act._currentHotspot = _selectedObject;
+ act._articleNumber = act._flags2;
+ }
+ }
+ }
+ return true;
+
+ case MEVENT_LEFT_HOLD:
+ // Left mouse hold
+ // Handle the scroller - the up/down buttons allow for multiple actions whilst the mouse is held down
+ if ((_highlightedElement == SCROLL_UP) || (_highlightedElement == SCROLL_DOWN)) {
+ if ((_nextScrollerTicks == 0) || (g_system->getMillis() >= _nextScrollerTicks)) {
+ // Handle scroll up/down action
+ _nextScrollerTicks = g_system->getMillis() + SCROLLER_DELAY;
+
+ if ((_highlightedElement == SCROLL_UP) && (_topIndex > 0))
+ --_topIndex;
+ if ((_highlightedElement == SCROLL_DOWN) && (_topIndex < (int)(_inventoryList.size() - 1)))
+ ++_topIndex;
+ }
+ }
+ return true;
+
+ case MEVENT_LEFT_DRAG:
+ // Left mouse drag
+ // Handle the the the scroller area that can be dragged to adjust the top displayed index
+ if (_highlightedElement == SCROLL_SCROLLER) {
+ // Calculate the new top index based on the Y position
+ const Common::Rect r(_screenObjects[SCROLL_SCROLLER]);
+ _topIndex = CLIP((int)(_inventoryList.size() - 1) * (y - r.top - 2) / (r.height() - 5),
+ 0, (int)_inventoryList.size() - 1);
+ }
+ return true;
+
+ case KEVENT_KEY:
+ if (_cheatKeyCtr == CHEAT_SEQUENCE_MAX)
+ handleCheatKey(param1);
+ handleKeypress(param1);
+ return true;
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+bool MadsInterfaceView::handleCheatKey(int32 keycode) {
+ switch (keycode) {
+ case Common::KEYCODE_SPACE:
+ // TODO: Move player to current destination
+ return true;
+
+ case Common::KEYCODE_t | (Common::KEYCODE_LALT):
+ case Common::KEYCODE_t | (Common::KEYCODE_RALT):
+ {
+ // Teleport to room
+ //Scene *sceneView = (Scene *)vm->_viewManager->getView(VIEWID_SCENE);
+
+
+ return true;
+ }
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+const char *CHEAT_SEQUENCE = "widepipe";
+
+bool MadsInterfaceView::handleKeypress(int32 keycode) {
+ int flags = keycode >> 24;
+ int kc = keycode & 0xffff;
+
+ // Capitalise the letter if necessary
+ if (_cheatKeyCtr < CHEAT_SEQUENCE_MAX) {
+ if ((flags & Common::KBD_CTRL) && (kc == CHEAT_SEQUENCE[_cheatKeyCtr])) {
+ ++_cheatKeyCtr;
+ if (_cheatKeyCtr == CHEAT_SEQUENCE_MAX)
+ Dialog::display(_vm, 22, cheatingEnabledDesc);
+ return true;
+ } else {
+ _cheatKeyCtr = 0;
+ }
+ }
+
+ // Handle the various keys
+ if ((keycode == Common::KEYCODE_ESCAPE) || (keycode == Common::KEYCODE_F1)) {
+ // Game menu
+ _madsVm->globals()->dialogType = DIALOG_GAME_MENU;
+ leaveScene();
+ return false;
+ } else if (flags & Common::KBD_CTRL) {
+ // Handling of the different control key combinations
+ switch (kc) {
+ case Common::KEYCODE_i:
+ // Mouse to inventory
+ warning("TODO: Mouse to inventory");
+ break;
+
+ case Common::KEYCODE_k:
+ // Toggle hotspots
+ warning("TODO: Toggle hotspots");
+ break;
+
+ case Common::KEYCODE_p:
+ // Player stats
+ warning("TODO: Player stats");
+ break;
+
+ case Common::KEYCODE_q:
+ // Quit game
+ break;
+
+ case Common::KEYCODE_s:
+ // Activate sound
+ warning("TODO: Activate sound");
+ break;
+
+ case Common::KEYCODE_u:
+ // Rotate player
+ warning("TODO: Rotate player");
+ break;
+
+ case Common::KEYCODE_v: {
+ // Release version
+ Dialog *dlg = new Dialog(_vm, GameReleaseInfoStr, GameReleaseTitleStr);
+ _vm->_viewManager->addView(dlg);
+ _vm->_viewManager->moveToFront(dlg);
+ return false;
+ }
+
+ default:
+ break;
+ }
+ } else if ((flags & Common::KBD_ALT) && (kc == Common::KEYCODE_q)) {
+ // Quit Game
+
+ } else {
+ // Standard keypresses
+ switch (kc) {
+ case Common::KEYCODE_F2:
+ // Save game
+ _madsVm->globals()->dialogType = DIALOG_SAVE;
+ leaveScene();
+ break;
+ case Common::KEYCODE_F3:
+ // Restore game
+ _madsVm->globals()->dialogType = DIALOG_RESTORE;
+ leaveScene();
+ break;
+ }
+ }
+//DIALOG_OPTIONS
+ return false;
+}
+
+void MadsInterfaceView::leaveScene() {
+ // Close the scene
+ View *view = _madsVm->_viewManager->getView(VIEWID_SCENE);
+ _madsVm->_viewManager->deleteView(view);
+}
+
} // End of namespace M4
diff --git a/engines/m4/mads_scene.h b/engines/m4/mads_scene.h
index c8a0da3aea..0269de75c8 100644
--- a/engines/m4/mads_scene.h
+++ b/engines/m4/mads_scene.h
@@ -33,27 +33,24 @@
namespace M4 {
#define INTERFACE_HEIGHT 106
-
+class MadsInterfaceView;
class MadsSceneResources: public SceneResources {
public:
int sceneId;
int artFileNum;
- int field_4;
+ int drawStyle;
int width;
int height;
-
- int objectCount;
- MadsObject objects[32];
+ Common::Array<MadsObject> objects;
+ Common::Array<Common::String> setNames;
- int walkSize;
- byte *walkData;
Common::Point playerPos;
int playerDir;
- MadsSceneResources() { walkSize = 0; walkData = NULL; playerDir = 0; }
- ~MadsSceneResources() { delete walkData; }
- void load(int sceneId);
+ MadsSceneResources() { playerDir = 0; }
+ ~MadsSceneResources() {}
+ void load(int sceneId, const char *resName, int v0, M4Surface *depthSurface, M4Surface *surface);
};
enum MadsActionMode {ACTMODE_NONE = 0, ACTMODE_VERB = 1, ACTMODE_OBJECT = 3, ACTMODE_TALK = 6};
@@ -96,6 +93,7 @@ private:
MadsEngine *_vm;
MadsSceneResources _sceneResources;
MadsAction _action;
+ Animation *_activeAnimation;
MadsSceneLogic _sceneLogic;
SpriteAsset *_playerSprites;
@@ -130,6 +128,7 @@ public:
int loadSceneSpriteSet(const char *setName);
void loadPlayerSprites(const char *prefix);
void showMADSV2TextBox(char *text, int x, int y, char *faceName);
+ void loadAnimation(const Common::String &animName, int v0);
MadsInterfaceView *getInterface() { return (MadsInterfaceView *)_interfaceSurface; }
MadsSceneResources &getSceneResources() { return _sceneResources; }
@@ -137,6 +136,56 @@ public:
void setStatusText(const char *text) {}//***DEPRECATED***
};
+#define CHEAT_SEQUENCE_MAX 8
+
+class IntegerList : public Common::Array<int> {
+public:
+ int indexOf(int v) {
+ for (uint i = 0; i < size(); ++i)
+ if (operator [](i) == v)
+ return i;
+ return -1;
+ }
+};
+
+enum InterfaceFontMode {ITEM_NORMAL, ITEM_HIGHLIGHTED, ITEM_SELECTED};
+
+enum InterfaceObjects {ACTIONS_START = 0, SCROLL_UP = 10, SCROLL_SCROLLER = 11, SCROLL_DOWN = 12,
+ INVLIST_START = 13, VOCAB_START = 18};
+
+class MadsInterfaceView : public GameInterfaceView {
+private:
+ IntegerList _inventoryList;
+ RectList _screenObjects;
+ int _highlightedElement;
+ int _topIndex;
+ uint32 _nextScrollerTicks;
+ int _cheatKeyCtr;
+
+ // Object display fields
+ int _selectedObject;
+ SpriteAsset *_objectSprites;
+ RGBList *_objectPalData;
+ int _objectFrameNumber;
+
+ void setFontMode(InterfaceFontMode newMode);
+ bool handleCheatKey(int32 keycode);
+ bool handleKeypress(int32 keycode);
+ void leaveScene();
+public:
+ MadsInterfaceView(MadsM4Engine *vm);
+ ~MadsInterfaceView();
+
+ virtual void initialise();
+ virtual void setSelectedObject(int objectNumber);
+ virtual void addObjectToInventory(int objectNumber);
+ int getSelectedObject() { return _selectedObject; }
+ int getInventoryObject(int objectIndex) { return _inventoryList[objectIndex]; }
+
+ void onRefresh(RectList *rects, M4Surface *destSurface);
+ bool onEvent(M4EventType eventType, int32 param1, int x, int y, bool &captureEvents);
+};
+
} // End of namespace M4
#endif
diff --git a/engines/m4/mads_views.cpp b/engines/m4/mads_views.cpp
index 7a71509041..c7b4f76a00 100644
--- a/engines/m4/mads_views.cpp
+++ b/engines/m4/mads_views.cpp
@@ -24,6 +24,7 @@
*/
#include "m4/m4_views.h"
+#include "m4/animation.h"
#include "m4/dialogs.h"
#include "m4/events.h"
#include "m4/font.h"
@@ -36,10 +37,19 @@
namespace M4 {
-static const int INV_ANIM_FRAME_SPEED = 2;
-static const int INVENTORY_X = 160;
-static const int INVENTORY_Y = 159;
-static const int SCROLLER_DELAY = 200;
+bool MadsSpriteSlot::operator==(const SpriteSlotSubset &other) const {
+ return (spriteListIndex == other.spriteListIndex) && (frameNumber == other.frameNumber) &&
+ (xp == other.xp) && (yp == other.yp) && (depth == other.depth) && (scale == other.scale);
+}
+
+void MadsSpriteSlot::copy(const SpriteSlotSubset &other) {
+ spriteListIndex = other.spriteListIndex;
+ frameNumber = other.frameNumber;
+ xp = other.xp;
+ yp = other.yp;
+ depth = other.depth;
+ scale = other.scale;
+}
//--------------------------------------------------------------------------
@@ -52,14 +62,21 @@ MadsSpriteSlots::MadsSpriteSlots(MadsView &owner): _owner(owner) {
startIndex = 0;
}
+MadsSpriteSlots::~MadsSpriteSlots() {
+ for (uint i = 0; i < _sprites.size(); ++i)
+ delete _sprites[i];
+}
+
void MadsSpriteSlots::clear() {
_owner._textDisplay.clear();
+ for (uint i = 0; i < _sprites.size(); ++i)
+ delete _sprites[i];
_sprites.clear();
// Reset the sprite slots list back to a single entry for a full screen refresh
startIndex = 1;
_entries[0].spriteType = FULL_SCREEN_REFRESH;
- _entries[0].timerIndex = -1;
+ _entries[0].seqIndex = -1;
}
int MadsSpriteSlots::getIndex() {
@@ -74,20 +91,31 @@ int MadsSpriteSlots::addSprites(const char *resName) {
Common::SeekableReadStream *data = _vm->res()->get(resName);
SpriteAsset *spriteSet = new SpriteAsset(_vm, data, data->size(), resName);
spriteSet->translate(_madsVm->_palette);
+ assert(spriteSet != NULL);
- _sprites.push_back(SpriteList::value_type(spriteSet));
+ _sprites.push_back(spriteSet);
_vm->res()->toss(resName);
return _sprites.size() - 1;
}
+void MadsSpriteSlots::deleteSprites(int listIndex) {
+ if (listIndex < 0)
+ return;
+
+ delete _sprites[listIndex];
+ _sprites[listIndex] = NULL;
+ if (listIndex == ((int)_sprites.size() - 1))
+ _sprites.remove_at(listIndex);
+}
+
/*
* Deletes the sprite slot with the given timer entry
*/
-void MadsSpriteSlots::deleteTimer(int timerIndex) {
+void MadsSpriteSlots::deleteTimer(int seqIndex) {
for (int idx = 0; idx < startIndex; ++idx) {
- if (_entries[idx].timerIndex == timerIndex)
- _entries[idx].spriteType = -1;
+ if (_entries[idx].seqIndex == seqIndex)
+ _entries[idx].spriteType = EXPIRED_SPRITE;
}
}
@@ -128,10 +156,10 @@ void MadsSpriteSlots::drawBackground() {
if (_entries[i].depth <= 1) {
// No depth, so simply copy the frame onto the background
- frame->copyTo(_owner._bgSurface, xp, yp);
+ frame->copyTo(_owner._bgSurface, xp, yp, 0);
} else {
// Depth was specified, so draw frame using scene's depth information
- frame->copyTo(_owner._bgSurface, xp, yp, _entries[i].depth, _owner._depthSurface, 100);
+ frame->copyTo(_owner._bgSurface, xp, yp, _entries[i].depth, _owner._depthSurface, 100, 0);
}
}
}
@@ -142,7 +170,7 @@ void MadsSpriteSlots::drawBackground() {
_owner._dirtyAreas[i].active = false;
}
-void MadsSpriteSlots::drawForeground(View *view) {
+void MadsSpriteSlots::drawForeground(View *view, int yOffset) {
DepthList depthList;
// Get a list of sprite object depths for active objects
@@ -162,13 +190,13 @@ void MadsSpriteSlots::drawForeground(View *view) {
DepthEntry &de = *i;
MadsSpriteSlot &slot = _entries[de.index];
assert(slot.spriteListIndex < (int)_sprites.size());
- SpriteAsset &spriteSet = *_sprites[slot.spriteListIndex].get();
+ SpriteAsset &spriteSet = *_sprites[slot.spriteListIndex];
if (slot.scale < 100) {
// Minimalised drawing
assert(slot.spriteListIndex < (int)_sprites.size());
- M4Sprite *spr = spriteSet.getFrame(slot.frameNumber - 1);
- spr->copyTo(view, slot.xp, slot.yp, slot.depth, _owner._depthSurface, slot.scale, 0);
+ M4Sprite *spr = spriteSet.getFrame((slot.frameNumber & 0x7fff) - 1);
+ spr->copyTo(view, slot.xp, slot.yp + yOffset, slot.depth, _owner._depthSurface, slot.scale, 0);
} else {
int xp, yp;
M4Sprite *spr = spriteSet.getFrame(slot.frameNumber - 1);
@@ -183,10 +211,10 @@ void MadsSpriteSlots::drawForeground(View *view) {
if (slot.depth > 1) {
// Draw the frame with depth processing
- spr->copyTo(view, xp, yp, slot.depth, _owner._depthSurface, 100, 0);
+ spr->copyTo(view, xp, yp + yOffset, slot.depth, _owner._depthSurface, 100, 0);
} else {
// No depth, so simply draw the image
- spr->copyTo(view, xp, yp, 0);
+ spr->copyTo(view, xp, yp + yOffset, 0);
}
}
}
@@ -204,6 +232,16 @@ void MadsSpriteSlots::setDirtyAreas() {
}
/**
+ * Flags the entire screen to be redrawn during the next drawing cycle
+ */
+void MadsSpriteSlots::fullRefresh() {
+ int idx = getIndex();
+
+ _entries[idx].spriteType = FULL_SCREEN_REFRESH;
+ _entries[idx].seqIndex = -1;
+}
+
+/**
* Removes any sprite slots that are no longer needed
*/
void MadsSpriteSlots::cleanUp() {
@@ -288,13 +326,12 @@ void MadsTextDisplay::setDirtyAreas2() {
}
}
-void MadsTextDisplay::draw(View *view) {
+void MadsTextDisplay::draw(View *view, int yOffset) {
for (uint idx = 0; idx < _entries.size(); ++idx) {
if (_entries[idx].active && (_entries[idx].expire >= 0)) {
- _entries[idx].font->setColours(_entries[idx].colour1,
- (_entries[idx].colour2 == 0) ? _entries[idx].colour1 : _entries[idx].colour2, 0xff);
+ _entries[idx].font->setColours(_entries[idx].colour1, _entries[idx].colour2, 0);
_entries[idx].font->writeString(view, _entries[idx].msg,
- _entries[idx].bounds.left, _entries[idx].bounds.top, _entries[idx].bounds.width(),
+ _entries[idx].bounds.left, _entries[idx].bounds.top + yOffset, _entries[idx].bounds.width(),
_entries[idx].spacing);
}
}
@@ -341,13 +378,13 @@ void MadsKernelMessageList::clear() {
_talkFont = _vm->_font->getFont(FONT_CONVERSATION_MADS);
}
-int MadsKernelMessageList::add(const Common::Point &pt, uint fontColour, uint8 flags, uint8 v2, uint32 timeout, const char *msg) {
+int MadsKernelMessageList::add(const Common::Point &pt, uint fontColour, uint8 flags, uint8 abortTimers, uint32 timeout, const char *msg) {
// Find a free slot
uint idx = 0;
while ((idx < _entries.size()) && ((_entries[idx].flags & KMSG_ACTIVE) != 0))
++idx;
if (idx == _entries.size()) {
- if (v2 == 0)
+ if (abortTimers == 0)
return -1;
error("MadsKernelList overflow");
@@ -362,28 +399,28 @@ int MadsKernelMessageList::add(const Common::Point &pt, uint fontColour, uint8 f
rec.textDisplayIndex = -1;
rec.timeout = timeout;
rec.frameTimer = _madsVm->_currentTimer;
- rec.field_1C = v2;
+ rec.abortTimers = abortTimers;
rec.abortMode = _owner._abortTimersMode2;
for (int i = 0; i < 3; ++i)
rec.actionNouns[i] = _madsVm->scene()->actionNouns[i];
- if (flags & KMSG_2)
+ if (flags & KMSG_OWNER_TIMEOUT)
rec.frameTimer = _owner._ticksAmount + _owner._newTimeout;
return idx;
}
-int MadsKernelMessageList::addQuote(int quoteId, int v2, uint32 timeout) {
+int MadsKernelMessageList::addQuote(int quoteId, int abortTimers, uint32 timeout) {
const char *quoteStr = _madsVm->globals()->getQuote(quoteId);
- return add(Common::Point(0, 0), 0x1110, KMSG_2 | KMSG_20, v2, timeout, quoteStr);
+ return add(Common::Point(0, 0), 0x1110, KMSG_OWNER_TIMEOUT | KMSG_CENTER_ALIGN, abortTimers, timeout, quoteStr);
}
-void MadsKernelMessageList::unk1(int msgIndex, int numTicks, int v2) {
+void MadsKernelMessageList::scrollMessage(int msgIndex, int numTicks, bool quoted) {
if (msgIndex < 0)
return;
- _entries[msgIndex].flags |= (v2 == 0) ? KMSG_8 : (KMSG_8 | KMSG_1);
+ _entries[msgIndex].flags |= quoted ? (KMSG_SCROLL | KMSG_QUOTED) : KMSG_SCROLL;
_entries[msgIndex].msgOffset = 0;
_entries[msgIndex].numTicks = numTicks;
_entries[msgIndex].frameTimer2 = _madsVm->_currentTimer;
@@ -392,7 +429,7 @@ void MadsKernelMessageList::unk1(int msgIndex, int numTicks, int v2) {
_entries[msgIndex].asciiChar = *msgP;
_entries[msgIndex].asciiChar2 = *(msgP + 1);
- if (_entries[msgIndex].flags & KMSG_2)
+ if (_entries[msgIndex].flags & KMSG_OWNER_TIMEOUT)
_entries[msgIndex].frameTimer2 = _owner._ticksAmount + _owner._newTimeout;
_entries[msgIndex].frameTimer = _entries[msgIndex].frameTimer2;
@@ -400,7 +437,7 @@ void MadsKernelMessageList::unk1(int msgIndex, int numTicks, int v2) {
void MadsKernelMessageList::setSeqIndex(int msgIndex, int seqIndex) {
if (msgIndex >= 0) {
- _entries[msgIndex].flags |= KMSG_4;
+ _entries[msgIndex].flags |= KMSG_SEQ_ENTRY;
_entries[msgIndex].sequenceIndex = seqIndex;
}
}
@@ -409,9 +446,9 @@ void MadsKernelMessageList::remove(int msgIndex) {
MadsKernelMessageEntry &rec = _entries[msgIndex];
if (rec.flags & KMSG_ACTIVE) {
- if (rec.flags & KMSG_8) {
- //*(rec.msg + rec.msgOffset) = rec.asciiChar;
- //*(rec.msg + rec.msgOffset + 1) = rec.asciiChar2;
+ if (rec.flags & KMSG_SCROLL) {
+ *(rec.msg + rec.msgOffset) = rec.asciiChar;
+ *(rec.msg + rec.msgOffset + 1) = rec.asciiChar2;
}
if (rec.textDisplayIndex >= 0)
@@ -442,26 +479,26 @@ void MadsKernelMessageList::processText(int msgIndex) {
uint32 currentTimer = _madsVm->_currentTimer;
bool flag = false;
- if ((msg.flags & KMSG_40) != 0) {
+ if ((msg.flags & KMSG_EXPIRE) != 0) {
_owner._textDisplay.expire(msg.textDisplayIndex);
msg.flags &= !KMSG_ACTIVE;
return;
}
- if ((msg.flags & KMSG_8) == 0) {
+ if ((msg.flags & KMSG_SCROLL) == 0) {
msg.timeout -= 3;
}
- if (msg.flags & KMSG_4) {
+ if (msg.flags & KMSG_SEQ_ENTRY) {
MadsSequenceEntry &seqEntry = _owner._sequenceList[msg.sequenceIndex];
if (seqEntry.doneFlag || !seqEntry.active)
msg.timeout = 0;
}
if ((msg.timeout <= 0) && (_owner._abortTimers == 0)) {
- msg.flags |= KMSG_40;
- if (msg.field_1C != 0) {
- _owner._abortTimers = msg.field_1C;
+ msg.flags |= KMSG_EXPIRE;
+ if (msg.abortTimers != 0) {
+ _owner._abortTimers = msg.abortTimers;
_owner._abortTimersMode = msg.abortMode;
if (_owner._abortTimersMode != ABORTMODE_1) {
@@ -474,9 +511,9 @@ void MadsKernelMessageList::processText(int msgIndex) {
msg.frameTimer = currentTimer + 3;
int x1 = 0, y1 = 0;
- if (msg.flags & KMSG_4) {
+ if (msg.flags & KMSG_SEQ_ENTRY) {
MadsSequenceEntry &seqEntry = _owner._sequenceList[msg.sequenceIndex];
- if (seqEntry.field_12) {
+ if (!seqEntry.nonFixed) {
SpriteAsset &spriteSet = _owner._spriteSlots.getSprite(seqEntry.spriteListIndex);
M4Sprite *frame = spriteSet.getFrame(seqEntry.frameIndex - 1);
x1 = frame->bounds().left;
@@ -487,7 +524,7 @@ void MadsKernelMessageList::processText(int msgIndex) {
}
}
- if (msg.flags & KMSG_2) {
+ if (msg.flags & KMSG_OWNER_TIMEOUT) {
if (word_8469E != 0) {
// TODO: Figure out various flags
} else {
@@ -499,7 +536,7 @@ void MadsKernelMessageList::processText(int msgIndex) {
x1 += msg.position.x;
y1 += msg.position.y;
- if ((msg.flags & KMSG_8) && (msg.frameTimer >= currentTimer)) {
+ if ((msg.flags & KMSG_SCROLL) && (msg.frameTimer >= currentTimer)) {
msg.msg[msg.msgOffset] = msg.asciiChar;
char *msgP = &msg.msg[++msg.msgOffset];
*msgP = msg.asciiChar2;
@@ -508,9 +545,10 @@ void MadsKernelMessageList::processText(int msgIndex) {
msg.asciiChar2 = *(msgP + 1);
if (!msg.asciiChar) {
+ // End of message
*msgP = '\0';
- msg.flags &= ~KMSG_8;
- } else if (msg.flags & KMSG_1) {
+ msg.flags &= ~KMSG_SCROLL;
+ } else if (msg.flags & KMSG_QUOTED) {
*msgP = '"';
*(msgP + 1) = '\0';
}
@@ -521,8 +559,8 @@ void MadsKernelMessageList::processText(int msgIndex) {
int strWidth = _talkFont->getWidth(msg.msg, _owner._textSpacing);
- if (msg.flags & KMSG_30) {
- x1 -= (msg.flags & KMSG_20) ? strWidth / 2 : strWidth;
+ if (msg.flags & (KMSG_RIGHT_ALIGN | KMSG_CENTER_ALIGN)) {
+ x1 -= (msg.flags & KMSG_CENTER_ALIGN) ? strWidth / 2 : strWidth;
}
// Make sure text appears entirely on-screen
@@ -817,10 +855,10 @@ void MadsDirtyAreas::mergeAreas(int idx1, int idx2) {
da1.textActive = true;
}
-void MadsDirtyAreas::copy(M4Surface *dest, M4Surface *src) {
+void MadsDirtyAreas::copy(M4Surface *dest, M4Surface *src, int yOffset) {
for (uint i = 0; i < _entries.size(); ++i) {
if (_entries[i].active && _entries[i].bounds.isValidRect())
- src->copyTo(dest, _entries[i].bounds, _entries[i].bounds.left, _entries[i].bounds.top);
+ src->copyTo(dest, _entries[i].bounds, _entries[i].bounds.left, _entries[i].bounds.top + yOffset);
}
}
@@ -855,14 +893,14 @@ bool MadsSequenceList::addSubEntry(int index, SequenceSubEntryMode mode, int fra
}
int MadsSequenceList::add(int spriteListIndex, int v0, int frameIndex, int triggerCountdown, int delayTicks, int extraTicks, int numTicks,
- int msgX, int msgY, char field_12, char scale, uint8 depth, int frameInc, SpriteAnimType animType, int numSprites,
+ int msgX, int msgY, bool nonFixed, char scale, uint8 depth, int frameInc, SpriteAnimType animType, int numSprites,
int frameStart) {
// Find a free slot
- uint timerIndex = 0;
- while ((timerIndex < _entries.size()) && (_entries[timerIndex].active))
- ++timerIndex;
- if (timerIndex == _entries.size())
+ uint seqIndex = 0;
+ while ((seqIndex < _entries.size()) && (_entries[seqIndex].active))
+ ++seqIndex;
+ if (seqIndex == _entries.size())
error("TimerList full");
if (frameStart <= 0)
@@ -873,76 +911,76 @@ int MadsSequenceList::add(int spriteListIndex, int v0, int frameIndex, int trigg
frameInc = 0;
// Set the list entry fields
- _entries[timerIndex].active = true;
- _entries[timerIndex].spriteListIndex = spriteListIndex;
- _entries[timerIndex].field_2 = v0;
- _entries[timerIndex].frameIndex = frameIndex;
- _entries[timerIndex].frameStart = frameStart;
- _entries[timerIndex].numSprites = numSprites;
- _entries[timerIndex].animType = animType;
- _entries[timerIndex].frameInc = frameInc;
- _entries[timerIndex].depth = depth;
- _entries[timerIndex].scale = scale;
- _entries[timerIndex].field_12 = field_12;
- _entries[timerIndex].msgPos.x = msgX;
- _entries[timerIndex].msgPos.y = msgY;
- _entries[timerIndex].numTicks = numTicks;
- _entries[timerIndex].extraTicks = extraTicks;
-
- _entries[timerIndex].timeout = _madsVm->_currentTimer + delayTicks;
-
- _entries[timerIndex].triggerCountdown = triggerCountdown;
- _entries[timerIndex].doneFlag = false;
- _entries[timerIndex].field_13 = 0;
- _entries[timerIndex].dynamicHotspotIndex = -1;
- _entries[timerIndex].entries.count = 0;
- _entries[timerIndex].abortMode = _owner._abortTimersMode2;
+ _entries[seqIndex].active = true;
+ _entries[seqIndex].spriteListIndex = spriteListIndex;
+ _entries[seqIndex].field_2 = v0;
+ _entries[seqIndex].frameIndex = frameIndex;
+ _entries[seqIndex].frameStart = frameStart;
+ _entries[seqIndex].numSprites = numSprites;
+ _entries[seqIndex].animType = animType;
+ _entries[seqIndex].frameInc = frameInc;
+ _entries[seqIndex].depth = depth;
+ _entries[seqIndex].scale = scale;
+ _entries[seqIndex].nonFixed = nonFixed;
+ _entries[seqIndex].msgPos.x = msgX;
+ _entries[seqIndex].msgPos.y = msgY;
+ _entries[seqIndex].numTicks = numTicks;
+ _entries[seqIndex].extraTicks = extraTicks;
+
+ _entries[seqIndex].timeout = _madsVm->_currentTimer + delayTicks;
+
+ _entries[seqIndex].triggerCountdown = triggerCountdown;
+ _entries[seqIndex].doneFlag = false;
+ _entries[seqIndex].field_13 = 0;
+ _entries[seqIndex].dynamicHotspotIndex = -1;
+ _entries[seqIndex].entries.count = 0;
+ _entries[seqIndex].abortMode = _owner._abortTimersMode2;
for (int i = 0; i < 3; ++i)
- _entries[timerIndex].actionNouns[i] = _madsVm->scene()->actionNouns[i];
+ _entries[seqIndex].actionNouns[i] = _madsVm->scene()->actionNouns[i];
- return timerIndex;
+ return seqIndex;
}
-void MadsSequenceList::remove(int timerIndex) {
- if (_entries[timerIndex].active) {
- if (_entries[timerIndex].dynamicHotspotIndex >= 0)
- _owner._dynamicHotspots.remove(_entries[timerIndex].dynamicHotspotIndex);
+void MadsSequenceList::remove(int seqIndex) {
+ if (_entries[seqIndex].active) {
+ if (_entries[seqIndex].dynamicHotspotIndex >= 0)
+ _owner._dynamicHotspots.remove(_entries[seqIndex].dynamicHotspotIndex);
}
- _entries[timerIndex].active = false;
- _owner._spriteSlots.deleteTimer(timerIndex);
+ _entries[seqIndex].active = false;
+ _owner._spriteSlots.deleteTimer(seqIndex);
}
-void MadsSequenceList::setSpriteSlot(int timerIndex, MadsSpriteSlot &spriteSlot) {
- MadsSequenceEntry &timerEntry = _entries[timerIndex];
- SpriteAsset &sprite = _owner._spriteSlots.getSprite(timerEntry.spriteListIndex);
+void MadsSequenceList::setSpriteSlot(int seqIndex, MadsSpriteSlot &spriteSlot) {
+ MadsSequenceEntry &timerEntry = _entries[seqIndex];
+ SpriteAsset &spriteSet = _owner._spriteSlots.getSprite(timerEntry.spriteListIndex);
- spriteSlot.spriteType = sprite.getAssetType() == 1 ? BACKGROUND_SPRITE : FOREGROUND_SPRITE;
- spriteSlot.timerIndex = timerIndex;
+ spriteSlot.spriteType = spriteSet.isBackground() ? BACKGROUND_SPRITE : FOREGROUND_SPRITE;
+ spriteSlot.seqIndex = seqIndex;
spriteSlot.spriteListIndex = timerEntry.spriteListIndex;
spriteSlot.frameNumber = ((timerEntry.field_2 == 1) ? 0x8000 : 0) | timerEntry.frameIndex;
spriteSlot.depth = timerEntry.depth;
spriteSlot.scale = timerEntry.scale;
- if (timerEntry.field_12 == 0) {
+ if (!timerEntry.nonFixed) {
spriteSlot.xp = timerEntry.msgPos.x;
spriteSlot.yp = timerEntry.msgPos.y;
} else {
- spriteSlot.xp = sprite.getFrame(timerEntry.frameIndex - 1)->x;
- spriteSlot.yp = sprite.getFrame(timerEntry.frameIndex - 1)->y;
+ spriteSlot.xp = spriteSet.getFrame(timerEntry.frameIndex - 1)->x;
+ spriteSlot.yp = spriteSet.getFrame(timerEntry.frameIndex - 1)->y;
}
}
-bool MadsSequenceList::loadSprites(int timerIndex) {
- MadsSequenceEntry &seqEntry = _entries[timerIndex];
+bool MadsSequenceList::loadSprites(int seqIndex) {
+ MadsSequenceEntry &seqEntry = _entries[seqIndex];
int slotIndex;
bool result = false;
int idx = -1;
- _owner._spriteSlots.deleteTimer(timerIndex);
+ _owner._spriteSlots.deleteTimer(seqIndex);
if (seqEntry.doneFlag) {
- remove(timerIndex);
+ remove(seqIndex);
return false;
}
@@ -951,7 +989,7 @@ bool MadsSequenceList::loadSprites(int timerIndex) {
seqEntry.doneFlag = true;
} else if ((slotIndex = _owner._spriteSlots.getIndex()) >= 0) {
MadsSpriteSlot &spriteSlot = _owner._spriteSlots[slotIndex];
- setSpriteSlot(timerIndex, spriteSlot);
+ setSpriteSlot(seqIndex, spriteSlot);
int x2 = 0, y2 = 0;
@@ -1079,8 +1117,8 @@ void MadsSequenceList::delay(uint32 v1, uint32 v2) {
}
}
-void MadsSequenceList::setAnimRange(int timerIndex, int startVal, int endVal) {
- MadsSequenceEntry &seqEntry = _entries[timerIndex];
+void MadsSequenceList::setAnimRange(int seqIndex, int startVal, int endVal) {
+ MadsSequenceEntry &seqEntry = _entries[seqIndex];
SpriteAsset &spriteSet = _owner._spriteSlots.getSprite(seqEntry.spriteListIndex);
int numSprites = spriteSet.getCount();
int tempStart = startVal, tempEnd = endVal;
@@ -1113,10 +1151,28 @@ void MadsSequenceList::setAnimRange(int timerIndex, int startVal, int endVal) {
seqEntry.frameIndex = (seqEntry.frameInc < 0) ? tempStart : tempEnd;
}
+void MadsSequenceList::scan() {
+ for (uint i = 0; i < _entries.size(); ++i) {
+ if (!_entries[i].active && (_entries[i].spriteListIndex != -1)) {
+ int idx = _owner._spriteSlots.getIndex();
+ setSpriteSlot(i, _owner._spriteSlots[idx]);
+ }
+ }
+}
+
+//--------------------------------------------------------------------------
+
+Animation::Animation(MadsM4Engine *vm): _vm(vm) {
+}
+
+Animation::~Animation() {
+}
+
//--------------------------------------------------------------------------
MadsView::MadsView(View *view): _view(view), _dynamicHotspots(*this), _sequenceList(*this),
_kernelMessages(*this), _spriteSlots(*this), _dirtyAreas(*this), _textDisplay(*this) {
+
_textSpacing = -1;
_ticksAmount = 3;
_newTimeout = 0;
@@ -1124,9 +1180,15 @@ MadsView::MadsView(View *view): _view(view), _dynamicHotspots(*this), _sequenceL
_abortTimers2 = 0;
_abortTimersMode = ABORTMODE_0;
_abortTimersMode2 = ABORTMODE_0;
-
+
+ _yOffset = 0;
_depthSurface = NULL;
_bgSurface = NULL;
+ _sceneAnimation = new MadsAnimation(_vm, this);
+}
+
+MadsView::~MadsView() {
+ delete _sceneAnimation;
}
void MadsView::refresh() {
@@ -1140,7 +1202,7 @@ void MadsView::refresh() {
_dirtyAreas.merge(1, DIRTY_AREAS_SIZE);
// Copy dirty areas to the main display surface
- _dirtyAreas.copy(_view, _bgSurface);
+ _dirtyAreas.copy(_view, _bgSurface, _yOffset);
// Handle dirty areas for foreground objects
_spriteSlots.setDirtyAreas();
@@ -1148,10 +1210,10 @@ void MadsView::refresh() {
_dirtyAreas.merge(1, DIRTY_AREAS_SIZE);
// Draw foreground sprites
- _spriteSlots.drawForeground(_view);
+ _spriteSlots.drawForeground(_view, _yOffset);
// Draw text elements onto the view
- _textDisplay.draw(_view);
+ _textDisplay.draw(_view, _yOffset);
// Remove any sprite slots that are no longer needed
_spriteSlots.cleanUp();
@@ -1160,427 +1222,4 @@ void MadsView::refresh() {
_textDisplay.cleanUp();
}
-/*--------------------------------------------------------------------------
- * MadsInterfaceView handles the user interface section at the bottom of
- * game screens in MADS games
- *--------------------------------------------------------------------------
- */
-
-MadsInterfaceView::MadsInterfaceView(MadsM4Engine *vm): GameInterfaceView(vm,
- Common::Rect(0, MADS_SURFACE_HEIGHT, vm->_screen->width(), vm->_screen->height())) {
- _screenType = VIEWID_INTERFACE;
- _highlightedElement = -1;
- _topIndex = 0;
- _selectedObject = -1;
- _cheatKeyCtr = 0;
-
- _objectSprites = NULL;
- _objectPalData = NULL;
-
- /* Set up the rect list for screen elements */
- // Actions
- for (int i = 0; i < 10; ++i)
- _screenObjects.addRect((i / 5) * 32 + 1, (i % 5) * 8 + MADS_SURFACE_HEIGHT + 2,
- ((i / 5) + 1) * 32 + 3, ((i % 5) + 1) * 8 + MADS_SURFACE_HEIGHT + 2);
-
- // Scroller elements (up arrow, scroller, down arrow)
- _screenObjects.addRect(73, 160, 82, 167);
- _screenObjects.addRect(73, 168, 82, 190);
- _screenObjects.addRect(73, 191, 82, 198);
-
- // Inventory object names
- for (int i = 0; i < 5; ++i)
- _screenObjects.addRect(89, 158 + i * 8, 160, 166 + i * 8);
-
- // Full rectangle area for all vocab actions
- for (int i = 0; i < 5; ++i)
- _screenObjects.addRect(239, 158 + i * 8, 320, 166 + i * 8);
-}
-
-MadsInterfaceView::~MadsInterfaceView() {
- delete _objectSprites;
-}
-
-void MadsInterfaceView::setFontMode(InterfaceFontMode newMode) {
- switch (newMode) {
- case ITEM_NORMAL:
- _vm->_font->setColors(4, 4, 0xff);
- break;
- case ITEM_HIGHLIGHTED:
- _vm->_font->setColors(5, 5, 0xff);
- break;
- case ITEM_SELECTED:
- _vm->_font->setColors(6, 6, 0xff);
- break;
- }
-}
-
-void MadsInterfaceView::initialise() {
- // Build up the inventory list
- _inventoryList.clear();
-
- for (uint i = 0; i < _madsVm->globals()->getObjectsSize(); ++i) {
- MadsObject *obj = _madsVm->globals()->getObject(i);
- if (obj->roomNumber == PLAYER_INVENTORY)
- _inventoryList.push_back(i);
- }
-
- // If the inventory has at least one object, select it
- if (_inventoryList.size() > 0)
- setSelectedObject(_inventoryList[0]);
-}
-
-void MadsInterfaceView::setSelectedObject(int objectNumber) {
- char resName[80];
-
- // Load inventory resource
- if (_objectSprites) {
- _vm->_palette->deleteRange(_objectPalData);
- delete _objectSprites;
- }
-
- // Check to make sure the object is in the inventory, and also visible on-screen
- int idx = _inventoryList.indexOf(objectNumber);
- if (idx == -1) {
- // Object wasn't found, so return
- _selectedObject = -1;
- return;
- }
-
- // Found the object
- if (idx < _topIndex)
- _topIndex = idx;
- else if (idx >= (_topIndex + 5))
- _topIndex = MAX(0, idx - 4);
-
- _selectedObject = objectNumber;
- sprintf(resName, "*OB%.3dI.SS", objectNumber);
-
- Common::SeekableReadStream *data = _vm->res()->get(resName);
- _objectSprites = new SpriteAsset(_vm, data, data->size(), resName);
- _vm->res()->toss(resName);
-
- // Slot it into available palette space
- _objectPalData = _objectSprites->getRgbList();
- _vm->_palette->addRange(_objectPalData);
- _objectSprites->translate(_objectPalData, true);
-
- _objectFrameNumber = 0;
-}
-
-void MadsInterfaceView::addObjectToInventory(int objectNumber) {
- if (_inventoryList.indexOf(objectNumber) == -1) {
- _madsVm->globals()->getObject(objectNumber)->roomNumber = PLAYER_INVENTORY;
- _inventoryList.push_back(objectNumber);
- }
-
- setSelectedObject(objectNumber);
-}
-
-void MadsInterfaceView::onRefresh(RectList *rects, M4Surface *destSurface) {
- _vm->_font->setFont(FONT_INTERFACE_MADS);
- char buffer[100];
-
- // Check to see if any dialog is currently active
- bool dialogVisible = _vm->_viewManager->getView(LAYER_DIALOG) != NULL;
-
- // Highlighting logic for action list
- int actionIndex = 0;
- for (int x = 0; x < 2; ++x) {
- for (int y = 0; y < 5; ++y, ++actionIndex) {
- // Determine the font colour depending on whether an item is selected. Note that the first action,
- // 'Look', is always 'selected', even when another action is clicked on
- setFontMode((_highlightedElement == actionIndex) ? ITEM_HIGHLIGHTED :
- ((actionIndex == 0) ? ITEM_SELECTED : ITEM_NORMAL));
-
- // Get the verb action and capitalise it
- const char *verbStr = _madsVm->globals()->getVocab(kVerbLook + actionIndex);
- strcpy(buffer, verbStr);
- if ((buffer[0] >= 'a') && (buffer[0] <= 'z')) buffer[0] -= 'a' - 'A';
-
- // Display the verb
- const Common::Rect r(_screenObjects[actionIndex]);
- _vm->_font->writeString(destSurface, buffer, r.left, r.top, r.width(), 0);
- }
- }
-
- // Check for highlighting of the scrollbar controls
- if ((_highlightedElement == SCROLL_UP) || (_highlightedElement == SCROLL_SCROLLER) || (_highlightedElement == SCROLL_DOWN)) {
- // Highlight the control's borders
- const Common::Rect r(_screenObjects[_highlightedElement]);
- destSurface->frameRect(r, 5);
- }
-
- // Draw the horizontal line in the scroller representing the current top selected
- const Common::Rect scroller(_screenObjects[SCROLL_SCROLLER]);
- int yP = (_inventoryList.size() < 2) ? 0 : (scroller.height() - 5) * _topIndex / (_inventoryList.size() - 1);
- destSurface->setColor(4);
- destSurface->hLine(scroller.left + 2, scroller.right - 3, scroller.top + 2 + yP);
-
- // List inventory items
- for (uint i = 0; i < 5; ++i) {
- if ((_topIndex + i) >= _inventoryList.size())
- break;
-
- const char *descStr = _madsVm->globals()->getVocab(_madsVm->globals()->getObject(
- _inventoryList[_topIndex + i])->descId);
- strcpy(buffer, descStr);
- if ((buffer[0] >= 'a') && (buffer[0] <= 'z')) buffer[0] -= 'a' - 'A';
-
- const Common::Rect r(_screenObjects[INVLIST_START + i]);
-
- // Set the highlighting of the inventory item
- if (_highlightedElement == (int)(INVLIST_START + i)) setFontMode(ITEM_HIGHLIGHTED);
- else if (_selectedObject == _inventoryList[_topIndex + i]) setFontMode(ITEM_SELECTED);
- else setFontMode(ITEM_NORMAL);
-
- // Write out it's description
- _vm->_font->writeString(destSurface, buffer, r.left, r.top, r.width(), 0);
- }
-
- // Handle the display of any currently selected object
- if (_objectSprites) {
- // Display object sprite. Note that the frame number isn't used directly, because it would result
- // in too fast an animation
- M4Sprite *spr = _objectSprites->getFrame(_objectFrameNumber / INV_ANIM_FRAME_SPEED);
- spr->copyTo(destSurface, INVENTORY_X, INVENTORY_Y, 0);
-
- if (!_madsVm->globals()->_config.invObjectsStill && !dialogVisible) {
- // If objects need to be animated, move to the next frame
- if (++_objectFrameNumber >= (_objectSprites->getCount() * INV_ANIM_FRAME_SPEED))
- _objectFrameNumber = 0;
- }
-
- // List the vocab actions for the currently selected object
- MadsObject *obj = _madsVm->globals()->getObject(_selectedObject);
- int yIndex = MIN(_highlightedElement - VOCAB_START, obj->vocabCount - 1);
-
- for (int i = 0; i < obj->vocabCount; ++i) {
- const Common::Rect r(_screenObjects[VOCAB_START + i]);
-
- // Get the vocab description and capitalise it
- const char *descStr = _madsVm->globals()->getVocab(obj->vocabList[i].vocabId);
- strcpy(buffer, descStr);
- if ((buffer[0] >= 'a') && (buffer[0] <= 'z')) buffer[0] -= 'a' - 'A';
-
- // Set the highlighting and display the entry
- setFontMode((i == yIndex) ? ITEM_HIGHLIGHTED : ITEM_NORMAL);
- _vm->_font->writeString(destSurface, buffer, r.left, r.top, r.width(), 0);
- }
- }
-}
-
-bool MadsInterfaceView::onEvent(M4EventType eventType, int32 param1, int x, int y, bool &captureEvents) {
- MadsAction &act = _madsVm->scene()->getAction();
-
- // If the mouse isn't being held down, then reset the repeated scroll timer
- if (eventType != MEVENT_LEFT_HOLD)
- _nextScrollerTicks = 0;
-
- // Handle various event types
- switch (eventType) {
- case MEVENT_MOVE:
- // If the cursor isn't in "wait mode", don't do any processing
- if (_vm->_mouse->getCursorNum() == CURSOR_WAIT)
- return true;
-
- // Ensure the cursor is the standard arrow
- _vm->_mouse->setCursorNum(CURSOR_ARROW);
-
- // Check if any interface element is currently highlighted
- _highlightedElement = _screenObjects.find(Common::Point(x, y));
-
- return true;
-
- case MEVENT_LEFT_CLICK:
- // Left mouse click
- {
- // Check if an inventory object was selected
- if ((_highlightedElement >= INVLIST_START) && (_highlightedElement < (INVLIST_START + 5))) {
- // Ensure there is an inventory item listed in that cell
- uint idx = _highlightedElement - INVLIST_START;
- if ((_topIndex + idx) < _inventoryList.size()) {
- // Set the selected object
- setSelectedObject(_inventoryList[_topIndex + idx]);
- }
- } else if ((_highlightedElement >= ACTIONS_START) && (_highlightedElement < (ACTIONS_START + 10))) {
- // A standard action was selected
- int verbId = kVerbLook + (_highlightedElement - ACTIONS_START);
- warning("Selected action #%d", verbId);
-
- } else if ((_highlightedElement >= VOCAB_START) && (_highlightedElement < (VOCAB_START + 5))) {
- // A vocab action was selected
- MadsObject *obj = _madsVm->globals()->getObject(_selectedObject);
- int vocabIndex = MIN(_highlightedElement - VOCAB_START, obj->vocabCount - 1);
- if (vocabIndex >= 0) {
- act._actionMode = ACTMODE_OBJECT;
- act._actionMode2 = ACTMODE2_2;
- act._flags1 = obj->vocabList[1].flags1;
- act._flags2 = obj->vocabList[1].flags2;
-
- act._currentHotspot = _selectedObject;
- act._articleNumber = act._flags2;
- }
- }
- }
- return true;
-
- case MEVENT_LEFT_HOLD:
- // Left mouse hold
- // Handle the scroller - the up/down buttons allow for multiple actions whilst the mouse is held down
- if ((_highlightedElement == SCROLL_UP) || (_highlightedElement == SCROLL_DOWN)) {
- if ((_nextScrollerTicks == 0) || (g_system->getMillis() >= _nextScrollerTicks)) {
- // Handle scroll up/down action
- _nextScrollerTicks = g_system->getMillis() + SCROLLER_DELAY;
-
- if ((_highlightedElement == SCROLL_UP) && (_topIndex > 0))
- --_topIndex;
- if ((_highlightedElement == SCROLL_DOWN) && (_topIndex < (int)(_inventoryList.size() - 1)))
- ++_topIndex;
- }
- }
- return true;
-
- case MEVENT_LEFT_DRAG:
- // Left mouse drag
- // Handle the the the scroller area that can be dragged to adjust the top displayed index
- if (_highlightedElement == SCROLL_SCROLLER) {
- // Calculate the new top index based on the Y position
- const Common::Rect r(_screenObjects[SCROLL_SCROLLER]);
- _topIndex = CLIP((int)(_inventoryList.size() - 1) * (y - r.top - 2) / (r.height() - 5),
- 0, (int)_inventoryList.size() - 1);
- }
- return true;
-
- case KEVENT_KEY:
- if (_cheatKeyCtr == CHEAT_SEQUENCE_MAX)
- handleCheatKey(param1);
- handleKeypress(param1);
- return true;
-
- default:
- break;
- }
-
- return false;
-}
-
-bool MadsInterfaceView::handleCheatKey(int32 keycode) {
- switch (keycode) {
- case Common::KEYCODE_SPACE:
- // TODO: Move player to current destination
- return true;
-
- case Common::KEYCODE_t | (Common::KEYCODE_LALT):
- case Common::KEYCODE_t | (Common::KEYCODE_RALT):
- {
- // Teleport to room
- //Scene *sceneView = (Scene *)vm->_viewManager->getView(VIEWID_SCENE);
-
-
- return true;
- }
-
- default:
- break;
- }
-
- return false;
-}
-
-const char *CHEAT_SEQUENCE = "widepipe";
-
-bool MadsInterfaceView::handleKeypress(int32 keycode) {
- int flags = keycode >> 24;
- int kc = keycode & 0xffff;
-
- // Capitalise the letter if necessary
- if (_cheatKeyCtr < CHEAT_SEQUENCE_MAX) {
- if ((flags & Common::KBD_CTRL) && (kc == CHEAT_SEQUENCE[_cheatKeyCtr])) {
- ++_cheatKeyCtr;
- if (_cheatKeyCtr == CHEAT_SEQUENCE_MAX)
- Dialog::display(_vm, 22, cheatingEnabledDesc);
- return true;
- } else {
- _cheatKeyCtr = 0;
- }
- }
-
- // Handle the various keys
- if ((keycode == Common::KEYCODE_ESCAPE) || (keycode == Common::KEYCODE_F1)) {
- // Game menu
- _madsVm->globals()->dialogType = DIALOG_GAME_MENU;
- leaveScene();
- return false;
- } else if (flags & Common::KBD_CTRL) {
- // Handling of the different control key combinations
- switch (kc) {
- case Common::KEYCODE_i:
- // Mouse to inventory
- warning("TODO: Mouse to inventory");
- break;
-
- case Common::KEYCODE_k:
- // Toggle hotspots
- warning("TODO: Toggle hotspots");
- break;
-
- case Common::KEYCODE_p:
- // Player stats
- warning("TODO: Player stats");
- break;
-
- case Common::KEYCODE_q:
- // Quit game
- break;
-
- case Common::KEYCODE_s:
- // Activate sound
- warning("TODO: Activate sound");
- break;
-
- case Common::KEYCODE_u:
- // Rotate player
- warning("TODO: Rotate player");
- break;
-
- case Common::KEYCODE_v: {
- // Release version
- Dialog *dlg = new Dialog(_vm, GameReleaseInfoStr, GameReleaseTitleStr);
- _vm->_viewManager->addView(dlg);
- _vm->_viewManager->moveToFront(dlg);
- return false;
- }
-
- default:
- break;
- }
- } else if ((flags & Common::KBD_ALT) && (kc == Common::KEYCODE_q)) {
- // Quit Game
-
- } else {
- // Standard keypresses
- switch (kc) {
- case Common::KEYCODE_F2:
- // Save game
- _madsVm->globals()->dialogType = DIALOG_SAVE;
- leaveScene();
- break;
- case Common::KEYCODE_F3:
- // Restore game
- _madsVm->globals()->dialogType = DIALOG_RESTORE;
- leaveScene();
- break;
- }
- }
-//DIALOG_OPTIONS
- return false;
-}
-
-void MadsInterfaceView::leaveScene() {
- // Close the scene
- View *view = _madsVm->_viewManager->getView(VIEWID_SCENE);
- _madsVm->_viewManager->deleteView(view);
-}
-
} // End of namespace M4
diff --git a/engines/m4/mads_views.h b/engines/m4/mads_views.h
index 52388a99e0..29adb7048a 100644
--- a/engines/m4/mads_views.h
+++ b/engines/m4/mads_views.h
@@ -34,19 +34,24 @@
namespace M4 {
-#define MADS_SURFACE_WIDTH 320
-#define MADS_SURFACE_HEIGHT 156
-#define MADS_SCREEN_HEIGHT 200
-#define MADS_Y_OFFSET ((MADS_SCREEN_HEIGHT - MADS_SURFACE_HEIGHT) / 2)
-
class MadsView;
enum AbortTimerMode {ABORTMODE_0 = 0, ABORTMODE_1 = 1, ABORTMODE_2 = 2};
+class SpriteSlotSubset {
+public:
+ int spriteListIndex;
+ int frameNumber;
+ int xp;
+ int yp;
+ int depth;
+ int scale;
+};
+
class MadsSpriteSlot {
public:
int spriteType;
- int timerIndex;
+ int seqIndex;
int spriteListIndex;
int frameNumber;
int xp;
@@ -55,25 +60,27 @@ public:
int scale;
MadsSpriteSlot() { }
+
+ bool operator==(const SpriteSlotSubset &other) const;
+ void copy(const SpriteSlotSubset &other);
};
#define SPRITE_SLOTS_SIZE 50
enum SpriteIdSpecial {
- BACKGROUND_SPRITE = -4, FULL_SCREEN_REFRESH = -2, FOREGROUND_SPRITE = 1
+ BACKGROUND_SPRITE = -4, FULL_SCREEN_REFRESH = -2, EXPIRED_SPRITE = -1, SPRITE_ZERO = 0, FOREGROUND_SPRITE = 1
};
-typedef Common::Array<Common::SharedPtr<SpriteAsset> > SpriteList;
-
class MadsSpriteSlots {
private:
MadsView &_owner;
Common::Array<MadsSpriteSlot> _entries;
- SpriteList _sprites;
+ Common::Array<SpriteAsset *> _sprites;
public:
int startIndex;
MadsSpriteSlots(MadsView &owner);
+ ~MadsSpriteSlots();
MadsSpriteSlot &operator[](int idx) {
assert(idx < SPRITE_SLOTS_SIZE);
@@ -81,17 +88,19 @@ public:
}
SpriteAsset &getSprite(int idx) {
assert(idx < (int)_sprites.size());
- return *_sprites[idx].get();
+ return *_sprites[idx];
}
int getIndex();
int addSprites(const char *resName);
+ void deleteSprites(int listIndex);
void clear();
- void deleteTimer(int timerIndex);
+ void deleteTimer(int seqIndex);
void drawBackground();
- void drawForeground(View *view);
+ void drawForeground(View *view, int yOffset);
void setDirtyAreas();
+ void fullRefresh();
void cleanUp();
};
@@ -130,17 +139,17 @@ public:
int add(int xp, int yp, uint fontColour, int charSpacing, const char *msg, Font *font);
void clear();
- void draw(View *view);
+ void draw(View *view, int yOffset);
void setDirtyAreas();
void setDirtyAreas2();
void cleanUp();
};
#define TIMED_TEXT_SIZE 10
-#define TEXT_4A_SIZE 30
+#define INDEFINITE_TIMEOUT 9999999
-enum KernelMessageFlags {KMSG_1 = 1, KMSG_2 = 2, KMSG_4 = 4, KMSG_8 = 8, KMSG_20 = 0x20, KMSG_30 = 0x30,
- KMSG_40 = 0x40, KMSG_ACTIVE = 0x80};
+enum KernelMessageFlags {KMSG_QUOTED = 1, KMSG_OWNER_TIMEOUT = 2, KMSG_SEQ_ENTRY = 4, KMSG_SCROLL = 8, KMSG_RIGHT_ALIGN = 0x10,
+ KMSG_CENTER_ALIGN = 0x20, KMSG_EXPIRE = 0x40, KMSG_ACTIVE = 0x80};
class MadsKernelMessageEntry {
public:
@@ -157,7 +166,7 @@ public:
uint32 frameTimer2;
uint32 frameTimer;
uint32 timeout;
- bool field_1C;
+ int abortTimers;
AbortTimerMode abortMode;
uint16 actionNouns[3];
char msg[100];
@@ -174,9 +183,9 @@ public:
MadsKernelMessageList(MadsView &owner);
void clear();
- int add(const Common::Point &pt, uint fontColour, uint8 flags, uint8 v2, uint32 timeout, const char *msg);
- int addQuote(int quoteId, int v2, uint32 timeout);
- void unk1(int msgIndex, int v1, int v2);
+ int add(const Common::Point &pt, uint fontColour, uint8 flags, uint8 abortTimers, uint32 timeout, const char *msg);
+ int addQuote(int quoteId, int abortTimers, uint32 timeout);
+ void scrollMessage(int msgIndex, int numTicks, bool quoted);
void setSeqIndex(int msgIndex, int seqIndex);
void remove(int msgIndex);
void reset();
@@ -280,7 +289,7 @@ public:
void merge(int startIndex, int count);
bool intersects(int idx1, int idx2);
void mergeAreas(int idx1, int idx2);
- void copy(M4Surface *dest, M4Surface *src);
+ void copy(M4Surface *dest, M4Surface *src, int yOffset);
};
enum SpriteAnimType {ANIMTYPE_CYCLED = 1, ANIMTYPE_REVERSIBLE = 2};
@@ -313,7 +322,7 @@ struct MadsSequenceEntry {
int scale;
int dynamicHotspotIndex;
- int field_12;
+ bool nonFixed;
int field_13;
Common::Point msgPos;
@@ -341,20 +350,35 @@ public:
void clear();
bool addSubEntry(int index, SequenceSubEntryMode mode, int frameIndex, int abortVal);
int add(int spriteListIndex, int v0, int v1, int triggerCountdown, int delayTicks, int extraTicks, int numTicks,
- int msgX, int msgY, char field_12, char scale, uint8 depth, int frameInc, SpriteAnimType animType,
+ int msgX, int msgY, bool nonFixed, char scale, uint8 depth, int frameInc, SpriteAnimType animType,
int numSprites, int frameStart);
- void remove(int timerIndex);
- void setSpriteSlot(int timerIndex, MadsSpriteSlot &spriteSlot);
- bool loadSprites(int timerIndex);
+ void remove(int seqIndex);
+ void setSpriteSlot(int seqIndex, MadsSpriteSlot &spriteSlot);
+ bool loadSprites(int seqIndex);
void tick();
void delay(uint32 v1, uint32 v2);
- void setAnimRange(int timerIndex, int startVal, int endVal);
+ void setAnimRange(int seqIndex, int startVal, int endVal);
+ void scan();
};
+class Animation {
+protected:
+ MadsM4Engine *_vm;
+public:
+ Animation(MadsM4Engine *vm);
+ virtual ~Animation();
+ virtual void initialise(const Common::String &filename, uint16 flags, M4Surface *walkSurface, M4Surface *sceneSurface) = 0;
+ virtual void load(const Common::String &filename, int v0) = 0;
+ virtual void update() = 0;
+ virtual void setCurrentFrame(int frameNumber) = 0;
+};
+
+
class MadsView {
private:
View *_view;
public:
+ Animation *_sceneAnimation;
MadsSpriteSlots _spriteSlots;
MadsTextDisplay _textDisplay;
MadsKernelMessageList _kernelMessages;
@@ -374,62 +398,14 @@ public:
M4Surface *_depthSurface;
M4Surface *_bgSurface;
+ int _yOffset;
public:
MadsView(View *view);
+ ~MadsView();
void refresh();
};
-#define CHEAT_SEQUENCE_MAX 8
-
-class IntegerList : public Common::Array<int> {
-public:
- int indexOf(int v) {
- for (uint i = 0; i < size(); ++i)
- if (operator [](i) == v)
- return i;
- return -1;
- }
-};
-
-enum InterfaceFontMode {ITEM_NORMAL, ITEM_HIGHLIGHTED, ITEM_SELECTED};
-
-enum InterfaceObjects {ACTIONS_START = 0, SCROLL_UP = 10, SCROLL_SCROLLER = 11, SCROLL_DOWN = 12,
- INVLIST_START = 13, VOCAB_START = 18};
-
-class MadsInterfaceView : public GameInterfaceView {
-private:
- IntegerList _inventoryList;
- RectList _screenObjects;
- int _highlightedElement;
- int _topIndex;
- uint32 _nextScrollerTicks;
- int _cheatKeyCtr;
-
- // Object display fields
- int _selectedObject;
- SpriteAsset *_objectSprites;
- RGBList *_objectPalData;
- int _objectFrameNumber;
-
- void setFontMode(InterfaceFontMode newMode);
- bool handleCheatKey(int32 keycode);
- bool handleKeypress(int32 keycode);
- void leaveScene();
-public:
- MadsInterfaceView(MadsM4Engine *vm);
- ~MadsInterfaceView();
-
- virtual void initialise();
- virtual void setSelectedObject(int objectNumber);
- virtual void addObjectToInventory(int objectNumber);
- int getSelectedObject() { return _selectedObject; }
- int getInventoryObject(int objectIndex) { return _inventoryList[objectIndex]; }
-
- void onRefresh(RectList *rects, M4Surface *destSurface);
- bool onEvent(M4EventType eventType, int32 param1, int x, int y, bool &captureEvents);
-};
-
}
#endif
diff --git a/engines/m4/viewmgr.h b/engines/m4/viewmgr.h
index 16c3d6ecc3..211e6087f4 100644
--- a/engines/m4/viewmgr.h
+++ b/engines/m4/viewmgr.h
@@ -42,6 +42,19 @@ namespace M4 {
class View;
class ViewManager;
+enum SceneTransition {
+ kTransitionNone = 0,
+ kTransitionFadeIn = 1,
+ kTransitionFadeIn2 = 2,
+ kTransitionBoxInBottomLeft = 3,
+ kTransitionBoxInBottomRight = 4,
+ kTransitionBoxInTopLeft = 5,
+ kTransitionBoxInTopRight = 6,
+ kTransitionPanLeftToRight = 7,
+ kTransitionPanRightToLeft = 8,
+ kTransitionCircleIn = 9
+};
+
enum {SCREEN_DIALOG, SCREEN_BUFFER, SCREEN_TEXT, SCREEN_TRANSPARENT};
enum ScreenEventType {SCREVENT_NONE = 0, SCREVENT_KEY = 1, SCREVENT_MOUSE = 2, SCREVENT_ALL = 3};
enum ScreenLayers {
diff --git a/engines/mohawk/detection.cpp b/engines/mohawk/detection.cpp
index a7b1fe7fae..7f2e0cb312 100644
--- a/engines/mohawk/detection.cpp
+++ b/engines/mohawk/detection.cpp
@@ -341,6 +341,24 @@ static const MohawkGameDescription gameDescriptions[] = {
0,
},
+ // Myst Masterpiece Edition
+ // French Windows
+ // From gamin (Included in "Myst: La Trilogie")
+ {
+ {
+ "myst",
+ "Masterpiece Edition",
+ AD_ENTRY1("MYST.DAT", "aea81633b2d2ae498f09072fb87263b6"),
+ Common::FR_FRA,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MYST,
+ GF_ME,
+ 0,
+ },
+
// Riven: The Sequel to Myst
// Version 1.0 (5CD)
// From clone2727
@@ -432,6 +450,24 @@ static const MohawkGameDescription gameDescriptions[] = {
},
// Riven: The Sequel to Myst
+ // Version ? (DVD, From "Myst: La Trilogie")
+ // From gamin
+ {
+ {
+ "riven",
+ "",
+ AD_ENTRY1("a_Data.MHK", "aff2a384aaa9a0e0ec51010f708c5c04"),
+ Common::FR_FRA,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_RIVEN,
+ GF_DVD,
+ 0,
+ },
+
+ // Riven: The Sequel to Myst
// Version ? (Demo, From "Prince of Persia Collector's Edition")
// From Clone2727
{
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 744b3f2d2c..c646855bc7 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -47,6 +47,7 @@ MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescriptio
_cardData.hasData = false;
_gameOver = false;
_activatedSLST = false;
+ _ignoreNextMouseUp = false;
_extrasFile = NULL;
// Attempt to let game run from the CDs
@@ -147,10 +148,15 @@ Common::Error MohawkEngine_Riven::run() {
runHotspotScript(_curHotspot, kMouseDownScript);
break;
case Common::EVENT_LBUTTONUP:
- if (_curHotspot >= 0)
- runHotspotScript(_curHotspot, kMouseUpScript);
- else
- checkInventoryClick();
+ // See RivenScript::switchCard() for more information on why we sometimes
+ // disable the next up event.
+ if (!_ignoreNextMouseUp) {
+ if (_curHotspot >= 0)
+ runHotspotScript(_curHotspot, kMouseUpScript);
+ else
+ checkInventoryClick();
+ }
+ _ignoreNextMouseUp = false;
break;
case Common::EVENT_KEYDOWN:
switch (event.kbd.keycode) {
@@ -233,6 +239,7 @@ void MohawkEngine_Riven::changeToStack(uint16 n) {
// Stop any videos playing
_video->stopVideos();
+ _video->clearMLST();
// Clear the old stack files out
for (uint32 i = 0; i < _mhk.size(); i++)
@@ -310,7 +317,6 @@ void MohawkEngine_Riven::refreshCard() {
_gfx->clearWaterEffects();
_gfx->_activatedPLSTs.clear();
_video->stopVideos();
- _video->_mlstRecords.clear();
_gfx->drawPLST(1);
_activatedSLST = false;
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index f014b76fb8..11c3a4c0cb 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -113,7 +113,6 @@ public:
Common::RandomSource *_rnd;
Card _cardData;
- bool _gameOver;
GUI::Debugger *getDebugger();
@@ -147,6 +146,10 @@ private:
uint32 *_vars;
uint32 _varCount;
+ // Miscellaneous
+ bool _gameOver;
+ bool _ignoreNextMouseUp;
+
public:
Common::SeekableReadStream *getExtrasResource(uint32 tag, uint16 id);
bool _activatedSLST;
@@ -180,6 +183,9 @@ public:
uint32 *getLocalVar(uint32 index);
uint32 *matchVarToString(Common::String varName);
uint32 *matchVarToString(const char *varName);
+
+ void setGameOver() { _gameOver = true; }
+ void ignoreNextMouseUp() { _ignoreNextMouseUp = true; }
};
} // End of namespace Mohawk
diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp
index 8b1ff45a66..4e6bba1c2a 100644
--- a/engines/mohawk/riven_external.cpp
+++ b/engines/mohawk/riven_external.cpp
@@ -210,7 +210,28 @@ void RivenExternal::runEndGame(uint16 video) {
_vm->_video->playMovieBlocking(video);
// TODO: Play until the last frame and then run the credits
- _vm->_gameOver = true;
+ _vm->setGameOver();
+}
+
+void RivenExternal::runDomeButtonMovie() {
+ // This command just plays the video of the button moving down and up.
+ _vm->_video->playMovieBlocking(2);
+}
+
+void RivenExternal::runDomeCheck() {
+ // Check if we clicked while the golden frame was showing
+
+ VideoHandle video = _vm->_video->findVideoHandle(1);
+ assert(video != NULL_VID_HANDLE);
+
+ int32 curFrame = _vm->_video->getCurFrame(video);
+ int32 frameCount = _vm->_video->getFrameCount(video);
+
+ // The final frame of the video is the 'golden' frame (double meaning: the
+ // frame that is the magic one is the one with the golden symbol) but we
+ // give a 3 frame leeway in either direction.
+ if (frameCount - curFrame < 3 || curFrame < 3)
+ *_vm->matchVarToString("domecheck") = 1;
}
// ------------------------------------------------------------------------------------
@@ -218,11 +239,13 @@ void RivenExternal::runEndGame(uint16 video) {
// ------------------------------------------------------------------------------------
void RivenExternal::xastartupbtnhide(uint16 argc, uint16 *argv) {
- // The original game hides the start/setup buttons depending on an ini entry. It's safe to ignore this command.
+ // The original game hides the start/setup buttons depending on an ini entry.
+ // It's safe to ignore this command.
}
void RivenExternal::xasetupcomplete(uint16 argc, uint16 *argv) {
- // The original game sets an ini entry to disable the setup button and use the start button only. It's safe to ignore this part of the command.
+ // The original game sets an ini entry to disable the setup button and use the
+ // start button only. It's safe to ignore this part of the command.
_vm->_sound->stopSound();
_vm->changeToCard(1);
}
@@ -514,13 +537,14 @@ void RivenExternal::xbupdateboiler(uint16 argc, uint16 *argv) {
if (heat) {
if (platform == 0) {
_vm->_video->activateMLST(7, _vm->getCurCard());
- // TODO: Play video (non-blocking)
+ _vm->_video->playMovie(7);
} else {
_vm->_video->activateMLST(8, _vm->getCurCard());
- // TODO: Play video (non-blocking)
+ _vm->_video->playMovie(8);
}
} else {
- // TODO: Stop MLST's 7 and 8
+ _vm->_video->stopMovie(7);
+ _vm->_video->stopMovie(8);
}
_vm->refreshCard();
@@ -627,11 +651,11 @@ void RivenExternal::xbisland190_slidermw(uint16 argc, uint16 *argv) {
}
void RivenExternal::xbscpbtn(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ runDomeButtonMovie();
}
void RivenExternal::xbisland_domecheck(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ runDomeCheck();
}
void RivenExternal::xvalvecontrol(uint16 argc, uint16 *argv) {
@@ -723,11 +747,11 @@ void RivenExternal::xgisland25_slidermw(uint16 argc, uint16 *argv) {
}
void RivenExternal::xgscpbtn(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ runDomeButtonMovie();
}
void RivenExternal::xgisland1490_domecheck(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ runDomeCheck();
}
void RivenExternal::xgplateau3160_dopools(uint16 argc, uint16 *argv) {
@@ -978,11 +1002,11 @@ void RivenExternal::xjdome25_slidermw(uint16 argc, uint16 *argv) {
}
void RivenExternal::xjscpbtn(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ runDomeButtonMovie();
}
void RivenExternal::xjisland3500_domecheck(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ runDomeCheck();
}
int RivenExternal::jspitElevatorLoop() {
@@ -1258,11 +1282,11 @@ void RivenExternal::xpisland990_elevcombo(uint16 argc, uint16 *argv) {
}
void RivenExternal::xpscpbtn(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ runDomeButtonMovie();
}
void RivenExternal::xpisland290_domecheck(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ runDomeCheck();
}
void RivenExternal::xpisland25_opencard(uint16 argc, uint16 *argv) {
@@ -1457,11 +1481,11 @@ void RivenExternal::xtakeit(uint16 argc, uint16 *argv) {
}
void RivenExternal::xtscpbtn(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ runDomeButtonMovie();
}
void RivenExternal::xtisland4990_domecheck(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ runDomeCheck();
}
void RivenExternal::xtisland5056_opencard(uint16 argc, uint16 *argv) {
diff --git a/engines/mohawk/riven_external.h b/engines/mohawk/riven_external.h
index 8270a00854..14bb51340c 100644
--- a/engines/mohawk/riven_external.h
+++ b/engines/mohawk/riven_external.h
@@ -57,6 +57,8 @@ private:
int jspitElevatorLoop();
void runDemoBoundaryDialog();
void runEndGame(uint16 video);
+ void runDomeCheck();
+ void runDomeButtonMovie();
// -----------------------------------------------------
// aspit (Main Menu, Books, Setup) external commands
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index fa6f336542..d574a455c6 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -298,13 +298,10 @@ void RivenScript::processCommands(bool runCommands) {
// Command 1: draw tBMP resource (tbmp_id, left, top, right, bottom, u0, u1, u2, u3)
void RivenScript::drawBitmap(uint16 op, uint16 argc, uint16 *argv) {
- if (argc < 5) {
- // Copy the image to the whole screen, ignoring the rest of the parameters
+ if (argc < 5) // Copy the image to the whole screen, ignoring the rest of the parameters
_vm->_gfx->copyImageToScreen(argv[0], 0, 0, 608, 392);
- } else {
- // Copy the image to a certain part of the screen
+ else // Copy the image to a certain part of the screen
_vm->_gfx->copyImageToScreen(argv[0], argv[1], argv[2], argv[3], argv[4]);
- }
// Now, update the screen
_vm->_gfx->updateScreen();
@@ -313,6 +310,12 @@ void RivenScript::drawBitmap(uint16 op, uint16 argc, uint16 *argv) {
// Command 2: go to card (card id)
void RivenScript::switchCard(uint16 op, uint16 argc, uint16 *argv) {
_vm->changeToCard(argv[0]);
+
+ // WORKAROUND: If we changed card on a mouse down event,
+ // we want to ignore the next mouse up event so we don't
+ // change card when lifting the mouse on the next card.
+ if (_scriptType == kMouseDownScript)
+ _vm->ignoreNextMouseUp();
}
// Command 3: play an SLST from the script
@@ -547,9 +550,8 @@ void RivenScript::activateSLST(uint16 op, uint16 argc, uint16 *argv) {
// Command 41: activate MLST record and play
void RivenScript::activateMLSTAndPlay(uint16 op, uint16 argc, uint16 *argv) {
- _vm->_video->enableMovie(argv[0] - 1);
_vm->_video->activateMLST(argv[0], _vm->getCurCard());
- // TODO: Play movie (blocking?)
+ _vm->_video->playMovie(argv[0]);
}
// Command 43: activate BLST record (card hotspot enabling lists)
diff --git a/engines/mohawk/video.cpp b/engines/mohawk/video.cpp
index adca805763..a45a4294c8 100644
--- a/engines/mohawk/video.cpp
+++ b/engines/mohawk/video.cpp
@@ -35,18 +35,19 @@ VideoManager::VideoManager(MohawkEngine* vm) : _vm(vm) {
}
VideoManager::~VideoManager() {
- _mlstRecords.clear();
stopVideos();
}
void VideoManager::pauseVideos() {
for (uint16 i = 0; i < _videoStreams.size(); i++)
- _videoStreams[i]->pauseVideo(true);
+ if (_videoStreams[i].video)
+ _videoStreams[i]->pauseVideo(true);
}
void VideoManager::resumeVideos() {
for (uint16 i = 0; i < _videoStreams.size(); i++)
- _videoStreams[i]->pauseVideo(false);
+ if (_videoStreams[i].video)
+ _videoStreams[i]->pauseVideo(false);
}
void VideoManager::stopVideos() {
@@ -89,7 +90,7 @@ void VideoManager::playMovieCentered(Common::String filename, bool clearScreen)
void VideoManager::waitUntilMovieEnds(VideoHandle videoHandle) {
bool continuePlaying = true;
- while (!_videoStreams[videoHandle]->endOfVideo() && !_vm->shouldQuit() && continuePlaying) {
+ while (_videoStreams[videoHandle].video && !_videoStreams[videoHandle]->endOfVideo() && !_vm->shouldQuit() && continuePlaying) {
if (updateBackgroundMovies())
_vm->_system->updateScreen();
@@ -120,8 +121,10 @@ void VideoManager::waitUntilMovieEnds(VideoHandle videoHandle) {
_vm->_system->delayMillis(10);
}
- _videoStreams[videoHandle]->close();
- _videoStreams.clear();
+ delete _videoStreams[videoHandle].video;
+ _videoStreams[videoHandle].video = 0;
+ _videoStreams[videoHandle].id = 0;
+ _videoStreams[videoHandle].filename.clear();
}
void VideoManager::playBackgroundMovie(Common::String filename, int16 x, int16 y, bool loop) {
@@ -152,8 +155,9 @@ bool VideoManager::updateBackgroundMovies() {
_videoStreams[i]->rewind();
} else {
delete _videoStreams[i].video;
- memset(&_videoStreams[i], 0, sizeof(VideoEntry));
- _videoStreams[i].video = NULL;
+ _videoStreams[i].video = 0;
+ _videoStreams[i].id = 0;
+ _videoStreams[i].filename.clear();
continue;
}
}
@@ -240,7 +244,15 @@ void VideoManager::activateMLST(uint16 mlstId, uint16 card) {
if (mlstRecord.u1 != 1)
warning("mlstRecord.u1 not 1");
+ // We've found a match, add it
if (mlstRecord.index == mlstId) {
+ // Make sure we don't have any duplicates
+ for (uint32 j = 0; j < _mlstRecords.size(); j++)
+ if (_mlstRecords[j].index == mlstRecord.index || _mlstRecords[j].code == mlstRecord.code) {
+ _mlstRecords.remove_at(j);
+ j--;
+ }
+
_mlstRecords.push_back(mlstRecord);
break;
}
@@ -249,6 +261,10 @@ void VideoManager::activateMLST(uint16 mlstId, uint16 card) {
delete mlstStream;
}
+void VideoManager::clearMLST() {
+ _mlstRecords.clear();
+}
+
void VideoManager::playMovie(uint16 id) {
for (uint16 i = 0; i < _mlstRecords.size(); i++)
if (_mlstRecords[i].code == id) {
@@ -274,8 +290,10 @@ void VideoManager::stopMovie(uint16 id) {
if (_mlstRecords[i].code == id)
for (uint16 j = 0; j < _videoStreams.size(); j++)
if (_mlstRecords[i].movieID == _videoStreams[j].id) {
- delete _videoStreams[i].video;
- memset(&_videoStreams[i].video, 0, sizeof(VideoEntry));
+ delete _videoStreams[j].video;
+ _videoStreams[j].video = 0;
+ _videoStreams[j].id = 0;
+ _videoStreams[j].filename.clear();
return;
}
}
@@ -374,4 +392,24 @@ VideoHandle VideoManager::createVideoHandle(Common::String filename, uint16 x, u
return _videoStreams.size() - 1;
}
+VideoHandle VideoManager::findVideoHandle(uint16 id) {
+ for (uint16 i = 0; i < _mlstRecords.size(); i++)
+ if (_mlstRecords[i].code == id)
+ for (uint16 j = 0; j < _videoStreams.size(); j++)
+ if (_videoStreams[j].video && _mlstRecords[i].movieID == _videoStreams[j].id)
+ return j;
+
+ return NULL_VID_HANDLE;
+}
+
+int32 VideoManager::getCurFrame(const VideoHandle &handle) {
+ assert(handle != NULL_VID_HANDLE);
+ return _videoStreams[handle]->getCurFrame();
+}
+
+uint32 VideoManager::getFrameCount(const VideoHandle &handle) {
+ assert(handle != NULL_VID_HANDLE);
+ return _videoStreams[handle]->getFrameCount();
+}
+
} // End of namespace Mohawk
diff --git a/engines/mohawk/video.h b/engines/mohawk/video.h
index 8cfe1527fb..6aa553e26b 100644
--- a/engines/mohawk/video.h
+++ b/engines/mohawk/video.h
@@ -82,6 +82,7 @@ public:
// Riven-related functions
void activateMLST(uint16 mlstId, uint16 card);
+ void clearMLST();
void enableMovie(uint16 id);
void disableMovie(uint16 id);
void disableAllMovies();
@@ -89,19 +90,23 @@ public:
void stopMovie(uint16 id);
void playMovieBlocking(uint16 id);
- // Riven-related variables
- Common::Array<MLSTRecord> _mlstRecords;
+ // Handle functions
+ VideoHandle findVideoHandle(uint16 id);
+ int32 getCurFrame(const VideoHandle &handle);
+ uint32 getFrameCount(const VideoHandle &handle);
private:
MohawkEngine *_vm;
- void waitUntilMovieEnds(VideoHandle videoHandle);
+ // Riven-related variables
+ Common::Array<MLSTRecord> _mlstRecords;
// Keep tabs on any videos playing
Common::Array<VideoEntry> _videoStreams;
VideoHandle createVideoHandle(uint16 id, uint16 x, uint16 y, bool loop);
VideoHandle createVideoHandle(Common::String filename, uint16 x, uint16 y, bool loop);
+ void waitUntilMovieEnds(VideoHandle videoHandle);
};
} // End of namespace Mohawk
diff --git a/engines/parallaction/parser_ns.cpp b/engines/parallaction/parser_ns.cpp
index be72cf73a1..ff24a06ceb 100644
--- a/engines/parallaction/parser_ns.cpp
+++ b/engines/parallaction/parser_ns.cpp
@@ -286,6 +286,7 @@ void LocationParser_ns::parseAnimation(AnimationList &list, char *name) {
debugC(5, kDebugParser, "parseAnimation(name: %s)", name);
if (_vm->_location.findAnimation(name)) {
+ _zoneProg++;
_script->skip("endanimation");
return;
}
@@ -1305,6 +1306,7 @@ void LocationParser_ns::parseZone(ZoneList &list, char *name) {
debugC(5, kDebugParser, "parseZone(name: %s)", name);
if (_vm->_location.findZone(name)) {
+ _zoneProg++;
_script->skip("endzone");
return;
}
diff --git a/engines/saga/saga.cpp b/engines/saga/saga.cpp
index 2911d9c451..ed8a9055ba 100644
--- a/engines/saga/saga.cpp
+++ b/engines/saga/saga.cpp
@@ -330,20 +330,6 @@ Common::Error SagaEngine::run() {
syncSoundSettings();
-
-#if 0
- // FIXME: Disabled this code for now. We want to get rid of OSystem::kFeatureAutoComputeDirtyRects
- // and this is the last place to make use of it. We need to find out whether doing
- // so causes any regressions. If it does, we can reenable it, if not, we can remove
- // this code in 0.13.0.
-
- // FIXME: This is the ugly way of reducing redraw overhead. It works
- // well for 320x200 but it's unclear how well it will work for
- // 640x480.
- if (getGameId() == GID_ITE)
- _system->setFeatureState(OSystem::kFeatureAutoComputeDirtyRects, true);
-#endif
-
int msec = 0;
_previousTicks = _system->getMillis();
diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp
index 30791740d0..814fbff636 100644
--- a/engines/sci/console.cpp
+++ b/engines/sci/console.cpp
@@ -75,10 +75,10 @@ Console::Console(SciEngine *engine) : GUI::Debugger() {
// Variables
DVar_Register("sleeptime_factor", &g_debug_sleeptime_factor, DVAR_INT, 0);
- DVar_Register("gc_interval", &script_gc_interval, DVAR_INT, 0);
+ DVar_Register("gc_interval", &engine->_gamestate->script_gc_interval, DVAR_INT, 0);
DVar_Register("simulated_key", &g_debug_simulated_key, DVAR_INT, 0);
DVar_Register("track_mouse_clicks", &g_debug_track_mouse_clicks, DVAR_BOOL, 0);
- DVar_Register("script_abort_flag", &script_abort_flag, DVAR_INT, 0);
+ DVar_Register("script_abort_flag", &_engine->_gamestate->script_abort_flag, DVAR_INT, 0);
// General
DCmd_Register("help", WRAP_METHOD(Console, cmdHelp));
@@ -105,6 +105,7 @@ Console::Console(SciEngine *engine) : GUI::Debugger() {
DCmd_Register("resource_types", WRAP_METHOD(Console, cmdResourceTypes));
DCmd_Register("list", WRAP_METHOD(Console, cmdList));
DCmd_Register("hexgrep", WRAP_METHOD(Console, cmdHexgrep));
+ DCmd_Register("verify_scripts", WRAP_METHOD(Console, cmdVerifyScripts));
// Game
DCmd_Register("save_game", WRAP_METHOD(Console, cmdSaveGame));
DCmd_Register("restore_game", WRAP_METHOD(Console, cmdRestoreGame));
@@ -277,8 +278,8 @@ void Console::postEnter() {
#if 0
// Unused
#define LOOKUP_SPECIES(species) (\
- (species >= 1000) ? species : *(s->_classtable[species].scriptposp) \
- + s->_classtable[species].class_offset)
+ (species >= 1000) ? species : *(s->_classTable[species].scriptposp) \
+ + s->_classTable[species].class_offset)
#endif
bool Console::cmdHelp(int argc, const char **argv) {
@@ -324,6 +325,7 @@ bool Console::cmdHelp(int argc, const char **argv) {
DebugPrintf(" resource_types - Shows the valid resource types\n");
DebugPrintf(" list - Lists all the resources of a given type\n");
DebugPrintf(" hexgrep - Searches some resources for a particular sequence of bytes, represented as hexadecimal numbers\n");
+ DebugPrintf(" verify_scripts - Performs sanity checks on SCI1.1-SCI2.1 game scripts (e.g. if they're up to 64KB in total)\n");
DebugPrintf("\n");
DebugPrintf("Game:\n");
DebugPrintf(" save_game - Saves the current game state to the hard disk\n");
@@ -593,14 +595,14 @@ bool Console::cmdSetParseNodes(int argc, const char **argv) {
}
bool Console::cmdRegisters(int argc, const char **argv) {
+ EngineState *s = _engine->_gamestate;
DebugPrintf("Current register values:\n");
- DebugPrintf("acc=%04x:%04x prev=%04x:%04x &rest=%x\n", PRINT_REG(_engine->_gamestate->r_acc), PRINT_REG(_engine->_gamestate->r_prev), scriptState.restAdjust);
+ DebugPrintf("acc=%04x:%04x prev=%04x:%04x &rest=%x\n", PRINT_REG(s->r_acc), PRINT_REG(s->r_prev), s->restAdjustCur);
- if (!_engine->_gamestate->_executionStack.empty()) {
- EngineState *s = _engine->_gamestate; // for PRINT_STK
+ if (!s->_executionStack.empty()) {
DebugPrintf("pc=%04x:%04x obj=%04x:%04x fp=ST:%04x sp=ST:%04x\n",
- PRINT_REG(scriptState.xs->addr.pc), PRINT_REG(scriptState.xs->objp),
- (unsigned)(scriptState.xs->fp - s->stack_base), (unsigned)(scriptState.xs->sp - s->stack_base));
+ PRINT_REG(s->xs->addr.pc), PRINT_REG(s->xs->objp),
+ (unsigned)(s->xs->fp - s->stack_base), (unsigned)(s->xs->sp - s->stack_base));
} else
DebugPrintf("<no execution stack: pc,obj,fp omitted>\n");
@@ -811,6 +813,40 @@ bool Console::cmdHexgrep(int argc, const char **argv) {
return true;
}
+bool Console::cmdVerifyScripts(int argc, const char **argv) {
+ if (getSciVersion() < SCI_VERSION_1_1) {
+ DebugPrintf("This script check is only meant for SCI1.1-SCI2.1 games\n");
+ return true;
+ }
+
+ Common::List<ResourceId> *resources = _engine->getResMan()->listResources(kResourceTypeScript);
+ sort(resources->begin(), resources->end(), ResourceIdLess());
+ Common::List<ResourceId>::iterator itr = resources->begin();
+
+ DebugPrintf("%d SCI1.1-SCI2.1 scripts found, performing sanity checks...\n", resources->size());
+
+ Resource *script, *heap;
+ while (itr != resources->end()) {
+ script = _engine->getResMan()->findResource(*itr, false);
+ if (!script)
+ DebugPrintf("Error: script %d couldn't be loaded\n", itr->number);
+
+ heap = _engine->getResMan()->findResource(*itr, false);
+ if (!heap)
+ DebugPrintf("Error: script %d doesn't have a corresponding heap\n", itr->number);
+
+ if (script && heap && (script->size + heap->size > 65535))
+ DebugPrintf("Error: script and heap %d together are larger than 64KB (%d bytes)\n",
+ itr->number, script->size + heap->size);
+
+ ++itr;
+ }
+
+ DebugPrintf("SCI1.1-SCI2.1 script check finished\n");
+
+ return true;
+}
+
bool Console::cmdList(int argc, const char **argv) {
if (argc < 2) {
DebugPrintf("Lists all the resources of a given type\n");
@@ -922,18 +958,18 @@ bool Console::cmdRestoreGame(int argc, const char **argv) {
bool Console::cmdRestartGame(int argc, const char **argv) {
_engine->_gamestate->restarting_flags |= SCI_GAME_IS_RESTARTING_NOW;
- script_abort_flag = 1;
+ _engine->_gamestate->script_abort_flag = 1;
return false;
}
bool Console::cmdClassTable(int argc, const char **argv) {
DebugPrintf("Available classes:\n");
- for (uint i = 0; i < _engine->_gamestate->_segMan->_classtable.size(); i++) {
- if (_engine->_gamestate->_segMan->_classtable[i].reg.segment) {
+ for (uint i = 0; i < _engine->_gamestate->_segMan->classTableSize(); i++) {
+ if (_engine->_gamestate->_segMan->_classTable[i].reg.segment) {
DebugPrintf(" Class 0x%x at %04x:%04x (script 0x%x)\n", i,
- PRINT_REG(_engine->_gamestate->_segMan->_classtable[i].reg),
- _engine->_gamestate->_segMan->_classtable[i].script);
+ PRINT_REG(_engine->_gamestate->_segMan->_classTable[i].reg),
+ _engine->_gamestate->_segMan->_classTable[i].script);
}
}
@@ -1245,13 +1281,13 @@ bool Console::segmentInfo(int nr) {
case SEG_TYPE_SCRIPT: {
Script *scr = (Script *)mobj;
- DebugPrintf("script.%03d locked by %d, bufsize=%d (%x)\n", scr->_nr, scr->getLockers(), (uint)scr->_bufSize, (uint)scr->_bufSize);
- if (scr->_exportTable)
- DebugPrintf(" Exports: %4d at %d\n", scr->_numExports, (int)(((byte *)scr->_exportTable) - ((byte *)scr->_buf)));
+ DebugPrintf("script.%03d locked by %d, bufsize=%d (%x)\n", scr->_nr, scr->getLockers(), (uint)scr->getBufSize(), (uint)scr->getBufSize());
+ if (scr->getExportTable())
+ DebugPrintf(" Exports: %4d at %d\n", scr->getExportsNr(), (int)(((const byte *)scr->getExportTable()) - ((const byte *)scr->_buf)));
else
DebugPrintf(" Exports: none\n");
- DebugPrintf(" Synonyms: %4d\n", scr->_numSynonyms);
+ DebugPrintf(" Synonyms: %4d\n", scr->getSynonymsNr());
if (scr->_localsBlock)
DebugPrintf(" Locals : %4d in segment 0x%x\n", scr->_localsBlock->_locals.size(), scr->_localsSegment);
@@ -1265,7 +1301,7 @@ bool Console::segmentInfo(int nr) {
for (it = scr->_objects.begin(); it != end; ++it) {
DebugPrintf(" ");
// Object header
- Object *obj = _engine->_gamestate->_segMan->getObject(it->_value.getPos());
+ const Object *obj = _engine->_gamestate->_segMan->getObject(it->_value.getPos());
if (obj)
DebugPrintf("[%04x:%04x] %s : %3d vars, %3d methods\n", PRINT_REG(it->_value.getPos()),
_engine->_gamestate->_segMan->getObjectName(it->_value.getPos()),
@@ -1312,7 +1348,7 @@ bool Console::segmentInfo(int nr) {
objpos.segment = nr;
DebugPrintf(" [%04x] %s; copy of ", i, _engine->_gamestate->_segMan->getObjectName(objpos));
// Object header
- Object *obj = _engine->_gamestate->_segMan->getObject(ct->_table[i].getPos());
+ const Object *obj = _engine->_gamestate->_segMan->getObject(ct->_table[i].getPos());
if (obj)
DebugPrintf("[%04x:%04x] %s : %3d vars, %3d methods\n", PRINT_REG(ct->_table[i].getPos()),
_engine->_gamestate->_segMan->getObjectName(ct->_table[i].getPos()),
@@ -1520,12 +1556,12 @@ bool Console::cmdToggleSound(int argc, const char **argv) {
int handle = id.segment << 16 | id.offset; // frobnicate handle
if (id.segment) {
- SegManager *segMan = _engine->_gamestate->_segMan; // for PUT_SEL32V
+ SegManager *segMan = _engine->_gamestate->_segMan; // for writeSelectorValue
_engine->_gamestate->_sound.sfx_song_set_status(handle, SOUND_STATUS_STOPPED);
_engine->_gamestate->_sound.sfx_remove_song(handle);
- PUT_SEL32V(segMan, id, SELECTOR(signal), SIGNAL_OFFSET);
- PUT_SEL32V(segMan, id, SELECTOR(nodePtr), 0);
- PUT_SEL32V(segMan, id, SELECTOR(handle), 0);
+ writeSelectorValue(segMan, id, SELECTOR(signal), SIGNAL_OFFSET);
+ writeSelectorValue(segMan, id, SELECTOR(nodePtr), 0);
+ writeSelectorValue(segMan, id, SELECTOR(handle), 0);
}
#else
@@ -1730,14 +1766,15 @@ bool Console::cmdGCNormalize(int argc, const char **argv) {
}
bool Console::cmdVMVarlist(int argc, const char **argv) {
+ EngineState *s = _engine->_gamestate;
const char *varnames[] = {"global", "local", "temp", "param"};
DebugPrintf("Addresses of variables in the VM:\n");
for (int i = 0; i < 4; i++) {
- DebugPrintf("%s vars at %04x:%04x ", varnames[i], PRINT_REG(make_reg(scriptState.variables_seg[i], scriptState.variables[i] - scriptState.variables_base[i])));
- if (scriptState.variables_max)
- DebugPrintf(" total %d", scriptState.variables_max[i]);
+ DebugPrintf("%s vars at %04x:%04x ", varnames[i], PRINT_REG(make_reg(s->variables_seg[i], s->variables[i] - s->variables_base[i])));
+ if (s->variables_max)
+ DebugPrintf(" total %d", s->variables_max[i]);
DebugPrintf("\n");
}
@@ -1755,6 +1792,7 @@ bool Console::cmdVMVars(int argc, const char **argv) {
return true;
}
+ EngineState *s = _engine->_gamestate;
const char *varnames[] = {"global", "local", "temp", "param"};
const char *varabbrev = "gltp";
const char *vartype_pre = strchr(varabbrev, *argv[1]);
@@ -1793,17 +1831,17 @@ bool Console::cmdVMVars(int argc, const char **argv) {
return true;
}
- if ((scriptState.variables_max) && (scriptState.variables_max[vartype] <= idx)) {
- DebugPrintf("Max. index is %d (0x%x)\n", scriptState.variables_max[vartype], scriptState.variables_max[vartype]);
+ if ((s->variables_max) && (s->variables_max[vartype] <= idx)) {
+ DebugPrintf("Max. index is %d (0x%x)\n", s->variables_max[vartype], s->variables_max[vartype]);
return true;
}
switch (argc) {
case 3:
- DebugPrintf("%s var %d == %04x:%04x\n", varnames[vartype], idx, PRINT_REG(scriptState.variables[vartype][idx]));
+ DebugPrintf("%s var %d == %04x:%04x\n", varnames[vartype], idx, PRINT_REG(s->variables[vartype][idx]));
break;
case 4:
- if (parse_reg_t(_engine->_gamestate, argv[3], &scriptState.variables[vartype][idx], true)) {
+ if (parse_reg_t(_engine->_gamestate, argv[3], &s->variables[vartype][idx], true)) {
DebugPrintf("Invalid value/address passed.\n");
DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
DebugPrintf("Or pass a decimal or hexadecimal value directly (e.g. 12, 1Ah)\n");
@@ -2036,7 +2074,7 @@ bool Console::cmdViewObject(int argc, const char **argv) {
bool Console::cmdViewActiveObject(int argc, const char **argv) {
DebugPrintf("Information on the currently active object or class:\n");
- printObject(scriptState.xs->objp);
+ printObject(_engine->_gamestate->xs->objp);
return true;
}
@@ -2049,7 +2087,7 @@ bool Console::cmdViewAccumulatorObject(int argc, const char **argv) {
}
bool Console::cmdScriptSteps(int argc, const char **argv) {
- DebugPrintf("Number of executed SCI operations: %d\n", script_step_counter);
+ DebugPrintf("Number of executed SCI operations: %d\n", _engine->_gamestate->script_step_counter);
return true;
}
@@ -2226,8 +2264,8 @@ bool Console::cmdDisassemble(int argc, const char **argv) {
return true;
}
- Object *obj = _engine->_gamestate->_segMan->getObject(objAddr);
- int selector_id = _engine->getKernel()->findSelector(argv[2]);
+ const Object *obj = _engine->_gamestate->_segMan->getObject(objAddr);
+ int selectorId = _engine->getKernel()->findSelector(argv[2]);
reg_t addr;
if (!obj) {
@@ -2235,12 +2273,12 @@ bool Console::cmdDisassemble(int argc, const char **argv) {
return true;
}
- if (selector_id < 0) {
+ if (selectorId < 0) {
DebugPrintf("Not a valid selector name.");
return true;
}
- if (lookup_selector(_engine->_gamestate->_segMan, objAddr, selector_id, NULL, &addr) != kSelectorMethod) {
+ if (lookupSelector(_engine->_gamestate->_segMan, objAddr, selectorId, NULL, &addr) != kSelectorMethod) {
DebugPrintf("Not a method.");
return true;
}
@@ -2320,20 +2358,20 @@ bool Console::cmdSend(int argc, const char **argv) {
}
const char *selector_name = argv[2];
- int selector_id = _engine->getKernel()->findSelector(selector_name);
+ int selectorId = _engine->getKernel()->findSelector(selector_name);
- if (selector_id < 0) {
+ if (selectorId < 0) {
DebugPrintf("Unknown selector: \"%s\"\n", selector_name);
return true;
}
- Object *o = _engine->_gamestate->_segMan->getObject(object);
+ const Object *o = _engine->_gamestate->_segMan->getObject(object);
if (o == NULL) {
DebugPrintf("Address \"%04x:%04x\" is not an object\n", PRINT_REG(object));
return true;
}
- SelectorType selector_type = lookup_selector(_engine->_gamestate->_segMan, object, selector_id, 0, 0);
+ SelectorType selector_type = lookupSelector(_engine->_gamestate->_segMan, object, selectorId, 0, 0);
if (selector_type == kSelectorNone) {
DebugPrintf("Object does not support selector: \"%s\"\n", selector_name);
@@ -2346,7 +2384,7 @@ bool Console::cmdSend(int argc, const char **argv) {
// Create the data block for send_selecor() at the top of the stack:
// [selector_number][argument_counter][arguments...]
StackPtr stackframe = _engine->_gamestate->_executionStack.back().sp;
- stackframe[0] = make_reg(0, selector_id);
+ stackframe[0] = make_reg(0, selectorId);
stackframe[1] = make_reg(0, send_argc);
for (int i = 0; i < send_argc; i++) {
if (parse_reg_t(_engine->_gamestate, argv[3+i], &stackframe[2+i], false)) {
@@ -2693,7 +2731,7 @@ bool Console::cmdQuit(int argc, const char **argv) {
if (!scumm_stricmp(argv[1], "game")) {
// Quit gracefully
- script_abort_flag = 1; // Terminate VM
+ _engine->_gamestate->script_abort_flag = 1; // Terminate VM
g_debugState.seeking = kDebugSeekNothing;
g_debugState.runningStep = 0;
@@ -3018,8 +3056,8 @@ int Console::printNode(reg_t addr) {
int Console::printObject(reg_t pos) {
EngineState *s = _engine->_gamestate; // for the several defines in this function
- Object *obj = s->_segMan->getObject(pos);
- Object *var_container = obj;
+ const Object *obj = s->_segMan->getObject(pos);
+ const Object *var_container = obj;
uint i;
if (!obj) {
@@ -3031,7 +3069,7 @@ int Console::printObject(reg_t pos) {
DebugPrintf("[%04x:%04x] %s : %3d vars, %3d methods\n", PRINT_REG(pos), s->_segMan->getObjectName(pos),
obj->getVarCount(), obj->getMethodCount());
- if (!(obj->getInfoSelector().offset & SCRIPT_INFO_CLASS))
+ if (!obj->isClass())
var_container = s->_segMan->getObject(obj->getSuperClassSelector());
DebugPrintf(" -- member variables:\n");
for (i = 0; (uint)i < obj->getVarCount(); i++) {
@@ -3048,7 +3086,7 @@ int Console::printObject(reg_t pos) {
if (!val.segment)
DebugPrintf(" (%d)", val.offset);
- Object *ref = s->_segMan->getObject(val);
+ const Object *ref = s->_segMan->getObject(val);
if (ref)
DebugPrintf(" (%s)", s->_segMan->getObjectName(val));
@@ -3060,7 +3098,7 @@ int Console::printObject(reg_t pos) {
DebugPrintf(" [%03x] %s = %04x:%04x\n", obj->getFuncSelector(i), selector_name(s, obj->getFuncSelector(i)), PRINT_REG(fptr));
}
if (s->_segMan->_heap[pos.segment]->getType() == SEG_TYPE_SCRIPT)
- DebugPrintf("\nOwner script:\t%d\n", s->_segMan->getScript(pos.segment)->_nr);
+ DebugPrintf("\nOwner script: %d\n", s->_segMan->getScript(pos.segment)->_nr);
return 0;
}
diff --git a/engines/sci/console.h b/engines/sci/console.h
index c88795ae26..2b13e03ef6 100644
--- a/engines/sci/console.h
+++ b/engines/sci/console.h
@@ -73,6 +73,7 @@ private:
bool cmdResourceTypes(int argc, const char **argv);
bool cmdList(int argc, const char **argv);
bool cmdHexgrep(int argc, const char **argv);
+ bool cmdVerifyScripts(int argc, const char **argv);
// Game
bool cmdSaveGame(int argc, const char **argv);
bool cmdRestoreGame(int argc, const char **argv);
diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp
index 06a83a98dc..1ccfc6bf02 100644
--- a/engines/sci/detection.cpp
+++ b/engines/sci/detection.cpp
@@ -140,9 +140,11 @@ static const OldNewIdTableEntry s_oldNewTable[] = {
{ "eco", "ecoquest", SCI_VERSION_NONE },
{ "eco2", "ecoquest2", SCI_VERSION_NONE }, // EcoQuest 2 demo
{ "rain", "ecoquest2", SCI_VERSION_NONE }, // EcoQuest 2 full
+ { "tales", "fairytales", SCI_VERSION_NONE },
{ "fp", "freddypharkas", SCI_VERSION_NONE },
{ "emc", "funseeker", SCI_VERSION_NONE },
{ "gk", "gk1", SCI_VERSION_NONE },
+ // gk2 is the same
{ "hoyledemo", "hoyle1", SCI_VERSION_NONE },
{ "cardgames", "hoyle1", SCI_VERSION_NONE },
{ "solitare", "hoyle2", SCI_VERSION_NONE },
@@ -152,6 +154,9 @@ static const OldNewIdTableEntry s_oldNewTable[] = {
{ "demo000", "kq1sci", SCI_VERSION_NONE },
{ "kq1", "kq1sci", SCI_VERSION_NONE },
{ "kq4", "kq4sci", SCI_VERSION_NONE },
+ // kq5 is the same
+ // kq6 is the same
+ // kq7 is the same
{ "mm1", "laurabow", SCI_VERSION_NONE },
{ "cb1", "laurabow", SCI_VERSION_NONE },
{ "lb2", "laurabow2", SCI_VERSION_NONE },
@@ -165,24 +170,30 @@ static const OldNewIdTableEntry s_oldNewTable[] = {
// lsl6 is the same
{ "mg", "mothergoose", SCI_VERSION_NONE },
{ "twisty", "pepper", SCI_VERSION_NONE },
+ { "scary", "phantasmagoria", SCI_VERSION_NONE },
+ // TODO: distinguish the full version of Phantasmagoria from the demo
{ "pq1", "pq1sci", SCI_VERSION_NONE },
{ "pq", "pq2", SCI_VERSION_NONE },
// pq3 is the same
// pq4 is the same
- { "tales", "fairytales", SCI_VERSION_NONE },
{ "hq", "qfg1", SCI_VERSION_NONE }, // QFG1 SCI0/EGA
{ "glory", "qfg1", SCI_VERSION_0_LATE }, // QFG1 SCI0/EGA
{ "trial", "qfg2", SCI_VERSION_NONE },
{ "hq2demo", "qfg2", SCI_VERSION_NONE },
+ // rama is the same
+ // TODO: distinguish the full version of rama from the demo
{ "thegame", "slater", SCI_VERSION_NONE },
{ "sq1demo", "sq1sci", SCI_VERSION_NONE },
{ "sq1", "sq1sci", SCI_VERSION_NONE },
// sq3 is the same
// sq4 is the same
// sq5 is the same
+ // sq6 is the same
+ // TODO: distinguish the full version of SQ6 from the demo
// torin is the same
- // TODO: SCI2.1, SCI3 IDs
+
+ // TODO: SCI3 IDs
{ "", "", SCI_VERSION_NONE }
};
@@ -222,6 +233,9 @@ Common::String convertSierraGameId(Common::String sierraId, uint32 *gameFlags, R
return "msastrochicken";
}
+ if (sierraId == "torin" && resources->size() == 226) // Torin's Passage demo
+ *gameFlags |= ADGF_DEMO;
+
for (const OldNewIdTableEntry *cur = s_oldNewTable; cur->oldId[0]; ++cur) {
if (sierraId == cur->oldId) {
// Distinguish same IDs from the SCI version
@@ -359,19 +373,6 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl
filename.toLowercase();
if (filename.contains("resource.map") || filename.contains("resmap.00") || filename.contains("Data1")) {
- // HACK: resource.map is located in the same directory as the other resource files,
- // therefore add the directory here, so that the game files can be opened later on
- // We now add the parent directory temporary to our SearchMan so the engine code
- // used in the detection can access all files via Common::File without any problems.
- // In all branches returning from this function, we need to have a call to
- // SearchMan.remove to remove it from the default directory pool again.
- //
- // A proper solution to remove this hack would be to have the code, which is needed
- // for detection, to operate on Stream objects, so they can be easily called from
- // the detection code. This might be easily to achieve through refactoring the
- // code needed for detection.
- assert(!SearchMan.hasArchive("SCI_detection"));
- SearchMan.addDirectory("SCI_detection", file->getParent());
foundResMap = true;
}
@@ -415,7 +416,6 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl
// If these files aren't found, it can't be SCI
if (!foundResMap && !foundRes000) {
- SearchMan.remove("SCI_detection");
return 0;
}
@@ -423,11 +423,10 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl
ViewType gameViews = resMan->getViewType();
// Have we identified the game views? If not, stop here
+ // Can't be SCI (or unsupported SCI views). Pinball Creep by sierra also uses resource.map/resource.000 files
+ // but doesnt share sci format at all, if we dont return 0 here we will detect this game as SCI
if (gameViews == kViewUnknown) {
- SearchMan.remove("SCI_detection");
delete resMan;
- // Can't be SCI (or unsupported SCI views). Pinball Creep by sierra also uses resource.map/resource.000 files
- // but doesnt share sci format at all, if we dont return 0 here we will detect this game as SCI
return 0;
}
@@ -435,7 +434,6 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl
// Is SCI32 compiled in? If not, and this is a SCI32 game,
// stop here
if (getSciVersion() >= SCI_VERSION_2) {
- SearchMan.remove("SCI_detection");
delete resMan;
return (const ADGameDescription *)&s_fallbackDesc;
}
@@ -450,7 +448,15 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl
s_fallbackDesc.platform = Common::kPlatformAmiga;
// Determine the game id
- Common::String gameId = convertSierraGameId(resMan->findSierraGameId(), &s_fallbackDesc.flags, resMan);
+ Common::String sierraGameId = resMan->findSierraGameId();
+
+ // If we don't have a game id, the game is not SCI
+ if (sierraGameId.empty()) {
+ delete resMan;
+ return 0;
+ }
+
+ Common::String gameId = convertSierraGameId(sierraGameId, &s_fallbackDesc.flags, resMan);
strncpy(s_fallbackGameIdBuf, gameId.c_str(), sizeof(s_fallbackGameIdBuf) - 1);
s_fallbackGameIdBuf[sizeof(s_fallbackGameIdBuf) - 1] = 0; // Make sure string is NULL terminated
s_fallbackDesc.gameid = s_fallbackGameIdBuf;
@@ -484,7 +490,6 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl
}
}
- delete resMan;
// Fill in extras field
if (!strcmp(s_fallbackDesc.gameid, "lsl1sci") ||
@@ -492,14 +497,14 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl
!strcmp(s_fallbackDesc.gameid, "sq1sci"))
s_fallbackDesc.extra = "VGA Remake";
- if (!strcmp(s_fallbackDesc.gameid, "qfg1") && !Common::File::exists("resource.001"))
+ if (!strcmp(s_fallbackDesc.gameid, "qfg1") && getSciVersion() == SCI_VERSION_1_1)
s_fallbackDesc.extra = "VGA Remake";
// Add "demo" to the description for demos
if (s_fallbackDesc.flags & ADGF_DEMO)
s_fallbackDesc.extra = "demo";
- SearchMan.remove("SCI_detection");
+ delete resMan;
return (const ADGameDescription *)&s_fallbackDesc;
}
diff --git a/engines/sci/engine/features.cpp b/engines/sci/engine/features.cpp
index e51867332a..48f7c2d64f 100644
--- a/engines/sci/engine/features.cpp
+++ b/engines/sci/engine/features.cpp
@@ -56,7 +56,7 @@ reg_t GameFeatures::getDetectionAddr(const Common::String &objName, Selector slc
}
if (methodNum == -1) {
- if (lookup_selector(_segMan, objAddr, slc, NULL, &addr) != kSelectorMethod) {
+ if (lookupSelector(_segMan, objAddr, slc, NULL, &addr) != kSelectorMethod) {
warning("getDetectionAddr: target selector is not a method of object %s", objName.c_str());
return NULL_REG;
}
@@ -87,7 +87,7 @@ bool GameFeatures::autoDetectSoundType() {
opcode = extOpcode >> 1;
// Check for end of script
- if (opcode == op_ret || offset >= script->_bufSize)
+ if (opcode == op_ret || offset >= script->getBufSize())
break;
// The play method of the Sound object pushes the DoSound command
@@ -189,7 +189,7 @@ SciVersion GameFeatures::detectSetCursorType() {
}
// Now we check what the number variable holds in the handCursor object.
- uint16 number = GET_SEL32V(_segMan, objAddr, SELECTOR(number));
+ uint16 number = readSelectorValue(_segMan, objAddr, SELECTOR(number));
// If the number is 0, it uses views and therefore the SCI1.1 kSetCursor semantics,
// otherwise it uses the SCI0 early kSetCursor semantics.
@@ -223,7 +223,7 @@ bool GameFeatures::autoDetectLofsType(int methodNum) {
opcode = extOpcode >> 1;
// Check for end of script
- if (opcode == op_ret || offset >= script->_bufSize)
+ if (opcode == op_ret || offset >= script->getBufSize())
break;
if (opcode == op_lofsa || opcode == op_lofss) {
@@ -231,13 +231,13 @@ bool GameFeatures::autoDetectLofsType(int methodNum) {
uint16 lofs = opparams[0];
// Check for going out of bounds when interpreting as abs/rel
- if (lofs >= script->_bufSize)
+ if (lofs >= script->getBufSize())
_lofsType = SCI_VERSION_0_EARLY;
if ((signed)offset + (int16)lofs < 0)
_lofsType = SCI_VERSION_1_MIDDLE;
- if ((signed)offset + (int16)lofs >= (signed)script->_bufSize)
+ if ((signed)offset + (int16)lofs >= (signed)script->getBufSize())
_lofsType = SCI_VERSION_1_MIDDLE;
if (_lofsType != SCI_VERSION_NONE)
@@ -266,7 +266,7 @@ SciVersion GameFeatures::detectLofsType() {
// Find a function of the game object which invokes lofsa/lofss
reg_t gameClass = _segMan->findObjectByName("Game");
- Object *obj = _segMan->getObject(gameClass);
+ const Object *obj = _segMan->getObject(gameClass);
bool found = false;
for (uint m = 0; m < obj->getMethodCount(); m++) {
@@ -309,7 +309,7 @@ bool GameFeatures::autoDetectGfxFunctionsType(int methodNum) {
opcode = extOpcode >> 1;
// Check for end of script
- if (opcode == op_ret || offset >= script->_bufSize)
+ if (opcode == op_ret || offset >= script->getBufSize())
break;
if (opcode == op_callk) {
@@ -332,18 +332,47 @@ bool GameFeatures::autoDetectGfxFunctionsType(int methodNum) {
SciVersion GameFeatures::detectGfxFunctionsType() {
if (_gfxFunctionsType == SCI_VERSION_NONE) {
- // This detection only works (and is only needed) for SCI0 games
- if (getSciVersion() >= SCI_VERSION_01) {
+ if (getSciVersion() == SCI_VERSION_0_EARLY) {
+ // Old SCI0 games always used old graphics functions
+ _gfxFunctionsType = SCI_VERSION_0_EARLY;
+ } else if (getSciVersion() >= SCI_VERSION_01) {
+ // SCI01 and newer games always used new graphics functions
_gfxFunctionsType = SCI_VERSION_0_LATE;
- } else if (getSciVersion() > SCI_VERSION_0_EARLY) {
+ } else { // SCI0 late
// Check if the game is using an overlay
- bool found = false;
+ bool searchRoomObj = false;
+ reg_t rmObjAddr = _segMan->findObjectByName("Rm");
+
+ if (_kernel->_selectorCache.overlay != -1) {
+ // The game has an overlay selector, check how it calls kDrawPicto determine
+ // the graphics functions type used
+ if (lookupSelector(_segMan, rmObjAddr, _kernel->_selectorCache.overlay, NULL, NULL) == kSelectorMethod) {
+ if (!autoDetectGfxFunctionsType()) {
+ warning("Graphics functions detection failed, taking an educated guess");
+
+ // Try detecting the graphics function types from the existence of the motionCue
+ // selector (which is a bit of a hack)
+ if (_kernel->findSelector("motionCue") != -1)
+ _gfxFunctionsType = SCI_VERSION_0_LATE;
+ else
+ _gfxFunctionsType = SCI_VERSION_0_EARLY;
+ }
+ } else {
+ // The game has an overlay selector, but it's not a method of the Rm object
+ // (like in Hoyle 1 and 2), so search for other methods
+ searchRoomObj = true;
+ }
+ } else {
+ // The game doesn't have an overlay selector, so search for it manually
+ searchRoomObj = true;
+ }
- if (_kernel->_selectorCache.overlay == -1) {
- // No overlay selector found, check if any method of the Rm object
- // is calling kDrawPic, as the overlay selector might be missing in demos
+ if (searchRoomObj) {
+ // If requested, check if any method of the Rm object is calling kDrawPic,
+ // as the overlay selector might be missing in demos
+ bool found = false;
- Object *obj = _segMan->getObject(_segMan->findObjectByName("Rm"));
+ const Object *obj = _segMan->getObject(rmObjAddr);
for (uint m = 0; m < obj->getMethodCount(); m++) {
found = autoDetectGfxFunctionsType(m);
if (found)
@@ -351,30 +380,11 @@ SciVersion GameFeatures::detectGfxFunctionsType() {
}
if (!found) {
- // No overlay selector found, therefore the game is definitely
- // using old graphics functions
+ // No method of the Rm object is calling kDrawPic, thus the game
+ // doesn't have overlays and is using older graphics functions
_gfxFunctionsType = SCI_VERSION_0_EARLY;
}
- } else { // _kernel->_selectorCache.overlay != -1
- // An in-between case: The game does not have a shiftParser
- // selector, but it does have an overlay selector, so it uses an
- // overlay. Therefore, check it to see how it calls kDrawPic to
- // determine the graphics functions type used
-
- if (!autoDetectGfxFunctionsType()) {
- warning("Graphics functions detection failed, taking an educated guess");
-
- // Try detecting the graphics function types from the existence of the motionCue
- // selector (which is a bit of a hack)
- if (_kernel->findSelector("motionCue") != -1)
- _gfxFunctionsType = SCI_VERSION_0_LATE;
- else
- _gfxFunctionsType = SCI_VERSION_0_EARLY;
- }
}
- } else { // (getSciVersion() == SCI_VERSION_0_EARLY)
- // Old SCI0 games always used old graphics functions
- _gfxFunctionsType = SCI_VERSION_0_EARLY;
}
debugC(1, kDebugLevelVM, "Detected graphics functions type: %s", getSciVersionDesc(_gfxFunctionsType));
@@ -402,7 +412,7 @@ bool GameFeatures::autoDetectSci21KernelType() {
opcode = extOpcode >> 1;
// Check for end of script
- if (opcode == op_ret || offset >= script->_bufSize)
+ if (opcode == op_ret || offset >= script->getBufSize())
break;
if (opcode == op_callk) {
@@ -455,7 +465,7 @@ bool GameFeatures::autoDetectMoveCountType() {
opcode = extOpcode >> 1;
// Check for end of script
- if (opcode == op_ret || offset >= script->_bufSize)
+ if (opcode == op_ret || offset >= script->getBufSize())
break;
if (opcode == op_callk) {
diff --git a/engines/sci/engine/game.cpp b/engines/sci/engine/game.cpp
index d7fdd9be6e..bc10099e52 100644
--- a/engines/sci/engine/game.cpp
+++ b/engines/sci/engine/game.cpp
@@ -67,13 +67,7 @@ int script_init_engine(EngineState *s) {
s->script_000 = s->_segMan->getScript(script_000_segment);
- s->sys_strings = s->_segMan->allocateSysStrings(&s->sys_strings_segment);
-
- // Allocate static buffer for savegame and CWD directories
- SystemString *str = &s->sys_strings->_strings[SYS_STRING_SAVEDIR];
- str->_name = "savedir";
- str->_maxSize = MAX_SAVE_DIR_SIZE;
- str->_value = (char *)calloc(MAX_SAVE_DIR_SIZE, sizeof(char));
+ s->_segMan->initSysStrings();
s->r_acc = s->r_prev = NULL_REG;
s->restAdjust = 0;
@@ -105,31 +99,26 @@ int game_init(EngineState *s) {
return 1;
}
- if (s->_voc) {
- s->_voc->parserIsValid = false; // Invalidate parser
- s->_voc->parser_event = NULL_REG; // Invalidate parser event
- s->_voc->parser_base = make_reg(s->sys_strings_segment, SYS_STRING_PARSER_BASE);
+ // Reset parser
+ Vocabulary *voc = g_sci->getVocabulary();
+ if (voc) {
+ voc->parserIsValid = false; // Invalidate parser
+ voc->parser_event = NULL_REG; // Invalidate parser event
+ voc->parser_base = make_reg(s->_segMan->getSysStringsSegment(), SYS_STRING_PARSER_BASE);
}
// Initialize menu TODO: Actually this should be another init()
if (g_sci->_gfxMenu)
g_sci->_gfxMenu->reset();
- s->successor = NULL; // No successor
-
- SystemString *str = &s->sys_strings->_strings[SYS_STRING_PARSER_BASE];
- str->_name = "parser-base";
- str->_maxSize = MAX_PARSER_BASE;
- str->_value = (char *)calloc(MAX_PARSER_BASE, sizeof(char));
+ s->restoring = false;
s->game_start_time = g_system->getMillis();
s->last_wait_time = s->game_start_time;
srand(g_system->getMillis()); // Initialize random number generator
-// script_dissect(0, s->_selectorNames);
- // The first entry in the export table of script 0 points to the game object
- s->_gameObj = s->_segMan->lookupScriptExport(0, 0);
+ s->_gameObj = g_sci->getResMan()->findGameObject();
#ifdef USE_OLD_MUSIC_FUNCTIONS
if (s->sfx_init_flags & SFX_STATE_FLAG_NOSOUND)
@@ -145,9 +134,8 @@ int game_init(EngineState *s) {
}
int game_exit(EngineState *s) {
- s->_executionStack.clear();
-
- if (!s->successor) {
+ if (!s->restoring) {
+ s->_executionStack.clear();
#ifdef USE_OLD_MUSIC_FUNCTIONS
s->_sound.sfx_exit();
// Reinit because some other code depends on having a valid state
diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp
index 49266f3a18..6ebee2dfbd 100644
--- a/engines/sci/engine/kernel.cpp
+++ b/engines/sci/engine/kernel.cpp
@@ -332,11 +332,12 @@ SciKernelFunction kfunct_mappers[] = {
DEFUN("DoSync", kDoSync, ".*"),
DEFUN("MemorySegment", kMemorySegment, "iri*"),
DEFUN("Intersections", kIntersections, "iiiiriiiri"),
+ DEFUN("MergePoly", kMergePoly, "rli"),
DEFUN("ResCheck", kResCheck, "iii*"),
DEFUN("SetQuitStr", kSetQuitStr, "r"),
DEFUN("ShowMovie", kShowMovie, ".*"),
DEFUN("SetVideoMode", kSetVideoMode, "i"),
- DEFUN("Platform", kPlatform, "i.*"),
+ DEFUN("Platform", kPlatform, ".*"),
DEFUN("TextColors", kTextColors, ".*"),
DEFUN("TextFonts", kTextFonts, ".*"),
DEFUN("Portrait", kPortrait, ".*"),
@@ -362,7 +363,7 @@ SciKernelFunction kfunct_mappers[] = {
DEFUN("ListIndexOf", kListIndexOf, "lZo"),
DEFUN("OnMe", kOnMe, "iio.*"),
DEFUN("InPolygon", kInPolygon, "iio"),
- DEFUN("CreateTextBitmap", kCreateTextBitmap, "iiio"),
+ DEFUN("CreateTextBitmap", kCreateTextBitmap, "i.*"),
// SCI2.1 Kernel Functions
DEFUN("Save", kSave, ".*"),
@@ -382,7 +383,6 @@ SciKernelFunction kfunct_mappers[] = {
DEFUN("ShiftScreen", kShiftScreen, ".*"),
DEFUN("ListOps", kListOps, ".*"),
DEFUN("ATan", kATan, ".*"),
- DEFUN("MergePoly", kMergePoly, ".*"),
DEFUN("Record", kRecord, ".*"),
DEFUN("PlayBack", kPlayBack, ".*"),
DEFUN("DbugStr", kDbugStr, ".*"),
@@ -628,7 +628,7 @@ int Kernel::findRegType(reg_t reg) {
switch (mobj->getType()) {
case SEG_TYPE_SCRIPT:
- if (reg.offset <= (*(Script *)mobj)._bufSize &&
+ if (reg.offset <= (*(Script *)mobj).getBufSize() &&
reg.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET &&
RAW_IS_OBJECT((*(Script *)mobj)._buf + reg.offset)) {
return ((Script *)mobj)->getObject(reg.offset) ? KSIG_OBJECT : KSIG_REF;
diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h
index 7717743e19..8f8f34f74e 100644
--- a/engines/sci/engine/kernel.h
+++ b/engines/sci/engine/kernel.h
@@ -414,6 +414,7 @@ reg_t kDoAudio(EngineState *s, int argc, reg_t *argv);
reg_t kDoSync(EngineState *s, int argc, reg_t *argv);
reg_t kMemorySegment(EngineState *s, int argc, reg_t *argv);
reg_t kIntersections(EngineState *s, int argc, reg_t *argv);
+reg_t kMergePoly(EngineState *s, int argc, reg_t *argv);
reg_t kResCheck(EngineState *s, int argc, reg_t *argv);
reg_t kSetQuitStr(EngineState *s, int argc, reg_t *argv);
reg_t kShowMovie(EngineState *s, int argc, reg_t *argv);
diff --git a/engines/sci/engine/kernel32.cpp b/engines/sci/engine/kernel32.cpp
index 465e0e92df..0afdc3f2eb 100644
--- a/engines/sci/engine/kernel32.cpp
+++ b/engines/sci/engine/kernel32.cpp
@@ -501,7 +501,7 @@ reg_t kArray(EngineState *s, int argc, reg_t *argv) {
if (!s->_segMan->isHeapObject(argv[1]))
return argv[1];
- return GET_SEL32(s->_segMan, argv[1], SELECTOR(data));
+ return readSelector(s->_segMan, argv[1], SELECTOR(data));
default:
error("Unknown kArray subop %d", argv[0].toUint16());
}
@@ -524,8 +524,12 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) {
}
case 1: // Size
return make_reg(0, s->_segMan->getString(argv[1]).size());
- case 2: // At (return value at an index)
+ case 2: { // At (return value at an index)
+ if (argv[1].segment == s->_segMan->getStringSegmentId())
+ return make_reg(0, s->_segMan->lookupString(argv[1])->getRawData()[argv[2].toUint16()]);
+
return make_reg(0, s->_segMan->getString(argv[1])[argv[2].toUint16()]);
+ }
case 3: { // Atput (put value at an index)
SciString *string = s->_segMan->lookupString(argv[1]);
@@ -563,28 +567,40 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) {
return argv[1];
}
case 6: { // Cpy
- Common::String string2 = s->_segMan->getString(argv[3]);
+ const char *string2 = 0;
+ uint32 string2Size = 0;
+
+ if (argv[3].segment == s->_segMan->getStringSegmentId()) {
+ SciString *string = s->_segMan->lookupString(argv[3]);
+ string2 = string->getRawData();
+ string2Size = string->getSize();
+ } else {
+ Common::String string = s->_segMan->getString(argv[3]);
+ string2 = string.c_str();
+ string2Size = string.size() + 1;
+ }
+
uint32 index1 = argv[2].toUint16();
uint32 index2 = argv[4].toUint16();
// The original engine ignores bad copies too
- if (index2 > string2.size())
+ if (index2 > string2Size)
break;
// A count of -1 means fill the rest of the array
- uint32 count = argv[5].toSint16() == -1 ? string2.size() - index2 + 1 : argv[5].toUint16();
+ uint32 count = argv[5].toSint16() == -1 ? string2Size - index2 + 1 : argv[5].toUint16();
// We have a special case here for argv[1] being a system string
- if (argv[1].segment == s->sys_strings_segment) {
+ if (argv[1].segment == s->_segMan->getSysStringsSegment()) {
// Resize if necessary
- if ((uint32)s->sys_strings->_strings[argv[1].toUint16()]._maxSize < index1 + count) {
- delete[] s->sys_strings->_strings[argv[1].toUint16()]._value;
- s->sys_strings->_strings[argv[1].toUint16()]._maxSize = index1 + count;
- s->sys_strings->_strings[argv[1].toUint16()]._value = new char[index1 + count];
- memset(s->sys_strings->_strings[argv[1].toUint16()]._value, 0, index1 + count);
+ const uint16 sysStringId = argv[1].toUint16();
+ if ((uint32)s->_segMan->sysStrings->_strings[sysStringId]._maxSize < index1 + count) {
+ free(s->_segMan->sysStrings->_strings[sysStringId]._value);
+ s->_segMan->sysStrings->_strings[sysStringId]._maxSize = index1 + count;
+ s->_segMan->sysStrings->_strings[sysStringId]._value = (char *)calloc(index1 + count, sizeof(char));
}
- strncpy(s->sys_strings->_strings[argv[1].toUint16()]._value + index1, string2.c_str() + index2, count);
+ strncpy(s->_segMan->sysStrings->_strings[sysStringId]._value + index1, string2 + index2, count);
} else {
SciString *string1 = s->_segMan->lookupString(argv[1]);
@@ -594,7 +610,7 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) {
// Note: We're accessing from c_str() here because the string's size ignores
// the trailing 0 and therefore triggers an assert when doing string2[i + index2].
for (uint16 i = 0; i < count; i++)
- string1->setValue(i + index1, string2.c_str()[i + index2]);
+ string1->setValue(i + index1, string2[i + index2]);
}
} return argv[1];
@@ -608,15 +624,25 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) {
return make_reg(0, strcmp(string1.c_str(), string2.c_str()));
}
case 8: { // Dup
- Common::String string = s->_segMan->getString(argv[1]);
+ const char *rawString = 0;
+ uint32 size = 0;
+
+ if (argv[1].segment == s->_segMan->getStringSegmentId()) {
+ SciString *string = s->_segMan->lookupString(argv[1]);
+ rawString = string->getRawData();
+ size = string->getSize();
+ } else {
+ Common::String string = s->_segMan->getString(argv[1]);
+ rawString = string.c_str();
+ size = string.size() + 1;
+ }
+
reg_t stringHandle;
SciString *dupString = s->_segMan->allocateString(&stringHandle);
- dupString->setSize(string.size() + 1);
+ dupString->setSize(size);
- for (uint32 i = 0; i < string.size(); i++)
- dupString->setValue(i, string.c_str()[i]);
-
- dupString->setValue(dupString->getSize() - 1, 0);
+ for (uint32 i = 0; i < size; i++)
+ dupString->setValue(i, rawString[i]);
return stringHandle;
}
@@ -624,7 +650,7 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) {
if (!s->_segMan->isHeapObject(argv[1]))
return argv[1];
- return GET_SEL32(s->_segMan, argv[1], SELECTOR(data));
+ return readSelector(s->_segMan, argv[1], SELECTOR(data));
case 10: // Stringlen
return make_reg(0, s->_segMan->strlen(argv[1]));
case 11: { // Printf
@@ -689,12 +715,12 @@ reg_t kDeleteScreenItem(EngineState *s, int argc, reg_t *argv) {
/*
reg_t viewObj = argv[0];
- uint16 viewId = GET_SEL32V(s->_segMan, viewObj, SELECTOR(view));
- int16 loopNo = GET_SEL32V(s->_segMan, viewObj, SELECTOR(loop));
- int16 celNo = GET_SEL32V(s->_segMan, viewObj, SELECTOR(cel));
+ uint16 viewId = readSelectorValue(s->_segMan, viewObj, SELECTOR(view));
+ int16 loopNo = readSelectorValue(s->_segMan, viewObj, SELECTOR(loop));
+ int16 celNo = readSelectorValue(s->_segMan, viewObj, SELECTOR(cel));
//int16 leftPos = 0;
//int16 topPos = 0;
- int16 priority = GET_SEL32V(s->_segMan, viewObj, SELECTOR(priority));
+ int16 priority = readSelectorValue(s->_segMan, viewObj, SELECTOR(priority));
//int16 control = 0;
*/
@@ -761,10 +787,10 @@ reg_t kOnMe(EngineState *s, int argc, reg_t *argv) {
Common::Rect nsRect;
// Get the bounding rectangle of the object
- nsRect.left = GET_SEL32V(s->_segMan, targetObject, SELECTOR(nsLeft));
- nsRect.top = GET_SEL32V(s->_segMan, targetObject, SELECTOR(nsTop));
- nsRect.right = GET_SEL32V(s->_segMan, targetObject, SELECTOR(nsRight));
- nsRect.bottom = GET_SEL32V(s->_segMan, targetObject, SELECTOR(nsBottom));
+ nsRect.left = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsLeft));
+ nsRect.top = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsTop));
+ nsRect.right = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsRight));
+ nsRect.bottom = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsBottom));
//warning("kOnMe: (%d, %d) on object %04x:%04x, parameter %d", argv[0].toUint16(), argv[1].toUint16(), PRINT_REG(argv[2]), argv[3].toUint16());
@@ -778,9 +804,16 @@ reg_t kInPolygon(EngineState *s, int argc, reg_t *argv) {
reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) {
// TODO: argument 0 is usually 0, and arguments 1 and 2 are usually 1
- reg_t object = argv[3];
- Common::String text = s->_segMan->getString(GET_SEL32(s->_segMan, object, SELECTOR(text)));
- debug("kCreateTextBitmap: %s", text.c_str());
+ switch (argv[0].toUint16()) {
+ case 0:
+ if (argc != 4) {
+ warning("kCreateTextBitmap(0): expected 4 arguments, got %i", argc);
+ return NULL_REG;
+ }
+ reg_t object = argv[3];
+ Common::String text = s->_segMan->getString(readSelector(s->_segMan, object, SELECTOR(text)));
+ debug("kCreateTextBitmap: %s", text.c_str());
+ }
return NULL_REG;
}
diff --git a/engines/sci/engine/kevent.cpp b/engines/sci/engine/kevent.cpp
index 156035b30d..fd7711f196 100644
--- a/engines/sci/engine/kevent.cpp
+++ b/engines/sci/engine/kevent.cpp
@@ -49,16 +49,18 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
SegManager *segMan = s->_segMan;
Common::Point mousePos;
+ // Limit the mouse cursor position, if necessary
+ g_sci->_gfxCursor->refreshPosition();
mousePos = g_sci->_gfxCursor->getPosition();
// If there's a simkey pending, and the game wants a keyboard event, use the
// simkey instead of a normal event
if (g_debug_simulated_key && (mask & SCI_EVENT_KEYBOARD)) {
- PUT_SEL32V(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD); // Keyboard event
- PUT_SEL32V(segMan, obj, SELECTOR(message), g_debug_simulated_key);
- PUT_SEL32V(segMan, obj, SELECTOR(modifiers), SCI_KEYMOD_NUMLOCK); // Numlock on
- PUT_SEL32V(segMan, obj, SELECTOR(x), mousePos.x);
- PUT_SEL32V(segMan, obj, SELECTOR(y), mousePos.y);
+ writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD); // Keyboard event
+ writeSelectorValue(segMan, obj, SELECTOR(message), g_debug_simulated_key);
+ writeSelectorValue(segMan, obj, SELECTOR(modifiers), SCI_KEYMOD_NUMLOCK); // Numlock on
+ writeSelectorValue(segMan, obj, SELECTOR(x), mousePos.x);
+ writeSelectorValue(segMan, obj, SELECTOR(y), mousePos.y);
g_debug_simulated_key = 0;
return make_reg(0, 1);
}
@@ -67,26 +69,26 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
oldy = mousePos.y;
curEvent = s->_event->get(mask);
- if (s->_voc)
- s->_voc->parser_event = NULL_REG; // Invalidate parser event
+ if (g_sci->getVocabulary())
+ g_sci->getVocabulary()->parser_event = NULL_REG; // Invalidate parser event
- PUT_SEL32V(segMan, obj, SELECTOR(x), mousePos.x);
- PUT_SEL32V(segMan, obj, SELECTOR(y), mousePos.y);
+ writeSelectorValue(segMan, obj, SELECTOR(x), mousePos.x);
+ writeSelectorValue(segMan, obj, SELECTOR(y), mousePos.y);
//s->_gui->moveCursor(s->gfx_state->pointer_pos.x, s->gfx_state->pointer_pos.y);
switch (curEvent.type) {
case SCI_EVENT_QUIT:
- quit_vm();
+ quit_vm(s);
break;
case SCI_EVENT_KEYBOARD:
- PUT_SEL32V(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD); // Keyboard event
+ writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD); // Keyboard event
s->r_acc = make_reg(0, 1);
- PUT_SEL32V(segMan, obj, SELECTOR(message), curEvent.character);
+ writeSelectorValue(segMan, obj, SELECTOR(message), curEvent.character);
// We only care about the translated character
- PUT_SEL32V(segMan, obj, SELECTOR(modifiers), curEvent.modifiers & modifier_mask);
+ writeSelectorValue(segMan, obj, SELECTOR(modifiers), curEvent.modifiers & modifier_mask);
break;
case SCI_EVENT_MOUSE_RELEASE:
@@ -111,9 +113,9 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
break;
}
- PUT_SEL32V(segMan, obj, SELECTOR(type), curEvent.type);
- PUT_SEL32V(segMan, obj, SELECTOR(message), 0);
- PUT_SEL32V(segMan, obj, SELECTOR(modifiers), (curEvent.modifiers | extra_bits) & modifier_mask);
+ writeSelectorValue(segMan, obj, SELECTOR(type), curEvent.type);
+ writeSelectorValue(segMan, obj, SELECTOR(message), 0);
+ writeSelectorValue(segMan, obj, SELECTOR(modifiers), (curEvent.modifiers | extra_bits) & modifier_mask);
s->r_acc = make_reg(0, 1);
}
break;
@@ -165,9 +167,9 @@ reg_t kMapKeyToDir(EngineState *s, int argc, reg_t *argv) {
reg_t obj = argv[0];
SegManager *segMan = s->_segMan;
- if (GET_SEL32V(segMan, obj, SELECTOR(type)) == SCI_EVENT_KEYBOARD) { // Keyboard
+ if (readSelectorValue(segMan, obj, SELECTOR(type)) == SCI_EVENT_KEYBOARD) { // Keyboard
int mover = -1;
- switch (GET_SEL32V(segMan, obj, SELECTOR(message))) {
+ switch (readSelectorValue(segMan, obj, SELECTOR(message))) {
case SCI_KEY_HOME:
mover = 8;
break;
@@ -201,8 +203,8 @@ reg_t kMapKeyToDir(EngineState *s, int argc, reg_t *argv) {
}
if (mover >= 0) {
- PUT_SEL32V(segMan, obj, SELECTOR(type), SCI_EVENT_JOYSTICK);
- PUT_SEL32V(segMan, obj, SELECTOR(message), mover);
+ writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_JOYSTICK);
+ writeSelectorValue(segMan, obj, SELECTOR(message), mover);
return make_reg(0, 1);
} else
return NULL_REG;
@@ -217,13 +219,13 @@ reg_t kGlobalToLocal(EngineState *s, int argc, reg_t *argv) {
SegManager *segMan = s->_segMan;
if (obj.segment) {
- int16 x = GET_SEL32V(segMan, obj, SELECTOR(x));
- int16 y = GET_SEL32V(segMan, obj, SELECTOR(y));
+ int16 x = readSelectorValue(segMan, obj, SELECTOR(x));
+ int16 y = readSelectorValue(segMan, obj, SELECTOR(y));
g_sci->_gfxCoordAdjuster->kernelGlobalToLocal(x, y, planeObject);
- PUT_SEL32V(segMan, obj, SELECTOR(x), x);
- PUT_SEL32V(segMan, obj, SELECTOR(y), y);
+ writeSelectorValue(segMan, obj, SELECTOR(x), x);
+ writeSelectorValue(segMan, obj, SELECTOR(y), y);
}
return s->r_acc;
@@ -236,13 +238,13 @@ reg_t kLocalToGlobal(EngineState *s, int argc, reg_t *argv) {
SegManager *segMan = s->_segMan;
if (obj.segment) {
- int16 x = GET_SEL32V(segMan, obj, SELECTOR(x));
- int16 y = GET_SEL32V(segMan, obj, SELECTOR(y));
+ int16 x = readSelectorValue(segMan, obj, SELECTOR(x));
+ int16 y = readSelectorValue(segMan, obj, SELECTOR(y));
g_sci->_gfxCoordAdjuster->kernelLocalToGlobal(x, y, planeObject);
- PUT_SEL32V(segMan, obj, SELECTOR(x), x);
- PUT_SEL32V(segMan, obj, SELECTOR(y), y);
+ writeSelectorValue(segMan, obj, SELECTOR(x), x);
+ writeSelectorValue(segMan, obj, SELECTOR(y), y);
}
return s->r_acc;
diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp
index e6b9a5388c..3e0ecd1a28 100644
--- a/engines/sci/engine/kfile.cpp
+++ b/engines/sci/engine/kfile.cpp
@@ -44,6 +44,8 @@ struct SavegameDesc {
int id;
int date;
int time;
+ int version;
+ char name[SCI_MAX_SAVENAME_LENGTH];
};
/*
@@ -245,13 +247,10 @@ static void fgets_wrapper(EngineState *s, char *dest, int maxsize, int handle) {
debugC(2, kDebugLevelFile, "FGets'ed \"%s\"", dest);
}
-static int _savegame_index_struct_compare(const void *a, const void *b) {
- const SavegameDesc *A = (const SavegameDesc *)a;
- const SavegameDesc *B = (const SavegameDesc *)b;
-
- if (B->date != A->date)
- return B->date - A->date;
- return B->time - A->time;
+static bool _savegame_index_struct_compare(const SavegameDesc &l, const SavegameDesc &r) {
+ if (l.date != r.date)
+ return (l.date > r.date);
+ return (l.time > r.time);
}
void listSavegames(Common::Array<SavegameDesc> &saves) {
@@ -265,7 +264,7 @@ void listSavegames(Common::Array<SavegameDesc> &saves) {
Common::SeekableReadStream *in;
if ((in = saveFileMan->openForLoading(filename))) {
SavegameMetadata meta;
- if (!get_savegame_metadata(in, &meta)) {
+ if (!get_savegame_metadata(in, &meta) || meta.savegame_name.empty()) {
// invalid
delete in;
continue;
@@ -278,6 +277,13 @@ void listSavegames(Common::Array<SavegameDesc> &saves) {
// We need to fix date in here, because we save DDMMYYYY instead of YYYYMMDD, so sorting wouldnt work
desc.date = ((desc.date & 0xFFFF) << 16) | ((desc.date & 0xFF0000) >> 8) | ((desc.date & 0xFF000000) >> 24);
desc.time = meta.savegame_time;
+ desc.version = meta.savegame_version;
+
+ if (meta.savegame_name.lastChar() == '\n')
+ meta.savegame_name.deleteLastChar();
+
+ Common::strlcpy(desc.name, meta.savegame_name.c_str(), SCI_MAX_SAVENAME_LENGTH);
+
debug(3, "Savegame in file %s ok, id %d", filename.c_str(), desc.id);
saves.push_back(desc);
@@ -285,35 +291,18 @@ void listSavegames(Common::Array<SavegameDesc> &saves) {
}
// Sort the list by creation date of the saves
- qsort(saves.begin(), saves.size(), sizeof(SavegameDesc), _savegame_index_struct_compare);
+ Common::sort(saves.begin(), saves.end(), _savegame_index_struct_compare);
}
bool Console::cmdListSaves(int argc, const char **argv) {
Common::Array<SavegameDesc> saves;
listSavegames(saves);
- Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
-
for (uint i = 0; i < saves.size(); i++) {
Common::String filename = g_sci->getSavegameName(saves[i].id);
- Common::SeekableReadStream *in;
- if ((in = saveFileMan->openForLoading(filename))) {
- SavegameMetadata meta;
- if (!get_savegame_metadata(in, &meta)) {
- // invalid
- delete in;
- continue;
- }
-
- if (!meta.savegame_name.empty()) {
- if (meta.savegame_name.lastChar() == '\n')
- meta.savegame_name.deleteLastChar();
-
- DebugPrintf("%s: '%s'\n", filename.c_str(), meta.savegame_name.c_str());
- }
- delete in;
- }
+ DebugPrintf("%s: '%s'\n", filename.c_str(), saves[i].name);
}
+
return true;
}
@@ -428,7 +417,7 @@ reg_t kGetSaveDir(EngineState *s, int argc, reg_t *argv) {
warning("kGetSaveDir called with %d parameter(s): %04x:%04x", argc, PRINT_REG(argv[0]));
#endif
- return make_reg(s->sys_strings_segment, SYS_STRING_SAVEDIR);
+ return make_reg(s->_segMan->getSysStringsSegment(), SYS_STRING_SAVEDIR);
}
reg_t kCheckFreeSpace(EngineState *s, int argc, reg_t *argv) {
@@ -449,90 +438,59 @@ reg_t kCheckFreeSpace(EngineState *s, int argc, reg_t *argv) {
reg_t kCheckSaveGame(EngineState *s, int argc, reg_t *argv) {
Common::String game_id = s->_segMan->getString(argv[0]);
- int savedir_nr = argv[1].toUint16();
+ uint16 savedir_nr = argv[1].toUint16();
debug(3, "kCheckSaveGame(%s, %d)", game_id.c_str(), savedir_nr);
Common::Array<SavegameDesc> saves;
listSavegames(saves);
- savedir_nr = saves[savedir_nr].id;
-
- if (savedir_nr > MAX_SAVEGAME_NR - 1) {
+ // Check for savegame slot being out of range
+ if (savedir_nr >= saves.size())
return NULL_REG;
- }
- Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
- Common::String filename = g_sci->getSavegameName(savedir_nr);
- Common::SeekableReadStream *in;
- if ((in = saveFileMan->openForLoading(filename))) {
- // found a savegame file
-
- SavegameMetadata meta;
- if (!get_savegame_metadata(in, &meta)) {
- // invalid
- s->r_acc = make_reg(0, 0);
- } else {
- s->r_acc = make_reg(0, 1);
- }
- delete in;
- } else {
- s->r_acc = make_reg(0, 1);
- }
+ // Check for compatible savegame version
+ int ver = saves[savedir_nr].version;
+ if (ver < MINIMUM_SAVEGAME_VERSION || ver > CURRENT_SAVEGAME_VERSION)
+ return NULL_REG;
- return s->r_acc;
+ // Otherwise we assume the savegame is OK
+ return make_reg(0, 1);
}
reg_t kGetSaveFiles(EngineState *s, int argc, reg_t *argv) {
Common::String game_id = s->_segMan->getString(argv[0]);
- reg_t nametarget = argv[1];
- reg_t *nameoffsets = s->_segMan->derefRegPtr(argv[2], 0);
debug(3, "kGetSaveFiles(%s)", game_id.c_str());
Common::Array<SavegameDesc> saves;
listSavegames(saves);
- s->r_acc = NULL_REG;
- Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
+ uint totalSaves = MIN<uint>(saves.size(), MAX_SAVEGAME_NR);
- for (uint i = 0; i < saves.size(); i++) {
- Common::String filename = g_sci->getSavegameName(saves[i].id);
- Common::SeekableReadStream *in;
- if ((in = saveFileMan->openForLoading(filename))) {
- // found a savegame file
+ reg_t *slot = s->_segMan->derefRegPtr(argv[2], totalSaves);
- SavegameMetadata meta;
- if (!get_savegame_metadata(in, &meta)) {
- // invalid
- delete in;
- continue;
- }
-
- if (!meta.savegame_name.empty()) {
- if (meta.savegame_name.lastChar() == '\n')
- meta.savegame_name.deleteLastChar();
-
- *nameoffsets = s->r_acc; // Store savegame ID
- ++s->r_acc.offset; // Increase number of files found
+ if (!slot) {
+ warning("kGetSaveFiles: %04X:%04X invalid or too small to hold slot data", PRINT_REG(argv[2]));
+ totalSaves = 0;
+ }
- nameoffsets++; // Make sure the next ID string address is written to the next pointer
- Common::String name = meta.savegame_name;
- if (name.size() > SCI_MAX_SAVENAME_LENGTH-1)
- name = Common::String(meta.savegame_name.c_str(), SCI_MAX_SAVENAME_LENGTH-1);
- s->_segMan->strcpy(nametarget, name.c_str());
+ const uint bufSize = (totalSaves * SCI_MAX_SAVENAME_LENGTH) + 1;
+ char *saveNames = new char[bufSize];
+ char *saveNamePtr = saveNames;
- // Increase name offset pointer accordingly
- nametarget.offset += SCI_MAX_SAVENAME_LENGTH;
- }
- delete in;
- }
+ for (uint i = 0; i < totalSaves; i++) {
+ *slot++ = make_reg(0, i); // Store slot
+ strcpy(saveNamePtr, saves[i].name);
+ saveNamePtr += SCI_MAX_SAVENAME_LENGTH;
}
- //free(gfname);
- s->_segMan->strcpy(nametarget, ""); // Terminate list
+ *saveNamePtr = 0; // Terminate list
- return s->r_acc;
+ s->_segMan->memcpy(argv[1], (byte *)saveNames, bufSize);
+ delete[] saveNames;
+
+ return make_reg(0, totalSaves);
}
reg_t kSaveGame(EngineState *s, int argc, reg_t *argv) {
diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp
index abc7efd743..abe55455de 100644
--- a/engines/sci/engine/kgraphics.cpp
+++ b/engines/sci/engine/kgraphics.cpp
@@ -23,8 +23,10 @@
*
*/
+#include "engines/util.h"
#include "graphics/cursorman.h"
#include "graphics/video/avi_decoder.h"
+#include "graphics/video/qt_decoder.h"
#include "graphics/surface.h"
#include "sci/sci.h"
@@ -51,8 +53,8 @@
namespace Sci {
void _k_dirloop(reg_t object, uint16 angle, EngineState *s, int argc, reg_t *argv) {
- GuiResourceId viewId = GET_SEL32V(s->_segMan, object, SELECTOR(view));
- uint16 signal = GET_SEL32V(s->_segMan, object, SELECTOR(signal));
+ GuiResourceId viewId = readSelectorValue(s->_segMan, object, SELECTOR(view));
+ uint16 signal = readSelectorValue(s->_segMan, object, SELECTOR(signal));
int16 loopNo;
int16 maxLoops;
bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
@@ -91,7 +93,7 @@ void _k_dirloop(reg_t object, uint16 angle, EngineState *s, int argc, reg_t *arg
if ((loopNo > 1) && (maxLoops < 4))
return;
- PUT_SEL32V(s->_segMan, object, SELECTOR(loop), loopNo);
+ writeSelectorValue(s->_segMan, object, SELECTOR(loop), loopNo);
}
static reg_t kSetCursorSci0(EngineState *s, int argc, reg_t *argv) {
@@ -146,6 +148,10 @@ static reg_t kSetCursorSci11(EngineState *s, int argc, reg_t *argv) {
int16 bottom = argv[2].toSint16();
int16 right = argv[3].toSint16();
+ // In SCI32, the right parameter seems to be divided by 2
+ if (getSciVersion() >= SCI_VERSION_2)
+ right *= 2;
+
if ((right >= left) && (bottom >= top)) {
Common::Rect rect = Common::Rect(left, top, right, bottom);
g_sci->_gfxCursor->kernelSetMoveZone(rect);
@@ -437,7 +443,7 @@ reg_t kCelWide(EngineState *s, int argc, reg_t *argv) {
reg_t kNumLoops(EngineState *s, int argc, reg_t *argv) {
reg_t object = argv[0];
- GuiResourceId viewId = GET_SEL32V(s->_segMan, object, SELECTOR(view));
+ GuiResourceId viewId = readSelectorValue(s->_segMan, object, SELECTOR(view));
int16 loopCount;
loopCount = g_sci->_gfxCache->kernelViewGetLoopCount(viewId);
@@ -449,8 +455,8 @@ reg_t kNumLoops(EngineState *s, int argc, reg_t *argv) {
reg_t kNumCels(EngineState *s, int argc, reg_t *argv) {
reg_t object = argv[0];
- GuiResourceId viewId = GET_SEL32V(s->_segMan, object, SELECTOR(view));
- int16 loopNo = GET_SEL32V(s->_segMan, object, SELECTOR(loop));
+ GuiResourceId viewId = readSelectorValue(s->_segMan, object, SELECTOR(view));
+ int16 loopNo = readSelectorValue(s->_segMan, object, SELECTOR(loop));
int16 celCount;
celCount = g_sci->_gfxCache->kernelViewGetCelCount(viewId, loopNo);
@@ -526,8 +532,8 @@ reg_t kBaseSetter(EngineState *s, int argc, reg_t *argv) {
// WORKAROUND for a problem in LSL1VGA. This allows the casino door to be opened,
// till the actual problem is found
if (!strcmp(g_sci->getGameID(), "lsl1sci") && s->currentRoomNumber() == 300) {
- int top = GET_SEL32V(s->_segMan, object, SELECTOR(brTop));
- PUT_SEL32V(s->_segMan, object, SELECTOR(brTop), top + 2);
+ int top = readSelectorValue(s->_segMan, object, SELECTOR(brTop));
+ writeSelectorValue(s->_segMan, object, SELECTOR(brTop), top + 2);
}
return s->r_acc;
@@ -741,12 +747,12 @@ Common::Rect kControlCreateRect(int16 x, int16 y, int16 x1, int16 y1) {
}
void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) {
- int16 type = GET_SEL32V(s->_segMan, controlObject, SELECTOR(type));
- int16 style = GET_SEL32V(s->_segMan, controlObject, SELECTOR(state));
- int16 x = GET_SEL32V(s->_segMan, controlObject, SELECTOR(nsLeft));
- int16 y = GET_SEL32V(s->_segMan, controlObject, SELECTOR(nsTop));
- GuiResourceId fontId = GET_SEL32V(s->_segMan, controlObject, SELECTOR(font));
- reg_t textReference = GET_SEL32(s->_segMan, controlObject, SELECTOR(text));
+ int16 type = readSelectorValue(s->_segMan, controlObject, SELECTOR(type));
+ int16 style = readSelectorValue(s->_segMan, controlObject, SELECTOR(state));
+ int16 x = readSelectorValue(s->_segMan, controlObject, SELECTOR(nsLeft));
+ int16 y = readSelectorValue(s->_segMan, controlObject, SELECTOR(nsTop));
+ GuiResourceId fontId = readSelectorValue(s->_segMan, controlObject, SELECTOR(font));
+ reg_t textReference = readSelector(s->_segMan, controlObject, SELECTOR(text));
Common::String text;
Common::Rect rect;
TextAlignment alignment;
@@ -762,8 +768,8 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) {
bool isAlias = false;
rect = kControlCreateRect(x, y,
- GET_SEL32V(s->_segMan, controlObject, SELECTOR(nsRight)),
- GET_SEL32V(s->_segMan, controlObject, SELECTOR(nsBottom)));
+ readSelectorValue(s->_segMan, controlObject, SELECTOR(nsRight)),
+ readSelectorValue(s->_segMan, controlObject, SELECTOR(nsBottom)));
if (!textReference.isNull())
text = s->_segMan->getString(textReference);
@@ -775,32 +781,32 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) {
return;
case SCI_CONTROLS_TYPE_TEXT:
- alignment = GET_SEL32V(s->_segMan, controlObject, SELECTOR(mode));
+ alignment = readSelectorValue(s->_segMan, controlObject, SELECTOR(mode));
debugC(2, kDebugLevelGraphics, "drawing text %04x:%04x ('%s') to %d,%d, mode=%d", PRINT_REG(controlObject), text.c_str(), x, y, alignment);
g_sci->_gfxControls->kernelDrawText(rect, controlObject, g_sci->strSplit(text.c_str()).c_str(), fontId, alignment, style, hilite);
return;
case SCI_CONTROLS_TYPE_TEXTEDIT:
- mode = GET_SEL32V(s->_segMan, controlObject, SELECTOR(mode));
- maxChars = GET_SEL32V(s->_segMan, controlObject, SELECTOR(max));
- cursorPos = GET_SEL32V(s->_segMan, controlObject, SELECTOR(cursor));
+ mode = readSelectorValue(s->_segMan, controlObject, SELECTOR(mode));
+ maxChars = readSelectorValue(s->_segMan, controlObject, SELECTOR(max));
+ cursorPos = readSelectorValue(s->_segMan, controlObject, SELECTOR(cursor));
debugC(2, kDebugLevelGraphics, "drawing edit control %04x:%04x (text %04x:%04x, '%s') to %d,%d", PRINT_REG(controlObject), PRINT_REG(textReference), text.c_str(), x, y);
g_sci->_gfxControls->kernelDrawTextEdit(rect, controlObject, g_sci->strSplit(text.c_str(), NULL).c_str(), fontId, mode, style, cursorPos, maxChars, hilite);
return;
case SCI_CONTROLS_TYPE_ICON:
- viewId = GET_SEL32V(s->_segMan, controlObject, SELECTOR(view));
+ viewId = readSelectorValue(s->_segMan, controlObject, SELECTOR(view));
{
- int l = GET_SEL32V(s->_segMan, controlObject, SELECTOR(loop));
+ int l = readSelectorValue(s->_segMan, controlObject, SELECTOR(loop));
loopNo = (l & 0x80) ? l - 256 : l;
- int c = GET_SEL32V(s->_segMan, controlObject, SELECTOR(cel));
+ int c = readSelectorValue(s->_segMan, controlObject, SELECTOR(cel));
celNo = (c & 0x80) ? c - 256 : c;
// Game-specific: *ONLY* the jones EGA/VGA sierra interpreter contain code using priority selector
// ALL other games use a hardcoded -1 (madness!)
// We are detecting jones/talkie as "jones" as well, but the sierra interpreter of talkie doesnt have this
// "hack". Hopefully it wont cause regressions (the code causes regressions if used against kq5/floppy)
if (!strcmp(g_sci->getGameID(), "jones"))
- priority = GET_SEL32V(s->_segMan, controlObject, SELECTOR(priority));
+ priority = readSelectorValue(s->_segMan, controlObject, SELECTOR(priority));
else
priority = -1;
}
@@ -813,17 +819,17 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) {
if (type == SCI_CONTROLS_TYPE_LIST_ALIAS)
isAlias = true;
- maxChars = GET_SEL32V(s->_segMan, controlObject, SELECTOR(x)); // max chars per entry
- cursorOffset = GET_SEL32V(s->_segMan, controlObject, SELECTOR(cursor));
+ maxChars = readSelectorValue(s->_segMan, controlObject, SELECTOR(x)); // max chars per entry
+ cursorOffset = readSelectorValue(s->_segMan, controlObject, SELECTOR(cursor));
if (g_sci->getKernel()->_selectorCache.topString != -1) {
// Games from early SCI1 onwards use topString
- upperOffset = GET_SEL32V(s->_segMan, controlObject, SELECTOR(topString));
+ upperOffset = readSelectorValue(s->_segMan, controlObject, SELECTOR(topString));
} else {
// Earlier games use lsTop or brTop
- if (lookup_selector(s->_segMan, controlObject, g_sci->getKernel()->_selectorCache.brTop, NULL, NULL) == kSelectorVariable)
- upperOffset = GET_SEL32V(s->_segMan, controlObject, SELECTOR(brTop));
+ if (lookupSelector(s->_segMan, controlObject, g_sci->getKernel()->_selectorCache.brTop, NULL, NULL) == kSelectorVariable)
+ upperOffset = readSelectorValue(s->_segMan, controlObject, SELECTOR(brTop));
else
- upperOffset = GET_SEL32V(s->_segMan, controlObject, SELECTOR(lsTop));
+ upperOffset = readSelectorValue(s->_segMan, controlObject, SELECTOR(lsTop));
}
// Count string entries in NULL terminated string list
@@ -874,8 +880,8 @@ reg_t kDrawControl(EngineState *s, int argc, reg_t *argv) {
// Disable the "Change Directory" button, as we don't allow the game engine to
// change the directory where saved games are placed
if (objName == "changeDirI") {
- int state = GET_SEL32V(s->_segMan, controlObject, SELECTOR(state));
- PUT_SEL32V(s->_segMan, controlObject, SELECTOR(state), (state | SCI_CONTROLS_STYLE_DISABLED) & ~SCI_CONTROLS_STYLE_ENABLED);
+ int state = readSelectorValue(s->_segMan, controlObject, SELECTOR(state));
+ writeSelectorValue(s->_segMan, controlObject, SELECTOR(state), (state | SCI_CONTROLS_STYLE_DISABLED) & ~SCI_CONTROLS_STYLE_ENABLED);
}
_k_GenericDrawControl(s, controlObject, false);
@@ -894,7 +900,7 @@ reg_t kEditControl(EngineState *s, int argc, reg_t *argv) {
reg_t eventObject = argv[1];
if (!controlObject.isNull()) {
- int16 controlType = GET_SEL32V(s->_segMan, controlObject, SELECTOR(type));
+ int16 controlType = readSelectorValue(s->_segMan, controlObject, SELECTOR(type));
switch (controlType) {
case SCI_CONTROLS_TYPE_TEXTEDIT:
@@ -1080,11 +1086,12 @@ reg_t kDisplay(EngineState *s, int argc, reg_t *argv) {
reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
// Hide the cursor if it's showing and then show it again if it was
// previously visible.
- bool reshowCursor;
-
- reshowCursor = g_sci->_gfxCursor->isVisible();
+ bool reshowCursor = g_sci->_gfxCursor->isVisible();
if (reshowCursor)
g_sci->_gfxCursor->kernelHide();
+
+ uint16 screenWidth = g_system->getWidth();
+ uint16 screenHeight = g_system->getHeight();
Graphics::VideoDecoder *videoDecoder = 0;
@@ -1094,8 +1101,18 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
if (g_sci->getPlatform() == Common::kPlatformMacintosh) {
// Mac QuickTime
// The only argument is the string for the video
- warning("TODO: Play QuickTime movie '%s'", filename.c_str());
- return s->r_acc;
+
+ // HACK: Switch to 16bpp graphics for Cinepak.
+ initGraphics(screenWidth, screenHeight, screenWidth > 320, NULL);
+
+ if (g_system->getScreenFormat().bytesPerPixel == 1) {
+ warning("This video requires >8bpp color to be displayed, but could not switch to RGB color mode.");
+ return NULL_REG;
+ }
+
+ videoDecoder = new Graphics::QuickTimeDecoder();
+ if (!videoDecoder->loadFile(filename))
+ error("Could not open '%s'", filename.c_str());
} else {
// DOS SEQ
// SEQ's are called with no subops, just the string and delay
@@ -1110,7 +1127,7 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
}
}
} else {
- // Windows AVI (Macintosh QuickTime? Need to check KQ6 Macintosh)
+ // Windows AVI
// TODO: This appears to be some sort of subop. case 0 contains the string
// for the video, so we'll just play it from there for now.
@@ -1142,10 +1159,10 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
}
if (videoDecoder) {
- uint16 x = (g_system->getWidth() - videoDecoder->getWidth()) / 2;
- uint16 y = (g_system->getHeight() - videoDecoder->getHeight()) / 2;
+ uint16 x = (screenWidth - videoDecoder->getWidth()) / 2;
+ uint16 y = (screenHeight - videoDecoder->getHeight()) / 2;
- while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo()) {
+ while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo()) {
if (videoDecoder->needsUpdate()) {
Graphics::Surface *frame = videoDecoder->decodeNextFrame();
if (frame) {
@@ -1164,9 +1181,15 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
g_system->delayMillis(10);
}
+
+ // HACK: Switch back to 8bpp if we played a QuickTime video.
+ // We also won't be copying the screen to the SCI screen...
+ if (g_system->getScreenFormat().bytesPerPixel != 1)
+ initGraphics(screenWidth, screenHeight, screenWidth > 320);
+ else
+ g_sci->_gfxScreen->kernelSyncWithFramebuffer();
delete videoDecoder;
- g_sci->_gfxScreen->kernelSyncWithFramebuffer();
}
if (reshowCursor)
diff --git a/engines/sci/engine/klists.cpp b/engines/sci/engine/klists.cpp
index c04454ca3d..f06f3eec77 100644
--- a/engines/sci/engine/klists.cpp
+++ b/engines/sci/engine/klists.cpp
@@ -155,28 +155,10 @@ reg_t kDisposeList(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
-static reg_t _k_new_node(EngineState *s, reg_t value, reg_t key) {
- reg_t nodebase;
- Node *n = s->_segMan->allocateNode(&nodebase);
-
- if (!n) {
- error("[Kernel] Out of memory while creating a node");
- return NULL_REG;
- }
-
- n->pred = n->succ = NULL_REG;
- n->key = key;
- n->value = value;
-
- return nodebase;
-}
-
reg_t kNewNode(EngineState *s, int argc, reg_t *argv) {
-
- if (argc == 1)
- s->r_acc = _k_new_node(s, argv[0], argv[0]);
- else
- s->r_acc = _k_new_node(s, argv[0], argv[1]);
+ reg_t nodeValue = argv[0];
+ reg_t nodeKey = (argc == 2) ? argv[1] : NULL_REG;
+ s->r_acc = s->_segMan->newNode(nodeValue, nodeKey);
debugC(2, kDebugLevelNodes, "New nodebase at %04x:%04x", PRINT_REG(s->r_acc));
@@ -415,11 +397,11 @@ reg_t kSort(EngineState *s, int argc, reg_t *argv) {
reg_t dest = argv[1];
reg_t order_func = argv[2];
- int input_size = (int16)GET_SEL32V(segMan, source, SELECTOR(size));
+ int input_size = (int16)readSelectorValue(segMan, source, SELECTOR(size));
int i;
- reg_t input_data = GET_SEL32(segMan, source, SELECTOR(elements));
- reg_t output_data = GET_SEL32(segMan, dest, SELECTOR(elements));
+ reg_t input_data = readSelector(segMan, source, SELECTOR(elements));
+ reg_t output_data = readSelector(segMan, dest, SELECTOR(elements));
List *list;
Node *node;
@@ -430,10 +412,10 @@ reg_t kSort(EngineState *s, int argc, reg_t *argv) {
if (output_data.isNull()) {
list = s->_segMan->allocateList(&output_data);
list->first = list->last = NULL_REG;
- PUT_SEL32(segMan, dest, SELECTOR(elements), output_data);
+ writeSelector(segMan, dest, SELECTOR(elements), output_data);
}
- PUT_SEL32V(segMan, dest, SELECTOR(size), input_size);
+ writeSelectorValue(segMan, dest, SELECTOR(size), input_size);
list = s->_segMan->lookupList(input_data);
node = s->_segMan->lookupNode(list->first);
@@ -442,7 +424,7 @@ reg_t kSort(EngineState *s, int argc, reg_t *argv) {
i = 0;
while (node) {
- invoke_selector(INV_SEL(s, order_func, doit, kStopOnInvalidSelector), 1, node->value);
+ invokeSelector(INV_SEL(s, order_func, doit, kStopOnInvalidSelector), 1, node->value);
temp_array[i].key = node->key;
temp_array[i].value = node->value;
temp_array[i].order = s->r_acc;
@@ -453,7 +435,7 @@ reg_t kSort(EngineState *s, int argc, reg_t *argv) {
qsort(temp_array, input_size, sizeof(sort_temp_t), sort_temp_cmp);
for (i = 0;i < input_size;i++) {
- reg_t lNode = _k_new_node(s, temp_array[i].key, temp_array[i].value);
+ reg_t lNode = s->_segMan->newNode(temp_array[i].value, temp_array[i].key);
_k_add_to_end(s, output_data, lNode);
}
@@ -533,15 +515,15 @@ reg_t kListEachElementDo(EngineState *s, int argc, reg_t *argv) {
curObject = curNode->value;
// First, check if the target selector is a variable
- if (lookup_selector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) {
+ if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) {
// This can only happen with 3 params (list, target selector, variable)
if (argc != 3) {
warning("kListEachElementDo: Attempted to modify a variable selector with %d params", argc);
} else {
- write_selector(s->_segMan, curObject, slc, argv[2]);
+ writeSelector(s->_segMan, curObject, slc, argv[2]);
}
} else {
- invoke_selector_argv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2);
+ invokeSelectorArgv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2);
}
curNode = s->_segMan->lookupNode(nextNode);
@@ -566,11 +548,11 @@ reg_t kListFirstTrue(EngineState *s, int argc, reg_t *argv) {
curObject = curNode->value;
// First, check if the target selector is a variable
- if (lookup_selector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) {
+ if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) {
// Can this happen with variable selectors?
warning("kListFirstTrue: Attempted to access a variable selector");
} else {
- invoke_selector_argv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2);
+ invokeSelectorArgv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2);
// Check if the result is true
if (!s->r_acc.isNull())
@@ -600,11 +582,11 @@ reg_t kListAllTrue(EngineState *s, int argc, reg_t *argv) {
curObject = curNode->value;
// First, check if the target selector is a variable
- if (lookup_selector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) {
+ if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) {
// Can this happen with variable selectors?
warning("kListAllTrue: Attempted to access a variable selector");
} else {
- invoke_selector_argv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2);
+ invokeSelectorArgv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2);
// Check if the result isn't true
if (s->r_acc.isNull())
diff --git a/engines/sci/engine/kmisc.cpp b/engines/sci/engine/kmisc.cpp
index c05a2bc57b..f91ba0fd82 100644
--- a/engines/sci/engine/kmisc.cpp
+++ b/engines/sci/engine/kmisc.cpp
@@ -41,7 +41,7 @@ reg_t kRestartGame(EngineState *s, int argc, reg_t *argv) {
s->shrinkStackToBase();
- script_abort_flag = 1; // Force vm to abort ASAP
+ s->script_abort_flag = 1; // Force vm to abort ASAP
return NULL_REG;
}
@@ -252,10 +252,15 @@ reg_t kMemory(EngineState *s, int argc, reg_t *argv) {
break;
}
case K_MEMORY_PEEK : {
+ if (!argv[1].segment) {
+ // This occurs in KQ5CD when interacting with certain objects
+ warning("Attempt to peek invalid memory at %04x:%04x", PRINT_REG(argv[1]));
+ return s->r_acc;
+ }
+
SegmentRef ref = s->_segMan->dereference(argv[1]);
if (!ref.isValid() || ref.maxSize < 2) {
- // This occurs in KQ5CD when interacting with certain objects
warning("Attempt to peek invalid memory at %04x:%04x", PRINT_REG(argv[1]));
return s->r_acc;
}
diff --git a/engines/sci/engine/kmovement.cpp b/engines/sci/engine/kmovement.cpp
index 42e690422c..499aeabcc6 100644
--- a/engines/sci/engine/kmovement.cpp
+++ b/engines/sci/engine/kmovement.cpp
@@ -158,8 +158,8 @@ reg_t kSetJump(EngineState *s, int argc, reg_t *argv) {
debugC(2, kDebugLevelBresen, "SetJump for object at %04x:%04x", PRINT_REG(object));
debugC(2, kDebugLevelBresen, "xStep: %d, yStep: %d", vx, vy);
- PUT_SEL32V(segMan, object, SELECTOR(xStep), vx);
- PUT_SEL32V(segMan, object, SELECTOR(yStep), vy);
+ writeSelectorValue(segMan, object, SELECTOR(xStep), vx);
+ writeSelectorValue(segMan, object, SELECTOR(yStep), vy);
return s->r_acc;
}
@@ -168,9 +168,9 @@ reg_t kSetJump(EngineState *s, int argc, reg_t *argv) {
#define _K_BRESEN_AXIS_Y 1
static void initialize_bresen(SegManager *segMan, int argc, reg_t *argv, reg_t mover, int step_factor, int deltax, int deltay) {
- reg_t client = GET_SEL32(segMan, mover, SELECTOR(client));
- int stepx = (int16)GET_SEL32V(segMan, client, SELECTOR(xStep)) * step_factor;
- int stepy = (int16)GET_SEL32V(segMan, client, SELECTOR(yStep)) * step_factor;
+ reg_t client = readSelector(segMan, mover, SELECTOR(client));
+ int stepx = (int16)readSelectorValue(segMan, client, SELECTOR(xStep)) * step_factor;
+ int stepy = (int16)readSelectorValue(segMan, client, SELECTOR(yStep)) * step_factor;
int numsteps_x = stepx ? (abs(deltax) + stepx - 1) / stepx : 0;
int numsteps_y = stepy ? (abs(deltay) + stepy - 1) / stepy : 0;
int bdi, i1;
@@ -191,15 +191,15 @@ static void initialize_bresen(SegManager *segMan, int argc, reg_t *argv, reg_t m
/* if (abs(deltax) > abs(deltay)) {*/ // Bresenham on y
if (numsteps_y < numsteps_x) {
- PUT_SEL32V(segMan, mover, SELECTOR(b_xAxis), _K_BRESEN_AXIS_Y);
- PUT_SEL32V(segMan, mover, SELECTOR(b_incr), (deltay < 0) ? -1 : 1);
+ writeSelectorValue(segMan, mover, SELECTOR(b_xAxis), _K_BRESEN_AXIS_Y);
+ writeSelectorValue(segMan, mover, SELECTOR(b_incr), (deltay < 0) ? -1 : 1);
//i1 = 2 * (abs(deltay) - abs(deltay_step * numsteps)) * abs(deltax_step);
//bdi = -abs(deltax);
i1 = 2 * (abs(deltay) - abs(deltay_step * (numsteps - 1))) * abs(deltax_step);
bdi = -abs(deltax);
} else { // Bresenham on x
- PUT_SEL32V(segMan, mover, SELECTOR(b_xAxis), _K_BRESEN_AXIS_X);
- PUT_SEL32V(segMan, mover, SELECTOR(b_incr), (deltax < 0) ? -1 : 1);
+ writeSelectorValue(segMan, mover, SELECTOR(b_xAxis), _K_BRESEN_AXIS_X);
+ writeSelectorValue(segMan, mover, SELECTOR(b_incr), (deltax < 0) ? -1 : 1);
//i1= 2 * (abs(deltax) - abs(deltax_step * numsteps)) * abs(deltay_step);
//bdi = -abs(deltay);
i1 = 2 * (abs(deltax) - abs(deltax_step * (numsteps - 1))) * abs(deltay_step);
@@ -207,26 +207,26 @@ static void initialize_bresen(SegManager *segMan, int argc, reg_t *argv, reg_t m
}
- PUT_SEL32V(segMan, mover, SELECTOR(dx), deltax_step);
- PUT_SEL32V(segMan, mover, SELECTOR(dy), deltay_step);
+ writeSelectorValue(segMan, mover, SELECTOR(dx), deltax_step);
+ writeSelectorValue(segMan, mover, SELECTOR(dy), deltay_step);
debugC(2, kDebugLevelBresen, "Init bresen for mover %04x:%04x: d=(%d,%d)", PRINT_REG(mover), deltax, deltay);
debugC(2, kDebugLevelBresen, " steps=%d, mv=(%d, %d), i1= %d, i2=%d",
numsteps, deltax_step, deltay_step, i1, bdi*2);
- //PUT_SEL32V(segMan, mover, SELECTOR(b_movCnt), numsteps); // Needed for HQ1/Ogre?
- PUT_SEL32V(segMan, mover, SELECTOR(b_di), bdi);
- PUT_SEL32V(segMan, mover, SELECTOR(b_i1), i1);
- PUT_SEL32V(segMan, mover, SELECTOR(b_i2), bdi * 2);
+ //writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), numsteps); // Needed for HQ1/Ogre?
+ writeSelectorValue(segMan, mover, SELECTOR(b_di), bdi);
+ writeSelectorValue(segMan, mover, SELECTOR(b_i1), i1);
+ writeSelectorValue(segMan, mover, SELECTOR(b_i2), bdi * 2);
}
reg_t kInitBresen(EngineState *s, int argc, reg_t *argv) {
SegManager *segMan = s->_segMan;
reg_t mover = argv[0];
- reg_t client = GET_SEL32(segMan, mover, SELECTOR(client));
+ reg_t client = readSelector(segMan, mover, SELECTOR(client));
- int deltax = (int16)GET_SEL32V(segMan, mover, SELECTOR(x)) - (int16)GET_SEL32V(segMan, client, SELECTOR(x));
- int deltay = (int16)GET_SEL32V(segMan, mover, SELECTOR(y)) - (int16)GET_SEL32V(segMan, client, SELECTOR(y));
+ int deltax = (int16)readSelectorValue(segMan, mover, SELECTOR(x)) - (int16)readSelectorValue(segMan, client, SELECTOR(x));
+ int deltay = (int16)readSelectorValue(segMan, mover, SELECTOR(y)) - (int16)readSelectorValue(segMan, client, SELECTOR(y));
int step_factor = (argc < 1) ? argv[1].toUint16() : 1;
initialize_bresen(s->_segMan, argc, argv, mover, step_factor, deltax, deltay);
@@ -240,42 +240,42 @@ reg_t kInitBresen(EngineState *s, int argc, reg_t *argv) {
reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) {
SegManager *segMan = s->_segMan;
reg_t mover = argv[0];
- reg_t client = GET_SEL32(segMan, mover, SELECTOR(client));
+ reg_t client = readSelector(segMan, mover, SELECTOR(client));
- int x = (int16)GET_SEL32V(segMan, client, SELECTOR(x));
- int y = (int16)GET_SEL32V(segMan, client, SELECTOR(y));
+ int x = (int16)readSelectorValue(segMan, client, SELECTOR(x));
+ int y = (int16)readSelectorValue(segMan, client, SELECTOR(y));
int oldx, oldy, destx, desty, dx, dy, bdi, bi1, bi2, movcnt, bdelta, axis;
- uint16 signal = GET_SEL32V(segMan, client, SELECTOR(signal));
+ uint16 signal = readSelectorValue(segMan, client, SELECTOR(signal));
int completed = 0;
- int max_movcnt = GET_SEL32V(segMan, client, SELECTOR(moveSpeed));
+ int max_movcnt = readSelectorValue(segMan, client, SELECTOR(moveSpeed));
if (getSciVersion() > SCI_VERSION_01)
signal &= ~kSignalHitObstacle;
- PUT_SEL32(segMan, client, SELECTOR(signal), make_reg(0, signal)); // This is a NOP for SCI0
+ writeSelector(segMan, client, SELECTOR(signal), make_reg(0, signal)); // This is a NOP for SCI0
oldx = x;
oldy = y;
- destx = (int16)GET_SEL32V(segMan, mover, SELECTOR(x));
- desty = (int16)GET_SEL32V(segMan, mover, SELECTOR(y));
- dx = (int16)GET_SEL32V(segMan, mover, SELECTOR(dx));
- dy = (int16)GET_SEL32V(segMan, mover, SELECTOR(dy));
- bdi = (int16)GET_SEL32V(segMan, mover, SELECTOR(b_di));
- bi1 = (int16)GET_SEL32V(segMan, mover, SELECTOR(b_i1));
- bi2 = (int16)GET_SEL32V(segMan, mover, SELECTOR(b_i2));
- movcnt = GET_SEL32V(segMan, mover, SELECTOR(b_movCnt));
- bdelta = (int16)GET_SEL32V(segMan, mover, SELECTOR(b_incr));
- axis = (int16)GET_SEL32V(segMan, mover, SELECTOR(b_xAxis));
+ destx = (int16)readSelectorValue(segMan, mover, SELECTOR(x));
+ desty = (int16)readSelectorValue(segMan, mover, SELECTOR(y));
+ dx = (int16)readSelectorValue(segMan, mover, SELECTOR(dx));
+ dy = (int16)readSelectorValue(segMan, mover, SELECTOR(dy));
+ bdi = (int16)readSelectorValue(segMan, mover, SELECTOR(b_di));
+ bi1 = (int16)readSelectorValue(segMan, mover, SELECTOR(b_i1));
+ bi2 = (int16)readSelectorValue(segMan, mover, SELECTOR(b_i2));
+ movcnt = readSelectorValue(segMan, mover, SELECTOR(b_movCnt));
+ bdelta = (int16)readSelectorValue(segMan, mover, SELECTOR(b_incr));
+ axis = (int16)readSelectorValue(segMan, mover, SELECTOR(b_xAxis));
//printf("movecnt %d, move speed %d\n", movcnt, max_movcnt);
if (g_sci->_features->handleMoveCount()) {
if (max_movcnt > movcnt) {
++movcnt;
- PUT_SEL32V(segMan, mover, SELECTOR(b_movCnt), movcnt); // Needed for HQ1/Ogre?
+ writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), movcnt); // Needed for HQ1/Ogre?
return NULL_REG;
} else {
movcnt = 0;
- PUT_SEL32V(segMan, mover, SELECTOR(b_movCnt), movcnt); // Needed for HQ1/Ogre?
+ writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), movcnt); // Needed for HQ1/Ogre?
}
}
@@ -288,7 +288,7 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) {
dy += bdelta;
}
- PUT_SEL32V(segMan, mover, SELECTOR(b_di), bdi);
+ writeSelectorValue(segMan, mover, SELECTOR(b_di), bdi);
x += dx;
y += dy;
@@ -310,24 +310,24 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) {
debugC(2, kDebugLevelBresen, "Finished mover %04x:%04x", PRINT_REG(mover));
}
- PUT_SEL32V(segMan, client, SELECTOR(x), x);
- PUT_SEL32V(segMan, client, SELECTOR(y), y);
+ writeSelectorValue(segMan, client, SELECTOR(x), x);
+ writeSelectorValue(segMan, client, SELECTOR(y), y);
debugC(2, kDebugLevelBresen, "New data: (x,y)=(%d,%d), di=%d", x, y, bdi);
if (g_sci->getKernel()->_selectorCache.cantBeHere != -1) {
- invoke_selector(INV_SEL(s, client, cantBeHere, kStopOnInvalidSelector), 0);
+ invokeSelector(INV_SEL(s, client, cantBeHere, kStopOnInvalidSelector), 0);
s->r_acc = make_reg(0, !s->r_acc.offset);
} else {
- invoke_selector(INV_SEL(s, client, canBeHere, kStopOnInvalidSelector), 0);
+ invokeSelector(INV_SEL(s, client, canBeHere, kStopOnInvalidSelector), 0);
}
if (!s->r_acc.offset) { // Contains the return value
- signal = GET_SEL32V(segMan, client, SELECTOR(signal));
+ signal = readSelectorValue(segMan, client, SELECTOR(signal));
- PUT_SEL32V(segMan, client, SELECTOR(x), oldx);
- PUT_SEL32V(segMan, client, SELECTOR(y), oldy);
- PUT_SEL32V(segMan, client, SELECTOR(signal), (signal | kSignalHitObstacle));
+ writeSelectorValue(segMan, client, SELECTOR(x), oldx);
+ writeSelectorValue(segMan, client, SELECTOR(y), oldy);
+ writeSelectorValue(segMan, client, SELECTOR(signal), (signal | kSignalHitObstacle));
debugC(2, kDebugLevelBresen, "Finished mover %04x:%04x by collision", PRINT_REG(mover));
completed = 1;
@@ -335,7 +335,7 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) {
if ((getSciVersion() >= SCI_VERSION_1_EGA))
if (completed)
- invoke_selector(INV_SEL(s, mover, moveDone, kStopOnInvalidSelector), 0);
+ invokeSelector(INV_SEL(s, mover, moveDone, kStopOnInvalidSelector), 0);
return make_reg(0, completed);
}
@@ -377,15 +377,15 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) {
return NULL_REG;
}
- client = GET_SEL32(segMan, avoider, SELECTOR(client));
+ client = readSelector(segMan, avoider, SELECTOR(client));
if (!s->_segMan->isHeapObject(client)) {
warning("DoAvoider() where client %04x:%04x is not an object", PRINT_REG(client));
return NULL_REG;
}
- looper = GET_SEL32(segMan, client, SELECTOR(looper));
- mover = GET_SEL32(segMan, client, SELECTOR(mover));
+ looper = readSelector(segMan, client, SELECTOR(looper));
+ mover = readSelector(segMan, client, SELECTOR(mover));
if (!s->_segMan->isHeapObject(mover)) {
if (mover.segment) {
@@ -394,38 +394,38 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
- destx = GET_SEL32V(segMan, mover, SELECTOR(x));
- desty = GET_SEL32V(segMan, mover, SELECTOR(y));
+ destx = readSelectorValue(segMan, mover, SELECTOR(x));
+ desty = readSelectorValue(segMan, mover, SELECTOR(y));
debugC(2, kDebugLevelBresen, "Doing avoider %04x:%04x (dest=%d,%d)", PRINT_REG(avoider), destx, desty);
- if (invoke_selector(INV_SEL(s, mover, doit, kContinueOnInvalidSelector) , 0)) {
+ if (invokeSelector(INV_SEL(s, mover, doit, kContinueOnInvalidSelector) , 0)) {
error("Mover %04x:%04x of avoider %04x:%04x doesn't have a doit() funcselector", PRINT_REG(mover), PRINT_REG(avoider));
return NULL_REG;
}
- mover = GET_SEL32(segMan, client, SELECTOR(mover));
+ mover = readSelector(segMan, client, SELECTOR(mover));
if (!mover.segment) // Mover has been disposed?
return s->r_acc; // Return gracefully.
- if (invoke_selector(INV_SEL(s, client, isBlocked, kContinueOnInvalidSelector) , 0)) {
+ if (invokeSelector(INV_SEL(s, client, isBlocked, kContinueOnInvalidSelector) , 0)) {
error("Client %04x:%04x of avoider %04x:%04x doesn't"
" have an isBlocked() funcselector", PRINT_REG(client), PRINT_REG(avoider));
return NULL_REG;
}
- dx = destx - GET_SEL32V(segMan, client, SELECTOR(x));
- dy = desty - GET_SEL32V(segMan, client, SELECTOR(y));
+ dx = destx - readSelectorValue(segMan, client, SELECTOR(x));
+ dy = desty - readSelectorValue(segMan, client, SELECTOR(y));
angle = getAngle(dx, dy);
debugC(2, kDebugLevelBresen, "Movement (%d,%d), angle %d is %sblocked", dx, dy, angle, (s->r_acc.offset) ? " " : "not ");
if (s->r_acc.offset) { // isBlocked() returned non-zero
int rotation = (rand() & 1) ? 45 : (360 - 45); // Clockwise/counterclockwise
- int oldx = GET_SEL32V(segMan, client, SELECTOR(x));
- int oldy = GET_SEL32V(segMan, client, SELECTOR(y));
- int xstep = GET_SEL32V(segMan, client, SELECTOR(xStep));
- int ystep = GET_SEL32V(segMan, client, SELECTOR(yStep));
+ int oldx = readSelectorValue(segMan, client, SELECTOR(x));
+ int oldy = readSelectorValue(segMan, client, SELECTOR(y));
+ int xstep = readSelectorValue(segMan, client, SELECTOR(xStep));
+ int ystep = readSelectorValue(segMan, client, SELECTOR(yStep));
int moves;
debugC(2, kDebugLevelBresen, " avoider %04x:%04x", PRINT_REG(avoider));
@@ -434,23 +434,23 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) {
int move_x = (int)(sin(angle * PI / 180.0) * (xstep));
int move_y = (int)(-cos(angle * PI / 180.0) * (ystep));
- PUT_SEL32V(segMan, client, SELECTOR(x), oldx + move_x);
- PUT_SEL32V(segMan, client, SELECTOR(y), oldy + move_y);
+ writeSelectorValue(segMan, client, SELECTOR(x), oldx + move_x);
+ writeSelectorValue(segMan, client, SELECTOR(y), oldy + move_y);
debugC(2, kDebugLevelBresen, "Pos (%d,%d): Trying angle %d; delta=(%d,%d)", oldx, oldy, angle, move_x, move_y);
- if (invoke_selector(INV_SEL(s, client, canBeHere, kContinueOnInvalidSelector) , 0)) {
+ if (invokeSelector(INV_SEL(s, client, canBeHere, kContinueOnInvalidSelector) , 0)) {
error("Client %04x:%04x of avoider %04x:%04x doesn't"
" have a canBeHere() funcselector", PRINT_REG(client), PRINT_REG(avoider));
return NULL_REG;
}
- PUT_SEL32V(segMan, client, SELECTOR(x), oldx);
- PUT_SEL32V(segMan, client, SELECTOR(y), oldy);
+ writeSelectorValue(segMan, client, SELECTOR(x), oldx);
+ writeSelectorValue(segMan, client, SELECTOR(y), oldy);
if (s->r_acc.offset) { // We can be here
debugC(2, kDebugLevelBresen, "Success");
- PUT_SEL32V(segMan, client, SELECTOR(heading), angle);
+ writeSelectorValue(segMan, client, SELECTOR(heading), angle);
return make_reg(0, angle);
}
@@ -463,17 +463,17 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) {
warning("DoAvoider failed for avoider %04x:%04x", PRINT_REG(avoider));
} else {
- int heading = GET_SEL32V(segMan, client, SELECTOR(heading));
+ int heading = readSelectorValue(segMan, client, SELECTOR(heading));
if (heading == -1)
return s->r_acc; // No change
- PUT_SEL32V(segMan, client, SELECTOR(heading), angle);
+ writeSelectorValue(segMan, client, SELECTOR(heading), angle);
s->r_acc = make_reg(0, angle);
if (looper.segment) {
- if (invoke_selector(INV_SEL(s, looper, doit, kContinueOnInvalidSelector), 2, angle, client)) {
+ if (invokeSelector(INV_SEL(s, looper, doit, kContinueOnInvalidSelector), 2, angle, client)) {
error("Looper %04x:%04x of avoider %04x:%04x doesn't"
" have a doit() funcselector", PRINT_REG(looper), PRINT_REG(avoider));
} else
diff --git a/engines/sci/engine/kparse.cpp b/engines/sci/engine/kparse.cpp
index 0254d21642..785ff39d22 100644
--- a/engines/sci/engine/kparse.cpp
+++ b/engines/sci/engine/kparse.cpp
@@ -42,6 +42,7 @@ reg_t kSaid(EngineState *s, int argc, reg_t *argv) {
reg_t heap_said_block = argv[0];
byte *said_block;
int new_lastmatch;
+ Vocabulary *voc = g_sci->getVocabulary();
#ifdef DEBUG_PARSER
const int debug_parser = 1;
#else
@@ -63,7 +64,7 @@ reg_t kSaid(EngineState *s, int argc, reg_t *argv) {
s->_voc->decipherSaidBlock(said_block);
#endif
- if (s->_voc->parser_event.isNull() || (GET_SEL32V(s->_segMan, s->_voc->parser_event, SELECTOR(claimed)))) {
+ if (voc->parser_event.isNull() || (readSelectorValue(s->_segMan, voc->parser_event, SELECTOR(claimed)))) {
return NULL_REG;
}
@@ -77,7 +78,7 @@ reg_t kSaid(EngineState *s, int argc, reg_t *argv) {
s->r_acc = make_reg(0, 1);
if (new_lastmatch != SAID_PARTIAL_MATCH)
- PUT_SEL32V(s->_segMan, s->_voc->parser_event, SELECTOR(claimed), 1);
+ writeSelectorValue(s->_segMan, voc->parser_event, SELECTOR(claimed), 1);
} else {
return NULL_REG;
@@ -92,15 +93,15 @@ reg_t kParse(EngineState *s, int argc, reg_t *argv) {
char *error;
ResultWordList words;
reg_t event = argv[1];
- Vocabulary *voc = s->_voc;
+ Vocabulary *voc = g_sci->getVocabulary();
- s->_voc->parser_event = event;
+ voc->parser_event = event;
bool res = voc->tokenizeString(words, string.c_str(), &error);
- s->_voc->parserIsValid = false; /* not valid */
+ voc->parserIsValid = false; /* not valid */
if (res && !words.empty()) {
- s->_voc->synonymizeTokens(words);
+ voc->synonymizeTokens(words);
s->r_acc = make_reg(0, 1);
@@ -115,32 +116,32 @@ reg_t kParse(EngineState *s, int argc, reg_t *argv) {
if (syntax_fail) {
s->r_acc = make_reg(0, 1);
- PUT_SEL32V(segMan, event, SELECTOR(claimed), 1);
+ writeSelectorValue(segMan, event, SELECTOR(claimed), 1);
- invoke_selector(INV_SEL(s, s->_gameObj, syntaxFail, kStopOnInvalidSelector), 2, s->_voc->parser_base, stringpos);
+ invokeSelector(INV_SEL(s, s->_gameObj, syntaxFail, kStopOnInvalidSelector), 2, voc->parser_base, stringpos);
/* Issue warning */
debugC(2, kDebugLevelParser, "Tree building failed");
} else {
- s->_voc->parserIsValid = true;
- PUT_SEL32V(segMan, event, SELECTOR(claimed), 0);
+ voc->parserIsValid = true;
+ writeSelectorValue(segMan, event, SELECTOR(claimed), 0);
#ifdef DEBUG_PARSER
- s->_voc->dumpParseTree();
+ voc->dumpParseTree();
#endif
}
} else {
s->r_acc = make_reg(0, 0);
- PUT_SEL32V(segMan, event, SELECTOR(claimed), 1);
+ writeSelectorValue(segMan, event, SELECTOR(claimed), 1);
if (error) {
- s->_segMan->strcpy(s->_voc->parser_base, error);
+ s->_segMan->strcpy(voc->parser_base, error);
debugC(2, kDebugLevelParser, "Word unknown: %s", error);
/* Issue warning: */
- invoke_selector(INV_SEL(s, s->_gameObj, wordFail, kStopOnInvalidSelector), 2, s->_voc->parser_base, stringpos);
+ invokeSelector(INV_SEL(s, s->_gameObj, wordFail, kStopOnInvalidSelector), 2, voc->parser_base, stringpos);
free(error);
return make_reg(0, 1); /* Tell them that it didn't work */
}
@@ -156,28 +157,29 @@ reg_t kSetSynonyms(EngineState *s, int argc, reg_t *argv) {
Node *node;
int script;
int numSynonyms = 0;
+ 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)
return s->r_acc;
- s->_voc->clearSynonyms();
+ voc->clearSynonyms();
- list = s->_segMan->lookupList(GET_SEL32(segMan, object, SELECTOR(elements)));
+ list = s->_segMan->lookupList(readSelector(segMan, object, SELECTOR(elements)));
node = s->_segMan->lookupNode(list->first);
while (node) {
reg_t objpos = node->value;
int seg;
- script = GET_SEL32V(segMan, objpos, SELECTOR(number));
+ script = readSelectorValue(segMan, objpos, SELECTOR(number));
seg = s->_segMan->getScriptSegment(script);
if (seg > 0)
numSynonyms = s->_segMan->getScript(seg)->getSynonymsNr();
if (numSynonyms) {
- byte *synonyms = s->_segMan->getScript(seg)->getSynonyms();
+ const byte *synonyms = s->_segMan->getScript(seg)->getSynonyms();
if (synonyms) {
debugC(2, kDebugLevelParser, "Setting %d synonyms for script.%d",
@@ -193,7 +195,7 @@ reg_t kSetSynonyms(EngineState *s, int argc, reg_t *argv) {
synonym_t tmp;
tmp.replaceant = (int16)READ_LE_UINT16(synonyms + i * 4);
tmp.replacement = (int16)READ_LE_UINT16(synonyms + i * 4 + 2);
- s->_voc->addSynonym(tmp);
+ voc->addSynonym(tmp);
}
} else
warning("Synonyms of script.%03d were requested, but script is not available", script);
diff --git a/engines/sci/engine/kpathing.cpp b/engines/sci/engine/kpathing.cpp
index 1152addeba..857ccc2a08 100644
--- a/engines/sci/engine/kpathing.cpp
+++ b/engines/sci/engine/kpathing.cpp
@@ -337,15 +337,15 @@ static void draw_point(EngineState *s, Common::Point p, int start, int width, in
static void draw_polygon(EngineState *s, reg_t polygon, int width, int height) {
SegManager *segMan = s->_segMan;
- reg_t points = GET_SEL32(segMan, polygon, SELECTOR(points));
+ reg_t points = readSelector(segMan, polygon, SELECTOR(points));
#ifdef ENABLE_SCI32
if (segMan->isHeapObject(points))
- points = GET_SEL32(segMan, points, SELECTOR(data));
+ points = readSelector(segMan, points, SELECTOR(data));
#endif
- int size = GET_SEL32V(segMan, polygon, SELECTOR(size));
- int type = GET_SEL32V(segMan, polygon, SELECTOR(type));
+ int size = readSelectorValue(segMan, polygon, SELECTOR(size));
+ int type = readSelectorValue(segMan, polygon, SELECTOR(type));
Common::Point first, prev;
int i;
@@ -386,15 +386,15 @@ static void draw_input(EngineState *s, reg_t poly_list, Common::Point start, Com
}
static void print_polygon(SegManager *segMan, reg_t polygon) {
- reg_t points = GET_SEL32(segMan, polygon, SELECTOR(points));
+ reg_t points = readSelector(segMan, polygon, SELECTOR(points));
#ifdef ENABLE_SCI32
if (segMan->isHeapObject(points))
- points = GET_SEL32(segMan, points, SELECTOR(data));
+ points = readSelector(segMan, points, SELECTOR(data));
#endif
- int size = GET_SEL32V(segMan, polygon, SELECTOR(size));
- int type = GET_SEL32V(segMan, polygon, SELECTOR(type));
+ int size = readSelectorValue(segMan, polygon, SELECTOR(size));
+ int type = readSelectorValue(segMan, polygon, SELECTOR(type));
int i;
Common::Point point;
@@ -1036,13 +1036,13 @@ static Polygon *convert_polygon(EngineState *s, reg_t polygon) {
// Returns : (Polygon *) The converted polygon, or NULL on error
SegManager *segMan = s->_segMan;
int i;
- reg_t points = GET_SEL32(segMan, polygon, SELECTOR(points));
- int size = GET_SEL32V(segMan, polygon, SELECTOR(size));
+ reg_t points = readSelector(segMan, polygon, SELECTOR(points));
+ int size = readSelectorValue(segMan, polygon, SELECTOR(size));
#ifdef ENABLE_SCI32
// SCI32 stores the actual points in the data property of points (in a new array)
if (segMan->isHeapObject(points))
- points = GET_SEL32(segMan, points, SELECTOR(data));
+ points = readSelector(segMan, points, SELECTOR(data));
#endif
if (size == 0) {
@@ -1050,7 +1050,7 @@ static Polygon *convert_polygon(EngineState *s, reg_t polygon) {
return NULL;
}
- Polygon *poly = new Polygon(GET_SEL32V(segMan, polygon, SELECTOR(type)));
+ Polygon *poly = new Polygon(readSelectorValue(segMan, polygon, SELECTOR(type)));
int skip = 0;
@@ -1121,7 +1121,7 @@ static PathfindingState *convert_polygon_set(EngineState *s, reg_t poly_list, Co
if (polygon) {
pf_s->polygons.push_back(polygon);
- count += GET_SEL32V(segMan, node->value, SELECTOR(size));
+ count += readSelectorValue(segMan, node->value, SELECTOR(size));
}
node = s->_segMan->lookupNode(node->succ);
@@ -1394,7 +1394,7 @@ reg_t kAvoidPath(EngineState *s, int argc, reg_t *argv) {
if (argc < 7)
error("[avoidpath] Not enough arguments");
- poly_list = (!argv[4].isNull() ? GET_SEL32(s->_segMan, argv[4], SELECTOR(elements)) : NULL_REG);
+ poly_list = (!argv[4].isNull() ? readSelector(s->_segMan, argv[4], SELECTOR(elements)) : NULL_REG);
width = argv[5].toUint16();
height = argv[6].toUint16();
if (argc > 7)
@@ -1694,4 +1694,38 @@ reg_t kIntersections(EngineState *s, int argc, reg_t *argv) {
}
}
+// This is a quite rare kernel function. An example of when it's called
+// is in QFG1VGA, after killing any monster.
+reg_t kMergePoly(EngineState *s, int argc, reg_t *argv) {
+ // 3 parameters: raw polygon data, polygon list, list size
+ reg_t polygonData = argv[0];
+
+ // TODO: actually merge the polygon
+ // In QFG1VGA, there are no immediately visible side-effects
+ // of this being a stub.
+
+#if 0
+ List *list = s->_segMan->lookupList(argv[1]);
+ Node *node = s->_segMan->lookupNode(list->first);
+ // List size is not needed
+
+ Polygon *polygon;
+ int count = 0;
+
+ while (node) {
+ polygon = convert_polygon(s, node->value);
+
+ if (polygon) {
+ count += readSelectorValue(s->_segMan, node->value, SELECTOR(size));
+ }
+
+ node = s->_segMan->lookupNode(node->succ);
+ }
+#endif
+
+ warning("Stub: kMergePoly");
+
+ return polygonData;
+}
+
} // End of namespace Sci
diff --git a/engines/sci/engine/kscripts.cpp b/engines/sci/engine/kscripts.cpp
index ba29f64966..722d0175d1 100644
--- a/engines/sci/engine/kscripts.cpp
+++ b/engines/sci/engine/kscripts.cpp
@@ -111,7 +111,7 @@ reg_t kResCheck(EngineState *s, int argc, reg_t *argv) {
reg_t kClone(EngineState *s, int argc, reg_t *argv) {
reg_t parent_addr = argv[0];
- Object *parent_obj = s->_segMan->getObject(parent_addr);
+ const Object *parent_obj = s->_segMan->getObject(parent_addr);
reg_t clone_addr;
Clone *clone_obj; // same as Object*
@@ -132,7 +132,7 @@ reg_t kClone(EngineState *s, int argc, reg_t *argv) {
*clone_obj = *parent_obj;
// Mark as clone
- clone_obj->setInfoSelector(make_reg(0, SCRIPT_INFO_CLONE));
+ clone_obj->markAsClone();
clone_obj->setSpeciesSelector(clone_obj->getPos());
if (parent_obj->isClass())
clone_obj->setSuperClassSelector(parent_obj->getPos());
@@ -154,14 +154,14 @@ reg_t kDisposeClone(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
- if (victim_obj->getInfoSelector().offset != SCRIPT_INFO_CLONE) {
+ if (!victim_obj->isClone()) {
//warning("Attempt to dispose something other than a clone at %04x", offset);
// SCI silently ignores this behaviour; some games actually depend on it
return s->r_acc;
}
// QFG3 clears clones with underbits set
- //if (GET_SEL32V(victim_addr, underBits))
+ //if (readSelectorValue(victim_addr, underBits))
// warning("Clone %04x:%04x was cleared with underBits set", PRINT_REG(victim_addr));
#if 0
@@ -181,7 +181,7 @@ reg_t kDisposeClone(EngineState *s, int argc, reg_t *argv) {
// Returns script dispatch address index in the supplied script
reg_t kScriptID(EngineState *s, int argc, reg_t *argv) {
int script = argv[0].toUint16();
- int index = (argc > 1) ? argv[1].toUint16() : 0;
+ uint16 index = (argc > 1) ? argv[1].toUint16() : 0;
if (argv[0].segment)
return argv[0];
@@ -193,18 +193,30 @@ reg_t kScriptID(EngineState *s, int argc, reg_t *argv) {
Script *scr = s->_segMan->getScript(scriptSeg);
- if (!scr->_numExports) {
- // FIXME: Is this fatal? This occurs in SQ4CD
- warning("Script 0x%x does not have a dispatch table", script);
+ if (!scr->getExportsNr()) {
+ // This is normal. Some scripts don't have a dispatch (exports) table,
+ // and this call is probably used to load them in memory, ignoring
+ // the return value. If only one argument is passed, this call is done
+ // only to load the script in memory. Thus, don't show any warning,
+ // as no return value is expected
+ if (argc == 2)
+ warning("Script 0x%x does not have a dispatch table and export %d "
+ "was requested from it", script, index);
return NULL_REG;
}
- if (index > scr->_numExports) {
- error("Dispatch index too big: %d > %d", index, scr->_numExports);
+ if (index > scr->getExportsNr()) {
+ error("Dispatch index too big: %d > %d", index, scr->getExportsNr());
return NULL_REG;
}
- return make_reg(scriptSeg, scr->validateExportFunc(index));
+ uint16 address = scr->validateExportFunc(index);
+
+ // Point to the heap for SCI1.1+ games
+ if (getSciVersion() >= SCI_VERSION_1_1)
+ address += scr->getScriptSize();
+
+ return make_reg(scriptSeg, address);
}
reg_t kDisposeScript(EngineState *s, int argc, reg_t *argv) {
@@ -244,7 +256,7 @@ reg_t kRespondsTo(EngineState *s, int argc, reg_t *argv) {
reg_t obj = argv[0];
int selector = argv[1].toUint16();
- return make_reg(0, s->_segMan->isHeapObject(obj) && lookup_selector(s->_segMan, obj, selector, NULL, NULL) != kSelectorNone);
+ return make_reg(0, s->_segMan->isHeapObject(obj) && lookupSelector(s->_segMan, obj, selector, NULL, NULL) != kSelectorNone);
}
} // End of namespace Sci
diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp
index 426c682e11..2681b612e9 100644
--- a/engines/sci/engine/kstring.cpp
+++ b/engines/sci/engine/kstring.cpp
@@ -138,10 +138,36 @@ reg_t kReadNumber(EngineState *s, int argc, reg_t *argv) {
while (isspace((unsigned char)*source))
source++; /* Skip whitespace */
- if (*source == '$') /* SCI uses this for hex numbers */
- return make_reg(0, (int16)strtol(source + 1, NULL, 16)); /* Hex */
- else
- return make_reg(0, (int16)strtol(source, NULL, 10)); /* Force decimal */
+ int16 result = 0;
+
+ if (*source == '$') {
+ // hexadecimal input
+ result = (int16)strtol(source + 1, NULL, 16);
+ } else {
+ // decimal input, we can not use strtol/atoi in here, because sierra used atoi BUT it was a non standard compliant
+ // atoi, that didnt do clipping. In SQ4 we get the door code in here and that's even larger than uint32!
+ if (*source == '-') {
+ result = -1;
+ source++;
+ }
+ while (*source) {
+ if ((*source < '0') || (*source > '9')) {
+ // Sierras atoi stopped processing at anything different than number
+ // 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");
+ }
+ break;
+ }
+ result *= 10;
+ result += *source - 0x30;
+ source++;
+ }
+ }
+
+ return make_reg(0, result);
}
@@ -241,7 +267,7 @@ reg_t kFormat(EngineState *s, int argc, reg_t *argv) {
#ifdef ENABLE_SCI32
// If the string is a string object, get to the actual string in the data selector
if (s->_segMan->isObject(reg))
- reg = GET_SEL32(s->_segMan, reg, SELECTOR(data));
+ reg = readSelector(s->_segMan, reg, SELECTOR(data));
#endif
Common::String tempsource = (reg == NULL_REG) ? "" : g_sci->getKernel()->lookupText(reg,
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index 8dc06dab01..9bf23dedf5 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -383,7 +383,7 @@ void EngineState::saveLoadWithSerializer(Common::Serializer &s) {
sync_SegManagerPtr(s, _segMan);
- syncArray<Class>(s, _segMan->_classtable);
+ syncArray<Class>(s, _segMan->_classTable);
#ifdef USE_OLD_MUSIC_FUNCTIONS
sync_songlib(s, _sound._songlib);
@@ -541,8 +541,8 @@ void Script::saveLoadWithSerializer(Common::Serializer &s) {
}
}
- s.syncAsSint32LE(_numExports);
- s.syncAsSint32LE(_numSynonyms);
+ s.skip(4, VER(9), VER(19)); // OBSOLETE: Used to be _numExports
+ s.skip(4, VER(9), VER(19)); // OBSOLETE: Used to be _numSynonyms
s.syncAsSint32LE(_lockers);
// Sync _objects. This is a hashmap, and we use the following on disk format:
@@ -615,7 +615,7 @@ void DynMem::saveLoadWithSerializer(Common::Serializer &s) {
void DataStack::saveLoadWithSerializer(Common::Serializer &s) {
s.syncAsUint32LE(_capacity);
if (s.isLoading()) {
- //free(entries);
+ free(_entries);
_entries = (reg_t *)calloc(_capacity, sizeof(reg_t));
}
}
@@ -731,15 +731,6 @@ int gamestate_save(EngineState *s, Common::WriteStream *fh, const char* savename
return 1;
}
-/*
- if (s->sound_server) {
- if ((s->sound_server->save)(s, dirname)) {
- warning("Saving failed for the sound subsystem");
- //chdir("..");
- return 1;
- }
- }
-*/
Common::Serializer ser(0, fh);
sync_SavegameMetadata(ser, meta);
Graphics::saveThumbnail(*fh);
@@ -748,26 +739,6 @@ int gamestate_save(EngineState *s, Common::WriteStream *fh, const char* savename
return 0;
}
-static byte *find_unique_script_block(EngineState *s, byte *buf, int type) {
- bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
-
- if (oldScriptHeader)
- buf += 2;
-
- do {
- int seeker_type = READ_LE_UINT16(buf);
-
- if (seeker_type == 0) break;
- if (seeker_type == type) return buf;
-
- int seeker_size = READ_LE_UINT16(buf + 2);
- assert(seeker_size > 0);
- buf += seeker_size;
- } while (1);
-
- return NULL;
-}
-
// TODO: This should probably be turned into an EngineState or DataStack method.
static void reconstruct_stack(EngineState *retval) {
SegmentId stack_seg = retval->_segMan->findSegmentByType(SEG_TYPE_STACK);
@@ -777,99 +748,37 @@ static void reconstruct_stack(EngineState *retval) {
retval->stack_top = stack->_entries + stack->_capacity;
}
-static void load_script(EngineState *s, Script *scr) {
- scr->_buf = (byte *)malloc(scr->_bufSize);
- assert(scr->_buf);
-
- Resource *script = g_sci->getResMan()->findResource(ResourceId(kResourceTypeScript, scr->_nr), 0);
- assert(script != 0);
-
- assert(scr->_bufSize >= script->size);
- memcpy(scr->_buf, script->data, script->size);
-
- if (getSciVersion() >= SCI_VERSION_1_1) {
- Resource *heap = g_sci->getResMan()->findResource(ResourceId(kResourceTypeHeap, scr->_nr), 0);
- assert(heap != 0);
-
- scr->_heapStart = scr->_buf + scr->_scriptSize;
-
- assert(scr->_bufSize - scr->_scriptSize <= heap->size);
- memcpy(scr->_heapStart, heap->data, heap->size);
- }
-}
-
// TODO: Move thie function to a more appropriate place, such as vm.cpp or script.cpp
void SegManager::reconstructScripts(EngineState *s) {
uint i;
- SegmentObj *mobj;
for (i = 0; i < _heap.size(); i++) {
- mobj = _heap[i];
- if (!mobj || mobj->getType() != SEG_TYPE_SCRIPT)
+ if (!_heap[i] || _heap[i]->getType() != SEG_TYPE_SCRIPT)
continue;
- Script *scr = (Script *)mobj;
-
- // FIXME: Unify this code with script_instantiate_* ?
- load_script(s, scr);
+ Script *scr = (Script *)_heap[i];
+ scr->load(g_sci->getResMan());
scr->_localsBlock = (scr->_localsSegment == 0) ? NULL : (LocalVariables *)(_heap[scr->_localsSegment]);
- if (getSciVersion() >= SCI_VERSION_1_1) {
- scr->_exportTable = 0;
- scr->_synonyms = 0;
- if (READ_LE_UINT16(scr->_buf + 6) > 0) {
- scr->setExportTableOffset(6);
- s->_segMan->scriptRelocateExportsSci11(i);
- }
- } else {
- scr->_exportTable = (uint16 *) find_unique_script_block(s, scr->_buf, SCI_OBJ_EXPORTS);
- scr->_synonyms = find_unique_script_block(s, scr->_buf, SCI_OBJ_SYNONYMS);
- scr->_exportTable += 3;
- }
- scr->_codeBlocks.clear();
- ObjMap::iterator it;
- const ObjMap::iterator end = scr->_objects.end();
- for (it = scr->_objects.begin(); it != end; ++it) {
- byte *data = scr->_buf + it->_value.getPos().offset;
- it->_value._baseObj = data;
- }
+ for (ObjMap::iterator it = scr->_objects.begin(); it != scr->_objects.end(); ++it)
+ it->_value._baseObj = scr->_buf + it->_value.getPos().offset;
}
for (i = 0; i < _heap.size(); i++) {
- mobj = _heap[i];
- if (!mobj || mobj->getType() != SEG_TYPE_SCRIPT)
+ if (!_heap[i] || _heap[i]->getType() != SEG_TYPE_SCRIPT)
continue;
- Script *scr = (Script *)mobj;
+ Script *scr = (Script *)_heap[i];
- // FIXME: Unify this code with Script::scriptObjInit ?
- ObjMap::iterator it;
- const ObjMap::iterator end = scr->_objects.end();
- for (it = scr->_objects.begin(); it != end; ++it) {
- byte *data = scr->_buf + it->_value.getPos().offset;
-
- if (getSciVersion() >= SCI_VERSION_1_1) {
- uint16 *funct_area = (uint16 *)(scr->_buf + READ_LE_UINT16( data + 6 ));
- uint16 *prop_area = (uint16 *)(scr->_buf + READ_LE_UINT16( data + 4 ));
-
- it->_value._baseMethod = funct_area;
- it->_value._baseVars = prop_area;
- } else {
- int funct_area = READ_LE_UINT16(data + SCRIPT_FUNCTAREAPTR_OFFSET);
- Object *_baseObj;
-
- _baseObj = s->_segMan->getObject(it->_value.getSpeciesSelector());
-
- if (!_baseObj) {
- warning("Object without a base class: Script %d, index %d (reg address %04x:%04x",
- scr->_nr, i, PRINT_REG(it->_value.getSpeciesSelector()));
- continue;
- }
- it->_value.setVarCount(_baseObj->getVarCount());
- it->_value._baseObj = _baseObj->_baseObj;
+ for (ObjMap::iterator it = scr->_objects.begin(); it != scr->_objects.end(); ++it) {
+ reg_t addr = it->_value.getPos();
+ Object *obj = scr->scriptObjInit(addr, false);
- it->_value._baseMethod = (uint16 *)(data + funct_area);
- it->_value._baseVars = (uint16 *)(data + it->_value.getVarCount() * 2 + SCRIPT_SELECTOR_OFFSET);
+ if (getSciVersion() < SCI_VERSION_1_1) {
+ if (!obj->initBaseObject(this, addr, false)) {
+ warning("Failed to locate base object for object at %04X:%04X; skipping", PRINT_REG(addr));
+ scr->scriptObjRemove(addr);
+ }
}
}
}
@@ -912,7 +821,6 @@ static void reconstruct_sounds(EngineState *s) {
#endif
void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
- EngineState *retval;
#ifdef USE_OLD_MUSIC_FUNCTIONS
SongLibrary temp;
#endif
@@ -947,84 +855,67 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
thumbnail = 0;
}
- // Create a new EngineState object
- retval = new EngineState(s->_voc, s->_segMan);
- retval->_event = s->_event;
-
- // Copy some old data
- retval->_soundCmd = s->_soundCmd;
-
- // Copy memory segment
- retval->_memorySegmentSize = s->_memorySegmentSize;
- memcpy(retval->_memorySegment, s->_memorySegment, s->_memorySegmentSize);
-
- retval->saveLoadWithSerializer(ser); // FIXME: Error handling?
+ s->reset(true);
+ s->saveLoadWithSerializer(ser); // FIXME: Error handling?
#ifdef USE_OLD_MUSIC_FUNCTIONS
s->_sound.sfx_exit();
#endif
// Set exec stack base to zero
- retval->execution_stack_base = 0;
+ s->execution_stack_base = 0;
// Now copy all current state information
#ifdef USE_OLD_MUSIC_FUNCTIONS
- temp = retval->_sound._songlib;
- retval->_sound.sfx_init(g_sci->getResMan(), s->sfx_init_flags, g_sci->_features->detectDoSoundType());
- retval->sfx_init_flags = s->sfx_init_flags;
- retval->_sound._songlib.freeSounds();
- retval->_sound._songlib = temp;
- retval->_soundCmd->updateSfxState(&retval->_sound);
+ temp = s->_sound._songlib;
+ s->_sound.sfx_init(g_sci->getResMan(), s->sfx_init_flags, g_sci->_features->detectDoSoundType());
+ s->sfx_init_flags = s->sfx_init_flags;
+ s->_sound._songlib.freeSounds();
+ s->_sound._songlib = temp;
+ s->_soundCmd->updateSfxState(&retval->_sound);
#endif
- reconstruct_stack(retval);
- retval->_segMan->reconstructScripts(retval);
- retval->_segMan->reconstructClones();
- retval->_gameObj = s->_gameObj;
- retval->script_000 = retval->_segMan->getScript(retval->_segMan->getScriptSegment(0, SCRIPT_GET_DONT_LOAD));
- retval->gc_countdown = GC_INTERVAL - 1;
- retval->sys_strings_segment = retval->_segMan->findSegmentByType(SEG_TYPE_SYS_STRINGS);
- retval->sys_strings = (SystemStrings *)(retval->_segMan->_heap[retval->sys_strings_segment]);
+ reconstruct_stack(s);
+ s->_segMan->reconstructScripts(s);
+ s->_segMan->reconstructClones();
+ s->_gameObj = s->_gameObj;
+ s->script_000 = s->_segMan->getScript(s->_segMan->getScriptSegment(0, SCRIPT_GET_DONT_LOAD));
+ s->gc_countdown = GC_INTERVAL - 1;
// Time state:
- retval->last_wait_time = g_system->getMillis();
- retval->game_start_time = g_system->getMillis();
-
- // static parser information:
-
- if (retval->_voc)
- retval->_voc->parser_base = make_reg(s->sys_strings_segment, SYS_STRING_PARSER_BASE);
+ s->last_wait_time = g_system->getMillis();
+ s->game_start_time = g_system->getMillis();
- retval->successor = NULL;
+ s->restoring = false;
#ifdef USE_OLD_MUSIC_FUNCTIONS
- retval->_sound._it = NULL;
- retval->_sound._flags = s->_sound._flags;
- retval->_sound._song = NULL;
- retval->_sound._suspended = s->_sound._suspended;
- reconstruct_sounds(retval);
+ s->_sound._it = NULL;
+ s->_sound._flags = s->_sound._flags;
+ s->_sound._song = NULL;
+ s->_sound._suspended = s->_sound._suspended;
+ reconstruct_sounds(s);
#else
- retval->_soundCmd->reconstructPlayList(meta.savegame_version);
+ s->_soundCmd->reconstructPlayList(meta.savegame_version);
#endif
// Message state:
- retval->_msgState = new MessageState(retval->_segMan);
+ s->_msgState = new MessageState(s->_segMan);
#ifdef ENABLE_SCI32
if (g_sci->_gui32) {
g_sci->_gui32->init();
} else {
#endif
- g_sci->_gui->resetEngineState(retval);
+ g_sci->_gui->resetEngineState(s);
g_sci->_gui->init(g_sci->_features->usesOldGfxFunctions());
#ifdef ENABLE_SCI32
}
#endif
- s->successor = retval; // Set successor
- script_abort_flag = 2; // Abort current game with replay
+ s->restoring = true;
+ s->script_abort_flag = 2; // Abort current game with replay
s->shrinkStackToBase();
}
diff --git a/engines/sci/engine/savegame.h b/engines/sci/engine/savegame.h
index bad79fca27..7be05381da 100644
--- a/engines/sci/engine/savegame.h
+++ b/engines/sci/engine/savegame.h
@@ -36,7 +36,7 @@ namespace Sci {
struct EngineState;
enum {
- CURRENT_SAVEGAME_VERSION = 19,
+ CURRENT_SAVEGAME_VERSION = 20,
MINIMUM_SAVEGAME_VERSION = 9
};
diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp
index e9b1ce3f28..1f32e50b67 100644
--- a/engines/sci/engine/script.cpp
+++ b/engines/sci/engine/script.cpp
@@ -123,13 +123,13 @@ void SegManager::createClassTable() {
error("SegManager: failed to open vocab 996");
int totalClasses = vocab996->size >> 2;
- _classtable.resize(totalClasses);
+ _classTable.resize(totalClasses);
for (uint16 classNr = 0; classNr < totalClasses; classNr++) {
uint16 scriptNr = READ_SCI11ENDIAN_UINT16(vocab996->data + classNr * 4 + 2);
- _classtable[classNr].reg = NULL_REG;
- _classtable[classNr].script = scriptNr;
+ _classTable[classNr].reg = NULL_REG;
+ _classTable[classNr].script = scriptNr;
}
_resMan->unlockResource(vocab996);
@@ -139,11 +139,11 @@ reg_t SegManager::getClassAddress(int classnr, ScriptLoadType lock, reg_t caller
if (classnr == 0xffff)
return NULL_REG;
- if (classnr < 0 || (int)_classtable.size() <= classnr || _classtable[classnr].script < 0) {
- error("[VM] Attempt to dereference class %x, which doesn't exist (max %x)", classnr, _classtable.size());
+ if (classnr < 0 || (int)_classTable.size() <= classnr || _classTable[classnr].script < 0) {
+ error("[VM] Attempt to dereference class %x, which doesn't exist (max %x)", classnr, _classTable.size());
return NULL_REG;
} else {
- Class *the_class = &_classtable[classnr];
+ Class *the_class = &_classTable[classnr];
if (!the_class->reg.segment) {
getScriptSegment(the_class->script, lock);
@@ -175,7 +175,7 @@ void SegManager::scriptInitialiseLocals(reg_t location) {
Script *scr = getScript(location.segment);
unsigned int count;
- VERIFY(location.offset + 1 < (uint16)scr->_bufSize, "Locals beyond end of script\n");
+ VERIFY(location.offset + 1 < (uint16)scr->getBufSize(), "Locals beyond end of script\n");
if (getSciVersion() >= SCI_VERSION_1_1)
count = READ_SCI11ENDIAN_UINT16(scr->_buf + location.offset - 2);
@@ -185,55 +185,38 @@ void SegManager::scriptInitialiseLocals(reg_t location) {
scr->_localsOffset = location.offset;
- if (!(location.offset + count * 2 + 1 < scr->_bufSize)) {
- warning("Locals extend beyond end of script: offset %04x, count %x vs size %x", location.offset, count, (uint)scr->_bufSize);
- count = (scr->_bufSize - location.offset) >> 1;
+ if (!(location.offset + count * 2 + 1 < scr->getBufSize())) {
+ warning("Locals extend beyond end of script: offset %04x, count %x vs size %x", location.offset, count, (uint)scr->getBufSize());
+ count = (scr->getBufSize() - location.offset) >> 1;
}
LocalVariables *locals = allocLocalsSegment(scr, count);
if (locals) {
uint i;
- byte *base = (byte *)(scr->_buf + location.offset);
+ const byte *base = (const byte *)(scr->_buf + location.offset);
for (i = 0; i < count; i++)
locals->_locals[i] = make_reg(0, READ_SCI11ENDIAN_UINT16(base + i * 2));
}
}
-void SegManager::scriptRelocateExportsSci11(SegmentId seg) {
- Script *scr = getScript(seg);
- for (int i = 0; i < scr->_numExports; i++) {
- /* We are forced to use an ugly heuristic here to distinguish function
- exports from object/class exports. The former kind points into the
- script resource, the latter into the heap resource. */
- uint16 location = READ_SCI11ENDIAN_UINT16((byte *)(scr->_exportTable + i));
-
- if ((location < scr->_heapSize - 1) && (READ_SCI11ENDIAN_UINT16(scr->_heapStart + location) == SCRIPT_OBJECT_MAGIC_NUMBER)) {
- WRITE_SCI11ENDIAN_UINT16((byte *)(scr->_exportTable + i), location + scr->_heapStart - scr->_buf);
- } else {
- // Otherwise it's probably a function export,
- // and we don't need to do anything.
- }
- }
-}
-
void SegManager::scriptInitialiseObjectsSci11(SegmentId seg) {
Script *scr = getScript(seg);
- byte *seeker = scr->_heapStart + 4 + READ_SCI11ENDIAN_UINT16(scr->_heapStart + 2) * 2;
+ const byte *seeker = scr->_heapStart + 4 + READ_SCI11ENDIAN_UINT16(scr->_heapStart + 2) * 2;
while (READ_SCI11ENDIAN_UINT16(seeker) == SCRIPT_OBJECT_MAGIC_NUMBER) {
- if (READ_SCI11ENDIAN_UINT16(seeker + 14) & SCRIPT_INFO_CLASS) {
+ if (READ_SCI11ENDIAN_UINT16(seeker + 14) & kInfoFlagClass) { // -info- selector
int classpos = seeker - scr->_buf;
int species = READ_SCI11ENDIAN_UINT16(seeker + 10);
- if (species < 0 || species >= (int)_classtable.size()) {
+ if (species < 0 || species >= (int)_classTable.size()) {
error("Invalid species %d(0x%x) not in interval [0,%d) while instantiating script %d",
- species, species, _classtable.size(), scr->_nr);
+ species, species, _classTable.size(), scr->_nr);
return;
}
- _classtable[species].reg.segment = seg;
- _classtable[species].reg.offset = classpos;
+ _classTable[species].reg.segment = seg;
+ _classTable[species].reg.offset = classpos;
}
seeker += READ_SCI11ENDIAN_UINT16(seeker + 2) * 2;
}
@@ -243,19 +226,20 @@ void SegManager::scriptInitialiseObjectsSci11(SegmentId seg) {
reg_t reg = make_reg(seg, seeker - scr->_buf);
Object *obj = scr->scriptObjInit(reg);
-#if 0
- if (obj->_variables[5].offset != 0xffff) {
- obj->_variables[5] = INST_LOOKUP_CLASS(obj->_variables[5].offset);
- baseObj = getObject(obj->_variables[5]);
- obj->variable_names_nr = baseObj->variables_nr;
- obj->_baseObj = baseObj->_baseObj;
- }
-#endif
-
// Copy base from species class, as we need its selector IDs
obj->setSuperClassSelector(
getClassAddress(obj->getSuperClassSelector().offset, SCRIPT_GET_LOCK, NULL_REG));
+ // If object is instance, get -propDict- from class and set it for this object
+ // This is needed for ::isMemberOf() to work.
+ // Example testcase - room 381 of sq4cd - if isMemberOf() doesn't work, talk-clicks on the robot will act like
+ // clicking on ego
+ if (!obj->isClass()) {
+ reg_t classObject = obj->getSuperClassSelector();
+ Object *classObj = getObject(classObject);
+ obj->setPropDictSelector(classObj->getPropDictSelector());
+ }
+
// Set the -classScript- selector to the script number.
// FIXME: As this selector is filled in at run-time, it is likely
// that it is supposed to hold a pointer. The Obj::isKindOf method
@@ -268,86 +252,24 @@ void SegManager::scriptInitialiseObjectsSci11(SegmentId seg) {
}
}
-
-
-int script_instantiate_common(ResourceManager *resMan, SegManager *segMan, int script_nr, Resource **script, Resource **heap, int *was_new) {
- *was_new = 1;
-
- *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0);
- if (getSciVersion() >= SCI_VERSION_1_1)
- *heap = resMan->findResource(ResourceId(kResourceTypeHeap, script_nr), 0);
-
- if (!*script || (getSciVersion() >= SCI_VERSION_1_1 && !heap)) {
- warning("Script 0x%x requested but not found", script_nr);
- if (getSciVersion() >= SCI_VERSION_1_1) {
- if (*heap)
- warning("Inconsistency: heap resource WAS found");
- else if (*script)
- warning("Inconsistency: script resource WAS found");
- }
- return 0;
- }
-
- SegmentId seg_id = segMan->getScriptSegment(script_nr);
- Script *scr = segMan->getScriptIfLoaded(seg_id);
- if (scr) {
- if (!scr->isMarkedAsDeleted()) {
- scr->incrementLockers();
- return seg_id;
- } else {
- scr->freeScript();
- }
- } else {
- scr = segMan->allocateScript(script_nr, &seg_id);
- if (!scr) { // ALL YOUR SCRIPT BASE ARE BELONG TO US
- error("Not enough heap space for script size 0x%x of script 0x%x (Should this happen?)", (*script)->size, script_nr);
- return 0;
- }
- }
-
- scr->init(script_nr, resMan);
-
- // Set heap position (beyond the size word)
- scr->setLockers(1);
- scr->setExportTableOffset(0);
- scr->setSynonymsOffset(0);
- scr->setSynonymsNr(0);
-
- *was_new = 0;
-
- return seg_id;
-}
-
-#define INST_LOOKUP_CLASS(id) ((id == 0xffff)? NULL_REG : segMan->getClassAddress(id, SCRIPT_GET_LOCK, addr))
-
-int script_instantiate_sci0(ResourceManager *resMan, SegManager *segMan, int script_nr) {
+void script_instantiate_sci0(Script *scr, int segmentId, SegManager *segMan) {
int objType;
uint32 objLength = 0;
- int relocation = -1;
- Resource *script;
- int was_new;
bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
- const int seg_id = script_instantiate_common(resMan, segMan, script_nr, &script, NULL, &was_new);
uint16 curOffset = oldScriptHeader ? 2 : 0;
- if (was_new)
- return seg_id;
-
- Script *scr = segMan->getScript(seg_id);
- scr->mcpyInOut(0, script->data, script->size);
-
if (oldScriptHeader) {
// Old script block
// There won't be a localvar block in this case
// Instead, the script starts with a 16 bit int specifying the
// number of locals we need; these are then allocated and zeroed.
- int locals_nr = READ_LE_UINT16(script->data);
- if (locals_nr)
- segMan->scriptInitialiseLocalsZero(seg_id, locals_nr);
+ int localsCount = READ_LE_UINT16(scr->_buf);
+ if (localsCount)
+ segMan->scriptInitialiseLocalsZero(segmentId, localsCount);
}
// Now do a first pass through the script objects to find the
- // export table and local variable block
+ // local variable blocks
do {
objType = scr->getHeap(curOffset);
@@ -355,48 +277,30 @@ int script_instantiate_sci0(ResourceManager *resMan, SegManager *segMan, int scr
break;
objLength = scr->getHeap(curOffset + 2);
-
- // This happens in some demos (e.g. the EcoQuest 1 demo). Not sure what is the
- // actual cause of it, but the scripts of these demos can't be loaded properly
- // and we're stuck forever in this loop, as objLength never changes
- if (!objLength) {
- warning("script_instantiate_sci0: objLength is 0, unable to parse script");
- return 0;
- }
-
curOffset += 4; // skip header
switch (objType) {
- case SCI_OBJ_EXPORTS:
- scr->setExportTableOffset(curOffset);
- break;
- case SCI_OBJ_SYNONYMS:
- scr->setSynonymsOffset(curOffset);
- scr->setSynonymsNr((objLength) / 4);
- break;
case SCI_OBJ_LOCALVARS:
- segMan->scriptInitialiseLocals(make_reg(seg_id, curOffset));
+ segMan->scriptInitialiseLocals(make_reg(segmentId, curOffset));
break;
-
case SCI_OBJ_CLASS: {
int classpos = curOffset - SCRIPT_OBJECT_MAGIC_OFFSET;
int species = scr->getHeap(curOffset - SCRIPT_OBJECT_MAGIC_OFFSET + SCRIPT_SPECIES_OFFSET);
- if (species < 0 || species >= (int)segMan->_classtable.size()) {
- if (species == (int)segMan->_classtable.size()) {
+ if (species < 0 || species >= (int)segMan->classTableSize()) {
+ if (species == (int)segMan->classTableSize()) {
// Happens in the LSL2 demo
warning("Applying workaround for an off-by-one invalid species access");
- segMan->_classtable.resize(segMan->_classtable.size() + 1);
+ segMan->resizeClassTable(segMan->classTableSize() + 1);
} else {
- warning("Invalid species %d(0x%x) not in interval "
- "[0,%d) while instantiating script %d\n",
- species, species, segMan->_classtable.size(),
- script_nr);
- return 0;
+ error("Invalid species %d(0x%x) not in interval "
+ "[0,%d) while instantiating script at segment %d\n",
+ species, species, segMan->classTableSize(),
+ segmentId);
+ return;
}
}
- segMan->_classtable[species].reg.segment = seg_id;
- segMan->_classtable[species].reg.offset = classpos;
+ segMan->setClassOffset(species, make_reg(segmentId, classpos));
// Set technical class position-- into the block allocated for it
}
break;
@@ -406,7 +310,7 @@ int script_instantiate_sci0(ResourceManager *resMan, SegManager *segMan, int scr
}
curOffset += objLength - 4;
- } while (objType != 0 && curOffset < script->size - 2);
+ } while (objType != 0 && curOffset < scr->getScriptSize() - 2);
// And now a second pass to adjust objects and class pointers, and the general pointers
objLength = 0;
@@ -420,7 +324,7 @@ int script_instantiate_sci0(ResourceManager *resMan, SegManager *segMan, int scr
objLength = scr->getHeap(curOffset + 2);
curOffset += 4; // skip header
- reg_t addr = make_reg(seg_id, curOffset);
+ reg_t addr = make_reg(segmentId, curOffset);
switch (objType) {
case SCI_OBJ_CODE:
@@ -429,77 +333,52 @@ int script_instantiate_sci0(ResourceManager *resMan, SegManager *segMan, int scr
case SCI_OBJ_OBJECT:
case SCI_OBJ_CLASS: { // object or class?
Object *obj = scr->scriptObjInit(addr);
+ obj->initSpecies(segMan, addr);
- // Instantiate the superclass, if neccessary
- obj->setSpeciesSelector(INST_LOOKUP_CLASS(obj->getSpeciesSelector().offset));
-
- Object *baseObj = segMan->getObject(obj->getSpeciesSelector());
-
- if (baseObj) {
- obj->setVarCount(baseObj->getVarCount());
- // Copy base from species class, as we need its selector IDs
- obj->_baseObj = baseObj->_baseObj;
-
- obj->setSuperClassSelector(INST_LOOKUP_CLASS(obj->getSuperClassSelector().offset));
- } else {
+ if (!obj->initBaseObject(segMan, addr)) {
warning("Failed to locate base object for object at %04X:%04X; skipping", PRINT_REG(addr));
-
scr->scriptObjRemove(addr);
}
} // if object or class
break;
- case SCI_OBJ_POINTERS: // A relocation table
- relocation = addr.offset;
- break;
-
default:
break;
}
curOffset += objLength - 4;
- } while (objType != 0 && curOffset < script->size - 2);
-
- if (relocation >= 0)
- scr->scriptRelocate(make_reg(seg_id, relocation));
-
- return seg_id; // instantiation successful
+ } while (objType != 0 && curOffset < scr->getScriptSize() - 2);
}
-int script_instantiate_sci11(ResourceManager *resMan, SegManager *segMan, int script_nr) {
- Resource *script, *heap;
- int was_new;
- const int seg_id = script_instantiate_common(resMan, segMan, script_nr, &script, &heap, &was_new);
-
- if (was_new)
- return seg_id;
-
- Script *scr = segMan->getScript(seg_id);
- int _heapStart = script->size;
-
- if (script->size & 2)
- _heapStart++;
-
- scr->mcpyInOut(0, script->data, script->size);
- scr->mcpyInOut(_heapStart, heap->data, heap->size);
-
- if (READ_SCI11ENDIAN_UINT16(script->data + 6) > 0)
- scr->setExportTableOffset(6);
-
- segMan->scriptInitialiseLocals(make_reg(seg_id, _heapStart + 4));
-
- segMan->scriptRelocateExportsSci11(seg_id);
- segMan->scriptInitialiseObjectsSci11(seg_id);
+int script_instantiate(ResourceManager *resMan, SegManager *segMan, int scriptNum) {
+ SegmentId segmentId = segMan->getScriptSegment(scriptNum);
+ Script *scr = segMan->getScriptIfLoaded(segmentId);
+ if (scr) {
+ if (!scr->isMarkedAsDeleted()) {
+ scr->incrementLockers();
+ return segmentId;
+ } else {
+ scr->freeScript();
+ }
+ } else {
+ scr = segMan->allocateScript(scriptNum, &segmentId);
+ }
- scr->heapRelocate(make_reg(seg_id, READ_SCI11ENDIAN_UINT16(heap->data)));
+ scr->init(scriptNum, resMan);
+ scr->load(resMan);
- return seg_id;
-}
+ if (getSciVersion() >= SCI_VERSION_1_1) {
+ int heapStart = scr->getScriptSize();
+ segMan->scriptInitialiseLocals(make_reg(segmentId, heapStart + 4));
+ segMan->scriptInitialiseObjectsSci11(segmentId);
+ scr->relocate(make_reg(segmentId, READ_SCI11ENDIAN_UINT16(scr->_heapStart)));
+ } else {
+ script_instantiate_sci0(scr, segmentId, segMan);
+ byte *relocationBlock = scr->findBlock(SCI_OBJ_POINTERS);
+ if (relocationBlock)
+ scr->relocate(make_reg(segmentId, relocationBlock - scr->_buf + 4));
+ }
-int script_instantiate(ResourceManager *resMan, SegManager *segMan, int script_nr) {
- if (getSciVersion() >= SCI_VERSION_1_1)
- return script_instantiate_sci11(resMan, segMan, script_nr);
- else
- return script_instantiate_sci0(resMan, segMan, script_nr);
+ return segmentId;
}
void script_uninstantiate_sci0(SegManager *segMan, int script_nr, SegmentId seg) {
@@ -528,7 +407,7 @@ void script_uninstantiate_sci0(SegManager *segMan, int script_nr, SegmentId seg)
superclass = scr->getHeap(reg.offset + SCRIPT_SUPERCLASS_OFFSET); // Get superclass...
if (superclass >= 0) {
- int superclass_script = segMan->_classtable[superclass].script;
+ int superclass_script = segMan->getClass(superclass).script;
if (superclass_script == script_nr) {
if (scr->getLockers())
@@ -562,9 +441,9 @@ void script_uninstantiate(SegManager *segMan, int script_nr) {
return;
// Free all classtable references to this script
- for (uint i = 0; i < segMan->_classtable.size(); i++)
- if (segMan->_classtable[i].reg.segment == segment)
- segMan->_classtable[i].reg = NULL_REG;
+ for (uint i = 0; i < segMan->classTableSize(); i++)
+ if (segMan->getClass(i).reg.segment == segment)
+ segMan->setClassOffset(i, NULL_REG);
if (getSciVersion() < SCI_VERSION_1_1)
script_uninstantiate_sci0(segMan, script_nr, segment);
diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp
index 4b60626b2e..b465ab3d4e 100644
--- a/engines/sci/engine/scriptdebug.cpp
+++ b/engines/sci/engine/scriptdebug.cpp
@@ -67,37 +67,6 @@ extern const char *selector_name(EngineState *s, int selector);
DebugState g_debugState;
-int propertyOffsetToId(SegManager *segMan, int prop_ofs, reg_t objp) {
- Object *obj = segMan->getObject(objp);
- byte *selectoroffset;
- int selectors;
-
- if (!obj) {
- warning("Applied propertyOffsetToId on non-object at %04x:%04x", PRINT_REG(objp));
- return -1;
- }
-
- selectors = obj->getVarCount();
-
- if (getSciVersion() < SCI_VERSION_1_1)
- selectoroffset = ((byte *)(obj->_baseObj)) + SCRIPT_SELECTOR_OFFSET + selectors * 2;
- else {
- if (!(obj->getInfoSelector().offset & SCRIPT_INFO_CLASS)) {
- obj = segMan->getObject(obj->getSuperClassSelector());
- selectoroffset = (byte *)obj->_baseVars;
- } else
- selectoroffset = (byte *)obj->_baseVars;
- }
-
- if (prop_ofs < 0 || (prop_ofs >> 1) >= selectors) {
- warning("Applied propertyOffsetToId to invalid property offset %x (property #%d not in [0..%d]) on object at %04x:%04x",
- prop_ofs, prop_ofs >> 1, selectors - 1, PRINT_REG(objp));
- return -1;
- }
-
- return READ_SCI11ENDIAN_UINT16(selectoroffset + prop_ofs);
-}
-
// Disassembles one command from the heap, returns address of next command or 0 if a ret was encountered.
reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecode) {
SegmentObj *mobj = s->_segMan->getSegment(pos.segment, SEG_TYPE_SCRIPT);
@@ -116,7 +85,7 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod
script_entity = (Script *)mobj;
scr = script_entity->_buf;
- scr_size = script_entity->_bufSize;
+ scr_size = script_entity->getBufSize();
if (pos.offset >= scr_size) {
warning("Trying to disassemble beyond end of script");
@@ -221,51 +190,52 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod
}
}
- if (pos == scriptState.xs->addr.pc) { // Extra information if debugging the current opcode
+ if (pos == s->xs->addr.pc) { // Extra information if debugging the current opcode
if ((opcode == op_pTos) || (opcode == op_sTop) || (opcode == op_pToa) || (opcode == op_aTop) ||
(opcode == op_dpToa) || (opcode == op_ipToa) || (opcode == op_dpTos) || (opcode == op_ipTos)) {
- int prop_ofs = scr[pos.offset + 1];
- int prop_id = propertyOffsetToId(s->_segMan, prop_ofs, scriptState.xs->objp);
-
- printf(" (%s)", selector_name(s, prop_id));
+ const Object *obj = s->_segMan->getObject(s->xs->objp);
+ if (!obj)
+ warning("Attempted to reference on non-object at %04x:%04x", PRINT_REG(s->xs->objp));
+ else
+ printf(" (%s)", selector_name(s, obj->propertyOffsetToId(s->_segMan, scr[pos.offset + 1])));
}
}
printf("\n");
- if (pos == scriptState.xs->addr.pc) { // Extra information if debugging the current opcode
+ if (pos == s->xs->addr.pc) { // Extra information if debugging the current opcode
if (opcode == op_callk) {
- int stackframe = (scr[pos.offset + 2] >> 1) + (scriptState.restAdjust);
- int argc = ((scriptState.xs->sp)[- stackframe - 1]).offset;
+ int stackframe = (scr[pos.offset + 2] >> 1) + (s->restAdjustCur);
+ int argc = ((s->xs->sp)[- stackframe - 1]).offset;
bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
if (!oldScriptHeader)
- argc += (scriptState.restAdjust);
+ argc += (s->restAdjustCur);
printf(" Kernel params: (");
for (int j = 0; j < argc; j++) {
- printf("%04x:%04x", PRINT_REG((scriptState.xs->sp)[j - stackframe]));
+ printf("%04x:%04x", PRINT_REG((s->xs->sp)[j - stackframe]));
if (j + 1 < argc)
printf(", ");
}
printf(")\n");
} else if ((opcode == op_send) || (opcode == op_self)) {
- int restmod = scriptState.restAdjust;
+ int restmod = s->restAdjustCur;
int stackframe = (scr[pos.offset + 1] >> 1) + restmod;
- reg_t *sb = scriptState.xs->sp;
+ reg_t *sb = s->xs->sp;
uint16 selector;
reg_t fun_ref;
while (stackframe > 0) {
int argc = sb[- stackframe + 1].offset;
const char *name = NULL;
- reg_t called_obj_addr = scriptState.xs->objp;
+ reg_t called_obj_addr = s->xs->objp;
if (opcode == op_send)
called_obj_addr = s->r_acc;
else if (opcode == op_self)
- called_obj_addr = scriptState.xs->objp;
+ called_obj_addr = s->xs->objp;
selector = sb[- stackframe].offset;
@@ -276,7 +246,7 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod
printf(" %s::%s[", name, (selector > kernel->getSelectorNamesSize()) ? "<invalid>" : selector_name(s, selector));
- switch (lookup_selector(s->_segMan, called_obj_addr, selector, 0, &fun_ref)) {
+ switch (lookupSelector(s->_segMan, called_obj_addr, selector, 0, &fun_ref)) {
case kSelectorMethod:
printf("FUNCT");
argc += restmod;
@@ -315,10 +285,10 @@ void script_debug(EngineState *s) {
#if 0
if (sci_debug_flags & _DEBUG_FLAG_LOGGING) {
printf("%d: acc=%04x:%04x ", script_step_counter, PRINT_REG(s->r_acc));
- disassemble(s, scriptState.xs->addr.pc, 0, 1);
- if (scriptState.seeking == kDebugSeekGlobal)
- printf("Global %d (0x%x) = %04x:%04x\n", scriptState.seekSpecial,
- scriptState.seekSpecial, PRINT_REG(s->script_000->_localsBlock->_locals[scriptState.seekSpecial]));
+ disassemble(s, s->xs->addr.pc, 0, 1);
+ if (s->seeking == kDebugSeekGlobal)
+ printf("Global %d (0x%x) = %04x:%04x\n", s->seekSpecial,
+ s->seekSpecial, PRINT_REG(s->script_000->_localsBlock->_locals[s->seekSpecial]));
}
#endif
@@ -328,16 +298,16 @@ void script_debug(EngineState *s) {
#endif
if (g_debugState.seeking && !g_debugState.breakpointWasHit) { // Are we looking for something special?
- SegmentObj *mobj = s->_segMan->getSegment(scriptState.xs->addr.pc.segment, SEG_TYPE_SCRIPT);
+ SegmentObj *mobj = s->_segMan->getSegment(s->xs->addr.pc.segment, SEG_TYPE_SCRIPT);
if (mobj) {
Script *scr = (Script *)mobj;
byte *code_buf = scr->_buf;
- int code_buf_size = scr->_bufSize;
- int opcode = scriptState.xs->addr.pc.offset >= code_buf_size ? 0 : code_buf[scriptState.xs->addr.pc.offset];
+ int code_buf_size = scr->getBufSize();
+ int opcode = s->xs->addr.pc.offset >= code_buf_size ? 0 : code_buf[s->xs->addr.pc.offset];
int op = opcode >> 1;
- int paramb1 = scriptState.xs->addr.pc.offset + 1 >= code_buf_size ? 0 : code_buf[scriptState.xs->addr.pc.offset + 1];
- int paramf1 = (opcode & 1) ? paramb1 : (scriptState.xs->addr.pc.offset + 2 >= code_buf_size ? 0 : (int16)READ_SCI11ENDIAN_UINT16(code_buf + scriptState.xs->addr.pc.offset + 1));
+ int paramb1 = s->xs->addr.pc.offset + 1 >= code_buf_size ? 0 : code_buf[s->xs->addr.pc.offset + 1];
+ int paramf1 = (opcode & 1) ? paramb1 : (s->xs->addr.pc.offset + 2 >= code_buf_size ? 0 : (int16)READ_SCI11ENDIAN_UINT16(code_buf + s->xs->addr.pc.offset + 1));
switch (g_debugState.seeking) {
case kDebugSeekSpecialCallk:
@@ -381,8 +351,8 @@ void script_debug(EngineState *s) {
}
}
- printf("Step #%d\n", script_step_counter);
- disassemble(s, scriptState.xs->addr.pc, 0, 1);
+ printf("Step #%d\n", s->script_step_counter);
+ disassemble(s, s->xs->addr.pc, 0, 1);
if (g_debugState.runningStep) {
g_debugState.runningStep--;
diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp
index b18d76e1a7..4d3e6f754e 100644
--- a/engines/sci/engine/seg_manager.cpp
+++ b/engines/sci/engine/seg_manager.cpp
@@ -54,7 +54,6 @@ SegManager::SegManager(ResourceManager *resMan) {
createClassTable();
}
-// Destroy the object, free the memorys if allocated before
SegManager::~SegManager() {
resetSegMan();
}
@@ -77,10 +76,29 @@ void SegManager::resetSegMan() {
Hunks_seg_id = 0;
// Reinitialize class table
- _classtable.clear();
+ _classTable.clear();
createClassTable();
}
+void SegManager::initSysStrings() {
+ sysStrings = (SystemStrings *)allocSegment(new SystemStrings(), &sysStringsSegment);
+
+ // Allocate static buffer for savegame and CWD directories
+ SystemString *strSaveDir = &sysStrings->_strings[SYS_STRING_SAVEDIR];
+ strSaveDir->_name = "savedir";
+ strSaveDir->_maxSize = MAX_SAVE_DIR_SIZE;
+ strSaveDir->_value = (char *)calloc(MAX_SAVE_DIR_SIZE, sizeof(char));
+ // Set the savegame dir (actually, we set it to a fake value,
+ // since we cannot let the game control where saves are stored)
+ ::strcpy(strSaveDir->_value, "");
+
+ // Allocate static buffer for the parser base
+ SystemString *strParserBase = &sysStrings->_strings[SYS_STRING_PARSER_BASE];
+ strParserBase->_name = "parser-base";
+ strParserBase->_maxSize = MAX_PARSER_BASE;
+ strParserBase->_value = (char *)calloc(MAX_PARSER_BASE, sizeof(char));
+}
+
SegmentId SegManager::findFreeSegment() const {
// FIXME: This is a very crude approach: We find a free segment id by scanning
// from the start. This can be slow if the number of segments becomes large.
@@ -156,7 +174,7 @@ int SegManager::deallocate(SegmentId seg, bool recursive) {
}
bool SegManager::isHeapObject(reg_t pos) {
- Object *obj = getObject(pos);
+ const Object *obj = getObject(pos);
if (obj == NULL || (obj && obj->isFreed()))
return false;
Script *scr = getScriptIfLoaded(pos.segment);
@@ -223,7 +241,7 @@ Object *SegManager::getObject(reg_t pos) {
warning("getObject(): Trying to get an invalid object");
} else if (mobj->getType() == SEG_TYPE_SCRIPT) {
Script *scr = (Script *)mobj;
- if (pos.offset <= scr->_bufSize && pos.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET
+ if (pos.offset <= scr->getBufSize() && pos.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET
&& RAW_IS_OBJECT(scr->_buf + pos.offset)) {
obj = scr->getObject(pos.offset);
}
@@ -234,7 +252,7 @@ Object *SegManager::getObject(reg_t pos) {
}
const char *SegManager::getObjectName(reg_t pos) {
- Object *obj = getObject(pos);
+ const Object *obj = getObject(pos);
if (!obj)
return "<no such object>";
@@ -275,7 +293,7 @@ reg_t SegManager::findObjectByName(const Common::String &name, int index) {
// It's a script or a clone table, scan all objects in it
for (; idx < max_index; ++idx) {
- Object *obj = NULL;
+ const Object *obj = NULL;
reg_t objpos;
objpos.offset = 0;
objpos.segment = i;
@@ -393,10 +411,6 @@ DataStack *SegManager::allocateStack(int size, SegmentId *segid) {
return retval;
}
-SystemStrings *SegManager::allocateSysStrings(SegmentId *segid) {
- return (SystemStrings *)allocSegment(new SystemStrings(), segid);
-}
-
void SegManager::freeHunkEntry(reg_t addr) {
if (addr.isNull()) {
warning("Attempt to free a Hunk from a null address");
@@ -485,7 +499,7 @@ void SegManager::reconstructClones() {
continue;
CloneTable::Entry &seeker = ct->_table[j];
- Object *baseObj = getObject(seeker.getSpeciesSelector());
+ const Object *baseObj = getObject(seeker.getSpeciesSelector());
seeker.cloneFromObject(baseObj);
if (!baseObj)
warning("Clone entry without a base class: %d", j);
@@ -523,6 +537,16 @@ Node *SegManager::allocateNode(reg_t *addr) {
return &(table->_table[offset]);
}
+reg_t SegManager::newNode(reg_t value, reg_t key) {
+ reg_t nodebase;
+ Node *n = allocateNode(&nodebase);
+ n->pred = n->succ = NULL_REG;
+ n->key = key;
+ n->value = value;
+
+ return nodebase;
+}
+
List *SegManager::lookupList(reg_t addr) {
if (getSegmentType(addr.segment) != SEG_TYPE_LISTS) {
warning("Attempt to use non-list %04x:%04x as list", PRINT_REG(addr));
diff --git a/engines/sci/engine/seg_manager.h b/engines/sci/engine/seg_manager.h
index e8bbdbdb3f..9312f51f9d 100644
--- a/engines/sci/engine/seg_manager.h
+++ b/engines/sci/engine/seg_manager.h
@@ -112,12 +112,6 @@ public:
SegmentId getScriptSegment(int script_nr, ScriptLoadType load);
// TODO: document this
- reg_t lookupScriptExport(int script_nr, int export_index) {
- SegmentId seg = getScriptSegment(script_nr, SCRIPT_GET_DONT_LOAD);
- return make_reg(seg, getScript(seg)->validateExportFunc(export_index));
- }
-
- // TODO: document this
reg_t getClassAddress(int classnr, ScriptLoadType lock, reg_t caller);
/**
@@ -188,15 +182,10 @@ public:
// 5. System Strings
/**
- * Allocates a system string table
- * See also sys_string_acquire();
- * @param[in] segid Segment ID of the stack
- * @returns The physical stack
+ * Initializes the system string table.
*/
- SystemStrings *allocateSysStrings(SegmentId *segid);
-
+ void initSysStrings();
- // 5. System Strings
// 6, 7. Lists and Nodes
@@ -215,6 +204,14 @@ public:
Node *allocateNode(reg_t *addr);
/**
+ * Allocate and initialize a new list node.
+ * @param[in] value The value to set the node to
+ * @param[in] key The key to set
+ * @return Pointer to the newly initialized list node
+ */
+ reg_t newNode(reg_t value, reg_t key);
+
+ /**
* Resolves a list pointer to a list.
* @param addr The address to resolve
* @return The list referenced, or NULL on error
@@ -432,12 +429,22 @@ public:
*/
reg_t findObjectByName(const Common::String &name, int index = -1);
- void scriptRelocateExportsSci11(SegmentId seg);
void scriptInitialiseObjectsSci11(SegmentId seg);
+ uint32 classTableSize() { return _classTable.size(); }
+ Class getClass(int index) { return _classTable[index]; }
+ void setClassOffset(int index, reg_t offset) { _classTable[index].reg = offset; }
+ void resizeClassTable(uint32 size) { _classTable.resize(size); }
+
+ /**
+ * Obtains the system strings segment ID
+ */
+ SegmentId getSysStringsSegment() { return sysStringsSegment; }
+
public: // TODO: make private
Common::Array<SegmentObj *> _heap;
- Common::Array<Class> _classtable; /**< Table of all classes */
+ // Only accessible from saveLoadWithSerializer()
+ Common::Array<Class> _classTable; /**< Table of all classes */
#ifdef ENABLE_SCI32
SciArray<reg_t> *allocateArray(reg_t *addr);
@@ -460,6 +467,13 @@ private:
SegmentId Nodes_seg_id; ///< ID of the (a) node segment
SegmentId Hunks_seg_id; ///< ID of the (a) hunk segment
+ /* System strings */
+ SegmentId sysStringsSegment;
+public: // TODO: make private. Only kString() needs direct access
+ SystemStrings *sysStrings;
+
+private:
+
#ifdef ENABLE_SCI32
SegmentId Arrays_seg_id;
SegmentId String_seg_id;
diff --git a/engines/sci/engine/segment.cpp b/engines/sci/engine/segment.cpp
index ab1a68d165..0e0a759d4b 100644
--- a/engines/sci/engine/segment.cpp
+++ b/engines/sci/engine/segment.cpp
@@ -27,6 +27,7 @@
#include "sci/sci.h"
#include "sci/engine/features.h"
+#include "sci/engine/script.h" // for SCI_OBJ_EXPORTS and SCI_OBJ_SYNONYMS
#include "sci/engine/segment.h"
#include "sci/engine/seg_manager.h"
#include "sci/engine/state.h"
@@ -100,8 +101,7 @@ Script::Script() : SegmentObj(SEG_TYPE_SCRIPT) {
_localsSegment = 0;
_localsBlock = NULL;
- _relocated = false;
- _markedAsDeleted = 0;
+ _markedAsDeleted = false;
}
Script::~Script() {
@@ -117,54 +117,39 @@ void Script::freeScript() {
_codeBlocks.clear();
}
-bool Script::init(int script_nr, ResourceManager *resMan) {
- setScriptSize(script_nr, resMan);
-
- _buf = (byte *)malloc(_bufSize);
-
- if (!_buf) {
- freeScript();
- warning("Not enough memory space for script size");
- _bufSize = 0;
- return false;
- }
+void Script::init(int script_nr, ResourceManager *resMan) {
+ Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0);
_localsOffset = 0;
_localsBlock = NULL;
_codeBlocks.clear();
- _relocated = false;
_markedAsDeleted = false;
_nr = script_nr;
-
- if (getSciVersion() >= SCI_VERSION_1_1)
- _heapStart = _buf + _scriptSize;
- else
- _heapStart = _buf;
-
- return true;
-}
-
-void Script::setScriptSize(int script_nr, ResourceManager *resMan) {
- Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0);
- Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, script_nr), 0);
- bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
+ _buf = 0;
+ _heapStart = 0;
_scriptSize = script->size;
- _heapSize = 0; // Set later
+ _bufSize = script->size;
+ _heapSize = 0;
- if (!script || (getSciVersion() >= SCI_VERSION_1_1 && !heap)) {
- error("SegManager::setScriptSize: failed to load %s", !script ? "script" : "heap");
- }
- if (oldScriptHeader) {
- _bufSize = script->size + READ_LE_UINT16(script->data) * 2;
- //locals_size = READ_LE_UINT16(script->data) * 2;
- } else if (getSciVersion() < SCI_VERSION_1_1) {
- _bufSize = script->size;
- } else {
- _bufSize = script->size + heap->size;
+ _lockers = 1;
+
+ if (getSciVersion() == SCI_VERSION_0_EARLY) {
+ _bufSize += READ_LE_UINT16(script->data) * 2;
+ } else if (getSciVersion() >= SCI_VERSION_1_1) {
+ /**
+ * In SCI11, the heap was in a separate space from the script.
+ * We append it to the end of the script, and adjust addressing accordingly.
+ * However, since we address the heap with a 16-bit pointer, the combined
+ * size of the stack and the heap must be 64KB. So far this has worked
+ * for SCI11, SCI2 and SCI21 games. SCI3 games use a different script format,
+ * and theoretically they can exceed the 64KB boundary using relocation.
+ */
+ Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, script_nr), 0);
+ _bufSize += heap->size;
_heapSize = heap->size;
// Ensure that the start of the heap resource can be word-aligned.
@@ -173,12 +158,56 @@ void Script::setScriptSize(int script_nr, ResourceManager *resMan) {
_scriptSize++;
}
- if (_bufSize > 65535) {
- error("Script and heap sizes combined exceed 64K."
- "This means a fundamental design bug was made in SCI\n"
- "regarding SCI1.1 games.\nPlease report this so it can be"
- "fixed in the next major version");
- return;
+ // As mentioned above, the script and the heap together should not exceed 64KB
+ if (_bufSize > 65535)
+ error("Script and heap sizes combined exceed 64K. This means a fundamental "
+ "design bug was made regarding SCI1.1 and newer games.\nPlease "
+ "report this error to the ScummVM team");
+ }
+}
+
+void Script::load(ResourceManager *resMan) {
+ Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, _nr), 0);
+ assert(script != 0);
+
+ _buf = (byte *)malloc(_bufSize);
+ assert(_buf);
+
+ assert(_bufSize >= script->size);
+ memcpy(_buf, script->data, script->size);
+
+ if (getSciVersion() >= SCI_VERSION_1_1) {
+ Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, _nr), 0);
+ assert(heap != 0);
+
+ _heapStart = _buf + _scriptSize;
+
+ assert(_bufSize - _scriptSize <= heap->size);
+ memcpy(_heapStart, heap->data, heap->size);
+ }
+
+ _codeBlocks.clear();
+
+ _exportTable = 0;
+ _numExports = 0;
+ _synonyms = 0;
+ _numSynonyms = 0;
+
+ if (getSciVersion() >= SCI_VERSION_1_1) {
+ if (READ_LE_UINT16(_buf + 1 + 5) > 0) {
+ _exportTable = (const uint16 *)(_buf + 1 + 5 + 2);
+ _numExports = READ_SCI11ENDIAN_UINT16(_exportTable - 1);
+ }
+ } else {
+ _exportTable = (const uint16 *)findBlock(SCI_OBJ_EXPORTS);
+ if (_exportTable) {
+ _numExports = READ_SCI11ENDIAN_UINT16(_exportTable + 1);
+ _exportTable += 3; // skip header plus 2 bytes (_exportTable is a uint16 pointer)
+ }
+ _synonyms = findBlock(SCI_OBJ_SYNONYMS);
+ if (_synonyms) {
+ _numSynonyms = READ_SCI11ENDIAN_UINT16(_synonyms + 2) / 4;
+ _synonyms += 4; // skip header
}
}
}
@@ -194,19 +223,26 @@ Object *Script::getObject(uint16 offset) {
return 0;
}
-Object *Script::scriptObjInit(reg_t obj_pos) {
+const Object *Script::getObject(uint16 offset) const {
+ if (_objects.contains(offset))
+ return &_objects[offset];
+ else
+ return 0;
+}
+
+Object *Script::scriptObjInit(reg_t obj_pos, bool fullObjectInit) {
Object *obj;
- if (getSciVersion() < SCI_VERSION_1_1)
+ if (getSciVersion() < SCI_VERSION_1_1 && fullObjectInit)
obj_pos.offset += 8; // magic offset (SCRIPT_OBJECT_MAGIC_OFFSET)
VERIFY(obj_pos.offset < _bufSize, "Attempt to initialize object beyond end of script\n");
obj = allocateObject(obj_pos.offset);
- VERIFY(obj_pos.offset + SCRIPT_FUNCTAREAPTR_OFFSET < (int)_bufSize, "Function area pointer stored beyond end of script\n");
+ VERIFY(obj_pos.offset + kOffsetFunctionArea < (int)_bufSize, "Function area pointer stored beyond end of script\n");
- obj->init(_buf, obj_pos);
+ obj->init(_buf, obj_pos, fullObjectInit);
return obj;
}
@@ -218,37 +254,34 @@ void Script::scriptObjRemove(reg_t obj_pos) {
_objects.erase(obj_pos.toUint16());
}
-int Script::relocateBlock(Common::Array<reg_t> &block, int block_location, SegmentId segment, int location) {
+// This helper function is used by Script::relocateLocal and Object::relocate
+static bool relocateBlock(Common::Array<reg_t> &block, int block_location, SegmentId segment, int location, size_t scriptSize) {
int rel = location - block_location;
if (rel < 0)
- return 0;
+ return false;
uint idx = rel >> 1;
if (idx >= block.size())
- return 0;
+ return false;
if (rel & 1) {
warning("Attempt to relocate odd variable #%d.5e (relative to %04x)\n", idx, block_location);
- return 0;
+ return false;
}
block[idx].segment = segment; // Perform relocation
if (getSciVersion() >= SCI_VERSION_1_1)
- block[idx].offset += _scriptSize;
+ block[idx].offset += scriptSize;
- return 1;
+ return true;
}
-int Script::relocateLocal(SegmentId segment, int location) {
+bool Script::relocateLocal(SegmentId segment, int location) {
if (_localsBlock)
- return relocateBlock(_localsBlock->_locals, _localsOffset, segment, location);
+ return relocateBlock(_localsBlock->_locals, _localsOffset, segment, location, _scriptSize);
else
- return 0; // No hands, no cookies
-}
-
-int Script::relocateObject(Object &obj, SegmentId segment, int location) {
- return relocateBlock(obj._variables, obj.getPos().offset, segment, location);
+ return false;
}
void Script::scriptAddCodeBlock(reg_t location) {
@@ -258,61 +291,35 @@ void Script::scriptAddCodeBlock(reg_t location) {
_codeBlocks.push_back(cb);
}
-void Script::scriptRelocate(reg_t block) {
- VERIFY(block.offset < (uint16)_bufSize && READ_SCI11ENDIAN_UINT16(_buf + block.offset) * 2 + block.offset < (uint16)_bufSize,
- "Relocation block outside of script\n");
-
- int count = READ_SCI11ENDIAN_UINT16(_buf + block.offset);
-
- for (int i = 0; i <= count; i++) {
- int pos = READ_SCI11ENDIAN_UINT16(_buf + block.offset + 2 + (i * 2));
- if (!pos)
- continue; // FIXME: A hack pending investigation
-
- if (!relocateLocal(block.segment, pos)) {
- bool done = false;
- uint k;
-
- ObjMap::iterator it;
- const ObjMap::iterator end = _objects.end();
- for (it = _objects.begin(); !done && it != end; ++it) {
- if (relocateObject(it->_value, block.segment, pos))
- done = true;
- }
-
- for (k = 0; !done && k < _codeBlocks.size(); k++) {
- if (pos >= _codeBlocks[k].pos.offset &&
- pos < _codeBlocks[k].pos.offset + _codeBlocks[k].size)
- done = true;
- }
+void Script::relocate(reg_t block) {
+ byte *heap = _buf;
+ uint16 heapSize = (uint16)_bufSize;
+ uint16 heapOffset = 0;
- if (!done) {
- printf("While processing relocation block %04x:%04x:\n", PRINT_REG(block));
- printf("Relocation failed for index %04x (%d/%d)\n", pos, i + 1, count);
- if (_localsBlock)
- printf("- locals: %d at %04x\n", _localsBlock->_locals.size(), _localsOffset);
- else
- printf("- No locals\n");
- for (it = _objects.begin(), k = 0; it != end; ++it, ++k)
- printf("- obj#%d at %04x w/ %d vars\n", k, it->_value.getPos().offset, it->_value.getVarCount());
- // SQ3 script 71 has broken relocation entries.
- printf("Trying to continue anyway...\n");
- }
- }
+ if (getSciVersion() >= SCI_VERSION_1_1) {
+ heap = _heapStart;
+ heapSize = (uint16)_heapSize;
+ heapOffset = _scriptSize;
}
-}
-void Script::heapRelocate(reg_t block) {
- VERIFY(block.offset < (uint16)_heapSize && READ_SCI11ENDIAN_UINT16(_heapStart + block.offset) * 2 + block.offset < (uint16)_bufSize,
+ VERIFY(block.offset < (uint16)heapSize && READ_SCI11ENDIAN_UINT16(heap + block.offset) * 2 + block.offset < (uint16)heapSize,
"Relocation block outside of script\n");
- if (_relocated)
- return;
- _relocated = true;
- int count = READ_SCI11ENDIAN_UINT16(_heapStart + block.offset);
+ int count = READ_SCI11ENDIAN_UINT16(heap + block.offset);
+ int exportIndex = 0;
for (int i = 0; i < count; i++) {
- int pos = READ_SCI11ENDIAN_UINT16(_heapStart + block.offset + 2 + (i * 2)) + _scriptSize;
+ int pos = READ_SCI11ENDIAN_UINT16(heap + block.offset + 2 + (exportIndex * 2)) + heapOffset;
+ // This occurs in SCI01/SCI1 games where every usually one export
+ // value is zero. It seems that in this situation, we should skip
+ // the export and move to the next one, though the total count
+ // of valid exports remains the same
+ if (!pos) {
+ exportIndex++;
+ pos = READ_SCI11ENDIAN_UINT16(heap + block.offset + 2 + (exportIndex * 2)) + heapOffset;
+ if (!pos)
+ error("Script::relocate(): Consecutive zero exports found");
+ }
if (!relocateLocal(block.segment, pos)) {
bool done = false;
@@ -321,22 +328,33 @@ void Script::heapRelocate(reg_t block) {
ObjMap::iterator it;
const ObjMap::iterator end = _objects.end();
for (it = _objects.begin(); !done && it != end; ++it) {
- if (relocateObject(it->_value, block.segment, pos))
+ if (it->_value.relocate(block.segment, pos, _scriptSize))
done = true;
}
+ // Sanity check for SCI0-SCI1
+ if (getSciVersion() < SCI_VERSION_1_1) {
+ for (k = 0; !done && k < _codeBlocks.size(); k++) {
+ if (pos >= _codeBlocks[k].pos.offset &&
+ pos < _codeBlocks[k].pos.offset + _codeBlocks[k].size)
+ done = true;
+ }
+ }
+
if (!done) {
- printf("While processing relocation block %04x:%04x:\n", PRINT_REG(block));
- printf("Relocation failed for index %04x (%d/%d)\n", pos, i + 1, count);
+ debug("While processing relocation block %04x:%04x:\n", PRINT_REG(block));
+ debug("Relocation failed for index %04x (%d/%d)\n", pos, exportIndex + 1, count);
if (_localsBlock)
- printf("- locals: %d at %04x\n", _localsBlock->_locals.size(), _localsOffset);
+ debug("- locals: %d at %04x\n", _localsBlock->_locals.size(), _localsOffset);
else
- printf("- No locals\n");
+ debug("- No locals\n");
for (it = _objects.begin(), k = 0; it != end; ++it, ++k)
- printf("- obj#%d at %04x w/ %d vars\n", k, it->_value.getPos().offset, it->_value.getVarCount());
- error("Breakpoint in %s, line %d", __FILE__, __LINE__);
+ debug("- obj#%d at %04x w/ %d vars\n", k, it->_value.getPos().offset, it->_value.getVarCount());
+ debug("Trying to continue anyway...\n");
}
}
+
+ exportIndex++;
}
}
@@ -357,16 +375,6 @@ void Script::setLockers(int lockers) {
_lockers = lockers;
}
-void Script::setExportTableOffset(int offset) {
- if (offset) {
- _exportTable = (uint16 *)(_buf + offset + 2);
- _numExports = READ_SCI11ENDIAN_UINT16((byte *)(_exportTable - 1));
- } else {
- _exportTable = NULL;
- _numExports = 0;
- }
-}
-
uint16 Script::validateExportFunc(int pubfunct) {
bool exportsAreWide = (g_sci->_features->detectLofsType() == SCI_VERSION_1_MIDDLE);
@@ -377,28 +385,36 @@ uint16 Script::validateExportFunc(int pubfunct) {
if (exportsAreWide)
pubfunct *= 2;
- uint16 offset = READ_SCI11ENDIAN_UINT16((byte *)(_exportTable + pubfunct));
+ uint16 offset = READ_SCI11ENDIAN_UINT16(_exportTable + pubfunct);
VERIFY(offset < _bufSize, "invalid export function pointer");
return offset;
}
-void Script::setSynonymsOffset(int offset) {
- _synonyms = _buf + offset;
-}
+byte *Script::findBlock(int type) {
+ byte *buf = _buf;
+ bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
-byte *Script::getSynonyms() const {
- return _synonyms;
-}
+ if (oldScriptHeader)
+ buf += 2;
-void Script::setSynonymsNr(int n) {
- _numSynonyms = n;
-}
+ do {
+ int seekerType = READ_LE_UINT16(buf);
+
+ if (seekerType == 0)
+ break;
+ if (seekerType == type)
+ return buf;
-int Script::getSynonymsNr() const {
- return _numSynonyms;
+ int seekerSize = READ_LE_UINT16(buf + 2);
+ assert(seekerSize > 0);
+ buf += seekerSize;
+ } while (1);
+
+ return NULL;
}
+
// memory operations
void Script::mcpyInOut(int dst, const void *src, size_t n) {
@@ -511,7 +527,7 @@ SegmentRef SystemStrings::dereference(reg_t pointer) {
//-------------------- script --------------------
-reg_t Script::findCanonicAddress(SegManager *segMan, reg_t addr) {
+reg_t Script::findCanonicAddress(SegManager *segMan, reg_t addr) const {
addr.offset = 0;
return addr;
}
@@ -527,13 +543,13 @@ void Script::freeAtAddress(SegManager *segMan, reg_t addr) {
segMan->deallocateScript(_nr);
}
-void Script::listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) {
+void Script::listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const {
(*note)(param, make_reg(segId, 0));
}
-void Script::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) {
+void Script::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const {
if (addr.offset <= _bufSize && addr.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET && RAW_IS_OBJECT(_buf + addr.offset)) {
- Object *obj = getObject(addr.offset);
+ const Object *obj = getObject(addr.offset);
if (obj) {
// Note all local variables, if we have a local variable environment
if (_localsSegment)
@@ -553,16 +569,14 @@ void Script::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback not
//-------------------- clones --------------------
-void CloneTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) {
- Clone *clone;
-
+void CloneTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const {
// assert(addr.segment == _segId);
if (!isValidEntry(addr.offset)) {
error("Unexpected request for outgoing references from clone at %04x:%04x", PRINT_REG(addr));
}
- clone = &(_table[addr.offset]);
+ const Clone *clone = &(_table[addr.offset]);
// Emit all member variables (including references to the 'super' delegate)
for (uint i = 0; i < clone->getVarCount(); i++)
@@ -597,7 +611,7 @@ void CloneTable::freeAtAddress(SegManager *segMan, reg_t addr) {
//-------------------- locals --------------------
-reg_t LocalVariables::findCanonicAddress(SegManager *segMan, reg_t addr) {
+reg_t LocalVariables::findCanonicAddress(SegManager *segMan, reg_t addr) const {
// Reference the owning script
SegmentId owner_seg = segMan->getScriptSegment(script_id);
@@ -606,7 +620,7 @@ reg_t LocalVariables::findCanonicAddress(SegManager *segMan, reg_t addr) {
return make_reg(owner_seg, 0);
}
-void LocalVariables::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) {
+void LocalVariables::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const {
// assert(addr.segment == _segId);
for (uint i = 0; i < _locals.size(); i++)
@@ -615,12 +629,12 @@ void LocalVariables::listAllOutgoingReferences(reg_t addr, void *param, NoteCall
//-------------------- stack --------------------
-reg_t DataStack::findCanonicAddress(SegManager *segMan, reg_t addr) {
+reg_t DataStack::findCanonicAddress(SegManager *segMan, reg_t addr) const {
addr.offset = 0;
return addr;
}
-void DataStack::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) {
+void DataStack::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const {
fprintf(stderr, "Emitting %d stack entries\n", _capacity);
for (int i = 0; i < _capacity; i++)
(*note)(param, _entries[i]);
@@ -633,13 +647,13 @@ void ListTable::freeAtAddress(SegManager *segMan, reg_t sub_addr) {
freeEntry(sub_addr.offset);
}
-void ListTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) {
+void ListTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const {
if (!isValidEntry(addr.offset)) {
warning("Invalid list referenced for outgoing references: %04x:%04x", PRINT_REG(addr));
return;
}
- List *list = &(_table[addr.offset]);
+ const List *list = &(_table[addr.offset]);
note(param, list->first);
note(param, list->last);
@@ -653,12 +667,12 @@ void NodeTable::freeAtAddress(SegManager *segMan, reg_t sub_addr) {
freeEntry(sub_addr.offset);
}
-void NodeTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) {
+void NodeTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const {
if (!isValidEntry(addr.offset)) {
warning("Invalid node referenced for outgoing references: %04x:%04x", PRINT_REG(addr));
return;
}
- Node *node = &(_table[addr.offset]);
+ const Node *node = &(_table[addr.offset]);
// We need all four here. Can't just stick with 'pred' OR 'succ' because node operations allow us
// to walk around from any given node
@@ -673,20 +687,43 @@ void NodeTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback
//-------------------- object ----------------------------
-Object *Object::getClass(SegManager *segMan) {
+void Object::init(byte *buf, reg_t obj_pos, bool initVariables) {
+ byte *data = buf + obj_pos.offset;
+ _baseObj = data;
+ _pos = obj_pos;
+
+ if (getSciVersion() < SCI_VERSION_1_1) {
+ _variables.resize(READ_LE_UINT16(data + kOffsetSelectorCounter));
+ _baseVars = (const uint16 *)(_baseObj + _variables.size() * 2);
+ _baseMethod = (const uint16 *)(data + READ_LE_UINT16(data + kOffsetFunctionArea));
+ _methodCount = READ_LE_UINT16(_baseMethod - 1);
+ } else {
+ _variables.resize(READ_SCI11ENDIAN_UINT16(data + 2));
+ _baseVars = (const uint16 *)(buf + READ_SCI11ENDIAN_UINT16(data + 4));
+ _baseMethod = (const uint16 *)(buf + READ_SCI11ENDIAN_UINT16(data + 6));
+ _methodCount = READ_SCI11ENDIAN_UINT16(_baseMethod);
+ }
+
+ if (initVariables) {
+ for (uint i = 0; i < _variables.size(); i++)
+ _variables[i] = make_reg(0, READ_SCI11ENDIAN_UINT16(data + (i * 2)));
+ }
+}
+
+const Object *Object::getClass(SegManager *segMan) const {
return isClass() ? this : segMan->getObject(getSuperClassSelector());
}
-int Object::locateVarSelector(SegManager *segMan, Selector slc) {
- byte *buf;
+int Object::locateVarSelector(SegManager *segMan, Selector slc) const {
+ const byte *buf;
uint varnum;
if (getSciVersion() < SCI_VERSION_1_1) {
varnum = getVarCount();
- int selector_name_offset = varnum * 2 + SCRIPT_SELECTOR_OFFSET;
+ int selector_name_offset = varnum * 2 + kOffsetSelectorSegment;
buf = _baseObj + selector_name_offset;
} else {
- Object *obj = getClass(segMan);
+ const Object *obj = getClass(segMan);
varnum = obj->getVariable(1).toUint16();
buf = (byte *)obj->_baseVars;
}
@@ -698,14 +735,72 @@ int Object::locateVarSelector(SegManager *segMan, Selector slc) {
return -1; // Failed
}
+bool Object::relocate(SegmentId segment, int location, size_t scriptSize) {
+ return relocateBlock(_variables, getPos().offset, segment, location, scriptSize);
+}
+
+int Object::propertyOffsetToId(SegManager *segMan, int propertyOffset) const {
+ int selectors = getVarCount();
+
+ if (propertyOffset < 0 || (propertyOffset >> 1) >= selectors) {
+ warning("Applied propertyOffsetToId to invalid property offset %x (property #%d not in [0..%d])",
+ propertyOffset, propertyOffset >> 1, selectors - 1);
+ return -1;
+ }
+
+ if (getSciVersion() < SCI_VERSION_1_1) {
+ const byte *selectoroffset = ((const byte *)(_baseObj)) + kOffsetSelectorSegment + selectors * 2;
+ return READ_SCI11ENDIAN_UINT16(selectoroffset + propertyOffset);
+ } else {
+ const Object *obj = this;
+ if (!isClass())
+ obj = segMan->getObject(getSuperClassSelector());
+
+ return READ_SCI11ENDIAN_UINT16((const byte *)obj->_baseVars + propertyOffset);
+ }
+}
+
+void Object::initSpecies(SegManager *segMan, reg_t addr) {
+ uint16 speciesOffset = getSpeciesSelector().offset;
+
+ if (speciesOffset == 0xffff) // -1
+ setSpeciesSelector(NULL_REG); // no species
+ else
+ setSpeciesSelector(segMan->getClassAddress(speciesOffset, SCRIPT_GET_LOCK, addr));
+}
+
+void Object::initSuperClass(SegManager *segMan, reg_t addr) {
+ uint16 superClassOffset = getSuperClassSelector().offset;
+
+ if (superClassOffset == 0xffff) // -1
+ setSuperClassSelector(NULL_REG); // no superclass
+ else
+ setSuperClassSelector(segMan->getClassAddress(superClassOffset, SCRIPT_GET_LOCK, addr));
+}
+
+bool Object::initBaseObject(SegManager *segMan, reg_t addr, bool doInitSuperClass) {
+ const Object *baseObj = segMan->getObject(getSpeciesSelector());
+
+ if (baseObj) {
+ _variables.resize(baseObj->getVarCount());
+ // Copy base from species class, as we need its selector IDs
+ _baseObj = baseObj->_baseObj;
+ if (doInitSuperClass)
+ initSuperClass(segMan, addr);
+ return true;
+ }
+
+ return false;
+}
+
//-------------------- dynamic memory --------------------
-reg_t DynMem::findCanonicAddress(SegManager *segMan, reg_t addr) {
+reg_t DynMem::findCanonicAddress(SegManager *segMan, reg_t addr) const {
addr.offset = 0;
return addr;
}
-void DynMem::listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) {
+void DynMem::listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const {
(*note)(param, make_reg(segId, 0));
}
@@ -724,13 +819,13 @@ void ArrayTable::freeAtAddress(SegManager *segMan, reg_t sub_addr) {
freeEntry(sub_addr.offset);
}
-void ArrayTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) {
+void ArrayTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const {
if (!isValidEntry(addr.offset)) {
warning("Invalid array referenced for outgoing references: %04x:%04x", PRINT_REG(addr));
return;
}
- SciArray<reg_t> *array = &(_table[addr.offset]);
+ const SciArray<reg_t> *array = &(_table[addr.offset]);
for (uint32 i = 0; i < array->getSize(); i++) {
reg_t value = array->getValue(i);
@@ -739,7 +834,7 @@ void ArrayTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback
}
}
-Common::String SciString::toString() {
+Common::String SciString::toString() const {
if (_type != 3)
error("SciString::toString(): Array is not a string");
@@ -750,7 +845,7 @@ Common::String SciString::toString() {
return string;
}
-void SciString::fromString(Common::String string) {
+void SciString::fromString(const Common::String &string) {
if (_type != 3)
error("SciString::fromString(): Array is not a string");
diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h
index 1089ada475..f1b6dccaa2 100644
--- a/engines/sci/engine/segment.h
+++ b/engines/sci/engine/segment.h
@@ -112,7 +112,7 @@ public:
*
* @param sub_addr base address whose canonic address is to be found
*/
- virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) { return sub_addr; }
+ virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const { return sub_addr; }
/**
* Deallocates all memory associated with the specified address.
@@ -125,7 +125,7 @@ public:
* @param note Invoked for each address on which free_at_address() makes sense
* @param param parameter passed to 'note'
*/
- virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) {}
+ virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const {}
/**
* Iterates over all references reachable from the specified object.
@@ -134,7 +134,7 @@ public:
* @param note Invoked for each outgoing reference within the object
* Note: This function may also choose to report numbers (segment 0) as adresses
*/
- virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) {}
+ virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const {}
};
@@ -194,8 +194,8 @@ public:
virtual bool isValidOffset(uint16 offset) const;
virtual SegmentRef dereference(reg_t pointer);
- virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr);
- virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note);
+ virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const;
+ virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const;
virtual void saveLoadWithSerializer(Common::Serializer &ser);
};
@@ -205,6 +205,22 @@ enum {
OBJECT_FLAG_FREED = (1 << 0)
};
+enum infoSelectorFlags {
+ kInfoFlagClone = 0x0001,
+ kInfoFlagClass = 0x8000
+};
+
+enum ObjectOffsets {
+ kOffsetLocalVariables = -6,
+ kOffsetFunctionArea = -4,
+ kOffsetSelectorCounter = -2,
+ kOffsetSelectorSegment = 0,
+ kOffsetInfoSelectorSci0 = 4,
+ kOffsetNamePointerSci0 = 6,
+ kOffsetInfoSelectorSci11 = 14,
+ kOffsetNamePointerSci11 = 16
+};
+
class Object {
public:
Object() {
@@ -214,31 +230,34 @@ public:
~Object() { }
- reg_t getSpeciesSelector() { return _variables[_offset]; }
+ reg_t getSpeciesSelector() const { return _variables[_offset]; }
void setSpeciesSelector(reg_t value) { _variables[_offset] = value; }
- reg_t getSuperClassSelector() { return _variables[_offset + 1]; }
+ reg_t getSuperClassSelector() const { return _variables[_offset + 1]; }
void setSuperClassSelector(reg_t value) { _variables[_offset + 1] = value; }
- reg_t getInfoSelector() { return _variables[_offset + 2]; }
- void setInfoSelector(reg_t value) { _variables[_offset + 2] = value; }
+ reg_t getInfoSelector() const { return _variables[_offset + 2]; }
+ void setInfoSelector(reg_t value) { _variables[_offset + 2] = value; }
- reg_t getNameSelector() { return _variables[_offset + 3]; }
- void setNameSelector(reg_t value) { _variables[_offset + 3] = value; }
+ reg_t getNameSelector() const { return _variables[_offset + 3]; }
+ void setNameSelector(reg_t value) { _variables[_offset + 3] = value; }
- reg_t getClassScriptSelector() { return _variables[4]; }
+ reg_t getPropDictSelector() const { return _variables[2]; }
+ void setPropDictSelector(reg_t value) { _variables[2] = value; }
+
+ reg_t getClassScriptSelector() const { return _variables[4]; }
void setClassScriptSelector(reg_t value) { _variables[4] = value; }
- Selector getVarSelector(uint16 i) { return READ_SCI11ENDIAN_UINT16(_baseVars + i); }
+ Selector getVarSelector(uint16 i) const { return READ_SCI11ENDIAN_UINT16(_baseVars + i); }
- reg_t getFunction(uint16 i) {
+ reg_t getFunction(uint16 i) const {
uint16 offset = (getSciVersion() < SCI_VERSION_1_1) ? _methodCount + 1 + i : i * 2 + 2;
- return make_reg(_pos.segment, READ_SCI11ENDIAN_UINT16((byte *) (_baseMethod + offset)));
+ return make_reg(_pos.segment, READ_SCI11ENDIAN_UINT16(_baseMethod + offset));
}
- Selector getFuncSelector(uint16 i) {
+ Selector getFuncSelector(uint16 i) const {
uint16 offset = (getSciVersion() < SCI_VERSION_1_1) ? i : i * 2 + 1;
- return READ_SCI11ENDIAN_UINT16((byte *) (_baseMethod + offset));
+ return READ_SCI11ENDIAN_UINT16(_baseMethod + offset);
}
/**
@@ -247,7 +266,7 @@ public:
* superclasses, i.e. failure may be returned even if one of the
* superclasses defines the funcselector
*/
- int funcSelectorPosition(Selector sel) {
+ int funcSelectorPosition(Selector sel) const {
for (uint i = 0; i < _methodCount; i++)
if (getFuncSelector(i) == sel)
return i;
@@ -256,61 +275,56 @@ public:
}
/**
- * Determines if the object explicitly defines slc as a varselector
- * Returns -1 if not found
+ * Determines if the object explicitly defines slc as a varselector.
+ * Returns -1 if not found.
*/
- int locateVarSelector(SegManager *segMan, Selector slc);
-
- bool isClass() { return (getInfoSelector().offset & SCRIPT_INFO_CLASS); }
- Object *getClass(SegManager *segMan);
+ int locateVarSelector(SegManager *segMan, Selector slc) const;
- void markAsFreed() { _flags |= OBJECT_FLAG_FREED; }
- bool isFreed() { return _flags & OBJECT_FLAG_FREED; }
+ bool isClass() const { return (getInfoSelector().offset & kInfoFlagClass); }
+ const Object *getClass(SegManager *segMan) const;
- void setVarCount(uint size) { _variables.resize(size); }
- uint getVarCount() { return _variables.size(); }
+ void markAsClone() { setInfoSelector(make_reg(0, kInfoFlagClone)); }
+ bool isClone() const { return (getInfoSelector().offset & kInfoFlagClone); }
- void init(byte *buf, reg_t obj_pos) {
- byte *data = (byte *)(buf + obj_pos.offset);
- _baseObj = data;
- _pos = obj_pos;
+ void markAsFreed() { _flags |= OBJECT_FLAG_FREED; }
+ bool isFreed() const { return _flags & OBJECT_FLAG_FREED; }
- if (getSciVersion() < SCI_VERSION_1_1) {
- _variables.resize(READ_LE_UINT16(data + SCRIPT_SELECTORCTR_OFFSET));
- _baseVars = (uint16 *)(_baseObj + _variables.size() * 2);
- _baseMethod = (uint16 *)(data + READ_LE_UINT16(data + SCRIPT_FUNCTAREAPTR_OFFSET));
- _methodCount = READ_LE_UINT16(_baseMethod - 1);
- } else {
- _variables.resize(READ_SCI11ENDIAN_UINT16(data + 2));
- _baseVars = (uint16 *)(buf + READ_SCI11ENDIAN_UINT16(data + 4));
- _baseMethod = (uint16 *)(buf + READ_SCI11ENDIAN_UINT16(data + 6));
- _methodCount = READ_SCI11ENDIAN_UINT16(_baseMethod);
- }
+ uint getVarCount() const { return _variables.size(); }
- for (uint i = 0; i < _variables.size(); i++)
- _variables[i] = make_reg(0, READ_SCI11ENDIAN_UINT16(data + (i * 2)));
- }
+ void init(byte *buf, reg_t obj_pos, bool initVariables = true);
- reg_t getVariable(uint var) { return _variables[var]; }
+ reg_t getVariable(uint var) const { return _variables[var]; }
+ reg_t &getVariableRef(uint var) { return _variables[var]; }
- uint16 getMethodCount() { return _methodCount; }
- reg_t getPos() { return _pos; }
+ uint16 getMethodCount() const { return _methodCount; }
+ reg_t getPos() const { return _pos; }
void saveLoadWithSerializer(Common::Serializer &ser);
- void cloneFromObject(Object *obj) {
+ void cloneFromObject(const Object *obj) {
_baseObj = obj ? obj->_baseObj : NULL;
_baseMethod = obj ? obj->_baseMethod : NULL;
_baseVars = obj ? obj->_baseVars : NULL;
}
+ bool relocate(SegmentId segment, int location, size_t scriptSize);
+
+ int propertyOffsetToId(SegManager *segMan, int propertyOffset) const;
+
+ void initSpecies(SegManager *segMan, reg_t addr);
+ void initSuperClass(SegManager *segMan, reg_t addr);
+ bool initBaseObject(SegManager *segMan, reg_t addr, bool doInitSuperClass = true);
+
// TODO: make private
- Common::Array<reg_t> _variables;
- byte *_baseObj; /**< base + object offset within base */
- uint16 *_baseVars; /**< Pointer to the varselector area for this object */
- uint16 *_baseMethod; /**< Pointer to the method selector area for this object */
+ // Only SegManager::reconstructScripts() is left needing direct access to these
+public:
+ const byte *_baseObj; /**< base + object offset within base */
private:
+ const uint16 *_baseVars; /**< Pointer to the varselector area for this object */
+ const uint16 *_baseMethod; /**< Pointer to the method selector area for this object */
+
+ Common::Array<reg_t> _variables;
uint16 _methodCount;
int _flags;
uint16 _offset;
@@ -328,21 +342,28 @@ class Script : public SegmentObj {
public:
int _nr; /**< Script number */
byte *_buf; /**< Static data buffer, or NULL if not used */
- size_t _bufSize;
- size_t _scriptSize;
- size_t _heapSize;
-
byte *_heapStart; /**< Start of heap if SCI1.1, NULL otherwise */
- uint16 *_exportTable; /**< Abs. offset of the export table or 0 if not present */
- int _numExports; /**< Number of entries in the exports table */
-
- byte *_synonyms; /**< Synonyms block or 0 if not present*/
- int _numSynonyms; /**< Number of entries in the synonyms block */
+ uint32 getScriptSize() { return _scriptSize; }
+ uint32 getHeapSize() { return _heapSize; }
+ uint32 getBufSize() { return _bufSize; }
protected:
int _lockers; /**< Number of classes and objects that require this script */
+private:
+ size_t _scriptSize;
+ size_t _heapSize;
+ size_t _bufSize;
+
+ const uint16 *_exportTable; /**< Abs. offset of the export table or 0 if not present */
+ uint16 _numExports; /**< Number of entries in the exports table */
+
+ const byte *_synonyms; /**< Synonyms block or 0 if not present*/
+ uint16 _numSynonyms; /**< Number of entries in the synonyms block */
+
+ Common::Array<CodeBlock> _codeBlocks;
+
public:
/**
* Table for objects, contains property variables.
@@ -354,8 +375,6 @@ public:
SegmentId _localsSegment; /**< The local variable segment */
LocalVariables *_localsBlock;
- Common::Array<CodeBlock> _codeBlocks;
- bool _relocated;
bool _markedAsDeleted;
public:
@@ -363,19 +382,21 @@ public:
~Script();
void freeScript();
- bool init(int script_nr, ResourceManager *resMan);
+ void init(int script_nr, ResourceManager *resMan);
+ void load(ResourceManager *resMan);
virtual bool isValidOffset(uint16 offset) const;
virtual SegmentRef dereference(reg_t pointer);
- virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr);
+ virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const;
virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr);
- virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note);
- virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note);
+ virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const;
+ virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const;
virtual void saveLoadWithSerializer(Common::Serializer &ser);
Object *allocateObject(uint16 offset);
Object *getObject(uint16 offset);
+ const Object *getObject(uint16 offset) const;
/**
* Informs the segment manager that a code block must be relocated
@@ -392,7 +413,7 @@ public:
* @returns A newly created Object describing the object,
* stored within the relevant script
*/
- Object *scriptObjInit(reg_t obj_pos);
+ Object *scriptObjInit(reg_t obj_pos, bool fullObjectInit = true);
/**
* Removes a script object
@@ -407,14 +428,10 @@ public:
* @param obj_pos Location (segment, offset) of the block
* @return Location of the relocation block
*/
- void scriptRelocate(reg_t block);
-
- void heapRelocate(reg_t block);
+ void relocate(reg_t block);
private:
- int relocateLocal(SegmentId segment, int location);
- int relocateBlock(Common::Array<reg_t> &block, int block_location, SegmentId segment, int location);
- int relocateObject(Object &obj, SegmentId segment, int location);
+ bool relocateLocal(SegmentId segment, int location);
public:
// script lock operations
@@ -435,22 +452,28 @@ public:
void setLockers(int lockers);
/**
+ * Retrieves a pointer to the exports of this script
+ * @return pointer to the exports.
+ */
+ const uint16 *getExportTable() const { return _exportTable; }
+
+ /**
+ * Retrieves the number of exports of script.
+ * @return the number of exports of this script
+ */
+ uint16 getExportsNr() const { return _numExports; }
+
+ /**
* Retrieves a pointer to the synonyms associated with this script
* @return pointer to the synonyms, in non-parsed format.
*/
- byte *getSynonyms() const;
+ const byte *getSynonyms() const { return _synonyms; }
/**
* Retrieves the number of synonyms associated with this script.
* @return the number of synonyms associated with this script
*/
- int getSynonymsNr() const;
-
- /**
- * Sets the script-relative offset of the exports table.
- * @param offset script-relative exports table offset
- */
- void setExportTableOffset(int offset);
+ uint16 getSynonymsNr() const { return _numSynonyms; }
/**
* Validate whether the specified public function is exported by
@@ -462,19 +485,6 @@ public:
uint16 validateExportFunc(int pubfunct);
/**
- * Sets the script-relative offset of the synonyms associated with this script.
- * @param offset script-relative offset of the synonyms block
- */
- void setSynonymsOffset(int offset);
-
- /**
- * Sets the number of synonyms associated with this script,
- * @param nr number of synonyms, as to be stored within the script
- */
- void setSynonymsNr(int nr);
-
-
- /**
* Marks the script as deleted.
* This will not actually delete the script. If references remain present on the
* heap or the stack, the script will stay in memory in a quasi-deleted state until
@@ -508,8 +518,10 @@ public:
*/
int16 getHeap(uint16 offset) const;
-private:
- void setScriptSize(int script_nr, ResourceManager *resMan);
+ /**
+ * Finds the pointer where a block of a specific type starts from
+ */
+ byte *findBlock(int type);
};
/** Data stack */
@@ -529,8 +541,8 @@ public:
virtual bool isValidOffset(uint16 offset) const;
virtual SegmentRef dereference(reg_t pointer);
- virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr);
- virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note);
+ virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const;
+ virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const;
virtual void saveLoadWithSerializer(Common::Serializer &ser);
};
@@ -618,7 +630,7 @@ public:
entries_used--;
}
- virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) {
+ virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const {
for (uint i = 0; i < _table.size(); i++)
if (isValidEntry(i))
(*note)(param, make_reg(segId, i));
@@ -631,7 +643,7 @@ struct CloneTable : public Table<Clone> {
CloneTable() : Table<Clone>(SEG_TYPE_CLONES) {}
virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr);
- virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note);
+ virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const;
virtual void saveLoadWithSerializer(Common::Serializer &ser);
};
@@ -642,7 +654,7 @@ struct NodeTable : public Table<Node> {
NodeTable() : Table<Node>(SEG_TYPE_NODES) {}
virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr);
- virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note);
+ virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const;
virtual void saveLoadWithSerializer(Common::Serializer &ser);
};
@@ -653,7 +665,7 @@ struct ListTable : public Table<List> {
ListTable() : Table<List>(SEG_TYPE_LISTS) {}
virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr);
- virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note);
+ virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const;
virtual void saveLoadWithSerializer(Common::Serializer &ser);
};
@@ -688,8 +700,8 @@ public:
virtual bool isValidOffset(uint16 offset) const;
virtual SegmentRef dereference(reg_t pointer);
- virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr);
- virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note);
+ virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const;
+ virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const;
virtual void saveLoadWithSerializer(Common::Serializer &ser);
};
@@ -782,7 +794,7 @@ public:
_size = _actualSize = size;
}
- T getValue(uint16 index) {
+ T getValue(uint16 index) const {
if (index >= _size)
error("SciArray::getValue(): %d is out of bounds (%d)", index, _size);
@@ -796,8 +808,8 @@ public:
_data[index] = value;
}
- byte getType() { return _type; }
- uint32 getSize() { return _size; }
+ byte getType() const { return _type; }
+ uint32 getSize() const { return _size; }
T *getRawData() { return _data; }
protected:
@@ -814,15 +826,15 @@ public:
// We overload destroy to ensure the string type is 3 after destroying
void destroy() { SciArray<char>::destroy(); _type = 3; }
- Common::String toString();
- void fromString(Common::String string);
+ Common::String toString() const;
+ void fromString(const Common::String &string);
};
struct ArrayTable : public Table<SciArray<reg_t> > {
ArrayTable() : Table<SciArray<reg_t> >(SEG_TYPE_ARRAY) {}
virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr);
- virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note);
+ virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const;
void saveLoadWithSerializer(Common::Serializer &ser);
SegmentRef dereference(reg_t pointer);
diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp
index aba134818b..04a1b8fbba 100644
--- a/engines/sci/engine/selector.cpp
+++ b/engines/sci/engine/selector.cpp
@@ -153,7 +153,6 @@ void Kernel::mapSelectors() {
FIND_SELECTOR(subtitleLang);
FIND_SELECTOR(parseLang);
FIND_SELECTOR(overlay);
- FIND_SELECTOR(setCursor);
FIND_SELECTOR(topString);
FIND_SELECTOR(scaleSignal);
FIND_SELECTOR(scaleX);
@@ -176,32 +175,32 @@ void Kernel::mapSelectors() {
#endif
}
-reg_t read_selector(SegManager *segMan, reg_t object, Selector selector_id) {
+reg_t readSelector(SegManager *segMan, reg_t object, Selector selectorId) {
ObjVarRef address;
- if (lookup_selector(segMan, object, selector_id, &address, NULL) != kSelectorVariable)
+ if (lookupSelector(segMan, object, selectorId, &address, NULL) != kSelectorVariable)
return NULL_REG;
else
return *address.getPointer(segMan);
}
-void write_selector(SegManager *segMan, reg_t object, Selector selector_id, reg_t value) {
+void writeSelector(SegManager *segMan, reg_t object, Selector selectorId, reg_t value) {
ObjVarRef address;
- if ((selector_id < 0) || (selector_id > (int)g_sci->getKernel()->getSelectorNamesSize())) {
+ if ((selectorId < 0) || (selectorId > (int)g_sci->getKernel()->getSelectorNamesSize())) {
warning("Attempt to write to invalid selector %d of"
- " object at %04x:%04x.", selector_id, PRINT_REG(object));
+ " object at %04x:%04x.", selectorId, PRINT_REG(object));
return;
}
- if (lookup_selector(segMan, object, selector_id, &address, NULL) != kSelectorVariable)
+ if (lookupSelector(segMan, object, selectorId, &address, NULL) != kSelectorVariable)
warning("Selector '%s' of object at %04x:%04x could not be"
- " written to", g_sci->getKernel()->getSelectorName(selector_id).c_str(), PRINT_REG(object));
+ " written to", g_sci->getKernel()->getSelectorName(selectorId).c_str(), PRINT_REG(object));
else
*address.getPointer(segMan) = value;
}
-int invoke_selector_argv(EngineState *s, reg_t object, int selector_id, SelectorInvocation noinvalid,
+int invokeSelectorArgv(EngineState *s, reg_t object, int selectorId, SelectorInvocation noinvalid,
int k_argc, StackPtr k_argp, int argc, const reg_t *argv) {
int i;
int framesize = 2 + 1 * argc;
@@ -209,21 +208,21 @@ int invoke_selector_argv(EngineState *s, reg_t object, int selector_id, Selector
int slc_type;
StackPtr stackframe = k_argp + k_argc;
- stackframe[0] = make_reg(0, selector_id); // The selector we want to call
+ stackframe[0] = make_reg(0, selectorId); // The selector we want to call
stackframe[1] = make_reg(0, argc); // Argument count
- slc_type = lookup_selector(s->_segMan, object, selector_id, NULL, &address);
+ slc_type = lookupSelector(s->_segMan, object, selectorId, NULL, &address);
if (slc_type == kSelectorNone) {
warning("Selector '%s' of object at %04x:%04x could not be invoked",
- g_sci->getKernel()->getSelectorName(selector_id).c_str(), PRINT_REG(object));
+ g_sci->getKernel()->getSelectorName(selectorId).c_str(), PRINT_REG(object));
if (noinvalid == kStopOnInvalidSelector)
error("[Kernel] Not recoverable: VM was halted");
return 1;
}
if (slc_type == kSelectorVariable) {
warning("Attempting to invoke variable selector %s of object %04x:%04x",
- g_sci->getKernel()->getSelectorName(selector_id).c_str(), PRINT_REG(object));
+ g_sci->getKernel()->getSelectorName(selectorId).c_str(), PRINT_REG(object));
return 0;
}
@@ -243,7 +242,7 @@ int invoke_selector_argv(EngineState *s, reg_t object, int selector_id, Selector
return 0;
}
-int invoke_selector(EngineState *s, reg_t object, int selector_id, SelectorInvocation noinvalid,
+int invokeSelector(EngineState *s, reg_t object, int selectorId, SelectorInvocation noinvalid,
int k_argc, StackPtr k_argp, int argc, ...) {
va_list argp;
reg_t *args = new reg_t[argc];
@@ -253,28 +252,28 @@ int invoke_selector(EngineState *s, reg_t object, int selector_id, SelectorInvoc
args[i] = va_arg(argp, reg_t);
va_end(argp);
- int retval = invoke_selector_argv(s, object, selector_id, noinvalid, k_argc, k_argp, argc, args);
+ int retval = invokeSelectorArgv(s, object, selectorId, noinvalid, k_argc, k_argp, argc, args);
delete[] args;
return retval;
}
-SelectorType lookup_selector(SegManager *segMan, reg_t obj_location, Selector selector_id, ObjVarRef *varp, reg_t *fptr) {
- Object *obj = segMan->getObject(obj_location);
+SelectorType lookupSelector(SegManager *segMan, reg_t obj_location, Selector selectorId, ObjVarRef *varp, reg_t *fptr) {
+ const Object *obj = segMan->getObject(obj_location);
int index;
bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
// Early SCI versions used the LSB in the selector ID as a read/write
// toggle, meaning that we must remove it for selector lookup.
if (oldScriptHeader)
- selector_id &= ~1;
+ selectorId &= ~1;
if (!obj) {
- error("lookup_selector(): Attempt to send to non-object or invalid script. Address was %04x:%04x",
+ error("lookupSelector(): Attempt to send to non-object or invalid script. Address was %04x:%04x",
PRINT_REG(obj_location));
}
- index = obj->locateVarSelector(segMan, selector_id);
+ index = obj->locateVarSelector(segMan, selectorId);
if (index >= 0) {
// Found it as a variable
@@ -286,7 +285,7 @@ SelectorType lookup_selector(SegManager *segMan, reg_t obj_location, Selector se
} else {
// Check if it's a method, with recursive lookup in superclasses
while (obj) {
- index = obj->funcSelectorPosition(selector_id);
+ index = obj->funcSelectorPosition(selectorId);
if (index >= 0) {
if (fptr)
*fptr = obj->getFunction(index);
@@ -301,7 +300,7 @@ SelectorType lookup_selector(SegManager *segMan, reg_t obj_location, Selector se
}
-// return _lookup_selector_function(segMan, obj, selector_id, fptr);
+// return _lookupSelector_function(segMan, obj, selectorId, fptr);
}
} // End of namespace Sci
diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h
index 70eeb34d93..f50b9ab1b3 100644
--- a/engines/sci/engine/selector.h
+++ b/engines/sci/engine/selector.h
@@ -34,20 +34,15 @@
namespace Sci {
-
-/******************** Selector functionality ********************/
-
enum SelectorInvocation {
kStopOnInvalidSelector = 0,
kContinueOnInvalidSelector = 1
};
-
/**
* Map a selector name to a selector id. Shortcut for accessing the selector cache.
*/
#define SELECTOR(_slc_) (g_sci->getKernel()->_selectorCache._slc_)
-//#define SELECTOR(_slc_) _slc_
/**
* Retrieves a selector from an object.
@@ -58,8 +53,8 @@ enum SelectorInvocation {
* This macro halts on error. 'selector' must be a selector name registered in vm.h's
* SelectorCache and mapped in script.cpp.
*/
-#define GET_SEL32(segMan, _obj_, _slc_) read_selector(segMan, _obj_, _slc_)
-#define GET_SEL32V(segMan, _obj_, _slc_) (GET_SEL32(segMan, _obj_, _slc_).offset)
+reg_t readSelector(SegManager *segMan, reg_t object, Selector selectorId);
+#define readSelectorValue(segMan, _obj_, _slc_) (readSelector(segMan, _obj_, _slc_).offset)
/**
* Writes a selector value to an object.
@@ -70,27 +65,25 @@ enum SelectorInvocation {
* This macro halts on error. 'selector' must be a selector name registered in vm.h's
* SelectorCache and mapped in script.cpp.
*/
-#define PUT_SEL32(segMan, _obj_, _slc_, _val_) write_selector(segMan, _obj_, _slc_, _val_)
-#define PUT_SEL32V(segMan, _obj_, _slc_, _val_) PUT_SEL32(segMan, _obj_, _slc_, make_reg(0, _val_))
+void writeSelector(SegManager *segMan, reg_t object, Selector selectorId, reg_t value);
+#define writeSelectorValue(segMan, _obj_, _slc_, _val_) writeSelector(segMan, _obj_, _slc_, make_reg(0, _val_))
+/**
+ * Invokes a selector from an object.
+ */
+int invokeSelector(EngineState *s, reg_t object, int selectorId, SelectorInvocation noinvalid,
+ int k_argc, StackPtr k_argp, int argc, ...);
+int invokeSelectorArgv(EngineState *s, reg_t object, int selectorId, SelectorInvocation noinvalid,
+ int k_argc, StackPtr k_argp, int argc, const reg_t *argv);
/**
- * Kludge for use with invoke_selector(). Used for compatibility with compilers
+ * Kludge for use with invokeSelector(). Used for compatibility with compilers
* that cannot handle vararg macros.
*/
#define INV_SEL(s, _object_, _selector_, _noinvalid_) \
s, _object_, g_sci->getKernel()->_selectorCache._selector_, _noinvalid_, argc, argv
-reg_t read_selector(SegManager *segMan, reg_t object, Selector selector_id);
-void write_selector(SegManager *segMan, reg_t object, Selector selector_id, reg_t value);
-int invoke_selector(EngineState *s, reg_t object, int selector_id, SelectorInvocation noinvalid,
- int k_argc, StackPtr k_argp, int argc, ...);
-int invoke_selector_argv(EngineState *s, reg_t object, int selector_id, SelectorInvocation noinvalid,
- int k_argc, StackPtr k_argp, int argc, const reg_t *argv);
-
-
-
} // End of namespace Sci
#endif // SCI_ENGINE_KERNEL_H
diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp
index dfc4c39464..b4a04f8826 100644
--- a/engines/sci/engine/state.cpp
+++ b/engines/sci/engine/state.cpp
@@ -69,51 +69,56 @@ static const uint16 s_halfWidthSJISMap[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
-EngineState::EngineState(Vocabulary *voc, SegManager *segMan)
-: _voc(voc), _segMan(segMan), _dirseeker() {
+EngineState::EngineState(SegManager *segMan)
+: _segMan(segMan), _dirseeker() {
+ reset(false);
+}
+
+EngineState::~EngineState() {
+ delete _msgState;
+}
+
+void EngineState::reset(bool isRestoring) {
#ifdef USE_OLD_MUSIC_FUNCTIONS
sfx_init_flags = 0;
#endif
- restarting_flags = 0;
-
- last_wait_time = 0;
+ if (!isRestoring) {
+ script_000 = 0;
+ _gameObj = NULL_REG;
- _fileHandles.resize(5);
+ _memorySegmentSize = 0;
+ _soundCmd = 0;
- execution_stack_base = 0;
- _executionStackPosChanged = false;
+ restarting_flags = 0;
- r_acc = NULL_REG;
- restAdjust = 0;
- r_prev = NULL_REG;
+ execution_stack_base = 0;
+ _executionStackPosChanged = false;
- stack_base = 0;
- stack_top = 0;
+ _fileHandles.resize(5);
- script_000 = 0;
+ r_acc = NULL_REG;
+ restAdjust = 0;
+ r_prev = NULL_REG;
- sys_strings_segment = 0;
- sys_strings = 0;
+ stack_base = 0;
+ stack_top = 0;
+ }
- _gameObj = NULL_REG;
+ last_wait_time = 0;
gc_countdown = 0;
- successor = 0;
-
_throttleCounter = 0;
_throttleLastTime = 0;
_throttleTrigger = false;
- _memorySegmentSize = 0;
+ script_abort_flag = 0;
+ script_step_counter = 0;
+ script_gc_interval = GC_INTERVAL;
- _soundCmd = 0;
-}
-
-EngineState::~EngineState() {
- delete _msgState;
+ restoring = false;
}
void EngineState::wait(int16 ticks) {
@@ -227,7 +232,7 @@ kLanguage SciEngine::getSciLanguage() {
lang = K_LANG_ENGLISH;
if (_kernel->_selectorCache.printLang != -1) {
- lang = (kLanguage)GET_SEL32V(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(printLang));
+ lang = (kLanguage)readSelectorValue(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(printLang));
if ((getSciVersion() >= SCI_VERSION_1_1) || (lang == K_LANG_NONE)) {
// If language is set to none, we use the language from the game detector.
@@ -262,7 +267,7 @@ kLanguage SciEngine::getSciLanguage() {
}
// Store language in printLang selector
- PUT_SEL32V(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(printLang), lang);
+ writeSelectorValue(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(printLang), lang);
}
}
@@ -274,7 +279,7 @@ Common::String SciEngine::strSplit(const char *str, const char *sep) {
kLanguage subLang = K_LANG_NONE;
if (_kernel->_selectorCache.subtitleLang != -1) {
- subLang = (kLanguage)GET_SEL32V(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(subtitleLang));
+ subLang = (kLanguage)readSelectorValue(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(subtitleLang));
}
kLanguage secondLang;
diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h
index ad9de9e13e..bcdf66d6ef 100644
--- a/engines/sci/engine/state.h
+++ b/engines/sci/engine/state.h
@@ -95,14 +95,13 @@ public:
struct EngineState : public Common::Serializable {
public:
- EngineState(Vocabulary *voc, SegManager *segMan);
+ EngineState(SegManager *segMan);
virtual ~EngineState();
virtual void saveLoadWithSerializer(Common::Serializer &ser);
public:
SegManager *_segMan; /**< The segment manager */
- Vocabulary *_voc;
/* Non-VM information */
@@ -144,13 +143,32 @@ public:
reg_t r_acc; /**< Accumulator */
int16 restAdjust; /**< &rest register (only used for save games) */
+ int16 restAdjustCur; /**< current &rest register (only used for save games) */
reg_t r_prev; /**< previous comparison result */
StackPtr stack_base; /**< Pointer to the least stack element */
StackPtr stack_top; /**< First invalid stack element */
+ // Script state
+ ExecStack *xs;
+ reg_t *variables[4]; ///< global, local, temp, param, as immediate pointers
+ reg_t *variables_base[4]; ///< Used for referencing VM ops
+ SegmentId variables_seg[4]; ///< Same as above, contains segment IDs
+ int variables_max[4]; ///< Max. values for all variables
+
Script *script_000; /**< script 000, e.g. for globals */
+ int loadFromLauncher;
+
+ /**
+ * Set this to 1 to abort script execution immediately. Aborting will
+ * leave the debug exec stack intact.
+ * Set it to 2 to force a replay afterwards.
+ */
+ int script_abort_flag; // Set to 1 to abort execution. Set to 2 to force a replay afterwards
+ int script_step_counter; // Counts the number of steps executed
+ int script_gc_interval; // Number of steps in between gcs
+
uint16 currentRoomNumber() const;
void setRoomNumber(uint16 roomNumber);
@@ -160,10 +178,6 @@ public:
*/
void shrinkStackToBase();
- /* System strings */
- SegmentId sys_strings_segment;
- SystemStrings *sys_strings;
-
reg_t _gameObj; /**< Pointer to the game object */
int gc_countdown; /**< Number of kernel calls until next gc */
@@ -179,7 +193,12 @@ public:
uint _memorySegmentSize;
byte _memorySegment[kMemorySegmentMax];
- EngineState *successor; /**< Successor of this state: Used for restoring */
+ /**
+ * Resets the engine state.
+ */
+ void reset(bool isRestoring);
+
+ bool restoring; /**< A flag to indicate if a game is being restored */
};
} // End of namespace Sci
diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp
index 7b6763f8f2..1dcdf450ba 100644
--- a/engines/sci/engine/vm.cpp
+++ b/engines/sci/engine/vm.cpp
@@ -46,13 +46,6 @@ const reg_t SIGNAL_REG = {0, SIGNAL_OFFSET};
//#define VM_DEBUG_SEND
-ScriptState scriptState; // FIXME: Avoid non-const global vars
-int g_loadFromLauncher; // FIXME: Avoid non-const global vars
-
-int script_abort_flag = 0; // Set to 1 to abort execution. Set to 2 to force a replay afterwards // FIXME: Avoid non-const global vars
-int script_step_counter = 0; // Counts the number of steps executed // FIXME: Avoid non-const global vars
-int script_gc_interval = GC_INTERVAL; // Number of steps in between gcs // FIXME: Avoid non-const global vars
-
#define SCI_XS_CALLEE_LOCALS ((SegmentId)-1)
/**
@@ -120,7 +113,7 @@ static reg_t &validate_property(Object *obj, int index) {
return dummyReg;
}
- return obj->_variables[index];
+ return obj->getVariableRef(index);
}
static StackPtr validate_stack_addr(EngineState *s, StackPtr sp) {
@@ -212,7 +205,7 @@ static void validate_write_var(reg_t *r, reg_t *stack_base, int type, int max, i
if (!stopGroopPos.isNull()) { // does the game have a stopGroop object?
// Find the "client" member variable of the stopGroop object, and update it
ObjVarRef varp;
- if (lookup_selector(segMan, stopGroopPos, kernel->_selectorCache.client, &varp, NULL) == kSelectorVariable) {
+ if (lookupSelector(segMan, stopGroopPos, kernel->_selectorCache.client, &varp, NULL) == kSelectorVariable) {
reg_t *clientVar = varp.getPointer(segMan);
*clientVar = value;
}
@@ -236,8 +229,8 @@ static void validate_write_var(reg_t *r, reg_t *stack_base, int type, int max, i
#endif
-#define READ_VAR(type, index, def) validate_read_var(scriptState.variables[type], s->stack_base, type, scriptState.variables_max[type], index, __LINE__, def)
-#define WRITE_VAR(type, index, value) validate_write_var(scriptState.variables[type], s->stack_base, type, scriptState.variables_max[type], index, __LINE__, value, s->_segMan, g_sci->getKernel())
+#define READ_VAR(type, index, def) validate_read_var(s->variables[type], s->stack_base, type, s->variables_max[type], index, __LINE__, def)
+#define WRITE_VAR(type, index, value) validate_write_var(s->variables[type], s->stack_base, type, s->variables_max[type], index, __LINE__, value, s->_segMan, g_sci->getKernel())
#define WRITE_VAR16(type, index, value) WRITE_VAR(type, index, make_reg(0, value));
#define ACC_ARITHMETIC_L(op) make_reg(0, (op validate_arithmetic(s->r_acc)))
@@ -251,8 +244,8 @@ static void validate_write_var(reg_t *r, reg_t *stack_base, int type, int max, i
#define PUSH(v) PUSH32(make_reg(0, v))
#define POP() (validate_arithmetic(POP32()))
// 32 bit:
-#define PUSH32(a) (*(validate_stack_addr(s, (scriptState.xs->sp)++)) = (a))
-#define POP32() (*(validate_stack_addr(s, --(scriptState.xs->sp))))
+#define PUSH32(a) (*(validate_stack_addr(s, (s->xs->sp)++)) = (a))
+#define POP32() (*(validate_stack_addr(s, --(s->xs->sp))))
ExecStack *execute_method(EngineState *s, uint16 script, uint16 pubfunct, StackPtr sp, reg_t calling_obj, uint16 argc, StackPtr argp) {
int seg = s->_segMan->getScriptSegment(script);
@@ -341,7 +334,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt
int selector;
int argc;
int origin = s->_executionStack.size()-1; // Origin: Used for debugging
- int print_send_action = 0;
+ bool printSendActions = false;
// We return a pointer to the new active ExecStack
// The selector calls we catch are stored below:
@@ -370,7 +363,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt
if (bp->type == BREAK_SELECTOR && !strncmp(bp->name.c_str(), method_name, cmplen)) {
Console *con = g_sci->getSciDebugger();
con->DebugPrintf("Break on %s (in [%04x:%04x])\n", method_name, PRINT_REG(send_obj));
- print_send_action = 1;
+ printSendActions = true;
g_debugState.debugging = true;
g_debugState.breakpointWasHit = true;
break;
@@ -383,7 +376,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt
#endif // VM_DEBUG_SEND
ObjVarRef varp;
- switch (lookup_selector(s->_segMan, send_obj, selector, &varp, &funcp)) {
+ switch (lookupSelector(s->_segMan, send_obj, selector, &varp, &funcp)) {
case kSelectorNone:
error("Send to invalid selector 0x%x of object at %04x:%04x", 0xffff & selector, PRINT_REG(send_obj));
break;
@@ -398,22 +391,34 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt
#endif // VM_DEBUG_SEND
// argc == 0: read selector
- // argc == 1: write selector
- // argc > 1: write selector?
- if (print_send_action && argc == 0) { // read selector
- printf("[read selector]\n");
- print_send_action = 0;
+ // argc != 0: write selector
+ if (printSendActions && !argc) { // read selector
+ debug("[read selector]\n");
+ printSendActions = false;
}
- if (print_send_action && argc > 0) {
+ if (printSendActions && argc) {
reg_t oldReg = *varp.getPointer(s->_segMan);
reg_t newReg = argp[1];
- printf("[write to selector: change %04x:%04x to %04x:%04x]\n", PRINT_REG(oldReg), PRINT_REG(newReg));
- print_send_action = 0;
+ debug("[write to selector: change %04x:%04x to %04x:%04x]\n", PRINT_REG(oldReg), PRINT_REG(newReg));
+ printSendActions = false;
}
- if (argc > 1)
- warning("send_selector(): more than 1 parameter (%d) while modifying a variable selector", argc);
+ if (argc > 1) {
+ // argc can indeed be bigger than 1 in some cases, and it seems correct
+ // (i.e. we should skip that many bytes later on)... question is, why
+ // does this occur? Could such calls be used to point to data after X
+ // bytes in the heap? What are the skipped bytes in this case?
+ // In SQ4CD, this occurs with the returnVal selector of object
+ // Sq4GlobalNarrator when the game starts, and right after the narrator
+ // is heard (e.g. after he talks when examining something)
+ reg_t oldReg = *varp.getPointer(s->_segMan);
+ reg_t newReg = argp[1];
+ warning("send_selector(): argc = %d while modifying variable selector "
+ "%x (%s) of object %04x:%04x (%s) from %04x:%04x to %04x:%04x",
+ argc, selector, g_sci->getKernel()->getSelectorName(selector).c_str(), PRINT_REG(send_obj),
+ s->_segMan->getObjectName(send_obj), PRINT_REG(oldReg), PRINT_REG(newReg));
+ }
{
CallsStruct call;
@@ -438,9 +443,9 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt
}
printf(") at %04x:%04x\n", PRINT_REG(funcp));
#endif // VM_DEBUG_SEND
- if (print_send_action) {
- printf("[invoke selector]\n");
- print_send_action = 0;
+ if (printSendActions) {
+ debug("[invoke selector]\n");
+ printSendActions = false;
}
{
@@ -456,7 +461,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt
}
break;
- } // switch (lookup_selector())
+ } // switch (lookupSelector())
framesize -= (2 + argc);
argp += argc + 1;
@@ -477,9 +482,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt
_exec_varselectors(s);
- if (s->_executionStack.empty())
- return NULL;
- return &(s->_executionStack.back());
+ return s->_executionStack.empty() ? NULL : &(s->_executionStack.back());
}
static ExecStack *add_exec_stack_varselector(Common::List<ExecStack> &execStack, reg_t objp, int argc, StackPtr argp, Selector selector, const ObjVarRef& address, int origin) {
@@ -565,11 +568,11 @@ static void callKernelFunc(EngineState *s, int kernelFuncNum, int argc) {
const KernelFuncWithSignature &kernelFunc = g_sci->getKernel()->_kernelFuncs[kernelFuncNum];
if (kernelFunc.signature
- && !g_sci->getKernel()->signatureMatch(kernelFunc.signature, argc, scriptState.xs->sp + 1)) {
+ && !g_sci->getKernel()->signatureMatch(kernelFunc.signature, argc, s->xs->sp + 1)) {
error("[VM] Invalid arguments to kernel call %x", kernelFuncNum);
}
- reg_t *argv = scriptState.xs->sp + 1;
+ reg_t *argv = s->xs->sp + 1;
if (!kernelFunc.isDummy) {
// Add stack frame to indicate we're executing a callk.
@@ -583,19 +586,26 @@ static void callKernelFunc(EngineState *s, int kernelFuncNum, int argc) {
//warning("callk %s", kernelFunc.orig_name.c_str());
- // TODO: SCI2/SCI2.1+ equivalent, once saving/loading works in SCI2/SCI2.1+
- if (g_loadFromLauncher >= 0 && kernelFuncNum == 0x8) {
- // A game is being loaded from the launcher, and kDisplay is called, all initialization has taken
- // place (i.e. menus have been constructed etc). Therefore, inject a kRestoreGame call
- // here, instead of the requested function.
- int saveSlot = g_loadFromLauncher;
- g_loadFromLauncher = -1; // invalidate slot, so that we don't load again
-
- if (saveSlot < 0)
- error("Requested to load invalid save slot"); // should never happen, really
-
- reg_t restoreArgv[2] = { NULL_REG, make_reg(0, saveSlot) }; // special call (argv[0] is NULL)
- kRestoreGame(s, 2, restoreArgv);
+ // TODO: SCI2.1 equivalent
+ if (s->loadFromLauncher >= 0 && (
+ (kernelFuncNum == 0x8 && getSciVersion() <= SCI_VERSION_1_1) || // DrawPic
+ (kernelFuncNum == 0x3d && getSciVersion() == SCI_VERSION_2) // GetSaveDir
+ //(kernelFuncNum == 0x28 && getSciVersion() == SCI_VERSION_2_1) // AddPlane
+ )) {
+
+ // A game is being loaded from the launcher, and the game is about to draw something on
+ // screen, hence all initialization has taken place (i.e. menus have been constructed etc).
+ // Therefore, inject a kRestoreGame call here, instead of the requested function.
+ // The restore call is injected here mainly for games which have a menu, as the menu is
+ // constructed when the game starts and is not reconstructed when a saved game is loaded.
+ int saveSlot = s->loadFromLauncher;
+ s->loadFromLauncher = -1; // invalidate slot, so that we don't load again
+
+ if (saveSlot < 0)
+ error("Requested to load invalid save slot"); // should never happen, really
+
+ reg_t restoreArgv[2] = { NULL_REG, make_reg(0, saveSlot) }; // special call (argv[0] is NULL)
+ kRestoreGame(s, 2, restoreArgv);
} else {
// Call kernel function
s->r_acc = kernelFunc.fun(s, argc, argv);
@@ -620,7 +630,7 @@ static void callKernelFunc(EngineState *s, int kernelFuncNum, int argc) {
static void gc_countdown(EngineState *s) {
if (s->gc_countdown-- <= 0) {
- s->gc_countdown = script_gc_interval;
+ s->gc_countdown = s->script_gc_interval;
run_gc(s);
}
}
@@ -708,13 +718,13 @@ void run_vm(EngineState *s, bool restoring) {
StackPtr s_temp; // Temporary stack pointer
int16 opparams[4]; // opcode parameters
- scriptState.restAdjust = s->restAdjust;
+ s->restAdjustCur = s->restAdjust;
// &rest adjusts the parameter count by this value
// Current execution data:
- scriptState.xs = &(s->_executionStack.back());
+ s->xs = &(s->_executionStack.back());
ExecStack *xs_new = NULL;
- Object *obj = s->_segMan->getObject(scriptState.xs->objp);
- Script *local_script = s->_segMan->getScriptIfLoaded(scriptState.xs->local_segment);
+ Object *obj = s->_segMan->getObject(s->xs->objp);
+ Script *local_script = s->_segMan->getScriptIfLoaded(s->xs->local_segment);
int old_execution_stack_base = s->execution_stack_base;
// Used to detect the stack bottom, for "physical" returns
const byte *code_buf = NULL; // (Avoid spurious warning)
@@ -729,20 +739,20 @@ void run_vm(EngineState *s, bool restoring) {
#ifndef DISABLE_VALIDATIONS
// Initialize maximum variable count
if (s->script_000->_localsBlock)
- scriptState.variables_max[VAR_GLOBAL] = s->script_000->_localsBlock->_locals.size();
+ s->variables_max[VAR_GLOBAL] = s->script_000->_localsBlock->_locals.size();
else
- scriptState.variables_max[VAR_GLOBAL] = 0;
+ s->variables_max[VAR_GLOBAL] = 0;
#endif
- scriptState.variables_seg[VAR_GLOBAL] = s->script_000->_localsSegment;
- scriptState.variables_seg[VAR_TEMP] = scriptState.variables_seg[VAR_PARAM] = s->_segMan->findSegmentByType(SEG_TYPE_STACK);
- scriptState.variables_base[VAR_TEMP] = scriptState.variables_base[VAR_PARAM] = s->stack_base;
+ s->variables_seg[VAR_GLOBAL] = s->script_000->_localsSegment;
+ s->variables_seg[VAR_TEMP] = s->variables_seg[VAR_PARAM] = s->_segMan->findSegmentByType(SEG_TYPE_STACK);
+ s->variables_base[VAR_TEMP] = s->variables_base[VAR_PARAM] = s->stack_base;
// SCI code reads the zeroth argument to determine argc
if (s->script_000->_localsBlock)
- scriptState.variables_base[VAR_GLOBAL] = scriptState.variables[VAR_GLOBAL] = s->script_000->_localsBlock->_locals.begin();
+ s->variables_base[VAR_GLOBAL] = s->variables[VAR_GLOBAL] = s->script_000->_localsBlock->_locals.begin();
else
- scriptState.variables_base[VAR_GLOBAL] = scriptState.variables[VAR_GLOBAL] = NULL;
+ s->variables_base[VAR_GLOBAL] = s->variables[VAR_GLOBAL] = NULL;
s->_executionStackPosChanged = true; // Force initialization
@@ -750,63 +760,63 @@ void run_vm(EngineState *s, bool restoring) {
int var_type; // See description below
int var_number;
- g_debugState.old_pc_offset = scriptState.xs->addr.pc.offset;
- g_debugState.old_sp = scriptState.xs->sp;
+ g_debugState.old_pc_offset = s->xs->addr.pc.offset;
+ g_debugState.old_sp = s->xs->sp;
if (s->_executionStackPosChanged) {
Script *scr;
- scriptState.xs = &(s->_executionStack.back());
+ s->xs = &(s->_executionStack.back());
s->_executionStackPosChanged = false;
- scr = s->_segMan->getScriptIfLoaded(scriptState.xs->addr.pc.segment);
+ scr = s->_segMan->getScriptIfLoaded(s->xs->addr.pc.segment);
if (!scr) {
// No script? Implicit return via fake instruction buffer
- warning("Running on non-existant script in segment %x", scriptState.xs->addr.pc.segment);
+ warning("Running on non-existant script in segment %x", s->xs->addr.pc.segment);
code_buf = _fake_return_buffer;
#ifndef DISABLE_VALIDATIONS
code_buf_size = 2;
#endif
- scriptState.xs->addr.pc.offset = 1;
+ s->xs->addr.pc.offset = 1;
scr = NULL;
obj = NULL;
} else {
- obj = s->_segMan->getObject(scriptState.xs->objp);
+ obj = s->_segMan->getObject(s->xs->objp);
code_buf = scr->_buf;
#ifndef DISABLE_VALIDATIONS
- code_buf_size = scr->_bufSize;
+ code_buf_size = scr->getBufSize();
#endif
- local_script = s->_segMan->getScriptIfLoaded(scriptState.xs->local_segment);
+ local_script = s->_segMan->getScriptIfLoaded(s->xs->local_segment);
if (!local_script) {
- warning("Could not find local script from segment %x", scriptState.xs->local_segment);
+ warning("Could not find local script from segment %x", s->xs->local_segment);
local_script = NULL;
- scriptState.variables_base[VAR_LOCAL] = scriptState.variables[VAR_LOCAL] = NULL;
+ s->variables_base[VAR_LOCAL] = s->variables[VAR_LOCAL] = NULL;
#ifndef DISABLE_VALIDATIONS
- scriptState.variables_max[VAR_LOCAL] = 0;
+ s->variables_max[VAR_LOCAL] = 0;
#endif
} else {
- scriptState.variables_seg[VAR_LOCAL] = local_script->_localsSegment;
+ s->variables_seg[VAR_LOCAL] = local_script->_localsSegment;
if (local_script->_localsBlock)
- scriptState.variables_base[VAR_LOCAL] = scriptState.variables[VAR_LOCAL] = local_script->_localsBlock->_locals.begin();
+ s->variables_base[VAR_LOCAL] = s->variables[VAR_LOCAL] = local_script->_localsBlock->_locals.begin();
else
- scriptState.variables_base[VAR_LOCAL] = scriptState.variables[VAR_LOCAL] = NULL;
+ s->variables_base[VAR_LOCAL] = s->variables[VAR_LOCAL] = NULL;
#ifndef DISABLE_VALIDATIONS
if (local_script->_localsBlock)
- scriptState.variables_max[VAR_LOCAL] = local_script->_localsBlock->_locals.size();
+ s->variables_max[VAR_LOCAL] = local_script->_localsBlock->_locals.size();
else
- scriptState.variables_max[VAR_LOCAL] = 0;
- scriptState.variables_max[VAR_TEMP] = scriptState.xs->sp - scriptState.xs->fp;
- scriptState.variables_max[VAR_PARAM] = scriptState.xs->argc + 1;
+ s->variables_max[VAR_LOCAL] = 0;
+ s->variables_max[VAR_TEMP] = s->xs->sp - s->xs->fp;
+ s->variables_max[VAR_PARAM] = s->xs->argc + 1;
#endif
}
- scriptState.variables[VAR_TEMP] = scriptState.xs->fp;
- scriptState.variables[VAR_PARAM] = scriptState.xs->variables_argp;
+ s->variables[VAR_TEMP] = s->xs->fp;
+ s->variables[VAR_PARAM] = s->xs->variables_argp;
}
}
- if (script_abort_flag || g_engine->shouldQuit())
+ if (s->script_abort_flag || g_engine->shouldQuit())
return; // Emergency
// Debug if this has been requested:
@@ -821,18 +831,20 @@ void run_vm(EngineState *s, bool restoring) {
}
#ifndef DISABLE_VALIDATIONS
- if (scriptState.xs->sp < scriptState.xs->fp)
- error("run_vm(): stack underflow");
+ if (s->xs->sp < s->xs->fp)
+ error("run_vm(): stack underflow, sp: %04x:%04x, fp: %04x:%04x",
+ PRINT_REG(*s->xs->sp), PRINT_REG(*s->xs->fp));
- scriptState.variables_max[VAR_TEMP] = scriptState.xs->sp - scriptState.xs->fp;
+ s->variables_max[VAR_TEMP] = s->xs->sp - s->xs->fp;
- if (scriptState.xs->addr.pc.offset >= code_buf_size)
- error("run_vm(): program counter gone astray");
+ if (s->xs->addr.pc.offset >= code_buf_size)
+ error("run_vm(): program counter gone astray, addr: %d, code buffer size: %d",
+ s->xs->addr.pc.offset, code_buf_size);
#endif
// Get opcode
byte extOpcode;
- scriptState.xs->addr.pc.offset += readPMachineInstruction(code_buf + scriptState.xs->addr.pc.offset, extOpcode, opparams);
+ s->xs->addr.pc.offset += readPMachineInstruction(code_buf + s->xs->addr.pc.offset, extOpcode, opparams);
const byte opcode = extOpcode >> 1;
switch (opcode) {
@@ -1063,16 +1075,16 @@ void run_vm(EngineState *s, bool restoring) {
case op_bt: // 0x17 (23)
if (s->r_acc.offset || s->r_acc.segment)
- scriptState.xs->addr.pc.offset += opparams[0];
+ s->xs->addr.pc.offset += opparams[0];
break;
case op_bnt: // 0x18 (24)
if (!(s->r_acc.offset || s->r_acc.segment))
- scriptState.xs->addr.pc.offset += opparams[0];
+ s->xs->addr.pc.offset += opparams[0];
break;
case op_jmp: // 0x19 (25)
- scriptState.xs->addr.pc.offset += opparams[0];
+ s->xs->addr.pc.offset += opparams[0];
break;
case op_ldi: // 0x1a (26)
@@ -1088,34 +1100,34 @@ void run_vm(EngineState *s, bool restoring) {
break;
case op_toss: // 0x1d (29)
- scriptState.xs->sp--;
+ s->xs->sp--;
break;
case op_dup: // 0x1e (30)
- r_temp = scriptState.xs->sp[-1];
+ r_temp = s->xs->sp[-1];
PUSH32(r_temp);
break;
case op_link: // 0x1f (31)
for (int i = 0; i < opparams[0]; i++)
- scriptState.xs->sp[i] = NULL_REG;
- scriptState.xs->sp += opparams[0];
+ s->xs->sp[i] = NULL_REG;
+ s->xs->sp += opparams[0];
break;
case op_call: { // 0x20 (32)
int argc = (opparams[1] >> 1) // Given as offset, but we need count
- + 1 + scriptState.restAdjust;
- StackPtr call_base = scriptState.xs->sp - argc;
- scriptState.xs->sp[1].offset += scriptState.restAdjust;
-
- xs_new = add_exec_stack_entry(s->_executionStack, make_reg(scriptState.xs->addr.pc.segment,
- scriptState.xs->addr.pc.offset + opparams[0]),
- scriptState.xs->sp, scriptState.xs->objp,
- (validate_arithmetic(*call_base)) + scriptState.restAdjust,
- call_base, NULL_SELECTOR, scriptState.xs->objp,
- s->_executionStack.size()-1, scriptState.xs->local_segment);
- scriptState.restAdjust = 0; // Used up the &rest adjustment
- scriptState.xs->sp = call_base;
+ + 1 + s->restAdjustCur;
+ StackPtr call_base = s->xs->sp - argc;
+ s->xs->sp[1].offset += s->restAdjustCur;
+
+ xs_new = add_exec_stack_entry(s->_executionStack, make_reg(s->xs->addr.pc.segment,
+ s->xs->addr.pc.offset + opparams[0]),
+ s->xs->sp, s->xs->objp,
+ (validate_arithmetic(*call_base)) + s->restAdjustCur,
+ call_base, NULL_SELECTOR, s->xs->objp,
+ s->_executionStack.size()-1, s->xs->local_segment);
+ s->restAdjustCur = 0; // Used up the &rest adjustment
+ s->xs->sp = call_base;
s->_executionStackPosChanged = true;
break;
@@ -1124,23 +1136,23 @@ void run_vm(EngineState *s, bool restoring) {
case op_callk: { // 0x21 (33)
gc_countdown(s);
- scriptState.xs->sp -= (opparams[1] >> 1) + 1;
+ s->xs->sp -= (opparams[1] >> 1) + 1;
bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
if (!oldScriptHeader) {
- scriptState.xs->sp -= scriptState.restAdjust;
- s->restAdjust = 0; // We just used up the scriptState.restAdjust, remember?
+ s->xs->sp -= s->restAdjustCur;
+ s->restAdjust = 0; // We just used up the s->restAdjustCur, remember?
}
- int argc = validate_arithmetic(scriptState.xs->sp[0]);
+ int argc = validate_arithmetic(s->xs->sp[0]);
if (!oldScriptHeader)
- argc += scriptState.restAdjust;
+ argc += s->restAdjustCur;
callKernelFunc(s, opparams[0], argc);
if (!oldScriptHeader)
- scriptState.restAdjust = s->restAdjust;
+ s->restAdjustCur = s->restAdjust;
// Calculate xs again: The kernel function might
// have spawned a new VM
@@ -1151,27 +1163,27 @@ void run_vm(EngineState *s, bool restoring) {
}
case op_callb: // 0x22 (34)
- temp = ((opparams[1] >> 1) + scriptState.restAdjust + 1);
- s_temp = scriptState.xs->sp;
- scriptState.xs->sp -= temp;
-
- scriptState.xs->sp[0].offset += scriptState.restAdjust;
- xs_new = execute_method(s, 0, opparams[0], s_temp, scriptState.xs->objp,
- scriptState.xs->sp[0].offset, scriptState.xs->sp);
- scriptState.restAdjust = 0; // Used up the &rest adjustment
+ temp = ((opparams[1] >> 1) + s->restAdjustCur + 1);
+ s_temp = s->xs->sp;
+ s->xs->sp -= temp;
+
+ s->xs->sp[0].offset += s->restAdjustCur;
+ xs_new = execute_method(s, 0, opparams[0], s_temp, s->xs->objp,
+ s->xs->sp[0].offset, s->xs->sp);
+ s->restAdjustCur = 0; // Used up the &rest adjustment
if (xs_new) // in case of error, keep old stack
s->_executionStackPosChanged = true;
break;
case op_calle: // 0x23 (35)
- temp = ((opparams[2] >> 1) + scriptState.restAdjust + 1);
- s_temp = scriptState.xs->sp;
- scriptState.xs->sp -= temp;
+ temp = ((opparams[2] >> 1) + s->restAdjustCur + 1);
+ s_temp = s->xs->sp;
+ s->xs->sp -= temp;
- scriptState.xs->sp[0].offset += scriptState.restAdjust;
- xs_new = execute_method(s, opparams[0], opparams[1], s_temp, scriptState.xs->objp,
- scriptState.xs->sp[0].offset, scriptState.xs->sp);
- scriptState.restAdjust = 0; // Used up the &rest adjustment
+ s->xs->sp[0].offset += s->restAdjustCur;
+ xs_new = execute_method(s, opparams[0], opparams[1], s_temp, s->xs->objp,
+ s->xs->sp[0].offset, s->xs->sp);
+ s->restAdjustCur = 0; // Used up the &rest adjustment
if (xs_new) // in case of error, keep old stack
s->_executionStackPosChanged = true;
@@ -1179,8 +1191,8 @@ void run_vm(EngineState *s, bool restoring) {
case op_ret: // 0x24 (36)
do {
- StackPtr old_sp2 = scriptState.xs->sp;
- StackPtr old_fp = scriptState.xs->fp;
+ StackPtr old_sp2 = s->xs->sp;
+ StackPtr old_fp = s->xs->fp;
ExecStack *old_xs = &(s->_executionStack.back());
if ((int)s->_executionStack.size() - 1 == s->execution_stack_base) { // Have we reached the base?
@@ -1189,7 +1201,7 @@ void run_vm(EngineState *s, bool restoring) {
s->_executionStack.pop_back();
s->_executionStackPosChanged = true;
- s->restAdjust = scriptState.restAdjust; // Update &rest
+ s->restAdjust = s->restAdjustCur; // Update &rest
return; // "Hard" return
}
@@ -1205,33 +1217,33 @@ void run_vm(EngineState *s, bool restoring) {
// Not reached the base, so let's do a soft return
s->_executionStack.pop_back();
s->_executionStackPosChanged = true;
- scriptState.xs = &(s->_executionStack.back());
+ s->xs = &(s->_executionStack.back());
- if (scriptState.xs->sp == CALL_SP_CARRY // Used in sends to 'carry' the stack pointer
- || scriptState.xs->type != EXEC_STACK_TYPE_CALL) {
- scriptState.xs->sp = old_sp2;
- scriptState.xs->fp = old_fp;
+ if (s->xs->sp == CALL_SP_CARRY // Used in sends to 'carry' the stack pointer
+ || s->xs->type != EXEC_STACK_TYPE_CALL) {
+ s->xs->sp = old_sp2;
+ s->xs->fp = old_fp;
}
- } while (scriptState.xs->type == EXEC_STACK_TYPE_VARSELECTOR);
+ } while (s->xs->type == EXEC_STACK_TYPE_VARSELECTOR);
// Iterate over all varselector accesses
s->_executionStackPosChanged = true;
- xs_new = scriptState.xs;
+ xs_new = s->xs;
break;
case op_send: // 0x25 (37)
- s_temp = scriptState.xs->sp;
- scriptState.xs->sp -= ((opparams[0] >> 1) + scriptState.restAdjust); // Adjust stack
+ s_temp = s->xs->sp;
+ s->xs->sp -= ((opparams[0] >> 1) + s->restAdjustCur); // Adjust stack
- scriptState.xs->sp[1].offset += scriptState.restAdjust;
+ s->xs->sp[1].offset += s->restAdjustCur;
xs_new = send_selector(s, s->r_acc, s->r_acc, s_temp,
- (int)(opparams[0] >> 1) + (uint16)scriptState.restAdjust, scriptState.xs->sp);
+ (int)(opparams[0] >> 1) + (uint16)s->restAdjustCur, s->xs->sp);
- if (xs_new && xs_new != scriptState.xs)
+ if (xs_new && xs_new != s->xs)
s->_executionStackPosChanged = true;
- scriptState.restAdjust = 0;
+ s->restAdjustCur = 0;
break;
@@ -1242,7 +1254,7 @@ void run_vm(EngineState *s, bool restoring) {
case op_class: // 0x28 (40)
s->r_acc = s->_segMan->getClassAddress((unsigned)opparams[0], SCRIPT_GET_LOCK,
- scriptState.xs->addr.pc);
+ s->xs->addr.pc);
break;
case 0x29: // (41)
@@ -1250,48 +1262,48 @@ void run_vm(EngineState *s, bool restoring) {
break;
case op_self: // 0x2a (42)
- s_temp = scriptState.xs->sp;
- scriptState.xs->sp -= ((opparams[0] >> 1) + scriptState.restAdjust); // Adjust stack
+ s_temp = s->xs->sp;
+ s->xs->sp -= ((opparams[0] >> 1) + s->restAdjustCur); // Adjust stack
- scriptState.xs->sp[1].offset += scriptState.restAdjust;
- xs_new = send_selector(s, scriptState.xs->objp, scriptState.xs->objp,
- s_temp, (int)(opparams[0] >> 1) + (uint16)scriptState.restAdjust,
- scriptState.xs->sp);
+ s->xs->sp[1].offset += s->restAdjustCur;
+ xs_new = send_selector(s, s->xs->objp, s->xs->objp,
+ s_temp, (int)(opparams[0] >> 1) + (uint16)s->restAdjustCur,
+ s->xs->sp);
- if (xs_new && xs_new != scriptState.xs)
+ if (xs_new && xs_new != s->xs)
s->_executionStackPosChanged = true;
- scriptState.restAdjust = 0;
+ s->restAdjustCur = 0;
break;
case op_super: // 0x2b (43)
- r_temp = s->_segMan->getClassAddress(opparams[0], SCRIPT_GET_LOAD, scriptState.xs->addr.pc);
+ r_temp = s->_segMan->getClassAddress(opparams[0], SCRIPT_GET_LOAD, s->xs->addr.pc);
if (!r_temp.segment)
error("[VM]: Invalid superclass in object");
else {
- s_temp = scriptState.xs->sp;
- scriptState.xs->sp -= ((opparams[1] >> 1) + scriptState.restAdjust); // Adjust stack
+ s_temp = s->xs->sp;
+ s->xs->sp -= ((opparams[1] >> 1) + s->restAdjustCur); // Adjust stack
- scriptState.xs->sp[1].offset += scriptState.restAdjust;
- xs_new = send_selector(s, r_temp, scriptState.xs->objp, s_temp,
- (int)(opparams[1] >> 1) + (uint16)scriptState.restAdjust,
- scriptState.xs->sp);
+ s->xs->sp[1].offset += s->restAdjustCur;
+ xs_new = send_selector(s, r_temp, s->xs->objp, s_temp,
+ (int)(opparams[1] >> 1) + (uint16)s->restAdjustCur,
+ s->xs->sp);
- if (xs_new && xs_new != scriptState.xs)
+ if (xs_new && xs_new != s->xs)
s->_executionStackPosChanged = true;
- scriptState.restAdjust = 0;
+ s->restAdjustCur = 0;
}
break;
case op_rest: // 0x2c (44)
temp = (uint16) opparams[0]; // First argument
- scriptState.restAdjust = MAX<int16>(scriptState.xs->argc - temp + 1, 0); // +1 because temp counts the paramcount while argc doesn't
+ s->restAdjustCur = MAX<int16>(s->xs->argc - temp + 1, 0); // +1 because temp counts the paramcount while argc doesn't
- for (; temp <= scriptState.xs->argc; temp++)
- PUSH32(scriptState.xs->variables_argp[temp]);
+ for (; temp <= s->xs->argc; temp++)
+ PUSH32(s->xs->variables_argp[temp]);
break;
@@ -1300,8 +1312,8 @@ void run_vm(EngineState *s, bool restoring) {
var_number = temp & 0x03; // Get variable type
// Get variable block offset
- r_temp.segment = scriptState.variables_seg[var_number];
- r_temp.offset = scriptState.variables[var_number] - scriptState.variables_base[var_number];
+ r_temp.segment = s->variables_seg[var_number];
+ r_temp.offset = s->variables[var_number] - s->variables_base[var_number];
if (temp & 0x08) // Add accumulator offset if requested
r_temp.offset += signed_validate_arithmetic(s->r_acc);
@@ -1314,7 +1326,7 @@ void run_vm(EngineState *s, bool restoring) {
case op_selfID: // 0x2e (46)
- s->r_acc = scriptState.xs->objp;
+ s->r_acc = s->xs->objp;
break;
case 0x2f: // (47)
@@ -1382,17 +1394,17 @@ void run_vm(EngineState *s, bool restoring) {
break;
case op_lofsa: // 0x39 (57)
- s->r_acc.segment = scriptState.xs->addr.pc.segment;
+ s->r_acc.segment = s->xs->addr.pc.segment;
switch (g_sci->_features->detectLofsType()) {
case SCI_VERSION_1_1:
- s->r_acc.offset = opparams[0] + local_script->_scriptSize;
+ s->r_acc.offset = opparams[0] + local_script->getScriptSize();
break;
case SCI_VERSION_1_MIDDLE:
s->r_acc.offset = opparams[0];
break;
default:
- s->r_acc.offset = scriptState.xs->addr.pc.offset + opparams[0];
+ s->r_acc.offset = s->xs->addr.pc.offset + opparams[0];
}
#ifndef DISABLE_VALIDATIONS
@@ -1404,17 +1416,17 @@ void run_vm(EngineState *s, bool restoring) {
break;
case op_lofss: // 0x3a (58)
- r_temp.segment = scriptState.xs->addr.pc.segment;
+ r_temp.segment = s->xs->addr.pc.segment;
switch (g_sci->_features->detectLofsType()) {
case SCI_VERSION_1_1:
- r_temp.offset = opparams[0] + local_script->_scriptSize;
+ r_temp.offset = opparams[0] + local_script->getScriptSize();
break;
case SCI_VERSION_1_MIDDLE:
r_temp.offset = opparams[0];
break;
default:
- r_temp.offset = scriptState.xs->addr.pc.offset + opparams[0];
+ r_temp.offset = s->xs->addr.pc.offset + opparams[0];
}
#ifndef DISABLE_VALIDATIONS
@@ -1440,10 +1452,10 @@ void run_vm(EngineState *s, bool restoring) {
case op_pushSelf: // 0x3e (62)
if (!(extOpcode & 1)) {
- PUSH32(scriptState.xs->objp);
+ PUSH32(s->xs->objp);
} else {
// Debug opcode op_file, skip null-terminated string (file name)
- while (code_buf[scriptState.xs->addr.pc.offset++]) ;
+ while (code_buf[s->xs->addr.pc.offset++]) ;
}
break;
@@ -1657,16 +1669,16 @@ void run_vm(EngineState *s, bool restoring) {
} // switch (opcode)
if (s->_executionStackPosChanged) // Force initialization
- scriptState.xs = xs_new;
+ s->xs = xs_new;
//#ifndef DISABLE_VALIDATIONS
- if (scriptState.xs != &(s->_executionStack.back())) {
+ if (s->xs != &(s->_executionStack.back())) {
warning("xs is stale (%p vs %p); last command was %02x",
- (void *)scriptState.xs, (void *)&(s->_executionStack.back()),
+ (void *)s->xs, (void *)&(s->_executionStack.back()),
opcode);
}
//#endif
- ++script_step_counter;
+ ++s->script_step_counter;
}
}
@@ -1676,17 +1688,16 @@ static void _init_stack_base_with_selector(EngineState *s, Selector selector) {
}
static EngineState *_game_run(EngineState *&s) {
- EngineState *successor = NULL;
- int game_is_finished = 0;
+ bool restoring = false;
if (DebugMan.isDebugChannelEnabled(kDebugLevelOnStartup))
g_sci->getSciDebugger()->attach();
do {
s->_executionStackPosChanged = false;
- run_vm(s, successor ? true : false);
+ run_vm(s, restoring);
if (s->restarting_flags & SCI_GAME_IS_RESTARTING_NOW) { // Restart was requested?
- successor = NULL;
+ restoring = false;
s->_executionStack.clear();
s->_executionStackPosChanged = false;
@@ -1700,17 +1711,15 @@ static EngineState *_game_run(EngineState *&s) {
send_selector(s, s->_gameObj, s->_gameObj, s->stack_base, 2, s->stack_base);
- script_abort_flag = 0;
+ s->script_abort_flag = 0;
s->restarting_flags = SCI_GAME_WAS_RESTARTED;
} else {
- successor = s->successor;
- if (successor) {
+ restoring = s->restoring;
+ if (restoring) {
game_exit(s);
- delete s;
- s = successor;
-
- if (script_abort_flag == 2) {
+ s->restoring = false;
+ if (s->script_abort_flag == 2) {
debugC(2, kDebugLevelVM, "Restarting with replay()");
s->_executionStack.clear(); // Restart with replay
@@ -1719,12 +1728,12 @@ static EngineState *_game_run(EngineState *&s) {
send_selector(s, s->_gameObj, s->_gameObj, s->stack_base, 2, s->stack_base);
}
- script_abort_flag = 0;
+ s->script_abort_flag = 0;
} else
- game_is_finished = 1;
+ break; // exit loop
}
- } while (!game_is_finished);
+ } while (true);
return s;
}
@@ -1750,19 +1759,18 @@ int game_run(EngineState **_s) {
return 0;
}
-void quit_vm() {
- script_abort_flag = 1; // Terminate VM
+void quit_vm(EngineState *s) {
+ s->script_abort_flag = 1; // Terminate VM
g_debugState.seeking = kDebugSeekNothing;
g_debugState.runningStep = 0;
}
-reg_t* ObjVarRef::getPointer(SegManager *segMan) const {
+reg_t *ObjVarRef::getPointer(SegManager *segMan) const {
Object *o = segMan->getObject(obj);
- if (!o) return 0;
- return &(o->_variables[varindex]);
+ return o ? &o->getVariableRef(varindex) : 0;
}
-reg_t* ExecStack::getVarPointer(SegManager *segMan) const {
+reg_t *ExecStack::getVarPointer(SegManager *segMan) const {
assert(type == EXEC_STACK_TYPE_VARSELECTOR);
return addr.varp.getPointer(segMan);
}
diff --git a/engines/sci/engine/vm.h b/engines/sci/engine/vm.h
index f6151dfc9d..67a6bd0dc3 100644
--- a/engines/sci/engine/vm.h
+++ b/engines/sci/engine/vm.h
@@ -43,44 +43,9 @@ class ResourceManager;
/** Number of bytes to be allocated for the stack */
#define VM_STACK_SIZE 0x1000
-/** Maximum number of calls residing on the stack */
-#define SCRIPT_MAX_EXEC_STACK 256
-/** Maximum number of entries in the class table */
-#define SCRIPT_MAX_CLASSTABLE_SIZE 256
-/** Maximum number of cloned objects on the heap */
-#define SCRIPT_MAX_CLONES 256
-
-
-/** Object-relative offset of the selector area inside a script */
-#define SCRIPT_SELECTOR_OFFSET 8 -8
-
-/** Object-relative offset of the pointer to the underlying script's local variables */
-#define SCRIPT_LOCALVARPTR_OFFSET 2 -8
-
-/** Object-relative offset of the selector counter */
-#define SCRIPT_SELECTORCTR_OFFSET 6 -8
-
-/** Object-relative offset of the offset of the function area */
-#define SCRIPT_FUNCTAREAPTR_OFFSET 4 -8
-
-/** Offset that has to be added to the function area pointer */
-#define SCRIPT_FUNCTAREAPTR_MAGIC 8 -8
-
-/** Offset of the name pointer */
-#define SCRIPT_NAME_OFFSET (getSciVersion() < SCI_VERSION_1_1 ? 14 -8 : 16)
-
-/** Object-relative offset of the -info- selector */
-#define SCRIPT_INFO_OFFSET (getSciVersion() < SCI_VERSION_1_1 ? 12 -8 : 14)
-
-/** Flag fo the -info- selector */
-#define SCRIPT_INFO_CLONE 0x0001
-
-/** Flag for the -info- selector */
-#define SCRIPT_INFO_CLASS 0x8000
-
-
/** Magical object identifier */
#define SCRIPT_OBJECT_MAGIC_NUMBER 0x1234
+
/** Offset of this identifier */
#define SCRIPT_OBJECT_MAGIC_OFFSET (getSciVersion() < SCI_VERSION_1_1 ? -8 : 0)
@@ -89,13 +54,10 @@ class ResourceManager;
#define SCRIPT_SUPERCLASS_OFFSET (getSciVersion() < SCI_VERSION_1_1 ? 10 -8 : 12)
-/** Magic adjustment value for lofsa and lofss */
-#define SCRIPT_LOFS_MAGIC 3
-
/** Stack pointer value: Use predecessor's value */
#define CALL_SP_CARRY NULL
-/** Types of selectors as returned by lookup_selector() below. */
+/** Types of selectors as returned by lookupSelector() below. */
enum SelectorType {
kSelectorNone = 0,
kSelectorVariable,
@@ -195,7 +157,6 @@ struct SelectorCache {
// Used for auto detection purposes
Selector overlay; ///< Used to determine if a game is using old gfx functions or not
- Selector setCursor; ///< For cursor semantics autodetection
// SCI1.1 Mac icon bar selectors
Selector iconIndex; ///< Used to index icon bar objects
@@ -263,41 +224,11 @@ enum {
VAR_PARAM = 3
};
-/**
- * Structure for storing the current internal state of the VM.
- */
-struct ScriptState {
- ExecStack *xs;
- int16 restAdjust;
- reg_t *variables[4]; ///< global, local, temp, param, as immediate pointers
- reg_t *variables_base[4]; ///< Used for referencing VM ops
- SegmentId variables_seg[4]; ///< Same as above, contains segment IDs
- int variables_max[4]; ///< Max. values for all variables
-};
-
-/**
- * The current internal state of the VM.
- */
-extern ScriptState scriptState;
-
-/**
- * Set this to 1 to abort script execution immediately. Aborting will
- * leave the debug exec stack intact.
- * Set it to 2 to force a replay afterwards.
- */
-extern int script_abort_flag;
-
/** Number of kernel calls in between gcs; should be < 50000 */
enum {
GC_INTERVAL = 32768
};
-/** Initially GC_DELAY, can be set at runtime */
-extern int script_gc_interval;
-
-/** Number of steps executed */
-extern int script_step_counter;
-
/**
* Executes function pubfunct of the specified script.
@@ -379,7 +310,7 @@ int script_init_engine(EngineState *);
* kSelectorMethod if the selector represents a
* method
*/
-SelectorType lookup_selector(SegManager *segMan, reg_t obj, Selector selectorid,
+SelectorType lookupSelector(SegManager *segMan, reg_t obj, Selector selectorid,
ObjVarRef *varp, reg_t *fptr);
/**
@@ -469,7 +400,7 @@ int game_exit(EngineState *s);
/**
* Instructs the virtual machine to abort
*/
-void quit_vm();
+void quit_vm(EngineState *s);
/**
* Read a PMachine instruction from a memory buffer and return its length.
diff --git a/engines/sci/event.cpp b/engines/sci/event.cpp
index 53f4675f56..c3be22b143 100644
--- a/engines/sci/event.cpp
+++ b/engines/sci/event.cpp
@@ -319,8 +319,6 @@ sciEvent SciEvent::get(unsigned int mask) {
//sci_event_t error_event = { SCI_EVT_ERROR, 0, 0, 0 };
sciEvent event = { 0, 0, 0, 0 };
- // TODO: we need to call Cursor::refreshPosition() before each screen update to limit the mouse cursor position
-
// Update the screen here, since it's called very often
g_system->updateScreen();
@@ -389,7 +387,6 @@ void SciEvent::sleep(uint32 msecs) {
while (true) {
// let backend process events and update the screen
get(SCI_EVENT_PEEK);
- // TODO: we need to call Cursor::refreshPosition() before each screen update to limit the mouse cursor position
time = g_system->getMillis();
if (time + 10 < wakeup_time) {
g_system->delayMillis(10);
diff --git a/engines/sci/graphics/animate.cpp b/engines/sci/graphics/animate.cpp
index 8a03c9579b..c201b2cfb7 100644
--- a/engines/sci/graphics/animate.cpp
+++ b/engines/sci/graphics/animate.cpp
@@ -92,10 +92,10 @@ bool GfxAnimate::invoke(List *list, int argc, reg_t *argv) {
}
}
- signal = GET_SEL32V(_s->_segMan, curObject, SELECTOR(signal));
+ signal = readSelectorValue(_s->_segMan, curObject, SELECTOR(signal));
if (!(signal & kSignalFrozen)) {
// Call .doit method of that object
- invoke_selector(_s, curObject, g_sci->getKernel()->_selectorCache.doit, kContinueOnInvalidSelector, argc, argv, 0);
+ invokeSelector(_s, curObject, g_sci->getKernel()->_selectorCache.doit, kContinueOnInvalidSelector, argc, argv, 0);
// Lookup node again, since the nodetable it was in may have been reallocated
curNode = _s->_segMan->lookupNode(curAddress);
}
@@ -165,21 +165,21 @@ void GfxAnimate::makeSortedList(List *list) {
// Get data from current object
listEntry->givenOrderNo = listNr;
- listEntry->viewId = GET_SEL32V(_s->_segMan, curObject, SELECTOR(view));
- listEntry->loopNo = GET_SEL32V(_s->_segMan, curObject, SELECTOR(loop));
- listEntry->celNo = GET_SEL32V(_s->_segMan, curObject, SELECTOR(cel));
- listEntry->paletteNo = GET_SEL32V(_s->_segMan, curObject, SELECTOR(palette));
- listEntry->x = GET_SEL32V(_s->_segMan, curObject, SELECTOR(x));
- listEntry->y = GET_SEL32V(_s->_segMan, curObject, SELECTOR(y));
- listEntry->z = GET_SEL32V(_s->_segMan, curObject, SELECTOR(z));
- listEntry->priority = GET_SEL32V(_s->_segMan, curObject, SELECTOR(priority));
- listEntry->signal = GET_SEL32V(_s->_segMan, curObject, SELECTOR(signal));
+ listEntry->viewId = readSelectorValue(_s->_segMan, curObject, SELECTOR(view));
+ listEntry->loopNo = readSelectorValue(_s->_segMan, curObject, SELECTOR(loop));
+ listEntry->celNo = readSelectorValue(_s->_segMan, curObject, SELECTOR(cel));
+ listEntry->paletteNo = readSelectorValue(_s->_segMan, curObject, SELECTOR(palette));
+ listEntry->x = readSelectorValue(_s->_segMan, curObject, SELECTOR(x));
+ listEntry->y = readSelectorValue(_s->_segMan, curObject, SELECTOR(y));
+ listEntry->z = readSelectorValue(_s->_segMan, curObject, SELECTOR(z));
+ listEntry->priority = readSelectorValue(_s->_segMan, curObject, SELECTOR(priority));
+ listEntry->signal = readSelectorValue(_s->_segMan, curObject, SELECTOR(signal));
if (getSciVersion() >= SCI_VERSION_1_1) {
// Cel scaling
- listEntry->scaleSignal = GET_SEL32V(_s->_segMan, curObject, SELECTOR(scaleSignal));
+ listEntry->scaleSignal = readSelectorValue(_s->_segMan, curObject, SELECTOR(scaleSignal));
if (listEntry->scaleSignal & kScaleSignalDoScaling) {
- listEntry->scaleX = GET_SEL32V(_s->_segMan, curObject, SELECTOR(scaleX));
- listEntry->scaleY = GET_SEL32V(_s->_segMan, curObject, SELECTOR(scaleY));
+ listEntry->scaleX = readSelectorValue(_s->_segMan, curObject, SELECTOR(scaleX));
+ listEntry->scaleY = readSelectorValue(_s->_segMan, curObject, SELECTOR(scaleY));
} else {
listEntry->scaleX = 128;
listEntry->scaleY = 128;
@@ -228,11 +228,11 @@ void GfxAnimate::fill(byte &old_picNotValid) {
// adjust loop and cel, if any of those is invalid
if (listEntry->loopNo >= view->getLoopCount()) {
listEntry->loopNo = 0;
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(loop), listEntry->loopNo);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(loop), listEntry->loopNo);
}
if (listEntry->celNo >= view->getCelCount(listEntry->loopNo)) {
listEntry->celNo = 0;
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(cel), listEntry->celNo);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(cel), listEntry->celNo);
}
// Create rect according to coordinates and given cel
@@ -241,17 +241,17 @@ void GfxAnimate::fill(byte &old_picNotValid) {
} else {
view->getCelRect(listEntry->loopNo, listEntry->celNo, listEntry->x, listEntry->y, listEntry->z, &listEntry->celRect);
}
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(nsLeft), listEntry->celRect.left);
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(nsTop), listEntry->celRect.top);
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(nsRight), listEntry->celRect.right);
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(nsBottom), listEntry->celRect.bottom);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsLeft), listEntry->celRect.left);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsTop), listEntry->celRect.top);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsRight), listEntry->celRect.right);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsBottom), listEntry->celRect.bottom);
signal = listEntry->signal;
// Calculate current priority according to y-coordinate
if (!(signal & kSignalFixedPriority)) {
listEntry->priority = _ports->kernelCoordinateToPriority(listEntry->y);
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(priority), listEntry->priority);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(priority), listEntry->priority);
}
if (signal & kSignalNoUpdate) {
@@ -291,14 +291,14 @@ void GfxAnimate::update() {
if (signal & kSignalNoUpdate) {
if (!(signal & kSignalRemoveView)) {
- bitsHandle = GET_SEL32(_s->_segMan, curObject, SELECTOR(underBits));
+ bitsHandle = readSelector(_s->_segMan, curObject, SELECTOR(underBits));
if (_screen->_picNotValid != 1) {
_paint16->bitsRestore(bitsHandle);
listEntry->showBitsFlag = true;
} else {
_paint16->bitsFree(bitsHandle);
}
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(underBits), 0);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(underBits), 0);
}
signal &= 0xFFFF ^ kSignalForceUpdate;
signal &= signal & kSignalViewUpdated ? 0xFFFF ^ (kSignalViewUpdated | kSignalNoUpdate) : 0xFFFF;
@@ -348,7 +348,7 @@ void GfxAnimate::update() {
bitsHandle = _paint16->bitsSave(listEntry->celRect, GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY);
else
bitsHandle = _paint16->bitsSave(listEntry->celRect, GFX_SCREEN_MASK_ALL);
- PUT_SEL32(_s->_segMan, curObject, SELECTOR(underBits), bitsHandle);
+ writeSelector(_s->_segMan, curObject, SELECTOR(underBits), bitsHandle);
}
listEntry->signal = signal;
}
@@ -396,7 +396,7 @@ void GfxAnimate::drawCels() {
if (!(signal & (kSignalNoUpdate | kSignalHidden | kSignalAlwaysUpdate))) {
// Save background
bitsHandle = _paint16->bitsSave(listEntry->celRect, GFX_SCREEN_MASK_ALL);
- PUT_SEL32(_s->_segMan, curObject, SELECTOR(underBits), bitsHandle);
+ writeSelector(_s->_segMan, curObject, SELECTOR(underBits), bitsHandle);
// draw corresponding cel
_paint16->drawCel(listEntry->viewId, listEntry->loopNo, listEntry->celNo, listEntry->celRect, listEntry->priority, listEntry->paletteNo, listEntry->scaleX, listEntry->scaleY);
@@ -432,10 +432,10 @@ void GfxAnimate::updateScreen(byte oldPicNotValid) {
if (listEntry->showBitsFlag || !(signal & (kSignalRemoveView | kSignalNoUpdate) ||
(!(signal & kSignalRemoveView) && (signal & kSignalNoUpdate) && oldPicNotValid))) {
- lsRect.left = GET_SEL32V(_s->_segMan, curObject, SELECTOR(lsLeft));
- lsRect.top = GET_SEL32V(_s->_segMan, curObject, SELECTOR(lsTop));
- lsRect.right = GET_SEL32V(_s->_segMan, curObject, SELECTOR(lsRight));
- lsRect.bottom = GET_SEL32V(_s->_segMan, curObject, SELECTOR(lsBottom));
+ 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));
workerRect = lsRect;
workerRect.clip(listEntry->celRect);
@@ -447,10 +447,10 @@ void GfxAnimate::updateScreen(byte oldPicNotValid) {
_paint16->bitsShow(lsRect);
workerRect = listEntry->celRect;
}
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(lsLeft), workerRect.left);
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(lsTop), workerRect.top);
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(lsRight), workerRect.right);
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(lsBottom), workerRect.bottom);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsLeft), workerRect.left);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsTop), workerRect.top);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsRight), workerRect.right);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsBottom), workerRect.bottom);
_paint16->bitsShow(workerRect);
if (signal & kSignalHidden) {
@@ -481,7 +481,7 @@ void GfxAnimate::restoreAndDelete(int argc, reg_t *argv) {
signal = listEntry->signal;
// Finally update signal
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(signal), signal);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(signal), signal);
listIterator++;
}
@@ -490,16 +490,16 @@ void GfxAnimate::restoreAndDelete(int argc, reg_t *argv) {
listEntry = *listIterator;
curObject = listEntry->object;
// We read out signal here again, this is not by accident but to ensure that we got an up-to-date signal
- signal = GET_SEL32V(_s->_segMan, curObject, SELECTOR(signal));
+ signal = readSelectorValue(_s->_segMan, curObject, SELECTOR(signal));
if ((signal & (kSignalNoUpdate | kSignalRemoveView)) == 0) {
- _paint16->bitsRestore(GET_SEL32(_s->_segMan, curObject, SELECTOR(underBits)));
- PUT_SEL32V(_s->_segMan, curObject, SELECTOR(underBits), 0);
+ _paint16->bitsRestore(readSelector(_s->_segMan, curObject, SELECTOR(underBits)));
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(underBits), 0);
}
if (signal & kSignalDisposeMe) {
// Call .delete_ method of that object
- invoke_selector(_s, curObject, g_sci->getKernel()->_selectorCache.delete_, kContinueOnInvalidSelector, argc, argv, 0);
+ invokeSelector(_s, curObject, g_sci->getKernel()->_selectorCache.delete_, kContinueOnInvalidSelector, argc, argv, 0);
}
listIterator--;
}
diff --git a/engines/sci/graphics/compare.cpp b/engines/sci/graphics/compare.cpp
index 36dd2d4aed..3102edc2fa 100644
--- a/engines/sci/graphics/compare.cpp
+++ b/engines/sci/graphics/compare.cpp
@@ -79,12 +79,12 @@ bool GfxCompare::canBeHereCheckRectList(reg_t checkObject, const Common::Rect &c
while (curNode) {
curObject = curNode->value;
if (curObject != checkObject) {
- signal = GET_SEL32V(_segMan, curObject, SELECTOR(signal));
+ signal = readSelectorValue(_segMan, curObject, SELECTOR(signal));
if ((signal & (kSignalIgnoreActor | kSignalRemoveView | kSignalNoUpdate)) == 0) {
- curRect.left = GET_SEL32V(_segMan, curObject, SELECTOR(brLeft));
- curRect.top = GET_SEL32V(_segMan, curObject, SELECTOR(brTop));
- curRect.right = GET_SEL32V(_segMan, curObject, SELECTOR(brRight));
- curRect.bottom = GET_SEL32V(_segMan, curObject, SELECTOR(brBottom));
+ 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
@@ -115,29 +115,29 @@ uint16 GfxCompare::kernelOnControl(byte screenMask, const Common::Rect &rect) {
void GfxCompare::kernelSetNowSeen(reg_t objectReference) {
GfxView *view = NULL;
Common::Rect celRect(0, 0);
- GuiResourceId viewId = (GuiResourceId)GET_SEL32V(_segMan, objectReference, SELECTOR(view));
+ GuiResourceId viewId = (GuiResourceId)readSelectorValue(_segMan, objectReference, SELECTOR(view));
// HACK: Ignore invalid views for now (perhaps unimplemented text views?)
if (viewId == 0xFFFF) // invalid view
return;
- int16 loopNo = GET_SEL32V(_segMan, objectReference, SELECTOR(loop));
- int16 celNo = GET_SEL32V(_segMan, objectReference, SELECTOR(cel));
- int16 x = (int16)GET_SEL32V(_segMan, objectReference, SELECTOR(x));
- int16 y = (int16)GET_SEL32V(_segMan, objectReference, SELECTOR(y));
+ int16 loopNo = readSelectorValue(_segMan, objectReference, SELECTOR(loop));
+ int16 celNo = readSelectorValue(_segMan, objectReference, SELECTOR(cel));
+ int16 x = (int16)readSelectorValue(_segMan, objectReference, SELECTOR(x));
+ int16 y = (int16)readSelectorValue(_segMan, objectReference, SELECTOR(y));
int16 z = 0;
if (_kernel->_selectorCache.z > -1)
- z = (int16)GET_SEL32V(_segMan, objectReference, SELECTOR(z));
+ z = (int16)readSelectorValue(_segMan, objectReference, SELECTOR(z));
// now get cel rectangle
view = _cache->getView(viewId);
view->getCelRect(loopNo, celNo, x, y, z, &celRect);
- if (lookup_selector(_segMan, objectReference, _kernel->_selectorCache.nsTop, NULL, NULL) == kSelectorVariable) {
- PUT_SEL32V(_segMan, objectReference, SELECTOR(nsLeft), celRect.left);
- PUT_SEL32V(_segMan, objectReference, SELECTOR(nsRight), celRect.right);
- PUT_SEL32V(_segMan, objectReference, SELECTOR(nsTop), celRect.top);
- PUT_SEL32V(_segMan, objectReference, SELECTOR(nsBottom), celRect.bottom);
+ if (lookupSelector(_segMan, objectReference, _kernel->_selectorCache.nsTop, NULL, NULL) == kSelectorVariable) {
+ writeSelectorValue(_segMan, objectReference, SELECTOR(nsLeft), celRect.left);
+ writeSelectorValue(_segMan, objectReference, SELECTOR(nsRight), celRect.right);
+ writeSelectorValue(_segMan, objectReference, SELECTOR(nsTop), celRect.top);
+ writeSelectorValue(_segMan, objectReference, SELECTOR(nsBottom), celRect.bottom);
}
}
@@ -147,10 +147,10 @@ bool GfxCompare::kernelCanBeHere(reg_t curObject, reg_t listReference) {
uint16 signal, controlMask;
bool result;
- checkRect.left = GET_SEL32V(_segMan, curObject, SELECTOR(brLeft));
- checkRect.top = GET_SEL32V(_segMan, curObject, SELECTOR(brTop));
- checkRect.right = GET_SEL32V(_segMan, curObject, SELECTOR(brRight));
- checkRect.bottom = GET_SEL32V(_segMan, curObject, SELECTOR(brBottom));
+ checkRect.left = readSelectorValue(_segMan, curObject, SELECTOR(brLeft));
+ checkRect.top = readSelectorValue(_segMan, curObject, SELECTOR(brTop));
+ checkRect.right = readSelectorValue(_segMan, curObject, SELECTOR(brRight));
+ checkRect.bottom = readSelectorValue(_segMan, curObject, SELECTOR(brBottom));
if (!checkRect.isValidRect()) { // can occur in Iceman - HACK? TODO: is this really occuring in sierra sci? check this
warning("kCan(t)BeHere - invalid rect %d, %d -> %d, %d", checkRect.left, checkRect.top, checkRect.right, checkRect.bottom);
@@ -159,8 +159,8 @@ bool GfxCompare::kernelCanBeHere(reg_t curObject, reg_t listReference) {
adjustedRect = _coordAdjuster->onControl(checkRect);
- signal = GET_SEL32V(_segMan, curObject, SELECTOR(signal));
- controlMask = GET_SEL32V(_segMan, curObject, SELECTOR(illegalBits));
+ signal = readSelectorValue(_segMan, curObject, SELECTOR(signal));
+ controlMask = readSelectorValue(_segMan, curObject, SELECTOR(illegalBits));
result = (isOnControl(GFX_SCREEN_MASK_CONTROL, adjustedRect) & controlMask) ? false : true;
if ((result) && (signal & (kSignalIgnoreActor | kSignalRemoveView)) == 0) {
List *list = _segMan->lookupList(listReference);
@@ -183,14 +183,14 @@ bool GfxCompare::kernelIsItSkip(GuiResourceId viewId, int16 loopNo, int16 celNo,
}
void GfxCompare::kernelBaseSetter(reg_t object) {
- if (lookup_selector(_segMan, object, _kernel->_selectorCache.brLeft, NULL, NULL) == kSelectorVariable) {
- int16 x = GET_SEL32V(_segMan, object, SELECTOR(x));
- int16 y = GET_SEL32V(_segMan, object, SELECTOR(y));
- int16 z = (_kernel->_selectorCache.z > -1) ? GET_SEL32V(_segMan, object, SELECTOR(z)) : 0;
- int16 yStep = GET_SEL32V(_segMan, object, SELECTOR(yStep));
- GuiResourceId viewId = GET_SEL32V(_segMan, object, SELECTOR(view));
- int16 loopNo = GET_SEL32V(_segMan, object, SELECTOR(loop));
- int16 celNo = GET_SEL32V(_segMan, object, SELECTOR(cel));
+ if (lookupSelector(_segMan, object, _kernel->_selectorCache.brLeft, NULL, NULL) == kSelectorVariable) {
+ int16 x = readSelectorValue(_segMan, object, SELECTOR(x));
+ int16 y = readSelectorValue(_segMan, object, SELECTOR(y));
+ int16 z = (_kernel->_selectorCache.z > -1) ? readSelectorValue(_segMan, object, SELECTOR(z)) : 0;
+ int16 yStep = readSelectorValue(_segMan, object, SELECTOR(yStep));
+ GuiResourceId viewId = readSelectorValue(_segMan, object, SELECTOR(view));
+ int16 loopNo = readSelectorValue(_segMan, object, SELECTOR(loop));
+ int16 celNo = readSelectorValue(_segMan, object, SELECTOR(cel));
// HACK: Ignore invalid views for now (perhaps unimplemented text views?)
if (viewId == 0xFFFF) // invalid view
@@ -203,10 +203,10 @@ void GfxCompare::kernelBaseSetter(reg_t object) {
celRect.bottom = y + 1;
celRect.top = celRect.bottom - yStep;
- PUT_SEL32V(_segMan, object, SELECTOR(brLeft), celRect.left);
- PUT_SEL32V(_segMan, object, SELECTOR(brRight), celRect.right);
- PUT_SEL32V(_segMan, object, SELECTOR(brTop), celRect.top);
- PUT_SEL32V(_segMan, object, SELECTOR(brBottom), celRect.bottom);
+ writeSelectorValue(_segMan, object, SELECTOR(brLeft), celRect.left);
+ writeSelectorValue(_segMan, object, SELECTOR(brRight), celRect.right);
+ writeSelectorValue(_segMan, object, SELECTOR(brTop), celRect.top);
+ writeSelectorValue(_segMan, object, SELECTOR(brBottom), celRect.bottom);
}
}
diff --git a/engines/sci/graphics/controls.cpp b/engines/sci/graphics/controls.cpp
index f744d6e7f1..26af9741c2 100644
--- a/engines/sci/graphics/controls.cpp
+++ b/engines/sci/graphics/controls.cpp
@@ -143,9 +143,9 @@ void GfxControls::texteditSetBlinkTime() {
}
void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) {
- uint16 cursorPos = GET_SEL32V(_segMan, controlObject, SELECTOR(cursor));
- uint16 maxChars = GET_SEL32V(_segMan, controlObject, SELECTOR(max));
- reg_t textReference = GET_SEL32(_segMan, controlObject, SELECTOR(text));
+ uint16 cursorPos = readSelectorValue(_segMan, controlObject, SELECTOR(cursor));
+ uint16 maxChars = readSelectorValue(_segMan, controlObject, SELECTOR(max));
+ reg_t textReference = readSelector(_segMan, controlObject, SELECTOR(text));
Common::String text;
uint16 textSize, eventType, eventKey = 0;
bool textChanged = false;
@@ -158,14 +158,14 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) {
if (!eventObject.isNull()) {
textSize = text.size();
- eventType = GET_SEL32V(_segMan, eventObject, SELECTOR(type));
+ eventType = readSelectorValue(_segMan, eventObject, SELECTOR(type));
switch (eventType) {
case SCI_EVENT_MOUSE_PRESS:
// TODO: Implement mouse support for cursor change
break;
case SCI_EVENT_KEYBOARD:
- eventKey = GET_SEL32V(_segMan, eventObject, SELECTOR(message));
+ eventKey = readSelectorValue(_segMan, eventObject, SELECTOR(message));
switch (eventKey) {
case SCI_KEY_BACKSPACE:
if (cursorPos > 0) {
@@ -207,9 +207,9 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) {
if (textChanged) {
GuiResourceId oldFontId = _text16->GetFontId();
- GuiResourceId fontId = GET_SEL32V(_segMan, controlObject, SELECTOR(font));
- rect = Common::Rect(GET_SEL32V(_segMan, controlObject, SELECTOR(nsLeft)), GET_SEL32V(_segMan, controlObject, SELECTOR(nsTop)),
- GET_SEL32V(_segMan, controlObject, SELECTOR(nsRight)), GET_SEL32V(_segMan, controlObject, SELECTOR(nsBottom)));
+ GuiResourceId fontId = readSelectorValue(_segMan, controlObject, SELECTOR(font));
+ rect = Common::Rect(readSelectorValue(_segMan, controlObject, SELECTOR(nsLeft)), readSelectorValue(_segMan, controlObject, SELECTOR(nsTop)),
+ readSelectorValue(_segMan, controlObject, SELECTOR(nsRight)), readSelectorValue(_segMan, controlObject, SELECTOR(nsBottom)));
_text16->SetFont(fontId);
if (textAddChar) {
// We check, if we are really able to add the new char
@@ -241,7 +241,7 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) {
}
}
- PUT_SEL32V(_segMan, controlObject, SELECTOR(cursor), cursorPos);
+ writeSelectorValue(_segMan, controlObject, SELECTOR(cursor), cursorPos);
}
int GfxControls::getPicNotValid() {
diff --git a/engines/sci/graphics/coordadjuster.cpp b/engines/sci/graphics/coordadjuster.cpp
index 40ef655be7..422df52f27 100644
--- a/engines/sci/graphics/coordadjuster.cpp
+++ b/engines/sci/graphics/coordadjuster.cpp
@@ -93,18 +93,18 @@ GfxCoordAdjuster32::~GfxCoordAdjuster32() {
}
void GfxCoordAdjuster32::kernelGlobalToLocal(int16 &x, int16 &y, reg_t planeObject) {
- //int16 resY = GET_SEL32V(_s->_segMan, planeObj, SELECTOR(resY));
- //int16 resX = GET_SEL32V(_s->_segMan, planeObj, SELECTOR(resX));
+ //int16 resY = readSelectorValue(_s->_segMan, planeObj, SELECTOR(resY));
+ //int16 resX = readSelectorValue(_s->_segMan, planeObj, SELECTOR(resX));
//*x = ( *x * _screen->getWidth()) / resX;
//*y = ( *y * _screen->getHeight()) / resY;
- x -= GET_SEL32V(_segMan, planeObject, SELECTOR(left));
- y -= GET_SEL32V(_segMan, planeObject, SELECTOR(top));
+ x -= readSelectorValue(_segMan, planeObject, SELECTOR(left));
+ y -= readSelectorValue(_segMan, planeObject, SELECTOR(top));
}
void GfxCoordAdjuster32::kernelLocalToGlobal(int16 &x, int16 &y, reg_t planeObject) {
- //int16 resY = GET_SEL32V(_s->_segMan, planeObj, SELECTOR(resY));
- //int16 resX = GET_SEL32V(_s->_segMan, planeObj, SELECTOR(resX));
- x += GET_SEL32V(_segMan, planeObject, SELECTOR(left));
- y += GET_SEL32V(_segMan, planeObject, SELECTOR(top));
+ //int16 resY = readSelectorValue(_s->_segMan, planeObj, SELECTOR(resY));
+ //int16 resX = readSelectorValue(_s->_segMan, planeObj, SELECTOR(resX));
+ x += readSelectorValue(_segMan, planeObject, SELECTOR(left));
+ y += readSelectorValue(_segMan, planeObject, SELECTOR(top));
//*x = ( *x * resX) / _screen->getWidth();
//*y = ( *y * resY) / _screen->getHeight();
}
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index 78253bd913..3cc5ca5447 100644
--- a/engines/sci/graphics/frameout.cpp
+++ b/engines/sci/graphics/frameout.cpp
@@ -54,7 +54,7 @@ GfxFrameout::~GfxFrameout() {
void GfxFrameout::kernelAddPlane(reg_t object) {
_planes.push_back(object);
- int16 planePri = GET_SEL32V(_segMan, object, SELECTOR(priority)) & 0xFFFF;
+ int16 planePri = readSelectorValue(_segMan, object, SELECTOR(priority)) & 0xFFFF;
if (planePri > _highPlanePri)
_highPlanePri = planePri;
}
@@ -74,7 +74,7 @@ void GfxFrameout::kernelDeletePlane(reg_t object) {
_highPlanePri = 0;
for (uint32 planeNr = 0; planeNr < _planes.size(); planeNr++) {
- int16 planePri = GET_SEL32V(_segMan, _planes[planeNr], SELECTOR(priority)) & 0xFFFF;
+ int16 planePri = readSelectorValue(_segMan, _planes[planeNr], SELECTOR(priority)) & 0xFFFF;
if (planePri > _highPlanePri)
_highPlanePri = planePri;
}
@@ -126,29 +126,29 @@ void GfxFrameout::kernelFrameout() {
for (uint32 planeNr = 0; planeNr < _planes.size(); planeNr++) {
planeObject = _planes[planeNr];
- planePriority = GET_SEL32V(_segMan, planeObject, SELECTOR(priority));
+ planePriority = readSelectorValue(_segMan, planeObject, SELECTOR(priority));
if (planePriority == -1) // Plane currently not meant to be shown
continue;
- planeRect.top = GET_SEL32V(_segMan, planeObject, SELECTOR(top));
- planeRect.left = GET_SEL32V(_segMan, planeObject, SELECTOR(left));
- planeRect.bottom = GET_SEL32V(_segMan, planeObject, SELECTOR(bottom));
- planeRect.right = GET_SEL32V(_segMan, planeObject, SELECTOR(right));
- planeResY = GET_SEL32V(_segMan, planeObject, SELECTOR(resY));
- planeResX = GET_SEL32V(_segMan, planeObject, SELECTOR(resX));
+ planeRect.top = readSelectorValue(_segMan, planeObject, SELECTOR(top));
+ planeRect.left = readSelectorValue(_segMan, planeObject, SELECTOR(left));
+ planeRect.bottom = readSelectorValue(_segMan, planeObject, SELECTOR(bottom));
+ planeRect.right = readSelectorValue(_segMan, planeObject, SELECTOR(right));
+ planeResY = readSelectorValue(_segMan, planeObject, SELECTOR(resY));
+ planeResX = readSelectorValue(_segMan, planeObject, SELECTOR(resX));
planeRect.top = (planeRect.top * _screen->getHeight()) / planeResY;
planeRect.left = (planeRect.left * _screen->getWidth()) / planeResX;
planeRect.bottom = (planeRect.bottom * _screen->getHeight()) / planeResY;
planeRect.right = (planeRect.right * _screen->getWidth()) / planeResX;
- planeBack = GET_SEL32V(_segMan, planeObject, SELECTOR(back));
+ planeBack = readSelectorValue(_segMan, planeObject, SELECTOR(back));
if (planeBack) {
_paint32->fillRect(planeRect, planeBack);
}
- planePictureNr = GET_SEL32V(_segMan, planeObject, SELECTOR(picture));
+ planePictureNr = readSelectorValue(_segMan, planeObject, SELECTOR(picture));
if ((planePictureNr != 0xFFFF) && (planePictureNr != 0xFFFE)) {
planePicture = new GfxPicture(_resMan, _coordAdjuster, 0, _screen, _palette, planePictureNr, false);
planePictureCels = planePicture->getSci32celCount();
@@ -161,19 +161,19 @@ void GfxFrameout::kernelFrameout() {
itemEntry = itemData;
for (uint32 itemNr = 0; itemNr < _screenItems.size(); itemNr++) {
itemObject = _screenItems[itemNr];
- itemPlane = GET_SEL32(_segMan, itemObject, SELECTOR(plane));
+ itemPlane = readSelector(_segMan, itemObject, SELECTOR(plane));
if (planeObject == itemPlane) {
// Found an item on current plane
- itemEntry->viewId = GET_SEL32V(_segMan, itemObject, SELECTOR(view));
- itemEntry->loopNo = GET_SEL32V(_segMan, itemObject, SELECTOR(loop));
- itemEntry->celNo = GET_SEL32V(_segMan, itemObject, SELECTOR(cel));
- itemEntry->x = GET_SEL32V(_segMan, itemObject, SELECTOR(x));
- itemEntry->y = GET_SEL32V(_segMan, itemObject, SELECTOR(y));
- itemEntry->z = GET_SEL32V(_segMan, itemObject, SELECTOR(z));
- itemEntry->priority = GET_SEL32V(_segMan, itemObject, SELECTOR(priority));
- itemEntry->signal = GET_SEL32V(_segMan, itemObject, SELECTOR(signal));
- itemEntry->scaleX = GET_SEL32V(_segMan, itemObject, SELECTOR(scaleX));
- itemEntry->scaleY = GET_SEL32V(_segMan, itemObject, SELECTOR(scaleY));
+ itemEntry->viewId = readSelectorValue(_segMan, itemObject, SELECTOR(view));
+ itemEntry->loopNo = readSelectorValue(_segMan, itemObject, SELECTOR(loop));
+ itemEntry->celNo = readSelectorValue(_segMan, itemObject, SELECTOR(cel));
+ itemEntry->x = readSelectorValue(_segMan, itemObject, SELECTOR(x));
+ itemEntry->y = readSelectorValue(_segMan, itemObject, SELECTOR(y));
+ itemEntry->z = readSelectorValue(_segMan, itemObject, SELECTOR(z));
+ itemEntry->priority = readSelectorValue(_segMan, itemObject, SELECTOR(priority));
+ itemEntry->signal = readSelectorValue(_segMan, itemObject, SELECTOR(signal));
+ itemEntry->scaleX = readSelectorValue(_segMan, itemObject, SELECTOR(scaleX));
+ itemEntry->scaleY = readSelectorValue(_segMan, itemObject, SELECTOR(scaleY));
itemEntry->object = itemObject;
itemEntry->y = ((itemEntry->y * _screen->getHeight()) / planeResY);
@@ -240,12 +240,12 @@ void GfxFrameout::kernelFrameout() {
// This doesn't work for SCI2.1 games...
if (getSciVersion() == SCI_VERSION_2) {
Kernel *kernel = g_sci->getKernel();
- if (lookup_selector(_segMan, itemEntry->object, kernel->_selectorCache.text, NULL, NULL) == kSelectorVariable) {
- Common::String text = _segMan->getString(GET_SEL32(_segMan, itemEntry->object, SELECTOR(text)));
- int16 fontRes = GET_SEL32V(_segMan, itemEntry->object, SELECTOR(font));
+ if (lookupSelector(_segMan, itemEntry->object, kernel->_selectorCache.text, NULL, NULL) == kSelectorVariable) {
+ Common::String text = _segMan->getString(readSelector(_segMan, itemEntry->object, SELECTOR(text)));
+ int16 fontRes = readSelectorValue(_segMan, itemEntry->object, SELECTOR(font));
GfxFont *font = new GfxFontFromResource(_resMan, _screen, fontRes);
- bool dimmed = GET_SEL32V(_segMan, itemEntry->object, SELECTOR(dimmed));
- uint16 foreColor = GET_SEL32V(_segMan, itemEntry->object, SELECTOR(fore));
+ bool dimmed = readSelectorValue(_segMan, itemEntry->object, SELECTOR(dimmed));
+ uint16 foreColor = readSelectorValue(_segMan, itemEntry->object, SELECTOR(fore));
uint16 curX = itemEntry->x;
uint16 curY = itemEntry->y;
for (uint32 i = 0; i < text.size(); i++) {
diff --git a/engines/sci/graphics/maciconbar.cpp b/engines/sci/graphics/maciconbar.cpp
index 2904bda22b..a06e98ccbf 100644
--- a/engines/sci/graphics/maciconbar.cpp
+++ b/engines/sci/graphics/maciconbar.cpp
@@ -48,7 +48,7 @@ void GfxMacIconBar::drawIcons() {
uint32 lastX = 0;
for (uint32 i = 0; i < _iconBarObjects.size(); i++) {
- uint32 iconIndex = GET_SEL32V(g_sci->getEngineState()->_segMan, _iconBarObjects[i], SELECTOR(iconIndex));
+ uint32 iconIndex = readSelectorValue(g_sci->getEngineState()->_segMan, _iconBarObjects[i], SELECTOR(iconIndex));
Resource *res = g_sci->getResMan()->findResource(ResourceId(kResourceTypeMacIconBarPictN, iconIndex + 1), false);
if (!res)
continue;
diff --git a/engines/sci/graphics/menu.cpp b/engines/sci/graphics/menu.cpp
index 5e3b419fe3..880e1aba12 100644
--- a/engines/sci/graphics/menu.cpp
+++ b/engines/sci/graphics/menu.cpp
@@ -378,7 +378,7 @@ void GfxMenu::calculateMenuAndItemWidth() {
}
reg_t GfxMenu::kernelSelect(reg_t eventObject) {
- int16 eventType = GET_SEL32V(_segMan, eventObject, SELECTOR(type));
+ int16 eventType = readSelectorValue(_segMan, eventObject, SELECTOR(type));
int16 keyPress, keyModifier;
Common::Point mousePosition;
GuiMenuItemList::iterator itemIterator = _itemList.begin();
@@ -390,8 +390,8 @@ reg_t GfxMenu::kernelSelect(reg_t eventObject) {
switch (eventType) {
case SCI_EVENT_KEYBOARD:
- keyPress = GET_SEL32V(_segMan, eventObject, SELECTOR(message));
- keyModifier = GET_SEL32V(_segMan, eventObject, SELECTOR(modifiers));
+ keyPress = readSelectorValue(_segMan, eventObject, SELECTOR(message));
+ keyModifier = readSelectorValue(_segMan, eventObject, SELECTOR(modifiers));
// If tab got pressed, handle it here as if it was Ctrl-I - at least sci0 also did it that way
if (keyPress == SCI_KEY_TAB) {
keyModifier = SCI_KEYMOD_CTRL;
@@ -465,7 +465,7 @@ reg_t GfxMenu::kernelSelect(reg_t eventObject) {
_ports->setPort(_oldPort);
if ((itemEntry) || (forceClaimed))
- PUT_SEL32(_segMan, eventObject, SELECTOR(claimed), make_reg(0, 1));
+ writeSelector(_segMan, eventObject, SELECTOR(claimed), make_reg(0, 1));
if (itemEntry)
return make_reg(0, (itemEntry->menuId << 8) | (itemEntry->id));
return NULL_REG;
diff --git a/engines/sci/graphics/paint16.cpp b/engines/sci/graphics/paint16.cpp
index a96189dbf0..ff4f3bec52 100644
--- a/engines/sci/graphics/paint16.cpp
+++ b/engines/sci/graphics/paint16.cpp
@@ -354,7 +354,8 @@ void GfxPaint16::bitsRestore(reg_t memoryHandle) {
}
void GfxPaint16::bitsFree(reg_t memoryHandle) {
- _segMan->freeHunkEntry(memoryHandle);
+ if (!memoryHandle.isNull()) // happens in KQ5CD
+ _segMan->freeHunkEntry(memoryHandle);
}
void GfxPaint16::kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, bool animationBlackoutFlag, bool mirroredFlag, bool addToFlag, int16 EGApaletteNo) {
diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp
index ab96856674..3c4cf7e964 100644
--- a/engines/sci/graphics/palette.cpp
+++ b/engines/sci/graphics/palette.cpp
@@ -56,6 +56,7 @@ GfxPalette::GfxPalette(ResourceManager *resMan, GfxScreen *screen, bool autoSetP
_sysPalette.colors[255].g = 255;
_sysPalette.colors[255].b = 255;
+ _sysPaletteChanged = false;
if (autoSetPalette) {
if (_resMan->getViewType() == kViewEga)
setEGA();
@@ -64,7 +65,6 @@ GfxPalette::GfxPalette(ResourceManager *resMan, GfxScreen *screen, bool autoSetP
else
kernelSetFromResource(999, true);
}
- _sysPaletteChanged = false;
}
GfxPalette::~GfxPalette() {
diff --git a/engines/sci/graphics/picture.cpp b/engines/sci/graphics/picture.cpp
index 74f651a88a..a59153f116 100644
--- a/engines/sci/graphics/picture.cpp
+++ b/engines/sci/graphics/picture.cpp
@@ -69,15 +69,20 @@ void GfxPicture::draw(int16 animationNr, bool mirroredFlag, bool addToFlag, int1
headerSize = READ_LE_UINT16(_resource->data);
switch (headerSize) {
case 0x26: // SCI 1.1 VGA picture
+ _resourceType = SCI_PICTURE_TYPE_SCI11;
+ if (_addToFlag)
+ _priority = 15;
drawSci11Vga();
break;
#ifdef ENABLE_SCI32
case 0x0e: // SCI32 VGA picture
+ _resourceType = SCI_PICTURE_TYPE_SCI32;
drawSci32Vga();
break;
#endif
default:
// VGA, EGA or Amiga vector data
+ _resourceType = SCI_PICTURE_TYPE_REGULAR;
drawVectorData(_resource->data, _resource->size);
}
}
@@ -108,9 +113,8 @@ void GfxPicture::drawSci11Vga() {
_palette->set(&palette, true);
// display Cel-data
- if (has_cel) {
- drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, 0, 0, false);
- }
+ if (has_cel)
+ drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, 0, 0);
// process vector data
drawVectorData(inbuffer + vector_dataPos, vector_size);
@@ -139,6 +143,7 @@ void GfxPicture::drawSci32Vga(int16 celNo) {
// HACK
_mirroredFlag = false;
_addToFlag = false;
+ _resourceType = SCI_PICTURE_TYPE_SCI32;
if ((celNo == -1) || (celNo == 0)) {
// Create palette and set it
@@ -155,14 +160,14 @@ void GfxPicture::drawSci32Vga(int16 celNo) {
cel_LiteralPos = READ_LE_UINT32(inbuffer + cel_headerPos + 28);
cel_relXpos = READ_LE_UINT16(inbuffer + cel_headerPos + 38);
cel_relYpos = READ_LE_UINT16(inbuffer + cel_headerPos + 40);
- drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, cel_relXpos, cel_relYpos, true);
+ drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, cel_relXpos, cel_relYpos);
cel_headerPos += 42;
celCount--;
}
}
#endif
-void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 callerX, int16 callerY, bool hasSci32Header) {
+void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 callerX, int16 callerY) {
byte *celBitmap = NULL;
byte *ptr = NULL;
byte *headerPtr = inbuffer + headerPos;
@@ -179,11 +184,16 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos
int pixelNr, pixelCount;
#ifdef ENABLE_SCI32
- if (!hasSci32Header) {
+ if (_resourceType != SCI_PICTURE_TYPE_SCI32) {
#endif
displaceX = (signed char)headerPtr[4];
displaceY = (unsigned char)headerPtr[5];
- clearColor = headerPtr[6];
+ if (_resourceType == SCI_PICTURE_TYPE_SCI11) {
+ // SCI1.1 uses hardcoded clearcolor for pictures, even if cel header specifies otherwise
+ clearColor = _screen->getColorWhite();
+ } else {
+ clearColor = headerPtr[6];
+ }
#ifdef ENABLE_SCI32
} else {
displaceX = READ_LE_UINT16(headerPtr + 4); // probably signed?!?
@@ -518,10 +528,29 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
}
break;
+ // Pattern opcodes are handled in sierra sci1.1+ as actual NOPs and normally they definitely should not occur
+ // inside picture data for such games
case PIC_OP_SET_PATTERN:
+ if (_resourceType >= SCI_PICTURE_TYPE_SCI11) {
+ if (strcmp(g_sci->getGameID(), "sq4") == 0) {
+ // WORKAROUND: For SQ4 / for some pictures handle this like a terminator
+ // This picture includes garbage data, first a set pattern w/o parameter and then short pattern
+ // I guess that garbage is a left over from the sq4-floppy (sci1) to sq4-cd (sci1.1) conversion
+ switch (_resourceId) {
+ case 381:
+ case 376:
+ return;
+ default:
+ break;
+ }
+ }
+ error("pic-operation set pattern inside sci1.1+ vector data");
+ }
pattern_Code = data[curPos++];
break;
case PIC_OP_SHORT_PATTERNS:
+ if (_resourceType >= SCI_PICTURE_TYPE_SCI11)
+ error("pic-operation short pattern inside sci1.1+ vector data");
vectorGetPatternTexture(data, curPos, pattern_Code, pattern_Texture);
vectorGetAbsCoords(data, curPos, x, y);
vectorPattern(x, y, pic_color, pic_priority, pic_control, pattern_Code, pattern_Texture);
@@ -532,6 +561,8 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
}
break;
case PIC_OP_MEDIUM_PATTERNS:
+ if (_resourceType >= SCI_PICTURE_TYPE_SCI11)
+ error("pic-operation medium pattern inside sci1.1+ vector data");
vectorGetPatternTexture(data, curPos, pattern_Code, pattern_Texture);
vectorGetAbsCoords(data, curPos, x, y);
vectorPattern(x, y, pic_color, pic_priority, pic_control, pattern_Code, pattern_Texture);
@@ -542,6 +573,8 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
}
break;
case PIC_OP_ABSOLUTE_PATTERN:
+ if (_resourceType >= SCI_PICTURE_TYPE_SCI11)
+ error("pic-operation absolute pattern inside sci1.1+ vector data");
while (vectorIsNonOpcode(data[curPos])) {
vectorGetPatternTexture(data, curPos, pattern_Code, pattern_Texture);
vectorGetAbsCoords(data, curPos, x, y);
@@ -585,7 +618,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
vectorGetAbsCoordsNoMirror(data, curPos, x, y);
size = READ_LE_UINT16(data + curPos); curPos += 2;
_priority = pic_priority; // set global priority so the cel gets drawn using current priority as well
- drawCelData(data, _resource->size, curPos, curPos + 8, 0, x, y, false);
+ drawCelData(data, _resource->size, curPos, curPos + 8, 0, x, y);
curPos += size;
break;
case PIC_OPX_EGA_SET_PRIORITY_TABLE:
@@ -627,7 +660,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
_priority = pic_priority; // set global priority so the cel gets drawn using current priority as well
if (pic_priority == 255)
_priority = 0; // if priority not set, use priority 0
- drawCelData(data, _resource->size, curPos, curPos + 8, 0, x, y, false);
+ drawCelData(data, _resource->size, curPos, curPos + 8, 0, x, y);
curPos += size;
break;
case PIC_OPX_VGA_PRIORITY_TABLE_EQDIST:
diff --git a/engines/sci/graphics/picture.h b/engines/sci/graphics/picture.h
index 3374c33b52..5a86539b37 100644
--- a/engines/sci/graphics/picture.h
+++ b/engines/sci/graphics/picture.h
@@ -32,6 +32,12 @@ namespace Sci {
#define SCI_PATTERN_CODE_USE_TEXTURE 0x20
#define SCI_PATTERN_CODE_PENSIZE 0x07
+enum {
+ SCI_PICTURE_TYPE_REGULAR = 0,
+ SCI_PICTURE_TYPE_SCI11 = 1,
+ SCI_PICTURE_TYPE_SCI32 = 2
+};
+
class GfxPorts;
class GfxScreen;
class GfxPalette;
@@ -57,7 +63,7 @@ private:
void initData(GuiResourceId resourceId);
void reset();
void drawSci11Vga();
- void drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 callerX, int16 callerY, bool hasSci32Header);
+ void drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 callerX, int16 callerY);
void drawVectorData(byte *data, int size);
bool vectorIsNonOpcode(byte pixel);
void vectorGetAbsCoords(byte *data, int &curPos, int16 &x, int16 &y);
@@ -80,6 +86,7 @@ private:
int16 _resourceId;
Resource *_resource;
+ int _resourceType;
int16 _animationNr;
bool _mirroredFlag;
diff --git a/engines/sci/module.mk b/engines/sci/module.mk
index 26ae1c16b1..a2cfd38f95 100644
--- a/engines/sci/module.mk
+++ b/engines/sci/module.mk
@@ -6,6 +6,7 @@ MODULE_OBJS := \
detection.o \
event.o \
resource.o \
+ resource_audio.o \
sci.o \
util.o \
engine/features.o \
diff --git a/engines/sci/parser/said.cpp b/engines/sci/parser/said.cpp
index 5cd1310ad3..f49704372a 100644
--- a/engines/sci/parser/said.cpp
+++ b/engines/sci/parser/said.cpp
@@ -2443,13 +2443,14 @@ static int augment_parse_nodes(parse_tree_node_t *parset, parse_tree_node_t *sai
int said(EngineState *s, byte *spec, bool verbose) {
int retval;
+ Vocabulary *voc = g_sci->getVocabulary();
- parse_tree_node_t *parse_tree_ptr = s->_voc->_parserNodes;
+ parse_tree_node_t *parse_tree_ptr = voc->_parserNodes;
- if (s->_voc->parserIsValid) {
+ if (voc->parserIsValid) {
if (said_parse_spec(spec)) {
printf("Offending spec was: ");
- s->_voc->decipherSaidBlock(spec);
+ voc->decipherSaidBlock(spec);
return SAID_NO_MATCH;
}
diff --git a/engines/sci/parser/said.y b/engines/sci/parser/said.y
index 27486c5794..cbb2ff3e62 100644
--- a/engines/sci/parser/said.y
+++ b/engines/sci/parser/said.y
@@ -799,13 +799,14 @@ static int augment_parse_nodes(parse_tree_node_t *parset, parse_tree_node_t *sai
int said(EngineState *s, byte *spec, bool verbose) {
int retval;
+ Vocabulary *voc = g_sci->getVocabulary();
- parse_tree_node_t *parse_tree_ptr = s->_voc->_parserNodes;
+ parse_tree_node_t *parse_tree_ptr = voc->_parserNodes;
- if (s->_voc->parserIsValid) {
+ if (voc->parserIsValid) {
if (said_parse_spec(spec)) {
printf("Offending spec was: ");
- s->_voc->decipherSaidBlock(spec);
+ voc->decipherSaidBlock(spec);
return SAID_NO_MATCH;
}
diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp
index 428e66ebe2..400f0b1e67 100644
--- a/engines/sci/resource.cpp
+++ b/engines/sci/resource.cpp
@@ -26,7 +26,6 @@
// Resource library
#include "common/file.h"
-#include "common/macresman.h"
#include "sci/resource.h"
#include "sci/util.h"
@@ -45,18 +44,6 @@ struct resource_index_t {
uint16 wSize;
};
-struct ResourceSource {
- ResSourceType source_type;
- bool scanned;
- Common::String location_name; // FIXME: Replace by FSNode ?
- const Common::FSNode *resourceFile;
- int volume_number;
- ResourceSource *associated_map;
- uint32 audioCompressionType;
- int32 *audioCompressionOffsetMapping;
- Common::MacResManager macResMan;
-};
-
//////////////////////////////////////////////////////////////////////
static SciVersion s_sciVersion = SCI_VERSION_NONE; // FIXME: Move this inside a suitable class, e.g. SciEngine
@@ -196,7 +183,7 @@ ResourceSource *ResourceManager::addExternalMap(const char *file_name, int volum
return newsrc;
}
-ResourceSource *ResourceManager::addExternalMap(const Common::FSNode *mapFile) {
+ResourceSource *ResourceManager::addExternalMap(const Common::FSNode *mapFile, int volume_nr) {
ResourceSource *newsrc = new ResourceSource();
newsrc->source_type = kSourceExtMap;
@@ -204,7 +191,7 @@ ResourceSource *ResourceManager::addExternalMap(const Common::FSNode *mapFile) {
newsrc->resourceFile = mapFile;
newsrc->scanned = false;
newsrc->associated_map = NULL;
- newsrc->volume_number = 0;
+ newsrc->volume_number = volume_nr;
_sources.push_back(newsrc);
return newsrc;
@@ -250,6 +237,7 @@ ResourceSource *ResourceManager::addPatchDir(const char *dirname) {
ResourceSource *newsrc = new ResourceSource();
newsrc->source_type = kSourceDirectory;
+ newsrc->resourceFile = 0;
newsrc->scanned = false;
newsrc->location_name = dirname;
@@ -270,37 +258,7 @@ ResourceSource *ResourceManager::getVolume(ResourceSource *map, int volume_nr) {
// Resource manager constructors and operations
-void ResourceManager::checkIfAudioVolumeIsCompressed(ResourceSource *source) {
- Common::File *file = getVolumeFile(source->location_name.c_str());
- if (!file) {
- warning("Failed to open %s", source->location_name.c_str());
- return;
- }
- file->seek(0, SEEK_SET);
- uint32 compressionType = file->readUint32BE();
- switch (compressionType) {
- case MKID_BE('MP3 '):
- case MKID_BE('OGG '):
- case MKID_BE('FLAC'):
- // Detected a compressed audio volume
- source->audioCompressionType = compressionType;
- // Now read the whole offset mapping table for later usage
- int32 recordCount = file->readUint32LE();
- if (!recordCount)
- error("compressed audio volume doesn't contain any entries!");
- int32 *offsetMapping = new int32[(recordCount + 1) * 2];
- source->audioCompressionOffsetMapping = offsetMapping;
- for (int recordNo = 0; recordNo < recordCount; recordNo++) {
- *offsetMapping++ = file->readUint32LE();
- *offsetMapping++ = file->readUint32LE();
- }
- // Put ending zero
- *offsetMapping++ = 0;
- *offsetMapping++ = file->size();
- }
-}
-
-bool ResourceManager::loadPatch(Resource *res, Common::File &file) {
+bool ResourceManager::loadPatch(Resource *res, Common::SeekableReadStream *file) {
// We assume that the resource type matches res->type
// We also assume that the current file position is right at the actual data (behind resourceid/headersize byte)
@@ -315,12 +273,12 @@ bool ResourceManager::loadPatch(Resource *res, Common::File &file) {
unsigned int really_read;
if (res->_headerSize > 0) {
- really_read = file.read(res->_header, res->_headerSize);
+ really_read = file->read(res->_header, res->_headerSize);
if (really_read != res->_headerSize)
error("Read %d bytes from %s but expected %d", really_read, res->_id.toString().c_str(), res->_headerSize);
}
- really_read = file.read(res->data, res->size);
+ really_read = file->read(res->data, res->size);
if (really_read != res->size)
error("Read %d bytes from %s but expected %d", really_read, res->_id.toString().c_str(), res->size);
@@ -338,77 +296,18 @@ bool ResourceManager::loadFromPatchFile(Resource *res) {
}
// Skip resourceid and header size byte
file.seek(2, SEEK_SET);
- return loadPatch(res, file);
-}
-
-bool ResourceManager::loadFromWaveFile(Resource *res, Common::File &file) {
- res->data = new byte[res->size];
-
- uint32 really_read = file.read(res->data, res->size);
- if (really_read != res->size)
- error("Read %d bytes from %s but expected %d", really_read, res->_id.toString().c_str(), res->size);
-
- res->_status = kResStatusAllocated;
- return true;
-}
-
-bool ResourceManager::loadFromAudioVolumeSCI11(Resource *res, Common::File &file) {
- // Check for WAVE files here
- uint32 riffTag = file.readUint32BE();
- if (riffTag == MKID_BE('RIFF')) {
- res->_headerSize = 0;
- res->size = file.readUint32LE();
- file.seek(-8, SEEK_CUR);
- return loadFromWaveFile(res, file);
- }
- file.seek(-4, SEEK_CUR);
-
- ResourceType type = (ResourceType)(file.readByte() & 0x7f);
- if (((res->_id.type == kResourceTypeAudio || res->_id.type == kResourceTypeAudio36) && (type != kResourceTypeAudio))
- || ((res->_id.type == kResourceTypeSync || res->_id.type == kResourceTypeSync36) && (type != kResourceTypeSync))) {
- warning("Resource type mismatch loading %s from %s", res->_id.toString().c_str(), file.getName());
- res->unalloc();
- return false;
- }
-
- res->_headerSize = file.readByte();
-
- if (type == kResourceTypeAudio) {
- if (res->_headerSize != 11 && res->_headerSize != 12) {
- warning("Unsupported audio header");
- res->unalloc();
- return false;
- }
-
- // Load sample size
- file.seek(7, SEEK_CUR);
- res->size = file.readUint32LE();
- // Adjust offset to point at the header data again
- file.seek(-11, SEEK_CUR);
- }
-
- return loadPatch(res, file);
-}
-
-bool ResourceManager::loadFromAudioVolumeSCI1(Resource *res, Common::File &file) {
- res->data = new byte[res->size];
-
- if (res->data == NULL) {
- error("Can't allocate %d bytes needed for loading %s", res->size, res->_id.toString().c_str());
- }
-
- unsigned int really_read = file.read(res->data, res->size);
- if (really_read != res->size)
- warning("Read %d bytes from %s but expected %d", really_read, res->_id.toString().c_str(), res->size);
-
- res->_status = kResStatusAllocated;
- return true;
+ return loadPatch(res, &file);
}
-Common::File *ResourceManager::getVolumeFile(const char *filename) {
+Common::SeekableReadStream *ResourceManager::getVolumeFile(ResourceSource *source) {
Common::List<Common::File *>::iterator it = _volumeFiles.begin();
Common::File *file;
+ if (source->resourceFile)
+ return source->resourceFile->createReadStream();
+
+ const char *filename = source->location_name.c_str();
+
// check if file is already opened
while (it != _volumeFiles.end()) {
file = *it;
@@ -459,10 +358,9 @@ void ResourceManager::loadResource(Resource *res) {
return;
}
- Common::File *file;
- // Either loading from volume or patch loading failed
- file = getVolumeFile(res->_source->location_name.c_str());
- if (!file) {
+ Common::SeekableReadStream *fileStream = getVolumeFile(res->_source);
+
+ if (!fileStream) {
warning("Failed to open %s", res->_source->location_name.c_str());
res->unalloc();
return;
@@ -470,8 +368,10 @@ void ResourceManager::loadResource(Resource *res) {
switch(res->_source->source_type) {
case kSourceWave:
- file->seek(res->_fileOffset, SEEK_SET);
- loadFromWaveFile(res, *file);
+ fileStream->seek(res->_fileOffset, SEEK_SET);
+ loadFromWaveFile(res, fileStream);
+ if (res->_source->resourceFile)
+ delete fileStream;
return;
case kSourceAudioVolume:
@@ -502,30 +402,39 @@ void ResourceManager::loadResource(Resource *res) {
if (!compressedOffset)
error("could not translate offset to compressed offset in audio volume");
- file->seek(compressedOffset, SEEK_SET);
+ fileStream->seek(compressedOffset, SEEK_SET);
switch (res->_id.type) {
case kResourceTypeAudio:
case kResourceTypeAudio36:
// Directly read the stream, compressed audio wont have resource type id and header size for SCI1.1
- loadFromAudioVolumeSCI1(res, *file);
+ loadFromAudioVolumeSCI1(res, fileStream);
+ if (res->_source->resourceFile)
+ delete fileStream;
return;
default:
break;
}
} else {
// original file, directly seek to given offset and get SCI1/SCI1.1 audio resource
- file->seek(res->_fileOffset, SEEK_SET);
+ fileStream->seek(res->_fileOffset, SEEK_SET);
}
if (getSciVersion() < SCI_VERSION_1_1)
- loadFromAudioVolumeSCI1(res, *file);
+ loadFromAudioVolumeSCI1(res, fileStream);
else
- loadFromAudioVolumeSCI11(res, *file);
+ loadFromAudioVolumeSCI11(res, fileStream);
+
+ if (res->_source->resourceFile)
+ delete fileStream;
return;
default:
- file->seek(res->_fileOffset, SEEK_SET);
- int error = decompress(res, file);
+ fileStream->seek(res->_fileOffset, SEEK_SET);
+ int error = decompress(res, fileStream);
+
+ if (res->_source->resourceFile)
+ delete fileStream;
+
if (error) {
warning("Error %d occured while reading %s from resource file: %s",
error, res->_id.toString().c_str(), sci_error_types[error]);
@@ -538,27 +447,14 @@ Resource *ResourceManager::testResource(ResourceId id) {
return _resMap.getVal(id, NULL);
}
-int sci0_get_compression_method(Common::ReadStream &stream) {
- uint16 compressionMethod;
-
- stream.readUint16LE();
- stream.readUint16LE();
- stream.readUint16LE();
- compressionMethod = stream.readUint16LE();
- if (stream.err())
- return SCI_ERROR_IO_ERROR;
-
- return compressionMethod;
-}
-
int ResourceManager::addAppropriateSources() {
Common::ArchiveMemberList files;
- if (Common::File::exists("RESOURCE.MAP")) {
+ if (Common::File::exists("resource.map")) {
// SCI0-SCI2 file naming scheme
- ResourceSource *map = addExternalMap("RESOURCE.MAP");
+ ResourceSource *map = addExternalMap("resource.map");
- SearchMan.listMatchingMembers(files, "RESOURCE.0??");
+ SearchMan.listMatchingMembers(files, "resource.0??");
for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
const Common::String name = (*x)->getName();
@@ -569,12 +465,12 @@ int ResourceManager::addAppropriateSources() {
}
#ifdef ENABLE_SCI32
// GK1CD hires content
- if (Common::File::exists("ALT.MAP") && Common::File::exists("RESOURCE.ALT"))
- addSource(addExternalMap("ALT.MAP", 10), kSourceVolume, "RESOURCE.ALT", 10);
+ if (Common::File::exists("alt.map") && Common::File::exists("resource.alt"))
+ addSource(addExternalMap("alt.map", 10), kSourceVolume, "resource.alt", 10);
#endif
} else if (Common::File::exists("Data1")) {
// Mac SCI1.1+ file naming scheme
- SearchMan.listMatchingMembers(files, "Data?");
+ SearchMan.listMatchingMembers(files, "Data?*");
for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
Common::String filename = (*x)->getName();
@@ -593,8 +489,8 @@ int ResourceManager::addAppropriateSources() {
} else {
// SCI2.1-SCI3 file naming scheme
Common::ArchiveMemberList mapFiles;
- SearchMan.listMatchingMembers(mapFiles, "RESMAP.0??");
- SearchMan.listMatchingMembers(files, "RESSCI.0??");
+ SearchMan.listMatchingMembers(mapFiles, "resmap.0??");
+ SearchMan.listMatchingMembers(files, "ressci.0??");
// We need to have the same number of maps as resource archives
if (mapFiles.empty() || files.empty() || mapFiles.size() != files.size())
@@ -616,9 +512,9 @@ int ResourceManager::addAppropriateSources() {
}
// SCI2.1 resource patches
- if (Common::File::exists("RESMAP.PAT") && Common::File::exists("RESSCI.PAT")) {
+ if (Common::File::exists("resmap.pat") && Common::File::exists("ressci.pat")) {
// We add this resource with a map which surely won't exist
- addSource(addExternalMap("RESMAP.PAT", 100), kSourceVolume, "RESSCI.PAT", 100);
+ addSource(addExternalMap("resmap.pat", 100), kSourceVolume, "ressci.pat", 100);
}
}
#else
@@ -627,14 +523,18 @@ int ResourceManager::addAppropriateSources() {
#endif
addPatchDir(".");
- if (Common::File::exists("MESSAGE.MAP"))
- addSource(addExternalMap("MESSAGE.MAP"), kSourceVolume, "RESOURCE.MSG", 0);
+ if (Common::File::exists("message.map"))
+ addSource(addExternalMap("message.map"), kSourceVolume, "resource.msg", 0);
return 1;
}
int ResourceManager::addAppropriateSources(const Common::FSList &fslist) {
ResourceSource *map = 0;
+#ifdef ENABLE_SCI32
+ ResourceSource *sci21PatchMap = 0;
+ const Common::FSNode *sci21PatchRes = 0;
+#endif
// First, find resource.map
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
@@ -644,17 +544,33 @@ int ResourceManager::addAppropriateSources(const Common::FSList &fslist) {
Common::String filename = file->getName();
filename.toLowercase();
- // TODO: Load the SCI2.1+ maps (resmap.*) in concurrence with the volumes to
- // get the proper volume numbers from the maps.
- if (filename.contains("resource.map") || filename.contains("resmap.000")) {
+ if (filename.contains("resource.map"))
map = addExternalMap(file);
- break;
+
+ if (filename.contains("resmap.0")) {
+ const char *dot = strrchr(file->getName().c_str(), '.');
+ int number = atoi(dot + 1);
+ map = addExternalMap(file, number);
}
+
+#ifdef ENABLE_SCI32
+ // SCI2.1 resource patches
+ if (filename.contains("resmap.pat"))
+ sci21PatchMap = addExternalMap(file, 100);
+
+ if (filename.contains("ressci.pat"))
+ sci21PatchRes = file;
+#endif
}
if (!map)
return 0;
+#ifdef ENABLE_SCI32
+ if (sci21PatchMap && sci21PatchRes)
+ addSource(sci21PatchMap, kSourceVolume, sci21PatchRes, 100);
+#endif
+
// Now find all the resource.0?? files
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
if (file->isDirectory())
@@ -695,36 +611,6 @@ int ResourceManager::addInternalSources() {
return 1;
}
-void ResourceManager::addNewGMPatch(const Common::String &gameId) {
- Common::String gmPatchFile;
-
- if (gameId == "ecoquest")
- gmPatchFile = "ECO1GM.PAT";
- else if (gameId == "hoyle3")
- gmPatchFile = "HOY3GM.PAT";
- else if (gameId == "hoyle3")
- gmPatchFile = "HOY3GM.PAT";
- else if (gameId == "lsl1sci")
- gmPatchFile = "LL1_GM.PAT";
- else if (gameId == "lsl5")
- gmPatchFile = "LL5_GM.PAT";
- else if (gameId == "longbow")
- gmPatchFile = "ROBNGM.PAT";
- else if (gameId == "sq1sci")
- gmPatchFile = "SQ1_GM.PAT";
- else if (gameId == "sq4")
- gmPatchFile = "SQ4_GM.PAT";
- else if (gameId == "fairytales")
- gmPatchFile = "TALEGM.PAT";
-
- if (!gmPatchFile.empty() && Common::File::exists(gmPatchFile)) {
- ResourceSource *psrcPatch = new ResourceSource;
- psrcPatch->source_type = kSourcePatch;
- psrcPatch->location_name = gmPatchFile;
- processPatch(psrcPatch, kResourceTypePatch, 4);
- }
-}
-
void ResourceManager::scanNewSources() {
for (Common::List<ResourceSource *>::iterator it = _sources.begin(); it != _sources.end(); ++it) {
ResourceSource *source = *it;
@@ -734,6 +620,9 @@ void ResourceManager::scanNewSources() {
switch (source->source_type) {
case kSourceDirectory:
readResourcePatches(source);
+#ifdef ENABLE_SCI32
+ readResourcePatchesBase36(source);
+#endif
readWaveAudioPatches();
break;
case kSourceExtMap:
@@ -989,7 +878,6 @@ const char *ResourceManager::versionDescription(ResVersion version) const {
ResourceManager::ResVersion ResourceManager::detectMapVersion() {
Common::SeekableReadStream *fileStream = 0;
- Common::File *file = 0;
byte buff[6];
ResourceSource *rsrc= 0;
@@ -1000,7 +888,7 @@ ResourceManager::ResVersion ResourceManager::detectMapVersion() {
if (rsrc->resourceFile) {
fileStream = rsrc->resourceFile->createReadStream();
} else {
- file = new Common::File();
+ Common::File *file = new Common::File();
file->open(rsrc->location_name);
if (file->isOpen())
fileStream = file;
@@ -1080,7 +968,6 @@ ResourceManager::ResVersion ResourceManager::detectMapVersion() {
ResourceManager::ResVersion ResourceManager::detectVolVersion() {
Common::SeekableReadStream *fileStream = 0;
- Common::File *file = 0;
ResourceSource *rsrc;
for (Common::List<ResourceSource *>::iterator it = _sources.begin(); it != _sources.end(); ++it) {
@@ -1090,7 +977,7 @@ ResourceManager::ResVersion ResourceManager::detectVolVersion() {
if (rsrc->resourceFile) {
fileStream = rsrc->resourceFile->createReadStream();
} else {
- file = new Common::File();
+ Common::File *file = new Common::File();
file->open(rsrc->location_name);
if (file->isOpen())
fileStream = file;
@@ -1179,49 +1066,66 @@ ResourceManager::ResVersion ResourceManager::detectVolVersion() {
}
// version-agnostic patch application
-void ResourceManager::processPatch(ResourceSource *source, ResourceType restype, int resnumber) {
- Common::File file;
+void ResourceManager::processPatch(ResourceSource *source, ResourceType resourceType, uint16 resourceNr, uint32 tuple) {
+ Common::SeekableReadStream *fileStream = 0;
Resource *newrsc;
- ResourceId resId = ResourceId(restype, resnumber);
- byte patchtype, patch_data_offset;
+ ResourceId resId = ResourceId(resourceType, resourceNr, tuple);
+ byte patchType, patchDataOffset;
int fsize;
- if (resnumber == -1)
- return;
- if (!file.open(source->location_name)) {
- warning("ResourceManager::processPatch(): failed to open %s", source->location_name.c_str());
- return;
+ // base36 encoded patches (i.e. audio36 and sync36) have the same type as their non-base36 encoded counterparts
+ if (resourceType == kResourceTypeAudio36)
+ resourceType = kResourceTypeAudio;
+
+ if (resourceType == kResourceTypeSync36)
+ resourceType = kResourceTypeSync;
+
+ if (source->resourceFile) {
+ fileStream = source->resourceFile->createReadStream();
+ } else {
+ Common::File *file = new Common::File();
+ if (!file->open(source->location_name)) {
+ warning("ResourceManager::processPatch(): failed to open %s", source->location_name.c_str());
+ return;
+ }
+ fileStream = file;
}
- fsize = file.size();
+ fsize = fileStream->size();
if (fsize < 3) {
debug("Patching %s failed - file too small", source->location_name.c_str());
return;
}
- patchtype = file.readByte() & 0x7F;
- patch_data_offset = file.readByte();
+ patchType = fileStream->readByte() & 0x7F;
+ patchDataOffset = fileStream->readByte();
- if (patchtype != restype) {
+ delete fileStream;
+
+ if (patchType != resourceType) {
debug("Patching %s failed - resource type mismatch", source->location_name.c_str());
}
// Fixes SQ5/German, patch file special case logic taken from SCI View disassembly
- if (patch_data_offset & 0x80) {
- switch (patch_data_offset & 0x7F) {
+ if (patchDataOffset & 0x80) {
+ switch (patchDataOffset & 0x7F) {
case 0:
- patch_data_offset = 24;
+ patchDataOffset = 24;
break;
case 1:
- patch_data_offset = 2;
+ patchDataOffset = 2;
+ break;
+ case 4:
+ patchDataOffset = 8;
break;
default:
- warning("Resource patch unsupported special case %X", patch_data_offset);
+ warning("Resource patch unsupported special case %X", patchDataOffset & 0x7F);
+ return;
}
}
- if (patch_data_offset + 2 >= fsize) {
+ if (patchDataOffset + 2 >= fsize) {
debug("Patching %s failed - patch starting at offset %d can't be in file of size %d",
- source->location_name.c_str(), patch_data_offset + 2, fsize);
+ source->location_name.c_str(), patchDataOffset + 2, fsize);
return;
}
// Prepare destination, if neccessary
@@ -1234,29 +1138,91 @@ void ResourceManager::processPatch(ResourceSource *source, ResourceType restype,
newrsc->_id = resId;
newrsc->_status = kResStatusNoMalloc;
newrsc->_source = source;
- newrsc->size = fsize - patch_data_offset - 2;
- newrsc->_headerSize = patch_data_offset;
+ newrsc->size = fsize - patchDataOffset - 2;
+ newrsc->_headerSize = patchDataOffset;
newrsc->_fileOffset = 0;
debugC(1, kDebugLevelResMan, "Patching %s - OK", source->location_name.c_str());
}
+#ifdef ENABLE_SCI32
+
+void ResourceManager::readResourcePatchesBase36(ResourceSource *source) {
+ // The base36 encoded audio36 and sync36 resources use a different naming scheme, because they
+ // cannot be described with a single resource number, but are a result of a
+ // <number, noun, verb, cond, seq> tuple. Please don't be confused with the normal audio patches
+ // (*.aud) and normal sync patches (*.syn). audio36 patches can be seen for example in the AUD
+ // folder of GK1CD, and are like this file: @0CS0M00.0X1. GK1CD is the first game where these
+ // have been observed. The actual audio36 and sync36 resources exist in SCI1.1 as well, but the
+ // first game where external patch files for them have been found is GK1CD. The names of these
+ // files are base36 encoded, and we handle their decoding here. audio36 files start with a '@',
+ // whereas sync36 start with a '#'. Mac versions begin with 'A' (probably meaning AIFF). Torin
+ // has several that begin with 'B'.
+
+ Common::String name, inputName;
+ Common::ArchiveMemberList files;
+ ResourceSource *psrcPatch;
+
+ for (int i = kResourceTypeAudio36; i <= kResourceTypeSync36; ++i) {
+ files.clear();
+
+ // audio36 resources start with a @, A, or B
+ // sync36 resources start with a #
+ if (i == kResourceTypeAudio36) {
+ SearchMan.listMatchingMembers(files, "@???????.???");
+ SearchMan.listMatchingMembers(files, "A???????.???");
+ SearchMan.listMatchingMembers(files, "B???????.???");
+ } else
+ SearchMan.listMatchingMembers(files, "#???????.???");
+
+ for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
+ name = (*x)->getName();
+ inputName = (*x)->getName();
+ inputName.toUppercase();
+ if (inputName.hasPrefix("BOOT")) // skip bootdisk.*
+ continue;
+
+ inputName.deleteChar(0); // delete the first character (type)
+ inputName.deleteChar(7); // delete the dot
+
+ // The base36 encoded resource contains the following:
+ // uint16 resourceId, byte noun, byte verb, byte cond, byte seq
+ uint16 resourceNr = strtol(Common::String(inputName.c_str(), 3).c_str(), 0, 36); // 3 characters
+ byte noun = strtol(Common::String(inputName.c_str() + 3, 2).c_str(), 0, 36); // 2 characters
+ byte verb = strtol(Common::String(inputName.c_str() + 5, 2).c_str(), 0, 36); // 2 characters
+ byte cond = strtol(Common::String(inputName.c_str() + 7, 2).c_str(), 0, 36); // 2 characters
+ byte seq = strtol(Common::String(inputName.c_str() + 9, 1).c_str(), 0, 36); // 1 character
+ ResourceId resource36((ResourceType)i, resourceNr, noun, verb, cond, seq);
+
+ /*
+ if (i == kResourceTypeAudio36)
+ debug("audio36 patch: %s => %s. tuple:%d, %s\n", name.c_str(), inputName.c_str(), resource36.tuple, resource36.toString().c_str());
+ else
+ debug("sync36 patch: %s => %s. tuple:%d, %s\n", name.c_str(), inputName.c_str(), resource36.tuple, resource36.toString().c_str());
+ */
+
+ psrcPatch = new ResourceSource;
+ psrcPatch->source_type = kSourcePatch;
+ psrcPatch->location_name = name;
+ psrcPatch->resourceFile = 0;
+ processPatch(psrcPatch, (ResourceType)i, resourceNr, resource36.tuple);
+ }
+ }
+}
+
+#endif
void ResourceManager::readResourcePatches(ResourceSource *source) {
-// Note: since some SCI1 games(KQ5 floppy, SQ4) might use SCI0 naming scheme for patch files
-// this function tries to read patch file with any supported naming scheme,
-// regardless of s_sciVersion value
+ // Note: since some SCI1 games(KQ5 floppy, SQ4) might use SCI0 naming scheme for patch files
+ // this function tries to read patch file with any supported naming scheme,
+ // regardless of s_sciVersion value
Common::String mask, name;
Common::ArchiveMemberList files;
- int number = -1;
+ uint16 resourceNr = 0;
const char *szResType;
ResourceSource *psrcPatch;
- for (int i = kResourceTypeView; i <= kResourceTypeRobot; ++i) {
- // TODO: add support for audio36 and sync36 files
- if (i == kResourceTypeAudio36 || i == kResourceTypeSync36)
- continue;
-
+ for (int i = kResourceTypeView; i <= kResourceTypeHeap; ++i) {
files.clear();
szResType = getResourceTypeName((ResourceType)i);
// SCI0 naming - type.nnn
@@ -1267,19 +1233,20 @@ void ResourceManager::readResourcePatches(ResourceSource *source) {
mask = "*.";
mask += resourceTypeSuffixes[i];
SearchMan.listMatchingMembers(files, mask);
+
for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
bool bAdd = false;
name = (*x)->getName();
// SCI1 scheme
if (isdigit(name[0])) {
- number = atoi(name.c_str());
+ resourceNr = atoi(name.c_str());
bAdd = true;
} else {
// SCI0 scheme
int resname_len = strlen(szResType);
if (scumm_strnicmp(name.c_str(), szResType, resname_len) == 0
&& !isalpha(name[resname_len + 1])) {
- number = atoi(name.c_str() + resname_len + 1);
+ resourceNr = atoi(name.c_str() + resname_len + 1);
bAdd = true;
}
}
@@ -1288,75 +1255,42 @@ void ResourceManager::readResourcePatches(ResourceSource *source) {
psrcPatch = new ResourceSource;
psrcPatch->source_type = kSourcePatch;
psrcPatch->location_name = name;
- processPatch(psrcPatch, (ResourceType)i, number);
+ psrcPatch->resourceFile = 0;
+ processPatch(psrcPatch, (ResourceType)i, resourceNr);
}
}
}
}
-void ResourceManager::readWaveAudioPatches() {
- // Here we do check for SCI1.1+ so we can patch wav files in as audio resources
- Common::ArchiveMemberList files;
- SearchMan.listMatchingMembers(files, "*.wav");
-
- for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
- Common::String name = (*x)->getName();
-
- if (isdigit(name[0])) {
- int number = atoi(name.c_str());
- ResourceSource *psrcPatch = new ResourceSource;
- psrcPatch->source_type = kSourceWave;
- psrcPatch->location_name = name;
- psrcPatch->volume_number = 0;
- psrcPatch->audioCompressionType = 0;
-
- ResourceId resId = ResourceId(kResourceTypeAudio, number);
-
- Resource *newrsc = NULL;
-
- // Prepare destination, if neccessary
- if (_resMap.contains(resId) == false) {
- newrsc = new Resource;
- _resMap.setVal(resId, newrsc);
- } else
- newrsc = _resMap.getVal(resId);
-
- // Get the size of the file
- Common::SeekableReadStream *stream = (*x)->createReadStream();
- uint32 fileSize = stream->size();
- delete stream;
-
- // Overwrite everything, because we're patching
- newrsc->_id = resId;
- newrsc->_status = kResStatusNoMalloc;
- newrsc->_source = psrcPatch;
- newrsc->size = fileSize;
- newrsc->_headerSize = 0;
- debugC(1, kDebugLevelResMan, "Patching %s - OK", psrcPatch->location_name.c_str());
- }
- }
-}
-
int ResourceManager::readResourceMapSCI0(ResourceSource *map) {
- Common::File file;
+ Common::SeekableReadStream *fileStream = 0;
Resource *res;
ResourceType type;
uint16 number, id;
uint32 offset;
- if (!file.open(map->location_name))
- return SCI_ERROR_RESMAP_NOT_FOUND;
+ if (map->resourceFile) {
+ fileStream = map->resourceFile->createReadStream();
+ if (!fileStream)
+ return SCI_ERROR_RESMAP_NOT_FOUND;
+ } else {
+ Common::File *file = new Common::File();
+ if (!file->open(map->location_name))
+ return SCI_ERROR_RESMAP_NOT_FOUND;
+ fileStream = file;
+ }
- file.seek(0, SEEK_SET);
+ fileStream->seek(0, SEEK_SET);
byte bMask = (_mapVersion == kResVersionSci1Middle) ? 0xF0 : 0xFC;
byte bShift = (_mapVersion == kResVersionSci1Middle) ? 28 : 26;
do {
- id = file.readUint16LE();
- offset = file.readUint32LE();
+ id = fileStream->readUint16LE();
+ offset = fileStream->readUint32LE();
- if (file.eos() || file.err()) {
+ if (fileStream->eos() || fileStream->err()) {
+ delete fileStream;
warning("Error while reading %s", map->location_name.c_str());
return SCI_ERROR_RESMAP_NOT_FOUND;
}
@@ -1385,15 +1319,26 @@ int ResourceManager::readResourceMapSCI0(ResourceSource *map) {
res->_id = resId;
_resMap.setVal(resId, res);
}
- } while (!file.eos());
+ } while (!fileStream->eos());
+
+ delete fileStream;
return 0;
}
int ResourceManager::readResourceMapSCI1(ResourceSource *map) {
- Common::File file;
+ Common::SeekableReadStream *fileStream = 0;
Resource *res;
- if (!file.open(map->location_name))
- return SCI_ERROR_RESMAP_NOT_FOUND;
+
+ if (map->resourceFile) {
+ fileStream = map->resourceFile->createReadStream();
+ if (!fileStream)
+ return SCI_ERROR_RESMAP_NOT_FOUND;
+ } else {
+ Common::File *file = new Common::File();
+ if (!file->open(map->location_name))
+ return SCI_ERROR_RESMAP_NOT_FOUND;
+ fileStream = file;
+ }
resource_index_t resMap[32];
memset(resMap, 0, sizeof(resource_index_t) * 32);
@@ -1404,8 +1349,8 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) {
// Read resource type and offsets to resource offsets block from .MAP file
// The last entry has type=0xFF (0x1F) and offset equals to map file length
do {
- type = file.readByte() & 0x1F;
- resMap[type].wOffset = file.readUint16LE();
+ type = fileStream->readByte() & 0x1F;
+ resMap[type].wOffset = fileStream->readUint16LE();
resMap[prevtype].wSize = (resMap[type].wOffset
- resMap[prevtype].wOffset) / nEntrySize;
prevtype = type;
@@ -1416,18 +1361,18 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) {
for (type = 0; type < 32; type++) {
if (resMap[type].wOffset == 0) // this resource does not exist in map
continue;
- file.seek(resMap[type].wOffset);
+ fileStream->seek(resMap[type].wOffset);
for (int i = 0; i < resMap[type].wSize; i++) {
- uint16 number = file.readUint16LE();
+ uint16 number = fileStream->readUint16LE();
int volume_nr = 0;
if (_mapVersion == kResVersionSci11) {
// offset stored in 3 bytes
- off = file.readUint16LE();
- off |= file.readByte() << 16;
+ off = fileStream->readUint16LE();
+ off |= fileStream->readByte() << 16;
off <<= 1;
} else {
// offset/volume stored in 4 bytes
- off = file.readUint32LE();
+ off = fileStream->readUint32LE();
if (_mapVersion < kResVersionSci11) {
volume_nr = off >> 28; // most significant 4 bits
off &= 0x0FFFFFFF; // least significant 28 bits
@@ -1435,7 +1380,8 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) {
// in SCI32 it's a plain offset
}
}
- if (file.eos() || file.err()) {
+ if (fileStream->eos() || fileStream->err()) {
+ delete fileStream;
warning("Error while reading %s", map->location_name.c_str());
return SCI_ERROR_RESMAP_NOT_FOUND;
}
@@ -1447,7 +1393,7 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) {
res->_id = resId;
// NOTE: We add the map's volume number here to the specified volume number
- // for SCI2.1 and SCI3 maps that are not RESMAP.000. The RESMAP.* files' numbers
+ // for SCI2.1 and SCI3 maps that are not resmap.000. The resmap.* files' numbers
// need to be used in concurrence with the volume specified in the map to get
// the actual resource file.
res->_source = getVolume(map, volume_nr + map->volume_number);
@@ -1455,6 +1401,8 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) {
}
}
}
+
+ delete fileStream;
return 0;
}
@@ -1478,7 +1426,8 @@ struct {
{ MKID_BE('HEP '), kResourceTypeHeap },
{ MKID_BE('IBIN'), kResourceTypeMacIconBarPictN },
{ MKID_BE('IBIS'), kResourceTypeMacIconBarPictS },
- { MKID_BE('PICT'), kResourceTypeMacPict }
+ { MKID_BE('PICT'), kResourceTypeMacPict },
+ { MKID_BE('SYN '), kResourceTypeSync }
};
static uint32 resTypeToMacTag(ResourceType type) {
@@ -1556,261 +1505,6 @@ void ResourceManager::addResource(ResourceId resId, ResourceSource *src, uint32
}
}
-void ResourceManager::removeAudioResource(ResourceId resId) {
- // Remove resource, unless it was loaded from a patch
- if (_resMap.contains(resId)) {
- Resource *res = _resMap.getVal(resId);
-
- if (res->_source->source_type == kSourceAudioVolume) {
- if (res->_status == kResStatusLocked) {
- warning("Failed to remove resource %s (still in use)", resId.toString().c_str());
- } else {
- if (res->_status == kResStatusEnqueued)
- removeFromLRU(res);
-
- _resMap.erase(resId);
- delete res;
- }
- }
- }
-}
-
-// Early SCI1.1 65535.MAP structure (uses RESOURCE.AUD):
-// =========
-// 6-byte entries:
-// w nEntry
-// dw offset
-
-// Late SCI1.1 65535.MAP structure (uses RESOURCE.SFX):
-// =========
-// 5-byte entries:
-// w nEntry
-// tb offset (cumulative)
-
-// Early SCI1.1 MAP structure:
-// ===============
-// 10-byte entries:
-// b noun
-// b verb
-// b cond
-// b seq
-// dw offset
-// w syncSize + syncAscSize
-
-// Late SCI1.1 MAP structure:
-// ===============
-// Header:
-// dw baseOffset
-// Followed by 7 or 11-byte entries:
-// b noun
-// b verb
-// b cond
-// b seq
-// tb cOffset (cumulative offset)
-// w syncSize (iff seq has bit 7 set)
-// w syncAscSize (iff seq has bit 6 set)
-
-int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
- bool isEarly = true;
- uint32 offset = 0;
- Resource *mapRes = findResource(ResourceId(kResourceTypeMap, map->volume_number), false);
-
- if (!mapRes) {
- warning("Failed to open %i.MAP", map->volume_number);
- return SCI_ERROR_RESMAP_NOT_FOUND;
- }
-
- ResourceSource *src = getVolume(map, 0);
-
- if (!src)
- return SCI_ERROR_NO_RESOURCE_FILES_FOUND;
-
- byte *ptr = mapRes->data;
-
- if (map->volume_number == 65535) {
- // Heuristic to detect late SCI1.1 map format
- if ((mapRes->size >= 6) && (ptr[mapRes->size - 6] != 0xff))
- isEarly = false;
-
- while (ptr < mapRes->data + mapRes->size) {
- uint16 n = READ_LE_UINT16(ptr);
- ptr += 2;
-
- if (n == 0xffff)
- break;
-
- if (isEarly) {
- offset = READ_LE_UINT32(ptr);
- ptr += 4;
- } else {
- offset += READ_LE_UINT24(ptr);
- ptr += 3;
- }
-
- addResource(ResourceId(kResourceTypeAudio, n), src, offset);
- }
- } else {
- // Heuristic to detect late SCI1.1 map format
- if ((mapRes->size >= 11) && (ptr[mapRes->size - 11] == 0xff))
- isEarly = false;
-
- if (!isEarly) {
- offset = READ_LE_UINT32(ptr);
- ptr += 4;
- }
-
- while (ptr < mapRes->data + mapRes->size) {
- uint32 n = READ_BE_UINT32(ptr);
- int syncSize = 0;
- ptr += 4;
-
- if (n == 0xffffffff)
- break;
-
- if (isEarly) {
- offset = READ_LE_UINT32(ptr);
- ptr += 4;
- } else {
- offset += READ_LE_UINT24(ptr);
- ptr += 3;
- }
-
- if (isEarly || (n & 0x80)) {
- syncSize = READ_LE_UINT16(ptr);
- ptr += 2;
-
- if (syncSize > 0)
- addResource(ResourceId(kResourceTypeSync36, map->volume_number, n & 0xffffff3f), src, offset, syncSize);
- }
-
- if (n & 0x40) {
- syncSize += READ_LE_UINT16(ptr);
- ptr += 2;
- }
-
- addResource(ResourceId(kResourceTypeAudio36, map->volume_number, n & 0xffffff3f), src, offset + syncSize);
- }
- }
-
- return 0;
-}
-
-// AUDIOnnn.MAP contains 10-byte entries:
-// Early format:
-// w 5 bits resource type and 11 bits resource number
-// dw 7 bits volume number and 25 bits offset
-// dw size
-// Later format:
-// w nEntry
-// dw offset+volume (as in resource.map)
-// dw size
-// ending with 10 0xFFs
-int ResourceManager::readAudioMapSCI1(ResourceSource *map, bool unload) {
- Common::File file;
-
- if (!file.open(map->location_name))
- return SCI_ERROR_RESMAP_NOT_FOUND;
-
- bool oldFormat = (file.readUint16LE() >> 11) == kResourceTypeAudio;
- file.seek(0);
-
- while (1) {
- uint16 n = file.readUint16LE();
- uint32 offset = file.readUint32LE();
- uint32 size = file.readUint32LE();
-
- if (file.eos() || file.err()) {
- warning("Error while reading %s", map->location_name.c_str());
- return SCI_ERROR_RESMAP_NOT_FOUND;
- }
-
- if (n == 0xffff)
- break;
-
- byte volume_nr;
-
- if (oldFormat) {
- n &= 0x07ff; // Mask out resource type
- volume_nr = offset >> 25; // most significant 7 bits
- offset &= 0x01ffffff; // least significant 25 bits
- } else {
- volume_nr = offset >> 28; // most significant 4 bits
- offset &= 0x0fffffff; // least significant 28 bits
- }
-
- ResourceSource *src = getVolume(map, volume_nr);
-
- if (src) {
- if (unload)
- removeAudioResource(ResourceId(kResourceTypeAudio, n));
- else
- addResource(ResourceId(kResourceTypeAudio, n), src, offset, size);
- } else {
- warning("Failed to find audio volume %i", volume_nr);
- }
- }
-
- return 0;
-}
-
-void ResourceManager::setAudioLanguage(int language) {
- if (_audioMapSCI1) {
- if (_audioMapSCI1->volume_number == language) {
- // This language is already loaded
- return;
- }
-
- // We already have a map loaded, so we unload it first
- readAudioMapSCI1(_audioMapSCI1, true);
-
- // Remove all volumes that use this map from the source list
- Common::List<ResourceSource *>::iterator it = _sources.begin();
- while (it != _sources.end()) {
- ResourceSource *src = *it;
- if (src->associated_map == _audioMapSCI1) {
- it = _sources.erase(it);
- delete src;
- } else {
- ++it;
- }
- }
-
- // Remove the map itself from the source list
- _sources.remove(_audioMapSCI1);
- delete _audioMapSCI1;
-
- _audioMapSCI1 = NULL;
- }
-
- char filename[9];
- snprintf(filename, 9, "AUDIO%03d", language);
-
- Common::String fullname = Common::String(filename) + ".MAP";
- if (!Common::File::exists(fullname)) {
- warning("No audio map found for language %i", language);
- return;
- }
-
- _audioMapSCI1 = addSource(NULL, kSourceExtAudioMap, fullname.c_str(), language);
-
- // Search for audio volumes for this language and add them to the source list
- Common::ArchiveMemberList files;
- SearchMan.listMatchingMembers(files, Common::String(filename) + ".0??");
- for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
- const Common::String name = (*x)->getName();
- const char *dot = strrchr(name.c_str(), '.');
- int number = atoi(dot + 1);
-
- addSource(_audioMapSCI1, kSourceAudioVolume, name.c_str(), number);
- }
-
- scanNewSources();
-}
-
-int ResourceManager::getAudioLanguage() const {
- return (_audioMapSCI1 ? _audioMapSCI1->volume_number : 0);
-}
-
int ResourceManager::readResourceInfo(Resource *res, Common::SeekableReadStream *file,
uint32&szPacked, ResourceCompression &compression) {
// SCI0 volume format: {wResId wPacked+4 wUnpacked wCompression} = 8 bytes
@@ -1961,7 +1655,7 @@ ResourceCompression ResourceManager::getViewCompression() {
// Test 10 views to see if any are compressed
for (int i = 0; i < 1000; i++) {
- Common::File *file;
+ Common::SeekableReadStream *fileStream = 0;
Resource *res = testResource(ResourceId(kResourceTypeView, i));
if (!res)
@@ -1970,16 +1664,23 @@ ResourceCompression ResourceManager::getViewCompression() {
if (res->_source->source_type != kSourceVolume)
continue;
- file = getVolumeFile(res->_source->location_name.c_str());
- if (!file)
+ fileStream = getVolumeFile(res->_source);
+
+ if (!fileStream)
continue;
- file->seek(res->_fileOffset, SEEK_SET);
+ fileStream->seek(res->_fileOffset, SEEK_SET);
uint32 szPacked;
ResourceCompression compression;
- if (readResourceInfo(res, file, szPacked, compression))
+ if (readResourceInfo(res, fileStream, szPacked, compression)) {
+ if (res->_source->resourceFile)
+ delete fileStream;
continue;
+ }
+
+ if (res->_source->resourceFile)
+ delete fileStream;
if (compression != kCompNone)
return compression;
@@ -2336,283 +2037,98 @@ bool ResourceManager::hasSci1Voc900() {
return offset == res->size;
}
-#define READ_UINT16(ptr) (!isSci11Mac() ? READ_LE_UINT16(ptr) : READ_BE_UINT16(ptr))
+// Same function as Script::findBlock(). Slight code
+// duplication here, but this has been done to keep the resource
+// manager independent from the rest of the engine
+static byte *findSci0ExportsBlock(byte *buffer) {
+ byte *buf = buffer;
+ bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
-Common::String ResourceManager::findSierraGameId() {
- Resource *script = findResource(ResourceId(kResourceTypeScript, 0), false);
- // In SCI0-SCI1, the heap is embedded in the script. In SCI1.1+, it's separated
- Resource *heap = 0;
- byte *seeker = 0;
+ if (oldScriptHeader)
+ buf += 2;
- // Seek to the name selector of the first export
- if (getSciVersion() < SCI_VERSION_1_1) {
- const int nameSelector = 3;
- int extraSci0EarlyBytes = (getSciVersion() == SCI_VERSION_0_EARLY) ? 2 : 0;
- byte *exportPtr = script->data + extraSci0EarlyBytes + 4 + 2;
- seeker = script->data + READ_UINT16(script->data + READ_UINT16(exportPtr) + nameSelector * 2);
- } else {
- const int nameSelector = 5 + 3;
- heap = findResource(ResourceId(kResourceTypeHeap, 0), false);
- byte *exportPtr = script->data + 4 + 2 + 2;
- seeker = heap->data + READ_UINT16(heap->data + READ_UINT16(exportPtr) + nameSelector * 2);
- }
+ do {
+ int seekerType = READ_LE_UINT16(buf);
- char sierraId[20];
- int i = 0;
- byte curChar = 0;
+ if (seekerType == 0)
+ break;
+ if (seekerType == 7) // exports
+ return buf;
- do {
- curChar = *(seeker + i);
- sierraId[i++] = curChar;
- } while (curChar != 0);
+ int seekerSize = READ_LE_UINT16(buf + 2);
+ assert(seekerSize > 0);
+ buf += seekerSize;
+ } while (1);
- return sierraId;
+ return NULL;
}
-#undef READ_UINT16
+reg_t ResourceManager::findGameObject(bool addSci11ScriptOffset) {
+ Resource *script = findResource(ResourceId(kResourceTypeScript, 0), false);
-SoundResource::SoundResource(uint32 resNumber, ResourceManager *resMan, SciVersion soundVersion) : _resMan(resMan), _soundVersion(soundVersion) {
- Resource *resource = _resMan->findResource(ResourceId(kResourceTypeSound, resNumber), true);
- int trackNr, channelNr;
- if (!resource)
- return;
+ if (!script)
+ return NULL_REG;
- _innerResource = resource;
+ byte *offsetPtr = 0;
- byte *data, *data2;
- byte *dataEnd;
- Channel *channel, *sampleChannel;
+ if (getSciVersion() < SCI_VERSION_1_1) {
+ byte *buf = (getSciVersion() == SCI_VERSION_0_EARLY) ? script->data + 2 : script->data;
- switch (_soundVersion) {
- case SCI_VERSION_0_EARLY:
- case SCI_VERSION_0_LATE:
- // SCI0 only has a header of 0x11/0x21 byte length and the actual midi track follows afterwards
- _trackCount = 1;
- _tracks = new Track[_trackCount];
- _tracks->digitalChannelNr = -1;
- _tracks->type = 0; // Not used for SCI0
- _tracks->channelCount = 1;
- // Digital sample data included? -> Add an additional channel
- if (resource->data[0] == 2)
- _tracks->channelCount++;
- _tracks->channels = new Channel[_tracks->channelCount];
- memset(_tracks->channels, 0, sizeof(Channel) * _tracks->channelCount);
- channel = &_tracks->channels[0];
- if (_soundVersion == SCI_VERSION_0_EARLY) {
- channel->data = resource->data + 0x11;
- channel->size = resource->size - 0x11;
+ // Check if the first block is the exports block (in most cases, it is)
+ bool exportsIsFirst = (READ_LE_UINT16(buf + 4) == 7);
+ if (exportsIsFirst) {
+ offsetPtr = buf + 4 + 2;
} else {
- channel->data = resource->data + 0x21;
- channel->size = resource->size - 0x21;
- }
- if (_tracks->channelCount == 2) {
- // Digital sample data included
- _tracks->digitalChannelNr = 1;
- sampleChannel = &_tracks->channels[1];
- // we need to find 0xFC (channel terminator) within the data
- data = channel->data;
- dataEnd = channel->data + channel->size;
- while ((data < dataEnd) && (*data != 0xfc))
- data++;
- // Skip any following 0xFCs as well
- while ((data < dataEnd) && (*data == 0xfc))
- data++;
- // Now adjust channels accordingly
- sampleChannel->data = data;
- sampleChannel->size = channel->size - (data - channel->data);
- channel->size = data - channel->data;
- // Read sample header information
- //Offset 14 in the header contains the frequency as a short integer. Offset 32 contains the sample length, also as a short integer.
- _tracks->digitalSampleRate = READ_LE_UINT16(sampleChannel->data + 14);
- _tracks->digitalSampleSize = READ_LE_UINT16(sampleChannel->data + 32);
- _tracks->digitalSampleStart = 0;
- _tracks->digitalSampleEnd = 0;
- sampleChannel->data += 44; // Skip over header
- sampleChannel->size -= 44;
- }
- break;
-
- case SCI_VERSION_1_EARLY:
- case SCI_VERSION_1_LATE:
- data = resource->data;
- // Count # of tracks
- _trackCount = 0;
- while ((*data++) != 0xFF) {
- _trackCount++;
- while (*data != 0xFF)
- data += 6;
- data++;
+ offsetPtr = findSci0ExportsBlock(script->data);
+ if (!offsetPtr)
+ error("Unable to find exports block from script 0");
+ offsetPtr += 4 + 2;
}
- _tracks = new Track[_trackCount];
- data = resource->data;
- for (trackNr = 0; trackNr < _trackCount; trackNr++) {
- // Track info starts with track type:BYTE
- // Then the channel information gets appended Unknown:WORD, ChannelOffset:WORD, ChannelSize:WORD
- // 0xFF:BYTE as terminator to end that track and begin with another track type
- // Track type 0xFF is the marker signifying the end of the tracks
-
- _tracks[trackNr].type = *data++;
- // Counting # of channels used
- data2 = data;
- _tracks[trackNr].channelCount = 0;
- while (*data2 != 0xFF) {
- data2 += 6;
- _tracks[trackNr].channelCount++;
- }
- _tracks[trackNr].channels = new Channel[_tracks[trackNr].channelCount];
- _tracks[trackNr].digitalChannelNr = -1; // No digital sound associated
- _tracks[trackNr].digitalSampleRate = 0;
- _tracks[trackNr].digitalSampleSize = 0;
- _tracks[trackNr].digitalSampleStart = 0;
- _tracks[trackNr].digitalSampleEnd = 0;
- if (_tracks[trackNr].type != 0xF0) { // Digital track marker - not supported currently
- for (channelNr = 0; channelNr < _tracks[trackNr].channelCount; channelNr++) {
- channel = &_tracks[trackNr].channels[channelNr];
- channel->prio = READ_LE_UINT16(data);
- channel->data = resource->data + READ_LE_UINT16(data + 2) + 2;
- channel->size = READ_LE_UINT16(data + 4) - 2; // Not counting channel header
- channel->number = *(channel->data - 2);
- channel->poly = *(channel->data - 1);
- channel->time = channel->prev = 0;
- if (channel->number == 0xFE) { // Digital channel
- _tracks[trackNr].digitalChannelNr = channelNr;
- _tracks[trackNr].digitalSampleRate = READ_LE_UINT16(channel->data);
- _tracks[trackNr].digitalSampleSize = READ_LE_UINT16(channel->data + 2);
- _tracks[trackNr].digitalSampleStart = READ_LE_UINT16(channel->data + 4);
- _tracks[trackNr].digitalSampleEnd = READ_LE_UINT16(channel->data + 6);
- channel->data += 8; // Skip over header
- channel->size -= 8;
- }
- data += 6;
- }
- } else {
- // Skip over digital track
- data += 6;
- }
- data++; // Skipping 0xFF that closes channels list
- }
- break;
-
- default:
- error("SoundResource: SCI version %d is unsupported", _soundVersion);
+ } else {
+ offsetPtr = script->data + 4 + 2 + 2;
}
-}
-
-SoundResource::~SoundResource() {
- for (int trackNr = 0; trackNr < _trackCount; trackNr++)
- delete[] _tracks[trackNr].channels;
- delete[] _tracks;
-
- _resMan->unlockResource(_innerResource);
-}
-
-#if 0
-SoundResource::Track* SoundResource::getTrackByNumber(uint16 number) {
- if (_soundVersion <= SCI_VERSION_0_LATE)
- return &_tracks[0];
-
- if (/*number >= 0 &&*/number < _trackCount)
- return &_tracks[number];
- return NULL;
-}
-#endif
+
+ int16 offset = !isSci11Mac() ? READ_LE_UINT16(offsetPtr) : READ_BE_UINT16(offsetPtr);
-SoundResource::Track *SoundResource::getTrackByType(byte type) {
- if (_soundVersion <= SCI_VERSION_0_LATE)
- return &_tracks[0];
+ // In SCI1.1 and newer, the heap is appended at the end of the script,
+ // so adjust the offset accordingly
+ if (getSciVersion() >= SCI_VERSION_1_1 && addSci11ScriptOffset) {
+ offset += script->size;
- for (int trackNr = 0; trackNr < _trackCount; trackNr++) {
- if (_tracks[trackNr].type == type)
- return &_tracks[trackNr];
+ // Ensure that the start of the heap is word-aligned - same as in Script::init()
+ if (script->size & 2)
+ offset++;
}
- return NULL;
-}
-SoundResource::Track *SoundResource::getDigitalTrack() {
- for (int trackNr = 0; trackNr < _trackCount; trackNr++) {
- if (_tracks[trackNr].digitalChannelNr != -1)
- return &_tracks[trackNr];
- }
- return NULL;
+ return make_reg(1, offset);
}
-// Gets the filter mask for SCI0 sound resources
-int SoundResource::getChannelFilterMask(int hardwareMask, bool wantsRhythm) {
- byte *data = _innerResource->data;
- int channelMask = 0;
-
- if (_soundVersion > SCI_VERSION_0_LATE)
- return 0;
-
- data++; // Skip over digital sample flag
-
- for (int channelNr = 0; channelNr < 16; channelNr++) {
- channelMask = channelMask >> 1;
-
- byte flags;
-
- if (_soundVersion == SCI_VERSION_0_EARLY) {
- // Each channel is specified by a single byte
- // Upper 4 bits of the byte is a voices count
- // Lower 4 bits -> bit 0 set: use for AdLib
- // bit 1 set: use for PCjr
- // bit 2 set: use for PC speaker
- // bit 3 set and bit 0 clear: control channel (15)
- // bit 3 set and bit 0 set: rhythm channel (9)
- // Note: control channel is dynamically assigned inside the drivers,
- // but seems to be fixed at 15 in the song data.
- flags = *data++;
-
- // Get device bits
- flags &= 0x7;
- } else {
- // Each channel is specified by 2 bytes
- // 1st byte is voices count
- // 2nd byte is play mask, which specifies if the channel is supposed to be played
- // by the corresponding hardware
-
- // Skip voice count
- data++;
-
- flags = *data++;
- }
-
- bool play;
- switch (channelNr) {
- case 15:
- // Always play control channel
- play = true;
- break;
- case 9:
- // Play rhythm channel when requested
- play = wantsRhythm;
- break;
- default:
- // Otherwise check for flag
- play = flags & hardwareMask;
- }
+Common::String ResourceManager::findSierraGameId() {
+ // In SCI0-SCI1, the heap is embedded in the script. In SCI1.1+, it's separated
+ Resource *heap = 0;
+ int nameSelector = 3;
- if (play) {
- // This Channel is supposed to be played by the hardware
- channelMask |= 0x8000;
- }
+ if (getSciVersion() < SCI_VERSION_1_1) {
+ heap = findResource(ResourceId(kResourceTypeScript, 0), false);
+ } else {
+ heap = findResource(ResourceId(kResourceTypeHeap, 0), false);
+ nameSelector += 5;
}
- return channelMask;
-}
+ if (!heap)
+ return "";
-byte SoundResource::getInitialVoiceCount(byte channel) {
- byte *data = _innerResource->data;
+ int16 gameObjectOffset = findGameObject(false).offset;
- if (_soundVersion > SCI_VERSION_0_LATE)
- return 0; // TODO
+ if (!gameObjectOffset)
+ return "";
- data++; // Skip over digital sample flag
+ // Seek to the name selector of the first export
+ byte *seeker = heap->data + READ_UINT16(heap->data + gameObjectOffset + nameSelector * 2);
+ Common::String sierraId;
+ sierraId += (const char *)seeker;
- if (_soundVersion == SCI_VERSION_0_EARLY)
- return data[channel] >> 4;
- else
- return data[channel * 2];
+ return sierraId;
}
} // End of namespace Sci
diff --git a/engines/sci/resource.h b/engines/sci/resource.h
index 8e83ed7bf0..533c81bdf5 100644
--- a/engines/sci/resource.h
+++ b/engines/sci/resource.h
@@ -26,8 +26,9 @@
#ifndef SCI_SCICORE_RESOURCE_H
#define SCI_SCICORE_RESOURCE_H
-#include "common/str.h"
#include "common/fs.h"
+#include "common/macresman.h"
+#include "common/str.h"
#include "sci/graphics/helpers.h" // for ViewType
#include "sci/decompressor.h"
@@ -121,7 +122,18 @@ const char *getResourceTypeName(ResourceType restype);
class ResourceManager;
-struct ResourceSource;
+
+struct ResourceSource {
+ ResSourceType source_type;
+ bool scanned;
+ Common::String location_name; // FIXME: Replace by FSNode ?
+ const Common::FSNode *resourceFile;
+ int volume_number;
+ ResourceSource *associated_map;
+ uint32 audioCompressionType;
+ int32 *audioCompressionOffsetMapping;
+ Common::MacResManager macResMan;
+};
class ResourceId {
public:
@@ -284,6 +296,14 @@ public:
*/
Common::String findSierraGameId();
+ /**
+ * Finds the location of the game object from script 0
+ * @param addSci11ScriptOffset: Adjust the return value for SCI1.1 and newer
+ * games. Needs to be false when the heap is accessed directly inside
+ * findSierraGameId().
+ */
+ reg_t findGameObject(bool addSci11ScriptOffset = true);
+
protected:
// Maximum number of bytes to allow being allocated for resources
// Note: maxMemory will not be interpreted as a hard limit, only as a restriction
@@ -301,8 +321,8 @@ protected:
ResourceMap _resMap;
Common::List<Common::File *> _volumeFiles; ///< list of opened volume files
ResourceSource *_audioMapSCI1; ///< Currently loaded audio map for SCI1
- ResVersion _volVersion; ///< RESOURCE.0xx version
- ResVersion _mapVersion; ///< RESOURCE.MAP version
+ ResVersion _volVersion; ///< resource.0xx version
+ ResVersion _mapVersion; ///< resource.map version
/**
* Initializes the resource manager
@@ -338,7 +358,7 @@ protected:
*/
ResourceSource *addExternalMap(const char *file_name, int volume_nr = 0);
- ResourceSource *addExternalMap(const Common::FSNode *mapFile);
+ ResourceSource *addExternalMap(const Common::FSNode *mapFile, int volume_nr = 0);
/**
* Add an internal (i.e., resource) map to the resource manager's list of sources.
@@ -373,13 +393,13 @@ protected:
*/
const char *versionDescription(ResVersion version) const;
- Common::File *getVolumeFile(const char *filename);
+ Common::SeekableReadStream *getVolumeFile(ResourceSource *source);
void loadResource(Resource *res);
- bool loadPatch(Resource *res, Common::File &file);
+ bool loadPatch(Resource *res, Common::SeekableReadStream *file);
bool loadFromPatchFile(Resource *res);
- bool loadFromWaveFile(Resource *res, Common::File &file);
- bool loadFromAudioVolumeSCI1(Resource *res, Common::File &file);
- bool loadFromAudioVolumeSCI11(Resource *res, Common::File &file);
+ bool loadFromWaveFile(Resource *res, Common::SeekableReadStream *file);
+ bool loadFromAudioVolumeSCI1(Resource *res, Common::SeekableReadStream *file);
+ bool loadFromAudioVolumeSCI11(Resource *res, Common::SeekableReadStream *file);
void freeOldResources();
int decompress(Resource *res, Common::SeekableReadStream *file);
int readResourceInfo(Resource *res, Common::SeekableReadStream *file, uint32&szPacked, ResourceCompression &compression);
@@ -432,7 +452,10 @@ protected:
* Reads patch files from a local directory.
*/
void readResourcePatches(ResourceSource *source);
- void processPatch(ResourceSource *source, ResourceType restype, int resnumber);
+#ifdef ENABLE_SCI32
+ void readResourcePatchesBase36(ResourceSource *source);
+#endif
+ void processPatch(ResourceSource *source, ResourceType resourceType, uint16 resourceNr, uint32 tuple = 0);
/**
* Process wave files as patches for Audio resources
@@ -492,6 +515,7 @@ public:
Track *getDigitalTrack();
int getChannelFilterMask(int hardwareMask, bool wantsRhythm);
byte getInitialVoiceCount(byte channel);
+ bool isChannelUsed(byte channel) const { return _channelsUsed & (1 << channel); }
private:
SciVersion _soundVersion;
@@ -499,6 +523,9 @@ private:
Track *_tracks;
Resource *_innerResource;
ResourceManager *_resMan;
+ uint16 _channelsUsed;
+
+ void setChannelUsed(byte channel) { _channelsUsed |= (1 << channel); }
};
} // End of namespace Sci
diff --git a/engines/sci/resource_audio.cpp b/engines/sci/resource_audio.cpp
new file mode 100644
index 0000000000..57efbdcb38
--- /dev/null
+++ b/engines/sci/resource_audio.cpp
@@ -0,0 +1,711 @@
+/* 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$
+ *
+ */
+
+// Resource library
+
+#include "common/file.h"
+
+#include "sci/resource.h"
+#include "sci/util.h"
+
+namespace Sci {
+
+void ResourceManager::checkIfAudioVolumeIsCompressed(ResourceSource *source) {
+ Common::SeekableReadStream *fileStream = getVolumeFile(source);
+
+ if (!fileStream) {
+ warning("Failed to open %s", source->location_name.c_str());
+ return;
+ }
+
+ fileStream->seek(0, SEEK_SET);
+ uint32 compressionType = fileStream->readUint32BE();
+ switch (compressionType) {
+ case MKID_BE('MP3 '):
+ case MKID_BE('OGG '):
+ case MKID_BE('FLAC'):
+ // Detected a compressed audio volume
+ source->audioCompressionType = compressionType;
+ // Now read the whole offset mapping table for later usage
+ int32 recordCount = fileStream->readUint32LE();
+ if (!recordCount)
+ error("compressed audio volume doesn't contain any entries!");
+ int32 *offsetMapping = new int32[(recordCount + 1) * 2];
+ source->audioCompressionOffsetMapping = offsetMapping;
+ for (int recordNo = 0; recordNo < recordCount; recordNo++) {
+ *offsetMapping++ = fileStream->readUint32LE();
+ *offsetMapping++ = fileStream->readUint32LE();
+ }
+ // Put ending zero
+ *offsetMapping++ = 0;
+ *offsetMapping++ = fileStream->size();
+ }
+
+ if (source->resourceFile)
+ delete fileStream;
+}
+
+bool ResourceManager::loadFromWaveFile(Resource *res, Common::SeekableReadStream *file) {
+ res->data = new byte[res->size];
+
+ uint32 really_read = file->read(res->data, res->size);
+ if (really_read != res->size)
+ error("Read %d bytes from %s but expected %d", really_read, res->_id.toString().c_str(), res->size);
+
+ res->_status = kResStatusAllocated;
+ return true;
+}
+
+bool ResourceManager::loadFromAudioVolumeSCI11(Resource *res, Common::SeekableReadStream *file) {
+ // Check for WAVE files here
+ uint32 riffTag = file->readUint32BE();
+ if (riffTag == MKID_BE('RIFF')) {
+ res->_headerSize = 0;
+ res->size = file->readUint32LE();
+ file->seek(-8, SEEK_CUR);
+ return loadFromWaveFile(res, file);
+ }
+ file->seek(-4, SEEK_CUR);
+
+ ResourceType type = (ResourceType)(file->readByte() & 0x7f);
+ if (((res->_id.type == kResourceTypeAudio || res->_id.type == kResourceTypeAudio36) && (type != kResourceTypeAudio))
+ || ((res->_id.type == kResourceTypeSync || res->_id.type == kResourceTypeSync36) && (type != kResourceTypeSync))) {
+ warning("Resource type mismatch loading %s", res->_id.toString().c_str());
+ res->unalloc();
+ return false;
+ }
+
+ res->_headerSize = file->readByte();
+
+ if (type == kResourceTypeAudio) {
+ if (res->_headerSize != 11 && res->_headerSize != 12) {
+ warning("Unsupported audio header");
+ res->unalloc();
+ return false;
+ }
+
+ // Load sample size
+ file->seek(7, SEEK_CUR);
+ res->size = file->readUint32LE();
+ // Adjust offset to point at the header data again
+ file->seek(-11, SEEK_CUR);
+ }
+
+ return loadPatch(res, file);
+}
+
+bool ResourceManager::loadFromAudioVolumeSCI1(Resource *res, Common::SeekableReadStream *file) {
+ res->data = new byte[res->size];
+
+ if (res->data == NULL) {
+ error("Can't allocate %d bytes needed for loading %s", res->size, res->_id.toString().c_str());
+ }
+
+ unsigned int really_read = file->read(res->data, res->size);
+ if (really_read != res->size)
+ warning("Read %d bytes from %s but expected %d", really_read, res->_id.toString().c_str(), res->size);
+
+ res->_status = kResStatusAllocated;
+ return true;
+}
+
+void ResourceManager::addNewGMPatch(const Common::String &gameId) {
+ Common::String gmPatchFile;
+
+ if (gameId == "ecoquest")
+ gmPatchFile = "ECO1GM.PAT";
+ else if (gameId == "hoyle3")
+ gmPatchFile = "HOY3GM.PAT";
+ else if (gameId == "hoyle3")
+ gmPatchFile = "HOY3GM.PAT";
+ else if (gameId == "lsl1sci")
+ gmPatchFile = "LL1_GM.PAT";
+ else if (gameId == "lsl5")
+ gmPatchFile = "LL5_GM.PAT";
+ else if (gameId == "longbow")
+ gmPatchFile = "ROBNGM.PAT";
+ else if (gameId == "sq1sci")
+ gmPatchFile = "SQ1_GM.PAT";
+ else if (gameId == "sq4")
+ gmPatchFile = "SQ4_GM.PAT";
+ else if (gameId == "fairytales")
+ gmPatchFile = "TALEGM.PAT";
+
+ if (!gmPatchFile.empty() && Common::File::exists(gmPatchFile)) {
+ ResourceSource *psrcPatch = new ResourceSource;
+ psrcPatch->source_type = kSourcePatch;
+ psrcPatch->resourceFile = 0;
+ psrcPatch->location_name = gmPatchFile;
+ processPatch(psrcPatch, kResourceTypePatch, 4);
+ }
+}
+
+void ResourceManager::readWaveAudioPatches() {
+ // Here we do check for SCI1.1+ so we can patch wav files in as audio resources
+ Common::ArchiveMemberList files;
+ SearchMan.listMatchingMembers(files, "*.wav");
+
+ for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
+ Common::String name = (*x)->getName();
+
+ if (isdigit(name[0])) {
+ int number = atoi(name.c_str());
+ ResourceSource *psrcPatch = new ResourceSource;
+ psrcPatch->source_type = kSourceWave;
+ psrcPatch->resourceFile = 0;
+ psrcPatch->location_name = name;
+ psrcPatch->volume_number = 0;
+ psrcPatch->audioCompressionType = 0;
+
+ ResourceId resId = ResourceId(kResourceTypeAudio, number);
+
+ Resource *newrsc = NULL;
+
+ // Prepare destination, if neccessary
+ if (_resMap.contains(resId) == false) {
+ newrsc = new Resource;
+ _resMap.setVal(resId, newrsc);
+ } else
+ newrsc = _resMap.getVal(resId);
+
+ // Get the size of the file
+ Common::SeekableReadStream *stream = (*x)->createReadStream();
+ uint32 fileSize = stream->size();
+ delete stream;
+
+ // Overwrite everything, because we're patching
+ newrsc->_id = resId;
+ newrsc->_status = kResStatusNoMalloc;
+ newrsc->_source = psrcPatch;
+ newrsc->size = fileSize;
+ newrsc->_headerSize = 0;
+ debugC(1, kDebugLevelResMan, "Patching %s - OK", psrcPatch->location_name.c_str());
+ }
+ }
+}
+
+void ResourceManager::removeAudioResource(ResourceId resId) {
+ // Remove resource, unless it was loaded from a patch
+ if (_resMap.contains(resId)) {
+ Resource *res = _resMap.getVal(resId);
+
+ if (res->_source->source_type == kSourceAudioVolume) {
+ if (res->_status == kResStatusLocked) {
+ warning("Failed to remove resource %s (still in use)", resId.toString().c_str());
+ } else {
+ if (res->_status == kResStatusEnqueued)
+ removeFromLRU(res);
+
+ _resMap.erase(resId);
+ delete res;
+ }
+ }
+ }
+}
+
+// Early SCI1.1 65535.MAP structure (uses RESOURCE.AUD):
+// =========
+// 6-byte entries:
+// w nEntry
+// dw offset
+
+// Late SCI1.1 65535.MAP structure (uses RESOURCE.SFX):
+// =========
+// 5-byte entries:
+// w nEntry
+// tb offset (cumulative)
+
+// Early SCI1.1 MAP structure:
+// ===============
+// 10-byte entries:
+// b noun
+// b verb
+// b cond
+// b seq
+// dw offset
+// w syncSize + syncAscSize
+
+// Late SCI1.1 MAP structure:
+// ===============
+// Header:
+// dw baseOffset
+// Followed by 7 or 11-byte entries:
+// b noun
+// b verb
+// b cond
+// b seq
+// tb cOffset (cumulative offset)
+// w syncSize (iff seq has bit 7 set)
+// w syncAscSize (iff seq has bit 6 set)
+
+int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
+ bool isEarly = true;
+ uint32 offset = 0;
+ Resource *mapRes = findResource(ResourceId(kResourceTypeMap, map->volume_number), false);
+
+ if (!mapRes) {
+ warning("Failed to open %i.MAP", map->volume_number);
+ return SCI_ERROR_RESMAP_NOT_FOUND;
+ }
+
+ ResourceSource *src = getVolume(map, 0);
+
+ if (!src)
+ return SCI_ERROR_NO_RESOURCE_FILES_FOUND;
+
+ byte *ptr = mapRes->data;
+
+ if (map->volume_number == 65535) {
+ // Heuristic to detect late SCI1.1 map format
+ if ((mapRes->size >= 6) && (ptr[mapRes->size - 6] != 0xff))
+ isEarly = false;
+
+ while (ptr < mapRes->data + mapRes->size) {
+ uint16 n = READ_LE_UINT16(ptr);
+ ptr += 2;
+
+ if (n == 0xffff)
+ break;
+
+ if (isEarly) {
+ offset = READ_LE_UINT32(ptr);
+ ptr += 4;
+ } else {
+ offset += READ_LE_UINT24(ptr);
+ ptr += 3;
+ }
+
+ addResource(ResourceId(kResourceTypeAudio, n), src, offset);
+ }
+ } else {
+ // Heuristic to detect late SCI1.1 map format
+ if ((mapRes->size >= 11) && (ptr[mapRes->size - 11] == 0xff))
+ isEarly = false;
+
+ if (!isEarly) {
+ offset = READ_LE_UINT32(ptr);
+ ptr += 4;
+ }
+
+ while (ptr < mapRes->data + mapRes->size) {
+ uint32 n = READ_BE_UINT32(ptr);
+ int syncSize = 0;
+ ptr += 4;
+
+ if (n == 0xffffffff)
+ break;
+
+ if (isEarly) {
+ offset = READ_LE_UINT32(ptr);
+ ptr += 4;
+ } else {
+ offset += READ_LE_UINT24(ptr);
+ ptr += 3;
+ }
+
+ if (isEarly || (n & 0x80)) {
+ syncSize = READ_LE_UINT16(ptr);
+ ptr += 2;
+
+ if (syncSize > 0)
+ addResource(ResourceId(kResourceTypeSync36, map->volume_number, n & 0xffffff3f), src, offset, syncSize);
+ }
+
+ if (n & 0x40) {
+ syncSize += READ_LE_UINT16(ptr);
+ ptr += 2;
+ }
+
+ addResource(ResourceId(kResourceTypeAudio36, map->volume_number, n & 0xffffff3f), src, offset + syncSize);
+ }
+ }
+
+ return 0;
+}
+
+// AUDIOnnn.MAP contains 10-byte entries:
+// Early format:
+// w 5 bits resource type and 11 bits resource number
+// dw 7 bits volume number and 25 bits offset
+// dw size
+// Later format:
+// w nEntry
+// dw offset+volume (as in resource.map)
+// dw size
+// ending with 10 0xFFs
+int ResourceManager::readAudioMapSCI1(ResourceSource *map, bool unload) {
+ Common::File file;
+
+ if (!file.open(map->location_name))
+ return SCI_ERROR_RESMAP_NOT_FOUND;
+
+ bool oldFormat = (file.readUint16LE() >> 11) == kResourceTypeAudio;
+ file.seek(0);
+
+ while (1) {
+ uint16 n = file.readUint16LE();
+ uint32 offset = file.readUint32LE();
+ uint32 size = file.readUint32LE();
+
+ if (file.eos() || file.err()) {
+ warning("Error while reading %s", map->location_name.c_str());
+ return SCI_ERROR_RESMAP_NOT_FOUND;
+ }
+
+ if (n == 0xffff)
+ break;
+
+ byte volume_nr;
+
+ if (oldFormat) {
+ n &= 0x07ff; // Mask out resource type
+ volume_nr = offset >> 25; // most significant 7 bits
+ offset &= 0x01ffffff; // least significant 25 bits
+ } else {
+ volume_nr = offset >> 28; // most significant 4 bits
+ offset &= 0x0fffffff; // least significant 28 bits
+ }
+
+ ResourceSource *src = getVolume(map, volume_nr);
+
+ if (src) {
+ if (unload)
+ removeAudioResource(ResourceId(kResourceTypeAudio, n));
+ else
+ addResource(ResourceId(kResourceTypeAudio, n), src, offset, size);
+ } else {
+ warning("Failed to find audio volume %i", volume_nr);
+ }
+ }
+
+ return 0;
+}
+
+void ResourceManager::setAudioLanguage(int language) {
+ if (_audioMapSCI1) {
+ if (_audioMapSCI1->volume_number == language) {
+ // This language is already loaded
+ return;
+ }
+
+ // We already have a map loaded, so we unload it first
+ readAudioMapSCI1(_audioMapSCI1, true);
+
+ // Remove all volumes that use this map from the source list
+ Common::List<ResourceSource *>::iterator it = _sources.begin();
+ while (it != _sources.end()) {
+ ResourceSource *src = *it;
+ if (src->associated_map == _audioMapSCI1) {
+ it = _sources.erase(it);
+ delete src;
+ } else {
+ ++it;
+ }
+ }
+
+ // Remove the map itself from the source list
+ _sources.remove(_audioMapSCI1);
+ delete _audioMapSCI1;
+
+ _audioMapSCI1 = NULL;
+ }
+
+ char filename[9];
+ snprintf(filename, 9, "AUDIO%03d", language);
+
+ Common::String fullname = Common::String(filename) + ".MAP";
+ if (!Common::File::exists(fullname)) {
+ warning("No audio map found for language %i", language);
+ return;
+ }
+
+ _audioMapSCI1 = addSource(NULL, kSourceExtAudioMap, fullname.c_str(), language);
+
+ // Search for audio volumes for this language and add them to the source list
+ Common::ArchiveMemberList files;
+ SearchMan.listMatchingMembers(files, Common::String(filename) + ".0??");
+ for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
+ const Common::String name = (*x)->getName();
+ const char *dot = strrchr(name.c_str(), '.');
+ int number = atoi(dot + 1);
+
+ addSource(_audioMapSCI1, kSourceAudioVolume, name.c_str(), number);
+ }
+
+ scanNewSources();
+}
+
+int ResourceManager::getAudioLanguage() const {
+ return (_audioMapSCI1 ? _audioMapSCI1->volume_number : 0);
+}
+
+SoundResource::SoundResource(uint32 resNumber, ResourceManager *resMan, SciVersion soundVersion) : _resMan(resMan), _soundVersion(soundVersion) {
+ Resource *resource = _resMan->findResource(ResourceId(kResourceTypeSound, resNumber), true);
+ int trackNr, channelNr;
+ if (!resource)
+ return;
+
+ _innerResource = resource;
+
+ byte *data, *data2;
+ byte *dataEnd;
+ Channel *channel, *sampleChannel;
+
+ _channelsUsed = 0;
+
+ switch (_soundVersion) {
+ case SCI_VERSION_0_EARLY:
+ case SCI_VERSION_0_LATE:
+ // SCI0 only has a header of 0x11/0x21 byte length and the actual midi track follows afterwards
+ _trackCount = 1;
+ _tracks = new Track[_trackCount];
+ _tracks->digitalChannelNr = -1;
+ _tracks->type = 0; // Not used for SCI0
+ _tracks->channelCount = 1;
+ // Digital sample data included? -> Add an additional channel
+ if (resource->data[0] == 2)
+ _tracks->channelCount++;
+ _tracks->channels = new Channel[_tracks->channelCount];
+ memset(_tracks->channels, 0, sizeof(Channel) * _tracks->channelCount);
+ channel = &_tracks->channels[0];
+ if (_soundVersion == SCI_VERSION_0_EARLY) {
+ channel->data = resource->data + 0x11;
+ channel->size = resource->size - 0x11;
+ } else {
+ channel->data = resource->data + 0x21;
+ channel->size = resource->size - 0x21;
+ }
+ if (_tracks->channelCount == 2) {
+ // Digital sample data included
+ _tracks->digitalChannelNr = 1;
+ sampleChannel = &_tracks->channels[1];
+ // we need to find 0xFC (channel terminator) within the data
+ data = channel->data;
+ dataEnd = channel->data + channel->size;
+ while ((data < dataEnd) && (*data != 0xfc))
+ data++;
+ // Skip any following 0xFCs as well
+ while ((data < dataEnd) && (*data == 0xfc))
+ data++;
+ // Now adjust channels accordingly
+ sampleChannel->data = data;
+ sampleChannel->size = channel->size - (data - channel->data);
+ channel->size = data - channel->data;
+ // Read sample header information
+ //Offset 14 in the header contains the frequency as a short integer. Offset 32 contains the sample length, also as a short integer.
+ _tracks->digitalSampleRate = READ_LE_UINT16(sampleChannel->data + 14);
+ _tracks->digitalSampleSize = READ_LE_UINT16(sampleChannel->data + 32);
+ _tracks->digitalSampleStart = 0;
+ _tracks->digitalSampleEnd = 0;
+ sampleChannel->data += 44; // Skip over header
+ sampleChannel->size -= 44;
+ }
+ break;
+
+ case SCI_VERSION_1_EARLY:
+ case SCI_VERSION_1_LATE:
+ data = resource->data;
+ // Count # of tracks
+ _trackCount = 0;
+ while ((*data++) != 0xFF) {
+ _trackCount++;
+ while (*data != 0xFF)
+ data += 6;
+ data++;
+ }
+ _tracks = new Track[_trackCount];
+ data = resource->data;
+ for (trackNr = 0; trackNr < _trackCount; trackNr++) {
+ // Track info starts with track type:BYTE
+ // Then the channel information gets appended Unknown:WORD, ChannelOffset:WORD, ChannelSize:WORD
+ // 0xFF:BYTE as terminator to end that track and begin with another track type
+ // Track type 0xFF is the marker signifying the end of the tracks
+
+ _tracks[trackNr].type = *data++;
+ // Counting # of channels used
+ data2 = data;
+ _tracks[trackNr].channelCount = 0;
+ while (*data2 != 0xFF) {
+ data2 += 6;
+ _tracks[trackNr].channelCount++;
+ }
+ _tracks[trackNr].channels = new Channel[_tracks[trackNr].channelCount];
+ _tracks[trackNr].digitalChannelNr = -1; // No digital sound associated
+ _tracks[trackNr].digitalSampleRate = 0;
+ _tracks[trackNr].digitalSampleSize = 0;
+ _tracks[trackNr].digitalSampleStart = 0;
+ _tracks[trackNr].digitalSampleEnd = 0;
+ if (_tracks[trackNr].type != 0xF0) { // Digital track marker - not supported currently
+ for (channelNr = 0; channelNr < _tracks[trackNr].channelCount; channelNr++) {
+ channel = &_tracks[trackNr].channels[channelNr];
+ channel->prio = READ_LE_UINT16(data);
+ channel->data = resource->data + READ_LE_UINT16(data + 2) + 2;
+ channel->size = READ_LE_UINT16(data + 4) - 2; // Not counting channel header
+ channel->number = *(channel->data - 2);
+ setChannelUsed(channel->number);
+ channel->poly = *(channel->data - 1);
+ channel->time = channel->prev = 0;
+ if (channel->number == 0xFE) { // Digital channel
+ _tracks[trackNr].digitalChannelNr = channelNr;
+ _tracks[trackNr].digitalSampleRate = READ_LE_UINT16(channel->data);
+ _tracks[trackNr].digitalSampleSize = READ_LE_UINT16(channel->data + 2);
+ _tracks[trackNr].digitalSampleStart = READ_LE_UINT16(channel->data + 4);
+ _tracks[trackNr].digitalSampleEnd = READ_LE_UINT16(channel->data + 6);
+ channel->data += 8; // Skip over header
+ channel->size -= 8;
+ }
+ data += 6;
+ }
+ } else {
+ // Skip over digital track
+ data += 6;
+ }
+ data++; // Skipping 0xFF that closes channels list
+ }
+ break;
+
+ default:
+ error("SoundResource: SCI version %d is unsupported", _soundVersion);
+ }
+}
+
+SoundResource::~SoundResource() {
+ for (int trackNr = 0; trackNr < _trackCount; trackNr++)
+ delete[] _tracks[trackNr].channels;
+ delete[] _tracks;
+
+ _resMan->unlockResource(_innerResource);
+}
+
+#if 0
+SoundResource::Track* SoundResource::getTrackByNumber(uint16 number) {
+ if (_soundVersion <= SCI_VERSION_0_LATE)
+ return &_tracks[0];
+
+ if (/*number >= 0 &&*/number < _trackCount)
+ return &_tracks[number];
+ return NULL;
+}
+#endif
+
+SoundResource::Track *SoundResource::getTrackByType(byte type) {
+ if (_soundVersion <= SCI_VERSION_0_LATE)
+ return &_tracks[0];
+
+ for (int trackNr = 0; trackNr < _trackCount; trackNr++) {
+ if (_tracks[trackNr].type == type)
+ return &_tracks[trackNr];
+ }
+ return NULL;
+}
+
+SoundResource::Track *SoundResource::getDigitalTrack() {
+ for (int trackNr = 0; trackNr < _trackCount; trackNr++) {
+ if (_tracks[trackNr].digitalChannelNr != -1)
+ return &_tracks[trackNr];
+ }
+ return NULL;
+}
+
+// Gets the filter mask for SCI0 sound resources
+int SoundResource::getChannelFilterMask(int hardwareMask, bool wantsRhythm) {
+ byte *data = _innerResource->data;
+ int channelMask = 0;
+
+ if (_soundVersion > SCI_VERSION_0_LATE)
+ return 0;
+
+ data++; // Skip over digital sample flag
+
+ for (int channelNr = 0; channelNr < 16; channelNr++) {
+ channelMask = channelMask >> 1;
+
+ byte flags;
+
+ if (_soundVersion == SCI_VERSION_0_EARLY) {
+ // Each channel is specified by a single byte
+ // Upper 4 bits of the byte is a voices count
+ // Lower 4 bits -> bit 0 set: use for AdLib
+ // bit 1 set: use for PCjr
+ // bit 2 set: use for PC speaker
+ // bit 3 set and bit 0 clear: control channel (15)
+ // bit 3 set and bit 0 set: rhythm channel (9)
+ // Note: control channel is dynamically assigned inside the drivers,
+ // but seems to be fixed at 15 in the song data.
+ flags = *data++;
+
+ // Get device bits
+ flags &= 0x7;
+ } else {
+ // Each channel is specified by 2 bytes
+ // 1st byte is voices count
+ // 2nd byte is play mask, which specifies if the channel is supposed to be played
+ // by the corresponding hardware
+
+ // Skip voice count
+ data++;
+
+ flags = *data++;
+ }
+
+ bool play;
+ switch (channelNr) {
+ case 15:
+ // Always play control channel
+ play = true;
+ break;
+ case 9:
+ // Play rhythm channel when requested
+ play = wantsRhythm;
+ break;
+ default:
+ // Otherwise check for flag
+ play = flags & hardwareMask;
+ }
+
+ if (play) {
+ // This Channel is supposed to be played by the hardware
+ channelMask |= 0x8000;
+ }
+ }
+
+ return channelMask;
+}
+
+byte SoundResource::getInitialVoiceCount(byte channel) {
+ byte *data = _innerResource->data;
+
+ if (_soundVersion > SCI_VERSION_0_LATE)
+ return 0; // TODO
+
+ data++; // Skip over digital sample flag
+
+ if (_soundVersion == SCI_VERSION_0_EARLY)
+ return data[channel] >> 4;
+ else
+ return data[channel * 2];
+}
+
+} // End of namespace Sci
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index 39f117475b..929bdf3307 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -56,8 +56,6 @@
namespace Sci {
-extern int g_loadFromLauncher;
-
SciEngine *g_sci = 0;
@@ -190,7 +188,7 @@ Common::Error SciEngine::run() {
_features = new GameFeatures(segMan, _kernel);
- _gamestate = new EngineState(_vocabulary, segMan);
+ _gamestate = new EngineState(segMan);
_gamestate->_event = new SciEvent(_resMan);
@@ -233,11 +231,6 @@ Common::Error SciEngine::run() {
script_adjust_opcode_formats(_gamestate);
_kernel->loadKernelNames(getGameID());
- // Set the savegame dir (actually, we set it to a fake value,
- // since we cannot let the game control where saves are stored)
- assert(_gamestate->sys_strings->_strings[SYS_STRING_SAVEDIR]._value != 0);
- strcpy(_gamestate->sys_strings->_strings[SYS_STRING_SAVEDIR]._value, "");
-
SciVersion soundVersion = _features->detectDoSoundType();
_gamestate->_soundCmd = new SoundCommandParser(_resMan, segMan, _kernel, _audio, soundVersion);
@@ -264,9 +257,9 @@ Common::Error SciEngine::run() {
// Check whether loading a savestate was requested
if (ConfMan.hasKey("save_slot")) {
- g_loadFromLauncher = ConfMan.getInt("save_slot");
+ _gamestate->loadFromLauncher = ConfMan.getInt("save_slot");
} else {
- g_loadFromLauncher = -1;
+ _gamestate->loadFromLauncher = -1;
}
game_run(&_gamestate); // Run the game
diff --git a/engines/sci/sound/audio.cpp b/engines/sci/sound/audio.cpp
index 331561eea4..7748c0505b 100644
--- a/engines/sci/sound/audio.cpp
+++ b/engines/sci/sound/audio.cpp
@@ -235,6 +235,7 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32
uint32 audioCompressionType = audioRes->getAudioCompressionType();
if (audioCompressionType) {
+#if (defined(USE_MAD) || defined(USE_VORBIS) || defined(USE_FLAC))
// Compressed audio made by our tool
byte *compressedData = (byte *)malloc(audioRes->size);
assert(compressedData);
@@ -261,6 +262,9 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32
#endif
break;
}
+#else
+ error("Compressed audio file encountered, but no appropriate decoder is compiled in");
+#endif
} else {
// Original source file
if (audioRes->_headerSize > 0) {
@@ -332,11 +336,11 @@ void AudioPlayer::setSoundSync(ResourceId id, reg_t syncObjAddr, SegManager *seg
_syncOffset = 0;
if (_syncResource) {
- PUT_SEL32V(segMan, syncObjAddr, SELECTOR(syncCue), 0);
+ writeSelectorValue(segMan, syncObjAddr, SELECTOR(syncCue), 0);
} else {
warning("setSoundSync: failed to find resource %s", id.toString().c_str());
// Notify the scripts to stop sound sync
- PUT_SEL32V(segMan, syncObjAddr, SELECTOR(syncCue), SIGNAL_OFFSET);
+ writeSelectorValue(segMan, syncObjAddr, SELECTOR(syncCue), SIGNAL_OFFSET);
}
}
@@ -352,8 +356,8 @@ void AudioPlayer::doSoundSync(reg_t syncObjAddr, SegManager *segMan) {
_syncOffset += 2;
}
- PUT_SEL32V(segMan, syncObjAddr, SELECTOR(syncTime), syncTime);
- PUT_SEL32V(segMan, syncObjAddr, SELECTOR(syncCue), syncCue);
+ writeSelectorValue(segMan, syncObjAddr, SELECTOR(syncTime), syncTime);
+ writeSelectorValue(segMan, syncObjAddr, SELECTOR(syncCue), syncCue);
}
}
diff --git a/engines/sci/sound/midiparser_sci.cpp b/engines/sci/sound/midiparser_sci.cpp
index 2068ea9a33..3ee8a3a83d 100644
--- a/engines/sci/sound/midiparser_sci.cpp
+++ b/engines/sci/sound/midiparser_sci.cpp
@@ -60,6 +60,9 @@ MidiParser_SCI::MidiParser_SCI(SciVersion soundVersion) :
_dataincToAdd = 0;
_resetOnPause = false;
_channelsUsed = 0;
+
+ for (int i = 0; i < 16; i++)
+ _channelRemap[i] = i;
}
MidiParser_SCI::~MidiParser_SCI() {
@@ -85,7 +88,6 @@ bool MidiParser_SCI::loadMusic(SoundResource::Track *track, MusicEntry *psnd, in
_tracks[0] = _mixedData;
setTrack(0);
_loopTick = 0;
- _channelsUsed = 0;
if (_soundVersion <= SCI_VERSION_0_LATE) {
// Set initial voice count
@@ -120,17 +122,20 @@ void MidiParser_SCI::unloadMusic() {
// Center the pitch wheels and hold pedal in preparation for the next piece of music
if (_driver) {
for (int i = 0; i < 16; ++i) {
- if (_channelsUsed & (1 << i)) {
+ if (isChannelUsed(i)) {
_driver->send(0xE0 | i, 0, 0x40); // Reset pitch wheel
_driver->send(0xB0 | i, 0x40, 0); // Reset hold pedal
}
}
}
+
+ for (int i = 0; i < 16; i++)
+ _channelRemap[i] = i;
}
void MidiParser_SCI::parseNextEvent(EventInfo &info) {
// Monitor which channels are used by this song
- _channelsUsed |= (1 << info.channel());
+ setChannelUsed(info.channel());
// Set signal AFTER waiting for delta, otherwise we would set signal too soon resulting in all sorts of bugs
if (_dataincAdd) {
@@ -322,7 +327,7 @@ byte MidiParser_SCI::midiGetNextChannel(long ticker) {
for (int i = 0; i < _track->channelCount; i++) {
if (_track->channels[i].time == -1) // channel ended
continue;
- next = *_track->channels[i].data; // when the next event shoudl occur
+ next = *_track->channels[i].data; // when the next event should occur
if (next == 0xF8) // 0xF8 means 240 ticks delay
next = 240;
next += _track->channels[i].time;
@@ -389,9 +394,18 @@ byte *MidiParser_SCI::midiMixChannels() {
channel->time = -1; // FIXME
break;
default: // MIDI command
- if (command & 0x80)
+ if (command & 0x80) {
par1 = *channel->data++;
- else {// running status
+
+ // TODO: Fix remapping
+
+#if 0
+ // Remap channel. Keep the upper 4 bits (command code) and change
+ // the lower 4 bits (channel)
+ byte remappedChannel = _channelRemap[par1 & 0xF];
+ par1 = (par1 & 0xF0) | (remappedChannel & 0xF);
+#endif
+ } else {// running status
par1 = command;
command = channel->prev;
}
diff --git a/engines/sci/sound/midiparser_sci.h b/engines/sci/sound/midiparser_sci.h
index f95c71ce2f..9d4b5a39da 100644
--- a/engines/sci/sound/midiparser_sci.h
+++ b/engines/sci/sound/midiparser_sci.h
@@ -71,7 +71,18 @@ public:
jumpToTick(0);
}
+ void remapChannel(byte channel, byte newChannel) {
+ assert(channel < 0xF); // don't touch special SCI channel 15
+ assert(newChannel < 0xF); // don't touch special SCI channel 15
+ _channelRemap[channel] = newChannel;
+ }
+
+ void clearUsedChannels() { _channelsUsed = 0; }
+
protected:
+ bool isChannelUsed(byte channel) const { return _channelsUsed & (1 << channel); }
+ void setChannelUsed(byte channel) { _channelsUsed |= (1 << channel); }
+
void parseNextEvent(EventInfo &info);
byte *midiMixChannels();
byte *midiFilterChannels(int channelMask);
@@ -93,6 +104,8 @@ protected:
// A 16-bit mask, containing the channels used
// by the currently parsed song
uint16 _channelsUsed;
+
+ byte _channelRemap[16];
};
} // End of namespace Sci
diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp
index 86c7669df9..fa5716e7cc 100644
--- a/engines/sci/sound/music.cpp
+++ b/engines/sci/sound/music.cpp
@@ -37,9 +37,6 @@
namespace Sci {
-// When defined, volume fading immediately sets the final sound volume
-#define DISABLE_VOLUME_FADING
-
SciMusic::SciMusic(SciVersion soundVersion)
: _soundVersion(soundVersion), _soundOn(true), _masterVolume(0) {
@@ -115,8 +112,6 @@ void SciMusic::clearPlayList() {
}
void SciMusic::pauseAll(bool pause) {
- Common::StackLock lock(_mutex);
-
const MusicList::iterator end = _playList.end();
for (MusicList::iterator i = _playList.begin(); i != end; ++i) {
soundToggle(*i, pause);
@@ -170,14 +165,29 @@ void SciMusic::setReverb(byte reverb) {
_pMidiDrv->setReverb(reverb);
}
-static int f_compare(const void *arg1, const void *arg2) {
- return ((const MusicEntry *)arg2)->priority - ((const MusicEntry *)arg1)->priority;
+static bool musicEntryCompare(const MusicEntry *l, const MusicEntry *r) {
+ return (l->priority > r->priority);
}
void SciMusic::sortPlayList() {
- MusicEntry ** pData = _playList.begin();
- qsort(pData, _playList.size(), sizeof(MusicEntry *), &f_compare);
+ // Sort the play list in descending priority order
+ Common::sort(_playList.begin(), _playList.end(), musicEntryCompare);
+}
+
+void SciMusic::findUsedChannels() {
+ // Reset list
+ for (int k = 0; k < 16; k++)
+ _usedChannels[k] = false;
+
+ const MusicList::const_iterator end = _playList.end();
+ for (MusicList::const_iterator i = _playList.begin(); i != end; ++i) {
+ for (int channel = 0; channel < 16; channel++) {
+ if ((*i)->soundRes && (*i)->soundRes->isChannelUsed(channel))
+ _usedChannels[channel] = true;
+ }
+ }
}
+
void SciMusic::soundInitSnd(MusicEntry *pSnd) {
int channelFilterMask = 0;
SoundResource::Track *track = pSnd->soundRes->getTrackByType(_pMidiDrv->getPlayId());
@@ -221,6 +231,27 @@ void SciMusic::soundInitSnd(MusicEntry *pSnd) {
pSnd->pauseCounter = 0;
+ // TODO: Fix channel remapping. This doesn't quite work... (e.g. no difference in LSL1VGA)
+#if 0
+ // Remap channels
+ findUsedChannels();
+
+ pSnd->pMidiParser->clearUsedChannels();
+
+ for (int i = 0; i < 16; i++) {
+ if (_usedChannels[i] && pSnd->soundRes->isChannelUsed(i)) {
+ int16 newChannel = getNextUnusedChannel();
+ if (newChannel >= 0) {
+ _usedChannels[newChannel] = true;
+ debug("Remapping channel %d to %d\n", i, newChannel);
+ pSnd->pMidiParser->remapChannel(i, newChannel);
+ } else {
+ warning("Attempt to remap channel %d, but no unused channels exist", i);
+ }
+ }
+ }
+#endif
+
// Find out what channels to filter for SCI0
channelFilterMask = pSnd->soundRes->getChannelFilterMask(_pMidiDrv->getPlayId(), _pMidiDrv->hasRhythmChannel());
pSnd->pMidiParser->loadMusic(track, pSnd, channelFilterMask, _soundVersion);
@@ -389,7 +420,12 @@ void SciMusic::soundResume(MusicEntry *pSnd) {
return;
if (pSnd->status != kSoundPaused)
return;
- soundPlay(pSnd);
+ if (pSnd->pStreamAud) {
+ _pMixer->pauseHandle(pSnd->hCurrentAud, false);
+ pSnd->status = kSoundPlaying;
+ } else {
+ soundPlay(pSnd);
+ }
}
void SciMusic::soundToggle(MusicEntry *pSnd, bool pause) {
@@ -531,15 +567,17 @@ void MusicEntry::doFade() {
fadeStep = 0;
fadeCompleted = true;
}
-
- // Only process MIDI streams in this thread, not digital sound effects
- if (pMidiParser) {
-#ifdef DISABLE_VOLUME_FADING
- // Signal fading to stop...
+#ifdef ENABLE_SCI32
+ // Disable fading for SCI32 - sound drivers have issues when fading in (gabriel knight 1 sierra title)
+ if (getSciVersion() >= SCI_VERSION_2) {
volume = fadeTo;
fadeStep = 0;
fadeCompleted = true;
+ }
#endif
+
+ // Only process MIDI streams in this thread, not digital sound effects
+ if (pMidiParser) {
pMidiParser->setVolume(volume);
}
diff --git a/engines/sci/sound/music.h b/engines/sci/sound/music.h
index 8f08065b99..83cd59e89b 100644
--- a/engines/sci/sound/music.h
+++ b/engines/sci/sound/music.h
@@ -197,7 +197,6 @@ public:
Common::Mutex _mutex;
protected:
- byte findAudEntry(uint16 nAud, byte&oVolume, uint32& oOffset, uint32&oSize);
void sortPlayList();
SciVersion _soundVersion;
@@ -211,10 +210,20 @@ protected:
bool _bMultiMidi;
private:
static void miditimerCallback(void *p);
+ void findUsedChannels();
+ int16 getNextUnusedChannel() const {
+ for (int i = 0; i < 16; i++) {
+ if (!_usedChannels[i])
+ return i;
+ }
+
+ return -1;
+ }
MusicList _playList;
bool _soundOn;
byte _masterVolume;
+ bool _usedChannels[16];
};
} // End of namespace Sci
diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp
index 40e3637372..ece4c1430c 100644
--- a/engines/sci/sound/soundcmd.cpp
+++ b/engines/sci/sound/soundcmd.cpp
@@ -50,9 +50,9 @@ namespace Sci {
#ifdef USE_OLD_MUSIC_FUNCTIONS
static void script_set_priority(ResourceManager *resMan, SegManager *segMan, SfxState *state, reg_t obj, int priority) {
- int song_nr = GET_SEL32V(segMan, obj, SELECTOR(number));
+ int song_nr = readSelectorValue(segMan, obj, SELECTOR(number));
Resource *song = resMan->findResource(ResourceId(kResourceTypeSound, song_nr), 0);
- int flags = GET_SEL32V(segMan, obj, SELECTOR(flags));
+ int flags = readSelectorValue(segMan, obj, SELECTOR(flags));
if (priority == -1) {
if (song->data[0] == 0xf0)
@@ -64,7 +64,7 @@ static void script_set_priority(ResourceManager *resMan, SegManager *segMan, Sfx
} else flags |= SCI1_SOUND_FLAG_SCRIPTED_PRI;
state->sfx_song_renice(FROBNICATE_HANDLE(obj), priority);
- PUT_SEL32V(segMan, obj, SELECTOR(flags), flags);
+ writeSelectorValue(segMan, obj, SELECTOR(flags), flags);
}
SongIterator *build_iterator(ResourceManager *resMan, int song_nr, SongIteratorType type, songit_id_t id) {
@@ -98,27 +98,27 @@ void process_sound_events(EngineState *s) { /* Get all sound events, apply their
case SI_LOOP:
debugC(2, kDebugLevelSound, "[process-sound] Song %04x:%04x looped (to %d)",
PRINT_REG(obj), cue);
- /* PUT_SEL32V(segMan, obj, SELECTOR(loops), GET_SEL32V(segMan, obj, SELECTOR(loop));; - 1);*/
- PUT_SEL32V(segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
+ /* writeSelectorValue(segMan, obj, SELECTOR(loops), readSelectorValue(segMan, obj, SELECTOR(loop));; - 1);*/
+ writeSelectorValue(segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
break;
case SI_RELATIVE_CUE:
debugC(2, kDebugLevelSound, "[process-sound] Song %04x:%04x received relative cue %d",
PRINT_REG(obj), cue);
- PUT_SEL32V(segMan, obj, SELECTOR(signal), cue + 0x7f);
+ writeSelectorValue(segMan, obj, SELECTOR(signal), cue + 0x7f);
break;
case SI_ABSOLUTE_CUE:
debugC(2, kDebugLevelSound, "[process-sound] Song %04x:%04x received absolute cue %d",
PRINT_REG(obj), cue);
- PUT_SEL32V(segMan, obj, SELECTOR(signal), cue);
+ writeSelectorValue(segMan, obj, SELECTOR(signal), cue);
break;
case SI_FINISHED:
debugC(2, kDebugLevelSound, "[process-sound] Song %04x:%04x finished",
PRINT_REG(obj));
- PUT_SEL32V(segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
- PUT_SEL32V(segMan, obj, SELECTOR(state), kSoundStopped);
+ writeSelectorValue(segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
+ writeSelectorValue(segMan, obj, SELECTOR(state), kSoundStopped);
break;
default:
@@ -253,7 +253,7 @@ void SoundCommandParser::cmdInitSound(reg_t obj, int16 value) {
if (!obj.segment)
return;
- int resourceId = GET_SEL32V(_segMan, obj, SELECTOR(number));
+ int resourceId = readSelectorValue(_segMan, obj, SELECTOR(number));
#ifdef USE_OLD_MUSIC_FUNCTIONS
@@ -267,7 +267,7 @@ void SoundCommandParser::cmdInitSound(reg_t obj, int16 value) {
SongIteratorType type = (_soundVersion <= SCI_VERSION_0_LATE) ? SCI_SONG_ITERATOR_TYPE_SCI0 : SCI_SONG_ITERATOR_TYPE_SCI1;
if (_soundVersion <= SCI_VERSION_0_LATE) {
- if (GET_SEL32V(_segMan, obj, SELECTOR(nodePtr))) {
+ if (readSelectorValue(_segMan, obj, SELECTOR(nodePtr))) {
_state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED);
_state->sfx_remove_song(handle);
}
@@ -281,11 +281,11 @@ void SoundCommandParser::cmdInitSound(reg_t obj, int16 value) {
// Notify the engine
if (_soundVersion <= SCI_VERSION_0_LATE)
- PUT_SEL32V(_segMan, obj, SELECTOR(state), kSoundInitialized);
+ writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundInitialized);
else
- PUT_SEL32(_segMan, obj, SELECTOR(nodePtr), obj);
+ writeSelector(_segMan, obj, SELECTOR(nodePtr), obj);
- PUT_SEL32(_segMan, obj, SELECTOR(handle), obj);
+ writeSelector(_segMan, obj, SELECTOR(handle), obj);
#else
@@ -302,10 +302,10 @@ void SoundCommandParser::cmdInitSound(reg_t obj, int16 value) {
newSound->soundRes = 0;
newSound->soundObj = obj;
- newSound->loop = GET_SEL32V(_segMan, obj, SELECTOR(loop));
- newSound->priority = GET_SEL32V(_segMan, obj, SELECTOR(pri)) & 0xFF;
- if (_soundVersion >= SCI_VERSION_1_LATE)
- newSound->volume = CLIP<int>(GET_SEL32V(_segMan, obj, SELECTOR(vol)), 0, MUSIC_VOLUME_MAX);
+ newSound->loop = readSelectorValue(_segMan, obj, SELECTOR(loop));
+ newSound->priority = readSelectorValue(_segMan, obj, SELECTOR(pri)) & 0xFF;
+ if (_soundVersion >= SCI_VERSION_1_EARLY)
+ newSound->volume = CLIP<int>(readSelectorValue(_segMan, obj, SELECTOR(vol)), 0, MUSIC_VOLUME_MAX);
// In SCI1.1 games, sound effects are started from here. If we can find
// a relevant audio resource, play it, otherwise switch to synthesized
@@ -327,11 +327,11 @@ void SoundCommandParser::cmdInitSound(reg_t obj, int16 value) {
if (newSound->soundRes || newSound->pStreamAud) {
// Notify the engine
if (_soundVersion <= SCI_VERSION_0_LATE)
- PUT_SEL32V(_segMan, obj, SELECTOR(state), kSoundInitialized);
+ writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundInitialized);
else
- PUT_SEL32(_segMan, obj, SELECTOR(nodePtr), obj);
+ writeSelector(_segMan, obj, SELECTOR(nodePtr), obj);
- PUT_SEL32(_segMan, obj, SELECTOR(handle), obj);
+ writeSelector(_segMan, obj, SELECTOR(handle), obj);
}
#endif
@@ -346,30 +346,30 @@ void SoundCommandParser::cmdPlaySound(reg_t obj, int16 value) {
if (_soundVersion <= SCI_VERSION_0_LATE) {
_state->sfx_song_set_status(handle, SOUND_STATUS_PLAYING);
- _state->sfx_song_set_loops(handle, GET_SEL32V(_segMan, obj, SELECTOR(loop)));
- PUT_SEL32V(_segMan, obj, SELECTOR(state), kSoundPlaying);
+ _state->sfx_song_set_loops(handle, readSelectorValue(_segMan, obj, SELECTOR(loop)));
+ writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundPlaying);
} else if (_soundVersion == SCI_VERSION_1_EARLY) {
_state->sfx_song_set_status(handle, SOUND_STATUS_PLAYING);
- _state->sfx_song_set_loops(handle, GET_SEL32V(_segMan, obj, SELECTOR(loop)));
- _state->sfx_song_renice(handle, GET_SEL32V(_segMan, obj, SELECTOR(pri)));
+ _state->sfx_song_set_loops(handle, readSelectorValue(_segMan, obj, SELECTOR(loop)));
+ _state->sfx_song_renice(handle, readSelectorValue(_segMan, obj, SELECTOR(pri)));
RESTORE_BEHAVIOR rb = (RESTORE_BEHAVIOR) value; /* Too lazy to look up a default value for this */
_state->_songlib.setSongRestoreBehavior(handle, rb);
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), 0);
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), 0);
} else if (_soundVersion == SCI_VERSION_1_LATE) {
- int looping = GET_SEL32V(_segMan, obj, SELECTOR(loop));
- //int vol = GET_SEL32V(_segMan, obj, SELECTOR(vol));
- int pri = GET_SEL32V(_segMan, obj, SELECTOR(pri));
+ int looping = readSelectorValue(_segMan, obj, SELECTOR(loop));
+ //int vol = readSelectorValue(_segMan, obj, SELECTOR(vol));
+ int pri = readSelectorValue(_segMan, obj, SELECTOR(pri));
int sampleLen = 0;
Song *song = _state->_songlib.findSong(handle);
- int songNumber = GET_SEL32V(_segMan, obj, SELECTOR(number));
+ int songNumber = readSelectorValue(_segMan, obj, SELECTOR(number));
- if (GET_SEL32V(_segMan, obj, SELECTOR(nodePtr)) && (song && songNumber != song->_resourceNum)) {
+ if (readSelectorValue(_segMan, obj, SELECTOR(nodePtr)) && (song && songNumber != song->_resourceNum)) {
_state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED);
_state->sfx_remove_song(handle);
- PUT_SEL32(_segMan, obj, SELECTOR(nodePtr), NULL_REG);
+ writeSelector(_segMan, obj, SELECTOR(nodePtr), NULL_REG);
}
- if (!GET_SEL32V(_segMan, obj, SELECTOR(nodePtr)) && obj.segment) {
+ if (!readSelectorValue(_segMan, obj, SELECTOR(nodePtr)) && obj.segment) {
// In SCI1.1 games, sound effects are started from here. If we can find
// a relevant audio resource, play it, otherwise switch to synthesized
// effects. If the resource exists, play it using map 65535 (sound
@@ -387,7 +387,7 @@ void SoundCommandParser::cmdPlaySound(reg_t obj, int16 value) {
warning("Could not open song number %d", songNumber);
// Send a "stop handle" event so that the engine won't wait forever here
_state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED);
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
return;
}
debugC(2, kDebugLevelSound, "Initializing song number %d", songNumber);
@@ -395,15 +395,15 @@ void SoundCommandParser::cmdPlaySound(reg_t obj, int16 value) {
handle), 0, handle, songNumber);
}
- PUT_SEL32(_segMan, obj, SELECTOR(nodePtr), obj);
- PUT_SEL32(_segMan, obj, SELECTOR(handle), obj);
+ writeSelector(_segMan, obj, SELECTOR(nodePtr), obj);
+ writeSelector(_segMan, obj, SELECTOR(handle), obj);
}
if (obj.segment) {
_state->sfx_song_set_status(handle, SOUND_STATUS_PLAYING);
_state->sfx_song_set_loops(handle, looping);
_state->sfx_song_renice(handle, pri);
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), 0);
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), 0);
}
}
@@ -415,7 +415,7 @@ void SoundCommandParser::cmdPlaySound(reg_t obj, int16 value) {
return;
}
- int resourceId = obj.segment ? GET_SEL32V(_segMan, obj, SELECTOR(number)) : -1;
+ int resourceId = obj.segment ? readSelectorValue(_segMan, obj, SELECTOR(number)) : -1;
if (musicSlot->resourceId != resourceId) { // another sound loaded into struct
cmdDisposeSound(obj, value);
@@ -423,25 +423,25 @@ void SoundCommandParser::cmdPlaySound(reg_t obj, int16 value) {
// Find slot again :)
musicSlot = _music->getSlot(obj);
}
- int16 loop = GET_SEL32V(_segMan, obj, SELECTOR(loop));
+ int16 loop = readSelectorValue(_segMan, obj, SELECTOR(loop));
debugC(2, kDebugLevelSound, "cmdPlaySound: resource number %d, loop %d", resourceId, loop);
- PUT_SEL32(_segMan, obj, SELECTOR(handle), obj);
+ writeSelector(_segMan, obj, SELECTOR(handle), obj);
if (_soundVersion >= SCI_VERSION_1_EARLY) {
- PUT_SEL32(_segMan, obj, SELECTOR(nodePtr), obj);
- PUT_SEL32V(_segMan, obj, SELECTOR(min), 0);
- PUT_SEL32V(_segMan, obj, SELECTOR(sec), 0);
- PUT_SEL32V(_segMan, obj, SELECTOR(frame), 0);
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), 0);
+ writeSelector(_segMan, obj, SELECTOR(nodePtr), obj);
+ writeSelectorValue(_segMan, obj, SELECTOR(min), 0);
+ writeSelectorValue(_segMan, obj, SELECTOR(sec), 0);
+ writeSelectorValue(_segMan, obj, SELECTOR(frame), 0);
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), 0);
} else {
- PUT_SEL32V(_segMan, obj, SELECTOR(state), kSoundPlaying);
+ writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundPlaying);
}
- musicSlot->loop = GET_SEL32V(_segMan, obj, SELECTOR(loop));
- musicSlot->priority = GET_SEL32V(_segMan, obj, SELECTOR(priority));
- if (_soundVersion >= SCI_VERSION_1_LATE)
- musicSlot->volume = GET_SEL32V(_segMan, obj, SELECTOR(vol));
+ musicSlot->loop = readSelectorValue(_segMan, obj, SELECTOR(loop));
+ musicSlot->priority = readSelectorValue(_segMan, obj, SELECTOR(priority));
+ if (_soundVersion >= SCI_VERSION_1_EARLY)
+ musicSlot->volume = readSelectorValue(_segMan, obj, SELECTOR(vol));
_music->soundPlay(musicSlot);
#endif
@@ -458,7 +458,7 @@ void SoundCommandParser::changeSoundStatus(reg_t obj, int newStatus) {
if (obj.segment) {
_state->sfx_song_set_status(handle, newStatus);
if (_soundVersion <= SCI_VERSION_0_LATE)
- PUT_SEL32V(_segMan, obj, SELECTOR(state), newStatus);
+ writeSelectorValue(_segMan, obj, SELECTOR(state), newStatus);
}
}
#endif
@@ -475,7 +475,7 @@ void SoundCommandParser::cmdDisposeSound(reg_t obj, int16 value) {
_state->sfx_remove_song(handle);
if (_soundVersion <= SCI_VERSION_0_LATE)
- PUT_SEL32V(_segMan, obj, SELECTOR(handle), 0x0000);
+ writeSelectorValue(_segMan, obj, SELECTOR(handle), 0x0000);
}
#else
@@ -489,11 +489,11 @@ void SoundCommandParser::cmdDisposeSound(reg_t obj, int16 value) {
cmdStopSound(obj, value);
_music->soundKill(musicSlot);
- PUT_SEL32V(_segMan, obj, SELECTOR(handle), 0);
+ writeSelectorValue(_segMan, obj, SELECTOR(handle), 0);
if (_soundVersion >= SCI_VERSION_1_EARLY)
- PUT_SEL32(_segMan, obj, SELECTOR(nodePtr), NULL_REG);
+ writeSelector(_segMan, obj, SELECTOR(nodePtr), NULL_REG);
else
- PUT_SEL32V(_segMan, obj, SELECTOR(state), kSoundStopped);
+ writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundStopped);
#endif
}
@@ -509,7 +509,7 @@ void SoundCommandParser::processStopSound(reg_t obj, int16 value, bool sampleFin
changeSoundStatus(obj, SOUND_STATUS_STOPPED);
if (_soundVersion >= SCI_VERSION_1_EARLY)
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
#else
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
@@ -518,9 +518,9 @@ void SoundCommandParser::processStopSound(reg_t obj, int16 value, bool sampleFin
}
if (_soundVersion <= SCI_VERSION_0_LATE) {
- PUT_SEL32V(_segMan, obj, SELECTOR(state), kSoundStopped);
+ writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundStopped);
} else {
- PUT_SEL32V(_segMan, obj, SELECTOR(handle), 0);
+ writeSelectorValue(_segMan, obj, SELECTOR(handle), 0);
}
// Set signal selector in sound SCI0 games only, when the sample has finished playing
@@ -530,7 +530,7 @@ void SoundCommandParser::processStopSound(reg_t obj, int16 value, bool sampleFin
// sfx drivers included
// We need to set signal in sound SCI1+ games all the time
if ((_soundVersion > SCI_VERSION_0_LATE) || sampleFinishedPlaying)
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
musicSlot->dataInc = 0;
musicSlot->signal = 0;
@@ -565,7 +565,7 @@ void SoundCommandParser::cmdPauseSound(reg_t obj, int16 value) {
if (_soundVersion <= SCI_VERSION_0_LATE) {
// Always pause the sound in SCI0 games. It's resumed in cmdResumeSound()
- PUT_SEL32V(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPaused);
+ writeSelectorValue(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPaused);
_music->soundPause(musicSlot);
} else {
_music->soundToggle(musicSlot, value);
@@ -590,7 +590,7 @@ void SoundCommandParser::cmdResumeSound(reg_t obj, int16 value) {
return;
}
- PUT_SEL32V(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPlaying);
+ writeSelectorValue(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPlaying);
_music->soundResume(musicSlot);
#endif
}
@@ -630,13 +630,12 @@ void SoundCommandParser::cmdFadeSound(reg_t obj, int16 value) {
#ifdef USE_OLD_MUSIC_FUNCTIONS
SongHandle handle = FROBNICATE_HANDLE(obj);
if (_soundVersion != SCI_VERSION_1_LATE) {
- /*s->sound_server->command(s, SOUND_COMMAND_FADE_HANDLE, obj, 120);*/ /* Fade out in 2 secs */
/* FIXME: The next couple of lines actually STOP the handle, rather
** than fading it! */
_state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED);
if (_soundVersion <= SCI_VERSION_0_LATE)
- PUT_SEL32V(_segMan, obj, SELECTOR(state), SOUND_STATUS_STOPPED);
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
+ writeSelectorValue(_segMan, obj, SELECTOR(state), SOUND_STATUS_STOPPED);
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
} else {
fade_params_t fade;
fade.final_volume = _argv[2].toUint16();
@@ -651,11 +650,11 @@ void SoundCommandParser::cmdFadeSound(reg_t obj, int16 value) {
/* FIXME: The next couple of lines actually STOP the handle, rather
** than fading it! */
if (_argv[5].toUint16()) {
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
_state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED);
} else {
// FIXME: Support fade-and-continue. For now, send signal right away.
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
}
}
#else
@@ -692,7 +691,7 @@ void SoundCommandParser::cmdFadeSound(reg_t obj, int16 value) {
// If sound is not playing currently, set signal directly
if (musicSlot->status != kSoundPlaying) {
warning("cmdFadeSound: fading requested, but sound is currently not playing");
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
}
debugC(2, kDebugLevelSound, "cmdFadeSound: to %d, step %d, ticker %d", musicSlot->fadeTo, musicSlot->fadeStep, musicSlot->fadeTickerStep);
@@ -714,8 +713,8 @@ void SoundCommandParser::cmdUpdateSound(reg_t obj, int16 value) {
#ifdef USE_OLD_MUSIC_FUNCTIONS
SongHandle handle = FROBNICATE_HANDLE(obj);
if (_soundVersion <= SCI_VERSION_0_LATE && obj.segment) {
- _state->sfx_song_set_loops(handle, GET_SEL32V(_segMan, obj, SELECTOR(loop)));
- script_set_priority(_resMan, _segMan, _state, obj, GET_SEL32V(_segMan, obj, SELECTOR(pri)));
+ _state->sfx_song_set_loops(handle, readSelectorValue(_segMan, obj, SELECTOR(loop)));
+ script_set_priority(_resMan, _segMan, _state, obj, readSelectorValue(_segMan, obj, SELECTOR(pri)));
}
#else
MusicEntry *musicSlot = _music->getSlot(obj);
@@ -724,11 +723,11 @@ void SoundCommandParser::cmdUpdateSound(reg_t obj, int16 value) {
return;
}
- musicSlot->loop = GET_SEL32V(_segMan, obj, SELECTOR(loop));
- int16 objVol = CLIP<int>(GET_SEL32V(_segMan, obj, SELECTOR(vol)), 0, 255);
+ musicSlot->loop = readSelectorValue(_segMan, obj, SELECTOR(loop));
+ int16 objVol = CLIP<int>(readSelectorValue(_segMan, obj, SELECTOR(vol)), 0, 255);
if (objVol != musicSlot->volume)
_music->soundSetVolume(musicSlot, objVol);
- uint32 objPrio = GET_SEL32V(_segMan, obj, SELECTOR(pri));
+ uint32 objPrio = readSelectorValue(_segMan, obj, SELECTOR(pri));
if (objPrio != musicSlot->priority)
_music->soundSetPriority(musicSlot, objPrio);
@@ -755,7 +754,7 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) {
debugC(2, kDebugLevelSound, "--- [CUE] %04x:%04x Absolute Cue: %d",
PRINT_REG(obj), signal);
debugC(2, kDebugLevelSound, "abs-signal %04X", signal);
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), signal);
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), signal);
break;
case SI_RELATIVE_CUE:
@@ -765,17 +764,17 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) {
/* FIXME to match commented-out semantics
* below, with proper storage of dataInc and
* signal in the iterator code. */
- PUT_SEL32V(_segMan, obj, SELECTOR(dataInc), signal);
+ writeSelectorValue(_segMan, obj, SELECTOR(dataInc), signal);
debugC(2, kDebugLevelSound, "rel-signal %04X", signal);
if (_soundVersion == SCI_VERSION_1_EARLY)
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), signal);
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), signal);
else
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), signal + 127);
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), signal + 127);
break;
case SI_FINISHED:
debugC(2, kDebugLevelSound, "--- [FINISHED] %04x:%04x", PRINT_REG(obj));
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
break;
case SI_LOOP:
@@ -784,30 +783,30 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) {
//switch (signal) {
//case 0x00:
- // if (dataInc != GET_SEL32V(segMan, obj, SELECTOR(dataInc))) {
- // PUT_SEL32V(segMan, obj, SELECTOR(dataInc), dataInc);
- // PUT_SEL32V(segMan, obj, SELECTOR(signal), dataInc+0x7f);
+ // if (dataInc != readSelectorValue(segMan, obj, SELECTOR(dataInc))) {
+ // writeSelectorValue(segMan, obj, SELECTOR(dataInc), dataInc);
+ // writeSelectorValue(segMan, obj, SELECTOR(signal), dataInc+0x7f);
// } else {
- // PUT_SEL32V(segMan, obj, SELECTOR(signal), signal);
+ // writeSelectorValue(segMan, obj, SELECTOR(signal), signal);
// }
// break;
//case 0xFF: // May be unnecessary
// s->_sound.sfx_song_set_status(handle, SOUND_STATUS_STOPPED);
// break;
//default :
- // if (dataInc != GET_SEL32V(segMan, obj, SELECTOR(dataInc))) {
- // PUT_SEL32V(segMan, obj, SELECTOR(dataInc), dataInc);
- // PUT_SEL32V(segMan, obj, SELECTOR(signal), dataInc + 0x7f);
+ // if (dataInc != readSelectorValue(segMan, obj, SELECTOR(dataInc))) {
+ // writeSelectorValue(segMan, obj, SELECTOR(dataInc), dataInc);
+ // writeSelectorValue(segMan, obj, SELECTOR(signal), dataInc + 0x7f);
// } else {
- // PUT_SEL32V(segMan, obj, SELECTOR(signal), signal);
+ // writeSelectorValue(segMan, obj, SELECTOR(signal), signal);
// }
// break;
//}
if (_soundVersion == SCI_VERSION_1_EARLY) {
- PUT_SEL32V(_segMan, obj, SELECTOR(min), min);
- PUT_SEL32V(_segMan, obj, SELECTOR(sec), sec);
- PUT_SEL32V(_segMan, obj, SELECTOR(frame), frame);
+ writeSelectorValue(_segMan, obj, SELECTOR(min), min);
+ writeSelectorValue(_segMan, obj, SELECTOR(sec), sec);
+ writeSelectorValue(_segMan, obj, SELECTOR(frame), frame);
}
#else
MusicEntry *musicSlot = _music->getSlot(obj);
@@ -828,7 +827,7 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) {
musicSlot->loop -= currentLoopCounter - musicSlot->sampleLoopCounter;
musicSlot->sampleLoopCounter = currentLoopCounter;
}
- if (!_music->soundIsActive(musicSlot)) {
+ if ((!_music->soundIsActive(musicSlot)) && (musicSlot->status != kSoundPaused)) {
processStopSound(obj, 0, true);
} else {
_music->updateAudioStreamTicker(musicSlot);
@@ -841,14 +840,14 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) {
} else if (musicSlot->pMidiParser) {
// Update MIDI slots
if (musicSlot->signal == 0) {
- if (musicSlot->dataInc != GET_SEL32V(_segMan, obj, SELECTOR(dataInc))) {
+ if (musicSlot->dataInc != readSelectorValue(_segMan, obj, SELECTOR(dataInc))) {
if (_kernel->_selectorCache.dataInc > -1)
- PUT_SEL32V(_segMan, obj, SELECTOR(dataInc), musicSlot->dataInc);
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), musicSlot->dataInc + 127);
+ writeSelectorValue(_segMan, obj, SELECTOR(dataInc), musicSlot->dataInc);
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), musicSlot->dataInc + 127);
}
} else {
// Sync the signal of the sound object
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), musicSlot->signal);
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), musicSlot->signal);
// We need to do this especially because state selector needs to get updated
if (musicSlot->signal == SIGNAL_OFFSET)
cmdStopSound(obj, 0);
@@ -856,14 +855,14 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) {
} else {
// Slot actually has no data (which would mean that a sound-resource w/ unsupported data is used
// (example lsl5 - sound resource 744 - it's roland exclusive
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
// If we don't set signal here, at least the switch to the mud wrestling room in lsl5 will not work
}
if (musicSlot->fadeCompleted) {
musicSlot->fadeCompleted = false;
// We need signal for sci0 at least in iceman as well (room 14, fireworks)
- PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
if (_soundVersion <= SCI_VERSION_0_LATE) {
cmdStopSound(obj, 0);
} else {
@@ -874,14 +873,14 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) {
// Sync loop selector for SCI0
if (_soundVersion <= SCI_VERSION_0_LATE)
- PUT_SEL32V(_segMan, obj, SELECTOR(loop), musicSlot->loop);
+ writeSelectorValue(_segMan, obj, SELECTOR(loop), musicSlot->loop);
musicSlot->signal = 0;
if (_soundVersion >= SCI_VERSION_1_EARLY) {
- PUT_SEL32V(_segMan, obj, SELECTOR(min), musicSlot->ticker / 3600);
- PUT_SEL32V(_segMan, obj, SELECTOR(sec), musicSlot->ticker % 3600 / 60);
- PUT_SEL32V(_segMan, obj, SELECTOR(frame), musicSlot->ticker);
+ writeSelectorValue(_segMan, obj, SELECTOR(min), musicSlot->ticker / 3600);
+ writeSelectorValue(_segMan, obj, SELECTOR(sec), musicSlot->ticker % 3600 / 60);
+ writeSelectorValue(_segMan, obj, SELECTOR(frame), musicSlot->ticker);
}
#endif
@@ -930,10 +929,10 @@ void SoundCommandParser::cmdStopAllSounds(reg_t obj, int16 value) {
const MusicList::iterator end = _music->getPlayListEnd();
for (MusicList::iterator i = _music->getPlayListStart(); i != end; ++i) {
if (_soundVersion <= SCI_VERSION_0_LATE) {
- PUT_SEL32V(_segMan, (*i)->soundObj, SELECTOR(state), kSoundStopped);
+ writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(state), kSoundStopped);
} else {
- PUT_SEL32V(_segMan, obj, SELECTOR(handle), 0);
- PUT_SEL32V(_segMan, (*i)->soundObj, SELECTOR(signal), SIGNAL_OFFSET);
+ writeSelectorValue(_segMan, obj, SELECTOR(handle), 0);
+ writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(signal), SIGNAL_OFFSET);
}
(*i)->dataInc = 0;
@@ -964,7 +963,7 @@ void SoundCommandParser::cmdSetSoundVolume(reg_t obj, int16 value) {
if (musicSlot->volume != value) {
musicSlot->volume = value;
_music->soundSetVolume(musicSlot, value);
- PUT_SEL32V(_segMan, obj, SELECTOR(vol), value);
+ writeSelectorValue(_segMan, obj, SELECTOR(vol), value);
}
#endif
}
@@ -991,12 +990,12 @@ void SoundCommandParser::cmdSetSoundPriority(reg_t obj, int16 value) {
warning("cmdSetSoundPriority: Attempt to unset song priority when there is no built-in value");
//pSnd->prio=0;field_15B=0
- PUT_SEL32V(_segMan, obj, SELECTOR(flags), GET_SEL32V(_segMan, obj, SELECTOR(flags)) & 0xFD);
+ writeSelectorValue(_segMan, obj, SELECTOR(flags), readSelectorValue(_segMan, obj, SELECTOR(flags)) & 0xFD);
} else {
// Scripted priority
//pSnd->field_15B=1;
- PUT_SEL32V(_segMan, obj, SELECTOR(flags), GET_SEL32V(_segMan, obj, SELECTOR(flags)) | 2);
+ writeSelectorValue(_segMan, obj, SELECTOR(flags), readSelectorValue(_segMan, obj, SELECTOR(flags)) | 2);
//DoSOund(0xF,hobj,w)
}
#endif
@@ -1007,7 +1006,7 @@ void SoundCommandParser::cmdSetSoundLoop(reg_t obj, int16 value) {
return;
#ifdef USE_OLD_MUSIC_FUNCTIONS
- if (!GET_SEL32(_segMan, obj, SELECTOR(nodePtr)).isNull()) {
+ if (!readSelector(_segMan, obj, SELECTOR(nodePtr)).isNull()) {
SongHandle handle = FROBNICATE_HANDLE(obj);
_state->sfx_song_set_loops(handle, value);
}
@@ -1032,7 +1031,7 @@ void SoundCommandParser::cmdSetSoundLoop(reg_t obj, int16 value) {
musicSlot->loop = 1; // actually plays the music once
}
- PUT_SEL32V(_segMan, obj, SELECTOR(loop), musicSlot->loop);
+ writeSelectorValue(_segMan, obj, SELECTOR(loop), musicSlot->loop);
#endif
}
@@ -1103,11 +1102,11 @@ void SoundCommandParser::reconstructPlayList(int savegame_version) {
}
if ((*i)->status == kSoundPlaying) {
if (savegame_version < 14) {
- (*i)->dataInc = GET_SEL32V(_segMan, (*i)->soundObj, SELECTOR(dataInc));
- (*i)->signal = GET_SEL32V(_segMan, (*i)->soundObj, SELECTOR(signal));
+ (*i)->dataInc = readSelectorValue(_segMan, (*i)->soundObj, SELECTOR(dataInc));
+ (*i)->signal = readSelectorValue(_segMan, (*i)->soundObj, SELECTOR(signal));
if (_soundVersion >= SCI_VERSION_1_LATE)
- (*i)->volume = GET_SEL32V(_segMan, (*i)->soundObj, SELECTOR(vol));
+ (*i)->volume = readSelectorValue(_segMan, (*i)->soundObj, SELECTOR(vol));
}
cmdPlaySound((*i)->soundObj, 0);
@@ -1143,7 +1142,7 @@ void SoundCommandParser::startNewSound(int number) {
MusicEntry *song = *_music->getPlayListStart();
reg_t soundObj = song->soundObj;
cmdDisposeSound(soundObj, 0);
- PUT_SEL32V(_segMan, soundObj, SELECTOR(number), number);
+ writeSelectorValue(_segMan, soundObj, SELECTOR(number), number);
cmdInitSound(soundObj, 0);
cmdPlaySound(soundObj, 0);
#endif
diff --git a/engines/scumm/debugger.cpp b/engines/scumm/debugger.cpp
index a0975839d6..ea29e25a1f 100644
--- a/engines/scumm/debugger.cpp
+++ b/engines/scumm/debugger.cpp
@@ -870,7 +870,7 @@ bool ScummDebugger::Cmd_Passcode(int argc, const char **argv) {
_detach_now = true;
} else {
- DebugPrintf("Use 'passcode <SEGA CD Passcode>'\n");
+ DebugPrintf("Current Passcode is %d \nUse 'passcode <SEGA CD Passcode>'\n",_vm->_scummVars[411]);
return true;
}
return false;
diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp
index 1f153094c1..d9c24ddca2 100644
--- a/engines/scumm/dialogs.cpp
+++ b/engines/scumm/dialogs.cpp
@@ -233,19 +233,6 @@ protected:
#endif
-class ConfigDialog : public GUI::OptionsDialog {
-protected:
-#ifdef SMALL_SCREEN_DEVICE
- GUI::Dialog *_keysDialog;
-#endif
-
-public:
- ConfigDialog();
- ~ConfigDialog();
-
- virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data);
-};
-
#pragma mark -
ScummDialog::ScummDialog(int x, int y, int w, int h) : GUI::Dialog(x, y, w, h) {
@@ -258,223 +245,31 @@ ScummDialog::ScummDialog(String name) : GUI::Dialog(name) {
#pragma mark -
-enum {
- kSaveCmd = 'SAVE',
- kLoadCmd = 'LOAD',
- kPlayCmd = 'PLAY',
- kOptionsCmd = 'OPTN',
- kHelpCmd = 'HELP',
- kAboutCmd = 'ABOU',
- kQuitCmd = 'QUIT',
- kChooseCmd = 'CHOS'
-};
-
-ScummMenuDialog::ScummMenuDialog(ScummEngine *scumm)
- : ScummDialog("ScummMain"), _vm(scumm) {
-
- new GUI::ButtonWidget(this, "ScummMain.Resume", "Resume", kPlayCmd, 'P');
-
- _loadButton = new GUI::ButtonWidget(this, "ScummMain.Load", "Load", kLoadCmd, 'L');
- _saveButton = new GUI::ButtonWidget(this, "ScummMain.Save", "Save", kSaveCmd, 'S');
-
- new GUI::ButtonWidget(this, "ScummMain.Options", "Options", kOptionsCmd, 'O');
#ifndef DISABLE_HELP
- new GUI::ButtonWidget(this, "ScummMain.Help", "Help", kHelpCmd, 'H');
-#endif
- new GUI::ButtonWidget(this, "ScummMain.About", "About", kAboutCmd, 'A');
-
- new GUI::ButtonWidget(this, "ScummMain.Quit", "Quit", kQuitCmd, 'Q');
- //
- // Create the sub dialog(s)
- //
- _aboutDialog = new GUI::AboutDialog();
- _optionsDialog = new ConfigDialog();
-#ifndef DISABLE_HELP
+ScummMenuDialog::ScummMenuDialog(ScummEngine *scumm)
+ : MainMenuDialog(scumm) {
_helpDialog = new HelpDialog(scumm->_game);
-#endif
- _saveDialog = new GUI::SaveLoadChooser("Save game:", "Save");
- _saveDialog->setSaveMode(true);
- _loadDialog = new GUI::SaveLoadChooser("Load game:", "Load");
- _loadDialog->setSaveMode(false);
+ _helpButton->setEnabled(true);
}
ScummMenuDialog::~ScummMenuDialog() {
- delete _aboutDialog;
- delete _optionsDialog;
-#ifndef DISABLE_HELP
delete _helpDialog;
-#endif
- delete _saveDialog;
- delete _loadDialog;
-}
-
-int ScummMenuDialog::runModal() {
- _loadButton->setEnabled(_vm->canLoadGameStateCurrently());
- _saveButton->setEnabled(_vm->canSaveGameStateCurrently());
- return ScummDialog::runModal();
-}
-
-void ScummMenuDialog::reflowLayout() {
- _loadButton->setEnabled(_vm->canLoadGameStateCurrently());
- _saveButton->setEnabled(_vm->canSaveGameStateCurrently());
- Dialog::reflowLayout();
}
void ScummMenuDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
switch (cmd) {
- case kSaveCmd:
- save();
- break;
- case kLoadCmd:
- load();
- break;
- case kPlayCmd:
- close();
- break;
- case kOptionsCmd:
- _optionsDialog->runModal();
- break;
- case kAboutCmd:
- _aboutDialog->runModal();
- break;
-#ifndef DISABLE_HELP
case kHelpCmd:
_helpDialog->runModal();
break;
-#endif
- case kQuitCmd:
- _vm->quitGame();
- close();
- break;
default:
- ScummDialog::handleCommand(sender, cmd, data);
- }
-}
-
-void ScummMenuDialog::save() {
- Common::String gameId = ConfMan.get("gameid");
-
- const EnginePlugin *plugin = 0;
- EngineMan.findGame(gameId, &plugin);
-
- int idx = _saveDialog->runModal(plugin, ConfMan.getActiveDomainName());
- if (idx >= 0) {
- String result(_saveDialog->getResultString());
- char buffer[20];
- const char *str;
- if (result.empty()) {
- // If the user was lazy and entered no save name, come up with a default name.
- sprintf(buffer, "Save %d", idx);
- str = buffer;
- } else
- str = result.c_str();
- _vm->requestSave(idx, str);
- close();
- }
-}
-
-void ScummMenuDialog::load() {
- Common::String gameId = ConfMan.get("gameid");
-
- const EnginePlugin *plugin = 0;
- EngineMan.findGame(gameId, &plugin);
-
- int idx = _loadDialog->runModal(plugin, ConfMan.getActiveDomainName());
- if (idx >= 0) {
- _vm->requestLoad(idx);
- close();
+ MainMenuDialog::handleCommand(sender, cmd, data);
}
}
#pragma mark -
enum {
- kKeysCmd = 'KEYS'
-};
-
-// FIXME: We use the empty string as domain name here. This tells the
-// ConfigManager to use the 'default' domain for all its actions. We do that
-// to get as close as possible to editing the 'active' settings.
-//
-// However, that requires bad & evil hacks in the ConfigManager code,
-// and even then still doesn't work quite correctly.
-// For example, if the transient domain contains 'false' for the 'fullscreen'
-// flag, but the user used a hotkey to switch to windowed mode, then the dialog
-// will display the wrong value anyway.
-//
-// Proposed solution consisting of multiple steps:
-// 1) Add special code to the open() code that reads out everything stored
-// in the transient domain that is controlled by this dialog, and updates
-// the dialog accordingly.
-// 2) Even more code is added to query the backend for current settings, like
-// the fullscreen mode flag etc., and also updates the dialog accordingly.
-// 3) The domain being edited is set to the active game domain.
-// 4) If the dialog is closed with the "OK" button, then we remove everything
-// stored in the transient domain (or at least everything corresponding to
-// switches in this dialog.
-// If OTOH the dialog is closed with "Cancel" we do no such thing.
-//
-// These changes will achieve two things at once: Allow us to get rid of using
-// "" as value for the domain, and in fact provide a somewhat better user
-// experience at the same time.
-ConfigDialog::ConfigDialog()
- : GUI::OptionsDialog("", "ScummConfig") {
-
- //
- // Sound controllers
- //
-
- addVolumeControls(this, "ScummConfig.");
-
- //
- // Some misc options
- //
-
- // SCUMM has a talkspeed range of 0-9
- addSubtitleControls(this, "ScummConfig.", 9);
-
- //
- // Add the buttons
- //
-
- new GUI::ButtonWidget(this, "ScummConfig.Ok", "OK", GUI::kOKCmd, 'O');
- new GUI::ButtonWidget(this, "ScummConfig.Cancel", "Cancel", GUI::kCloseCmd, 'C');
-#ifdef SMALL_SCREEN_DEVICE
- new GUI::ButtonWidget(this, "ScummConfig.Keys", "Keys", kKeysCmd, 'K');
- _keysDialog = NULL;
-#endif
-}
-
-ConfigDialog::~ConfigDialog() {
-#ifdef SMALL_SCREEN_DEVICE
- delete _keysDialog;
-#endif
-}
-
-void ConfigDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
- switch (cmd) {
- case kKeysCmd:
-#ifdef SMALL_SCREEN_DEVICE
- //
- // Create the sub dialog(s)
- //
- _keysDialog = new GUI::KeysDialog();
- _keysDialog->runModal();
- delete _keysDialog;
- _keysDialog = NULL;
-#endif
- break;
- default:
- GUI::OptionsDialog::handleCommand (sender, cmd, data);
- }
-}
-
-#ifndef DISABLE_HELP
-
-#pragma mark -
-
-enum {
kNextCmd = 'NEXT',
kPrevCmd = 'PREV'
};
diff --git a/engines/scumm/dialogs.h b/engines/scumm/dialogs.h
index 7889027dcf..41a8ec83c1 100644
--- a/engines/scumm/dialogs.h
+++ b/engines/scumm/dialogs.h
@@ -27,9 +27,8 @@
#include "common/str.h"
#include "gui/dialog.h"
-#include "gui/options.h"
#include "gui/widget.h"
-#include "gui/saveload.h"
+#include "engines/dialogs.h"
#include "scumm/detection.h"
@@ -52,32 +51,17 @@ protected:
typedef Common::String String;
};
-class ScummMenuDialog : public ScummDialog {
+#ifndef DISABLE_HELP
+class ScummMenuDialog : public MainMenuDialog {
public:
ScummMenuDialog(ScummEngine *scumm);
~ScummMenuDialog();
virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data);
- virtual void reflowLayout();
-
- int runModal();
protected:
- ScummEngine *_vm;
-
- GUI::Dialog *_aboutDialog;
- GUI::Dialog *_optionsDialog;
-#ifndef DISABLE_HELP
GUI::Dialog *_helpDialog;
-#endif
- GUI::SaveLoadChooser *_saveDialog;
- GUI::SaveLoadChooser *_loadDialog;
-
- GUI::ButtonWidget *_loadButton;
- GUI::ButtonWidget *_saveButton;
-
- void save();
- void load();
};
+#endif
/**
* A dialog which displays an arbitrary message to the user and returns
diff --git a/engines/scumm/he/resource_he.cpp b/engines/scumm/he/resource_he.cpp
index 886ee99e57..c259c3ffd2 100644
--- a/engines/scumm/he/resource_he.cpp
+++ b/engines/scumm/he/resource_he.cpp
@@ -633,8 +633,10 @@ Win32ResExtractor::WinResource *Win32ResExtractor::list_pe_resources(WinLibrary
wr[c].children = fi->first_resource + (FROM_LE_32(dirent[c].offset_to_data) & ~IMAGE_RESOURCE_DATA_IS_DIRECTORY);
/* fill in wr->id, wr->numeric_id */
- if (!decode_pe_resource_id(fi, wr + c, FROM_LE_32(dirent[c].name)))
+ if (!decode_pe_resource_id(fi, wr + c, FROM_LE_32(dirent[c].name))) {
+ free(wr);
return NULL;
+ }
}
return wr;
diff --git a/engines/scumm/imuse/instrument.cpp b/engines/scumm/imuse/instrument.cpp
index efe15f308f..57a842f297 100644
--- a/engines/scumm/imuse/instrument.cpp
+++ b/engines/scumm/imuse/instrument.cpp
@@ -421,11 +421,11 @@ Instrument_Roland::Instrument_Roland(Serializer *s) {
memset(&_instrument, 0, sizeof(_instrument));
}
-void Instrument_Roland::saveOrLoad (Serializer *s) {
+void Instrument_Roland::saveOrLoad(Serializer *s) {
if (s->isSaving()) {
- s->saveBytes (&_instrument, sizeof(_instrument));
+ s->saveBytes(&_instrument, sizeof(_instrument));
} else {
- s->loadBytes (&_instrument, sizeof(_instrument));
+ s->loadBytes(&_instrument, sizeof(_instrument));
memcpy(&_instrument_name, &_instrument.common.name, sizeof(_instrument.common.name));
_instrument_name[10] = '\0';
if (!_native_mt32 && getEquivalentGM() >= 128) {
diff --git a/engines/scumm/input.cpp b/engines/scumm/input.cpp
index 8a9570f534..dc3a5d26b3 100644
--- a/engines/scumm/input.cpp
+++ b/engines/scumm/input.cpp
@@ -508,7 +508,7 @@ void ScummEngine::processKeyboard(Common::KeyState lastKeyHit) {
if (VAR_SAVELOAD_SCRIPT != 0xFF && _currentRoom != 0)
runScript(VAR(VAR_SAVELOAD_SCRIPT), 0, 0, 0);
- scummMenuDialog(); // Display GUI
+ openMainMenuDialog(); // Display global main menu
if (VAR_SAVELOAD_SCRIPT != 0xFF && _currentRoom != 0)
runScript(VAR(VAR_SAVELOAD_SCRIPT2), 0, 0, 0);
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 2359d4a04f..bb50ce7bb2 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -108,7 +108,7 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
_language(dr.language),
_debugger(0),
_currentScript(0xFF), // Let debug() work on init stage
- _messageDialog(0), _pauseDialog(0), _scummMenuDialog(0), _versionDialog(0) {
+ _messageDialog(0), _pauseDialog(0), _versionDialog(0) {
if (_game.platform == Common::kPlatformNES) {
_gdi = new GdiNES(this);
@@ -140,7 +140,6 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
_fileHandle = 0;
-
// Init all vars
_v0ObjectIndex = false;
_v0ObjectInInventory = false;
@@ -152,7 +151,6 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
_sound = NULL;
memset(&vm, 0, sizeof(vm));
_pauseDialog = NULL;
- _scummMenuDialog = NULL;
_versionDialog = NULL;
_fastMode = 0;
_actors = NULL;
@@ -552,6 +550,12 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
for (int i = 0; i < ARRAYSIZE(debugChannels); ++i)
DebugMan.addDebugChannel(debugChannels[i].flag, debugChannels[i].channel, debugChannels[i].desc);
+#ifndef DISABLE_HELP
+ // Create custom GMM dialog providing a help subdialog
+ assert(!_mainMenuDialog);
+ _mainMenuDialog = new ScummMenuDialog(this);
+#endif
+
g_eventRec.registerRandomSource(_rnd, "scumm");
}
@@ -572,7 +576,6 @@ ScummEngine::~ScummEngine() {
delete _charset;
delete _messageDialog;
delete _pauseDialog;
- delete _scummMenuDialog;
delete _versionDialog;
delete _fileHandle;
@@ -2437,13 +2440,6 @@ void ScummEngine::versionDialog() {
runDialog(*_versionDialog);
}
-void ScummEngine::scummMenuDialog() {
- if (!_scummMenuDialog)
- _scummMenuDialog = new ScummMenuDialog(this);
- runDialog(*_scummMenuDialog);
- syncSoundSettings();
-}
-
void ScummEngine::confirmExitDialog() {
ConfirmDialog d(this, 6);
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 885ab790de..42322ba5a2 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -530,7 +530,6 @@ protected:
Dialog *_pauseDialog;
Dialog *_messageDialog;
Dialog *_versionDialog;
- Dialog *_scummMenuDialog;
virtual int runDialog(Dialog &dialog);
void confirmExitDialog();
@@ -538,7 +537,6 @@ protected:
void pauseDialog();
void messageDialog(const char *message);
void versionDialog();
- void scummMenuDialog();
char displayMessage(const char *altButton, const char *message, ...) GCC_PRINTF(3, 4);
diff --git a/engines/tinsel/handle.cpp b/engines/tinsel/handle.cpp
index de573feee2..60eb08a2dd 100644
--- a/engines/tinsel/handle.cpp
+++ b/engines/tinsel/handle.cpp
@@ -288,7 +288,8 @@ void LoadFile(MEMHANDLE *pH) {
}
// extract and zero terminate the filename
- Common::strlcpy(szFilename, pH->szName, sizeof(pH->szName));
+ memcpy(szFilename, pH->szName, sizeof(pH->szName));
+ szFilename[sizeof(pH->szName)] = 0;
if (f.open(szFilename)) {
// read the data
diff --git a/engines/tinsel/saveload.cpp b/engines/tinsel/saveload.cpp
index b90ace4613..b010ad1fcb 100644
--- a/engines/tinsel/saveload.cpp
+++ b/engines/tinsel/saveload.cpp
@@ -180,7 +180,8 @@ static void syncSavedMover(Common::Serializer &s, SAVED_MOVER &sm) {
static void syncSavedActor(Common::Serializer &s, SAVED_ACTOR &sa) {
s.syncAsUint16LE(sa.actorID);
s.syncAsUint16LE(sa.zFactor);
- s.syncAsUint32LE(sa.bAlive);
+ s.syncAsUint16LE(sa.bAlive);
+ s.syncAsUint16LE(sa.bHidden);
s.syncAsUint32LE(sa.presFilm);
s.syncAsUint16LE(sa.presRnum);
s.syncAsUint16LE(sa.presPlayX);
diff --git a/engines/tucker/sequences.cpp b/engines/tucker/sequences.cpp
index 68f5301a80..633ed2790d 100644
--- a/engines/tucker/sequences.cpp
+++ b/engines/tucker/sequences.cpp
@@ -491,7 +491,6 @@ AnimationSequencePlayer::AnimationSequencePlayer(OSystem *system, Audio::Mixer *
_offscreenBuffer = (uint8 *)malloc(kScreenWidth * kScreenHeight);
_updateScreenWidth = 0;
_updateScreenPicture = false;
- _updateScreenOffset = 0;
_picBufPtr = _pic2BufPtr = 0;
}
@@ -537,9 +536,9 @@ void AnimationSequencePlayer::mainLoop() {
}
// budttle2.flc is shorter in french version ; start the background music
// earlier and skip any sounds effects
- if (_seqNum == 19 && _flicPlayer[0].getFrameCount() == 127) {
+ if (_seqNum == 19 && _flicPlayer[0].getFrameCount() == 126) {
_soundSeqDataIndex = 6;
- _frameCounter = 79;
+ _frameCounter = 80;
}
}
(this->*(_updateFunc[_updateFuncIndex].play))();
@@ -765,10 +764,10 @@ void AnimationSequencePlayer::openAnimation(int index, const char *fileName) {
}
}
-bool AnimationSequencePlayer::decodeNextAnimationFrame(int index) {
+bool AnimationSequencePlayer::decodeNextAnimationFrame(int index, bool copyDirtyRects) {
::Graphics::Surface *surface = _flicPlayer[index].decodeNextFrame();
- if (_seqNum == 19) {
+ if (!copyDirtyRects) {
for (uint16 y = 0; (y < surface->h) && (y < kScreenHeight); y++)
memcpy(_offscreenBuffer + y * kScreenWidth, (byte *)surface->pixels + y * surface->pitch, surface->w);
} else {
@@ -807,13 +806,13 @@ void AnimationSequencePlayer::playIntroSeq19_20() {
// cogs, and is being replayed when an intro credit appears
::Graphics::Surface *surface = 0;
- if (_flicPlayer[0].getCurFrame() >= 117) {
+ if (_flicPlayer[0].getCurFrame() >= 115) {
surface = _flicPlayer[1].decodeNextFrame();
if (_flicPlayer[1].endOfVideo())
_flicPlayer[1].reset();
}
- bool framesLeft = decodeNextAnimationFrame(0);
+ bool framesLeft = decodeNextAnimationFrame(0, false);
if (surface)
for (int i = 0; i < kScreenWidth * kScreenHeight; ++i)
@@ -841,19 +840,28 @@ void AnimationSequencePlayer::displayLoadingScreen() {
void AnimationSequencePlayer::initPicPart4() {
_updateScreenWidth = 320;
_updateScreenPicture = true;
- _updateScreenOffset = 0;
+ _updateScreenCounter = 0;
+ _updateScreenIndex = -1;
}
void AnimationSequencePlayer::drawPicPart4() {
- static const uint8 offsetsTable[77] = {
- 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4,
- 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 3, 3, 3,
- 3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1
- };
- _updateScreenWidth = _updateScreenWidth - offsetsTable[_updateScreenOffset];
- ++_updateScreenOffset;
+ static const uint8 offsets[] = { 1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1 };
+ if (_updateScreenIndex == -1) {
+ for (int i = 0; i < 256; ++i) {
+ if (memcmp(_animationPalette + i * 4, _picBufPtr + 32 + i * 3, 3) != 0) {
+ memcpy(_animationPalette + i * 4, _picBufPtr + 32 + i * 3, 3);
+ _animationPalette[i * 4 + 3] = 0;
+ }
+ }
+ }
+ if (_updateScreenCounter == 0) {
+ static const uint8 counter[] = { 1, 2, 3, 4, 5, 35, 5, 4, 3, 2, 1 };
+ ++_updateScreenIndex;
+ assert(_updateScreenIndex < ARRAYSIZE(counter));
+ _updateScreenCounter = counter[_updateScreenIndex];
+ }
+ --_updateScreenCounter;
+ _updateScreenWidth -= offsets[_updateScreenIndex];
for (int y = 0; y < 200; ++y) {
memcpy(_offscreenBuffer + y * 320, _picBufPtr + 800 + y * 640 + _updateScreenWidth, 320);
}
@@ -875,7 +883,7 @@ void AnimationSequencePlayer::loadIntroSeq3_4() {
void AnimationSequencePlayer::playIntroSeq3_4() {
if (!_updateScreenPicture) {
bool framesLeft = decodeNextAnimationFrame(0);
- if (_flicPlayer[0].getCurFrame() == 707) {
+ if (_flicPlayer[0].getCurFrame() == 705) {
initPicPart4();
}
if (!framesLeft) {
@@ -914,17 +922,10 @@ void AnimationSequencePlayer::drawPic2Part10() {
}
void AnimationSequencePlayer::drawPic1Part10() {
- ::Graphics::Surface *surface = _flicPlayer[0].decodeNextFrame();
- _flicPlayer[0].copyDirtyRectsToBuffer(_offscreenBuffer, kScreenWidth);
- ++_frameCounter;
-
- if (_flicPlayer[0].hasDirtyPalette())
- getRGBPalette(0);
-
int offset = 0;
for (int y = 0; y < kScreenHeight; ++y) {
for (int x = 0; x < kScreenWidth; ++x) {
- byte color = *((byte *)surface->pixels + offset);
+ byte color = _offscreenBuffer[offset];
if (color == 0)
color = _picBufPtr[800 + y * 640 + _updateScreenWidth + x];
@@ -943,22 +944,24 @@ void AnimationSequencePlayer::loadIntroSeq9_10() {
}
void AnimationSequencePlayer::playIntroSeq9_10() {
- if (_flicPlayer[0].getCurFrame() >= 265 && _flicPlayer[0].getCurFrame() <= 296) {
+ const int nextFrame = _flicPlayer[0].getCurFrame() + 1;
+ if (nextFrame >= 263 && nextFrame <= 294) {
+ decodeNextAnimationFrame(0, false);
drawPic1Part10();
_updateScreenWidth += 6;
- } else if (_flicPlayer[0].getCurFrame() == 985) {
+ } else if (nextFrame == 983) {
decodeNextAnimationFrame(0);
drawPic2Part10();
- } else if (_flicPlayer[0].getCurFrame() >= 989 && _flicPlayer[0].getCurFrame() <= 997) {
+ } else if (nextFrame >= 987 && nextFrame <= 995) {
+ decodeNextAnimationFrame(0, false);
drawPic1Part10();
_updateScreenWidth -= 25;
if (_updateScreenWidth < 0) {
_updateScreenWidth = 0;
}
- }
-
- if (_flicPlayer[0].endOfVideo())
+ } else if (!decodeNextAnimationFrame(0)) {
_changeToNextSequence = true;
+ }
}
void AnimationSequencePlayer::loadIntroSeq21_22() {
diff --git a/engines/tucker/tucker.h b/engines/tucker/tucker.h
index d9810c7929..86f5843e77 100644
--- a/engines/tucker/tucker.h
+++ b/engines/tucker/tucker.h
@@ -934,7 +934,7 @@ private:
void unloadAnimation();
uint8 *loadPicture(const char *fileName);
void openAnimation(int index, const char *fileName);
- bool decodeNextAnimationFrame(int index);
+ bool decodeNextAnimationFrame(int index, bool copyDirtyRects = true);
void loadIntroSeq17_18();
void playIntroSeq17_18();
void loadIntroSeq19_20();
@@ -975,7 +975,8 @@ private:
uint8 *_offscreenBuffer;
int _updateScreenWidth;
int _updateScreenPicture;
- int _updateScreenOffset;
+ int _updateScreenCounter;
+ int _updateScreenIndex;
int _frameCounter;
int _frameTime;
uint32 _lastFrameTime;
diff --git a/graphics/font.h b/graphics/font.h
index 6927e0e076..0e65e9418c 100644
--- a/graphics/font.h
+++ b/graphics/font.h
@@ -67,10 +67,10 @@ public:
int getStringWidth(const Common::String &str) const;
/**
- * Take a text (which may contain newlines characters) and word wrap it so thata
+ * Take a text (which may contain newline characters) and word wrap it so that
* no text line is wider than maxWidth pixels. If necessary, additional line breaks
- * are generated, preferably between words (i.e. were whitespaces are).
- * The resulting lines are appended to the string list lines.
+ * are generated, preferably between words (i.e. where whitespaces are).
+ * The resulting lines are appended to the lines string list.
* It returns the maximal width of any of the new lines (i.e. a value which is less
* or equal to maxWidth).
*
diff --git a/graphics/video/avi_decoder.cpp b/graphics/video/avi_decoder.cpp
index f19ca4aa28..4c3c770c60 100644
--- a/graphics/video/avi_decoder.cpp
+++ b/graphics/video/avi_decoder.cpp
@@ -154,10 +154,8 @@ void AviDecoder::handleStreamHeader() {
sHeader.bufferSize = _fileStream->readUint32LE();
sHeader.quality = _fileStream->readUint32LE();
sHeader.sampleSize = _fileStream->readUint32LE();
- sHeader.frame.left = _fileStream->readSint16LE();
- sHeader.frame.top = _fileStream->readSint16LE();
- sHeader.frame.right = _fileStream->readSint16LE();
- sHeader.frame.bottom = _fileStream->readSint16LE();
+
+ _fileStream->skip(sHeader.size - 48); // Skip over the remainder of the chunk (frame)
if (_fileStream->readUint32BE() != ID_STRF)
error("Could not find STRF tag");
@@ -188,11 +186,14 @@ void AviDecoder::handleStreamHeader() {
_palette[i * 3 + 2] = _fileStream->readByte();
_palette[i * 3 + 1] = _fileStream->readByte();
_palette[i * 3] = _fileStream->readByte();
- /*_palette[i * 4 + 3] = */_fileStream->readByte();
+ _fileStream->readByte();
}
_dirtyPalette = true;
}
+
+ if (!_vidsHeader.streamHandler)
+ _vidsHeader.streamHandler = _bmInfo.compression;
} else if (sHeader.streamType == ID_AUDS) {
_audsHeader = sHeader;
@@ -202,6 +203,11 @@ void AviDecoder::handleStreamHeader() {
_wvInfo.avgBytesPerSec = _fileStream->readUint32LE();
_wvInfo.blockAlign = _fileStream->readUint16LE();
_wvInfo.size = _fileStream->readUint16LE();
+
+ // AVI seems to treat the sampleSize as including the second
+ // channel as well, so divide for our sake.
+ if (_wvInfo.channels == 2)
+ _audsHeader.sampleSize /= 2;
}
}
@@ -325,6 +331,9 @@ Surface *AviDecoder::decodeNextFrame() {
else
flags |= Audio::FLAG_UNSIGNED;
+ if (_wvInfo.channels == 2)
+ flags |= Audio::FLAG_STEREO;
+
_audStream->queueBuffer(data, chunkSize, DisposeAfterUse::YES, flags);
_fileStream->skip(chunkSize & 1); // Alignment
} else if (getStreamType(nextTag) == 'dc' || getStreamType(nextTag) == 'id' ||
@@ -396,7 +405,7 @@ PixelFormat AviDecoder::getPixelFormat() const {
Audio::QueuingAudioStream *AviDecoder::createAudioStream() {
if (_wvInfo.tag == AVI_WAVE_FORMAT_PCM)
- return Audio::makeQueuingAudioStream(AUDIO_RATE, false);
+ return Audio::makeQueuingAudioStream(AUDIO_RATE, _wvInfo.channels == 2);
if (_wvInfo.tag != 0) // No sound
warning ("Unsupported AVI audio format %d", _wvInfo.tag);
diff --git a/graphics/video/qt_decoder.cpp b/graphics/video/qt_decoder.cpp
index 5e50772024..49d2b0aed9 100644
--- a/graphics/video/qt_decoder.cpp
+++ b/graphics/video/qt_decoder.cpp
@@ -146,9 +146,8 @@ PixelFormat QuickTimeDecoder::getPixelFormat() const {
}
void QuickTimeDecoder::rewind() {
- delete _videoCodec; _videoCodec = NULL;
- _curFrame = -1;
- _startTime = _nextFrameStartTime = 0;
+ VideoDecoder::reset();
+ _nextFrameStartTime = 0;
// Restart the audio too
stopAudio();
@@ -243,11 +242,7 @@ Surface *QuickTimeDecoder::scaleSurface(Surface *frame) {
}
bool QuickTimeDecoder::endOfVideo() const {
- return (!_audStream || _audStream->endOfData()) && (!_videoCodec || _curFrame >= (int32)getFrameCount() - 1);
-}
-
-bool QuickTimeDecoder::needsUpdate() const {
- return !endOfVideo() && getTimeToNextFrame() == 0;
+ return (!_audStream || _audStream->endOfData()) && (!_videoCodec || VideoDecoder::endOfVideo());
}
uint32 QuickTimeDecoder::getElapsedTime() const {
@@ -284,7 +279,12 @@ bool QuickTimeDecoder::loadFile(const Common::String &filename) {
MOVatom atom = { 0, 0, 0xffffffff };
if (_resFork->hasResFork()) {
- _fd = _resFork->getResource(MKID_BE('moov'), 0x80);
+ // Search for a 'moov' resource
+ Common::MacResIDArray idArray = _resFork->getResIDArray(MKID_BE('moov'));
+
+ if (!idArray.empty())
+ _fd = _resFork->getResource(MKID_BE('moov'), idArray[0]);
+
if (_fd) {
atom.size = _fd->size();
if (readDefault(atom) < 0 || !_foundMOOV)
@@ -384,31 +384,31 @@ void QuickTimeDecoder::init() {
void QuickTimeDecoder::initParseTable() {
static const ParseTable p[] = {
- { MKID_BE('dinf'), &QuickTimeDecoder::readDefault },
- { MKID_BE('dref'), &QuickTimeDecoder::readLeaf },
- { MKID_BE('edts'), &QuickTimeDecoder::readDefault },
- { MKID_BE('elst'), &QuickTimeDecoder::readELST },
- { MKID_BE('hdlr'), &QuickTimeDecoder::readHDLR },
- { MKID_BE('mdat'), &QuickTimeDecoder::readMDAT },
- { MKID_BE('mdhd'), &QuickTimeDecoder::readMDHD },
- { MKID_BE('mdia'), &QuickTimeDecoder::readDefault },
- { MKID_BE('minf'), &QuickTimeDecoder::readDefault },
- { MKID_BE('moov'), &QuickTimeDecoder::readMOOV },
- { MKID_BE('mvhd'), &QuickTimeDecoder::readMVHD },
- { MKID_BE('smhd'), &QuickTimeDecoder::readLeaf },
- { MKID_BE('stbl'), &QuickTimeDecoder::readDefault },
- { MKID_BE('stco'), &QuickTimeDecoder::readSTCO },
- { MKID_BE('stsc'), &QuickTimeDecoder::readSTSC },
- { MKID_BE('stsd'), &QuickTimeDecoder::readSTSD },
- { MKID_BE('stss'), &QuickTimeDecoder::readSTSS },
- { MKID_BE('stsz'), &QuickTimeDecoder::readSTSZ },
- { MKID_BE('stts'), &QuickTimeDecoder::readSTTS },
- { MKID_BE('tkhd'), &QuickTimeDecoder::readTKHD },
- { MKID_BE('trak'), &QuickTimeDecoder::readTRAK },
- { MKID_BE('udta'), &QuickTimeDecoder::readLeaf },
- { MKID_BE('vmhd'), &QuickTimeDecoder::readLeaf },
- { MKID_BE('cmov'), &QuickTimeDecoder::readCMOV },
- { MKID_BE('wave'), &QuickTimeDecoder::readWAVE },
+ { &QuickTimeDecoder::readDefault, MKID_BE('dinf') },
+ { &QuickTimeDecoder::readLeaf, MKID_BE('dref') },
+ { &QuickTimeDecoder::readDefault, MKID_BE('edts') },
+ { &QuickTimeDecoder::readELST, MKID_BE('elst') },
+ { &QuickTimeDecoder::readHDLR, MKID_BE('hdlr') },
+ { &QuickTimeDecoder::readMDAT, MKID_BE('mdat') },
+ { &QuickTimeDecoder::readMDHD, MKID_BE('mdhd') },
+ { &QuickTimeDecoder::readDefault, MKID_BE('mdia') },
+ { &QuickTimeDecoder::readDefault, MKID_BE('minf') },
+ { &QuickTimeDecoder::readMOOV, MKID_BE('moov') },
+ { &QuickTimeDecoder::readMVHD, MKID_BE('mvhd') },
+ { &QuickTimeDecoder::readLeaf, MKID_BE('smhd') },
+ { &QuickTimeDecoder::readDefault, MKID_BE('stbl') },
+ { &QuickTimeDecoder::readSTCO, MKID_BE('stco') },
+ { &QuickTimeDecoder::readSTSC, MKID_BE('stsc') },
+ { &QuickTimeDecoder::readSTSD, MKID_BE('stsd') },
+ { &QuickTimeDecoder::readSTSS, MKID_BE('stss') },
+ { &QuickTimeDecoder::readSTSZ, MKID_BE('stsz') },
+ { &QuickTimeDecoder::readSTTS, MKID_BE('stts') },
+ { &QuickTimeDecoder::readTKHD, MKID_BE('tkhd') },
+ { &QuickTimeDecoder::readTRAK, MKID_BE('trak') },
+ { &QuickTimeDecoder::readLeaf, MKID_BE('udta') },
+ { &QuickTimeDecoder::readLeaf, MKID_BE('vmhd') },
+ { &QuickTimeDecoder::readCMOV, MKID_BE('cmov') },
+ { &QuickTimeDecoder::readWAVE, MKID_BE('wave') },
{ 0, 0 }
};
@@ -422,7 +422,7 @@ int QuickTimeDecoder::readDefault(MOVatom atom) {
a.offset = atom.offset;
- while(((total_size + 8) < atom.size) && !_fd->eos() && !err) {
+ while(((total_size + 8) < atom.size) && !_fd->eos() && _fd->pos() < _fd->size() && !err) {
a.size = atom.size;
a.type = 0;
@@ -432,7 +432,7 @@ int QuickTimeDecoder::readDefault(MOVatom atom) {
// Some QuickTime videos with resource forks have mdat chunks
// that are of size 0. Adjust it so it's the correct size.
- if (a.size == 0)
+ if (a.type == MKID_BE('mdat') && a.size == 0)
a.size = _fd->size();
}
@@ -1158,6 +1158,7 @@ void QuickTimeDecoder::close() {
delete _streams[i];
delete _fd;
+ _fd = 0;
if (_scaledSurface) {
_scaledSurface->free();
diff --git a/graphics/video/qt_decoder.h b/graphics/video/qt_decoder.h
index 545866f9e5..db4ff8180b 100644
--- a/graphics/video/qt_decoder.h
+++ b/graphics/video/qt_decoder.h
@@ -112,7 +112,6 @@ public:
bool isVideoLoaded() const { return _fd != 0; }
Surface *decodeNextFrame();
- bool needsUpdate() const;
bool endOfVideo() const;
uint32 getElapsedTime() const;
uint32 getTimeToNextFrame() const;
@@ -138,8 +137,8 @@ protected:
};
struct ParseTable {
- uint32 type;
int (QuickTimeDecoder::*func)(MOVatom atom);
+ uint32 type;
};
struct MOVstts {
diff --git a/graphics/video/smk_decoder.cpp b/graphics/video/smk_decoder.cpp
index f27a70f78c..0b7de774eb 100644
--- a/graphics/video/smk_decoder.cpp
+++ b/graphics/video/smk_decoder.cpp
@@ -361,7 +361,7 @@ SmackerDecoder::~SmackerDecoder() {
}
uint32 SmackerDecoder::getElapsedTime() const {
- if (_audioStream)
+ if (_audioStream && _audioStarted)
return _mixer->getSoundElapsedTime(_audioHandle);
return VideoDecoder::getElapsedTime();
@@ -422,24 +422,36 @@ bool SmackerDecoder::load(Common::SeekableReadStream &stream) {
for (i = 0; i < 7; ++i) {
// AudioRate - Frequency and format information for each sound track, up to 7 audio tracks.
// The 32 constituent bits have the following meaning:
- // * bit 31 - data is compressed
+ // * bit 31 - indicates Huffman + DPCM compression
// * bit 30 - indicates that audio data is present for this track
// * bit 29 - 1 = 16-bit audio; 0 = 8-bit audio
// * bit 28 - 1 = stereo audio; 0 = mono audio
- // * bits 27-26 - if both set to zero - use v2 sound decompression
+ // * bit 27 - indicates Bink RDFT compression
+ // * bit 26 - indicates Bink DCT compression
// * bits 25-24 - unused
// * bits 23-0 - audio sample rate
uint32 audioInfo = _fileStream->readUint32LE();
- _header.audioInfo[i].isCompressed = audioInfo & 0x80000000;
_header.audioInfo[i].hasAudio = audioInfo & 0x40000000;
_header.audioInfo[i].is16Bits = audioInfo & 0x20000000;
_header.audioInfo[i].isStereo = audioInfo & 0x10000000;
- _header.audioInfo[i].hasV2Compression = !(audioInfo & 0x8000000) &&
- !(audioInfo & 0x4000000);
_header.audioInfo[i].sampleRate = audioInfo & 0xFFFFFF;
- if (_header.audioInfo[i].hasAudio && i == 0)
- _audioStream = Audio::makeQueuingAudioStream(_header.audioInfo[0].sampleRate, _header.audioInfo[0].isStereo);
+ if (audioInfo & 0x8000000)
+ _header.audioInfo[i].compression = kCompressionRDFT;
+ else if (audioInfo & 0x4000000)
+ _header.audioInfo[i].compression = kCompressionDCT;
+ else if (audioInfo & 0x80000000)
+ _header.audioInfo[i].compression = kCompressionDPCM;
+ else
+ _header.audioInfo[i].compression = kCompressionNone;
+
+ if (_header.audioInfo[i].hasAudio) {
+ if (_header.audioInfo[i].compression == kCompressionRDFT || _header.audioInfo[i].compression == kCompressionDCT)
+ warning("Unhandled Smacker v2 audio compression");
+
+ if (i == 0)
+ _audioStream = Audio::makeQueuingAudioStream(_header.audioInfo[0].sampleRate, _header.audioInfo[0].isStereo);
+ }
}
_header.dummy = _fileStream->readUint32LE();
@@ -528,7 +540,7 @@ Surface *SmackerDecoder::decodeNextFrame() {
chunkSize = _fileStream->readUint32LE();
chunkSize -= 4; // subtract the first 4 bytes (chunk size)
- if (_header.audioInfo[i].isCompressed) {
+ if (_header.audioInfo[i].compression != kCompressionNone) {
dataSizeUnpacked = _fileStream->readUint32LE();
chunkSize -= 4; // subtract the next 4 bytes (unpacked data size)
} else {
@@ -541,7 +553,11 @@ Surface *SmackerDecoder::decodeNextFrame() {
_fileStream->read(soundBuffer, chunkSize);
- if (_header.audioInfo[i].isCompressed) {
+ if (_header.audioInfo[i].compression == kCompressionRDFT || _header.audioInfo[i].compression == kCompressionDCT) {
+ // TODO: Compressed audio (Bink RDFT/DCT encoded)
+ free(soundBuffer);
+ continue;
+ } else if (_header.audioInfo[i].compression == kCompressionDPCM) {
// Compressed audio (Huffman DPCM encoded)
queueCompressedBuffer(soundBuffer, chunkSize, dataSizeUnpacked, i);
free(soundBuffer);
diff --git a/graphics/video/smk_decoder.h b/graphics/video/smk_decoder.h
index 437f47f2d6..6bf671f318 100644
--- a/graphics/video/smk_decoder.h
+++ b/graphics/video/smk_decoder.h
@@ -80,12 +80,18 @@ private:
uint getBlockRun(int index) { return (index <= 58) ? index + 1 : 128 << (index - 59); }
void queueCompressedBuffer(byte *buffer, uint32 bufferSize, uint32 unpackedSize, int streamNum);
+ enum AudioCompression {
+ kCompressionNone,
+ kCompressionDPCM,
+ kCompressionRDFT,
+ kCompressionDCT
+ };
+
struct AudioInfo {
- bool isCompressed;
+ AudioCompression compression;
bool hasAudio;
bool is16Bits;
bool isStereo;
- bool hasV2Compression;
uint32 sampleRate;
};
diff --git a/gui/about.cpp b/gui/about.cpp
index 74851caf94..8b2f470bf6 100644
--- a/gui/about.cpp
+++ b/gui/about.cpp
@@ -81,30 +81,9 @@ AboutDialog::AboutDialog()
: Dialog(10, 20, 300, 174),
_scrollPos(0), _scrollTime(0), _willClose(false) {
- int i;
+ reflowLayout();
- const int screenW = g_system->getOverlayWidth();
- const int screenH = g_system->getOverlayHeight();
-
- _xOff = g_gui.xmlEval()->getVar("Globals.About.XOffset", 5);
- _yOff = g_gui.xmlEval()->getVar("Globals.About.YOffset", 5);
- int outerBorder = g_gui.xmlEval()->getVar("Globals.About.OuterBorder");
-
- _w = screenW - 2 * outerBorder;
- _h = screenH - 2 * outerBorder;
-
- _lineHeight = g_gui.getFontHeight() + 3;
-
- // Heuristic to compute 'optimal' dialog width
- int maxW = _w - 2*_xOff;
- _w = 0;
- for (i = 0; i < ARRAYSIZE(credits); i++) {
- int tmp = g_gui.getStringWidth(credits[i] + 5);
- if (_w < tmp && tmp <= maxW) {
- _w = tmp;
- }
- }
- _w += 2*_xOff;
+ int i;
for (i = 0; i < 1; i++)
_lines.push_back("");
@@ -151,10 +130,6 @@ AboutDialog::AboutDialog()
for (i = 0; i < ARRAYSIZE(credits); i++)
addLine(credits[i]);
-
- // Center the dialog
- _x = (screenW - _w) / 2;
- _y = (screenH - _h) / 2;
}
void AboutDialog::addLine(const char *str) {
@@ -294,6 +269,7 @@ void AboutDialog::handleKeyUp(Common::KeyState state) {
void AboutDialog::reflowLayout() {
Dialog::reflowLayout();
+ int i;
const int screenW = g_system->getOverlayWidth();
const int screenH = g_system->getOverlayHeight();
@@ -309,7 +285,7 @@ void AboutDialog::reflowLayout() {
// Heuristic to compute 'optimal' dialog width
int maxW = _w - 2*_xOff;
_w = 0;
- for (int i = 0; i < ARRAYSIZE(credits); i++) {
+ for (i = 0; i < ARRAYSIZE(credits); i++) {
int tmp = g_gui.getStringWidth(credits[i] + 5);
if (_w < tmp && tmp <= maxW) {
_w = tmp;
@@ -317,8 +293,7 @@ void AboutDialog::reflowLayout() {
}
_w += 2*_xOff;
- _lineHeight = g_gui.getFontHeight() + 3;
-
+ // Center the dialog
_x = (screenW - _w) / 2;
_y = (screenH - _h) / 2;
}
diff --git a/gui/credits.h b/gui/credits.h
index 4bf3e429b0..0df894a642 100644
--- a/gui/credits.h
+++ b/gui/credits.h
@@ -189,6 +189,9 @@ static const char *credits[] = {
"",
"",
"C1""Backend Teams",
+"C1""Android",
+"C0""Angus Lees",
+"",
"C1""Dreamcast",
"C0""Marcus Comstedt",
"",
diff --git a/gui/editable.cpp b/gui/editable.cpp
index 723384ed44..058f08e233 100644
--- a/gui/editable.cpp
+++ b/gui/editable.cpp
@@ -65,7 +65,7 @@ void EditableWidget::setEditString(const String &str) {
// TODO: We probably should filter the input string here,
// e.g. using tryInsertChar.
_editString = str;
- _caretPos = _editString.size();
+ _caretPos = 0;
}
bool EditableWidget::tryInsertChar(byte c, int pos) {
diff --git a/gui/themes/default.inc b/gui/themes/default.inc
index 75c4fb73de..93897a7d88 100644
--- a/gui/themes/default.inc
+++ b/gui/themes/default.inc
@@ -1,38 +1,397 @@
"<?xml version = '1.0'?>"
-"<layout_info resolution='320xY,256x240,Xx272'> "
+"<render_info> "
+"<palette> "
+"<color name='black' "
+"rgb='0,0,0' "
+"/> "
+"<color name='lightgrey' "
+"rgb='104,104,104' "
+"/> "
+"<color name='darkgrey' "
+"rgb='64,64,64' "
+"/> "
+"<color name='green' "
+"rgb='32,160,32' "
+"/> "
+"<color name='green2' "
+"rgb='0,255,0' "
+"/> "
+"</palette> "
+"<fonts> "
+"<font id='text_default' "
+"file='default' "
+"/> "
+"<font id='text_button' "
+"file='default' "
+"/> "
+"<font id='text_normal' "
+"file='default' "
+"/> "
+"<text_color id='color_normal' "
+"color='green' "
+"/> "
+"<text_color id='color_normal_inverted' "
+"color='black' "
+"/> "
+"<text_color id='color_normal_hover' "
+"color='green2' "
+"/> "
+"<text_color id='color_normal_disabled' "
+"color='lightgrey' "
+"/> "
+"<text_color id='color_alternative' "
+"color='lightgrey' "
+"/> "
+"<text_color id='color_alternative_inverted' "
+"color='255,255,255' "
+"/> "
+"<text_color id='color_alternative_hover' "
+"color='176,176,176' "
+"/> "
+"<text_color id='color_alternative_disabled' "
+"color='darkgrey' "
+"/> "
+"<text_color id='color_button' "
+"color='green' "
+"/> "
+"<text_color id='color_button_hover' "
+"color='green2' "
+"/> "
+"<text_color id='color_button_disabled' "
+"color='lightgrey' "
+"/> "
+"</fonts> "
+"<defaults fill='foreground' fg_color='darkgrey' bg_color='black' shadow='0' bevel_color='lightgrey'/> "
+"<drawdata id='text_selection' cache='false'> "
+"<drawstep func='square' "
+"fill='foreground' "
+"fg_color='lightgrey' "
+"/> "
+"</drawdata> "
+"<drawdata id='text_selection_focus' cache='false'> "
+"<drawstep func='square' "
+"fill='foreground' "
+"fg_color='green' "
+"/> "
+"</drawdata> "
+"<drawdata id='mainmenu_bg' cache='false'> "
+"<drawstep func='fill' "
+"fill='foreground' "
+"fg_color='black' "
+"/> "
+"</drawdata> "
+"<drawdata id='special_bg' cache='false'> "
+"<drawstep func='bevelsq' "
+"bevel='2' "
+"/> "
+"</drawdata> "
+"<drawdata id='separator' cache='false'> "
+"<drawstep func='square' "
+"fill='foreground' "
+"height='2' "
+"ypos='center' "
+"fg_color='lightgrey' "
+"/> "
+"</drawdata> "
+"<drawdata id='scrollbar_base' cache='false'> "
+"<drawstep func='bevelsq' "
+"bevel='2' "
+"/> "
+"</drawdata> "
+"<drawdata id='scrollbar_handle_hover' cache='false'> "
+"<drawstep func='square' "
+"fill='foreground' "
+"fg_color='green2' "
+"/> "
+"</drawdata> "
+"<drawdata id='scrollbar_handle_idle' cache='false'> "
+"<drawstep func='square' "
+"fill='foreground' "
+"fg_color='green' "
+"/> "
+"</drawdata> "
+"<drawdata id='scrollbar_button_idle' cache='false'> "
+"<drawstep func='bevelsq' "
+"bevel='2' "
+"fill='none' "
+"/> "
+"<drawstep func='triangle' "
+"fg_color='green' "
+"fill='foreground' "
+"width='auto' "
+"height='auto' "
+"xpos='center' "
+"ypos='center' "
+"orientation='top' "
+"/> "
+"</drawdata> "
+"<drawdata id='scrollbar_button_hover' cache='false'> "
+"<drawstep func='bevelsq' "
+"bevel='2' "
+"fill='none' "
+"/> "
+"<drawstep func='triangle' "
+"fg_color='green2' "
+"fill='foreground' "
+"width='auto' "
+"height='auto' "
+"xpos='center' "
+"ypos='center' "
+"orientation='top' "
+"/> "
+"</drawdata> "
+"<drawdata id='tab_active' cache='false'> "
+"<text font='text_default' "
+"text_color='color_normal_hover' "
+"vertical_align='center' "
+"horizontal_align='center' "
+"/> "
+"<drawstep func='tab' "
+"bevel='2' "
+"radius='0' "
+"fill='none' "
+"/> "
+"</drawdata> "
+"<drawdata id='tab_inactive' cache='false'> "
+"<text font='text_default' "
+"text_color='color_normal' "
+"vertical_align='center' "
+"horizontal_align='center' "
+"/> "
+"<drawstep func='tab' "
+"bevel='2' "
+"radius='0' "
+"fill='none' "
+"/> "
+"</drawdata> "
+"<drawdata id='tab_background' cache='false'> "
+"</drawdata> "
+"<drawdata id='widget_slider' cache='false'> "
+"<drawstep func='bevelsq' "
+"bevel='2' "
+"fill='none' "
+"/> "
+"</drawdata> "
+"<drawdata id='slider_disabled' cache='false'> "
+"<drawstep func='square' "
+"fill='foreground' "
+"fg_color='lightgrey' "
+"/> "
+"</drawdata> "
+"<drawdata id='slider_full' cache='false'> "
+"<drawstep func='square' "
+"fill='foreground' "
+"fg_color='green' "
+"/> "
+"</drawdata> "
+"<drawdata id='slider_hover' cache='false'> "
+"<drawstep func='square' "
+"fill='foreground' "
+"fg_color='green2' "
+"/> "
+"</drawdata> "
+"<drawdata id='widget_small' cache='false'> "
+"<drawstep func='bevelsq' "
+"bevel='2' "
+"fill='none' "
+"/> "
+"</drawdata> "
+"<drawdata id='popup_idle' cache='false'> "
+"<drawstep func='bevelsq' "
+"bevel='2' "
+"fill='none' "
+"/> "
+"<drawstep func='triangle' "
+"fg_color='green' "
+"fill='foreground' "
+"width='height' "
+"height='auto' "
+"xpos='right' "
+"ypos='center' "
+"orientation='bottom' "
+"/> "
+"<text font='text_default' "
+"text_color='color_normal' "
+"vertical_align='center' "
+"horizontal_align='left' "
+"/> "
+"</drawdata> "
+"<drawdata id='popup_disabled' cache='false'> "
+"<drawstep func='bevelsq' "
+"bevel='2' "
+"fill='none' "
+"/> "
+"<drawstep func='triangle' "
+"fg_color='lightgrey' "
+"fill='foreground' "
+"width='height' "
+"height='auto' "
+"xpos='right' "
+"ypos='center' "
+"orientation='bottom' "
+"/> "
+"<text font='text_default' "
+"text_color='color_normal_disabled' "
+"vertical_align='center' "
+"horizontal_align='left' "
+"/> "
+"</drawdata> "
+"<drawdata id='popup_hover' cache='false'> "
+"<drawstep func='bevelsq' "
+"bevel='2' "
+"fill='none' "
+"/> "
+"<drawstep func='triangle' "
+"fg_color='green2' "
+"fill='foreground' "
+"width='height' "
+"height='auto' "
+"xpos='right' "
+"ypos='center' "
+"orientation='bottom' "
+"/> "
+"<text font='text_default' "
+"text_color='color_normal_hover' "
+"vertical_align='center' "
+"horizontal_align='left' "
+"/> "
+"</drawdata> "
+"<drawdata id='widget_textedit' cache='false'> "
+"<drawstep func='bevelsq' "
+"bevel='2' "
+"fill='none' "
+"/> "
+"</drawdata> "
+"<drawdata id='plain_bg' cache='false'> "
+"<drawstep func='bevelsq' "
+"bevel='2' "
+"/> "
+"</drawdata> "
+"<drawdata id='caret' cache='false'> "
+"<drawstep func='square' "
+"fill='foreground' "
+"fg_color='lightgrey' "
+"/> "
+"</drawdata> "
+"<drawdata id='default_bg' cache='false'> "
+"<drawstep func='bevelsq' "
+"bevel='2' "
+"/> "
+"</drawdata> "
+"<drawdata id='button_idle' cache='false'> "
+"<text font='text_button' "
+"text_color='color_button' "
+"vertical_align='center' "
+"horizontal_align='center' "
+"/> "
+"<drawstep func='bevelsq' "
+"bevel='2' "
+"fill='none' "
+"/> "
+"</drawdata> "
+"<drawdata id='button_hover' cache='false'> "
+"<text font='text_button' "
+"text_color='color_button_hover' "
+"vertical_align='center' "
+"horizontal_align='center' "
+"/> "
+"<drawstep func='bevelsq' "
+"bevel='2' "
+"fill='none' "
+"/> "
+"</drawdata> "
+"<drawdata id='button_disabled' cache='false'> "
+"<text font='text_button' "
+"text_color='color_button_disabled' "
+"vertical_align='center' "
+"horizontal_align='center' "
+"/> "
+"<drawstep func='bevelsq' "
+"bevel='2' "
+"fill='none' "
+"/> "
+"</drawdata> "
+"<drawdata id='checkbox_disabled' cache='false'> "
+"<text font='text_default' "
+"text_color='color_normal_disabled' "
+"vertical_align='top' "
+"horizontal_align='left' "
+"/> "
+"<drawstep func='bevelsq' "
+"bevel='2' "
+"fill='none' "
+"/> "
+"</drawdata> "
+"<drawdata id='checkbox_selected' cache='false'> "
+"<text font='text_default' "
+"text_color='color_normal' "
+"vertical_align='top' "
+"horizontal_align='left' "
+"/> "
+"<drawstep func='bevelsq' "
+"bevel='2' "
+"fill='none' "
+"/> "
+"<drawstep func='cross' "
+"fill='foreground' "
+"stroke='2' "
+"fg_color='green' "
+"/> "
+"</drawdata> "
+"<drawdata id='checkbox_default' cache='false'> "
+"<text font='text_default' "
+"text_color='color_normal' "
+"vertical_align='top' "
+"horizontal_align='left' "
+"/> "
+"<drawstep func='bevelsq' "
+"bevel='2' "
+"fill='none' "
+"/> "
+"</drawdata> "
+"<drawdata id='widget_default' cache='false'> "
+"<drawstep func='bevelsq' "
+"bevel='2' "
+"/> "
+"</drawdata> "
+"</render_info> "
+"<layout_info resolution='-320xY,-256x240,-Xx272'> "
"<globals> "
-"<def var='Line.Height' value='12' /> "
-"<def var='Font.Height' value='10' /> "
-"<def var='About.OuterBorder' value='10'/> "
-"<def var='Layout.Spacing' value='8'/> "
+"<def var='Line.Height' value='16' /> "
+"<def var='Font.Height' value='16' /> "
+"<def var='About.OuterBorder' value='80'/> "
+"<def var='Layout.Spacing' value='8' /> "
"<def var='ShowLauncherLogo' value='0'/> "
"<def var='ShowGlobalMenuLogo' value='0'/> "
"<def var='ShowSearchPic' value='0'/> "
-"<def var='SaveLoadChooser.ExtInfo.Visible' value='0'/> "
-"<def var='KeyMapper.Spacing' value='5'/> "
-"<def var='KeyMapper.LabelWidth' value='80'/> "
-"<def var='KeyMapper.ButtonWidth' value='60'/> "
-"<widget name='Button' "
-"size='72,16' "
-"/> "
-"<widget name='Slider' "
-"size='85,12' "
-"/> "
+"<def var='SaveLoadChooser.ExtInfo.Visible' value='1'/> "
+"<def var='KeyMapper.Spacing' value='10'/> "
+"<def var='KeyMapper.LabelWidth' value='100'/> "
+"<def var='KeyMapper.ButtonWidth' value='80'/> "
"<widget name='OptionsLabel' "
"size='110,Globals.Line.Height' "
"textalign='right' "
"/> "
"<widget name='SmallLabel' "
-"size='18,Globals.Line.Height' "
+"size='24,Globals.Line.Height' "
+"/> "
+"<widget name='ShortOptionsLabel' "
+"size='60,Globals.Line.Height' "
+"/> "
+"<widget name='Button' "
+"size='108,24' "
+"/> "
+"<widget name='Slider' "
+"size='128,18' "
"/> "
"<widget name='PopUp' "
-"size='-1,15' "
+"size='-1,19' "
"/> "
"<widget name='Checkbox' "
-"size='-1,Globals.Line.Height' "
+"size='-1,14' "
"/> "
"<widget name='ListWidget' "
-"padding='5,0,0,0' "
+"padding='5,0,8,0' "
"/> "
"<widget name='PopUpWidget' "
"padding='7,5,0,0' "
@@ -44,25 +403,25 @@
"padding='7,5,5,5' "
"/> "
"<widget name='Scrollbar' "
-"size='9,0' "
+"size='15,0' "
"/> "
"<widget name='TabWidget.Tab' "
-"size='45,16' "
-"padding='0,0,2,0' "
+"size='75,27' "
+"padding='0,0,8,0' "
"/> "
"<widget name='TabWidget.NavButton' "
-"size='32,18' "
+"size='15,18' "
"padding='0,3,4,0' "
"/> "
"</globals> "
"<dialog name='Launcher' overlays='screen'> "
-"<layout type='vertical' center='true' padding='8,8,4,4'> "
+"<layout type='vertical' center='true' padding='16,16,8,8'> "
"<widget name='Version' "
"height='Globals.Line.Height' "
"/> "
"<layout type='horizontal' spacing='5' padding='10,0,0,0'> "
"<widget name='SearchDesc' "
-"width='50' "
+"width='60' "
"height='Globals.Line.Height' "
"textalign='right' "
"/> "
@@ -79,36 +438,37 @@
"<widget name='GameList'/> "
"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
"<widget name='LoadGameButton' "
-"height='12' "
+"height='20' "
"/> "
"<widget name='AddGameButton' "
-"height='12' "
+"height='20' "
"/> "
"<widget name='EditGameButton' "
-"height='12' "
+"height='20' "
"/> "
"<widget name='RemoveGameButton' "
-"height='12' "
+"height='20' "
"/> "
"</layout> "
+"<space size='4'/> "
"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
"<widget name='QuitButton' "
-"height='12' "
+"height='20' "
"/> "
"<widget name='AboutButton' "
-"height='12' "
+"height='20' "
"/> "
"<widget name='OptionsButton' "
-"height='12' "
+"height='20' "
"/> "
"<widget name='StartButton' "
-"height='12' "
+"height='20' "
"/> "
"</layout> "
"</layout> "
"</dialog> "
-"<dialog name='Browser' overlays='screen' inset='8' shading='dim'> "
-"<layout type='vertical' padding='8,8,0,4'> "
+"<dialog name='Browser' overlays='Dialog.Launcher.GameList' shading='dim'> "
+"<layout type='vertical' padding='8,8,8,8'> "
"<widget name='Headline' "
"height='Globals.Line.Height' "
"/> "
@@ -116,7 +476,7 @@
"height='Globals.Line.Height' "
"/> "
"<widget name='List'/> "
-"<layout type='horizontal' padding='0,0,8,0'> "
+"<layout type='horizontal' padding='0,0,16,0'> "
"<widget name='Up' "
"type='Button' "
"/> "
@@ -130,10 +490,10 @@
"</layout> "
"</layout> "
"</dialog> "
-"<dialog name='GlobalOptions' overlays='screen' inset='16' shading='dim'> "
+"<dialog name='GlobalOptions' overlays='Dialog.Launcher.GameList' shading='dim'> "
"<layout type='vertical' padding='0,0,0,0'> "
"<widget name='TabWidget'/> "
-"<layout type='horizontal' padding='8,8,8,8'> "
+"<layout type='horizontal' padding='16,16,16,16'> "
"<space/> "
"<widget name='Cancel' "
"type='Button' "
@@ -146,7 +506,7 @@
"</dialog> "
"<dialog name='GlobalOptions_Graphics' overlays='Dialog.GlobalOptions.TabWidget'> "
"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
"<widget name='grModePopupDesc' "
"type='OptionsLabel' "
"/> "
@@ -154,7 +514,7 @@
"type='PopUp' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
"<widget name='grRenderPopupDesc' "
"type='OptionsLabel' "
"/> "
@@ -172,7 +532,7 @@
"</dialog> "
"<dialog name='GlobalOptions_Audio' overlays='Dialog.GlobalOptions.TabWidget'> "
"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
"<widget name='auMidiPopupDesc' "
"type='OptionsLabel' "
"/> "
@@ -180,7 +540,7 @@
"type='PopUp' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
"<widget name='auOPLPopupDesc' "
"type='OptionsLabel' "
"/> "
@@ -188,7 +548,7 @@
"type='PopUp' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
"<widget name='auSampleRatePopupDesc' "
"type='OptionsLabel' "
"/> "
@@ -196,16 +556,16 @@
"type='PopUp' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
"<widget name='subToggleDesc' "
"type='OptionsLabel' "
"/> "
"<widget name='subToggleButton' "
-"width='128' "
+"width='150' "
"height='Globals.Slider.Height' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
"<widget name='subSubtitleSpeedDesc' "
"type='OptionsLabel' "
"/> "
@@ -219,8 +579,9 @@
"</layout> "
"</dialog> "
"<dialog name='GlobalOptions_Volume' overlays='Dialog.GlobalOptions.TabWidget'> "
-"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='16,16,16,16' spacing='8'> "
+"<layout type='vertical' padding='0,0,0,0' spacing='8'> "
+"<layout type='horizontal' padding='0,0,0,0'> "
"<widget name='vcMusicText' "
"type='OptionsLabel' "
"/> "
@@ -231,7 +592,7 @@
"type='SmallLabel' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0'> "
"<widget name='vcSfxText' "
"type='OptionsLabel' "
"/> "
@@ -242,7 +603,7 @@
"type='SmallLabel' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0'> "
"<widget name='vcSpeechText' "
"type='OptionsLabel' "
"/> "
@@ -253,8 +614,8 @@
"type='SmallLabel' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
-"<space size='110' /> "
+"</layout> "
+"<layout type='vertical' padding='24,0,24,0' center='true'> "
"<widget name='vcMuteCheckbox' "
"type='Checkbox' "
"/> "
@@ -263,7 +624,7 @@
"</dialog> "
"<dialog name='GlobalOptions_MIDI' overlays='Dialog.GlobalOptions.TabWidget'> "
"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
"<widget name='mcFontButton' "
"type='Button' "
"/> "
@@ -284,7 +645,7 @@
"<widget name='mcGSCheckbox' "
"type='Checkbox' "
"/> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0'> "
"<widget name='mcMidiGainText' "
"type='OptionsLabel' "
"/> "
@@ -300,7 +661,7 @@
"</dialog> "
"<dialog name='GlobalOptions_Paths' overlays='Dialog.GlobalOptions.TabWidget'> "
"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='16'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
"<widget name='SaveButton' "
"type='Button' "
"/> "
@@ -308,7 +669,7 @@
"height='Globals.Line.Height' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='16'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
"<widget name='ThemeButton' "
"type='Button' "
"/> "
@@ -316,7 +677,7 @@
"height='Globals.Line.Height' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='16'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
"<widget name='ExtraButton' "
"type='Button' "
"/> "
@@ -336,7 +697,7 @@
"</dialog> "
"<dialog name='GlobalOptions_Misc' overlays='Dialog.GlobalOptions.TabWidget'> "
"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='16'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
"<widget name='ThemeButton' "
"type='Button' "
"/> "
@@ -344,21 +705,17 @@
"height='Globals.Line.Height' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
"<widget name='RendererPopupDesc' "
-"width='80' "
-"height='Globals.Line.Height' "
-"textalign='right' "
+"type='OptionsLabel' "
"/> "
"<widget name='RendererPopup' "
"type='PopUp' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
"<widget name='AutosavePeriodPopupDesc' "
-"width='80' "
-"height='Globals.Line.Height' "
-"textalign='right' "
+"type='OptionsLabel' "
"/> "
"<widget name='AutosavePeriodPopup' "
"type='PopUp' "
@@ -393,10 +750,10 @@
"</layout> "
"</layout> "
"</dialog> "
-"<dialog name='GameOptions' overlays='screen' inset='16' shading='dim'> "
+"<dialog name='GameOptions' overlays='Dialog.Launcher.GameList' shading='dim'> "
"<layout type='vertical' padding='0,0,0,0' spacing='16'> "
"<widget name='TabWidget'/> "
-"<layout type='horizontal' padding='8,8,8,8'> "
+"<layout type='horizontal' padding='16,16,16,4'> "
"<space/> "
"<widget name='Cancel' "
"type='Button' "
@@ -408,7 +765,7 @@
"</layout> "
"</dialog> "
"<dialog name='GameOptions_Graphics' overlays='Dialog.GlobalOptions.TabWidget'> "
-"<layout type='vertical' padding='8,8,8,8' spacing='6'> "
+"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
"<widget name='EnableTabCheckbox' "
"type='Checkbox' "
"/> "
@@ -416,7 +773,7 @@
"</layout> "
"</dialog> "
"<dialog name='GameOptions_Audio' overlays='Dialog.GlobalOptions.TabWidget'> "
-"<layout type='vertical' padding='8,8,8,8' spacing='6'> "
+"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
"<widget name='EnableTabCheckbox' "
"type='Checkbox' "
"/> "
@@ -424,7 +781,7 @@
"</layout> "
"</dialog> "
"<dialog name='GameOptions_MIDI' overlays='Dialog.GlobalOptions.TabWidget'> "
-"<layout type='vertical' padding='8,8,8,8' spacing='6'> "
+"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
"<widget name='EnableTabCheckbox' "
"type='Checkbox' "
"/> "
@@ -432,7 +789,7 @@
"</layout> "
"</dialog> "
"<dialog name='GameOptions_Volume' overlays='Dialog.GlobalOptions.TabWidget'> "
-"<layout type='vertical' padding='8,8,8,8' spacing='6'> "
+"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
"<widget name='EnableTabCheckbox' "
"type='Checkbox' "
"/> "
@@ -440,43 +797,34 @@
"</layout> "
"</dialog> "
"<dialog name='GameOptions_Game' overlays='Dialog.GameOptions.TabWidget' shading='dim'> "
-"<layout type='vertical' padding='8,8,8,8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='vertical' padding='16,16,16,16'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
"<widget name='Id' "
-"width='35' "
-"height='Globals.Line.Height' "
-"textalign='right' "
+"type='OptionsLabel' "
"/> "
"<widget name='Domain' "
"type='PopUp' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
"<widget name='Name' "
-"width='35' "
-"height='Globals.Line.Height' "
-"textalign='right' "
+"type='OptionsLabel' "
"/> "
"<widget name='Desc' "
"type='PopUp' "
"/> "
"</layout> "
-"<space size='8'/> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
"<widget name='LangPopupDesc' "
-"width='60' "
-"height='Globals.Line.Height' "
-"textalign='right' "
+"type='OptionsLabel' "
"/> "
"<widget name='LangPopup' "
"type='PopUp' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
"<widget name='PlatformPopupDesc' "
-"width='60' "
-"height='Globals.Line.Height' "
-"textalign='right' "
+"type='OptionsLabel' "
"/> "
"<widget name='PlatformPopup' "
"type='PopUp' "
@@ -485,8 +833,8 @@
"</layout> "
"</dialog> "
"<dialog name='GameOptions_Paths' overlays='Dialog.GameOptions.TabWidget' shading='dim'> "
-"<layout type='vertical' padding='8,8,8,8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> "
+"<layout type='vertical' padding='16,16,16,16'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
"<widget name='Savepath' "
"type='Button' "
"/> "
@@ -494,7 +842,7 @@
"height='Globals.Line.Height' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
"<widget name='Extrapath' "
"type='Button' "
"/> "
@@ -502,7 +850,7 @@
"height='Globals.Line.Height' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
"<widget name='Gamepath' "
"type='Button' "
"/> "
@@ -513,86 +861,57 @@
"</layout> "
"</dialog> "
"<dialog name='GlobalMenu' overlays='screen_center'> "
-"<layout type='vertical' padding='8,8,4,6' center='true'> "
+"<layout type='vertical' padding='16,16,16,16' center='true'> "
"<widget name='Title' "
-"width='160' "
-"height='4' "
+"width='210' "
+"height='Globals.Line.Height' "
"/> "
"<widget name='Version' "
-"width='160' "
-"height='4' "
-"/> "
-"<space size='1'/> "
-"<widget name='Load' "
-"width='120' "
-"height='12' "
-"/> "
-"<widget name='Save' "
-"width='120' "
-"height='12' "
-"/> "
-"<space size='1'/> "
-"<widget name='Options' "
-"width='120' "
-"height='12' "
-"/> "
-"<widget name='About' "
-"width='120' "
-"height='12' "
-"/> "
-"<space size='1'/> "
-"<widget name='Resume' "
-"width='120' "
-"height='12' "
-"/> "
-"<widget name='RTL' "
-"width='120' "
-"height='12' "
-"/> "
-"<widget name='Quit' "
-"width='120' "
-"height='12' "
+"width='210' "
+"height='Globals.Line.Height' "
"/> "
-"</layout> "
-"</dialog> "
-"<dialog name='ScummMain' overlays='screen_center'> "
-"<layout type='vertical' padding='8,8,8,8'> "
"<widget name='Resume' "
-"width='Globals.Button.Width' "
-"height='14' "
+"width='150' "
+"height='Globals.Button.Height' "
"/> "
-"<space size='2'/> "
+"<space size='10'/> "
"<widget name='Load' "
-"width='Globals.Button.Width' "
-"height='14' "
+"width='150' "
+"height='Globals.Button.Height' "
"/> "
"<widget name='Save' "
-"width='Globals.Button.Width' "
-"height='14' "
+"width='150' "
+"height='Globals.Button.Height' "
"/> "
-"<space size='2'/> "
+"<space size='10'/> "
"<widget name='Options' "
-"width='Globals.Button.Width' "
-"height='14' "
+"width='150' "
+"height='Globals.Button.Height' "
"/> "
"<widget name='Help' "
-"width='Globals.Button.Width' "
-"height='14' "
+"width='150' "
+"height='Globals.Button.Height' "
"/> "
"<widget name='About' "
-"width='Globals.Button.Width' "
-"height='14' "
+"width='150' "
+"height='Globals.Button.Height' "
+"/> "
+"<space size='10'/> "
+"<widget name='RTL' "
+"width='150' "
+"height='Globals.Button.Height' "
"/> "
-"<space size='2'/> "
"<widget name='Quit' "
-"width='Globals.Button.Width' "
-"height='14' "
+"width='150' "
+"height='Globals.Button.Height' "
"/> "
"</layout> "
"</dialog> "
-"<dialog name='ScummConfig' overlays='screen_center'> "
+"<dialog name='GlobalConfig' overlays='screen_center'> "
"<layout type='vertical' padding='8,8,8,8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0'> "
+"<layout type='vertical' padding='0,0,0,0' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='8'> "
"<widget name='vcMusicText' "
"type='OptionsLabel' "
"/> "
@@ -603,7 +922,7 @@
"type='SmallLabel' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='8'> "
"<widget name='vcSfxText' "
"type='OptionsLabel' "
"/> "
@@ -614,7 +933,7 @@
"type='SmallLabel' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='8'> "
"<widget name='vcSpeechText' "
"type='OptionsLabel' "
"/> "
@@ -625,24 +944,25 @@
"type='SmallLabel' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
-"<space size='110' /> "
+"</layout> "
+"<layout type='vertical' padding='24,24,24,24' center='true'> "
"<widget name='vcMuteCheckbox' "
"type='Checkbox' "
-"width='80' "
+"width='80' "
"/> "
"</layout> "
-"<space size='4' /> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"</layout> "
+"<space size='8' /> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
"<widget name='subToggleDesc' "
"type='OptionsLabel' "
"/> "
"<widget name='subToggleButton' "
-"width='128' "
+"width='158' "
"height='Globals.Slider.Height' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
"<widget name='subSubtitleSpeedDesc' "
"type='OptionsLabel' "
"/> "
@@ -653,8 +973,8 @@
"type='SmallLabel' "
"/> "
"</layout> "
-"<space size='20'/> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='4'> "
+"<space size='60'/> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
"<widget name='Keys' "
"type='Button' "
"/> "
@@ -669,15 +989,23 @@
"</layout> "
"</dialog> "
"<dialog name='SaveLoadChooser' overlays='screen' inset='8' shading='dim'> "
-"<layout type='vertical' padding='8,8,8,8' center='true'> "
-"<widget name='Title' height='Globals.Line.Height'/> "
+"<layout type='vertical' padding='8,8,8,32' center='true'> "
+"<widget name='Title' "
+"height='Globals.Line.Height' "
+"/> "
+"<layout type='horizontal' padding='0,0,0,16' spacing='16'> "
"<widget name='List' /> "
-"<layout type='horizontal' padding='0,0,16,0'> "
+"<widget name='Thumbnail' "
+"width='180' "
+"height='200' "
+"/> "
+"</layout> "
+"<layout type='horizontal' padding='0,0,0,0'> "
"<space/> "
"<widget name='Delete' "
"type='Button' "
"/> "
-"<space size='16'/> "
+"<space size='32'/> "
"<widget name='Cancel' "
"type='Button' "
"/> "
@@ -687,16 +1015,16 @@
"</layout> "
"</layout> "
"</dialog> "
-"<dialog name='ScummHelp' overlays='screen'> "
-"<layout type='vertical' padding='8,8,8,8'> "
+"<dialog name='ScummHelp' overlays='screen_center'> "
+"<layout type='vertical' padding='8,8,8,8' center='true'> "
"<widget name='Title' "
-"width='180' "
+"width='320' "
"height='Globals.Line.Height' "
"/> "
"<widget name='HelpText' "
-"height='140' "
+"height='200' "
"/> "
-"<layout type='horizontal' padding='0,0,0,0'> "
+"<layout type='horizontal' padding='0,0,16,0'> "
"<widget name='Prev' "
"type='Button' "
"/> "
@@ -711,20 +1039,20 @@
"</layout> "
"</dialog> "
"<dialog name='MassAdd' overlays='screen_center' shading='dim'> "
-"<layout type='vertical' padding='4,4,16,4' center='true'> "
+"<layout type='vertical' padding='8,8,32,8' center='true'> "
"<widget name='DirProgressText' "
-"width='280' "
+"width='480' "
"height='Globals.Line.Height' "
"/> "
"<widget name='GameProgressText' "
-"width='280' "
+"width='480' "
"height='Globals.Line.Height' "
"/> "
"<widget name='GameList' "
-"width='280' "
-"height='100' "
+"width='480' "
+"height='250' "
"/> "
-"<layout type='horizontal' padding='4,4,4,4'> "
+"<layout type='horizontal' padding='8,8,8,8'> "
"<widget name='Ok' "
"type='Button' "
"/> "
@@ -735,20 +1063,20 @@
"</layout> "
"</dialog> "
"<dialog name='KeyMapper' overlays='screen_center' shading='dim'> "
-"<layout type='vertical' padding='8,8,8,8' spacing='10' center='true'> "
+"<layout type='vertical' padding='8,8,32,8' spacing='10' center='true'> "
"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
"<widget name='PopupDesc' "
"type='OptionsLabel' "
"/> "
"<widget name='Popup' "
"type='PopUp' "
-"width='150' "
+"width='400' "
"height='Globals.Line.Height' "
"/> "
"</layout> "
"<widget name='KeymapArea' "
-"width='300' "
-"height='120' "
+"width='600' "
+"height='280' "
"/> "
"<widget name='Close' "
"type='Button' "
@@ -756,43 +1084,40 @@
"</layout> "
"</dialog> "
"</layout_info> "
-"<layout_info resolution='-320xY,-256x240,-Xx272'> "
+"<layout_info resolution='320xY,256x240,Xx272'> "
"<globals> "
-"<def var='Line.Height' value='16' /> "
-"<def var='Font.Height' value='16' /> "
-"<def var='About.OuterBorder' value='80'/> "
-"<def var='Layout.Spacing' value='8' /> "
+"<def var='Line.Height' value='12' /> "
+"<def var='Font.Height' value='10' /> "
+"<def var='About.OuterBorder' value='10'/> "
+"<def var='Layout.Spacing' value='8'/> "
"<def var='ShowLauncherLogo' value='0'/> "
"<def var='ShowGlobalMenuLogo' value='0'/> "
"<def var='ShowSearchPic' value='0'/> "
-"<def var='SaveLoadChooser.ExtInfo.Visible' value='1'/> "
-"<def var='KeyMapper.Spacing' value='10'/> "
-"<def var='KeyMapper.LabelWidth' value='100'/> "
-"<def var='KeyMapper.ButtonWidth' value='80'/> "
+"<def var='SaveLoadChooser.ExtInfo.Visible' value='0'/> "
+"<def var='KeyMapper.Spacing' value='5'/> "
+"<def var='KeyMapper.LabelWidth' value='80'/> "
+"<def var='KeyMapper.ButtonWidth' value='60'/> "
+"<widget name='Button' "
+"size='72,16' "
+"/> "
+"<widget name='Slider' "
+"size='85,12' "
+"/> "
"<widget name='OptionsLabel' "
"size='110,Globals.Line.Height' "
"textalign='right' "
"/> "
"<widget name='SmallLabel' "
-"size='24,Globals.Line.Height' "
-"/> "
-"<widget name='ShortOptionsLabel' "
-"size='60,Globals.Line.Height' "
-"/> "
-"<widget name='Button' "
-"size='108,24' "
-"/> "
-"<widget name='Slider' "
-"size='128,18' "
+"size='18,Globals.Line.Height' "
"/> "
"<widget name='PopUp' "
-"size='-1,19' "
+"size='-1,15' "
"/> "
"<widget name='Checkbox' "
-"size='-1,14' "
+"size='-1,Globals.Line.Height' "
"/> "
"<widget name='ListWidget' "
-"padding='5,0,8,0' "
+"padding='5,0,0,0' "
"/> "
"<widget name='PopUpWidget' "
"padding='7,5,0,0' "
@@ -804,25 +1129,25 @@
"padding='7,5,5,5' "
"/> "
"<widget name='Scrollbar' "
-"size='15,0' "
+"size='9,0' "
"/> "
"<widget name='TabWidget.Tab' "
-"size='75,27' "
-"padding='0,0,8,0' "
+"size='45,16' "
+"padding='0,0,2,0' "
"/> "
"<widget name='TabWidget.NavButton' "
-"size='15,18' "
+"size='32,18' "
"padding='0,3,4,0' "
"/> "
"</globals> "
"<dialog name='Launcher' overlays='screen'> "
-"<layout type='vertical' center='true' padding='16,16,8,8'> "
+"<layout type='vertical' center='true' padding='8,8,4,4'> "
"<widget name='Version' "
"height='Globals.Line.Height' "
"/> "
"<layout type='horizontal' spacing='5' padding='10,0,0,0'> "
"<widget name='SearchDesc' "
-"width='60' "
+"width='50' "
"height='Globals.Line.Height' "
"textalign='right' "
"/> "
@@ -839,37 +1164,36 @@
"<widget name='GameList'/> "
"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
"<widget name='LoadGameButton' "
-"height='20' "
+"height='12' "
"/> "
"<widget name='AddGameButton' "
-"height='20' "
+"height='12' "
"/> "
"<widget name='EditGameButton' "
-"height='20' "
+"height='12' "
"/> "
"<widget name='RemoveGameButton' "
-"height='20' "
+"height='12' "
"/> "
"</layout> "
-"<space size='4'/> "
"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
"<widget name='QuitButton' "
-"height='20' "
+"height='12' "
"/> "
"<widget name='AboutButton' "
-"height='20' "
+"height='12' "
"/> "
"<widget name='OptionsButton' "
-"height='20' "
+"height='12' "
"/> "
"<widget name='StartButton' "
-"height='20' "
+"height='12' "
"/> "
"</layout> "
"</layout> "
"</dialog> "
-"<dialog name='Browser' overlays='Dialog.Launcher.GameList' shading='dim'> "
-"<layout type='vertical' padding='8,8,8,8'> "
+"<dialog name='Browser' overlays='screen' inset='8' shading='dim'> "
+"<layout type='vertical' padding='8,8,0,4'> "
"<widget name='Headline' "
"height='Globals.Line.Height' "
"/> "
@@ -877,7 +1201,7 @@
"height='Globals.Line.Height' "
"/> "
"<widget name='List'/> "
-"<layout type='horizontal' padding='0,0,16,0'> "
+"<layout type='horizontal' padding='0,0,8,0'> "
"<widget name='Up' "
"type='Button' "
"/> "
@@ -891,10 +1215,10 @@
"</layout> "
"</layout> "
"</dialog> "
-"<dialog name='GlobalOptions' overlays='Dialog.Launcher.GameList' shading='dim'> "
+"<dialog name='GlobalOptions' overlays='screen' inset='16' shading='dim'> "
"<layout type='vertical' padding='0,0,0,0'> "
"<widget name='TabWidget'/> "
-"<layout type='horizontal' padding='16,16,16,16'> "
+"<layout type='horizontal' padding='8,8,8,8'> "
"<space/> "
"<widget name='Cancel' "
"type='Button' "
@@ -907,7 +1231,7 @@
"</dialog> "
"<dialog name='GlobalOptions_Graphics' overlays='Dialog.GlobalOptions.TabWidget'> "
"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
"<widget name='grModePopupDesc' "
"type='OptionsLabel' "
"/> "
@@ -915,7 +1239,7 @@
"type='PopUp' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
"<widget name='grRenderPopupDesc' "
"type='OptionsLabel' "
"/> "
@@ -933,7 +1257,7 @@
"</dialog> "
"<dialog name='GlobalOptions_Audio' overlays='Dialog.GlobalOptions.TabWidget'> "
"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
"<widget name='auMidiPopupDesc' "
"type='OptionsLabel' "
"/> "
@@ -941,7 +1265,7 @@
"type='PopUp' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
"<widget name='auOPLPopupDesc' "
"type='OptionsLabel' "
"/> "
@@ -949,7 +1273,7 @@
"type='PopUp' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
"<widget name='auSampleRatePopupDesc' "
"type='OptionsLabel' "
"/> "
@@ -957,16 +1281,16 @@
"type='PopUp' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
"<widget name='subToggleDesc' "
"type='OptionsLabel' "
"/> "
"<widget name='subToggleButton' "
-"width='150' "
+"width='128' "
"height='Globals.Slider.Height' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
"<widget name='subSubtitleSpeedDesc' "
"type='OptionsLabel' "
"/> "
@@ -980,9 +1304,8 @@
"</layout> "
"</dialog> "
"<dialog name='GlobalOptions_Volume' overlays='Dialog.GlobalOptions.TabWidget'> "
-"<layout type='horizontal' padding='16,16,16,16' spacing='8'> "
-"<layout type='vertical' padding='0,0,0,0' spacing='8'> "
-"<layout type='horizontal' padding='0,0,0,0'> "
+"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
"<widget name='vcMusicText' "
"type='OptionsLabel' "
"/> "
@@ -993,7 +1316,7 @@
"type='SmallLabel' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
"<widget name='vcSfxText' "
"type='OptionsLabel' "
"/> "
@@ -1004,7 +1327,7 @@
"type='SmallLabel' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
"<widget name='vcSpeechText' "
"type='OptionsLabel' "
"/> "
@@ -1015,8 +1338,8 @@
"type='SmallLabel' "
"/> "
"</layout> "
-"</layout> "
-"<layout type='vertical' padding='24,0,24,0' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<space size='110' /> "
"<widget name='vcMuteCheckbox' "
"type='Checkbox' "
"/> "
@@ -1025,7 +1348,7 @@
"</dialog> "
"<dialog name='GlobalOptions_MIDI' overlays='Dialog.GlobalOptions.TabWidget'> "
"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> "
"<widget name='mcFontButton' "
"type='Button' "
"/> "
@@ -1046,7 +1369,7 @@
"<widget name='mcGSCheckbox' "
"type='Checkbox' "
"/> "
-"<layout type='horizontal' padding='0,0,0,0'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
"<widget name='mcMidiGainText' "
"type='OptionsLabel' "
"/> "
@@ -1062,7 +1385,7 @@
"</dialog> "
"<dialog name='GlobalOptions_Paths' overlays='Dialog.GlobalOptions.TabWidget'> "
"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='16'> "
"<widget name='SaveButton' "
"type='Button' "
"/> "
@@ -1070,7 +1393,7 @@
"height='Globals.Line.Height' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='16'> "
"<widget name='ThemeButton' "
"type='Button' "
"/> "
@@ -1078,7 +1401,7 @@
"height='Globals.Line.Height' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='16'> "
"<widget name='ExtraButton' "
"type='Button' "
"/> "
@@ -1098,7 +1421,7 @@
"</dialog> "
"<dialog name='GlobalOptions_Misc' overlays='Dialog.GlobalOptions.TabWidget'> "
"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='16'> "
"<widget name='ThemeButton' "
"type='Button' "
"/> "
@@ -1106,17 +1429,21 @@
"height='Globals.Line.Height' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
"<widget name='RendererPopupDesc' "
-"type='OptionsLabel' "
+"width='80' "
+"height='Globals.Line.Height' "
+"textalign='right' "
"/> "
"<widget name='RendererPopup' "
"type='PopUp' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
"<widget name='AutosavePeriodPopupDesc' "
-"type='OptionsLabel' "
+"width='80' "
+"height='Globals.Line.Height' "
+"textalign='right' "
"/> "
"<widget name='AutosavePeriodPopup' "
"type='PopUp' "
@@ -1151,10 +1478,10 @@
"</layout> "
"</layout> "
"</dialog> "
-"<dialog name='GameOptions' overlays='Dialog.Launcher.GameList' shading='dim'> "
+"<dialog name='GameOptions' overlays='screen' inset='16' shading='dim'> "
"<layout type='vertical' padding='0,0,0,0' spacing='16'> "
"<widget name='TabWidget'/> "
-"<layout type='horizontal' padding='16,16,16,4'> "
+"<layout type='horizontal' padding='8,8,8,8'> "
"<space/> "
"<widget name='Cancel' "
"type='Button' "
@@ -1166,7 +1493,7 @@
"</layout> "
"</dialog> "
"<dialog name='GameOptions_Graphics' overlays='Dialog.GlobalOptions.TabWidget'> "
-"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
+"<layout type='vertical' padding='8,8,8,8' spacing='6'> "
"<widget name='EnableTabCheckbox' "
"type='Checkbox' "
"/> "
@@ -1174,7 +1501,7 @@
"</layout> "
"</dialog> "
"<dialog name='GameOptions_Audio' overlays='Dialog.GlobalOptions.TabWidget'> "
-"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
+"<layout type='vertical' padding='8,8,8,8' spacing='6'> "
"<widget name='EnableTabCheckbox' "
"type='Checkbox' "
"/> "
@@ -1182,7 +1509,7 @@
"</layout> "
"</dialog> "
"<dialog name='GameOptions_MIDI' overlays='Dialog.GlobalOptions.TabWidget'> "
-"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
+"<layout type='vertical' padding='8,8,8,8' spacing='6'> "
"<widget name='EnableTabCheckbox' "
"type='Checkbox' "
"/> "
@@ -1190,7 +1517,7 @@
"</layout> "
"</dialog> "
"<dialog name='GameOptions_Volume' overlays='Dialog.GlobalOptions.TabWidget'> "
-"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
+"<layout type='vertical' padding='8,8,8,8' spacing='6'> "
"<widget name='EnableTabCheckbox' "
"type='Checkbox' "
"/> "
@@ -1198,34 +1525,43 @@
"</layout> "
"</dialog> "
"<dialog name='GameOptions_Game' overlays='Dialog.GameOptions.TabWidget' shading='dim'> "
-"<layout type='vertical' padding='16,16,16,16'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='vertical' padding='8,8,8,8'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
"<widget name='Id' "
-"type='OptionsLabel' "
+"width='35' "
+"height='Globals.Line.Height' "
+"textalign='right' "
"/> "
"<widget name='Domain' "
"type='PopUp' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
"<widget name='Name' "
-"type='OptionsLabel' "
+"width='35' "
+"height='Globals.Line.Height' "
+"textalign='right' "
"/> "
"<widget name='Desc' "
"type='PopUp' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<space size='8'/> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
"<widget name='LangPopupDesc' "
-"type='OptionsLabel' "
+"width='60' "
+"height='Globals.Line.Height' "
+"textalign='right' "
"/> "
"<widget name='LangPopup' "
"type='PopUp' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
"<widget name='PlatformPopupDesc' "
-"type='OptionsLabel' "
+"width='60' "
+"height='Globals.Line.Height' "
+"textalign='right' "
"/> "
"<widget name='PlatformPopup' "
"type='PopUp' "
@@ -1234,8 +1570,8 @@
"</layout> "
"</dialog> "
"<dialog name='GameOptions_Paths' overlays='Dialog.GameOptions.TabWidget' shading='dim'> "
-"<layout type='vertical' padding='16,16,16,16'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='vertical' padding='8,8,8,8'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> "
"<widget name='Savepath' "
"type='Button' "
"/> "
@@ -1243,7 +1579,7 @@
"height='Globals.Line.Height' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> "
"<widget name='Extrapath' "
"type='Button' "
"/> "
@@ -1251,7 +1587,7 @@
"height='Globals.Line.Height' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> "
"<widget name='Gamepath' "
"type='Button' "
"/> "
@@ -1262,81 +1598,55 @@
"</layout> "
"</dialog> "
"<dialog name='GlobalMenu' overlays='screen_center'> "
-"<layout type='vertical' padding='16,16,16,16' center='true'> "
+"<layout type='vertical' padding='2,2,4,6' center='true' spacing='6'> "
"<widget name='Title' "
-"width='210' "
-"height='Globals.Line.Height' "
+"width='160' "
+"height='4' "
"/> "
"<widget name='Version' "
-"width='210' "
-"height='Globals.Line.Height' "
-"/> "
-"<widget name='Resume' "
-"width='150' "
-"height='Globals.Button.Height' "
+"width='160' "
+"height='4' "
"/> "
-"<space size='10'/> "
+"<space size='1'/> "
"<widget name='Load' "
-"width='150' "
-"height='Globals.Button.Height' "
+"width='120' "
+"height='12' "
"/> "
"<widget name='Save' "
-"width='150' "
-"height='Globals.Button.Height' "
+"width='120' "
+"height='12' "
"/> "
-"<space size='10'/> "
+"<space size='1'/> "
"<widget name='Options' "
-"width='150' "
-"height='Globals.Button.Height' "
-"/> "
-"<widget name='About' "
-"width='150' "
-"height='Globals.Button.Height' "
+"width='120' "
+"height='12' "
"/> "
-"<space size='10'/> "
-"<widget name='RTL' "
-"width='150' "
-"height='Globals.Button.Height' "
+"<widget name='Help' "
+"width='120' "
+"height='12' "
"/> "
-"<widget name='Quit' "
-"width='150' "
-"height='Globals.Button.Height' "
+"<widget name='About' "
+"width='120' "
+"height='12' "
"/> "
-"</layout> "
-"</dialog> "
-"<dialog name='ScummMain' overlays='screen_center'> "
-"<layout type='vertical' padding='8,8,8,8'> "
+"<space size='1'/> "
"<widget name='Resume' "
-"type='Button' "
-"/> "
-"<space size='15'/> "
-"<widget name='Load' "
-"type='Button' "
-"/> "
-"<widget name='Save' "
-"type='Button' "
-"/> "
-"<space size='15'/> "
-"<widget name='Options' "
-"type='Button' "
-"/> "
-"<widget name='Help' "
-"type='Button' "
+"width='120' "
+"height='12' "
"/> "
-"<widget name='About' "
-"type='Button' "
+"<widget name='RTL' "
+"width='120' "
+"height='12' "
"/> "
-"<space size='15'/> "
"<widget name='Quit' "
-"type='Button' "
+"width='120' "
+"height='12' "
"/> "
"</layout> "
"</dialog> "
-"<dialog name='ScummConfig' overlays='screen_center'> "
+"<dialog name='GlobalConfig' overlays='screen_center'> "
"<layout type='vertical' padding='8,8,8,8'> "
-"<layout type='horizontal' padding='0,0,0,0'> "
-"<layout type='vertical' padding='0,0,0,0' center='true'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='8'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
"<widget name='vcMusicText' "
"type='OptionsLabel' "
"/> "
@@ -1347,7 +1657,7 @@
"type='SmallLabel' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='8'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
"<widget name='vcSfxText' "
"type='OptionsLabel' "
"/> "
@@ -1358,7 +1668,7 @@
"type='SmallLabel' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='8'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
"<widget name='vcSpeechText' "
"type='OptionsLabel' "
"/> "
@@ -1369,25 +1679,24 @@
"type='SmallLabel' "
"/> "
"</layout> "
-"</layout> "
-"<layout type='vertical' padding='24,24,24,24' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<space size='110' /> "
"<widget name='vcMuteCheckbox' "
"type='Checkbox' "
-"width='80' "
+"width='80' "
"/> "
"</layout> "
-"</layout> "
-"<space size='8' /> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
+"<space size='4' /> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
"<widget name='subToggleDesc' "
"type='OptionsLabel' "
"/> "
"<widget name='subToggleButton' "
-"width='158' "
+"width='128' "
"height='Globals.Slider.Height' "
"/> "
"</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
"<widget name='subSubtitleSpeedDesc' "
"type='OptionsLabel' "
"/> "
@@ -1398,8 +1707,8 @@
"type='SmallLabel' "
"/> "
"</layout> "
-"<space size='60'/> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
+"<space size='20'/> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='4'> "
"<widget name='Keys' "
"type='Button' "
"/> "
@@ -1414,23 +1723,15 @@
"</layout> "
"</dialog> "
"<dialog name='SaveLoadChooser' overlays='screen' inset='8' shading='dim'> "
-"<layout type='vertical' padding='8,8,8,32' center='true'> "
-"<widget name='Title' "
-"height='Globals.Line.Height' "
-"/> "
-"<layout type='horizontal' padding='0,0,0,16' spacing='16'> "
+"<layout type='vertical' padding='8,8,8,8' center='true'> "
+"<widget name='Title' height='Globals.Line.Height'/> "
"<widget name='List' /> "
-"<widget name='Thumbnail' "
-"width='180' "
-"height='200' "
-"/> "
-"</layout> "
-"<layout type='horizontal' padding='0,0,0,0'> "
+"<layout type='horizontal' padding='0,0,16,0'> "
"<space/> "
"<widget name='Delete' "
"type='Button' "
"/> "
-"<space size='32'/> "
+"<space size='16'/> "
"<widget name='Cancel' "
"type='Button' "
"/> "
@@ -1440,16 +1741,16 @@
"</layout> "
"</layout> "
"</dialog> "
-"<dialog name='ScummHelp' overlays='screen_center'> "
-"<layout type='vertical' padding='8,8,8,8' center='true'> "
+"<dialog name='ScummHelp' overlays='screen'> "
+"<layout type='vertical' padding='8,8,8,8'> "
"<widget name='Title' "
-"width='320' "
+"width='180' "
"height='Globals.Line.Height' "
"/> "
"<widget name='HelpText' "
-"height='200' "
+"height='140' "
"/> "
-"<layout type='horizontal' padding='0,0,16,0'> "
+"<layout type='horizontal' padding='0,0,0,0'> "
"<widget name='Prev' "
"type='Button' "
"/> "
@@ -1464,20 +1765,20 @@
"</layout> "
"</dialog> "
"<dialog name='MassAdd' overlays='screen_center' shading='dim'> "
-"<layout type='vertical' padding='8,8,32,8' center='true'> "
+"<layout type='vertical' padding='4,4,16,4' center='true'> "
"<widget name='DirProgressText' "
-"width='480' "
+"width='280' "
"height='Globals.Line.Height' "
"/> "
"<widget name='GameProgressText' "
-"width='480' "
+"width='280' "
"height='Globals.Line.Height' "
"/> "
"<widget name='GameList' "
-"width='480' "
-"height='250' "
+"width='280' "
+"height='100' "
"/> "
-"<layout type='horizontal' padding='8,8,8,8'> "
+"<layout type='horizontal' padding='4,4,4,4'> "
"<widget name='Ok' "
"type='Button' "
"/> "
@@ -1488,20 +1789,20 @@
"</layout> "
"</dialog> "
"<dialog name='KeyMapper' overlays='screen_center' shading='dim'> "
-"<layout type='vertical' padding='8,8,32,8' spacing='10' center='true'> "
+"<layout type='vertical' padding='8,8,8,8' spacing='10' center='true'> "
"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
"<widget name='PopupDesc' "
"type='OptionsLabel' "
"/> "
"<widget name='Popup' "
"type='PopUp' "
-"width='400' "
+"width='150' "
"height='Globals.Line.Height' "
"/> "
"</layout> "
"<widget name='KeymapArea' "
-"width='600' "
-"height='280' "
+"width='300' "
+"height='120' "
"/> "
"<widget name='Close' "
"type='Button' "
@@ -1509,359 +1810,3 @@
"</layout> "
"</dialog> "
"</layout_info> "
-"<render_info> "
-"<palette> "
-"<color name='black' "
-"rgb='0,0,0' "
-"/> "
-"<color name='lightgrey' "
-"rgb='104,104,104' "
-"/> "
-"<color name='darkgrey' "
-"rgb='64,64,64' "
-"/> "
-"<color name='green' "
-"rgb='32,160,32' "
-"/> "
-"<color name='green2' "
-"rgb='0,255,0' "
-"/> "
-"</palette> "
-"<fonts> "
-"<font id='text_default' "
-"file='default' "
-"/> "
-"<font id='text_button' "
-"file='default' "
-"/> "
-"<font id='text_normal' "
-"file='default' "
-"/> "
-"<text_color id='color_normal' "
-"color='green' "
-"/> "
-"<text_color id='color_normal_inverted' "
-"color='black' "
-"/> "
-"<text_color id='color_normal_hover' "
-"color='green2' "
-"/> "
-"<text_color id='color_normal_disabled' "
-"color='lightgrey' "
-"/> "
-"<text_color id='color_alternative' "
-"color='lightgrey' "
-"/> "
-"<text_color id='color_alternative_inverted' "
-"color='255,255,255' "
-"/> "
-"<text_color id='color_alternative_hover' "
-"color='176,176,176' "
-"/> "
-"<text_color id='color_alternative_disabled' "
-"color='darkgrey' "
-"/> "
-"<text_color id='color_button' "
-"color='green' "
-"/> "
-"<text_color id='color_button_hover' "
-"color='green2' "
-"/> "
-"<text_color id='color_button_disabled' "
-"color='lightgrey' "
-"/> "
-"</fonts> "
-"<defaults fill='foreground' fg_color='darkgrey' bg_color='black' shadow='0' bevel_color='lightgrey'/> "
-"<drawdata id='text_selection' cache='false'> "
-"<drawstep func='square' "
-"fill='foreground' "
-"fg_color='lightgrey' "
-"/> "
-"</drawdata> "
-"<drawdata id='text_selection_focus' cache='false'> "
-"<drawstep func='square' "
-"fill='foreground' "
-"fg_color='green' "
-"/> "
-"</drawdata> "
-"<drawdata id='mainmenu_bg' cache='false'> "
-"<drawstep func='fill' "
-"fill='foreground' "
-"fg_color='black' "
-"/> "
-"</drawdata> "
-"<drawdata id='special_bg' cache='false'> "
-"<drawstep func='bevelsq' "
-"bevel='2' "
-"/> "
-"</drawdata> "
-"<drawdata id='separator' cache='false'> "
-"<drawstep func='square' "
-"fill='foreground' "
-"height='2' "
-"ypos='center' "
-"fg_color='lightgrey' "
-"/> "
-"</drawdata> "
-"<drawdata id='scrollbar_base' cache='false'> "
-"<drawstep func='bevelsq' "
-"bevel='2' "
-"/> "
-"</drawdata> "
-"<drawdata id='scrollbar_handle_hover' cache='false'> "
-"<drawstep func='square' "
-"fill='foreground' "
-"fg_color='green2' "
-"/> "
-"</drawdata> "
-"<drawdata id='scrollbar_handle_idle' cache='false'> "
-"<drawstep func='square' "
-"fill='foreground' "
-"fg_color='green' "
-"/> "
-"</drawdata> "
-"<drawdata id='scrollbar_button_idle' cache='false'> "
-"<drawstep func='bevelsq' "
-"bevel='2' "
-"fill='none' "
-"/> "
-"<drawstep func='triangle' "
-"fg_color='green' "
-"fill='foreground' "
-"width='auto' "
-"height='auto' "
-"xpos='center' "
-"ypos='center' "
-"orientation='top' "
-"/> "
-"</drawdata> "
-"<drawdata id='scrollbar_button_hover' cache='false'> "
-"<drawstep func='bevelsq' "
-"bevel='2' "
-"fill='none' "
-"/> "
-"<drawstep func='triangle' "
-"fg_color='green2' "
-"fill='foreground' "
-"width='auto' "
-"height='auto' "
-"xpos='center' "
-"ypos='center' "
-"orientation='top' "
-"/> "
-"</drawdata> "
-"<drawdata id='tab_active' cache='false'> "
-"<text font='text_default' "
-"text_color='color_normal_hover' "
-"vertical_align='center' "
-"horizontal_align='center' "
-"/> "
-"<drawstep func='tab' "
-"bevel='2' "
-"radius='0' "
-"fill='none' "
-"/> "
-"</drawdata> "
-"<drawdata id='tab_inactive' cache='false'> "
-"<text font='text_default' "
-"text_color='color_normal' "
-"vertical_align='center' "
-"horizontal_align='center' "
-"/> "
-"<drawstep func='tab' "
-"bevel='2' "
-"radius='0' "
-"fill='none' "
-"/> "
-"</drawdata> "
-"<drawdata id='tab_background' cache='false'> "
-"</drawdata> "
-"<drawdata id='widget_slider' cache='false'> "
-"<drawstep func='bevelsq' "
-"bevel='2' "
-"fill='none' "
-"/> "
-"</drawdata> "
-"<drawdata id='slider_disabled' cache='false'> "
-"<drawstep func='square' "
-"fill='foreground' "
-"fg_color='lightgrey' "
-"/> "
-"</drawdata> "
-"<drawdata id='slider_full' cache='false'> "
-"<drawstep func='square' "
-"fill='foreground' "
-"fg_color='green' "
-"/> "
-"</drawdata> "
-"<drawdata id='slider_hover' cache='false'> "
-"<drawstep func='square' "
-"fill='foreground' "
-"fg_color='green2' "
-"/> "
-"</drawdata> "
-"<drawdata id='widget_small' cache='false'> "
-"<drawstep func='bevelsq' "
-"bevel='2' "
-"fill='none' "
-"/> "
-"</drawdata> "
-"<drawdata id='popup_idle' cache='false'> "
-"<drawstep func='bevelsq' "
-"bevel='2' "
-"fill='none' "
-"/> "
-"<drawstep func='triangle' "
-"fg_color='green' "
-"fill='foreground' "
-"width='height' "
-"height='auto' "
-"xpos='right' "
-"ypos='center' "
-"orientation='bottom' "
-"/> "
-"<text font='text_default' "
-"text_color='color_normal' "
-"vertical_align='center' "
-"horizontal_align='left' "
-"/> "
-"</drawdata> "
-"<drawdata id='popup_disabled' cache='false'> "
-"<drawstep func='bevelsq' "
-"bevel='2' "
-"fill='none' "
-"/> "
-"<drawstep func='triangle' "
-"fg_color='lightgrey' "
-"fill='foreground' "
-"width='height' "
-"height='auto' "
-"xpos='right' "
-"ypos='center' "
-"orientation='bottom' "
-"/> "
-"<text font='text_default' "
-"text_color='color_normal_disabled' "
-"vertical_align='center' "
-"horizontal_align='left' "
-"/> "
-"</drawdata> "
-"<drawdata id='popup_hover' cache='false'> "
-"<drawstep func='bevelsq' "
-"bevel='2' "
-"fill='none' "
-"/> "
-"<drawstep func='triangle' "
-"fg_color='green2' "
-"fill='foreground' "
-"width='height' "
-"height='auto' "
-"xpos='right' "
-"ypos='center' "
-"orientation='bottom' "
-"/> "
-"<text font='text_default' "
-"text_color='color_normal_hover' "
-"vertical_align='center' "
-"horizontal_align='left' "
-"/> "
-"</drawdata> "
-"<drawdata id='widget_textedit' cache='false'> "
-"<drawstep func='bevelsq' "
-"bevel='2' "
-"fill='none' "
-"/> "
-"</drawdata> "
-"<drawdata id='plain_bg' cache='false'> "
-"<drawstep func='bevelsq' "
-"bevel='2' "
-"/> "
-"</drawdata> "
-"<drawdata id='caret' cache='false'> "
-"<drawstep func='square' "
-"fill='foreground' "
-"fg_color='lightgrey' "
-"/> "
-"</drawdata> "
-"<drawdata id='default_bg' cache='false'> "
-"<drawstep func='bevelsq' "
-"bevel='2' "
-"/> "
-"</drawdata> "
-"<drawdata id='button_idle' cache='false'> "
-"<text font='text_button' "
-"text_color='color_button' "
-"vertical_align='center' "
-"horizontal_align='center' "
-"/> "
-"<drawstep func='bevelsq' "
-"bevel='2' "
-"fill='none' "
-"/> "
-"</drawdata> "
-"<drawdata id='button_hover' cache='false'> "
-"<text font='text_button' "
-"text_color='color_button_hover' "
-"vertical_align='center' "
-"horizontal_align='center' "
-"/> "
-"<drawstep func='bevelsq' "
-"bevel='2' "
-"fill='none' "
-"/> "
-"</drawdata> "
-"<drawdata id='button_disabled' cache='false'> "
-"<text font='text_button' "
-"text_color='color_button_disabled' "
-"vertical_align='center' "
-"horizontal_align='center' "
-"/> "
-"<drawstep func='bevelsq' "
-"bevel='2' "
-"fill='none' "
-"/> "
-"</drawdata> "
-"<drawdata id='checkbox_disabled' cache='false'> "
-"<text font='text_default' "
-"text_color='color_normal_disabled' "
-"vertical_align='top' "
-"horizontal_align='left' "
-"/> "
-"<drawstep func='bevelsq' "
-"bevel='2' "
-"fill='none' "
-"/> "
-"</drawdata> "
-"<drawdata id='checkbox_selected' cache='false'> "
-"<text font='text_default' "
-"text_color='color_normal' "
-"vertical_align='top' "
-"horizontal_align='left' "
-"/> "
-"<drawstep func='bevelsq' "
-"bevel='2' "
-"fill='none' "
-"/> "
-"<drawstep func='cross' "
-"fill='foreground' "
-"stroke='2' "
-"fg_color='green' "
-"/> "
-"</drawdata> "
-"<drawdata id='checkbox_default' cache='false'> "
-"<text font='text_default' "
-"text_color='color_normal' "
-"vertical_align='top' "
-"horizontal_align='left' "
-"/> "
-"<drawstep func='bevelsq' "
-"bevel='2' "
-"fill='none' "
-"/> "
-"</drawdata> "
-"<drawdata id='widget_default' cache='false'> "
-"<drawstep func='bevelsq' "
-"bevel='2' "
-"/> "
-"</drawdata> "
-"</render_info> "
diff --git a/gui/themes/scummclassic.zip b/gui/themes/scummclassic.zip
index 9733c7a97e..67cbbf7990 100644
--- a/gui/themes/scummclassic.zip
+++ b/gui/themes/scummclassic.zip
Binary files differ
diff --git a/gui/themes/scummclassic/classic_layout.stx b/gui/themes/scummclassic/classic_layout.stx
index 27164673b4..14f19a7151 100644
--- a/gui/themes/scummclassic/classic_layout.stx
+++ b/gui/themes/scummclassic/classic_layout.stx
@@ -582,6 +582,10 @@
width = '150'
height = 'Globals.Button.Height'
/>
+ <widget name = 'Help'
+ width = '150'
+ height = 'Globals.Button.Height'
+ />
<widget name = 'About'
width = '150'
height = 'Globals.Button.Height'
@@ -598,36 +602,7 @@
</layout>
</dialog>
- <dialog name = 'ScummMain' overlays = 'screen_center'>
- <layout type = 'vertical' padding = '8, 8, 8, 8'>
- <widget name = 'Resume'
- type = 'Button'
- />
- <space size = '15'/>
- <widget name = 'Load'
- type = 'Button'
- />
- <widget name = 'Save'
- type = 'Button'
- />
- <space size = '15'/>
- <widget name = 'Options'
- type = 'Button'
- />
- <widget name = 'Help'
- type = 'Button'
- />
- <widget name = 'About'
- type = 'Button'
- />
- <space size = '15'/>
- <widget name = 'Quit'
- type = 'Button'
- />
- </layout>
- </dialog>
-
- <dialog name = 'ScummConfig' overlays = 'screen_center'>
+ <dialog name = 'GlobalConfig' overlays = 'screen_center'>
<layout type = 'vertical' padding = '8, 8, 8, 8'>
<layout type = 'horizontal' padding = '0, 0, 0, 0'>
<layout type = 'vertical' padding = '0, 0, 0, 0' center = 'true'>
diff --git a/gui/themes/scummclassic/classic_layout_lowres.stx b/gui/themes/scummclassic/classic_layout_lowres.stx
index c8ecb1abaf..8a0180db3c 100644
--- a/gui/themes/scummclassic/classic_layout_lowres.stx
+++ b/gui/themes/scummclassic/classic_layout_lowres.stx
@@ -565,7 +565,7 @@
</dialog>
<dialog name = 'GlobalMenu' overlays = 'screen_center'>
- <layout type = 'vertical' padding = '8, 8, 4, 6' center = 'true'>
+ <layout type = 'vertical' padding = '2, 2, 4, 6' center = 'true' spacing='6'>
<widget name = 'Title'
width = '160'
height = '4'
@@ -588,6 +588,10 @@
width = '120'
height = '12'
/>
+ <widget name = 'Help'
+ width = '120'
+ height = '12'
+ />
<widget name = 'About'
width = '120'
height = '12'
@@ -608,43 +612,7 @@
</layout>
</dialog>
- <dialog name = 'ScummMain' overlays = 'screen_center'>
- <layout type = 'vertical' padding = '8, 8, 8, 8'>
- <widget name = 'Resume'
- width = 'Globals.Button.Width'
- height = '14'
- />
- <space size = '2'/>
- <widget name = 'Load'
- width = 'Globals.Button.Width'
- height = '14'
- />
- <widget name = 'Save'
- width = 'Globals.Button.Width'
- height = '14'
- />
- <space size = '2'/>
- <widget name = 'Options'
- width = 'Globals.Button.Width'
- height = '14'
- />
- <widget name = 'Help'
- width = 'Globals.Button.Width'
- height = '14'
- />
- <widget name = 'About'
- width = 'Globals.Button.Width'
- height = '14'
- />
- <space size = '2'/>
- <widget name = 'Quit'
- width = 'Globals.Button.Width'
- height = '14'
- />
- </layout>
- </dialog>
-
- <dialog name = 'ScummConfig' overlays = 'screen_center'>
+ <dialog name = 'GlobalConfig' overlays = 'screen_center'>
<layout type = 'vertical' padding = '8, 8, 8, 8'>
<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
<widget name = 'vcMusicText'
diff --git a/gui/themes/scummmodern.zip b/gui/themes/scummmodern.zip
index e6cda4ddbb..b3dc16b67e 100644
--- a/gui/themes/scummmodern.zip
+++ b/gui/themes/scummmodern.zip
Binary files differ
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index 2fd14f37aa..25add37462 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -595,6 +595,10 @@
width = '150'
height = 'Globals.Button.Height'
/>
+ <widget name = 'Help'
+ width = '150'
+ height = 'Globals.Button.Height'
+ />
<widget name = 'About'
width = '150'
height = 'Globals.Button.Height'
@@ -611,36 +615,7 @@
</layout>
</dialog>
- <dialog name = 'ScummMain' overlays = 'screen_center'>
- <layout type = 'vertical' padding = '8, 8, 8, 8'>
- <widget name = 'Resume'
- type = 'Button'
- />
- <space size = '15'/>
- <widget name = 'Load'
- type = 'Button'
- />
- <widget name = 'Save'
- type = 'Button'
- />
- <space size = '15'/>
- <widget name = 'Options'
- type = 'Button'
- />
- <widget name = 'Help'
- type = 'Button'
- />
- <widget name = 'About'
- type = 'Button'
- />
- <space size = '15'/>
- <widget name = 'Quit'
- type = 'Button'
- />
- </layout>
- </dialog>
-
- <dialog name = 'ScummConfig' overlays = 'screen_center'>
+ <dialog name = 'GlobalConfig' overlays = 'screen_center'>
<layout type = 'vertical' padding = '8, 8, 8, 8' spacing = '8'>
<layout type = 'horizontal' padding = '0, 0, 0, 0'>
<layout type = 'vertical' padding = '0, 0, 0, 0' spacing = '8' center = 'true'>
diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
index e837e41e82..1c2f83a3b7 100644
--- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx
+++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
@@ -561,7 +561,7 @@
</dialog>
<dialog name = 'GlobalMenu' overlays = 'screen_center'>
- <layout type = 'vertical' padding = '4, 4, 4, 4' center = 'true'>
+ <layout type = 'vertical' padding = '4, 4, 4, 4' center = 'true' spacing='2'>
<widget name = 'Title'
width = '160'
height = 'Globals.Line.Height'
@@ -584,6 +584,10 @@
width = '120'
height = 'Globals.Button.Height'
/>
+ <widget name = 'Help'
+ width = '120'
+ height = 'Globals.Button.Height'
+ />
<widget name = 'About'
width = '120'
height = 'Globals.Button.Height'
@@ -604,36 +608,7 @@
</layout>
</dialog>
- <dialog name = 'ScummMain' overlays = 'screen_center'>
- <layout type = 'vertical' padding = '4, 4, 4, 4'>
- <widget name = 'Resume'
- type = 'Button'
- />
- <space size = '8'/>
- <widget name = 'Load'
- type = 'Button'
- />
- <widget name = 'Save'
- type = 'Button'
- />
- <space size = '8'/>
- <widget name = 'Options'
- type = 'Button'
- />
- <widget name = 'Help'
- type = 'Button'
- />
- <widget name = 'About'
- type = 'Button'
- />
- <space size = '8'/>
- <widget name = 'Quit'
- type = 'Button'
- />
- </layout>
- </dialog>
-
- <dialog name = 'ScummConfig' overlays = 'screen_center'>
+ <dialog name = 'GlobalConfig' overlays = 'screen_center'>
<layout type = 'vertical' padding = '8, 8, 8, 8'>
<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
<widget name = 'vcMusicText'
diff --git a/ports.mk b/ports.mk
index 4d819f54f9..8f51ebb005 100644
--- a/ports.mk
+++ b/ports.mk
@@ -44,7 +44,9 @@ bundle: scummvm-static
cp $(srcdir)/icons/scummvm.icns $(bundle_name)/Contents/Resources/
cp $(DIST_FILES_DOCS) $(bundle_name)/
cp $(DIST_FILES_THEMES) $(bundle_name)/Contents/Resources/
+ifdef DIST_FILES_ENGINEDATA
cp $(DIST_FILES_ENGINEDATA) $(bundle_name)/Contents/Resources/
+endif
$(srcdir)/tools/credits.pl --rtf > $(bundle_name)/Contents/Resources/Credits.rtf
chmod 644 $(bundle_name)/Contents/Resources/*
cp scummvm-static $(bundle_name)/Contents/MacOS/scummvm
@@ -56,7 +58,9 @@ iphonebundle: iphone
cp $(srcdir)/dists/iphone/Info.plist $(bundle_name)/
cp $(DIST_FILES_DOCS) $(bundle_name)/
cp $(DIST_FILES_THEMES) $(bundle_name)/
+ifdef DIST_FILES_ENGINEDATA
cp $(DIST_FILES_ENGINEDATA) $(bundle_name)/
+endif
$(STRIP) scummvm
ldid -S scummvm
chmod 755 scummvm
@@ -151,7 +155,9 @@ win32dist: $(EXECUTABLE)
mkdir -p $(WIN32PATH)
$(STRIP) $(EXECUTABLE) -o $(WIN32PATH)/$(EXECUTABLE)
cp $(DIST_FILES_THEMES) $(WIN32PATH)
+ifdef DIST_FILES_ENGINEDATA
cp $(DIST_FILES_ENGINEDATA) $(WIN32PATH)
+endif
cp $(srcdir)/AUTHORS $(WIN32PATH)/AUTHORS.txt
cp $(srcdir)/COPYING $(WIN32PATH)/COPYING.txt
cp $(srcdir)/COPYING.LGPL $(WIN32PATH)/COPYING.LGPL.txt
@@ -172,7 +178,9 @@ aos4dist: $(EXECUTABLE)
$(STRIP) $(EXECUTABLE) -o $(AOS4PATH)/$(EXECUTABLE)
cp icons/scummvm.info $(AOS4PATH)/$(EXECUTABLE).info
cp $(DIST_FILES_THEMES) $(AOS4PATH)/themes/
+ifdef DIST_FILES_ENGINEDATA
cp $(DIST_FILES_ENGINEDATA) $(AOS4PATH)/extras/
+endif
cp $(srcdir)/AUTHORS $(AOS4PATH)/AUTHORS.txt
cp $(srcdir)/COPYING $(AOS4PATH)/COPYING.txt
cp $(srcdir)/COPYING.LGPL $(AOS4PATH)/COPYING.LGPL.txt
diff --git a/sound/decoders/adpcm.cpp b/sound/decoders/adpcm.cpp
index 7a85bc24d5..c8a907d13e 100644
--- a/sound/decoders/adpcm.cpp
+++ b/sound/decoders/adpcm.cpp
@@ -724,6 +724,10 @@ int16 Ima_ADPCMStream::decodeIMA(byte code, int channel) {
}
RewindableAudioStream *makeADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, typesADPCM type, int rate, int channels, uint32 blockAlign) {
+ // If size is 0, report the entire size of the stream
+ if (!size)
+ size = stream->size();
+
switch (type) {
case kADPCMOki:
return new Oki_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign);
diff --git a/sound/decoders/mp3.cpp b/sound/decoders/mp3.cpp
index f66d6324ef..732ae58b67 100644
--- a/sound/decoders/mp3.cpp
+++ b/sound/decoders/mp3.cpp
@@ -36,7 +36,9 @@
#include <mad.h>
-
+#if defined(__PSP__)
+ #include "backends/platform/psp/mp3.h"
+#endif
namespace Audio {
@@ -347,7 +349,18 @@ int MP3Stream::readBuffer(int16 *buffer, const int numSamples) {
SeekableAudioStream *makeMP3Stream(
Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse) {
+
+#if defined(__PSP__)
+ SeekableAudioStream *s = 0;
+
+ if (Mp3PspStream::isOkToCreateStream())
+ s = new Mp3PspStream(stream, disposeAfterUse);
+
+ if (!s) // go to regular MAD mp3 stream if ME fails
+ s = new MP3Stream(stream, disposeAfterUse);
+#else
SeekableAudioStream *s = new MP3Stream(stream, disposeAfterUse);
+#endif
if (s && s->endOfData()) {
delete s;
return 0;
diff --git a/sound/decoders/voc.cpp b/sound/decoders/voc.cpp
index 5663861f05..e9af7ece3f 100644
--- a/sound/decoders/voc.cpp
+++ b/sound/decoders/voc.cpp
@@ -384,7 +384,7 @@ AudioStream *makeVOCStream(Common::SeekableReadStream *stream, byte flags, uint
SeekableAudioStream *makeVOCStream(Common::SeekableReadStream *stream, byte flags, DisposeAfterUse::Flag disposeAfterUse) {
#ifdef STREAM_AUDIO_FROM_DISK
- return makeVOCDiskStreamNoLoop(*stream, flags, disposeAfterUse);
+ return makeVOCDiskStreamNoLoop(stream, flags, disposeAfterUse);
#else
int size, rate;
diff --git a/sound/decoders/vorbis.cpp b/sound/decoders/vorbis.cpp
index 39068603de..64869d7843 100644
--- a/sound/decoders/vorbis.cpp
+++ b/sound/decoders/vorbis.cpp
@@ -35,7 +35,7 @@
#include "sound/audiocd.h"
#ifdef USE_TREMOR
-#ifdef __GP32__ // GP32 uses custom libtremor
+#if defined(ANDROID) || defined(__GP32__) // custom libtremor locations
#include <ivorbisfile.h>
#else
#include <tremor/ivorbisfile.h>
diff --git a/sound/midiparser.cpp b/sound/midiparser.cpp
index 99319461e9..8ae2bad71a 100644
--- a/sound/midiparser.cpp
+++ b/sound/midiparser.cpp
@@ -47,6 +47,10 @@ _num_tracks(0),
_active_track(255),
_abort_parse(0) {
memset(_active_notes, 0, sizeof(_active_notes));
+ _next_event.start = NULL;
+ _next_event.delta = 0;
+ _next_event.event = 0;
+ _next_event.length = 0;
}
void MidiParser::property(int prop, int value) {
diff --git a/sound/mods/rjp1.cpp b/sound/mods/rjp1.cpp
index fc1b49e9e9..be376d61a4 100644
--- a/sound/mods/rjp1.cpp
+++ b/sound/mods/rjp1.cpp
@@ -422,7 +422,7 @@ void Rjp1::setupNote(Rjp1Channel *channel, int16 period) {
channel->envelopeMode = 4;
channel->data = channel->waveData;
channel->pos = READ_BE_UINT16(note + 16);
- channel->len = READ_BE_UINT16(note + 18);
+ channel->len = channel->pos + READ_BE_UINT16(note + 18);
channel->setupNewNote = true;
}
}
diff --git a/test/common/str.h b/test/common/str.h
index 16fb0859db..6581c37cdb 100644
--- a/test/common/str.h
+++ b/test/common/str.h
@@ -118,6 +118,30 @@ class StringTestSuite : public CxxTest::TestSuite
TS_ASSERT_EQUALS(foo3, "fooasdkadklasdjklasdjlkasjdlkasjdklasjdlkjasdasd""fooasdkadklasdjklasdjlkasjdlkasjdklasjdlkjasdasd");
}
+ void test_refCount5() {
+ // using external storage
+ Common::String foo1("HelloHelloHelloHelloAndHi");
+ Common::String foo2(foo1);
+
+ for (Common::String::iterator i = foo2.begin(); i != foo2.end(); ++i)
+ *i = 'h';
+
+ TS_ASSERT_EQUALS(foo1, "HelloHelloHelloHelloAndHi");
+ TS_ASSERT_EQUALS(foo2, "hhhhhhhhhhhhhhhhhhhhhhhhh");
+ }
+
+ void test_refCount6() {
+ // using internal storage
+ Common::String foo1("Hello");
+ Common::String foo2(foo1);
+
+ for (Common::String::iterator i = foo2.begin(); i != foo2.end(); ++i)
+ *i = 'h';
+
+ TS_ASSERT_EQUALS(foo1, "Hello");
+ TS_ASSERT_EQUALS(foo2, "hhhhh");
+ }
+
void test_self_asignment() {
Common::String foo1("12345678901234567890123456789012");
foo1 = foo1.c_str() + 2;
diff --git a/tools/create_msvc/create_msvc.cpp b/tools/create_msvc/create_msvc.cpp
index a2636dab21..f418971239 100644
--- a/tools/create_msvc/create_msvc.cpp
+++ b/tools/create_msvc/create_msvc.cpp
@@ -511,8 +511,6 @@ int main(int argc, char *argv[]) {
// 4103 (alignment changed after including header, may be due to missing #pragma pack(pop))
// used by pack-start / pack-end
//
- // 4121 (alignment of a member was sensitive to packing)
- //
// 4127 (conditional expression is constant)
// used in a lot of engines
//
@@ -566,7 +564,6 @@ int main(int argc, char *argv[]) {
projectWarnings["lure"] = "4189;4355";
projectWarnings["kyra"] = "4355";
projectWarnings["m4"] = "4355";
- projectWarnings["mohawk"] = "4121";
ProjectProvider *provider = NULL;
diff --git a/tools/credits.pl b/tools/credits.pl
index 165834b9b9..bff1251f7a 100755
--- a/tools/credits.pl
+++ b/tools/credits.pl
@@ -666,6 +666,10 @@ begin_credits("Credits");
begin_section("Backend Teams");
+ begin_section("Android");
+ add_person("Angus Lees", "Gus", "");
+ end_section();
+
begin_section("Dreamcast");
add_person("Marcus Comstedt", "", "");
end_section();
diff --git a/tools/update-version.pl b/tools/update-version.pl
index f151ae7514..81aa5c27f9 100755
--- a/tools/update-version.pl
+++ b/tools/update-version.pl
@@ -40,6 +40,7 @@ my @subs_files = qw(
dists/iphone/Info.plist
dists/irix/scummvm.spec
dists/wii/meta.xml
+ dists/android/AndroidManifest.xml
backends/platform/psp/README.PSP
);